> On Sun, Nov 12, 2023 at 01:45:06PM +0100, felix.winkelm...@bevuta.com wrote: > > See commit message. > > Nice to make some progress on this! > > However, I tested the example given in the ticket: > > (define begin -) > (begin 0 1) => 1 ;; expected: -1 > > This still evaluates to 1. >
Yes, this is admittedly all a bit ugly. Please find attached 2 patches: the first addresses the endless expansion loop caused by our recent change in ##sys#canonicalize-body. The second patch is a new version of the "override" patch, with some additional changes to address the example above and a followup problem that came up during testing. I post the two patches together, because the latter includes the change for the former. Please apply at least the former, if you still have doubts about the override changes. felix
From c30be664a4492db70d06edd17baffc3950ec6d45 Mon Sep 17 00:00:00 2001 From: felix <fe...@call-with-current-continuation.org> Date: Tue, 14 Nov 2023 13:14:46 +0100 Subject: [PATCH] Fix hygienic comparison for "begin" forms when expanding body --- expand.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expand.scm b/expand.scm index ec94086a..f18838b3 100644 --- a/expand.scm +++ b/expand.scm @@ -462,7 +462,7 @@ (define (comp s id) (let ((f (or (lookup id se) (lookup id (##sys#macro-environment))))) - (or (eq? f id) (eq? s id)))) + (or (eq? f s) (eq? s id)))) (define (comp-def def) (lambda (id) (let repeat ((id id)) -- 2.40.0
From f98af2c34a59d885738c2f1ce5c3977d8f2abd6c Mon Sep 17 00:00:00 2001 From: felix <fe...@call-with-current-continuation.org> Date: Tue, 14 Nov 2023 13:16:38 +0100 Subject: [PATCH] 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. --- core.scm | 21 ++++++++++++--------- eval.scm | 2 ++ expand.scm | 31 ++++++++++++++++++++----------- modules.scm | 36 +++++++++++++++++++++++++----------- tests/module-tests.scm | 13 +++++++++++++ tests/syntax-tests.scm | 24 ++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 31 deletions(-) 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))) -- 2.40.0