Skip to content

Commit

Permalink
Rollup merge of #93313 - tmiasko:uninhabited, r=tmandry
Browse files Browse the repository at this point in the history
Check if call return type is visibly uninhabited when building MIR

The main motivation behind the change is to expose information about diverging
calls to the generator transform and match the precision of drop range tracking
which already understands that call expressions with visibly uninhabited types
diverges.

This change should also accept strictly more programs than before. That is
programs that were previously rejected due to errors raised by control-flow
sensitive checks in a code that is no longer considered reachable.

Fixes #93161.
  • Loading branch information
Dylan-DPC authored Apr 20, 2022
2 parents d39864d + 6f8a1ee commit 38e3f52
Show file tree
Hide file tree
Showing 19 changed files with 167 additions and 98 deletions.
13 changes: 9 additions & 4 deletions compiler/rustc_mir_build/src/build/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
func: fun,
args,
cleanup: None,
// FIXME(varkor): replace this with an uninhabitedness-based check.
// This requires getting access to the current module to call
// `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
destination: if expr.ty.is_never() {
// The presence or absence of a return edge affects control-flow sensitive
// MIR checks and ultimately whether code is accepted or not. We can only
// omit the return edge if a return type is visibly uninhabited to a module
// that makes the call.
destination: if this.tcx.is_ty_uninhabited_from(
this.parent_module,
expr.ty,
this.param_env,
) {
None
} else {
Some((destination, success))
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ struct Builder<'a, 'tcx> {

def_id: DefId,
hir_id: hir::HirId,
parent_module: DefId,
check_overflow: bool,
fn_span: Span,
arg_count: usize,
Expand Down Expand Up @@ -807,15 +808,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);

let lint_level = LintLevel::Explicit(hir_id);
let param_env = tcx.param_env(def.did);
let mut builder = Builder {
thir,
tcx,
infcx,
typeck_results: tcx.typeck_opt_const_arg(def),
region_scope_tree: tcx.region_scope_tree(def.did),
param_env: tcx.param_env(def.did),
param_env,
def_id: def.did.to_def_id(),
hir_id,
parent_module: tcx.parent_module(hir_id).to_def_id(),
check_overflow,
cfg: CFG { basic_blocks: IndexVec::new() },
fn_span: span,
Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/set-discriminant-invalid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl IntoError<Error> for Api
#[no_mangle]
fn into_error(self, error: Self::Source) -> Error {
Error::Api {
source: (|v| v)(error),
source: error,
}
}
}
Expand Down
49 changes: 24 additions & 25 deletions src/test/mir-opt/inline/inline_diverging.h.Inline.diff
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@
fn h() -> () {
let mut _0: (); // return place in scope 0 at $DIR/inline-diverging.rs:21:12: 21:12
let _1: (!, !); // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+ let mut _2: fn() -> ! {sleep}; // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+ let mut _9: (); // in scope 0 at $DIR/inline-diverging.rs:27:13: 27:16
+ let mut _10: (); // in scope 0 at $DIR/inline-diverging.rs:28:13: 28:16
+ let mut _2: (!, !); // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+ let mut _3: fn() -> ! {sleep}; // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+ let mut _10: (); // in scope 0 at $DIR/inline-diverging.rs:27:13: 27:16
+ let mut _11: (); // in scope 0 at $DIR/inline-diverging.rs:28:13: 28:16
+ scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) { // at $DIR/inline-diverging.rs:22:5: 22:22
+ debug f => _2; // in scope 1 at $DIR/inline-diverging.rs:26:36: 26:37
+ let _3: !; // in scope 1 at $DIR/inline-diverging.rs:27:9: 27:10
+ let mut _4: &fn() -> ! {sleep}; // in scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
+ let mut _6: &fn() -> ! {sleep}; // in scope 1 at $DIR/inline-diverging.rs:28:13: 28:14
+ let mut _7: !; // in scope 1 at $DIR/inline-diverging.rs:29:6: 29:7
+ let mut _8: !; // in scope 1 at $DIR/inline-diverging.rs:29:9: 29:10
+ debug f => _3; // in scope 1 at $DIR/inline-diverging.rs:26:36: 26:37
+ let _4: !; // in scope 1 at $DIR/inline-diverging.rs:27:9: 27:10
+ let mut _5: &fn() -> ! {sleep}; // in scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
+ let mut _7: &fn() -> ! {sleep}; // in scope 1 at $DIR/inline-diverging.rs:28:13: 28:14
+ let mut _8: !; // in scope 1 at $DIR/inline-diverging.rs:29:6: 29:7
+ let mut _9: !; // in scope 1 at $DIR/inline-diverging.rs:29:9: 29:10
+ scope 2 {
+ debug a => _3; // in scope 2 at $DIR/inline-diverging.rs:27:9: 27:10
+ let _5: !; // in scope 2 at $DIR/inline-diverging.rs:28:9: 28:10
+ debug a => _4; // in scope 2 at $DIR/inline-diverging.rs:27:9: 27:10
+ let _6: !; // in scope 2 at $DIR/inline-diverging.rs:28:9: 28:10
+ scope 3 {
+ debug b => _5; // in scope 3 at $DIR/inline-diverging.rs:28:9: 28:10
+ debug b => _6; // in scope 3 at $DIR/inline-diverging.rs:28:9: 28:10
+ }
+ scope 6 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) { // at $DIR/inline-diverging.rs:28:13: 28:16
+ scope 7 (inlined sleep) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL
Expand All @@ -33,27 +34,25 @@

bb0: {
StorageLive(_1); // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
- _1 = call_twice::<!, fn() -> ! {sleep}>(sleep) -> bb1; // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
- call_twice::<!, fn() -> ! {sleep}>(sleep); // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+ StorageLive(_2); // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+ _2 = sleep; // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+ StorageLive(_3); // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
+ _3 = sleep; // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
// mir::Constant
- // + span: $DIR/inline-diverging.rs:22:5: 22:15
- // + literal: Const { ty: fn(fn() -> ! {sleep}) -> (!, !) {call_twice::<!, fn() -> ! {sleep}>}, val: Value(Scalar(<ZST>)) }
- // mir::Constant
// + span: $DIR/inline-diverging.rs:22:16: 22:21
// + literal: Const { ty: fn() -> ! {sleep}, val: Value(Scalar(<ZST>)) }
+ StorageLive(_3); // scope 1 at $DIR/inline-diverging.rs:27:9: 27:10
+ StorageLive(_4); // scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
+ _4 = &_2; // scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
+ StorageLive(_9); // scope 1 at $DIR/inline-diverging.rs:27:13: 27:16
+ _9 = const (); // scope 1 at $DIR/inline-diverging.rs:27:13: 27:16
+ StorageLive(_4); // scope 1 at $DIR/inline-diverging.rs:27:9: 27:10
+ StorageLive(_5); // scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
+ _5 = &_3; // scope 1 at $DIR/inline-diverging.rs:27:13: 27:14
+ StorageLive(_10); // scope 1 at $DIR/inline-diverging.rs:27:13: 27:16
+ _10 = const (); // scope 1 at $DIR/inline-diverging.rs:27:13: 27:16
+ goto -> bb1; // scope 5 at $DIR/inline-diverging.rs:39:5: 39:12
}

bb1: {
- StorageDead(_1); // scope 0 at $DIR/inline-diverging.rs:22:22: 22:23
- _0 = const (); // scope 0 at $DIR/inline-diverging.rs:21:12: 23:2
- return; // scope 0 at $DIR/inline-diverging.rs:23:2: 23:2
+ }
+
+ bb1: {
+ goto -> bb1; // scope 5 at $DIR/inline-diverging.rs:39:5: 39:12
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/mir-opt/issue_72181_1.main.mir_map.0.mir
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn main() -> () {
StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
StorageLive(_3); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43
_3 = (); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43
_2 = transmute::<(), Void>(move _3) -> [return: bb1, unwind: bb4]; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44
transmute::<(), Void>(move _3) -> bb4; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44
// mir::Constant
// + span: $DIR/issue-72181-1.rs:17:9: 17:40
// + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Void {transmute::<(), Void>}, val: Value(Scalar(<ZST>)) }
Expand Down
22 changes: 6 additions & 16 deletions src/test/ui/consts/const-eval/ub-enum.32bit.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -119,27 +119,17 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran
78 00 00 00 ff ff ff ff │ x.......
}

error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:92:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:92:77
|
LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<enum-variant(Ok)>.0.1: encountered a value of uninhabited type Never
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
00 00 00 00 00 00 00 00 │ ........
}
| ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type

error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:94:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:94:77
|
LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<enum-variant(Ok)>.0.1: encountered a value of the never type `!`
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
00 00 00 00 00 00 00 00 │ ........
}
| ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type

error: aborting due to 13 previous errors

Expand Down
22 changes: 6 additions & 16 deletions src/test/ui/consts/const-eval/ub-enum.64bit.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -119,27 +119,17 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran
78 00 00 00 ff ff ff ff │ x.......
}

error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:92:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:92:77
|
LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<enum-variant(Ok)>.0.1: encountered a value of uninhabited type Never
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
00 00 00 00 00 00 00 00 │ ........
}
| ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type

error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:94:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:94:77
|
LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<enum-variant(Ok)>.0.1: encountered a value of the never type `!`
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
00 00 00 00 00 00 00 00 │ ........
}
| ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type

