You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
test.js:7:10,10: string
This type is incompatible with
test.js:3:42,45: number
Got: Found 0 errors
I did pull the thread here, and I am quite amazed how smart Flow is. Here's what I understand:
Before we start processing the if { ... } statement, we grab the current env and store it away. Let's call this env0. We copy that env and swap in the copy. That copy is env1 and is the environment for the consequent block.
In the consequent block, the (FunT,CallT) flow will set scope entries' specific = general—and we can see an expected failure if we remove the if {...} wrapping around the the f() call. I added some printf debugging in Flow_js.havoc_ctx and I can observe x's specific type changing from Num(0) to Root (rank = 0, unresolved = { lower = [NumT(0),StrT("")]; }).
There's no alternate block on the if statement, but we copy env0 again. Let's call that env2, which is equal to env0/uninteresting here.
Then, after the if { ... } statement is processed, we merge env0 with env1 and env2. We can see in env1 that x's specific type is number | string, but x is not in the changeset, so we never merge the entries from the two envs. Thus, we "revert" back to the env where x is just NumT.
So, it seems that the problem is that x is not in the changeset when we merge the envs. I suspect this is because the changeset that does include x is thrown away when we call Env_js.pop_env after processing the function declaration for f.
If my hypothesis is correct, the fix for this one is going to be pretty involved. FWIW, I came to this test case trying to pick apart this logic and understand it, which I need to do in order to implement let/const. We run into a few similar issues when we add lex scopes, and I am still working out how to manage changesets. Seems like we should figure out this issue and let/const together.
Definitely need some feedback on this one. Thanks :)
The text was updated successfully, but these errors were encountered:
Oh, perhaps it's as simple as installing x into the "correct" changeset—that is, not the top one, but the one that corresponds to the scope where the binding is declared.
(Edit: it doesn't help—we explicitly ignore changeset updates outside of the if stmt)
letadd_change_varname=letrec loopacc=function|scope::scopes, changeset::changesets ->
(matchScope.get_entry name scope with|Some_ ->
let (vars, refs) = changeset inlet vars =SSet.add name vars inList.rev_append acc ((vars, refs)::changesets);
|None ->
loop (changeset::acc) (scopes, changesets))
|_ -> assert_false "uh oh"in
changesets := loop [] (!scopes, !changesets)
Yup, you nailed it. The problem is that the havocing that happens in havoc_ctx is lost when envs are swapped around.
The solution is nontrivial but reasonably contained - and it's a step towards dealing properly with read/write sets in functions (so that we can blow away only the variables that a closure actually writes to, among other things). I did a little work on this over the weekend, but could describe it to you in a couple sentences if you wanted to take a crack at it. Otherwise I can probably get it landed by mid-week. Either way, thanks for bumping the issue!
Given the following:
I expected an error, something like:
Got:
Found 0 errors
I did pull the thread here, and I am quite amazed how smart Flow is. Here's what I understand:
if { ... }
statement, we grab the current env and store it away. Let's call thisenv0
. We copy that env and swap in the copy. That copy isenv1
and is the environment for the consequent block.(FunT,CallT)
flow will set scope entries'specific = general
—and we can see an expected failure if we remove theif {...}
wrapping around the thef()
call. I added some printf debugging inFlow_js.havoc_ctx
and I can observex
's specific type changing fromNum(0)
toRoot (rank = 0, unresolved = { lower = [NumT(0),StrT("")]; })
.if
statement, but we copyenv0
again. Let's call thatenv2
, which is equal toenv0
/uninteresting here.if { ... }
statement is processed, we mergeenv0
withenv1
andenv2
. We can see inenv1
thatx
's specific type isnumber | string
, butx
is not in the changeset, so we never merge the entries from the two envs. Thus, we "revert" back to the env wherex
is justNumT
.So, it seems that the problem is that
x
is not in the changeset when we merge the envs. I suspect this is because the changeset that does includex
is thrown away when we callEnv_js.pop_env
after processing the function declaration forf
.If my hypothesis is correct, the fix for this one is going to be pretty involved. FWIW, I came to this test case trying to pick apart this logic and understand it, which I need to do in order to implement let/const. We run into a few similar issues when we add lex scopes, and I am still working out how to manage changesets. Seems like we should figure out this issue and let/const together.
Definitely need some feedback on this one. Thanks :)
The text was updated successfully, but these errors were encountered: