Skip to content
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

Do not consider match/let/ref of place that evaluates to ! to diverge, disallow coercions from them too #129392

Merged

Conversation

compiler-errors
Copy link
Member

@compiler-errors compiler-errors commented Aug 22, 2024

Fixes #117288.

This PR implements a heuristic which disables two things that are currently being performed on the HIR when we have expressions that involve place-like expressions that point to !. Specifically, it will (in certain cases explained below):

(1.) Disable the NeverToAny coercion we implicitly insert for !.

Which fixes this inadvertent, sneaky unsoundness:

unsafe {
    let x: *const ! = &0 as *const u8 as *const !;
    let _: () = *x;
}

which is UB because currently rust emits an implicit NeverToAny coercion even though we really shouldn't be, since there's no read of the value pointed by x.

(2.) Disable the logic which considers expression which evaluate to ! to diverge, which affects the type returned by the containing block.

Which fixes this unsoundness:

fn make_up_a_value<T>() -> T {
    unsafe {
        let x: *const ! = &0 as *const u8 as *const !;
        let _ = *x;
    }
}

We disable these two operations if the expression is a place-like expression (locals, statics, field projections, index operations, and deref operations), and if the parent expression is either:
(1.) the LHS of an assignment
(2.) AddrOf
(3.) A match or let unless all of the patterns consitute a read, which is explained below:

And finally, a pattern currently is considered to constitute a read unless it is a wildcard, or an OR pattern. An OR pattern is considered to constitute a read if all of its subpatterns constitute a read, to remain as conservative as possible in cases like _ | subpat or subpat | _.

All other patterns are considered currently to constitute a read. Specifically, because NeverToAny is a coercion performed on a value and not a place, Struct { .. } on a ! type must be a coercion currently, and we currently rely on this behavior to allow us to perform coercions like let _: i32 = x; where x: !.

This is already considered UB by miri, but also means it does not affect the preexisting UB in this case:

let Struct { .. } = *never_ptr;

Even though it's likely up for debate since we're not actually reading any data out of the struct, it almost certainly causes inference changes which I do NOT want to fix in this PR.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 22, 2024
@compiler-errors
Copy link
Member Author

@bors try

bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 22, 2024
…verge-but-more, r=<try>

[EXPERIMENT] Do not consider match/let/raw-ref of deref that evalautes to ! to diverge

This is the more involved version of rust-lang#129371. It doesn't fully fix rust-lang#117288, since we still need to fix the fact that never type fallback can cause an unintended load via the `NeverToAny` coercion. But I did want to probe crater to see if anyone relies on this behavior currently, since that's almost certainly UB.

r? `@ghost`
@bors
Copy link
Contributor

bors commented Aug 22, 2024

⌛ Trying commit 5250e5d with merge 4096397...

@bors
Copy link
Contributor

bors commented Aug 22, 2024

☀️ Try build successful - checks-actions
Build commit: 4096397 (4096397122f3daf755d7a75d9716317f02ae7690)

@compiler-errors
Copy link
Member Author

@bors try

@bors
Copy link
Contributor

bors commented Aug 22, 2024

⌛ Trying commit 1ee0400 with merge 3254088...

bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 22, 2024
…verge-but-more, r=<try>

[EXPERIMENT] Do not consider match/let/raw-ref of deref that evalautes to ! to diverge

This is the more involved version of rust-lang#129371. It's pretty ugly rn.

r? `@ghost`
@compiler-errors
Copy link
Member Author

@bors try

bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 22, 2024
…verge-but-more, r=<try>

[EXPERIMENT] Do not consider match/let/raw-ref of deref that evalautes to ! to diverge

This is the more involved version of rust-lang#129371. It's pretty ugly rn.

r? `@ghost`
@bors
Copy link
Contributor

bors commented Aug 22, 2024

⌛ Trying commit 9bb136b with merge de0e806...

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Contributor

bors commented Aug 22, 2024

