Skip to content

[compiler] Effects for Return/MaybeThrow terminals #33429

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

Draft
wants to merge 10 commits into
base: gh/josephsavona/112/base
Choose a base branch
from

Conversation

josephsavona
Copy link
Member

@josephsavona josephsavona commented Jun 4, 2025

Stack from ghstack (oldest at bottom):

Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact, the only things that can throw their lvalue are call expressions! c = a.b can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

This was referenced Jun 3, 2025
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Jun 4, 2025
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[ghstack-poisoned]
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[ghstack-poisoned]
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[ghstack-poisoned]
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[ghstack-poisoned]
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[ghstack-poisoned]
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[ghstack-poisoned]
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[ghstack-poisoned]
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[ghstack-poisoned]
Adds explicit freeze effects for Return terminals and, more importantly, adds effects for MaybeThrow terminals. MaybeThrow is super interesting. The idea of the terminal is to represent that control-flow can break from nearly any instruction to the error handler (catch). InferMutableRanges has a pass that explicitly handles the corresponding data flow, saying that for any instruction in a block ending in MaybeThrow, to alias the instruction's lvalue to the catch handler. This is to handle cases like this:

```js
const x = [];
try {
  throwInput(x);
} catch (x_alias) {
  mutate(x_alias); // mutates x!
}
```

One realization is that this logic was overly pessimistic: most instruction types cannot actually throw their lvalue. In fact,  the only things that can throw their lvalue are call expressions! `c = a.b` can throw, for example, but only with an error generated by the runtime, not with a user value.

Doing this allows us to encode the data flow once and then not have to handle wiring up this data again later.

[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