From 62b359094f0dcebdb03e4bbc0e4934ce38d8623a Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 11 Sep 2018 12:41:56 +0100 Subject: [PATCH 01/23] Check for uninhabitedness instead of never --- src/librustc/cfg/construct.rs | 3 +- src/librustc/middle/liveness.rs | 6 +-- src/librustc/ty/sty.rs | 11 +++++ .../borrow_check/nll/type_check/mod.rs | 3 +- src/librustc_mir/build/expr/into.rs | 5 +- src/librustc_mir/hair/pattern/check_match.rs | 11 +---- src/test/debuginfo/nil-enum.rs | 47 ++++--------------- 7 files changed, 26 insertions(+), 60 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index c5d6ce24c5df4..0464b2f3454d3 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -415,8 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { args: I) -> CFGIndex { let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - // FIXME(canndrew): This is_never should probably be an is_uninhabited. - if self.tables.expr_ty(call_expr).is_never() { + if self.tables.expr_ty(call_expr).conservative_is_uninhabited() { self.add_unreachable_node() } else { ret diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index e576951417f93..6e3db217d7af1 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1197,8 +1197,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Call(ref f, ref args) => { - // FIXME(canndrew): This is_never should really be an is_uninhabited - let succ = if self.tables.expr_ty(expr).is_never() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { self.s.exit_ln } else { succ @@ -1208,8 +1207,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::MethodCall(.., ref args) => { - // FIXME(canndrew): This is_never should really be an is_uninhabited - let succ = if self.tables.expr_ty(expr).is_never() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { self.s.exit_ln } else { succ diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index f757f48e987d8..037adb2c53ca7 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1543,6 +1543,17 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + pub fn conservative_is_uninhabited(&self) -> bool { + // Checks whether a type is definitely uninhabited. This is + // conservative: for some types that are uninhabited we return `false`, + // but we only return `true` for types that are definitely uninhabited. + match self.sty { + ty::Never => true, + ty::Adt(def, _) => def.variants.is_empty(), + _ => false + } + } + pub fn is_primitive(&self) -> bool { match self.sty { Bool | Char | Int(_) | Uint(_) | Float(_) => true, diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 4807abe2bdd19..39019b35beb18 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1546,8 +1546,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } None => { - // FIXME(canndrew): This is_never should probably be an is_uninhabited - if !sig.output().is_never() { + if !sig.output().conservative_is_uninhabited() { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 0e7305e076ede..77cb4820ba460 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -275,8 +275,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { exit_block.unit() } ExprKind::Call { ty, fun, args, from_hir_call } => { - // FIXME(canndrew): This is_never should probably be an is_uninhabited - let diverges = expr.ty.is_never(); + let diverges = expr.ty.conservative_is_uninhabited(); let intrinsic = match ty.sty { ty::FnDef(def_id, _) => { let f = ty.fn_sig(this.hir.tcx()); @@ -332,7 +331,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { func: fun, args, cleanup: Some(cleanup), - destination: if diverges { + destination: if expr.ty.conservative_is_uninhabited() { None } else { Some((destination.clone(), success)) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index bfa2e53b9e0cf..1f1967eca6a18 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -230,7 +230,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - self.conservative_is_uninhabited(pat_ty) + pat_ty.conservative_is_uninhabited() }; if !scrutinee_is_uninhabited { // We know the type is inhabited, so this must be wrong @@ -258,15 +258,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { }) } - fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool { - // "rustc-1.0-style" uncontentious uninhabitableness check - match scrutinee_ty.sty { - ty::Never => true, - ty::Adt(def, _) => def.variants.is_empty(), - _ => false - } - } - fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) { let module = self.tcx.hir().get_module_parent(pat.id); MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| { diff --git a/src/test/debuginfo/nil-enum.rs b/src/test/debuginfo/nil-enum.rs index ab42b2eff99f8..27e8e9d17e82c 100644 --- a/src/test/debuginfo/nil-enum.rs +++ b/src/test/debuginfo/nil-enum.rs @@ -1,55 +1,24 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// NOTE Instantiating an empty enum is UB. This test may break in the future. - -// LLDB can't handle zero-sized values +// LLDB can't handle zero-sized values. // ignore-lldb - -// Require LLVM with DW_TAG_variant_part and a gdb that can read it. -// gdb 8.2.0 crashes on this test case, see -// https://sourceware.org/bugzilla/show_bug.cgi?id=23626 -// This will be fixed in the next release, which will be >= 8.2.1. -// min-system-llvm-version: 7.0 -// min-gdb-version: 8.2.1 - // compile-flags:-g // gdb-command:run -// gdb-command:print first -// gdbr-check:$1 = nil_enum::ANilEnum {<No data fields>} - -// gdb-command:print second -// gdbr-check:$2 = nil_enum::AnotherNilEnum {<No data fields>} +// gdb-command:print *first +// gdbg-check:$1 = {<No data fields>} +// gdbr-check:$1 = <error reading variable> #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] #![feature(maybe_uninit)] #![omit_gdb_pretty_printer_section] -use std::mem::MaybeUninit; - -enum ANilEnum {} -enum AnotherNilEnum {} +enum Void {} -// This test relies on gdbg printing the string "{<No data fields>}" for empty -// structs (which may change some time) -// The error from gdbr is expected since nil enums are not supposed to exist. fn main() { - unsafe { - let first: ANilEnum = MaybeUninit::uninitialized().into_inner(); - let second: AnotherNilEnum = MaybeUninit::uninitialized().into_inner(); + let first: *const Void = 1 as *const _; - zzz(); // #break - } + zzz(); // #break } -fn zzz() {()} +fn zzz() {} From 88afbf2d99bd8720802b87d3cd8852b090063456 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 11 Sep 2018 13:04:21 +0100 Subject: [PATCH 02/23] Make uninhabitedness checking more intelligent --- src/librustc/cfg/construct.rs | 2 +- src/librustc/middle/liveness.rs | 4 +-- src/librustc/ty/sty.rs | 32 +++++++++++++++++-- .../borrow_check/nll/type_check/mod.rs | 2 +- src/librustc_mir/build/expr/into.rs | 2 +- src/librustc_mir/hair/pattern/check_match.rs | 2 +- 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 0464b2f3454d3..1521b7a69ab03 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -415,7 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { args: I) -> CFGIndex { let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - if self.tables.expr_ty(call_expr).conservative_is_uninhabited() { + if self.tables.expr_ty(call_expr).conservative_is_uninhabited(self.tcx) { self.add_unreachable_node() } else { ret diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 6e3db217d7af1..2b9a44429a0a6 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1197,7 +1197,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Call(ref f, ref args) => { - let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) { self.s.exit_ln } else { succ @@ -1207,7 +1207,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::MethodCall(.., ref args) => { - let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) { self.s.exit_ln } else { succ diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 037adb2c53ca7..152d9a323031b 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1543,14 +1543,40 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } - pub fn conservative_is_uninhabited(&self) -> bool { + pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { // Checks whether a type is definitely uninhabited. This is // conservative: for some types that are uninhabited we return `false`, // but we only return `true` for types that are definitely uninhabited. match self.sty { ty::Never => true, - ty::Adt(def, _) => def.variants.is_empty(), - _ => false + ty::Adt(def, _) => { + // Any ADT is uninhabited if: + // (a) It has no variants (i.e. an empty `enum`); + // (b) Each of its variants (a single one in the case of a `struct`) has at least + // one uninhabited field. + def.variants.iter().all(|var| { + var.fields.iter().any(|field| { + tcx.type_of(field.did).conservative_is_uninhabited(tcx) + }) + }) + } + ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited(tcx)), + ty::Array(ty, len) => { + match len.val.try_to_scalar() { + // If the array is definitely non-empty, it's uninhabited if + // the type of its elements is uninhabited. + Some(n) if !n.is_null() => ty.conservative_is_uninhabited(tcx), + _ => false + } + } + ty::Ref(..) => { + // Though references to uninhabited types are trivially uninhabited + // theoretically, null references are permitted in unsafe code (as + // long as the value is not dereferenced), so we treat all references + // as inhabited. + false + } + _ => false, } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 39019b35beb18..aea48ead428a4 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1546,7 +1546,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } None => { - if !sig.output().conservative_is_uninhabited() { + if !sig.output().conservative_is_uninhabited(self.tcx()) { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 77cb4820ba460..4b12562887ae0 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -331,7 +331,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { func: fun, args, cleanup: Some(cleanup), - destination: if expr.ty.conservative_is_uninhabited() { + destination: if expr.ty.conservative_is_uninhabited(this.hir.tcx()) { None } else { Some((destination.clone(), success)) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 1f1967eca6a18..5a83fe76d7c53 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -230,7 +230,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - pat_ty.conservative_is_uninhabited() + pat_ty.conservative_is_uninhabited(self.tcx) }; if !scrutinee_is_uninhabited { // We know the type is inhabited, so this must be wrong From ffce4fbd6c7e1e2ac778cdb385cc9aca5e1bb6cb Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 11 Sep 2018 14:55:12 +0100 Subject: [PATCH 03/23] Update uninhabited matches tests --- .../uninhabited-matches-feature-gated.rs | 4 +-- .../uninhabited-matches-feature-gated.stderr | 26 +------------------ 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs index 1d3f8ff12d865..a3b99deac233b 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs @@ -20,10 +20,10 @@ fn main() { let _ = match x {}; //~ ERROR non-exhaustive let x: (Void,) = unsafe { std::mem::uninitialized() }; - let _ = match x {}; //~ ERROR non-exhaustive + let _ = match x {}; // okay let x: [Void; 1] = unsafe { std::mem::uninitialized() }; - let _ = match x {}; //~ ERROR non-exhaustive + let _ = match x {}; // okay let x: &[Void] = unsafe { std::mem::uninitialized() }; let _ = match x { //~ ERROR non-exhaustive diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index f4974b8fa3854..658ed9f466091 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -16,30 +16,6 @@ help: ensure that all possible cases are being handled, possibly by adding wildc LL | let _ = match x {}; //~ ERROR non-exhaustive | ^ -error[E0004]: non-exhaustive patterns: type `(Void,)` is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:23:19 - | -LL | let _ = match x {}; //~ ERROR non-exhaustive - | ^ - | -help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - --> $DIR/uninhabited-matches-feature-gated.rs:23:19 - | -LL | let _ = match x {}; //~ ERROR non-exhaustive - | ^ - -error[E0004]: non-exhaustive patterns: type `[Void; 1]` is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:26:19 - | -LL | let _ = match x {}; //~ ERROR non-exhaustive - | ^ - | -help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - --> $DIR/uninhabited-matches-feature-gated.rs:26:19 - | -LL | let _ = match x {}; //~ ERROR non-exhaustive - | ^ - error[E0004]: non-exhaustive patterns: `&[_]` not covered --> $DIR/uninhabited-matches-feature-gated.rs:29:19 | @@ -58,7 +34,7 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered LL | let Ok(x) = x; | ^^^^^ pattern `Err(_)` not covered -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors Some errors occurred: E0004, E0005. For more information about an error, try `rustc --explain E0004`. From 64c2a310e1bb489a97e6e8624201b632e31de5aa Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Fri, 14 Sep 2018 22:46:13 +0100 Subject: [PATCH 04/23] Address comments --- src/librustc/ty/sty.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 152d9a323031b..46f22fa86a6b5 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1549,8 +1549,12 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { // but we only return `true` for types that are definitely uninhabited. match self.sty { ty::Never => true, + ty::Adt(def, _) if def.is_union() => { + // For now, `union`s are never considered uninhabited. + false + } ty::Adt(def, _) => { - // Any ADT is uninhabited if: + // Any ADT is uninhabited if either: // (a) It has no variants (i.e. an empty `enum`); // (b) Each of its variants (a single one in the case of a `struct`) has at least // one uninhabited field. From 9f609f9feff30324a2fc99ed0f25010d64205dd6 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Thu, 20 Sep 2018 20:22:02 +0100 Subject: [PATCH 05/23] Fix Ref inhabitedness comment --- src/librustc/ty/sty.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 46f22fa86a6b5..0c859dea8da9c 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1574,10 +1574,9 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } ty::Ref(..) => { - // Though references to uninhabited types are trivially uninhabited - // theoretically, null references are permitted in unsafe code (as - // long as the value is not dereferenced), so we treat all references - // as inhabited. + // References to uninitialised memory is valid for any type, including + // uninhabited types, in unsafe code, so we treat all references as + // inhabited. false } _ => false, From 4c88be3592ce027629baacc71c450aa997f066ff Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 16 Oct 2018 16:31:41 +0100 Subject: [PATCH 06/23] Fix handling of divergent dicriminants --- src/librustc_mir/build/expr/into.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 4b12562887ae0..e344d84586980 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -275,7 +275,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { exit_block.unit() } ExprKind::Call { ty, fun, args, from_hir_call } => { - let diverges = expr.ty.conservative_is_uninhabited(); let intrinsic = match ty.sty { ty::FnDef(def_id, _) => { let f = ty.fn_sig(this.hir.tcx()); From 419d2d8f319e30d4b8d071c10d427637056131a9 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 16 Oct 2018 21:16:48 +0100 Subject: [PATCH 07/23] Update const eval uninhabited messages --- src/test/ui/consts/const-eval/ub-uninhabit.stderr | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.stderr index c5ac72b639c05..cadcedd623919 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.stderr +++ b/src/test/ui/consts/const-eval/ub-uninhabit.stderr @@ -2,9 +2,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:19:1 | LL | const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^^ + | | + | entered unreachable code | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior + = note: #[deny(const_err)] on by default error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:22:1 @@ -18,9 +20,9 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:25:1 | LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at [0] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^^ + | | + | entered unreachable code error: aborting due to 3 previous errors From d065a499417e03ac8e3ab0f48aeddbe13741a2b4 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 16 Oct 2018 21:36:29 +0100 Subject: [PATCH 08/23] Nonempty arrays of uninhabited arrays are Abi::Uninhabited --- src/librustc/ty/layout.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 87d745e5cea77..da3c00f5e0855 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -551,13 +551,19 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { let size = element.size.checked_mul(count, dl) .ok_or(LayoutError::SizeOverflow(ty))?; + let abi = if size != Size::ZERO && ty.conservative_is_uninhabited(tcx) { + Abi::Uninhabited + } else { + Abi::Aggregate { sized: true } + }; + tcx.intern_layout(LayoutDetails { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldPlacement::Array { stride: element.size, count }, - abi: Abi::Aggregate { sized: true }, + abi, align: element.align, size }) From 51e1c6437e64cdf7fa6c75a3bfb2bb3f57d7313b Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 16 Oct 2018 21:36:43 +0100 Subject: [PATCH 09/23] conservative_is_uninhabited implies abi.is_uninhabited --- src/librustc/ty/layout.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index da3c00f5e0855..c210372c4e7bb 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -191,7 +191,14 @@ fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty::tls::enter_context(&icx, |_| { let cx = LayoutCx { tcx, param_env }; - cx.layout_raw_uncached(ty) + let layout = cx.layout_raw_uncached(ty); + // Type-level uninhabitedness should always imply ABI uninhabitedness. + if let Ok(layout) = layout { + if ty.conservative_is_uninhabited(tcx) { + assert!(layout.abi.is_uninhabited()); + } + } + layout }) }) } @@ -205,12 +212,11 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { pub struct LayoutCx<'tcx, C> { pub tcx: C, - pub param_env: ty::ParamEnv<'tcx> + pub param_env: ty::ParamEnv<'tcx>, } impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { - fn layout_raw_uncached(&self, ty: Ty<'tcx>) - -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> { + fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> { let tcx = self.tcx; let param_env = self.param_env; let dl = self.data_layout(); From 6e5e54f7351bef5d20cf9c12142f7adbbc15bdd1 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Thu, 18 Oct 2018 16:58:10 +0100 Subject: [PATCH 10/23] Use unions for uninhabitedness checking rather than mem::transmute --- src/librustc/ty/layout.rs | 2 +- src/test/ui/consts/const-eval/ub-uninhabit.rs | 26 +++++++------------ .../ui/consts/const-eval/ub-uninhabit.stderr | 16 +++++------- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index c210372c4e7bb..3ebdd44aba9f8 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -557,7 +557,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { let size = element.size.checked_mul(count, dl) .ok_or(LayoutError::SizeOverflow(ty))?; - let abi = if size != Size::ZERO && ty.conservative_is_uninhabited(tcx) { + let abi = if count != 0 && ty.conservative_is_uninhabited(tcx) { Abi::Uninhabited } else { Abi::Aggregate { sized: true } diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.rs b/src/test/ui/consts/const-eval/ub-uninhabit.rs index 74713af2ea033..ba083df4bee25 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.rs +++ b/src/test/ui/consts/const-eval/ub-uninhabit.rs @@ -1,13 +1,3 @@ -// Copyright 2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - #![feature(const_transmute)] #![allow(const_err)] // make sure we cannot allow away the errors tested here @@ -16,14 +6,18 @@ use std::mem; #[derive(Copy, Clone)] enum Bar {} -const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) }; -//~^ ERROR it is undefined behavior to use this value +union TransmuteUnion<A: Clone + Copy, B: Clone + Copy> { + a: A, + b: B, +} + +const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b }; +//~^ ERROR this constant likely exhibits undefined behavior const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; //~^ ERROR it is undefined behavior to use this value -const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) }; -//~^ ERROR it is undefined behavior to use this value +const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b }; +//~^ ERROR this constant likely exhibits undefined behavior -fn main() { -} +fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.stderr index cadcedd623919..c4ed503635a54 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.stderr +++ b/src/test/ui/consts/const-eval/ub-uninhabit.stderr @@ -1,12 +1,10 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:19:1 | -LL | const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^^ - | | - | entered unreachable code +LL | const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type | - = note: #[deny(const_err)] on by default + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:22:1 @@ -19,10 +17,10 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; error[E0080]: it is undefined behavior to use this value --> $DIR/ub-uninhabit.rs:25:1 | -LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^^ - | | - | entered unreachable code +LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited 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 rust compiler repository if you believe it should not be considered undefined behavior error: aborting due to 3 previous errors From a38ff377e7420d33fb67349fdcda3fe9fde329d1 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Fri, 19 Oct 2018 15:09:07 +0100 Subject: [PATCH 11/23] Improve `conservative_is_uninhabited` comment --- src/librustc/ty/sty.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 0c859dea8da9c..3dab7f4dd777b 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1543,10 +1543,14 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + /// Checks whether a type is definitely uninhabited. This is + /// conservative: for some types that are uninhabited we return `false`, + /// but we only return `true` for types that are definitely uninhabited. + /// `ty.conservative_is_uninhabited` implies that any value of type `ty` + /// will be `Abi::Uninhabited`. pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { - // Checks whether a type is definitely uninhabited. This is - // conservative: for some types that are uninhabited we return `false`, - // but we only return `true` for types that are definitely uninhabited. + // FIXME(varkor): we can make this less conversative by substituting concrete + // type arguments. match self.sty { ty::Never => true, ty::Adt(def, _) if def.is_union() => { From 13af92f4592debacd3df601e380f35faec64e63f Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 23 Oct 2018 19:13:49 +0100 Subject: [PATCH 12/23] Add note on nonzero-sized uninhabited types --- src/librustc/ty/sty.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 3dab7f4dd777b..8381101290eee 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1547,7 +1547,8 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { /// conservative: for some types that are uninhabited we return `false`, /// but we only return `true` for types that are definitely uninhabited. /// `ty.conservative_is_uninhabited` implies that any value of type `ty` - /// will be `Abi::Uninhabited`. + /// will be `Abi::Uninhabited`. (Note that uninhabited types may have nonzero + /// size, to account for partial initialisation. See #49298 for details.) pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { // FIXME(varkor): we can make this less conversative by substituting concrete // type arguments. From 9c66599c6a551ac7b897f08f111f225861b2824c Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Wed, 24 Oct 2018 17:33:29 +0100 Subject: [PATCH 13/23] Address unused variables warning with TcpStream --- src/libstd/net/tcp.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 5aa043b0fcb2c..347e795c4f71d 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -729,6 +729,9 @@ impl TcpListener { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + // On WASM, `TcpStream` is uninhabited (as it's unsupported) and so + // the `a` variable here is technically unused. + #[cfg_attr(target_arch = "wasm32", allow(unused_variables))] self.0.accept().map(|(a, b)| (TcpStream(a), b)) } From cb4bd5a22cb0e522d5e753af8ba853ddea68620c Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Sat, 3 Nov 2018 18:38:00 +0000 Subject: [PATCH 14/23] Update ub-uninhabit tests --- src/librustc/ty/sty.rs | 4 ++-- src/test/ui/consts/const-eval/ub-uninhabit.rs | 4 ++-- src/test/ui/consts/const-eval/ub-uninhabit.stderr | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 8381101290eee..ceb423f309229 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1571,10 +1571,10 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited(tcx)), ty::Array(ty, len) => { - match len.val.try_to_scalar() { + match len.assert_usize(tcx) { // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. - Some(n) if !n.is_null() => ty.conservative_is_uninhabited(tcx), + Some(n) if n != 0 => ty.conservative_is_uninhabited(tcx), _ => false } } diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.rs b/src/test/ui/consts/const-eval/ub-uninhabit.rs index ba083df4bee25..42cba02f579b7 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.rs +++ b/src/test/ui/consts/const-eval/ub-uninhabit.rs @@ -12,12 +12,12 @@ union TransmuteUnion<A: Clone + Copy, B: Clone + Copy> { } const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b }; -//~^ ERROR this constant likely exhibits undefined behavior +//~^ ERROR it is undefined behavior to use this value const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; //~^ ERROR it is undefined behavior to use this value const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b }; -//~^ ERROR this constant likely exhibits undefined behavior +//~^ ERROR it is undefined behavior to use this value fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.stderr index c4ed503635a54..5d6b87c392b3f 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.stderr +++ b/src/test/ui/consts/const-eval/ub-uninhabit.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:19:1 + --> $DIR/ub-uninhabit.rs:13:1 | LL | const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type @@ -7,7 +7,7 @@ LL | const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b } = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:22:1 + --> $DIR/ub-uninhabit.rs:16:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref> @@ -15,7 +15,7 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:25:1 + --> $DIR/ub-uninhabit.rs:19:1 | LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type From 20415af14207cd4aae261de8ae7af81d4afdc21a Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Sat, 3 Nov 2018 19:18:24 +0000 Subject: [PATCH 15/23] Add privately uninhabited dead code test --- .../privately-uninhabited-dead-code.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/test/ui/uninhabited/privately-uninhabited-dead-code.rs diff --git a/src/test/ui/uninhabited/privately-uninhabited-dead-code.rs b/src/test/ui/uninhabited/privately-uninhabited-dead-code.rs new file mode 100644 index 0000000000000..602857ab1be33 --- /dev/null +++ b/src/test/ui/uninhabited/privately-uninhabited-dead-code.rs @@ -0,0 +1,19 @@ +// compile-pass + +#![deny(unreachable_code)] + +mod foo { + enum Bar {} + + #[allow(dead_code)] + pub struct Foo { + value: Bar, // "privately" uninhabited + } + + pub fn give_foo() -> Foo { panic!() } +} + +fn main() { + foo::give_foo(); + println!("Hello, world!"); // ok: we can't tell that this code is dead +} From 210e2347335dbb097aefcd8f3002a0c4e72aa9ed Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 6 Nov 2018 19:38:01 +0000 Subject: [PATCH 16/23] Make liveness analysis respect privacy --- src/librustc/middle/liveness.rs | 6 ++++-- src/test/ui/uninhabited/privately-uninhabited-dead-code.rs | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 2b9a44429a0a6..b99b668b2db7e 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1197,7 +1197,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Call(ref f, ref args) => { - let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) { + let m = self.ir.tcx.hir.get_module_parent(expr.id); + let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) { self.s.exit_ln } else { succ @@ -1207,7 +1208,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::MethodCall(.., ref args) => { - let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) { + let m = self.ir.tcx.hir.get_module_parent(expr.id); + let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) { self.s.exit_ln } else { succ diff --git a/src/test/ui/uninhabited/privately-uninhabited-dead-code.rs b/src/test/ui/uninhabited/privately-uninhabited-dead-code.rs index 602857ab1be33..9fe8a5c832c5a 100644 --- a/src/test/ui/uninhabited/privately-uninhabited-dead-code.rs +++ b/src/test/ui/uninhabited/privately-uninhabited-dead-code.rs @@ -1,6 +1,6 @@ // compile-pass -#![deny(unreachable_code)] +#![deny(unused_variables)] mod foo { enum Bar {} @@ -14,6 +14,7 @@ mod foo { } fn main() { + let a = 42; foo::give_foo(); - println!("Hello, world!"); // ok: we can't tell that this code is dead + println!("Hello, {}", a); // ok: we can't tell that this code is dead } From 4d8a6eac397a4acdd5965c4c9f04d236ada2e266 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 6 Nov 2018 22:19:07 +0000 Subject: [PATCH 17/23] Fix some misbehaving tests --- src/test/run-pass/binding/empty-types-in-patterns.rs | 1 + src/test/run-pass/issues/issue-41696.rs | 1 + src/test/ui/consts/const-eval/ub-uninhabit.stderr | 6 +++--- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/run-pass/binding/empty-types-in-patterns.rs b/src/test/run-pass/binding/empty-types-in-patterns.rs index 7fb7eec125601..f595bb0da9a27 100644 --- a/src/test/run-pass/binding/empty-types-in-patterns.rs +++ b/src/test/run-pass/binding/empty-types-in-patterns.rs @@ -14,6 +14,7 @@ #![feature(slice_patterns)] #![allow(unreachable_patterns)] #![allow(unreachable_code)] +#![allow(unused_variables)] #[allow(dead_code)] fn foo(z: !) { diff --git a/src/test/run-pass/issues/issue-41696.rs b/src/test/run-pass/issues/issue-41696.rs index 3937f9c193077..5a5a2adc124e6 100644 --- a/src/test/run-pass/issues/issue-41696.rs +++ b/src/test/run-pass/issues/issue-41696.rs @@ -11,6 +11,7 @@ // run-pass #![allow(dead_code)] #![allow(unused_variables)] +#![recursion_limit = "128"] // this used to cause exponential code-size blowup during LLVM passes. #![feature(test)] diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.stderr index 5d6b87c392b3f..c8842ecc23ca2 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.stderr +++ b/src/test/ui/consts/const-eval/ub-uninhabit.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:13:1 + --> $DIR/ub-uninhabit.rs:14:1 | LL | const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type @@ -7,7 +7,7 @@ LL | const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b } = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:16:1 + --> $DIR/ub-uninhabit.rs:17:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref> @@ -15,7 +15,7 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:19:1 + --> $DIR/ub-uninhabit.rs:20:1 | LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type From 6561732f887e9e379293821d4d802a6fa1c44fd6 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 20 Nov 2018 19:07:17 +0000 Subject: [PATCH 18/23] Consider privacy in more locations --- src/librustc/cfg/construct.rs | 3 ++- src/librustc/ty/layout.rs | 4 ++-- src/librustc/ty/sty.rs | 10 +++++----- .../borrow_check/nll/type_check/mod.rs | 2 +- src/librustc_mir/build/expr/into.rs | 14 +++++++------- src/librustc_mir/hair/pattern/check_match.rs | 2 +- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 1521b7a69ab03..a33dca0ac046e 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -415,7 +415,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { args: I) -> CFGIndex { let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - if self.tables.expr_ty(call_expr).conservative_is_uninhabited(self.tcx) { + let m = self.tcx.hir.get_module_parent(call_expr.id); + if self.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(call_expr)) { self.add_unreachable_node() } else { ret diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 3ebdd44aba9f8..f4506c8e81976 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -194,7 +194,7 @@ fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let layout = cx.layout_raw_uncached(ty); // Type-level uninhabitedness should always imply ABI uninhabitedness. if let Ok(layout) = layout { - if ty.conservative_is_uninhabited(tcx) { + if ty.conservative_is_privately_uninhabited(tcx) { assert!(layout.abi.is_uninhabited()); } } @@ -557,7 +557,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { let size = element.size.checked_mul(count, dl) .ok_or(LayoutError::SizeOverflow(ty))?; - let abi = if count != 0 && ty.conservative_is_uninhabited(tcx) { + let abi = if count != 0 && ty.conservative_is_privately_uninhabited(tcx) { Abi::Uninhabited } else { Abi::Aggregate { sized: true } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index ceb423f309229..2189267cb0b05 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1546,10 +1546,10 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { /// Checks whether a type is definitely uninhabited. This is /// conservative: for some types that are uninhabited we return `false`, /// but we only return `true` for types that are definitely uninhabited. - /// `ty.conservative_is_uninhabited` implies that any value of type `ty` + /// `ty.conservative_is_privately_uninhabited` implies that any value of type `ty` /// will be `Abi::Uninhabited`. (Note that uninhabited types may have nonzero /// size, to account for partial initialisation. See #49298 for details.) - pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { + pub fn conservative_is_privately_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { // FIXME(varkor): we can make this less conversative by substituting concrete // type arguments. match self.sty { @@ -1565,16 +1565,16 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { // one uninhabited field. def.variants.iter().all(|var| { var.fields.iter().any(|field| { - tcx.type_of(field.did).conservative_is_uninhabited(tcx) + tcx.type_of(field.did).conservative_is_privately_uninhabited(tcx) }) }) } - ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited(tcx)), + ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_privately_uninhabited(tcx)), ty::Array(ty, len) => { match len.assert_usize(tcx) { // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. - Some(n) if n != 0 => ty.conservative_is_uninhabited(tcx), + Some(n) if n != 0 => ty.conservative_is_privately_uninhabited(tcx), _ => false } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index aea48ead428a4..c01502e44d944 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1546,7 +1546,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } None => { - if !sig.output().conservative_is_uninhabited(self.tcx()) { + if !sig.output().conservative_is_privately_uninhabited(self.tcx()) { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index e344d84586980..8660bbadf3dc2 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -330,11 +330,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { func: fun, args, cleanup: Some(cleanup), - destination: if expr.ty.conservative_is_uninhabited(this.hir.tcx()) { - None - } else { - Some((destination.clone(), success)) - }, + destination: + if expr.ty.conservative_is_privately_uninhabited(this.hir.tcx()) { + None + } else { + Some((destination.clone(), success)) + }, from_hir_call, }, ); @@ -419,8 +420,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }); let rvalue = unpack!(block = this.as_local_rvalue(block, expr)); - this.cfg - .push_assign(block, source_info, destination, rvalue); + this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() } }; diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 5a83fe76d7c53..66de4e6b37f11 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -230,7 +230,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - pat_ty.conservative_is_uninhabited(self.tcx) + pat_ty.is_never() }; if !scrutinee_is_uninhabited { // We know the type is inhabited, so this must be wrong From 3dd5034967ab61bfe4dd906fe794ced1279b318a Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 20 Nov 2018 21:16:25 +0000 Subject: [PATCH 19/23] Restore old match behaviour --- src/librustc_mir/hair/pattern/check_match.rs | 6 ++++- .../uninhabited-matches-feature-gated.rs | 4 +-- .../uninhabited-matches-feature-gated.stderr | 26 ++++++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 66de4e6b37f11..a251b723d61fc 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -230,7 +230,11 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - pat_ty.is_never() + match pat_ty.sty { + ty::Never => true, + ty::Adt(def, _) => def.variants.is_empty(), + _ => false + } }; if !scrutinee_is_uninhabited { // We know the type is inhabited, so this must be wrong diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs index a3b99deac233b..1d3f8ff12d865 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs @@ -20,10 +20,10 @@ fn main() { let _ = match x {}; //~ ERROR non-exhaustive let x: (Void,) = unsafe { std::mem::uninitialized() }; - let _ = match x {}; // okay + let _ = match x {}; //~ ERROR non-exhaustive let x: [Void; 1] = unsafe { std::mem::uninitialized() }; - let _ = match x {}; // okay + let _ = match x {}; //~ ERROR non-exhaustive let x: &[Void] = unsafe { std::mem::uninitialized() }; let _ = match x { //~ ERROR non-exhaustive diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index 658ed9f466091..5b5b6df85fc0f 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -16,6 +16,30 @@ help: ensure that all possible cases are being handled, possibly by adding wildc LL | let _ = match x {}; //~ ERROR non-exhaustive | ^ +error[E0004]: non-exhaustive patterns: type (Void,) is non-empty + --> $DIR/uninhabited-matches-feature-gated.rs:23:19 + | +LL | let _ = match x {}; //~ ERROR non-exhaustive + | ^ + | +help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + --> $DIR/uninhabited-matches-feature-gated.rs:23:19 + | +LL | let _ = match x {}; //~ ERROR non-exhaustive + | ^ + +error[E0004]: non-exhaustive patterns: type [Void; 1] is non-empty + --> $DIR/uninhabited-matches-feature-gated.rs:26:19 + | +LL | let _ = match x {}; //~ ERROR non-exhaustive + | ^ + | +help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + --> $DIR/uninhabited-matches-feature-gated.rs:26:19 + | +LL | let _ = match x {}; //~ ERROR non-exhaustive + | ^ + error[E0004]: non-exhaustive patterns: `&[_]` not covered --> $DIR/uninhabited-matches-feature-gated.rs:29:19 | @@ -34,7 +58,7 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered LL | let Ok(x) = x; | ^^^^^ pattern `Err(_)` not covered -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors Some errors occurred: E0004, E0005. For more information about an error, try `rustc --explain E0004`. From 19ea2d1c8b7c36f8b9006ce46f2b719cc122cd52 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 11 Dec 2018 12:18:51 +0000 Subject: [PATCH 20/23] Add a mir-opt test --- src/test/mir-opt/uninhabited-enum.rs | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/test/mir-opt/uninhabited-enum.rs diff --git a/src/test/mir-opt/uninhabited-enum.rs b/src/test/mir-opt/uninhabited-enum.rs new file mode 100644 index 0000000000000..904a9c43c1bcc --- /dev/null +++ b/src/test/mir-opt/uninhabited-enum.rs @@ -0,0 +1,37 @@ +#![feature(never_type)] + +pub enum Void {} + +#[no_mangle] +pub fn process_never(input: *const !) { + let _input = unsafe { &*input }; +} + +#[no_mangle] +pub fn process_void(input: *const Void) { + let _input = unsafe { &*input }; + // In the future, this should end with `unreachable`, but we currently only do + // unreachability analysis for `!`. +} + +fn main() {} + +// END RUST SOURCE +// +// START rustc.process_never.SimplifyLocals.after.mir +// bb0: { +// StorageLive(_2); +// _2 = &(*_1); +// StorageDead(_2); +// unreachable; +// } +// END rustc.process_never.SimplifyLocals.after.mir +// +// START rustc.process_void.SimplifyLocals.after.mir +// bb0: { +// StorageLive(_2); +// _2 = &(*_1); +// StorageDead(_2); +// return; +// } +// END rustc.process_void.SimplifyLocals.after.mir From 573c1ffb78d6b012d565081b8d72a488b7e9bb81 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 11 Dec 2018 12:19:16 +0000 Subject: [PATCH 21/23] Add a FIXME for mir build unreachable destination checking --- src/librustc_mir/build/expr/into.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 8660bbadf3dc2..146bf53830656 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -330,12 +330,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { func: fun, args, cleanup: Some(cleanup), - destination: - if expr.ty.conservative_is_privately_uninhabited(this.hir.tcx()) { - None - } else { - Some((destination.clone(), success)) - }, + // 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() { + None + } else { + Some((destination.clone(), success)) + }, from_hir_call, }, ); From 2ba3e66fa6500cacd91535ef2b3235cac0d268d5 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Tue, 11 Dec 2018 12:19:23 +0000 Subject: [PATCH 22/23] Update tests --- src/librustc/cfg/construct.rs | 2 +- src/librustc/middle/liveness.rs | 4 ++-- src/test/ui/consts/validate_never_arrays.stderr | 2 +- .../ui/uninhabited/uninhabited-matches-feature-gated.stderr | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index a33dca0ac046e..4881f10fac25e 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -415,7 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { args: I) -> CFGIndex { let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - let m = self.tcx.hir.get_module_parent(call_expr.id); + let m = self.tcx.hir().get_module_parent(call_expr.id); if self.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(call_expr)) { self.add_unreachable_node() } else { diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index b99b668b2db7e..57b3449fe8417 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1197,7 +1197,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Call(ref f, ref args) => { - let m = self.ir.tcx.hir.get_module_parent(expr.id); + let m = self.ir.tcx.hir().get_module_parent(expr.id); let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) { self.s.exit_ln } else { @@ -1208,7 +1208,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::MethodCall(.., ref args) => { - let m = self.ir.tcx.hir.get_module_parent(expr.id); + let m = self.ir.tcx.hir().get_module_parent(expr.id); let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) { self.s.exit_ln } else { diff --git a/src/test/ui/consts/validate_never_arrays.stderr b/src/test/ui/consts/validate_never_arrays.stderr index 0b639240bb348..b9d181a76dd9b 100644 --- a/src/test/ui/consts/validate_never_arrays.stderr +++ b/src/test/ui/consts/validate_never_arrays.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/validate_never_arrays.rs:3:1 | LL | const FOO: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref>[0] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref> | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index 5b5b6df85fc0f..f4974b8fa3854 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -16,7 +16,7 @@ help: ensure that all possible cases are being handled, possibly by adding wildc LL | let _ = match x {}; //~ ERROR non-exhaustive | ^ -error[E0004]: non-exhaustive patterns: type (Void,) is non-empty +error[E0004]: non-exhaustive patterns: type `(Void,)` is non-empty --> $DIR/uninhabited-matches-feature-gated.rs:23:19 | LL | let _ = match x {}; //~ ERROR non-exhaustive @@ -28,7 +28,7 @@ help: ensure that all possible cases are being handled, possibly by adding wildc LL | let _ = match x {}; //~ ERROR non-exhaustive | ^ -error[E0004]: non-exhaustive patterns: type [Void; 1] is non-empty +error[E0004]: non-exhaustive patterns: type `[Void; 1]` is non-empty --> $DIR/uninhabited-matches-feature-gated.rs:26:19 | LL | let _ = match x {}; //~ ERROR non-exhaustive From 0a8b6967935c4da016afb4ee0b74e12e6e40ad63 Mon Sep 17 00:00:00 2001 From: varkor <github@varkor.com> Date: Thu, 20 Dec 2018 19:24:16 +0000 Subject: [PATCH 23/23] Remove nil-enum test --- src/test/debuginfo/nil-enum.rs | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 src/test/debuginfo/nil-enum.rs diff --git a/src/test/debuginfo/nil-enum.rs b/src/test/debuginfo/nil-enum.rs deleted file mode 100644 index 27e8e9d17e82c..0000000000000 --- a/src/test/debuginfo/nil-enum.rs +++ /dev/null @@ -1,24 +0,0 @@ -// LLDB can't handle zero-sized values. -// ignore-lldb - -// compile-flags:-g -// gdb-command:run - -// gdb-command:print *first -// gdbg-check:$1 = {<No data fields>} -// gdbr-check:$1 = <error reading variable> - -#![allow(unused_variables)] -#![feature(omit_gdb_pretty_printer_section)] -#![feature(maybe_uninit)] -#![omit_gdb_pretty_printer_section] - -enum Void {} - -fn main() { - let first: *const Void = 1 as *const _; - - zzz(); // #break -} - -fn zzz() {}