@@ -31,6 +31,49 @@ function add_lambda_local!(ctx::ClosureConversionCtx, id)
3131 init_lambda_binding (ctx. lambda_bindings, id)
3232end
3333
34+ # Access captured variable from inside a closure
35+ function captured_var_access (ctx, ex)
36+ cinfo = ctx. closure_info
37+ field_sym = cinfo. field_syms[cinfo. field_name_inds[ex. var_id]]
38+ @ast ctx ex [K " call"
39+ " getfield" :: K"core"
40+ # FIXME : attributing the self binding to srcref=ex gives misleading printing.
41+ # We should carry provenance with each binding to fix this.
42+ ctx. lambda_bindings. self:: K"BindingId"
43+ field_sym
44+ ]
45+ end
46+
47+ function get_box_contents (ctx:: ClosureConversionCtx , var, box_ex)
48+ undef_var = new_mutable_var (ctx, var, lookup_binding (ctx, var. var_id). name)
49+ @ast ctx var [K " block"
50+ box := box_ex
51+ # Lower in an UndefVar check to a similarly named variable
52+ # (ref #20016) so that closure lowering Box introduction
53+ # doesn't impact the error message and the compiler is expected
54+ # to fold away the extraneous null check
55+ #
56+ # TODO : Ideally the runtime would rely on provenance info for
57+ # this error and we can remove isdefined check.
58+ [K " if" [K " call"
59+ " isdefined" :: K"core"
60+ box
61+ " contents" :: K"Symbol"
62+ ]
63+ :: K"TOMBSTONE"
64+ [K " block"
65+ [K " newvar" undef_var]
66+ undef_var
67+ ]
68+ ]
69+ [K " call"
70+ " getfield" :: K"core"
71+ box
72+ " contents" :: K"Symbol"
73+ ]
74+ ]
75+ end
76+
3477# Convert `ex` to `type` by calling `convert(type, ex)` when necessary.
3578#
3679# Used for converting the right hand side of an assignment to a typed local or
@@ -118,37 +161,32 @@ function convert_assignment(ctx, ex)
118161 if binfo. kind == :global
119162 convert_global_assignment (ctx, ex, var, rhs0)
120163 else
121- closed = false # TODO
122- captured = false # TODO
123- @assert binfo. kind == :local
124- if isnothing (binfo. type) && ! closed && ! captured
164+ @assert binfo. kind == :local || binfo. kind == :argument
165+ lbinfo = get (ctx. lambda_bindings. bindings, var. var_id, nothing )
166+ self_captured = ! isnothing (lbinfo) && lbinfo. is_captured
167+ captured = binfo. is_captured
168+ if isnothing (binfo. type) && ! self_captured && ! captured
125169 @ast ctx ex [K " =" var rhs0]
126170 else
127- @assert binfo. kind == :local
128171 # Typed local
129- tmp_rhs0 = is_simple_atom (ctx, rhs0) ? nothing : ssavar (ctx, rhs0)
130- rhs1 = isnothing (tmp_rhs0) ? rhs0 : tmp_rhs0
131- rhs = isnothing (binfo. type) ? rhs1 :
132- convert_for_type_decl (ctx, ex, rhs1, _convert_closures (ctx, binfo. type), true )
133- assgn = if closed
134- @assert false # TODO
135- elseif captured
136- @assert false # TODO
137- else
138- @ast ctx ex [K " =" var rhs]
139- end
140- if isnothing (tmp_rhs0)
141- @ast ctx ex [K " block"
142- assgn
143- rhs0
172+ tmp_rhs0 = ssavar (ctx, rhs0)
173+ rhs = isnothing (binfo. type) ? tmp_rhs0 :
174+ convert_for_type_decl (ctx, ex, tmp_rhs0, _convert_closures (ctx, binfo. type), true )
175+ assignment = if self_captured || captured
176+ @ast ctx ex [K " call"
177+ " setfield!" :: K"core"
178+ self_captured ? captured_var_access (ctx, var) : var
179+ " contents" :: K"Symbol"
180+ rhs
144181 ]
145182 else
146- @ast ctx ex [K " block"
147- [K " =" tmp_rhs0 rhs0]
148- assgn
149- tmp_rhs0
150- ]
183+ @ast ctx ex [K " =" var rhs]
151184 end
185+ @ast ctx ex [K " block"
186+ [K " =" tmp_rhs0 rhs0]
187+ assignment
188+ tmp_rhs0
189+ ]
152190 end
153191 end
154192end
@@ -237,40 +275,10 @@ function _convert_closures(ctx::ClosureConversionCtx, ex)
237275 if k == K " BindingId"
238276 id = ex. var_id
239277 lbinfo = get (ctx. lambda_bindings. bindings, id, nothing )
240- if ! isnothing (lbinfo) && lbinfo. is_captured
241- cinfo = ctx. closure_info
242- field_sym = cinfo. field_syms[cinfo. field_name_inds[id]]
243- undef_var = new_mutable_var (ctx, ex, lookup_binding (ctx, id). name)
244- @ast ctx ex [K " block"
245- box := [K " call"
246- " getfield" :: K"core"
247- ctx. lambda_bindings. self:: K"BindingId"
248- field_sym
249- ]
250- # Lower in an UndefVar check to a similarly named variable
251- # (ref #20016) so that closure lowering Box introduction
252- # doesn't impact the error message and the compiler is expected
253- # to fold away the extraneous null check
254- #
255- # TODO : Ideally the runtime would rely on provenance info for
256- # this error and we can remove isdefined check.
257- [K " if" [K " call"
258- " isdefined" :: K"core"
259- box
260- " contents" :: K"Symbol"
261- ]
262- " nothing" :: K"core"
263- [K " block"
264- [K " newvar" undef_var]
265- undef_var
266- ]
267- ]
268- [K " call"
269- " getfield" :: K"core"
270- box
271- " contents" :: K"Symbol"
272- ]
273- ]
278+ if ! isnothing (lbinfo) && lbinfo. is_captured # TODO : && vinfo:asgn cv ??
279+ get_box_contents (ctx, ex, captured_var_access (ctx, ex))
280+ elseif lookup_binding (ctx, id). is_captured # TODO : && vinfo:asgn vi
281+ get_box_contents (ctx, ex, ex)
274282 else
275283 ex
276284 end
@@ -295,6 +303,16 @@ function _convert_closures(ctx::ClosureConversionCtx, ex)
295303 _convert_closures (ctx, ex[2 ])
296304 ]
297305 end
306+ elseif k == K " local"
307+ var = ex[1 ]
308+ binfo = lookup_binding (ctx, var)
309+ if binfo. is_captured
310+ @ast ctx ex [K " =" var [K " call" " Box" :: K"core" ]]
311+ elseif ! binfo. is_always_defined
312+ @ast ctx ex [K " newvar" var]
313+ else
314+ makeleaf (ctx, ex, K " TOMBSTONE" )
315+ end
298316 elseif k == K " ::"
299317 _convert_closures (ctx,
300318 @ast ctx ex [K " call"
@@ -322,10 +340,7 @@ function _convert_closures(ctx::ClosureConversionCtx, ex)
322340 ctx. closure_infos[func_name_id] = closure_info
323341 init_closure_args = SyntaxList (ctx)
324342 for id in field_orig_bindings
325- # FIXME : This isn't actually correct: we need to convert
326- # all outer references to boxes too!
327- push! (init_closure_args, _convert_closures (ctx,
328- @ast ctx ex [K " call" " Box" :: K"core" id:: K"BindingId" ]))
343+ push! (init_closure_args, @ast ctx ex id:: K"BindingId" )
329344 end
330345 @ast ctx ex [K " block"
331346 [K " =" func_name
@@ -380,18 +395,30 @@ function closure_convert_lambda(ctx, ex)
380395 @assert kind (ex) == K " lambda"
381396 body_stmts = SyntaxList (ctx)
382397 toplevel_stmts = ex. is_toplevel_thunk ? body_stmts : ctx. toplevel_stmts
398+ lambda_bindings = ex. lambda_bindings
383399 ctx2 = ClosureConversionCtx (ctx. graph, ctx. bindings, ctx. mod,
384- ctx. closure_bindings, ctx. closure_info, ex . lambda_bindings,
400+ ctx. closure_bindings, ctx. closure_info, lambda_bindings,
385401 toplevel_stmts, ctx. closure_infos)
386402 lambda_children = SyntaxList (ctx)
387- push! (lambda_children, _convert_closures (ctx2, ex[1 ]))
388- push! (lambda_children, _convert_closures (ctx2, ex[2 ]))
403+ args = ex[1 ]
404+ push! (lambda_children, args)
405+ push! (lambda_children, ex[2 ])
389406
390- # Convert body. This is done as a special case to allow inner calls to
391- # _convert_closures to also add to body_stmts in the case that
392- # ex.is_toplevel_thunk is true.
393- in_body_stmts = kind (ex[3 ]) != K " block" ? ex[3 : 3 ] : ex[3 ][1 : end ]
394- for e in in_body_stmts
407+ # Add box initializations for arguments which are captured by an inner lambda
408+ for arg in children (args)
409+ kind (arg) != K " Placeholder" || continue
410+ binfo = lookup_binding (ctx, arg)
411+ if binfo. is_captured # TODO : && binfo.is_assigned
412+ push! (body_stmts, @ast ctx arg [K " ="
413+ arg
414+ [K " call" " Box" :: K"core" arg]
415+ ])
416+ end
417+ end
418+ # Convert body. Note that _convert_closures may call `push!(body_stmts, e)`
419+ # internally for any expressions `e` which need to be moved to top level.
420+ input_body_stmts = kind (ex[3 ]) != K " block" ? ex[3 : 3 ] : ex[3 ][1 : end ]
421+ for e in input_body_stmts
395422 push! (body_stmts, _convert_closures (ctx2, e))
396423 end
397424 push! (lambda_children, @ast ctx2 ex[3 ] [K " block" body_stmts... ])
0 commit comments