Commit Graph
20495 Commits
Author SHA1 Message Date
Joe Savona d1b18bb709 Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-09 11:11:42 -07:00
Joe Savona 05d6af6122 Update base for Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-09 11:11:41 -07:00
Joe Savona 68150fefd7 Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-06 18:06:08 -07:00
Joe Savona 99c165b5d3 Update base for Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-06 18:06:07 -07:00
Joe Savona 63b97ccd68 Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-06 12:58:34 -07:00
Joe Savona b15ee85add Update base for Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-06 12:58:33 -07:00
Joe Savona 39b149ac96 Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-06 12:42:45 -07:00
Joe Savona 5c01660a80 Update base for Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-06 12:42:44 -07:00
Joe Savona 61678e9a3d Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-06 09:57:14 -07:00
Joe Savona 53971df337 Update base for Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-06 09:57:14 -07:00
Joe Savona be4b4033f5 Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-05 22:13:50 -07:00
Joe Savona 2478a92bf1 Update base for Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-05 22:13:50 -07:00
Joe Savona 93bf8a0d7f Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-05 14:17:29 -07:00
Joe Savona 05290f5ba2 Update base for Update on "[compiler][newinference] ensure fixpoint converges for loops w backedges"
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-05 14:17:28 -07:00
Joe Savona ba3cadffe9 [compiler][newinference] ensure fixpoint converges for loops w backedges
The fixpoint converges based on the abstract values being equal, but we synthesize new values cached based on the effects and new effects can be synthesized on the fly. So here we intern the effect objects by "value" (cache key computed from the value) to ensure that effects are stable, values cached based on them are stable, and that the fixpoint can converge.

[ghstack-poisoned]
2025-06-05 14:00:56 -07:00
Joe Savona 301dd5c973 Update on "[compiler][newinference] Fix for phi types, extracting primitives from objects"
First a quick fix: if we have a known type for the lvalue of CreateFrom, we can drop the effect. This is a bit awkward generally because the types and abstract values overlap a bit, and i'd prefer to only look at types during one phase. So maybe move all the type-checking to where we generate effects, and then after that applyEffect() doesn't have to consider types.

The larger fix is for InferMutationAliasingRanges. When processing a mutation we have to walk the graph in both forward and backwards directions. Consider `alias a -> b, mutate(b)`. We have to walk back the alias chain from b to a, and mark a as mutated too. But for `alias a -> b, mutate(a)`, we also have to mark b as mutated — walking forwards along the alias chain.

But phis are a bit different. You can have a mutation of one of the phi operands, such that you have `a, b -> phi c, mutate(a)`. Here, we do need to mark c as mutated, but we should not walk back to b — it's a different operand! a and b don't alias together.

There are now about 150 fixtures failing, but they're in a few categories and all of them are addressable:
* Infinite loops. `applyEffect()` creates new un-memoized effect values which means that any input with a backedge (loop) will spin until my infinite loop detection throws. This is somewhat tedious to address but it's a pragmatic concern and not a flaw in the model. I also need to convince myself that the approach in InferMutationAliasingRanges is safe for loops, but first i have to get inputs w loops to even reach that phase.
* LoadContext/StoreContext - i currently treat these too similarly to regular load/store, ie assuming the mutations happen in order. One idea is to treat LoadContext as a mutate instead of an alias, just to make sure all instances get grouped together.
* InvalidReact detection. We already synthesize MutateFrozen/MutateGlobal effects but we don't throw these as errors yet. This is probably the largest category of failing tests, which means overall this is actually "pretty close" (so, 50% of the way there).

[ghstack-poisoned]
2025-06-05 14:00:54 -07:00
Joe Savona 56fcd7dc46 Update base for Update on "[compiler][newinference] Fix for phi types, extracting primitives from objects"
First a quick fix: if we have a known type for the lvalue of CreateFrom, we can drop the effect. This is a bit awkward generally because the types and abstract values overlap a bit, and i'd prefer to only look at types during one phase. So maybe move all the type-checking to where we generate effects, and then after that applyEffect() doesn't have to consider types.

The larger fix is for InferMutationAliasingRanges. When processing a mutation we have to walk the graph in both forward and backwards directions. Consider `alias a -> b, mutate(b)`. We have to walk back the alias chain from b to a, and mark a as mutated too. But for `alias a -> b, mutate(a)`, we also have to mark b as mutated — walking forwards along the alias chain.