error: aborting due to 13 previous errors

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/const-eval/ub-enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute
// All variants are uninhabited but also have data.
// Use `0` as constant to make behavior endianess-independent.
const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
//~^ ERROR is undefined behavior
//~^ ERROR evaluation of constant value failed
const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) };
//~^ ERROR is undefined behavior
//~^ ERROR evaluation of constant value failed

fn main() {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@ LL | unsafe { std::mem::transmute(()) }
LL | const FOO: [Empty; 3] = [foo(); 3];
| ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:13:26

error[E0080]: it is undefined behavior to use this value
--> $DIR/validate_uninhabited_zsts.rs:16:1
error[E0080]: evaluation of constant value failed
--> $DIR/validate_uninhabited_zsts.rs:16:35
|
LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at [0]: encountered a value of uninhabited type Empty
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 0, align: 1) {}
| ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type

warning: the type `!` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:4:14
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@ LL | unsafe { std::mem::transmute(()) }
LL | const FOO: [Empty; 3] = [foo(); 3];
| ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:13:26

error[E0080]: it is undefined behavior to use this value
--> $DIR/validate_uninhabited_zsts.rs:16:1
error[E0080]: evaluation of constant value failed
--> $DIR/validate_uninhabited_zsts.rs:16:35
|
LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at [0]: encountered a value of uninhabited type Empty
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 0, align: 1) {}
| ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type

