Properly reset variables between cond clauses#15459
Conversation
|
|
||
| # Reset the context vars to the head definition to compute the falsy type | ||
| context = Of.reset_vars(body_context, context) | ||
| context = Of.reset_vars(body_context, initial_context) |
There was a problem hiding this comment.
This context of_expr(head, term(), head, %{stack | reverse_arrow: :cache}, initial_context) is a broad context, it should not be forcing the variable x to be a tuple unless we are considering that both sides of is_tuple(x) and tuple_size(x) == 2 are executed or there is another bug.
This only happens with defguard?
There was a problem hiding this comment.
To be clear, we are not expecting to be a tuple because the expected type is term() and not @truthy.
There was a problem hiding this comment.
@josevalim you're right, this is not a defguard thing but an andalso one (which unlike and - which is case - isn't typed as a lazy construct, as it should).
cond do
:erlang.andalso(is_tuple(x), tuple_size(x) == 2) -> :pair
is_atom(x) -> :atom
endThe problem is not with cond itself. Will clause this PR.
Although variables defined in the clause head like arg1 semantically shouldn't be leaking to the next clause, so I still think there might be something incorrect about context management.
There was a problem hiding this comment.
@josevalim I think the correct approach is to add a specific clause for :erlang.orelse / :erlang.andalso in expr.ex, right?
There was a problem hiding this comment.
Yes, that's exactly the root cause, we are considering the right-hand side is always executed. I will look into a fix because unfortunately it is not that trivial, otherwise we can end-up with :erlang.orelse(foo, bar) having more precision than Kernel.or(foo, bar) or Kernel.||(foo, bar).
Close #15450 (tentative fix)
Explanation:
In
initial_context, we just havex. But the guard expands to:So after
of_expr, we addedarg1to the context which doesn't get reset byreset_vars.Not sure yet why it actually influences
xthough, still investigating.To be backported.