But phis are a bit different. You can have a mutation of one of the phi operands, such that you have `a, b -> phi c, mutate(a)`. Here, we do need to mark c as mutated, but we should not walk back to b — it's a different operand! a and b don't alias together.

There are now about 150 fixtures failing, but they're in a few categories and all of them are addressable:
* Infinite loops. `applyEffect()` creates new un-memoized effect values which means that any input with a backedge (loop) will spin until my infinite loop detection throws. This is somewhat tedious to address but it's a pragmatic concern and not a flaw in the model. I also need to convince myself that the approach in InferMutationAliasingRanges is safe for loops, but first i have to get inputs w loops to even reach that phase.
* LoadContext/StoreContext - i currently treat these too similarly to regular load/store, ie assuming the mutations happen in order. One idea is to treat LoadContext as a mutate instead of an alias, just to make sure all instances get grouped together.
* InvalidReact detection. We already synthesize MutateFrozen/MutateGlobal effects but we don't throw these as errors yet. This is probably the largest category of failing tests, which means overall this is actually "pretty close" (so, 50% of the way there).

[ghstack-poisoned]
2025-06-05 14:00:54 -07:00
Joe Savona 58ad52f2e5 [compiler][newinference] Fix for phi types, extracting primitives from objects
First a quick fix: if we have a known type for the lvalue of CreateFrom, we can drop the effect. This is a bit awkward generally because the types and abstract values overlap a bit, and i'd prefer to only look at types during one phase. So maybe move all the type-checking to where we generate effects, and then after that applyEffect() doesn't have to consider types.

The larger fix is for InferMutationAliasingRanges. When processing a mutation we have to walk the graph in both forward and backwards directions. Consider `alias a -> b, mutate(b)`. We have to walk back the alias chain from b to a, and mark a as mutated too. But for `alias a -> b, mutate(a)`, we also have to mark b as mutated — walking forwards along the alias chain.

But phis are a bit different. You can have a mutation of one of the phi operands, such that you have `a, b -> phi c, mutate(a)`. Here, we do need to mark c as mutated, but we should not walk back to b — it's a different operand! a and b don't alias together.

There are now about 150 fixtures failing, but they're in a few categories and all of them are addressable:
* Infinite loops. `applyEffect()` creates new un-memoized effect values which means that any input with a backedge (loop) will spin until my infinite loop detection throws. This is somewhat tedious to address but it's a pragmatic concern and not a flaw in the model. I also need to convince myself that the approach in InferMutationAliasingRanges is safe for loops, but first i have to get inputs w loops to even reach that phase.
* LoadContext/StoreContext - i currently treat these too similarly to regular load/store, ie assuming the mutations happen in order. One idea is to treat LoadContext as a mutate instead of an alias, just to make sure all instances get grouped together.
* InvalidReact detection. We already synthesize MutateFrozen/MutateGlobal effects but we don't throw these as errors yet. This is probably the largest category of failing tests, which means overall this is actually "pretty close" (so, 50% of the way there).

[ghstack-poisoned]
2025-06-04 17:01:15 -07:00
Joe Savona 446989017a Update on "[compiler][newinference] Fixes for transitive function capturing, mutation via property loads"
Fixes for a few cases:
* If you extract part of a value and mutate the part, that has to count as mutating the whole. See the new fixture, but this also came up with an existing test that has an array whose items are modified via array.map with a lambda that mutates its input. The key change here is preserving the CreateFrom effect (not downgrading to Alias) and then handling CreateFrom specially in range inference. Mutations of values derived from CreateFrom are transitive against the value they came from. We handle this by keeping a queue of not just the places to visit during mutation, but whether to visit them transitively or not.
    * TODO: we may also want to track whether we've seen a value as transitive or not
* For that array.map case w the mutable lambda, we weren't bubbling up the effects correctly so that we knew the param was mutated. Basically we inferred this information and then didn't record it. There is a bit of plumbing here.
* Similarly, if a function expression returns a function expression and the inner one mutates context, we weren't propagating that up to the outer function expression. 