warning: the type `!` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:4:14
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const FOO: [Empty; 3] = [foo(); 3];

#[warn(const_err)]
const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
//~^ ERROR it is undefined behavior to use this value
//~^ ERROR evaluation of constant value failed
//~| WARN the type `Empty` does not permit zero-initialization

fn main() {
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/generator/issue-93161.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// edition:2021
// run-pass
// compile-flags: -Zdrop-tracking

#![feature(never_type)]

Expand Down Expand Up @@ -32,7 +33,7 @@ fn never() -> Never {
}

async fn includes_never(crash: bool, x: u32) -> u32 {
let mut result = async { x * x }.await;
let result = async { x * x }.await;
if !crash {
return result;
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/statics/uninhabited-static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ extern {

static VOID2: Void = unsafe { std::mem::transmute(()) }; //~ ERROR static of uninhabited type
//~| WARN: previously accepted
//~| ERROR undefined behavior to use this value
//~| ERROR could not evaluate static initializer
//~| WARN: type `Void` does not permit zero-initialization
static NEVER2: Void = unsafe { std::mem::transmute(()) }; //~ ERROR static of uninhabited type
//~| WARN: previously accepted
//~| ERROR undefined behavior to use this value
//~| ERROR could not evaluate static initializer
//~| WARN: type `Void` does not permit zero-initialization

fn main() {}
18 changes: 6 additions & 12 deletions src/test/ui/statics/uninhabited-static.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,17 @@ LL | static NEVER2: Void = unsafe { std::mem::transmute(()) };
= note: for more information, see issue #74840 <https://github.com/rust-lang/rust/issues/74840>
= note: uninhabited statics cannot be initialized, and any access would be an immediate error

error[E0080]: it is undefined behavior to use this value
--> $DIR/uninhabited-static.rs:12:1
error[E0080]: could not evaluate static initializer
--> $DIR/uninhabited-static.rs:12:31
|
LL | static VOID2: Void = unsafe { std::mem::transmute(()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Void
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 0, align: 1) {}
| ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type

error[E0080]: it is undefined behavior to use this value
--> $DIR/uninhabited-static.rs:16:1
error[E0080]: could not evaluate static initializer
--> $DIR/uninhabited-static.rs:16:32
|
LL | static NEVER2: Void = unsafe { std::mem::transmute(()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Void
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 0, align: 1) {}
| ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type

warning: the type `Void` does not permit zero-initialization
--> $DIR/uninhabited-static.rs:12:31
Expand Down
29 changes: 29 additions & 0 deletions src/test/ui/uninhabited/privately-uninhabited-mir-call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Verifies that MIR building for a call expression respects
// privacy when checking if a call return type is uninhabited.

pub mod widget {
enum Unimplemented {}
pub struct Widget(Unimplemented);

impl Widget {
pub fn new() -> Widget {
todo!();
}
}

pub fn f() {
let x: &mut u32;
Widget::new();
// Ok. Widget type returned from new is known to be uninhabited
// and the following code is considered unreachable.
*x = 1;
}
}

fn main() {
let y: &mut u32;
widget::Widget::new();
// Error. Widget type is not known to be uninhabited here,
// so the following code is considered reachable.
*y = 2; //~ ERROR use of possibly-uninitialized variable
}
Loading

0 comments on commit 38e3f52

Please sign in to comment.