Skip to content

Fix exit types of variables assigned inside while conditions#10759

Merged
asterite merged 2 commits intocrystal-lang:masterfrom
HertzDevil:bug/while-cond-assignments
Jun 8, 2021
Merged

Fix exit types of variables assigned inside while conditions#10759
asterite merged 2 commits intocrystal-lang:masterfrom
HertzDevil:bug/while-cond-assignments

Conversation

@HertzDevil
Copy link
Contributor

@HertzDevil HertzDevil commented May 28, 2021

Fixes #10350.

a = 1
while ((b = 1); a)
  a = nil
  b = "hello"
end
typeof(b) # => Int32
def foo(x, y)
  rand < 0.5 ? 1 : nil
end

while foo(b = 1, a = 1)
  a = nil
  b = "hello"
end
typeof(b) # => Int32

It turns out get_while_cond_assign_target isn't needed at all; if a while condition declares multiple variables, all these variables outside the loop can receive their types in the conditions, except that if a variable is nil-if-read (i.e. it is not initialized in all execution paths of the condition expression), then it also receives the type at the end of the while body because the assignments in the condition might not be run after a loop iteration:

while rand < 0.5 ? (x = 1; rand < 0.5) : false
  x = 'a'
end

# `x` has the type of `1`
# since `x = 1` might not be run and `x` does not exist before the loop,
# `x` has also the type of `'a'` and becomes nilable
typeof(x) # => (Char | Int32 | Nil)
# before: # => (Char | Nil)

One known issue is simply reading the variable in the loop body resets the nil-if-read status of that variable (in Crystal::MainVisitor#visit(node : Var)):

while rand < 0.5 ? (x = 1; rand < 0.5) : false
  x # variable read
  x = 'a'
end

typeof(x) # => (Int32 | Nil)
# before: # => (Char | Nil)

This is added as a pending test case. A workaround is to define x before the while expression, so that it is never nil-if-read. (I suspect the reason this doesn't happen to if expressions is because they have before_then_vars whereas while loops have no such equivalent.) This does not impact #10350.

The second commit fixes that.

@straight-shoota straight-shoota added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:lang topic:compiler:semantic and removed topic:lang labels May 28, 2021
Copy link
Member

@asterite asterite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

@asterite asterite added this to the 1.1.0 milestone Jun 8, 2021
@asterite asterite merged commit 2a5404f into crystal-lang:master Jun 8, 2021
@HertzDevil HertzDevil deleted the bug/while-cond-assignments branch June 8, 2021 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:compiler:semantic

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid memory access on while condition with assignment of different type

3 participants