when leaving a non-tail let, allow bound vals to be collected
authorAndy Wingo <wingo@pobox.com>
Wed, 9 Nov 2011 22:45:53 +0000 (23:45 +0100)
committerAndy Wingo <wingo@pobox.com>
Wed, 9 Nov 2011 22:45:53 +0000 (23:45 +0100)
* module/language/tree-il/compile-glil.scm (flatten-lambda-case): Clear
  lexical stack slots at the end of a non-tail let, letrec, or fix.
  Fixes http://debbugs.gnu.org/9900.

* test-suite/tests/gc.test ("gc"): Add test.

module/language/tree-il/compile-glil.scm
test-suite/tests/gc.test

index 3daac7f..de55026 100644 (file)
           (if (eq? context 'tail)
               (emit-code #f (make-glil-call 'return 1)))))
     
+    ;; After lexical binding forms in non-tail context, call this
+    ;; function to clear stack slots, allowing their previous values to
+    ;; be collected.
+    (define (clear-stack-slots context syms)
+      (case context
+        ((push drop)
+         (for-each (lambda (v)
+                     (and=>
+                      ;; Can be #f if the var is labels-allocated.
+                      (hashq-ref allocation v)
+                      (lambda (h)
+                        (pmatch (hashq-ref h self)
+                          ((#t _ . ,n)
+                           (emit-code #f (make-glil-void))
+                           (emit-code #f (make-glil-lexical #t #f 'set n)))
+                          (,loc (error "bad let var allocation" x loc))))))
+                   syms))))
+
     (record-case x
       ((<void>)
        (case context
                      (,loc (error "bad let var allocation" x loc))))
                  (reverse gensyms))
        (comp-tail body)
+       (clear-stack-slots context gensyms)
        (emit-code #f (make-glil-unbind)))
 
       ((<letrec> src in-order? names gensyms vals body)
                        (,loc (error "bad letrec var allocation" x loc))))
                    (reverse gensyms))))
        (comp-tail body)
+       (clear-stack-slots context gensyms)
        (emit-code #f (make-glil-unbind)))
 
       ((<fix> src names gensyms vals body)
          (comp-tail body)
          (if new-RA
              (emit-label new-RA))
+         (clear-stack-slots context gensyms)
          (emit-code #f (make-glil-unbind))))
 
       ((<let-values> src exp body)
                           (,loc (error "bad let-values var allocation" x loc))))
                       (reverse gensyms))
             (comp-tail body)
+            (clear-stack-slots context gensyms)
             (emit-code #f (make-glil-unbind))))))
 
       ;; much trickier than i thought this would be, at first, due to the need
index 57643e8..25dc577 100644 (file)
 ;;;; License along with this library; if not, write to the Free Software
 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-(use-modules (ice-9 documentation)
-            (test-suite lib))
+(define-module (test-suite tests gc)
+  #:use-module (ice-9 documentation)
+  #:use-module (test-suite lib)
+  #:use-module ((system base compile) #:select (compile)))
 
 
 ;;;
       (add-hook! after-gc-hook thunk)
       (gc)
       (remove-hook! after-gc-hook thunk)
-      foo)))
+      foo))
 
-
-(with-test-prefix "gc"
   (pass-if "Unused modules are removed"
     (let* ((guard (make-guardian))
            (total 1000))
       (stack-cleanup 20)
 
       (gc)
-      (gc) ;; twice: have to kill the weak vectors.
-      (gc) ;; thrice: because the test doesn't succeed with only
-           ;; one gc round. not sure why.
+      (gc)   ;; twice: have to kill the weak vectors.
+      (gc)   ;; thrice: because the test doesn't succeed with only
+      ;; one gc round. not sure why.
 
       (= (let lp ((i 0))
            (if (guard)
                (lp (1+ i))
                i))
-         total))))
+         total)))
+
+  (pass-if "Lexical vars are collectable"
+    (procedure?
+     (compile
+      '(begin
+         (define guardian (make-guardian))
+         (let ((f (lambda () (display "test\n"))))
+           (guardian f))
+         (gc)(gc)(gc)
+         (guardian))))))