Placate a number of `syntax-check' verifications.
[bpt/guile.git] / module / language / tree-il / fix-letrec.scm
index bef31ce..f387df1 100644 (file)
@@ -1,6 +1,6 @@
 ;;; transformation of letrec into simpler forms
 
-;; Copyright (C) 2009 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
 
 ;;;; This library is free software; you can redistribute it and/or
 ;;;; modify it under the terms of the GNU Lesser General Public
   #:export (fix-letrec!))
 
 ;; For a detailed discussion, see "Fixing Letrec: A Faithful Yet
-;; Efficient Implementation of Schemes Recursive Binding Construct", by
+;; Efficient Implementation of Scheme's Recursive Binding Construct", by
 ;; Oscar Waddell, Dipanwita Sarkar, and R. Kent Dybvig.
 
 (define fix-fold
   (make-tree-il-folder unref ref set simple lambda complex))
 
-(define (simple-expression? x bound-vars)
+(define (simple-expression? x bound-vars simple-primitive?)
   (record-case x
     ((<void>) #t)
     ((<const>) #t)
     ((<lexical-ref> gensym)
      (not (memq gensym bound-vars)))
     ((<conditional> test consequent alternate)
-     (and (simple-expression? test bound-vars)
-          (simple-expression? consequent bound-vars)
-          (simple-expression? alternate bound-vars)))
+     (and (simple-expression? test bound-vars simple-primitive?)
+          (simple-expression? consequent bound-vars simple-primitive?)
+          (simple-expression? alternate bound-vars simple-primitive?)))
     ((<sequence> exps)
-     (and-map (lambda (x) (simple-expression? x bound-vars))
+     (and-map (lambda (x) (simple-expression? x bound-vars simple-primitive?))
               exps))
     ((<application> proc args)
      (and (primitive-ref? proc)
-          (effect-free-primitive? (primitive-ref-name proc))
-          (and-map (lambda (x) (simple-expression? x bound-vars))
+          (simple-primitive? (primitive-ref-name proc))
+          ;; FIXME: check arity?
+          (and-map (lambda (x)
+                     (simple-expression? x bound-vars simple-primitive?))
                    args)))
     (else #f)))
 
                                simple
                                lambda*
                                complex))
-                      ((<letrec> vars)
-                       (values (append vars unref)
+                      ((<letrec> gensyms)
+                       (values (append gensyms unref)
                                ref
                                set
                                simple
                                lambda*
                                complex))
-                      ((<let> vars)
-                       (values (append vars unref)
+                      ((<let> gensyms)
+                       (values (append gensyms unref)
                                ref
                                set
                                simple
                        (values unref ref set simple lambda* complex))))
                   (lambda (x unref ref set simple lambda* complex)
                     (record-case x
-                      ((<letrec> (orig-vars vars) vals)
-                       (let lp ((vars orig-vars) (vals vals)
+                      ((<letrec> in-order? (orig-gensyms gensyms) vals)
+                       (let lp ((gensyms orig-gensyms) (vals vals)
                                 (s '()) (l '()) (c '()))
                          (cond
-                          ((null? vars)
-                           (values unref
+                          ((null? gensyms)
+                           ;; Unreferenced complex vars are still
+                           ;; complex for letrec*.  We need to update
+                           ;; our algorithm to "Fixing letrec reloaded"
+                           ;; to fix this.
+                           (values (if in-order?
+                                       (lset-difference eq? unref c)
+                                       unref)
                                    ref
                                    set
                                    (append s simple)
                                    (append l lambda*)
                                    (append c complex)))
-                          ((memq (car vars) unref)
-                           (lp (cdr vars) (cdr vals)
-                               s l c))
-                          ((memq (car vars) set)
-                           (lp (cdr vars) (cdr vals)
-                               s l (cons (car vars) c)))
+                          ((memq (car gensyms) unref)
+                           ;; See above note about unref and letrec*.
+                           (if (and in-order?
+                                    (not (lambda? (car vals)))
+                                    (not (simple-expression?
+                                          (car vals) orig-gensyms
+                                          effect+exception-free-primitive?)))
+                               (lp (cdr gensyms) (cdr vals)
+                                   s l (cons (car gensyms) c))
+                               (lp (cdr gensyms) (cdr vals)
+                                   s l c)))
+                          ((memq (car gensyms) set)
+                           (lp (cdr gensyms) (cdr vals)
+                               s l (cons (car gensyms) c)))
                           ((lambda? (car vals))
-                           (lp (cdr vars) (cdr vals)
-                               s (cons (car vars) l) c))
-                          ((simple-expression? (car vals) orig-vars)
-                           (lp (cdr vars) (cdr vals)
-                               (cons (car vars) s) l c))
+                           (lp (cdr gensyms) (cdr vals)
+                               s (cons (car gensyms) l) c))
+                          ((simple-expression?
+                            (car vals) orig-gensyms
+                            (if in-order?
+                                effect+exception-free-primitive?
+                                effect-free-primitive?))
+                           ;; For letrec*, we can't consider e.g. `car' to be
+                           ;; "simple", as it could raise an exception. Hence
+                           ;; effect+exception-free-primitive? above.
+                           (lp (cdr gensyms) (cdr vals)
+                               (cons (car gensyms) s) l c))
                           (else
-                           (lp (cdr vars) (cdr vals)
-                               s l (cons (car vars) c))))))
-                      ((<let> (orig-vars vars) vals)
+                           (lp (cdr gensyms) (cdr vals)
+                               s l (cons (car gensyms) c))))))
+                      ((<let> (orig-gensyms gensyms) vals)
                        ;; The point is to compile let-bound lambdas as
                        ;; efficiently as we do letrec-bound lambdas, so
                        ;; we use the same algorithm for analyzing the
-                       ;; vars. There is no problem recursing into the
+                       ;; gensyms. There is no problem recursing into the
                        ;; bindings after the let, because all variables
                        ;; have been renamed.
-                       (let lp ((vars orig-vars) (vals vals)
+                       (let lp ((gensyms orig-gensyms) (vals vals)
                                 (s '()) (l '()) (c '()))
                          (cond
-                          ((null? vars)
+                          ((null? gensyms)
                            (values unref
                                    ref
                                    set
                                    (append s simple)
                                    (append l lambda*)
                                    (append c complex)))
-                          ((memq (car vars) unref)
-                           (lp (cdr vars) (cdr vals)
+                          ((memq (car gensyms) unref)
+                           (lp (cdr gensyms) (cdr vals)
                                s l c))
-                          ((memq (car vars) set)
-                           (lp (cdr vars) (cdr vals)
-                               s l (cons (car vars) c)))
+                          ((memq (car gensyms) set)
+                           (lp (cdr gensyms) (cdr vals)
+                               s l (cons (car gensyms) c)))
                           ((and (lambda? (car vals))
-                                (not (memq (car vars) set)))
-                           (lp (cdr vars) (cdr vals)
-                               s (cons (car vars) l) c))
+                                (not (memq (car gensyms) set)))
+                           (lp (cdr gensyms) (cdr vals)
+                               s (cons (car gensyms) l) c))
                           ;; There is no difference between simple and
                           ;; complex, for the purposes of let. Just lump
                           ;; them all into complex.
                           (else
-                           (lp (cdr vars) (cdr vals)
-                               s l (cons (car vars) c))))))
+                           (lp (cdr gensyms) (cdr vals)
+                               s l (cons (car gensyms) c))))))
                       (else
                        (values unref ref set simple lambda* complex))))
                   '()
               (make-sequence #f (list exp (make-void #f)))
               x))
 
