-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
never_patterns: Count !
bindings as diverging
#120104
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
tests/ui/rfcs/rfc-0000-never_patterns/120240-async-fn-never-arg.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// edition: 2018 | ||
// known-bug: #120240 | ||
#![feature(never_patterns)] | ||
#![allow(incomplete_features)] | ||
|
||
fn main() {} | ||
|
||
enum Void {} | ||
|
||
// Divergence is not detected. | ||
async fn async_never(!: Void) -> ! {} // gives an error | ||
|
||
// Divergence is detected | ||
async fn async_let(x: Void) -> ! { | ||
let ! = x; | ||
} |
12 changes: 12 additions & 0 deletions
12
tests/ui/rfcs/rfc-0000-never_patterns/120240-async-fn-never-arg.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
error[E0308]: mismatched types | ||
--> $DIR/120240-async-fn-never-arg.rs:11:36 | ||
| | ||
LL | async fn async_never(!: Void) -> ! {} // gives an error | ||
| ^^ expected `!`, found `()` | ||
| | ||
= note: expected type `!` | ||
found unit type `()` | ||
|
||
error: aborting due to 1 previous error | ||
|
||
For more information about this error, try `rustc --explain E0308`. |
36 changes: 36 additions & 0 deletions
36
tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#![feature(never_patterns)] | ||
#![allow(incomplete_features)] | ||
#![deny(unreachable_patterns)] | ||
#![deny(unreachable_code)] | ||
|
||
fn main() {} | ||
|
||
enum Void {} | ||
|
||
fn never_arg(!: Void) -> u32 { | ||
println!(); | ||
//~^ ERROR unreachable statement | ||
} | ||
|
||
fn ref_never_arg(&!: &Void) -> u32 { | ||
println!(); | ||
//~^ ERROR unreachable statement | ||
} | ||
|
||
fn never_let() -> u32 { | ||
let ptr: *const Void = std::ptr::null(); | ||
unsafe { | ||
let ! = *ptr; | ||
} | ||
println!(); | ||
//~^ ERROR unreachable statement | ||
} | ||
|
||
fn never_match() -> u32 { | ||
let ptr: *const Void = std::ptr::null(); | ||
unsafe { | ||
match *ptr { ! }; | ||
} | ||
println!(); | ||
//~^ ERROR unreachable statement | ||
} |
49 changes: 49 additions & 0 deletions
49
tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
error: unreachable statement | ||
--> $DIR/diverge-causes-unreachable-code.rs:11:5 | ||
| | ||
LL | fn never_arg(!: Void) -> u32 { | ||
| - any code following a never pattern is unreachable | ||
LL | println!(); | ||
| ^^^^^^^^^^ unreachable statement | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/diverge-causes-unreachable-code.rs:4:9 | ||
| | ||
LL | #![deny(unreachable_code)] | ||
| ^^^^^^^^^^^^^^^^ | ||
= note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
error: unreachable statement | ||
--> $DIR/diverge-causes-unreachable-code.rs:16:5 | ||
| | ||
LL | fn ref_never_arg(&!: &Void) -> u32 { | ||
| -- any code following a never pattern is unreachable | ||
LL | println!(); | ||
| ^^^^^^^^^^ unreachable statement | ||
| | ||
= note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
error: unreachable statement | ||
--> $DIR/diverge-causes-unreachable-code.rs:25:5 | ||
| | ||
LL | let ! = *ptr; | ||
| - any code following a never pattern is unreachable | ||
LL | } | ||
LL | println!(); | ||
| ^^^^^^^^^^ unreachable statement | ||
| | ||
= note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
error: unreachable statement | ||
--> $DIR/diverge-causes-unreachable-code.rs:34:5 | ||
| | ||
LL | match *ptr { ! }; | ||
| ---------------- any code following this `match` expression is unreachable, as all arms diverge | ||
LL | } | ||
LL | println!(); | ||
| ^^^^^^^^^^ unreachable statement | ||
| | ||
= note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
error: aborting due to 4 previous errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#![feature(never_patterns)] | ||
#![feature(let_chains)] | ||
#![allow(incomplete_features)] | ||
#![deny(unreachable_patterns)] | ||
|
||
fn main() {} | ||
|
||
enum Void {} | ||
|
||
// Contrast with `./diverges.rs`: merely having an empty type around isn't enough to diverge. | ||
|
||
fn wild_void(_: Void) -> u32 {} | ||
//~^ ERROR: mismatched types | ||
|
||
fn wild_let() -> u32 { | ||
let ptr: *const Void = std::ptr::null(); | ||
unsafe { | ||
//~^ ERROR: mismatched types | ||
let _ = *ptr; | ||
} | ||
} | ||
|
||
fn wild_match() -> u32 { | ||
let ptr: *const Void = std::ptr::null(); | ||
unsafe { | ||
match *ptr { | ||
_ => {} //~ ERROR: mismatched types | ||
} | ||
} | ||
} | ||
|
||
fn binding_void(_x: Void) -> u32 {} | ||
//~^ ERROR: mismatched types | ||
|
||
fn binding_let() -> u32 { | ||
let ptr: *const Void = std::ptr::null(); | ||
unsafe { | ||
//~^ ERROR: mismatched types | ||
let _x = *ptr; | ||
} | ||
} | ||
|
||
fn binding_match() -> u32 { | ||
let ptr: *const Void = std::ptr::null(); | ||
unsafe { | ||
match *ptr { | ||
_x => {} //~ ERROR: mismatched types | ||
} | ||
} | ||
} | ||
|
||
// Don't confuse this with a `let !` statement. | ||
fn let_chain(x: Void) -> u32 { | ||
if let true = true && let ! = x {} | ||
//~^ ERROR: mismatched types | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
error[E0308]: mismatched types | ||
--> $DIR/diverges-not.rs:12:26 | ||
| | ||
LL | fn wild_void(_: Void) -> u32 {} | ||
| --------- ^^^ expected `u32`, found `()` | ||
| | | ||
| implicitly returns `()` as its body has no tail or `return` expression | ||
|
||
error[E0308]: mismatched types | ||
--> $DIR/diverges-not.rs:17:5 | ||
| | ||
LL | / unsafe { | ||
LL | | | ||
LL | | let _ = *ptr; | ||
LL | | } | ||
| |_____^ expected `u32`, found `()` | ||
|
||
error[E0308]: mismatched types | ||
--> $DIR/diverges-not.rs:27:18 | ||
| | ||
LL | _ => {} | ||
| ^^ expected `u32`, found `()` | ||
|
||
error[E0308]: mismatched types | ||
--> $DIR/diverges-not.rs:32:30 | ||
| | ||
LL | fn binding_void(_x: Void) -> u32 {} | ||
| ------------ ^^^ expected `u32`, found `()` | ||
| | | ||
| implicitly returns `()` as its body has no tail or `return` expression | ||
|
||
error[E0308]: mismatched types | ||
--> $DIR/diverges-not.rs:37:5 | ||
| | ||
LL | / unsafe { | ||
LL | | | ||
LL | | let _x = *ptr; | ||
LL | | } | ||
| |_____^ expected `u32`, found `()` | ||
|
||
error[E0308]: mismatched types | ||
--> $DIR/diverges-not.rs:47:19 | ||
| | ||
LL | _x => {} | ||
| ^^ expected `u32`, found `()` | ||
|
||
error[E0308]: mismatched types | ||
--> $DIR/diverges-not.rs:54:37 | ||
| | ||
LL | if let true = true && let ! = x {} | ||
| ^^ expected `u32`, found `()` | ||
|
||
error: aborting due to 7 previous errors | ||
|
||
For more information about this error, try `rustc --explain E0308`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// check-pass | ||
// edition: 2018 | ||
#![feature(never_patterns)] | ||
#![allow(incomplete_features)] | ||
#![deny(unreachable_patterns)] | ||
|
||
fn main() {} | ||
|
||
enum Void {} | ||
|
||
// A never pattern alone diverges. | ||
|
||
fn never_arg(!: Void) -> ! {} | ||
|
||
fn never_arg_returns_anything<T>(!: Void) -> T {} | ||
|
||
fn ref_never_arg(&!: &Void) -> ! {} | ||
|
||
fn never_let() -> ! { | ||
let ptr: *const Void = std::ptr::null(); | ||
unsafe { | ||
let ! = *ptr; | ||
} | ||
} | ||
|
||
fn never_match() -> ! { | ||
let ptr: *const Void = std::ptr::null(); | ||
unsafe { | ||
match *ptr { ! }; | ||
} | ||
// Ensures this typechecks because of divergence and not the type of the match expression. | ||
println!(); | ||
} | ||
|
||
// Note: divergence is not detected for async fns when the `!` is in the argument (#120240). | ||
async fn async_let(x: Void) -> ! { | ||
let ! = x; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so I understand what this is doing, but I'm not certain if I understand why it's doing this. Is this to make some test pass?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
old_diverges
bit you mean? That's because if an expression diverges then it typechecks as!
. So before we check a new expression we hide the previous state ofdiverges
. Else in the following1
would be given type!
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, why you added this
is_whole_body
thing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. Well the presence of
!
in the arguments should affect the type of the function body, but not the type of any other expression in the function. So I only look atfunction_diverges_because_of_empty_arguments
ifexpr
is the function bodyThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The obvious thing to do would have been to set
fcx.diverges
directly while checking the function arguments, but that resulted in:as well as a type error because the mechanism I explained above ensures that the type of the
{}
was not affected by previousdiverges
so the body is typechecked as()
x)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I could use
diverges
directly instead of addingfunction_diverges_because_of_empty_arguments
🤔 . But I still needis_whole_body
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand why this is implemented the way it is, though I'm still chewing on whether there's a better approach here. My main concern is the non-locality -- it relies on the fact that when type checking the top expression of the body, we'll always flow through
check_expr_with_expectation_and_args
first. While I believe typeck will remain in that way, it seems somewhat easy for a refactoring of control flow to make this no longer true always.Ideally, we'd treat the initial check_expr call for the top expr of a body in a privileged way, and do this logic there, but currently all of that is tangled into the coercion code that we use to check the body as if it were an implicit return value. Sigh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an aside, could you please double check that this still behaves the right way for
async fn
?implicitly desugars to:
so it should behave correctly afaict, but I think we should have tests for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not a fan of the non-locality and the mutability of my implementation but I felt way out of my depth to refactor anything.
Good catch on the async fn, it in fact does not work. I'm surprised because I looked at all the call sites of
check_pat_top
and they all do something with never patterns except expression-let
sThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird, it does desugar to what you said, but doesn't seem to detect the divergence