Skip to content

Commit c70b2c4

Browse files
committed
fix macro goto/label hygiene
same basic idea as PR #22985 did for macro variables also fixes #23135
1 parent 104dce7 commit c70b2c4

File tree

4 files changed

+71
-44
lines changed

4 files changed

+71
-44
lines changed

base/essentials.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,11 @@ macro inbounds(blk)
300300
end
301301

302302
macro label(name::Symbol)
303-
Expr(:symboliclabel, name)
303+
return esc(Expr(:symboliclabel, name))
304304
end
305305

306306
macro goto(name::Symbol)
307-
Expr(:symbolicgoto, name)
307+
return esc(Expr(:symbolicgoto, name))
308308
end
309309

310310
# SimpleVector

src/julia-syntax.scm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2538,7 +2538,7 @@
25382538
(define (resolve-scopes- e env outerglobals implicitglobals lam renames newlam)
25392539
(cond ((symbol? e) (let ((r (assq e renames)))
25402540
(if r (cdr r) e))) ;; return the renaming for e, or e
2541-
((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel global))) e)
2541+
((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel global symbolicgoto symboliclabel))) e)
25422542
((eq? (car e) 'local) '(null)) ;; remove local decls
25432543
((eq? (car e) 'local-def) '(null)) ;; remove local decls
25442544
((eq? (car e) 'implicit-global) '(null)) ;; remove implicit-global decls

src/macroexpand.scm

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -453,39 +453,36 @@
453453
;; and wrap globals in (globalref module var) for macro's home module
454454
(resolve-expansion-vars-with-new-env e '() m '() #f #t))
455455

456-
(define (find-symbolic-labels e)
457-
(let ((defs (table))
458-
(refs (table)))
459-
(find-symbolic-label-defs e defs)
460-
(find-symbolic-label-refs e refs)
461-
(table.foldl
462-
(lambda (label v labels)
463-
(if (has? refs label)
464-
(cons label labels)
465-
labels))
466-
'() defs)))
467-
468-
(define (rename-symbolic-labels- e relabel)
456+
(define (rename-symbolic-labels- e relabels parent-scope)
469457
(cond
470458
((or (not (pair? e)) (quoted? e)) e)
471-
((eq? (car e) 'symbolicgoto)
472-
(let ((newlabel (assq (cadr e) relabel)))
473-
(if newlabel `(symbolicgoto ,(cdr newlabel)) e)))
474-
((eq? (car e) 'symboliclabel)
475-
(let ((newlabel (assq (cadr e) relabel)))
476-
(if newlabel `(symboliclabel ,(cdr newlabel)) e)))
477-
(else (map (lambda (x) (rename-symbolic-labels- x relabel)) e))))
459+
((eq? (car e) 'hygienic-scope)
460+
(let ((parent-scope (list relabels parent-scope))
461+
(body (cadr e))
462+
(m (caddr e)))
463+
`(hygienic-scope ,(rename-symbolic-labels- (cadr e) (table) parent-scope) ,m)))
464+
((and (eq? (car e) 'escape) (not (null? parent-scope)))
465+
`(escape ,(apply rename-symbolic-labels- (cadr e) parent-scope)))
466+
((or (eq? (car e) 'symbolicgoto) (eq? (car e) 'symboliclabel))
467+
(let* ((s (cadr e))
468+
(havelabel (if (or (null? parent-scope) (not (symbol? s))) s (get relabels s #f)))
469+
(newlabel (if havelabel havelabel (named-gensy s))))
470+
(if (not havelabel) (put! relabels s newlabel))
471+
`(,(car e) ,newlabel)))
472+
(else
473+
(cons (car e)
474+
(map (lambda (x) (rename-symbolic-labels- x relabels parent-scope))
475+
(cdr e))))))
478476

479477
(define (rename-symbolic-labels e)
480-
(let* ((labels (find-symbolic-labels e))
481-
(relabel (pair-with-gensyms labels)))
482-
(rename-symbolic-labels- e relabel)))
478+
(rename-symbolic-labels- e (table) '()))
483479

484480
;; macro expander entry point
485481

486482
(define (julia-expand-macros e (max-depth -1))
487483
(julia-expand-macroscopes
488-
(julia-expand-macros- '() e max-depth)))
484+
(rename-symbolic-labels
485+
(julia-expand-macros- '() e max-depth))))
489486

490487
(define (julia-expand-macros- m e max-depth)
491488
(cond ((= max-depth 0) e)
@@ -504,7 +501,7 @@
504501
(let ((form (car form)) ;; form is the expression returned from expand-macros
505502
(modu (cdr form))) ;; modu is the macro's def module
506503
`(hygienic-scope
507-
,(julia-expand-macros- (cons modu m) (rename-symbolic-labels form) (- max-depth 1))
504+
,(julia-expand-macros- (cons modu m) form (- max-depth 1))
508505
,modu))))
509506
((eq? (car e) 'module) e)
510507
((eq? (car e) 'escape)

test/goto.jl

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

33
# Basic goto tests
4+
expand(x) = Base.expand(@__MODULE__, x)
45

56
function goto_test1()
67
@goto a
@@ -11,8 +12,13 @@ end
1112
@test goto_test1()
1213

1314

14-
@test_throws ErrorException eval(
15-
quote
15+
@test eval(:(@label a)) === nothing
16+
17+
@test Expr(:error, "label \"a\" referenced but not defined") ==
18+
expand(:(@goto a))
19+
20+
@test Expr(:error, "label \"a\" defined multiple times") ==
21+
expand(quote
1622
function goto_test2()
1723
@goto a
1824
@label a
@@ -22,17 +28,16 @@ end
2228
end)
2329

2430

25-
@test_throws ErrorException eval(
26-
quote
31+
@test Expr(:error, "label \"a\" referenced but not defined") ==
32+
expand(quote
2733
function goto_test3()
2834
@goto a
2935
return
3036
end
3137
end)
3238

33-
34-
@test_throws ErrorException eval(
35-
quote
39+
@test Expr(:error, "misplaced label") ==
40+
expand(quote
3641
function goto_test4()
3742
@goto a
3843
try
@@ -43,23 +48,48 @@ end
4348
end)
4449

4550

46-
# test that labels in macros are reassigned
47-
macro goto_test5_macro()
48-
@label a
51+
# test that labels in macros are reassigned if unescaped
52+
macro goto_test5_macro1()
53+
return :(@label a)
54+
end
55+
macro goto_test5_macro2()
56+
return :(@goto a)
57+
end
58+
macro goto_test5_macro3()
59+
return esc(:(@label a))
4960
end
5061

51-
@test_throws ErrorException eval(
52-
quote
53-
function goto_test5()
62+
@test Expr(:error, "label \"a\" referenced but not defined") ==
63+
expand(quote
64+
function goto_test5_1()
5465
@goto a
55-
@goto_test5_macro
66+
@goto_test5_macro1
5667
return
5768
end
5869
end)
5970

71+
let e = expand(quote
72+
function goto_test5_2()
73+
@goto_test5_macro2
74+
@label a
75+
return
76+
end
77+
end)
78+
@test (e::Expr).head === :error
79+
@test ismatch(r"label \"#\d+#a\" referenced but not defined", e.args[1])
80+
end
81+
82+
function goto_test5_3()
83+
@goto a
84+
return false
85+
@goto_test5_macro3
86+
return true
87+
end
88+
@test goto_test5_3()
89+
6090

61-
@test_throws ErrorException eval(
62-
quote
91+
@test Expr(:error, "goto from a try/finally block is not permitted") ==
92+
expand(quote
6393
function goto_test6()
6494
try
6595
@goto a

0 commit comments

Comments
 (0)