~ chicken-core (chicken-5) ae578568d3c6ca9dcb775217a90d77ffae1d7725
commit ae578568d3c6ca9dcb775217a90d77ffae1d7725 Author: Peter Bex <peter@more-magic.net> AuthorDate: Sun Apr 9 15:20:51 2017 +0200 Commit: Evan Hanson <evhan@foldling.org> CommitDate: Mon Apr 10 07:11:16 2017 +1200 Reject define-values in expression contexts. This allows us to detect when define-values is being used in an expression context, without it inadvertently defining toplevel variables. To make this work, ##core#define-toplevel is now removed in favour of a new ##core#ensure-toplevel-definition. All defining forms will expand to a call to this new core form plus a set!. The tests for define in expression context were incorrect too, the expression would result in an error (as expected) even if define didn't error, because + would receive a void value. Signed-off-by: Evan Hanson <evhan@foldling.org> diff --git a/chicken-syntax.scm b/chicken-syntax.scm index b97e733d..4441055c 100644 --- a/chicken-syntax.scm +++ b/chicken-syntax.scm @@ -366,14 +366,17 @@ (##sys#er-transformer (lambda (form r c) (##sys#check-syntax 'define-values form '(_ lambda-list _)) - (##sys#decompose-lambda-list - (cadr form) - (lambda (vars argc rest) - (for-each (lambda (nm) - (let ((name (##sys#get nm '##core#macro-alias nm))) - (##sys#register-export name (##sys#current-module)))) - vars))) - `(,(r 'set!-values) ,@(cdr form)))))) + `(##core#begin + ,@(##sys#decompose-lambda-list + (cadr form) + (lambda (vars argc rest) + (for-each (lambda (nm) + (let ((name (##sys#get nm '##core#macro-alias nm))) + (##sys#register-export name (##sys#current-module)))) + vars) + (map (lambda (nm) `(##core#ensure-toplevel-definition ,nm)) + vars))) + ,(##sys#expand-multiple-values-assignment (cadr form) (caddr form))))))) (##sys#extend-macro-environment 'let-values '() diff --git a/core.scm b/core.scm index 8fc8fc2a..d7b3aae0 100644 --- a/core.scm +++ b/core.scm @@ -110,7 +110,7 @@ ; (##core#lambda <variable> <body>) ; (##core#lambda ({<variable>}+ [. <variable>]) <body>) ; (##core#set! <variable> <exp>) -; (##core#define-toplevel <variable> <exp>) +; (##core#ensure-toplevel-definition <variable>) ; (##core#begin <exp> ...) ; (##core#include <string> <string> | #f) ; (##core#loop-lambda <llist> <body>) @@ -1043,65 +1043,71 @@ (set-real-names! aliases vars) `(##core#lambda ,aliases ,body) ) ) - ((##core#set! ##core#define-toplevel) - (let* ([var0 (cadr x)] - [var (lookup var0 se)] - [ln (get-line x)] - [val (caddr x)] ) - (when (and (eq? name '##core#define-toplevel) (not tl?)) - (quit-compiling - "~atoplevel definition of `~s' in non-toplevel context" - (if ln (sprintf "(~a) - " ln) "") - var)) - (when (memq var unlikely-variables) - (warning - (sprintf "assignment to variable `~s' possibly unintended" - var))) - (cond ((assq var foreign-variables) - => (lambda (fv) - (let ([type (second fv)] - [tmp (gensym)] ) - (walk - `(let ([,tmp ,(foreign-type-convert-argument val type)]) - (##core#inline_update - (,(third fv) ,type) - ,(foreign-type-check tmp type) ) ) - e se #f #f h ln #f)))) - ((assq var location-pointer-map) - => (lambda (a) - (let* ([type (third a)] - [tmp (gensym)] ) - (walk - `(let ([,tmp ,(foreign-type-convert-argument val type)]) - (##core#inline_loc_update - (,type) - ,(second a) - ,(foreign-type-check tmp type) ) ) - e se #f #f h ln #f)))) - (else - (unless (memq var e) ; global? - (set! var (or (##sys#get var '##core#primitive) - (##sys#alias-global-hook var #t dest))) - (when safe-globals-flag - (mark-variable var '##compiler#always-bound-to-procedure) - (mark-variable var '##compiler#always-bound)) - (when emit-debug-info - (set! val - `(let ((,var ,val)) - (##core#debug-event "C_DEBUG_GLOBAL_ASSIGN" ',var) - ,var)))) - (cond ((##sys#macro? var) - (warning - (sprintf "assigned global variable `~S' is syntax ~A" - var - (if ln (sprintf "(~a)" ln) "") )) - (when undefine-shadowed-macros (##sys#undefine-macro! var) ) ) - ((and ##sys#notices-enabled - (assq var (##sys#current-environment))) - (##sys#notice "assignment to imported value binding" var))) - (when (keyword? var) - (warning (sprintf "assignment to keyword `~S'" var) )) - `(set! ,var ,(walk val e se var0 (memq var e) h ln #f)))))) + ((##core#ensure-toplevel-definition) + (unless tl? + (let* ((var0 (cadr x)) + (var (lookup var0 se)) + (ln (get-line x))) + (quit-compiling + "~atoplevel definition of `~s' in non-toplevel context" + (if ln (sprintf "(~a) - " ln) "") + var))) + '(##core#undefined)) + + ((##core#set!) + (let* ((var0 (cadr x)) + (var (lookup var0 se)) + (ln (get-line x)) + (val (caddr x))) + (when (memq var unlikely-variables) + (warning + (sprintf "assignment to variable `~s' possibly unintended" + var))) + (cond ((assq var foreign-variables) + => (lambda (fv) + (let ((type (second fv)) + (tmp (gensym))) + (walk + `(let ((,tmp ,(foreign-type-convert-argument val type))) + (##core#inline_update + (,(third fv) ,type) + ,(foreign-type-check tmp type))) + e se #f #f h ln #f)))) + ((assq var location-pointer-map) + => (lambda (a) + (let* ((type (third a)) + (tmp (gensym))) + (walk + `(let ((,tmp ,(foreign-type-convert-argument val type))) + (##core#inline_loc_update + (,type) + ,(second a) + ,(foreign-type-check tmp type))) + e se #f #f h ln #f)))) + (else + (unless (memq var e) ; global? + (set! var (or (##sys#get var '##core#primitive) + (##sys#alias-global-hook var #t dest))) + (when safe-globals-flag + (mark-variable var '##compiler#always-bound-to-procedure) + (mark-variable var '##compiler#always-bound)) + (when emit-debug-info + (set! val + `(let ((,var ,val)) + (##core#debug-event "C_DEBUG_GLOBAL_ASSIGN" ',var) + ,var)))) + (cond ((##sys#macro? var) + (warning + (sprintf "assigned global variable `~S' is syntax ~A" + var + (if ln (sprintf "(~a)" ln) ""))) + (when undefine-shadowed-macros (##sys#undefine-macro! var))) + ((and ##sys#notices-enabled + (assq var (##sys#current-environment))) + (##sys#notice "assignment to imported value binding" var))) + (when (keyword? var) + (warning (sprintf "assignment to keyword `~S'" var))) + `(set! ,var ,(walk val e se var0 (memq var e) h ln #f)))))) ((##core#debug-event) `(##core#debug-event diff --git a/eval.scm b/eval.scm index 72977a8e..89b1f82f 100644 --- a/eval.scm +++ b/eval.scm @@ -371,13 +371,17 @@ [x3 (compile `(##core#begin ,@(##sys#slot (##sys#slot body 1) 1)) e #f tf cntr se tl?)] ) (lambda (v) (##core#app x1 v) (##core#app x2 v) (##core#app x3 v)) ) ) ) ) ] - [(##core#set! ##core#define-toplevel) + ((##core#ensure-toplevel-definition) + (unless tl? + (##sys#error "toplevel definition in non-toplevel context for variable" (cadr x))) + (compile + '(##core#undefined) e #f tf cntr se #f)) + + [(##core#set!) (let ((var (cadr x))) - (when (and (eq? head '##core#define-toplevel) (not tl?)) - (##sys#error "toplevel definition in non-toplevel context for variable" var)) (receive (i j) (lookup var e se) (let ((val (compile (caddr x) e var tf cntr se #f))) - (cond [(not i) + (cond ((not i) (when ##sys#notices-enabled (and-let* ((a (assq var (##sys#current-environment))) ((symbol? (cdr a)))) @@ -392,12 +396,12 @@ (##sys#error 'eval "environment is not mutable" evalenv var)) ;XXX var? (lambda (v) (##sys#persist-symbol var) - (##sys#setslot var 0 (##core#app val v))) ) ) ] - [(zero? i) (lambda (v) (##sys#setslot (##sys#slot v 0) j (##core#app val v)))] - [else + (##sys#setslot var 0 (##core#app val v)))))) + ((zero? i) (lambda (v) (##sys#setslot (##sys#slot v 0) j (##core#app val v)))) + (else (lambda (v) (##sys#setslot - (##core#inline "C_u_i_list_ref" v i) j (##core#app val v)) ) ] ) ) ) ) ] + (##core#inline "C_u_i_list_ref" v i) j (##core#app val v))))))))] [(##core#let) (let* ([bindings (cadr x)] diff --git a/expand.scm b/expand.scm index d1d8ee34..02e69de1 100644 --- a/expand.scm +++ b/expand.scm @@ -1076,9 +1076,11 @@ (##sys#register-export name (##sys#current-module))) (when (c (r 'define) head) (chicken.expand#defjam-error x)) - `(##core#define-toplevel - ,head - ,(if (pair? body) (car body) '(##core#undefined))) ) + `(##core#begin + (##core#ensure-toplevel-definition ,head) + (##core#set! + ,head + ,(if (pair? body) (car body) '(##core#undefined))))) ((pair? (car head)) (##sys#check-syntax 'define form '(_ (_ . lambda-list) . #(_ 1))) (loop (chicken.expand#expand-curried-define head body '()))) ;XXX '() should be se diff --git a/tests/syntax-tests.scm b/tests/syntax-tests.scm index 6cbb7511..49f9d641 100644 --- a/tests/syntax-tests.scm +++ b/tests/syntax-tests.scm @@ -785,7 +785,8 @@ ;;; Definitions in expression contexts are rejected (#1309) -(f (eval '(+ 1 2 (define x 3) 4))) +(f (eval '(+ 1 2 (begin (define x 3) x) 4))) +(f (eval '(+ 1 2 (begin (define-values (x y) (values 3 4)) x) 4))) (f (eval '(display (define x 1)))) ;; Some tests for nested but valid definition expressions: (t 2 (eval '(begin (define x 1) 2)))Trap