Skip to content

Commit c3eedce

Browse files
committed
explicitly track unbound variables during lowering
rather than renaming conflicting locals, it is easier to explicitly track the free symbols that may be global or sparams by marking that they came from the outer scope
1 parent 7b5b3f3 commit c3eedce

File tree

3 files changed

+79
-33
lines changed

3 files changed

+79
-33
lines changed

src/ast.scm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
((top) (deparse (cadr e)))
5454
((core) (string "Core." (deparse (cadr e))))
5555
((globalref) (string (deparse (cadr e)) "." (deparse (caddr e))))
56+
((outerref) (string (deparse (cadr e))))
5657
((:)
5758
(string (deparse (cadr e)) ': (deparse (caddr e))
5859
(if (length> e 3)
@@ -105,7 +106,7 @@
105106

106107
;; predicates and accessors
107108

108-
(define (quoted? e) (memq (car e) '(quote top core globalref line break inert)))
109+
(define (quoted? e) (memq (car e) '(quote top core globalref outerref line break inert meta)))
109110

110111
(define (lam:args x) (cadr x))
111112
(define (lam:vars x) (llist-vars (lam:args x)))

src/julia-syntax.scm

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,12 @@
225225
(else
226226
(error "malformed type parameter list"))))
227227

228-
(define (method-expr-name m) (cadr m))
228+
(define (method-expr-name m)
229+
(let ((name (cadr m)))
230+
(cond ((not (pair? name)) name)
231+
((eq? (car name) 'outerref) (cadr name))
232+
;((eq? (car name) 'globalref) (caddr name))
233+
(else name))))
229234

230235
;; extract static parameter names from a (method ...) expression
231236
(define (method-expr-static-parameters m)
@@ -243,6 +248,7 @@
243248
(define (sym-ref? e)
244249
(or (symbol? e)
245250
(and (length= e 3) (eq? (car e) 'globalref))
251+
(and (length= e 2) (eq? (car e) 'outerref))
246252
(and (length= e 3) (eq? (car e) '|.|)
247253
(or (atom? (cadr e)) (sym-ref? (cadr e)))
248254
(pair? (caddr e)) (memq (car (caddr e)) '(quote inert))
@@ -292,6 +298,15 @@
292298
(map (lambda (x) (replace-vars x renames))
293299
(cdr e))))))
294300

301+
(define (replace-outer-vars e renames)
302+
(cond ((and (pair? e) (eq? (car e) 'outerref)) (lookup (cadr e) renames e))
303+
((or (not (pair? e)) (quoted? e)) e)
304+
((memq (car e) '(-> function scope-block)) e)
305+
(else
306+
(cons (car e)
307+
(map (lambda (x) (replace-outer-vars x renames))
308+
(cdr e))))))
309+
295310
;; construct the (method ...) expression for one primitive method definition,
296311
;; assuming optional and keyword args are already handled
297312
(define (method-def-expr- name sparams argl body isstaged (rett 'Any))
@@ -675,8 +690,8 @@
675690
(if (length> params (length type-params))
676691
(error "too few type parameters specified in \"new{...}\""))
677692
(let ((Texpr (if (null? type-params)
678-
`(globalref ,(current-julia-module) ,Tname)
679-
`(curly (globalref ,(current-julia-module) ,Tname)
693+
`(outerref ,Tname)
694+
`(curly (outerref ,Tname)
680695
,@type-params))))
681696
(cond ((length> args (length field-names))
682697
`(call (top error) "new: too many arguments"))
@@ -1516,7 +1531,7 @@
15161531
,(loop (cdr tail)))))))))))
15171532

15181533
(define (expand-forms e)
1519-
(if (or (atom? e) (memq (car e) '(quote inert top core globalref line module toplevel ssavalue null meta)))
1534+
(if (or (atom? e) (memq (car e) '(quote inert top core globalref outerref line module toplevel ssavalue null meta)))
15201535
e
15211536
(let ((ex (get expand-table (car e) #f)))
15221537
(if ex
@@ -2306,6 +2321,8 @@
23062321
((symbol? e) (if (not (memq e bound)) (put! tab e #t)) tab)
23072322
((or (not (pair? e)) (quoted? e)) tab)
23082323
((memq (car e) '(lambda scope-block module toplevel)) tab)
2324+
((eq? (car e) 'break-block) (unbound-vars (caddr e) bound tab))
2325+
((eq? (car e) 'with-static-parameters) (unbound-vars (cadr e) bound tab))
23092326
(else (for-each (lambda (x) (unbound-vars x bound tab))
23102327
(cdr e))
23112328
tab)))
@@ -2319,9 +2336,10 @@
23192336
(define (resolve-scopes- e env implicitglobals lam renames newlam)
23202337
(cond ((symbol? e) (let ((r (assq e renames)))
23212338
(if r (cdr r) e))) ;; return the renaming for e, or e
2322-
((or (not (pair? e)) (quoted? e) (eq? (car e) 'toplevel)) e)
2339+
((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel global))) e)
23232340
((eq? (car e) 'local) '(null)) ;; remove local decls
23242341
((eq? (car e) 'local-def) '(null)) ;; remove local decls
2342+
((eq? (car e) 'implicit-global) '(null)) ;; remove implicit-global decls
23252343
((eq? (car e) 'lambda)
23262344
(let* ((lv (lam:vars e))
23272345
(env (append lv env))
@@ -2358,32 +2376,33 @@
23582376
'()
23592377
(filter (lambda (v) (or (memq v env)
23602378
(memq v other-locals)
2361-
(memq v implicitglobals)
23622379
(memq v (caddr lam))))
23632380
vars))))
23642381
(need-rename (need-rename? vars))
23652382
(need-rename-def (need-rename? vars-def))
23662383
;; new gensym names for conflicting variables
23672384
(renamed (map named-gensy need-rename))
23682385
(renamed-def (map named-gensy need-rename-def))
2386+
(new-env (append all-vars glob env)) ;; all variables declared in or outside blok
2387+
(new-iglo-table ;; initial list of implicit globals from outside blok which aren't part of the local vars
2388+
(let ((tab (table)))
2389+
(for-each (lambda (v) (if (not (memq v all-vars)) (put! tab v #t))) iglo)
2390+
(for-each (lambda (v) (if (not (memq v all-vars)) (put! tab v #t))) implicitglobals)
2391+
tab))
2392+
(new-iglo (table.keys ;; compute list of all globals used implicitly in blok
2393+
(unbound-vars blok
2394+
new-env ;; list of everything else
2395+
new-iglo-table)))
23692396
;; combine the list of new renamings with the inherited list
23702397
(new-renames (append (map cons need-rename renamed) ;; map from definition name -> gensym name
23712398
(map cons need-rename-def renamed-def)
2399+
(map (lambda (g) (cons g `(outerref ,g))) new-iglo)
23722400
(filter (lambda (ren) ;; old renames list, with anything in vars removed
23732401
(not (or (memq (car ren) all-vars)
23742402
(memq (car ren) iglo)
2403+
(memq (car ren) implicitglobals)
23752404
(memq (car ren) glob))))
23762405
renames)))
2377-
(new-env (append all-vars glob env)) ;; all variables declared in or outside blok
2378-
(new-iglo-table ;; initial list of implicit globals from outside blok
2379-
(let ((tab (table)))
2380-
(for-each (lambda (v) (put! tab v #t)) iglo)
2381-
(for-each (lambda (v) (put! tab v #t)) implicitglobals)
2382-
tab))
2383-
(new-iglo (table.keys ;; compute list of all globals used implicitly in blok
2384-
(unbound-vars blok
2385-
new-env ;; list of everything else
2386-
new-iglo-table)))
23872406
(body (resolve-scopes- blok new-env new-iglo lam new-renames #f))
23882407
(real-new-vars (append (diff vars need-rename) renamed))
23892408
(real-new-vars-def (append (diff vars-def need-rename-def) renamed-def)))
@@ -2402,6 +2421,13 @@
24022421
(map (lambda (v) `(local-def ,v)) real-new-vars-def)))))
24032422
((eq? (car e) 'module)
24042423
(error "module expression not at top level"))
2424+
((eq? (car e) 'break-block)
2425+
`(break-block ,(cadr e) ;; ignore type symbol of break-block expression
2426+
,(resolve-scopes- (caddr e) env implicitglobals lam renames #f))) ;; body of break-block expression
2427+
((eq? (car e) 'with-static-parameters)
2428+
`(with-static-parameters ;; ignore list of sparams in break-block expression
2429+
,(resolve-scopes- (cadr e) env implicitglobals lam renames #f)
2430+
,@(cddr e))) ;; body of break-block expression
24052431
(else
24062432
(cons (car e)
24072433
(map (lambda (x)
@@ -2420,6 +2446,9 @@
24202446
(define (free-vars- e tab)
24212447
(cond ((or (eq? e 'true) (eq? e 'false) (eq? e UNUSED)) tab)
24222448
((symbol? e) (put! tab e #t))
2449+
((and (pair? e) (eq? (car e) 'outerref)) (put! tab (cadr e) #t))
2450+
((and (pair? e) (eq? (car e) 'break-block)) (free-vars- (caddr e) tab))
2451+
((and (pair? e) (eq? (car e) 'with-static-parameters)) (free-vars- (cadr e) tab))
24232452
((or (atom? e) (quoted? e)) tab)
24242453
((eq? (car e) 'lambda)
24252454
(let ((bound (lambda-all-vars e)))
@@ -2768,7 +2797,7 @@ f(x) = yt(x)
27682797
(or (atom? e)
27692798
(memq (car e) '(quote top core line inert local local-def unnecessary
27702799
meta inbounds boundscheck simdloop
2771-
implicit-global global globalref
2800+
implicit-global global globalref outerref
27722801
const = null method call))))
27732802
(lam:body lam))))
27742803
(unused (map cadr (filter (lambda (x) (memq (car x) '(method =)))
@@ -2843,7 +2872,7 @@ f(x) = yt(x)
28432872
((atom? e) e)
28442873
(else
28452874
(case (car e)
2846-
((quote top core globalref line break inert module toplevel null meta) e)
2875+
((quote top core globalref outerref line break inert module toplevel null meta) e)
28472876
((=)
28482877
(let ((var (cadr e))
28492878
(rhs (cl-convert (caddr e) fname lam namemap toplevel interp)))
@@ -2948,12 +2977,15 @@ f(x) = yt(x)
29482977
(capt-vars (diff all-capt-vars capt-sp)) ; remove capt-sp from capt-vars
29492978
(find-locals-in-method-sig (lambda (methdef)
29502979
(expr-find-all
2951-
(lambda (s) (and (not (eq? name s))
2952-
(not (memq s capt-sp))
2953-
(or ;(local? s) ; TODO: make this work for local variables too?
2954-
(memq s (lam:sp lam)))))
2980+
(lambda (e) (and (pair? e) (eq? (car e) 'outerref)
2981+
(let ((s (cadr e)))
2982+
(and (symbol? s)
2983+
(not (eq? name s))
2984+
(not (memq s capt-sp))
2985+
(or ;(local? s) ; TODO: make this work for local variables too?
2986+
(memq s (lam:sp lam)))))))
29552987
(caddr methdef)
2956-
identity)))
2988+
(lambda (e) (cadr e)))))
29572989
(sig-locals (simple-sort
29582990
(delete-duplicates ;; locals used in sig from all definitions
29592991
(apply append ;; will convert these into sparams for dispatch
@@ -2978,7 +3010,7 @@ f(x) = yt(x)
29783010
(let* ((iskw ;; TODO jb/functions need more robust version of this
29793011
(contains (lambda (x) (eq? x 'kwftype)) sig))
29803012
(renamemap (map cons closure-param-names closure-param-syms))
2981-
(arg-defs (replace-vars
3013+
(arg-defs (replace-outer-vars
29823014
(fix-function-arg-type sig type-name iskw namemap closure-param-syms)
29833015
renamemap)))
29843016
(append (map (lambda (gs tvar)
@@ -3001,7 +3033,7 @@ f(x) = yt(x)
30013033
v)))
30023034
capt-vars))
30033035
(P (append
3004-
closure-param-names
3036+
(map (lambda (n) `(outerref ,n)) closure-param-names)
30053037
(filter identity (map (lambda (v ve)
30063038
(if (is-var-boxed? v lam)
30073039
#f
@@ -3131,7 +3163,7 @@ f(x) = yt(x)
31313163
(cdr lst))))
31323164
(simple? (every (lambda (x) (or (simple-atom? x) (symbol? x) (ssavalue? x)
31333165
(and (pair? x)
3134-
(memq (car x) '(quote inert top core globalref copyast)))))
3166+
(memq (car x) '(quote inert top core globalref outerref copyast)))))
31353167
lst)))
31363168
(let loop ((lst lst)
31373169
(vals '()))
@@ -3144,7 +3176,7 @@ f(x) = yt(x)
31443176
(not (simple-atom? arg)) (not (ssavalue? arg))
31453177
(not (simple-atom? aval)) (not (ssavalue? aval))
31463178
(not (and (pair? arg)
3147-
(memq (car arg) '(& quote inert top core globalref copyast))))
3179+
(memq (car arg) '(& quote inert top core globalref outerref copyast))))
31483180
(not (and (symbol? arg)
31493181
(or (null? (cdr lst))
31503182
(null? vals)))))
@@ -3169,14 +3201,15 @@ f(x) = yt(x)
31693201
;; from the current function.
31703202
(define (compile e break-labels value tail)
31713203
(if (or (not (pair? e)) (memq (car e) '(null ssavalue quote inert top core copyast the_exception $
3172-
globalref cdecl stdcall fastcall thiscall)))
3204+
globalref outerref cdecl stdcall fastcall thiscall)))
31733205
(let ((e (if (and arg-map (symbol? e))
31743206
(get arg-map e e)
31753207
e)))
31763208
(cond (tail (emit-return e))
31773209
(value e)
31783210
((or (eq? e 'true) (eq? e 'false)) #f)
31793211
((symbol? e) (emit e) #f) ;; keep symbols for undefined-var checking
3212+
((and (pair? e) (eq? (car e) 'outerref)) (emit e) #f) ;; keep globals for undefined-var checking
31803213
(else #f)))
31813214
(case (car e)
31823215
((call new)
@@ -3538,10 +3571,10 @@ f(x) = yt(x)
35383571
(define (renumber-slots e)
35393572
(cond ((symbol? e)
35403573
(let ((idx (get slot-table e #f)))
3541-
(or (and idx `(slot ,idx))
3542-
(let ((idx (get sp-table e #f)))
3543-
(or (and idx `(static_parameter ,idx))
3544-
e)))))
3574+
(if idx `(slot ,idx) e)))
3575+
((and (pair? e) (eq? (car e) 'outerref))
3576+
(let ((idx (get sp-table (cadr e) #f)))
3577+
(if idx `(static_parameter ,idx) (cadr e))))
35453578
((or (atom? e) (quoted? e)) e)
35463579
((ssavalue? e)
35473580
(let ((idx (or (get ssavalue-table (cadr e) #f)

test/core.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,18 @@ end
560560
@test f19333(0) == 7
561561
@test x19333 == 5
562562

563+
function h19333()
564+
s = 0
565+
for (i, j) in ((1, 2),)
566+
s += i + j # use + as a global
567+
end
568+
for (k, +) in ((3, 4),)
569+
s -= (k - +) # use + as a local
570+
end
571+
return s
572+
end
573+
@test h19333() == 4
574+
563575
# let - new variables, including undefinedness
564576
function let_undef()
565577
first = true

0 commit comments

Comments
 (0)