Frank Frank 07 Apr 2004 When an Icon or Unicon procedure creates a co-expression, that co-expression maintains a copy of all the dynamic local variables of that procedure. Clearly, it makes sense to include copies of the dynamic variables actually referenced by the co-expression, i.e. those dynamic variable that actually appear in the "create" expression. But does it make sense for the co-expression to include copies of dynamic variables that are NOT referenced? Consider this example: global warming procedure hello_world() local paper, hero static shock ... coexp1 := create find( shock, !warming || hero ) ... end The co-expression assigned to coexp1 is required to have its own copy of the dynamic variable hero, since hero is needed to evaluate the expression. But this co-expression would also have a copy of the dynamic variable paper, even though this variable is never referenced by co-expression and cannot possibly affect this co-expression's evaluation. This can result in the Icon / Unicon to maintain unreachable memory, even after garbage collection. For example paper := some_really_really_big_structure() coexp1 := create find( shock, !warming || hero ) ... paper := &null # This does NOT permit garbage collection of the really, really # large structure, since the copy of the paper variable in coexp1 # still has this structure as its initial value. The memory wasted by unused local variables is the source of this longstanding Icon bug (going back to at least version 7). If an expression such as x := create some_expr_not_involving_x() is used in a loop, and x is a local variable, unreferenceable co-expressions are generated by each successive create operation. These co-expressions are not garbage collected. This problem can be circumvented by making x a static or global variable or by assigning a value to x before the create operation, as in x := &null x := create some_expr_not_involving_x() To understand this bug (and work-around), consider what happens when x := create some_expr_not_involving_x() is evaluated the first time. A co-expression (call it coexp_1) is created. Since x is a local variable, coexp_1 has a copy of x, initialized to the current value of x. After coexp_1 is created, it is stored in the local variable x. Next consider what happens with this assignment statement in the second iteration of the loop. A new co-expression is created (call it coexp_2) and this co-expression has a copy of x. At this time, however, x equals coexp_1, so the copy of x in coexp_2 is initially equal to coexp_1. Now assuming that coexp_1 is not stored anywhere else, coexp_1 becomes unreachable once coexp_2 is assigned to x. Unfortunately, coexp_1 is still referenced by the copy of x in coexp_2, so coexp_1 is not garbage collected. This problem gets worse with further iterations. The third iteration of the above assignment statement will create the co-expression coexp_3 with a copy of x with the initial value coexp_2, which in turn holds a copy of x with the initial value of coexp_1. After coexp_3 is stored in x, coexp_1 and coexp_2 is unreachable, but cannot be garbage collected. If there were 100 iterations, 100 co-expressions are created, even though only one of them can be accessed! Ideally, a co-expression should only contain copies of the local variables it uses. We can basically get the same effect by making the co-expression variable block a copy of the local procedure block, but initialize each unreferenced variable with &null. For example, if the current local variables are hero and paper, then coexp1 := find( shock, !warming || hero ) may have copies of hero and paper, but only hero would be initialized with its dynamic counterpart; paper would be initialized as &null. Is this worth fixing? If so, how would you approach it. I have some ideas as to how we could do this.