From 2e04872818b4a7ea04eb52b877d4023eb8ae88c2 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 11 Sep 2018 14:45:18 +0100 Subject: [PATCH 1/5] Lint functions taking unhabited parameters with unreachable_code --- src/librustc_typeck/check/mod.rs | 30 +++++++++++++++++++ .../uninhabited-function-parameter-warning.rs | 17 +++++++++++ ...nhabited-function-parameter-warning.stderr | 19 ++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs create mode 100644 src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d78d7273a36e6..0b8ee3c1fb925 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/test/ui/uninhabited/uninhabited-function-parameter-warning.rs b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs new file mode 100644 index 0000000000000..494f4efe382b9 --- /dev/null +++ b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs @@ -0,0 +1,17 @@ +#![deny(unreachable_code)] + +enum Void {} + +fn foo(a: (), b: Void) { //~ ERROR functions with parameters of uninhabited types are uncallable + a +} + +trait Foo { + fn foo(a: Self); +} + +impl Foo for Void { + fn foo(a: 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 0000000000000..5a4575aa58913 --- /dev/null +++ b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr @@ -0,0 +1,19 @@ +error: functions with parameters of uninhabited types are uncallable + --> $DIR/uninhabited-function-parameter-warning.rs:5: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: aborting due to previous error + From df3035ad80aae1804a1686acbd2b97c8f6148493 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 20 Nov 2018 22:33:43 +0000 Subject: [PATCH 2/5] Fix tests making use of uninhabited arguments --- .../run-pass/drop/drop-uninhabited-enum.rs | 8 +++++--- src/test/run-pass/issues/issue-3037.rs | 16 ++++++++-------- src/test/run-pass/issues/issue-46855.stderr | 18 ++++++++++++++++++ src/test/run-pass/issues/issue-50731.rs | 3 +++ src/test/run-pass/never-type-rvalues.rs | 1 + .../match/match-no-arms-unreachable-after.rs | 11 +++++++---- .../match-no-arms-unreachable-after.stderr | 2 +- src/test/ui/reachable/expr_call.rs | 4 ++-- .../ui/unreachable/unwarned-match-on-never.rs | 12 ++++++++---- .../unreachable/unwarned-match-on-never.stderr | 12 ++++++------ 10 files changed, 59 insertions(+), 28 deletions(-) create mode 100644 src/test/run-pass/issues/issue-46855.stderr diff --git a/src/test/run-pass/drop/drop-uninhabited-enum.rs b/src/test/run-pass/drop/drop-uninhabited-enum.rs index b3566f68533bd..56ada47a07ec9 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 ff4d32c284073..60dfef278cbfe 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 0000000000000..a4f913711f8d8 --- /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 209c1e1279b5f..9873b15d856c0 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 2de8567924e39..604178a447efc 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 8f83fd1a31803..99c3ceabdfbba 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 bd136245c1ae9..fcca5262c6b74 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 1eaa96c3ce773..cf9523cc847fc 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/unreachable/unwarned-match-on-never.rs b/src/test/ui/unreachable/unwarned-match-on-never.rs index 71f8fe3a783e2..c435095644e76 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 8807e5f04e585..3d3aafff4f696 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 | | () => (), From 7d86754ae61ad4745420d98d705d4cb7c9924c76 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 28 Dec 2018 18:36:33 +0000 Subject: [PATCH 3/5] Add test case for methods in traits that take uninhabited types These should not currently be linted. --- .../ui/uninhabited/uninhabited-function-parameter-warning.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs index 494f4efe382b9..22ce479cfc2b1 100644 --- a/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs +++ b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs @@ -8,10 +8,14 @@ fn foo(a: (), b: Void) { //~ ERROR functions with parameters of uninhabited type 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() {} From 2c8f042c204f22166fda3498a1dbaa56a37de6a9 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 28 Dec 2018 18:52:15 +0000 Subject: [PATCH 4/5] Add tests for privately/publicly uninhabited types in function signatures --- .../uninhabited-function-parameter-warning.rs | 20 +++++++++++++++++++ ...nhabited-function-parameter-warning.stderr | 16 +++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs index 22ce479cfc2b1..f3a3d917dbbbe 100644 --- a/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs +++ b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs @@ -2,10 +2,30 @@ 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); diff --git a/src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr index 5a4575aa58913..e161638a56276 100644 --- a/src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr +++ b/src/test/ui/uninhabited/uninhabited-function-parameter-warning.stderr @@ -1,5 +1,5 @@ error: functions with parameters of uninhabited types are uncallable - --> $DIR/uninhabited-function-parameter-warning.rs:5:1 + --> $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 @@ -15,5 +15,17 @@ note: lint level defined here LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +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 From adac7a13750fd89801d25696d13ad71201f6bbcf Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 3 Jan 2019 10:29:36 +0000 Subject: [PATCH 5/5] Allow unreachable_code for uncallable functions in WASM --- src/libstd/sys/cloudabi/shims/fs.rs | 3 +++ src/libstd/sys/cloudabi/shims/pipe.rs | 3 +++ src/libstd/sys/sgx/fs.rs | 3 +++ src/libstd/sys/sgx/pipe.rs | 3 +++ src/libstd/sys/wasm/fs.rs | 3 +++ src/libstd/sys/wasm/pipe.rs | 3 +++ 6 files changed, 18 insertions(+) diff --git a/src/libstd/sys/cloudabi/shims/fs.rs b/src/libstd/sys/cloudabi/shims/fs.rs index 3af10a74c7d45..d74e5e2ea86df 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 30ef79dd769b4..885ebdfa5377f 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 8b1c4476bc417..b8cc2f8896362 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 ac48a6dc03323..08af256cde05a 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 8b1c4476bc417..e302c8dd914a0 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 ac48a6dc03323..b901d4a448abc 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,