[ghstack-poisoned]
2025-06-04 17:01:13 -07:00
Joe Savona 95336ce174 Update base for Update on "[compiler][newinference] Fixes for transitive function capturing, mutation via property loads"
Fixes for a few cases:
* If you extract part of a value and mutate the part, that has to count as mutating the whole. See the new fixture, but this also came up with an existing test that has an array whose items are modified via array.map with a lambda that mutates its input. The key change here is preserving the CreateFrom effect (not downgrading to Alias) and then handling CreateFrom specially in range inference. Mutations of values derived from CreateFrom are transitive against the value they came from. We handle this by keeping a queue of not just the places to visit during mutation, but whether to visit them transitively or not.
    * TODO: we may also want to track whether we've seen a value as transitive or not
* For that array.map case w the mutable lambda, we weren't bubbling up the effects correctly so that we knew the param was mutated. Basically we inferred this information and then didn't record it. There is a bit of plumbing here.
* Similarly, if a function expression returns a function expression and the inner one mutates context, we weren't propagating that up to the outer function expression. 

[ghstack-poisoned]
2025-06-04 17:01:12 -07:00
Joe Savona 752fff2be8 Update on "[compiler][newinference] Fixes for transitive function capturing, mutation via property loads"
Fixes for a few cases:
* If you extract part of a value and mutate the part, that has to count as mutating the whole. See the new fixture, but this also came up with an existing test that has an array whose items are modified via array.map with a lambda that mutates its input. The key change here is preserving the CreateFrom effect (not downgrading to Alias) and then handling CreateFrom specially in range inference. Mutations of values derived from CreateFrom are transitive against the value they came from. We handle this by keeping a queue of not just the places to visit during mutation, but whether to visit them transitively or not.
    * TODO: we may also want to track whether we've seen a value as transitive or not
* For that array.map case w the mutable lambda, we weren't bubbling up the effects correctly so that we knew the param was mutated. Basically we inferred this information and then didn't record it. There is a bit of plumbing here.
* Similarly, if a function expression returns a function expression and the inner one mutates context, we weren't propagating that up to the outer function expression. 

[ghstack-poisoned]
2025-06-04 16:48:27 -07:00
Joe Savona 4f7bff315c Update base for Update on "[compiler][newinference] Fixes for transitive function capturing, mutation via property loads"
Fixes for a few cases:
* If you extract part of a value and mutate the part, that has to count as mutating the whole. See the new fixture, but this also came up with an existing test that has an array whose items are modified via array.map with a lambda that mutates its input. The key change here is preserving the CreateFrom effect (not downgrading to Alias) and then handling CreateFrom specially in range inference. Mutations of values derived from CreateFrom are transitive against the value they came from. We handle this by keeping a queue of not just the places to visit during mutation, but whether to visit them transitively or not.
    * TODO: we may also want to track whether we've seen a value as transitive or not
* For that array.map case w the mutable lambda, we weren't bubbling up the effects correctly so that we knew the param was mutated. Basically we inferred this information and then didn't record it. There is a bit of plumbing here.
* Similarly, if a function expression returns a function expression and the inner one mutates context, we weren't propagating that up to the outer function expression. 

[ghstack-poisoned]
2025-06-04 16:48:26 -07:00
Joe Savona 4da1c43e98 [compiler][newinference] Fixes for transitive function capturing, mutation via property loads
[ghstack-poisoned]
2025-06-03 17:01:07 -07:00
Joe Savona 8103b21833 [compiler] Effects for Return/MaybeThrow terminals
[ghstack-poisoned]
2025-06-03 17:01:02 -07:00
Joe Savona f8e7b48740 Update on "[compiler] Post/Pre-FixUpdate, try/catch, ConditionallyMutateIterator support"
[ghstack-poisoned]
2025-06-03 17:01:00 -07:00
Joe Savona cb6bca4198 Update base for Update on "[compiler] Post/Pre-FixUpdate, try/catch, ConditionallyMutateIterator support"
[ghstack-poisoned]
2025-06-03 17:00:59 -07:00
Joe Savona b5fb1bf44a [compiler] Post/Pre-FixUpdate, try/catch, ConditionallyMutateIterator support
[ghstack-poisoned]
2025-06-03 11:34:02 -07:00
Joe Savona c282c605bf Update on "[compiler] Further improve new range inference"
Further refine the abstract interpretation for InferMutationAliasingRanges to account for forward data flow: Alias/Capture a -> b, mutate(a) => mutate(b) ie mutation affects aliases of the place being mutated, not just things that place aliased.

