diff --git a/spec/compiler/interpreter/bugs_spec.cr b/spec/compiler/interpreter/bugs_spec.cr index 9c44624330ca..5ed95837e301 100644 --- a/spec/compiler/interpreter/bugs_spec.cr +++ b/spec/compiler/interpreter/bugs_spec.cr @@ -237,5 +237,26 @@ describe Crystal::Repl::Interpreter do typeof(foo) == Foo CRYSTAL end + + it "looks up local vars in parent scopes after looking up local vars in current scope and closured scope (#15489)" do + interpret(<<-CRYSTAL).should eq("parser") + def capture(&block) + block + end + + def scoped(&) + yield 1 + end + + scoped do |parser| + capture do + parser + end + parser # Error: BUG: missing downcast_distinct from String to Int32 (Crystal::NonGenericClassType to Crystal::IntegerType) + end + + parser = "parser" + CRYSTAL + end end end diff --git a/src/compiler/crystal/interpreter/compiler.cr b/src/compiler/crystal/interpreter/compiler.cr index c22f4c628ec5..bba9d5093415 100644 --- a/src/compiler/crystal/interpreter/compiler.cr +++ b/src/compiler/crystal/interpreter/compiler.cr @@ -791,8 +791,9 @@ class Crystal::Repl::Compiler < Crystal::Visitor end def lookup_local_var_or_closured_var(name : String) : LocalVar | ClosuredVar - lookup_local_var?(name) || + lookup_local_var?(name, at: @block_level) || lookup_closured_var?(name) || + lookup_local_var?(name, from: @block_level - 1) || raise("BUG: can't find closured var or local var #{name}") end @@ -800,19 +801,18 @@ class Crystal::Repl::Compiler < Crystal::Visitor lookup_local_var?(name) || raise("BUG: can't find local var #{name}") end - def lookup_local_var?(name : String) : LocalVar? - block_level = @block_level - while block_level >= 0 - index = @local_vars.name_to_index?(name, block_level) - if index - type = @local_vars.type(name, block_level) - return LocalVar.new(index, type) + def lookup_local_var?(name : String, *, from : Int32 = @block_level) : LocalVar? + from.downto(0) do |block_level| + if local_var = lookup_local_var?(name, at: block_level) + return local_var end - - block_level -= 1 end + end - nil + def lookup_local_var?(name : String, *, at block_level : Int32) : LocalVar? + if index = @local_vars.name_to_index?(name, block_level) + LocalVar.new(index, @local_vars.type(name, block_level)) + end end def lookup_closured_var(name : String) : ClosuredVar