☀️ Try build successful - checks-actions
Build commit: de0e806 (de0e80659cee4f27282e60d63bbb8c5271a0d147)

@compiler-errors
Copy link
Member Author

@craterbot check

@craterbot
Copy link
Collaborator

👌 Experiment pr-129392 created and queued.
🤖 Automatically detected try build de0e806
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 22, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 27, 2024
[CRATER] Crater Rollup

This is a " crater rollup" of:
* rust-lang#126452
* rust-lang#128784
* rust-lang#129392
* rust-lang#129422
* rust-lang#129543

**What is a crater rollup?** It's simply a crater job that is run on all of the containing PRs *together*, and then we can set the crates list for each of these jobs to just the failures after it's done. It should cut out on the bulk of "normal" crates that do nothing and simply just take time to build.

r? `@ghost`
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 28, 2024
[CRATER] Crater Rollup

This is a " crater rollup" of:
* rust-lang#126452
* rust-lang#128784
* rust-lang#129392
* rust-lang#129422
* rust-lang#129543
* rust-lang#129604

**What is a crater rollup?** It's simply a crater job that is run on all of the containing PRs *together*, and then we can set the crates list for each of these jobs to just the failures after it's done. It should cut out on the bulk of "normal" crates that do nothing and simply just take time to build.

r? `@ghost`
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 28, 2024
[CRATER] Crater Rollup

This is a " crater rollup" of:
* rust-lang#126452
* rust-lang#128784
* rust-lang#129392
* rust-lang#129422
* rust-lang#129543
* rust-lang#129604

**What is a crater rollup?** It's simply a crater job that is run on all of the containing PRs *together*, and then we can set the crates list for each of these jobs to just the failures after it's done. It should cut out on the bulk of "normal" crates that do nothing and simply just take time to build.

r? `@ghost`
@craterbot
Copy link
Collaborator

📝 Configuration of the pr-129392 experiment changed.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@traviscross
Copy link
Contributor

@craterbot p=1

(Bump priority is this will run quickly.)

@craterbot
Copy link
Collaborator

📝 Configuration of the pr-129392 experiment changed.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🚧 Experiment pr-129392 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🎉 Experiment pr-129392 is completed!
📊 0 regressed and 1 fixed (98236 total)
📰 Open the full report.

⚠️ If you notice any spurious failure please add them to the blacklist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@rust-log-analyzer

This comment has been minimized.

@compiler-errors compiler-errors force-pushed the raw-ref-op-doesnt-diverge-but-more branch from 8e0621e to e8d5eb2 Compare October 5, 2024 23:10
@compiler-errors
Copy link
Member Author

Rebased to add a match arm for new node type.

@bors r=lcnr

@bors
Copy link
Contributor

bors commented Oct 5, 2024

📌 Commit e8d5eb2 has been approved by lcnr

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 5, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Oct 6, 2024
…verge-but-more, r=lcnr

Do not consider match/let/ref of place that evaluates to `!` to diverge, disallow coercions from them too

Fixes rust-lang#117288.

This PR implements a heuristic which disables two things that are currently being performed on the HIR when we have **expressions that involve place-like expressions that point to `!`**. Specifically, it will (in certain cases explained below):

### (1.) Disable the `NeverToAny` coercion we implicitly insert for `!`.

Which fixes this inadvertent, sneaky unsoundness:

```
unsafe {
    let x: *const ! = &0 as *const u8 as *const !;
    let _: () = *x;
}
```

which is UB because currently rust emits an *implicit* NeverToAny coercion even though we really shouldn't be, since there's no read of the value pointed by `x`.

### (2.) Disable the logic which considers expression which evaluate to `!` to diverge, which affects the type returned by the containing block.

Which fixes this unsoundness:

```
fn make_up_a_value<T>() -> T {
    unsafe {
        let x: *const ! = &0 as *const u8 as *const !;
        let _ = *x;
    }
}
```