Fixes inference of which context vars of a function mutate, using the precise inference from the Range pass.

Adds MutateFrozen and MutateGlobal effects but they're not fully hooked up yet.

[ghstack-poisoned]
2025-06-03 11:34:00 -07:00
Joe Savona 014e4c8316 Update base for Update on "[compiler] Further improve new range inference"
Further refine the abstract interpretation for InferMutationAliasingRanges to account for forward data flow: Alias/Capture a -> b, mutate(a) => mutate(b) ie mutation affects aliases of the place being mutated, not just things that place aliased.

Fixes inference of which context vars of a function mutate, using the precise inference from the Range pass.

Adds MutateFrozen and MutateGlobal effects but they're not fully hooked up yet.

[ghstack-poisoned]
2025-06-03 11:34:00 -07:00
Joe Savona 0bcb683326 Update on "[compiler] Further improve new range inference"
Further refine the abstract interpretation for InferMutationAliasingRanges to account for forward data flow: Alias/Capture a -> b, mutate(a) => mutate(b) ie mutation affects aliases of the place being mutated, not just things that place aliased.

Fixes inference of which context vars of a function mutate, using the precise inference from the Range pass.

Adds MutateFrozen and MutateGlobal effects but they're not fully hooked up yet.

[ghstack-poisoned]
2025-06-03 09:01:43 -07:00
Joe Savona 316625c4cb Update base for Update on "[compiler] Further improve new range inference"
Further refine the abstract interpretation for InferMutationAliasingRanges to account for forward data flow: Alias/Capture a -> b, mutate(a) => mutate(b) ie mutation affects aliases of the place being mutated, not just things that place aliased.

Fixes inference of which context vars of a function mutate, using the precise inference from the Range pass.

Adds MutateFrozen and MutateGlobal effects but they're not fully hooked up yet.

[ghstack-poisoned]
2025-06-03 09:01:42 -07:00
Joe Savona 8cdd63963e [compiler] Further improve new range inference
Further refine the abstract interpretation for InferMutationAliasingRanges to account for forward data flow: Alias/Capture a -> b, mutate(a) => mutate(b) ie mutation affects aliases of the place being mutated, not just things that place aliased.

Fixes inference of which context vars of a function mutate, using the precise inference from the Range pass.

Adds MutateFrozen and MutateGlobal effects but they're not fully hooked up yet.

[ghstack-poisoned]
2025-06-02 18:32:17 -07:00
Joe Savona fccfbfbd7f Update on "[compiler] InferMutationAliasingRanges precisely models which values mutate when"
It turns out that InferMutationAliasingRanges does need a fixpoint loop, but the approach is arguably simpler overall and more precise than the previous implementation. Like InferMutationAliasingEffects (which is the new InferReferenceEffects), we build an abstract model of the heap. But here we know what the effects are, so we can do abstract interpretation of the effects. Each abstract value stores a set of values that it has captured (for transitive mutation), while each variable keeps a set of values it may directly mutate (for assign/alias/capturefrom).

This means that at each mutation, we can mark _exactly_ the set of variables/values that are affected by that specific instruction. This means we can correctly infer that `mutate(b)` can't impact `a` here:

```
a = make();
b = make();
mutate(b); // when we interpret the mutation here, a isn't captured yet
b.a = a;
```

We will need to make this a fixpoint, but only if there are backedges in the CFG.

[ghstack-poisoned]
2025-06-02 18:32:15 -07:00
Joe Savona 56bde89475 Update base for Update on "[compiler] InferMutationAliasingRanges precisely models which values mutate when"
It turns out that InferMutationAliasingRanges does need a fixpoint loop, but the approach is arguably simpler overall and more precise than the previous implementation. Like InferMutationAliasingEffects (which is the new InferReferenceEffects), we build an abstract model of the heap. But here we know what the effects are, so we can do abstract interpretation of the effects. Each abstract value stores a set of values that it has captured (for transitive mutation), while each variable keeps a set of values it may directly mutate (for assign/alias/capturefrom).

This means that at each mutation, we can mark _exactly_ the set of variables/values that are affected by that specific instruction. This means we can correctly infer that `mutate(b)` can't impact `a` here:

```
a = make();
b = make();
mutate(b); // when we interpret the mutation here, a isn't captured yet
b.a = a;
```

We will need to make this a fixpoint, but only if there are backedges in the CFG.

[ghstack-poisoned]
2025-06-02 18:32:15 -07:00
Joe Savona 8ed07a13b2 [compiler] InferMutationAliasingRanges precisely models which values mutate when
It turns out that InferMutationAliasingRanges does need a fixpoint loop, but the approach is arguably simpler overall and more precise than the previous implementation. Like InferMutationAliasingEffects (which is the new InferReferenceEffects), we build an abstract model of the heap. But here we know what the effects are, so we can do abstract interpretation of the effects. Each abstract value stores a set of values that it has captured (for transitive mutation), while each variable keeps a set of values it may directly mutate (for assign/alias/capturefrom).

This means that at each mutation, we can mark _exactly_ the set of variables/values that are affected by that specific instruction. This means we can correctly infer that `mutate(b)` can't impact `a` here:

```
a = make();
b = make();
mutate(b); // when we interpret the mutation here, a isn't captured yet
b.a = a;
```

We will need to make this a fixpoint, but only if there are backedges in the CFG.

[ghstack-poisoned]
2025-06-01 17:19:54 -07:00
Joe Savona 5554f608ac Update on "[compiler] Fix mutable ranges for StoreContext"
[ghstack-poisoned]
2025-06-01 17:19:52 -07:00
Joe Savona 8cce5cca2c Update base for Update on "[compiler] Fix mutable ranges for StoreContext"
[ghstack-poisoned]
2025-06-01 17:19:51 -07:00
Joe Savona 58a19b2e7a [compiler] Fix mutable ranges for StoreContext
[ghstack-poisoned]
2025-05-30 16:30:03 -07:00
Joe Savona 411e50f2c2 Update on "[compiler] Distinguish Alias/Assign effects"
Alias was akward becuase it had a specific data flow / mutability relationship — direct mutations affect both sides - but also populated the output. We need the ability to express the aliasing dataflwo/mutability relationship w/o expressing actual assignment.

An example of this is that for an unknown method call `const z = x.y()`, we want to express the Alias-style mutability relationship btw z and x (Muate(z) => Mutate(x)), but we want to assume that `z !== x`.

This ends up being really clean, the places that should use Assign are very obvious (LoadLocal, StoreLocal) and most places keep using Alias.

[ghstack-poisoned]
2025-05-30 16:30:01 -07:00
Joe Savona 1718763795 Update base for Update on "[compiler] Distinguish Alias/Assign effects"
Alias was akward becuase it had a specific data flow / mutability relationship — direct mutations affect both sides - but also populated the output. We need the ability to express the aliasing dataflwo/mutability relationship w/o expressing actual assignment.

An example of this is that for an unknown method call `const z = x.y()`, we want to express the Alias-style mutability relationship btw z and x (Muate(z) => Mutate(x)), but we want to assume that `z !== x`.

This ends up being really clean, the places that should use Assign are very obvious (LoadLocal, StoreLocal) and most places keep using Alias.

[ghstack-poisoned]
2025-05-30 16:30:01 -07:00
Joe Savona 207cf384b0 [compiler] Distinguish Alias/Assign effects
Alias was akward becuase it had a specific data flow / mutability relationship — direct mutations affect both sides - but also populated the output. We need the ability to express the aliasing dataflwo/mutability relationship w/o expressing actual assignment.

An example of this is that for an unknown method call `const z = x.y()`, we want to express the Alias-style mutability relationship btw z and x (Muate(z) => Mutate(x)), but we want to assume that `z !== x`.

This ends up being really clean, the places that should use Assign are very obvious (LoadLocal, StoreLocal) and most places keep using Alias.

