Skip to content

Commit 49f9d28

Browse files
asteriteMartin Verzilli
authored and
Martin Verzilli
committedSep 16, 2017
Codegen: don't incorrectly consider local as closured. Fixes #4948
1 parent eb1f102 commit 49f9d28

File tree

2 files changed

+31
-4
lines changed

2 files changed

+31
-4
lines changed
 

Diff for: ‎spec/compiler/codegen/closure_spec.cr

+20
Original file line numberDiff line numberDiff line change
@@ -661,4 +661,24 @@ describe "Code gen: closure" do
661661
foo2.call.x
662662
)).to_i.should eq(42)
663663
end
664+
665+
it "doesn't incorrectly consider local as closured (#4948)" do
666+
codegen(%(
667+
arg = 1
668+
669+
f1 = ->{
670+
# Here 'local' isn't to be confused with
671+
# the outer closured 'local'
672+
local = 1
673+
local + arg
674+
}
675+
676+
arg = 2
677+
678+
local = 4_i64
679+
f2 = ->{ local.to_i }
680+
681+
f1.call + f2.call
682+
))
683+
end
664684
end

Diff for: ‎src/compiler/crystal/codegen/fun.cr

+11-4
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class Crystal::CodeGenVisitor
9494

9595
if is_closure
9696
clear_current_debug_location if @debug.line_numbers?
97-
setup_closure_vars context.closure_vars.not_nil!
97+
setup_closure_vars target_def.vars, context.closure_vars.not_nil!
9898
else
9999
context.reset_closure
100100
end
@@ -364,19 +364,26 @@ class Crystal::CodeGenVisitor
364364
end
365365
end
366366

367-
def setup_closure_vars(closure_vars, context = self.context, closure_ptr = fun_literal_closure_ptr)
367+
def setup_closure_vars(def_vars, closure_vars, context = self.context, closure_ptr = fun_literal_closure_ptr)
368368
if context.closure_skip_parent
369369
parent_context = context.closure_parent_context.not_nil!
370-
setup_closure_vars(parent_context.closure_vars.not_nil!, parent_context, closure_ptr)
370+
setup_closure_vars(def_vars, parent_context.closure_vars.not_nil!, parent_context, closure_ptr)
371371
else
372372
closure_vars.each_with_index do |var, i|
373+
# A closured var in this context might have the same name as
374+
# a local var in another context, for example if the local var
375+
# was defined before the closured var. In this case, don't
376+
# consider the local var as closured.
377+
def_var = def_vars.try &.[var.name]?
378+
next if def_var && !def_var.closured?
379+
373380
self.context.vars[var.name] = LLVMVar.new(gep(closure_ptr, 0, i, var.name), var.type)
374381
end
375382

376383
if (closure_parent_context = context.closure_parent_context) &&
377384
(parent_vars = closure_parent_context.closure_vars)
378385
parent_closure_ptr = gep(closure_ptr, 0, closure_vars.size, "parent_ptr")
379-
setup_closure_vars(parent_vars, closure_parent_context, load(parent_closure_ptr, "parent"))
386+
setup_closure_vars(def_vars, parent_vars, closure_parent_context, load(parent_closure_ptr, "parent"))
380387
elsif closure_self = context.closure_self
381388
offset = context.closure_parent_context ? 1 : 0
382389
self_value = gep(closure_ptr, 0, closure_vars.size + offset, "self")

0 commit comments

Comments
 (0)
Please sign in to comment.