Skip to content

Commit

Permalink
fix unescaping in global expressions (#47719)
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.

(cherry picked from commit cc25a13)
  • Loading branch information
simeonschaub authored and KristofferC committed Dec 21, 2022
1 parent 3014999 commit 6247a89
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 9 deletions.
31 changes: 22 additions & 9 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))
(list (car e) (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,15 +357,15 @@
(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))))))
((using import export meta line inbounds boundscheck loopinfo) (map unescape e))
((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)
((symbolicgoto) e)
Expand Down
23 changes: 23 additions & 0 deletions test/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2802,3 +2802,26 @@ end
let ex = :(const $(esc(:x)) = 1; (::typeof(2))() = $(esc(:x)))
@test macroexpand(Main, Expr(:var"hygienic-scope", ex, Main)).args[3].args[1] == :((::$(GlobalRef(Main, :typeof))(2))())
end

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 6247a89

Please sign in to comment.