diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d78d7273a36e..0b8ee3c1fb92 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1082,11 +1082,19 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, GatherLocalsVisitor { fcx: &fcx, parent_id: outer_node_id, }.visit_body(body); // Add formal parameters. + let mut uninhabited_args = vec![]; for (arg_ty, arg) in fn_sig.inputs().iter().zip(&body.arguments) { // Check the pattern. fcx.check_pat_walk(&arg.pat, arg_ty, ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true); + // If any of a function's parameters have a type that is uninhabited, then it + // cannot be called (because its arguments cannot be constructed). + let module = fcx.tcx.hir().get_module_parent(fn_id); + if fcx.tcx.is_ty_uninhabited_from(module, arg_ty) { + uninhabited_args.push(arg); + } + // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings // for simple cases like `fn foo(x: Trait)`, @@ -1098,6 +1106,28 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, fcx.write_ty(arg.hir_id, arg_ty); } + if !uninhabited_args.is_empty() { + match fcx.tcx.hir().find(fcx.tcx.hir().get_parent(fn_id)) { + Some(Node::Item(&hir::Item { node: ItemKind::Impl(..), .. })) => { + // We only want to warn for functions with parameters of uninhabited types if they + // are not required (e.g. trait implementations). In the future, such + // implementations may be unnecessary, but for now they are required. + } + _ => { + let mut err = fcx.tcx.struct_span_lint_node( + lint::builtin::UNREACHABLE_CODE, + fn_id, + fcx.tcx.hir().span(fn_id), + "functions with parameters of uninhabited types are uncallable", + ); + for arg in uninhabited_args { + err.span_label(arg.pat.span, format!("this parameter has an uninhabited type")); + } + err.emit(); + } + } + } + let fn_hir_id = fcx.tcx.hir().node_to_hir_id(fn_id); inherited.tables.borrow_mut().liberated_fn_sigs_mut().insert(fn_hir_id, fn_sig); diff --git a/src/libstd/sys/cloudabi/shims/fs.rs b/src/libstd/sys/cloudabi/shims/fs.rs index 3af10a74c7d4..d74e5e2ea86d 100644 --- a/src/libstd/sys/cloudabi/shims/fs.rs +++ b/src/libstd/sys/cloudabi/shims/fs.rs @@ -251,6 +251,9 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } +// `FilePermissions` is uninhabited in CloudABI, so this function is +// uncallable (but necessary for the public interface). +#[allow(unreachable_code)] pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } diff --git a/src/libstd/sys/cloudabi/shims/pipe.rs b/src/libstd/sys/cloudabi/shims/pipe.rs index 30ef79dd769b..885ebdfa5377 100644 --- a/src/libstd/sys/cloudabi/shims/pipe.rs +++ b/src/libstd/sys/cloudabi/shims/pipe.rs @@ -17,6 +17,9 @@ impl AnonPipe { } } +// `AnonPipe` is uninhabited in CloudABI, so this function is +// uncallable (but necessary for the public interface). +#[allow(unreachable_code)] pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { match p1.0 {} } diff --git a/src/libstd/sys/sgx/fs.rs b/src/libstd/sys/sgx/fs.rs index 8b1c4476bc41..b8cc2f889636 100644 --- a/src/libstd/sys/sgx/fs.rs +++ b/src/libstd/sys/sgx/fs.rs @@ -253,6 +253,9 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } +// `FilePermissions` is uninhabited in SGX, so this function is +// uncallable (but necessary for the public interface). +#[allow(unreachable_code)] pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } diff --git a/src/libstd/sys/sgx/pipe.rs b/src/libstd/sys/sgx/pipe.rs index ac48a6dc0332..08af256cde05 100644 --- a/src/libstd/sys/sgx/pipe.rs +++ b/src/libstd/sys/sgx/pipe.rs @@ -17,6 +17,9 @@ impl AnonPipe { } } +// `AnonPipe` is uninhabited in SGX, so this function is +// uncallable (but necessary for the public interface). +#[allow(unreachable_code)] pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, diff --git a/src/libstd/sys/wasm/fs.rs b/src/libstd/sys/wasm/fs.rs index 8b1c4476bc41..e302c8dd914a 100644 --- a/src/libstd/sys/wasm/fs.rs +++ b/src/libstd/sys/wasm/fs.rs @@ -253,6 +253,9 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } +// `FilePermissions` is uninhabited in WASM, so this function is +// uncallable (but necessary for the public interface). +#[allow(unreachable_code)] pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } diff --git a/src/libstd/sys/wasm/pipe.rs b/src/libstd/sys/wasm/pipe.rs index ac48a6dc0332..b901d4a448ab 100644 --- a/src/libstd/sys/wasm/pipe.rs +++ b/src/libstd/sys/wasm/pipe.rs @@ -17,6 +17,9 @@ impl AnonPipe { } } +// `AnonPipe` is uninhabited in WASM, so this function is +// uncallable (but necessary for the public interface). +#[allow(unreachable_code)] pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, diff --git a/src/test/run-pass/drop/drop-uninhabited-enum.rs b/src/test/run-pass/drop/drop-uninhabited-enum.rs index b3566f68533b..56ada47a07ec 100644 --- a/src/test/run-pass/drop/drop-uninhabited-enum.rs +++ b/src/test/run-pass/drop/drop-uninhabited-enum.rs @@ -1,14 +1,16 @@ // run-pass #![allow(dead_code)] #![allow(unused_variables)] -// pretty-expanded FIXME #23616 +#![allow(unreachable_code)] -enum Foo { } +enum Foo {} impl Drop for Foo { fn drop(&mut self) { } } -fn foo(x: Foo) { } +fn foo() { + let _x: Foo = unimplemented!(); +} fn main() { } diff --git a/src/test/run-pass/issues/issue-3037.rs b/src/test/run-pass/issues/issue-3037.rs index ff4d32c28407..60dfef278cbf 100644 --- a/src/test/run-pass/issues/issue-3037.rs +++ b/src/test/run-pass/issues/issue-3037.rs @@ -1,16 +1,16 @@ // run-pass #![allow(dead_code)] +#![allow(unused_variables)] // pretty-expanded FIXME #23616 -#![allow(non_camel_case_types)] -enum what { } +enum Void {} -fn what_to_string(x: what) -> String -{ - match x { - } +fn void() -> Void { + unimplemented!() } -pub fn main() -{ +fn void_to_string() -> String { + match void() {} } + +pub fn main() {} diff --git a/src/test/run-pass/issues/issue-46855.stderr b/src/test/run-pass/issues/issue-46855.stderr new file mode 100644 index 000000000000..a4f913711f8d --- /dev/null +++ b/src/test/run-pass/issues/issue-46855.stderr @@ -0,0 +1,18 @@ +warning: functions with parameters of uninhabited types are uncallable + --> $DIR/issue-46855.rs:15:1 + | +LL | fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 } + | ^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this parameter has an uninhabited type + | + = note: #[warn(unreachable_code)] on by default + +warning: functions with parameters of uninhabited types are uncallable + --> $DIR/issue-46855.rs:17:1 + | +LL | fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { x } + | ^^^^^^^--------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this parameter has an uninhabited type + diff --git a/src/test/run-pass/issues/issue-50731.rs b/src/test/run-pass/issues/issue-50731.rs index 209c1e1279b5..9873b15d856c 100644 --- a/src/test/run-pass/issues/issue-50731.rs +++ b/src/test/run-pass/issues/issue-50731.rs @@ -1,6 +1,9 @@ // run-pass enum Void {} + +#[allow(unreachable_code)] fn foo(_: Result<(Void, u32), (Void, String)>) {} + fn main() { let _: fn(_) = foo; } diff --git a/src/test/run-pass/never-type-rvalues.rs b/src/test/run-pass/never-type-rvalues.rs index 2de8567924e3..604178a447ef 100644 --- a/src/test/run-pass/never-type-rvalues.rs +++ b/src/test/run-pass/never-type-rvalues.rs @@ -2,6 +2,7 @@ #![allow(dead_code)] #![allow(path_statements)] #![allow(unreachable_patterns)] +#![allow(unreachable_code)] fn never_direct(x: !) { x; diff --git a/src/test/ui/match/match-no-arms-unreachable-after.rs b/src/test/ui/match/match-no-arms-unreachable-after.rs index 8f83fd1a3180..99c3ceabdfbb 100644 --- a/src/test/ui/match/match-no-arms-unreachable-after.rs +++ b/src/test/ui/match/match-no-arms-unreachable-after.rs @@ -3,10 +3,13 @@ enum Void { } -fn foo(v: Void) { - match v { } - let x = 2; //~ ERROR unreachable +fn bar() -> Void { + unreachable!() } -fn main() { +fn foo() { + match bar() { } + let x = 2; //~ ERROR unreachable } + +fn main() {} diff --git a/src/test/ui/match/match-no-arms-unreachable-after.stderr b/src/test/ui/match/match-no-arms-unreachable-after.stderr index bd136245c1ae..fcca5262c6b7 100644 --- a/src/test/ui/match/match-no-arms-unreachable-after.stderr +++ b/src/test/ui/match/match-no-arms-unreachable-after.stderr @@ -1,5 +1,5 @@ error: unreachable statement - --> $DIR/match-no-arms-unreachable-after.rs:8:5 + --> $DIR/match-no-arms-unreachable-after.rs:12:5 | LL | let x = 2; //~ ERROR unreachable | ^^^^^^^^^^ diff --git a/src/test/ui/reachable/expr_call.rs b/src/test/ui/reachable/expr_call.rs index 1eaa96c3ce77..cf9523cc847f 100644 --- a/src/test/ui/reachable/expr_call.rs +++ b/src/test/ui/reachable/expr_call.rs @@ -4,9 +4,9 @@ #![allow(dead_code)] #![deny(unreachable_code)] -fn foo(x: !, y: usize) { } +fn foo(x: (), y: usize) {} -fn bar(x: !) { } +fn bar(x: ()) {} fn a() { // the `22` is unreachable: diff --git a/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs new file mode 100644 index 000000000000..f3a3d917dbbb --- /dev/null +++ b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs @@ -0,0 +1,41 @@ +#![deny(unreachable_code)] + +enum Void {} + +mod hide { + pub struct PrivatelyUninhabited(::Void); + + pub struct PubliclyUninhabited(pub ::Void); +} + +// Check that functions with (publicly) uninhabited parameters trigger a lint. + +fn foo(a: (), b: Void) { //~ ERROR functions with parameters of uninhabited types are uncallable + a +} + +fn bar(a: (), b: hide::PrivatelyUninhabited) { // ok + a +} + +fn baz(a: (), b: hide::PubliclyUninhabited) { + //~^ ERROR functions with parameters of uninhabited types are uncallable + a +} + +// Check that trait methods with uninhabited parameters do not trigger a lint +// (at least for now). + +trait Foo { + fn foo(a: Self); + + fn bar(b: Void); +} + +impl Foo for Void { + fn foo(a: Void) {} // ok + + fn bar(b: Void) {} // ok +} + +fn main() {} diff --git a/src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr new file mode 100644 index 000000000000..e161638a5627 --- /dev/null +++ b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr @@ -0,0 +1,31 @@ +error: functions with parameters of uninhabited types are uncallable + --> $DIR/uninhabited-function-parameter-warning.rs:13:1 + | +LL | fn foo(a: (), b: Void) { //~ ERROR functions with parameters of uninhabited types are uncallable + | ^ - this parameter has an uninhabited type + | _| + | | +LL | | a +LL | | } + | |_^ + | +note: lint level defined here + --> $DIR/uninhabited-function-parameter-warning.rs:1:9 + | +LL | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: functions with parameters of uninhabited types are uncallable + --> $DIR/uninhabited-function-parameter-warning.rs:21:1 + | +LL | fn baz(a: (), b: hide::PubliclyUninhabited) { + | ^ - this parameter has an uninhabited type + | _| + | | +LL | | //~^ ERROR functions with parameters of uninhabited types are uncallable +LL | | a +LL | | } + | |_^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/unreachable/unwarned-match-on-never.rs b/src/test/ui/unreachable/unwarned-match-on-never.rs index 71f8fe3a783e..c435095644e7 100644 --- a/src/test/ui/unreachable/unwarned-match-on-never.rs +++ b/src/test/ui/unreachable/unwarned-match-on-never.rs @@ -1,13 +1,17 @@ +#![feature(never_type)] + #![deny(unreachable_code)] #![allow(dead_code)] -#![feature(never_type)] +fn never() -> ! { + unimplemented!() +} -fn foo(x: !) -> bool { +fn foo() -> bool { // Explicit matches on the never type are unwarned. - match x {} + match never() {} // But matches in unreachable code are warned. - match x {} //~ ERROR unreachable expression + match never() {} //~ ERROR unreachable expression } fn bar() { diff --git a/src/test/ui/unreachable/unwarned-match-on-never.stderr b/src/test/ui/unreachable/unwarned-match-on-never.stderr index 8807e5f04e58..3d3aafff4f69 100644 --- a/src/test/ui/unreachable/unwarned-match-on-never.stderr +++ b/src/test/ui/unreachable/unwarned-match-on-never.stderr @@ -1,23 +1,23 @@ error: unreachable expression - --> $DIR/unwarned-match-on-never.rs:10:5 + --> $DIR/unwarned-match-on-never.rs:14:5 | -LL | match x {} //~ ERROR unreachable expression - | ^^^^^^^^^^ +LL | match never() {} //~ ERROR unreachable expression + | ^^^^^^^^^^^^^^^^ | note: lint level defined here - --> $DIR/unwarned-match-on-never.rs:1:9 + --> $DIR/unwarned-match-on-never.rs:3:9 | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ error: unreachable arm - --> $DIR/unwarned-match-on-never.rs:15:15 + --> $DIR/unwarned-match-on-never.rs:19:15 | LL | () => () //~ ERROR unreachable arm | ^^ error: unreachable expression - --> $DIR/unwarned-match-on-never.rs:21:5 + --> $DIR/unwarned-match-on-never.rs:25:5 | LL | / match () { //~ ERROR unreachable expression LL | | () => (),