[ghstack-poisoned]
2025-05-30 15:57:18 -07:00
Joe Savona e44415480d Update on "[compiler] Effect inference across signatures and user-provided callbacks"
This ties all the ideas together, showing the promise of the new inference. When applying effects, when we encounter an Apply effect we now check to see if the function we're calling is a locally defined FunctionExpression. If so, we construct a signature for it on the fly (we already have created the effects in AnalyzeFunctions), substitute in the args to get a set of effects we can apply, and then recursively apply those effects.

This required adding an ability for signatures to declare additional temporary places that they can reference. For example, Array.prototype.map needs a temporary to represent the items it extracts from the receiver array, and another temporary for the result of calling the user-provided function.

This also meant adding a `CreateFunction` effect which a) allows us to preserve the FunctionExpression value in the inference state (the previous Create effect meant we just created a dummy ObjectExpression) and b) allows dynamically constructing the ValueKind of the function based on whether it actually captures any mutable values.

Lots of other little fixes as well, such as changing function related effects (and PropertyLoad) to use Alias instead of Capture so that subsequent mutations of the output count as mutations of the input.

[ghstack-poisoned]
2025-05-30 15:57:16 -07:00
Joe Savona 37f3daa79d Update base for Update on "[compiler] Effect inference across signatures and user-provided callbacks"
This ties all the ideas together, showing the promise of the new inference. When applying effects, when we encounter an Apply effect we now check to see if the function we're calling is a locally defined FunctionExpression. If so, we construct a signature for it on the fly (we already have created the effects in AnalyzeFunctions), substitute in the args to get a set of effects we can apply, and then recursively apply those effects.

This required adding an ability for signatures to declare additional temporary places that they can reference. For example, Array.prototype.map needs a temporary to represent the items it extracts from the receiver array, and another temporary for the result of calling the user-provided function.

This also meant adding a `CreateFunction` effect which a) allows us to preserve the FunctionExpression value in the inference state (the previous Create effect meant we just created a dummy ObjectExpression) and b) allows dynamically constructing the ValueKind of the function based on whether it actually captures any mutable values.

Lots of other little fixes as well, such as changing function related effects (and PropertyLoad) to use Alias instead of Capture so that subsequent mutations of the output count as mutations of the input.

[ghstack-poisoned]
2025-05-30 15:57:16 -07:00
Joe Savona 79760fefcb [compiler] Effect inference across signatures and user-provided callbacks
This ties all the ideas together, showing the promise of the new inference. When applying effects, when we encounter an Apply effect we now check to see if the function we're calling is a locally defined FunctionExpression. If so, we construct a signature for it on the fly (we already have created the effects in AnalyzeFunctions), substitute in the args to get a set of effects we can apply, and then recursively apply those effects.

This required adding an ability for signatures to declare additional temporary places that they can reference. For example, Array.prototype.map needs a temporary to represent the items it extracts from the receiver array, and another temporary for the result of calling the user-provided function.

This also meant adding a `CreateFunction` effect which a) allows us to preserve the FunctionExpression value in the inference state (the previous Create effect meant we just created a dummy ObjectExpression) and b) allows dynamically constructing the ValueKind of the function based on whether it actually captures any mutable values.

Lots of other little fixes as well, such as changing function related effects (and PropertyLoad) to use Alias instead of Capture so that subsequent mutations of the output count as mutations of the input.

[ghstack-poisoned]
2025-05-30 11:33:45 -07:00
Joe Savona 68fd43acca Update on "[compiler] Receiver is mutate? for functions wo signatures"
[ghstack-poisoned]
2025-05-30 11:33:43 -07:00
Joe Savona c516349df5 Update base for Update on "[compiler] Receiver is mutate? for functions wo signatures"
[ghstack-poisoned]
2025-05-30 11:33:42 -07:00
Joe Savona 148aa1901f [compiler] Receiver is mutate? for functions wo signatures
[ghstack-poisoned]
2025-05-29 17:30:51 -07:00
Joe Savona 126541bca8 [compiler] Handle legacy mutableIfOperandsMutable signatures
[ghstack-poisoned]
2025-05-29 17:30:46 -07:00
Joe Savona 17c114a281 [compiler] Prep for making new/call/etc use Apply effects
[ghstack-poisoned]
2025-05-29 17:30:42 -07:00
Joe Savona ac2340e479 [compiler] Bailout on mutations of frozen/global values
[ghstack-poisoned]
2025-05-29 17:30:37 -07:00