~ chicken-core (chicken-5) 75c0461c8b541a3b03e337e7546ce3b0ea4e6931


commit 75c0461c8b541a3b03e337e7546ce3b0ea4e6931
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Wed Nov 8 12:26:38 2023 +0100
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Fri Nov 10 14:57:29 2023 +0100

    Detect redefinitions of defining forms correctly (#1132)
    
    The scanning for local definitions in ##sys#canonicalize-body used
    what I think is incorrect logic to detect whether references to
    local "define-*" forms need to be expanded.
    
    The two problems where: ##sys#macro-environment was not consulted,
    so the global (default) definition would never be found to be compared
    with the stored meaning in "define-definition", etc., resulting in
    the fallback mode of merely testing for eq? to be used in all cases.
    
    Second, after looking up the entry in the syntactic environment, the
    value could result in a reference to another definition, so the
    lookup operation needs to be repeated.
    
    I have added test cases, as given in #1132 and removed an existing
    test that seems to be wrong.
    
    Signed-off-by: Peter Bex <peter@more-magic.net>

diff --git a/NEWS b/NEWS
index 32650079..f8d0f085 100644
--- a/NEWS
+++ b/NEWS
@@ -58,6 +58,7 @@
 - Syntax expander
   - When passing a module as an environment to eval, correctly resolve
     identifiers in macro expansions (#1295 reported by Caolan McMahon).
+  - Internal definitions honor rebindings of core special forms (#1132).
 
 - Compiler
   - When emitting types files, the output list is now sorted, to ensure
diff --git a/expand.scm b/expand.scm
index ba4737b5..67ddf228 100644
--- a/expand.scm
+++ b/expand.scm
@@ -460,14 +460,20 @@
 (define ##sys#canonicalize-body
   (lambda (body #!optional (se (##sys#current-environment)) cs?)
     (define (comp s id)
-      (let ((f (lookup id se)))
-	(or (eq? s f)
-	    (case s
-	      ((define) (if f (eq? f define-definition) (eq? s id)))
-	      ((define-syntax) (if f (eq? f define-syntax-definition) (eq? s id)))
-	      ((define-values) (if f (eq? f define-values-definition) (eq? s id)))
-	      ((import) (if f (eq? f import-definition) (eq? s id)))
-	      (else (eq? s id))))))
+      (let ((f (or (lookup id se)
+                   (lookup id (##sys#macro-environment)))))
+        (or (eq? f id) (eq? s id))))
+    (define (comp-def def)
+      (lambda (id)
+        (let repeat ((id id))
+          (let ((f (or (lookup id se)
+                       (lookup id (##sys#macro-environment)))))
+            (or (eq? f def)
+                (and (symbol? f) (repeat f)))))))
+    (define comp-define (comp-def define-definition))
+    (define comp-define-syntax (comp-def define-syntax-definition))
+    (define comp-define-values (comp-def define-values-definition))
+    (define comp-import (comp-def import-definition))
     (define (fini vars vals mvars body)
       (if (and (null? vars) (null? mvars))
 	  ;; Macro-expand body, and restart when defines are found.
@@ -482,13 +488,13 @@
 		    (if (and (pair? x)
 			     (let ((d (car x)))
 			       (and (symbol? d)
-				    (or (comp 'define d)
-					(comp 'define-values d)
-					(comp 'define-syntax d)
-					(comp '##core#begin d)
-					(comp 'import d)))))
+				    (or (comp '##core#begin d)
+                                        (comp-define d)
+					(comp-define-values d)
+					(comp-define-syntax d)
+					(comp-import d)))))
 			;; Stupid hack to avoid expanding imports
-			(if (comp 'import (car x))
+			(if (comp-import (car x))
 			    (loop rest (cons x exps))
 			    (cons
 			     '##core#begin
@@ -547,7 +553,7 @@
 	       ((and (list? (car body))
 		     (>= 3 (length (car body))) 
 		     (symbol? (caar body))
-		     (comp 'define-syntax (caar body)))
+		     (comp-define-syntax (caar body)))
 		(let ((def (car body)))
 		  ;; This check is insufficient, if introduced by
 		  ;; different expansions, but better than nothing:
@@ -570,7 +576,7 @@
 	      (if (not (symbol? head))
 		  (fini vars vals mvars body)
 		  (cond
-		   ((comp 'define head)
+		   ((comp-define head)
 		     (##sys#check-syntax 'define x '(_ _ . #(_ 0)) #f se)
 		     (let loop2 ((x x))
 		       (let ((head (cadr x)))
@@ -597,10 +603,10 @@
 				      (cons (list (car head)) vars)
 				      (cons `(##core#lambda ,(cdr head) ,@(cddr x)) vals)
 				      (cons #f mvars)))))))
-		    ((comp 'define-syntax head)
+		    ((comp-define-syntax head)
 		     (##sys#check-syntax 'define-syntax x '(_ _ . #(_ 1)) se)
 		     (fini/syntax vars vals mvars body))
-		    ((comp 'define-values head)
+		    ((comp-define-values head)
 		     ;;XXX check for any of the variables being `define-values'
 		     (##sys#check-syntax 'define-values x '(_ lambda-list _) #f se)
 		     (loop rest (cons (cadr x) vars) (cons (caddr x) vals) (cons #t mvars)))
diff --git a/tests/module-tests-2.scm b/tests/module-tests-2.scm
index 2fc33f23..e9b8e8a3 100644
--- a/tests/module-tests-2.scm
+++ b/tests/module-tests-2.scm
@@ -86,18 +86,20 @@
   (import m1)
   ((lambda () (f1)))) ; should use new lambda (but should be folded by compiler)
 
-
-;;; local define should work even with redefined define
-
+;; #1132 - internal definitions honor redefinitions of defining forms
 (module m3 ()
   (import (rename scheme (define s:define)))
   (import (only (chicken base) assert))
   (define-syntax define
     (syntax-rules ()
-      ((_) (display 'oink))))
+      ((_) (display 'oink))
+      ((_ var value) (s:define var (+ value 1)))))
   (define)
+  ;; Internal definition uses new "define"
   (let ()
     (define a 1)
-    (assert (= a 1)))
-  (define)
-  (newline))
+    (assert (= a 2)))
+
+  ;; Toplevel definition also uses new "define"
+  (define b 5)
+  (assert (= b 6)))
Trap