Skip to content

[compiler] InferMutationAliasingRanges precisely models which values mutate when #33401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from

Conversation

josephsavona
Copy link
Member

@josephsavona josephsavona commented Jun 2, 2025

Stack from ghstack (oldest at bottom):

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.

…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]
This was referenced Jun 2, 2025
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Jun 2, 2025
josephsavona added a commit that referenced this pull request Jun 2, 2025
…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-source-id: 6fd1662
Pull Request resolved: #33401
Comment on lines -76 to -80
* TODO: this will incorrectly mark values as mutated in the following case:
* 1. Create value x
* 2. Create value y
* 3. Transitively mutate y
* 4. Capture x -> y
Copy link
Member Author

Choose a reason for hiding this comment

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

this todo is fixed, among other issues

…ich 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]
…ich 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]
…ich 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]
Fertuna92 pushed a commit to Fertuna92/Facebook that referenced this pull request Jun 4, 2025
…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-source-id: 6fd1662
Pull Request resolved: facebook/react#33401
…ich 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]
…ich 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]
…ich 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]
…ich 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]
…ich 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]
…ich 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]
…ich 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]
…ich 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]
…ich 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]
…ich 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]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants