Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compiler: fix a couple of issues with while and rescue #7806

Merged
merged 1 commit into from May 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions spec/compiler/codegen/while_spec.cr
Expand Up @@ -165,4 +165,19 @@ describe "Codegen: while" do
10
)).to_i.should eq(10)
end

it "doesn't crash on while true begin break rescue (#7786)" do
codegen(%(
require "prelude"

while true
begin
foo = 1
break
rescue
end
end
foo
))
end
end
29 changes: 29 additions & 0 deletions spec/compiler/semantic/while_spec.cr
Expand Up @@ -197,4 +197,33 @@ describe "Semantic: while" do
b
)) { nilable types["Foo"] }
end

it "doesn't type var as nilable after break inside rescue" do
assert_type(%(
while true
begin
foo = 1
break
rescue
end
end
foo
)) { int32 }
end

it "types variable as nilable if raise before assign" do
assert_type(%(
require "prelude"

while true
begin
raise "oops"
foo = 12345
rescue
end
break
end
foo
)) { nilable int32 }
end
end
16 changes: 11 additions & 5 deletions src/compiler/crystal/semantic/main_visitor.cr
Expand Up @@ -352,6 +352,10 @@ module Crystal
check_closured meta_var

if var.nil_if_read?
# Once we know a variable is nil if read we mark it as nilable
var.bind_to(@program.nil_var)
var.nil_if_read = false

meta_var.bind_to(@program.nil_var) unless meta_var.dependencies.try &.any? &.same?(@program.nil_var)
node.bind_to(@program.nil_var)
end
Expand Down Expand Up @@ -2193,18 +2197,21 @@ module Crystal
after_while_var.bind_to(while_var)
nilable = false
if endless
# In an endless loop if there's a break before a variable is declared,
# that variable becomes nilable.
unless all_break_vars.try &.all? &.has_key?(name)
# In an endless loop if not all variable with the given name end up
# in a break it means that they can be nilable.
# Alternatively, if any var that ends in a break is nil-if-read then
# the resulting variable will be nil-if-read too.
if !all_break_vars.try(&.all? &.has_key?(name)) ||
all_break_vars.try(&.any? &.[name]?.try &.nil_if_read?)
nilable = true
end
else
nilable = true
end
if nilable
after_while_var.bind_to(@program.nil_var)
after_while_var.nil_if_read = true
end

after_while_vars[name] = after_while_var
end
end
Expand Down Expand Up @@ -2827,7 +2834,6 @@ module Crystal
rescue_vars.each do |name, var|
after_var = (after_vars[name] ||= new_meta_var(name))
if var.nil_if_read? || !body_vars[name]?
after_var.bind_to(program.nil_var)
after_var.nil_if_read = true
end
after_var.bind_to(var)
Expand Down