dillon1000/react
Commit
Browse files [wip] Fix phi inference, expose InferMutableRange issue
> Update: this is now passing all tests. The approach is likely wrong, and even
if it's fine it needs some cleanup. Putting up for review as folks (esp
@gsathya) have time.
## Background
InferTypes was intended to infer types for phi identifiers, but by accident we
ended up storing the inferred type on `phi.type` instead of `phi.id.type`, which
is the type that usages of the phi will reference. Because of this, we weren't
actually inferring types for several cases, for example if both if/else branches
assign `x` to an array literal, we'd ideally like the corresponding phi id to be
typed as a BuiltInArray:
```javascript
let x;
let y = { ... };
if (cond) {
x = [];
} else {
x = [];
}
// x should be BuiltnArray here. We inferred that on Phi.type but the x here
wouldn't get that type previously
x.push(y);
```
## Circular Types
I started by removing the `Phi.type` property and updating inference to store
the result of phi unification on `phi.id.type` — but this revealed other issues.
First was this can create circular types when there are loops. The solution is
to basically allow circular types _for phis only_, and when we detect them we
remove the cycle. Basically whenever we have a situation where we have some type
variable X, and a type Y that is a (nested) phi type one of whose transitive
operands contains X, we remove X from the transitive type and attempt to
collapse the phi type upwards if all of its remaining operands are the same:
```
X=Type(1)
Y=Phi [
Type(2),
Type(3) = Phi [
Type(1), // <-- cycle but we can prune this
Type(2),
Type(2),
]
]
=>
X=Type(1)
Y=Phi [
Type(2),
Type(3) = Phi [ // all remaining operands are the same, we can prune this
Type(2),
Type(2),
]
]
=>
X=Type(1)
Y=Phi [ // all remaining operands are the same, we can prune this
Type(2),
Type(2),
]
=>
X=Type(1)
Y=Type(2)
```
We have to do this not just doing unify(), but also in `get()` since there are
cases where we don't know yet which type variables we can remove from a phi.
Without also doing the pruning in get, we get an infinite loop.
## Reactive Scope Alignment
The above fixed the circular types, but exposed some new cases that can occur in
terms of mutable ranges and ast structures: it wasn't possible before to have a
Store on a phi node in practice, since that relied on type information which we
didn't have for phis.
The new validation that all instructions for a scope are part of that scope
caught a couple issues, which were basically like this:
```
[1] Sequence
...
[9] StoreLocal x@0[9:28]
[10] ...
```
Note that scope 0 starts at instruction 9, but that instruction is not at the
block scope level. The first instruction at the block scope level that is within
the range of scope 0 is instruction 10, which is after the scope should have
started! So I also had to update AlignScopesToBlockScopes to handle the case of
logical, conditional, and sequence expressions: we sometime need to adjust a
scope start earlier in case they contain instructions that should start a scope.Changed paths13 files
First-parent comparisoncompiler/packages/babel-plugin-react-forget/src/ReactiveScopes/AlignReactiveScopesToBlockScopes.ts ModifiedA compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/memoize-value-block-value-conditional.expect.md AddedA compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/memoize-value-block-value-conditional.js AddedA compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/memoize-value-block-value-logical-no-sequence.expect.md AddedA compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/memoize-value-block-value-logical-no-sequence.js AddedA compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/memoize-value-block-value-logical.expect.md AddedA compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/memoize-value-block-value-logical.js AddedA compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/memoize-value-block-value-sequence.expect.md AddedA compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/memoize-value-block-value-sequence.js AddedM compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md ModifiedM compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md ModifiedM compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary-with-mutation.expect.md ModifiedM compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md ModifiedPatch
Files changed
Rendering syntax-highlighted changes…