Skip to content

Commit

Permalink
fix unescaping in global expressions
Browse files Browse the repository at this point in the history
This fixes some issues around macro hygiene in `global` expressions.
Apparently we always treat l-values in global expressions as being
escaped, but we still need to be careful to handle type annotations and
destructuring correctly.
  • Loading branch information
simeonschaub committed Nov 28, 2022
1 parent 60668c5 commit 329f477
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 8 deletions.
29 changes: 21 additions & 8 deletions src/macroexpand.scm
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,19 @@
(cadr e)
e))

(define (unescape-global-lhs e env m parent-scope inarg)
(cond ((not (pair? e)) e)
((eq? (car e) 'escape) (cadr e))
((memq (car e) '(parameters tuple))
(list* (car e) (map (lambda (e)
(unescape-global-lhs e env m parent-scope inarg))
(cdr e))))
((and (memq (car e) '(|::| kw)) (length= e 3))
`(:: ,(unescape-global-lhs (cadr e) env m parent-scope inarg)
,(resolve-expansion-vars-with-new-env (caddr e) env m parent-scope inarg)))
(else
(resolve-expansion-vars-with-new-env e env m parent-scope inarg))))

(define (typedef-expr-name e)
(cond ((atom? e) e)
((or (eq? (car e) 'curly) (eq? (car e) '<:)) (typedef-expr-name (cadr e)))
Expand Down Expand Up @@ -344,14 +357,14 @@
(m (cadr scope))
(parent-scope (cdr parent-scope)))
(resolve-expansion-vars-with-new-env (cadr e) env m parent-scope inarg))))
((global) (let ((arg (cadr e)))
(cond ((symbol? arg) e)
((assignment? arg)
`(global
(= ,(unescape (cadr arg))
,(resolve-expansion-vars-with-new-env (caddr arg) env m parent-scope inarg))))
(else
`(global ,(resolve-expansion-vars-with-new-env arg env m parent-scope inarg))))))
((global)
`(global
,@(map (lambda (arg)
(if (assignment? arg)
`(= ,(unescape-global-lhs (cadr arg) env m parent-scope inarg)
,(resolve-expansion-vars-with-new-env (caddr arg) env m parent-scope inarg))
(unescape-global-lhs arg env m parent-scope inarg)))
(cdr e))))
((using import export meta line inbounds boundscheck loopinfo inline noinline) (map unescape e))
((macrocall) e) ; invalid syntax anyways, so just act like it's quoted.
((symboliclabel) e)
Expand Down
23 changes: 23 additions & 0 deletions test/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3429,3 +3429,26 @@ end
elseif false || (()->true)()
42
end)) == 42

macro _macroexpand(x, m=__module__)
:($__source__; macroexpand($m, Expr(:var"hygienic-scope", $(esc(Expr(:quote, x))), $m)))
end

@testset "unescaping in :global expressions" begin
m = @__MODULE__
@test @_macroexpand(global x::T) == :(global x::$(GlobalRef(m, :T)))
@test @_macroexpand(global (x, $(esc(:y)))) == :(global (x, y))
@test @_macroexpand(global (x::S, $(esc(:y))::$(esc(:T)))) ==
:(global (x::$(GlobalRef(m, :S)), y::T))
@test @_macroexpand(global (; x, $(esc(:y)))) == :(global (; x, y))
@test @_macroexpand(global (; x::S, $(esc(:y))::$(esc(:T)))) ==
:(global (; x::$(GlobalRef(m, :S)), y::T))

@test @_macroexpand(global x::T = a) == :(global x::$(GlobalRef(m, :T)) = $(GlobalRef(m, :a)))
@test @_macroexpand(global (x, $(esc(:y))) = a) == :(global (x, y) = $(GlobalRef(m, :a)))
@test @_macroexpand(global (x::S, $(esc(:y))::$(esc(:T))) = a) ==
:(global (x::$(GlobalRef(m, :S)), y::T) = $(GlobalRef(m, :a)))
@test @_macroexpand(global (; x, $(esc(:y))) = a) == :(global (; x, y) = $(GlobalRef(m, :a)))
@test @_macroexpand(global (; x::S, $(esc(:y))::$(esc(:T))) = a) ==
:(global (; x::$(GlobalRef(m, :S)), y::T) = $(GlobalRef(m, :a)))
end

0 comments on commit 329f477

Please sign in to comment.