Skip to content

Commit

Permalink
Fix: rebind while condition variables after while body
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored and ysbaddaden committed Jun 30, 2018
1 parent 2bbf72f commit d6ba746
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 0 deletions.
28 changes: 28 additions & 0 deletions spec/compiler/semantic/while_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,32 @@ describe "Semantic: while" do
a
)) { nilable int32 }
end

it "rebinds condition variable after while body (#6158)" do
assert_type(%(
class Foo
@parent : self?
def parent
@parent
end
end
class Bar
def initialize(@parent : Foo)
end
def parent
@parent
end
end
a = Foo.new
b = Bar.new(a)
while b = b.parent
break if 1 == 1
end
b
)) { nilable types["Foo"] }
end
end
16 changes: 16 additions & 0 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2094,6 +2094,22 @@ module Crystal
node.body.accept self
end

# After while's body, bind variables *before* the condition to the
# ones after the body, because the loop will repeat.
#
# For example:
#
# x = exp
# # x starts with the type of exp
# while x = x.method
# # but after the loop, the x above (in x.method)
# # should now also get the type of x.method, recursively
# end
before_cond_vars.each do |name, before_cond_var|
var = @vars[name]?
before_cond_var.bind_to(var) if var && !var.same?(before_cond_var)
end

cond = node.cond.single_expression

endless_while = cond.true_literal?
Expand Down

0 comments on commit d6ba746

Please sign in to comment.