We disable these two operations **if** the expression is a place-like expression (locals, statics, field projections, index operations, and deref operations), and if the parent expression is either:
(1.) the LHS of an assignment
(2.) AddrOf
(3.) A match or let **unless** all of the *patterns consitute a read*, which is explained below:

And finally, a pattern currently is considered to constitute a read **unless** it is a wildcard, or an OR pattern. An OR pattern is considered to constitute a read if all of its subpatterns constitute a read, to remain as conservative as possible in cases like `_ | subpat` or `subpat | _`.

All other patterns are considered currently to constitute a read. Specifically, because `NeverToAny` is a coercion performed on a *value* and not a *place*, `Struct { .. }` on a `!` type must be a coercion currently, and we currently rely on this behavior to allow us to perform coercions like `let _: i32 = x;` where `x: !`.

This is already considered UB by [miri](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf3a2246433fe43fdc07d1389c276c9), but also means it does not affect the preexisting UB in this case:

```
let Struct { .. } = *never_ptr;
```

Even though it's likely up for debate since we're not actually reading any data out of the struct, it almost certainly causes inference changes which I do *NOT* want to fix in this PR.
@bors
Copy link
Contributor

bors commented Oct 6, 2024

⌛ Testing commit e8d5eb2 with merge a3e3233...

@rust-log-analyzer
Copy link
Collaborator

A job failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)

@bors
Copy link
Contributor

bors commented Oct 6, 2024

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Oct 6, 2024
@compiler-errors
Copy link
Member Author

@bors retry

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 6, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Oct 6, 2024
…iaskrgr

Rollup of 5 pull requests

Successful merges:

 - rust-lang#129392 (Do not consider match/let/ref of place that evaluates to `!` to diverge, disallow coercions from them too)
 - rust-lang#131279 (update "build/host" symlink comment)
 - rust-lang#131312 (On function and method calls in patterns, link to the book)
 - rust-lang#131315 (bootstrap: add `std_features` config)
 - rust-lang#131316 (Fix typo in primitive_docs.rs)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Oct 6, 2024
…verge-but-more, r=lcnr

Do not consider match/let/ref of place that evaluates to `!` to diverge, disallow coercions from them too

Fixes rust-lang#117288.

This PR implements a heuristic which disables two things that are currently being performed on the HIR when we have **expressions that involve place-like expressions that point to `!`**. Specifically, it will (in certain cases explained below):

### (1.) Disable the `NeverToAny` coercion we implicitly insert for `!`.

Which fixes this inadvertent, sneaky unsoundness:

```
unsafe {
    let x: *const ! = &0 as *const u8 as *const !;
    let _: () = *x;
}
```

which is UB because currently rust emits an *implicit* NeverToAny coercion even though we really shouldn't be, since there's no read of the value pointed by `x`.

### (2.) Disable the logic which considers expression which evaluate to `!` to diverge, which affects the type returned by the containing block.

Which fixes this unsoundness:

```
fn make_up_a_value<T>() -> T {
    unsafe {
        let x: *const ! = &0 as *const u8 as *const !;
        let _ = *x;
    }
}
```

We disable these two operations **if** the expression is a place-like expression (locals, statics, field projections, index operations, and deref operations), and if the parent expression is either:
(1.) the LHS of an assignment
(2.) AddrOf
(3.) A match or let **unless** all of the *patterns consitute a read*, which is explained below:

And finally, a pattern currently is considered to constitute a read **unless** it is a wildcard, or an OR pattern. An OR pattern is considered to constitute a read if all of its subpatterns constitute a read, to remain as conservative as possible in cases like `_ | subpat` or `subpat | _`.

All other patterns are considered currently to constitute a read. Specifically, because `NeverToAny` is a coercion performed on a *value* and not a *place*, `Struct { .. }` on a `!` type must be a coercion currently, and we currently rely on this behavior to allow us to perform coercions like `let _: i32 = x;` where `x: !`.

This is already considered UB by [miri](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf3a2246433fe43fdc07d1389c276c9), but also means it does not affect the preexisting UB in this case:

```
let Struct { .. } = *never_ptr;
```

Even though it's likely up for debate since we're not actually reading any data out of the struct, it almost certainly causes inference changes which I do *NOT* want to fix in this PR.
@bors
Copy link
Contributor

bors commented Oct 6, 2024

⌛ Testing commit e8d5eb2 with merge c2c0e52...

@matthiaskrgr
Copy link
Member

@bors retry
yielding to #131320

bors added a commit to rust-lang-ci/rust that referenced this pull request Oct 6, 2024
…iaskrgr

Rollup of 5 pull requests

Successful merges:

 - rust-lang#129392 (Do not consider match/let/ref of place that evaluates to `!` to diverge, disallow coercions from them too)
 - rust-lang#131279 (update "build/host" symlink comment)
 - rust-lang#131312 (On function and method calls in patterns, link to the book)
 - rust-lang#131315 (bootstrap: add `std_features` config)
 - rust-lang#131316 (Fix typo in primitive_docs.rs)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 9aaebd4 into rust-lang:master Oct 6, 2024
6 of 7 checks passed
@rustbot rustbot added this to the 1.83.0 milestone Oct 6, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Oct 6, 2024
Rollup merge of rust-lang#129392 - compiler-errors:raw-ref-op-doesnt-diverge-but-more, r=lcnr

Do not consider match/let/ref of place that evaluates to `!` to diverge, disallow coercions from them too

Fixes rust-lang#117288.

This PR implements a heuristic which disables two things that are currently being performed on the HIR when we have **expressions that involve place-like expressions that point to `!`**. Specifically, it will (in certain cases explained below):

### (1.) Disable the `NeverToAny` coercion we implicitly insert for `!`.

Which fixes this inadvertent, sneaky unsoundness:

```
unsafe {
    let x: *const ! = &0 as *const u8 as *const !;
    let _: () = *x;
}
```

which is UB because currently rust emits an *implicit* NeverToAny coercion even though we really shouldn't be, since there's no read of the value pointed by `x`.

### (2.) Disable the logic which considers expression which evaluate to `!` to diverge, which affects the type returned by the containing block.

Which fixes this unsoundness:

```
fn make_up_a_value<T>() -> T {
    unsafe {
        let x: *const ! = &0 as *const u8 as *const !;
        let _ = *x;
    }
}
```

We disable these two operations **if** the expression is a place-like expression (locals, statics, field projections, index operations, and deref operations), and if the parent expression is either:
(1.) the LHS of an assignment
(2.) AddrOf
(3.) A match or let **unless** all of the *patterns consitute a read*, which is explained below:

And finally, a pattern currently is considered to constitute a read **unless** it is a wildcard, or an OR pattern. An OR pattern is considered to constitute a read if all of its subpatterns constitute a read, to remain as conservative as possible in cases like `_ | subpat` or `subpat | _`.

All other patterns are considered currently to constitute a read. Specifically, because `NeverToAny` is a coercion performed on a *value* and not a *place*, `Struct { .. }` on a `!` type must be a coercion currently, and we currently rely on this behavior to allow us to perform coercions like `let _: i32 = x;` where `x: !`.

This is already considered UB by [miri](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf3a2246433fe43fdc07d1389c276c9), but also means it does not affect the preexisting UB in this case:

```
let Struct { .. } = *never_ptr;
```

Even though it's likely up for debate since we're not actually reading any data out of the struct, it almost certainly causes inference changes which I do *NOT* want to fix in this PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. F-never_type `#![feature(never_type)]` finished-final-comment-period The final comment period is finished for this PR / Issue. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-types Relevant to the types team, which will review and decide on the PR/issue. to-announce Announce this issue on triage meeting
Projects
None yet
Development

Successfully merging this pull request may close these issues.

incorrect UB when when a ! place is constructed (but not loaded!)