-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #85556 - FabianWolff:issue-85071, r=estebank,jackh726
Warn about unreachable code following an expression with an uninhabited type This pull request fixes #85071. The issue is that liveness analysis currently is "smarter" than reachability analysis when it comes to detecting uninhabited types: Unreachable code is detected during type checking, where full type information is not yet available. Therefore, the check for type inhabitedness is quite crude: https://github.com/rust-lang/rust/blob/fc81ad22c453776de16acf9938976930cf8c9401/compiler/rustc_typeck/src/check/expr.rs#L202-L205 i.e. it only checks for `!`, but not other, non-trivially uninhabited types, such as empty enums, structs containing an uninhabited type, etc. By contrast, liveness analysis, which runs after type checking, can benefit from the more sophisticated `tcx.is_ty_uninhabited_from()`: https://github.com/rust-lang/rust/blob/fc81ad22c453776de16acf9938976930cf8c9401/compiler/rustc_passes/src/liveness.rs#L981 https://github.com/rust-lang/rust/blob/fc81ad22c453776de16acf9938976930cf8c9401/compiler/rustc_passes/src/liveness.rs#L996 This can lead to confusing warnings when a variable is reported as unused, but the use of the variable is not reported as unreachable. For instance: ```rust enum Foo {} fn f() -> Foo {todo!()} fn main() { let x = f(); let _ = x; } ``` currently leads to ``` warning: unused variable: `x` --> t1.rs:5:9 | 5 | let x = f(); | ^ help: if this is intentional, prefix it with an underscore: `_x` | = note: `#[warn(unused_variables)]` on by default warning: 1 warning emitted ``` which is confusing, because `x` _appears_ to be used in line 6. With my changes, I get: ``` warning: unreachable expression --> t1.rs:6:13 | 5 | let x = f(); | --- any code following this expression is unreachable 6 | let _ = x; | ^ unreachable expression | = note: `#[warn(unreachable_code)]` on by default note: this expression has type `Foo`, which is uninhabited --> t1.rs:5:13 | 5 | let x = f(); | ^^^ warning: unused variable: `x` --> t1.rs:5:9 | 5 | let x = f(); | ^ help: if this is intentional, prefix it with an underscore: `_x` | = note: `#[warn(unused_variables)]` on by default warning: 2 warnings emitted ``` My implementation is slightly inelegant because unreachable code warnings can now be issued in two different places (during type checking and during liveness analysis), but I think it is the solution with the least amount of unnecessary code duplication, given that the new warning integrates nicely with liveness analysis, where unreachable code is already implicitly detected for the purpose of finding unused variables.
- Loading branch information
Showing
5 changed files
with
181 additions
and
31 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// A slight variation of issue-85071.rs. Here, a method is called instead | ||
// of a function, and the warning is about an unreachable definition | ||
// instead of an unreachable expression. | ||
|
||
// check-pass | ||
|
||
#![warn(unused_variables,unreachable_code)] | ||
|
||
enum Foo {} | ||
|
||
struct S; | ||
impl S { | ||
fn f(&self) -> Foo {todo!()} | ||
} | ||
|
||
fn main() { | ||
let s = S; | ||
let x = s.f(); | ||
//~^ WARNING: unused variable: `x` | ||
let _y = x; | ||
//~^ WARNING: unreachable definition | ||
} |
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,34 @@ | ||
warning: unreachable definition | ||
--> $DIR/issue-85071-2.rs:20:9 | ||
| | ||
LL | let x = s.f(); | ||
| ----- any code following this expression is unreachable | ||
LL | | ||
LL | let _y = x; | ||
| ^^ unreachable definition | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/issue-85071-2.rs:7:26 | ||
| | ||
LL | #![warn(unused_variables,unreachable_code)] | ||
| ^^^^^^^^^^^^^^^^ | ||
note: this expression has type `Foo`, which is uninhabited | ||
--> $DIR/issue-85071-2.rs:18:13 | ||
| | ||
LL | let x = s.f(); | ||
| ^^^^^ | ||
|
||
warning: unused variable: `x` | ||
--> $DIR/issue-85071-2.rs:18:9 | ||
| | ||
LL | let x = s.f(); | ||
| ^ help: if this is intentional, prefix it with an underscore: `_x` | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/issue-85071-2.rs:7:9 | ||
| | ||
LL | #![warn(unused_variables,unreachable_code)] | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
warning: 2 warnings emitted | ||
|
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,19 @@ | ||
// Checks that an unreachable code warning is emitted when an expression is | ||
// preceded by an expression with an uninhabited type. Previously, the | ||
// variable liveness analysis was "smarter" than the reachability analysis | ||
// in this regard, which led to confusing "unused variable" warnings | ||
// without an accompanying explanatory "unreachable expression" warning. | ||
|
||
// check-pass | ||
|
||
#![warn(unused_variables,unreachable_code)] | ||
|
||
enum Foo {} | ||
fn f() -> Foo {todo!()} | ||
|
||
fn main() { | ||
let x = f(); | ||
//~^ WARNING: unused variable: `x` | ||
let _ = x; | ||
//~^ WARNING: unreachable expression | ||
} |
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,34 @@ | ||
warning: unreachable expression | ||
--> $DIR/issue-85071.rs:17:13 | ||
| | ||
LL | let x = f(); | ||
| --- any code following this expression is unreachable | ||
LL | | ||
LL | let _ = x; | ||
| ^ unreachable expression | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/issue-85071.rs:9:26 | ||
| | ||
LL | #![warn(unused_variables,unreachable_code)] | ||
| ^^^^^^^^^^^^^^^^ | ||
note: this expression has type `Foo`, which is uninhabited | ||
--> $DIR/issue-85071.rs:15:13 | ||
| | ||
LL | let x = f(); | ||
| ^^^ | ||
|
||
warning: unused variable: `x` | ||
--> $DIR/issue-85071.rs:15:9 | ||
| | ||
LL | let x = f(); | ||
| ^ help: if this is intentional, prefix it with an underscore: `_x` | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/issue-85071.rs:9:9 | ||
| | ||
LL | #![warn(unused_variables,unreachable_code)] | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
warning: 2 warnings emitted | ||
|