-         ((<letrec> src names vars vals body)
-          (let ((binds (map list vars names vals)))
+         ((<letrec> src in-order? names gensyms vals body)
+          (let ((binds (map list gensyms names vals)))
+            ;; The bindings returned by this function need to appear in the same
+            ;; order that they appear in the letrec.
             (define (lookup set)
-              (map (lambda (v) (assq v binds))
-                   (lset-intersection eq? vars set)))
+              (let lp ((binds binds))
+                (cond
+                 ((null? binds) '())
+                 ((memq (caar binds) set)
+                  (cons (car binds) (lp (cdr binds))))
+                 (else (lp (cdr binds))))))
             (let ((u (lookup unref))
                   (s (lookup simple))
                   (l (lookup lambda*))
                   ;; The right-hand-sides of the unreferenced
                   ;; bindings, for effect.
                   (map caddr u)
-                  (if (null? c)
-                      ;; No complex bindings, just emit the body.
-                      (list body)
-                      (list
-                       ;; Evaluate the the "complex" bindings, in a `let' to
-                       ;; indicate that order doesn't matter, and bind to
-                       ;; their variables.
-                       (let ((tmps (map (lambda (x) (gensym)) c)))
-                         (make-let
-                          #f (map cadr c) tmps (map caddr c)
-                          (make-sequence
-                           #f
-                           (map (lambda (x tmp)
-                                  (make-lexical-set
-                                   #f (cadr x) (car x)
-                                   (make-lexical-ref #f (cadr x) tmp)))
-                                c tmps))))
-                       ;; Finally, the body.
-                       body)))))))))
+                  (cond
+                   ((null? c)
+                    ;; No complex bindings, just emit the body.
+                    (list body))
+                   (in-order?
+                    ;; For letrec*, assign complex bindings in order, then the
+                    ;; body.
+                    (append
+                     (map (lambda (c)
+                            (make-lexical-set #f (cadr c) (car c)
+                                              (caddr c)))
+                          c)
+                     (list body)))
+                   (else
+                    ;; Otherwise for plain letrec, evaluate the "complex"
+                    ;; bindings, in a `let' to indicate that order doesn't
+                    ;; matter, and bind to their variables.
+                    (list
+                     (let ((tmps (map (lambda (x) (gensym)) c)))
+                       (make-let
+                        #f (map cadr c) tmps (map caddr c)
+                        (make-sequence
+                         #f
+                         (map (lambda (x tmp)
+                                (make-lexical-set
+                                 #f (cadr x) (car x)
+                                 (make-lexical-ref #f (cadr x) tmp)))
+                              c tmps))))
+                     body))))))))))
 
-         ((<let> src names vars vals body)
-          (let ((binds (map list vars names vals)))
+         ((<let> src names gensyms vals body)
+          (let ((binds (map list gensyms names vals)))
             (define (lookup set)
               (map (lambda (v) (assq v binds))
-                   (lset-intersection eq? vars set)))
+                   (lset-intersection eq? gensyms set)))
             (let ((u (lookup unref))
                   (l (lookup lambda*))
                   (c (lookup complex)))
          
          (else x)))
      x)))
+
+;;; Local Variables:
+;;; eval: (put 'record-case 'scheme-indent-function 1)
+;;; End: