~ chicken-core (chicken-5) 3d035c6c6bc90cedca2e450b4f5f1f17aa19c229


commit 3d035c6c6bc90cedca2e450b4f5f1f17aa19c229
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Tue Nov 14 13:16:38 2023 +0100
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Wed Dec 20 15:36:31 2023 +0100

    Retain current identifier status as syntax or value binding
    
    Currently, toplevel value- and macro-bindings for an identifier are distinctly
    stored in separate places, resulting in the effect that a macro definition
    will shadow a value-binding (see also #1166).
    
    One way to address this would be to remove syntax-bindings when a toplevel
    identifier is "define"d and vice versa, but this will require a lot of
    searching and re-consing of (possibly large) environment a-lists.
    
    The approach chosen here is to store a global property on the symbol
    that names the identifier which specifies whether a value-binding
    should override any existing syntax binding (and the other way around).
    
    Some attempt is made to properly restore the "override" status when
    processing modules.
    
    Patch updated to address definition-binding lookup loop in bodies
    and ensure toplevel identifiers are correctly checked for the override
    property. Also clear override-status for all imports.
    
    Signed-off-by: Peter Bex <peter@more-magic.net>

diff --git a/core.scm b/core.scm
index a551204f..8f6b85bc 100644
--- a/core.scm
+++ b/core.scm
@@ -907,6 +907,7 @@
 					 `(##core#lambda ,(cdadr x) ,@(cddr x))
 					 (caddr x)))
 			       (name (lookup var)))
+                          (##sys#put/restore! name '##sys#override 'syntax)
 			  (##sys#register-syntax-export name (##sys#current-module) body)
 			  (##sys#extend-macro-environment
 			   name
@@ -924,6 +925,7 @@
 			(let* ((var (cadr x))
 			       (body (caddr x))
 			       (name (lookup var)))
+                          (##sys#put/restore! name '##sys#override 'syntax)
 			  (when body
 			    (set! compiler-syntax
 			      (alist-cons
@@ -1109,15 +1111,16 @@
 			  `(##core#lambda ,aliases ,body) ) )
 
 		       ((##core#ensure-toplevel-definition)
-			(unless tl?
-			  (let* ((var0 (cadr x))
-				 (var (lookup var0))
-				 (ln (get-line-number x)))
-			   (quit-compiling
-			    "~atoplevel definition of `~s' in non-toplevel context"
-			    (if ln (sprintf "(~a) - " ln) "")
-			    var)))
-			'(##core#undefined))
+                         (let* ((var0 (cadr x))
+                                (var (lookup var0)))
+                           (unless tl?
+                             (let ((ln (get-line-number x)))
+                               (quit-compiling
+                                 "~atoplevel definition of `~s' in non-toplevel context"
+                                (if ln (sprintf "(~a) - " ln) "")
+                                var)))
+                           (##sys#put/restore! var '##sys#override 'value)
+                           '(##core#undefined)))
 
 		       ((##core#set!)
 			(let* ((var0 (cadr x))
diff --git a/eval.scm b/eval.scm
index 68fba6ff..e760aad0 100644
--- a/eval.scm
+++ b/eval.scm
@@ -265,6 +265,7 @@
 			 ((##core#ensure-toplevel-definition)
 			  (unless tl?
 			    (##sys#error "toplevel definition in non-toplevel context for variable" (cadr x)))
+                          (##sys#put/restore! (cadr x) '##sys#override 'value)
 			  (compile
 			   '(##core#undefined) e #f tf cntr #f))
 
@@ -508,6 +509,7 @@
 				 (name (rename var)))
 			    (when (and static (not (assq var (##sys#current-environment))))
 			      (##sys#error 'eval "environment is not mutable" evalenv var))
+                            (##sys#put/restore! name '##sys#override 'syntax)
 			    (##sys#register-syntax-export 
 			     name (##sys#current-module)
 			     body)	; not really necessary, it only shouldn't be #f
diff --git a/expand.scm b/expand.scm
index ec94086a..f100ce89 100644
--- a/expand.scm
+++ b/expand.scm
@@ -56,6 +56,7 @@
 (include "mini-srfi-1.scm")
 
 (define-syntax d (syntax-rules () ((_ . _) (void))))
+;(define-syntax d (syntax-rules () ((_ args ...) (print args ...))))
 
 ;; Macro to avoid "unused variable map-se" when "d" is disabled
 (define-syntax map-se
@@ -261,10 +262,13 @@
 	    (let ((head2 (or (lookup head dse) head)))
 	      (unless (pair? head2)
 		(set! head2 (or (lookup head2 (##sys#macro-environment)) head2)) )
-	      (cond [(eq? head2 '##core#let)
+	      (cond ((and (pair? head2)
+                          (eq? (##sys#get head '##sys#override) 'value))
+                     (values exp #f))
+                    ((eq? head2 '##core#let)
 		     (##sys#check-syntax 'let body '#(_ 2) #f dse)
-		     (let ([bindings (car body)])
-		       (cond [(symbol? bindings) ; expand named let
+		     (let ((bindings (car body)))
+		       (cond ((symbol? bindings) ; expand named let
 			      (##sys#check-syntax 'let body '(_ #((variable _) 0) . #(_ 1)) #f dse)
 			      (let ([bs (cadr body)])
 				(values
@@ -275,8 +279,8 @@
 				       ,(map (lambda (b) (car b)) bs) ,@(cddr body))])
 				    ,bindings)
 				   ,@(##sys#map cadr bs) )
-				 #t) ) ]
-			     [else (values exp #f)] ) ) ]
+				 #t) ) )
+			     (else (values exp #f)) ) ) )
 		    ((and cs? (symbol? head2) (getp head2 '##compiler#compiler-syntax)) =>
 		     (lambda (cs)
 		       (let ((result (call-handler head (car cs) exp (cdr cs) #t)))
@@ -285,7 +289,7 @@
 				(when ##sys#compiler-syntax-hook
 				  (##sys#compiler-syntax-hook head2 result))
 				(loop result))))))
-		    [else (expand head exp head2)] ) )
+		    (else (expand head exp head2)) ) )
 	    (values exp #f) ) )
       (values exp #f) ) ) )
 
@@ -462,16 +466,20 @@
     (define (comp s id)
       (let ((f (or (lookup id se)
                    (lookup id (##sys#macro-environment)))))
-        (or (eq? f id) (eq? s id))))
+        (and (or (not (symbol? f))
+                 (not (eq? (##sys#get id '##sys#override) 'value)))
+             (or (eq? f s) (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)
-                     (not (eq? f id))
-                     (repeat f)))))))
+            (and (or (not (symbol? f))
+                     (not (eq? (##sys#get id '##sys#override) 'value)))
+                 (or (eq? f def)
+                     (and (symbol? f) 
+                          (not (eq? f id))
+                          (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))
@@ -569,6 +577,7 @@
       ;; Each #t in "mvars" indicates an MV-capable "var".  Non-MV
       ;; vars (#f in mvars) are 1-element lambda-lists for simplicity.
       (let loop ((body body) (vars '()) (vals '()) (mvars '()))
+        (d "BODY: " body)
 	(if (not (pair? body))
 	    (fini vars vals mvars body)
 	    (let* ((x (car body))
diff --git a/modules.scm b/modules.scm
index c6b77acd..ac4f0dc2 100644
--- a/modules.scm
+++ b/modules.scm
@@ -534,18 +534,27 @@
 
 	  (##sys#error (get-output-string out))))
 
+      (define (filter-sdlist mod)
+        (let loop ((syms (module-defined-syntax-list mod)))
+          (cond ((null? syms) '())
+                ((eq? (##sys#get (caar syms) '##sys#override) 'value)
+                 (loop (cdr syms)))
+                (else (cons (assq (caar syms) (##sys#macro-environment))
+                            (loop (cdr syms)))))))
+
       (let* ((explist (module-export-list mod))
 	     (name (module-name mod))
 	     (dlist (module-defined-list mod))
 	     (elist (module-exist-list mod))
 	     (missing #f)
-	     (sdlist (map (lambda (sym) (assq (car sym) (##sys#macro-environment)))
-			  (module-defined-syntax-list mod)))
+	     (sdlist (filter-sdlist mod))
 	     (sexports
 	      (if (eq? #t explist)
 		  (merge-se (module-sexports mod) sdlist)
 		  (let loop ((me (##sys#macro-environment)))
 		    (cond ((null? me) '())
+                          ((eq? (##sys#get (caar me) '##sys#override) 'value)
+                           (loop (cdr me)))
 			  ((find-export (caar me) mod #f)
 			   (cons (car me) (loop (cdr me))))
 			  (else (loop (cdr me)))))))
@@ -555,7 +564,9 @@
 		    '()
 		    (let* ((h (car xl))
 			   (id (if (symbol? h) h (car h))))
-		      (cond ((assq id sexports) (loop (cdr xl)))
+		      (cond ((eq? (##sys#get id '##sys#override) 'syntax)
+                              (loop (cdr xl)))
+                            ((assq id sexports) (loop (cdr xl)))
                             (else 
                               (cons 
                                 (cons 
@@ -810,17 +821,20 @@
     (dd `(S: ,(if cm (module-name cm) '<toplevel>) ,(map-se vss)))
     (for-each
      (lambda (imp)
-       (and-let* ((id (car imp))
-                  (a (assq id (import-env)))
-                  (aid (cdr imp))
-                  ((not (eq? aid (cdr a)))))
-         (##sys#notice "re-importing already imported identifier" id)))
+       (let ((id (car imp)))
+         (##sys#put! id '##sys#override #f)
+         (and-let* ((a (assq id (import-env)))
+                    (aid (cdr imp))
+                    ((not (eq? aid (cdr a)))))
+              (##sys#notice "re-importing already imported identifier" id))))
      vsv)
     (for-each
      (lambda (imp)
-       (and-let* ((a (assq (car imp) (macro-env)))
-                  ((not (eq? (cdr imp) (cdr a)))))
-         (##sys#notice "re-importing already imported syntax" (car imp))))
+       (let ((id (car imp)))
+         (##sys#put! id '##sys#override #f)
+         (and-let* ((a (assq (car imp) (macro-env)))
+                    ((not (eq? (cdr imp) (cdr a)))))
+              (##sys#notice "re-importing already imported syntax" (car imp)))))
      vss)
     (when reexp?
       (unless cm
diff --git a/tests/module-tests.scm b/tests/module-tests.scm
index 4d15c88f..2105b081 100644
--- a/tests/module-tests.scm
+++ b/tests/module-tests.scm
@@ -402,6 +402,19 @@
   (assert (equal? (alias) '(123)))
   (assert (equal? bar 99)))
 
+;; corner case, found by DeeEff, actually not really a good idea,
+;; but the expander looped here endlessly
+(module m36 (xcons)
+  (import scheme)
+  (define (xcons x y) (cons y x)))
+  
+(module m37 ()
+  (import (rename m36
+                  (xcons m36#xcons)))
+  (import scheme (chicken base))
+  (define (xcons x y) (m36#xcons 'X x))
+  (assert (equal? '(1 . X) (xcons 1 2))))
+
 (test-end "modules")
 
 (test-exit)
diff --git a/tests/syntax-tests.scm b/tests/syntax-tests.scm
index a788469a..336707eb 100644
--- a/tests/syntax-tests.scm
+++ b/tests/syntax-tests.scm
@@ -1334,3 +1334,27 @@ other-eval
 ;; changes, and any other imports are simply aliases.
 ;;(t 'old (reimported-foo reimported-foo))
 (t 'new (reimported-foo reimported-foo))
+
+;; #1166
+(module val-vs-syn1 *
+  (import scheme)
+  (define-syntax bar (syntax-rules () ((_) 'bar)))
+  (define (bar) 99)
+)
+
+(module test-val-vs-syn1 ()
+   (import scheme (chicken base) val-vs-syn1)
+   (assert (eq? 99 (bar))))
+
+(module val-vs-syn2 *
+  (import scheme)
+  (define (bar) 99)
+  (define-syntax bar (syntax-rules () ((_) 'bar)))
+)
+
+(module test-val-vs-syn2 ()
+   (import scheme (chicken base) val-vs-syn2)
+   (assert (eq? 'bar (bar))))
+
+(define begin -)
+(assert (eq? -1 (begin 0 1)))
Trap