Well, imagine your type-system was able to tell you exactly what roots are mutable in what scope. Now you can run a garbage collection on a single scope.
That's not how it works, but in pseudo code:
collect {
// Some code
return ...;
}
Well, if you don't have a type-system that tells you what is mutable, you have to either scan all the mutable roots, or maintain a write-barrier to know what could have captured data in the scope.
The problem with write-barriers, which is the go-to solution for generational GCs. Is that it is a conservative approach. Meaning, it will promote garbage in the cases where the pointer that lived outside of the scope is dead.
If you use a write-barrier, what is going to happen is that [A, B, C] is going to be promoted, because the barrier is going to track the object myObject, and it doesn't realize that it's dead.
However, instead, imagine you have a type-system that tells you that in that scope, the only thing that can be mutated is myObject!
Well, now you can run that in a loop, without accumulating garbage!
So, myObject and the list are allocated in different generations/scopes/etc, and we'd typically trip the write barrier, if we kept the program as is. Right. I don't see how it follows after, that we can avoid the barrier based on just mutability information. It is true that we can only "capture" data with the involvement of mutable objects, but I am struggling to figure out what more we can derive from mutability information. It feels like I'm missing some inference step between "the only thing that can be mutated is myObject" and "now you can run that in a loop, without accumulating garbage" and I can't think of one.
I would be more tempted as a compiler writer to delete all the code, since nothing is ever read outside the loop; but such analysis again doesn't depend on mutability type information per se, rather on what mutations occur in the program.
collect { // Some code return ...; }
Well, if you don't have a type-system that tells you what is mutable, you have to either scan all the mutable roots, or maintain a write-barrier to know what could have captured data in the scope.
The problem with write-barriers, which is the go-to solution for generational GCs. Is that it is a conservative approach. Meaning, it will promote garbage in the cases where the pointer that lived outside of the scope is dead.
Let's take an example, again, in pseudo code:
myObject = { field1: []};
collect { myObject.field1 = [A, B, C]; myObject = null; }
If you use a write-barrier, what is going to happen is that [A, B, C] is going to be promoted, because the barrier is going to track the object myObject, and it doesn't realize that it's dead.
However, instead, imagine you have a type-system that tells you that in that scope, the only thing that can be mutated is myObject!
Well, now you can run that in a loop, without accumulating garbage!
Makes sense?