From 7c486416cb68efcefcf216037d1bcaab60e88133 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 08:52:22 +0100 Subject: [PATCH 1/6] allow the use of tuple struct constructors as functions --- src/terminator/mod.rs | 28 +++++++++++++++++++ .../run-pass/tuple_like_struct_constructor.rs | 5 ++++ 2 files changed, 33 insertions(+) create mode 100644 tests/run-pass/tuple_like_struct_constructor.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 6959da650b4..1b5ff9da9d7 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -229,6 +229,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (def_id, substs, Vec::new()) }; + // FIXME(eddyb) Detect ADT constructors more efficiently. + if let Some(adt_def) = fn_ty.sig.skip_binder().output().ty_adt_def() { + if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) { + // technically they can diverge, but only if one of their arguments diverges, so it doesn't matter + let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); + let dest_ty = self.tcx.item_type(adt_def.did); + let dest_layout = self.type_layout(dest_ty)?; + match *dest_layout { + Layout::Univariant { ref variant, .. } => { + assert_eq!(v.disr_val.to_u128_unchecked(), 0); + let offsets = variant.offsets.iter().map(|s| s.bytes()); + + // FIXME: don't allocate for single or dual field structs + let dest = self.force_allocation(lvalue)?.to_ptr(); + + for (offset, (value, value_ty)) in offsets.into_iter().zip(args) { + let field_dest = dest.offset(offset); + self.write_value_to_ptr(value, field_dest, value_ty)?; + } + }, + // FIXME: enum variant constructors + _ => bug!("bad layout for tuple struct constructor: {:?}", dest_layout), + } + self.goto_block(target); + return Ok(()); + } + } + let mir = self.load_mir(resolved_def_id)?; let (return_lvalue, return_to_block) = match destination { Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), diff --git a/tests/run-pass/tuple_like_struct_constructor.rs b/tests/run-pass/tuple_like_struct_constructor.rs new file mode 100644 index 00000000000..05e8893de17 --- /dev/null +++ b/tests/run-pass/tuple_like_struct_constructor.rs @@ -0,0 +1,5 @@ +fn main() { + #[derive(PartialEq, Eq, Debug)] + struct A(i32); + assert_eq!(Some(42).map(A), Some(A(42))); +} From 753dbcf158822199e8ae8c1aee99fd6cb036b623 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 09:41:36 +0100 Subject: [PATCH 2/6] add a test for dereferencing a pointer to a `!` --- src/error.rs | 3 +++ src/terminator/mod.rs | 2 +- tests/compile-fail/never_say_never.rs | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/never_say_never.rs diff --git a/src/error.rs b/src/error.rs index bf01ae27d2a..bf1c71f089d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -52,6 +52,7 @@ pub enum EvalError<'tcx> { ReallocatedFrozenMemory, DeallocatedFrozenMemory, Layout(layout::LayoutError<'tcx>), + Unreachable, } pub type EvalResult<'tcx, T> = Result>; @@ -122,6 +123,8 @@ impl<'tcx> Error for EvalError<'tcx> { "rustc layout computation failed", EvalError::UnterminatedCString(_) => "attempted to get length of a null terminated string, but no null found before end of allocation", + EvalError::Unreachable => + "entered unreachable code", } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 1b5ff9da9d7..1b302523ff2 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { DropAndReplace { .. } => unimplemented!(), Resume => unimplemented!(), - Unreachable => unimplemented!(), + Unreachable => return Err(EvalError::Unreachable), } Ok(()) diff --git a/tests/compile-fail/never_say_never.rs b/tests/compile-fail/never_say_never.rs new file mode 100644 index 00000000000..5d7e9fec62c --- /dev/null +++ b/tests/compile-fail/never_say_never.rs @@ -0,0 +1,12 @@ +#![feature(never_type)] +#![allow(unreachable_code)] + +fn main() { + let y = &5; + let x: ! = unsafe { + *(y as *const _ as *const !) //~ ERROR entered unreachable code + }; + f(x) +} + +fn f(x: !) -> ! { x } From b5f824fd9c34f54961831fdef4107ed31ba45e04 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 09:59:00 +0100 Subject: [PATCH 3/6] fix ICE when transmuting inhabited types to uninhabited --- src/terminator/mod.rs | 5 ++++- tests/compile-fail/never_transmute_humans.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/never_transmute_humans.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 1b302523ff2..4c02adbd07a 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -200,7 +200,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::RustIntrinsic => { let ty = fn_ty.sig.0.output(); let layout = self.type_layout(ty)?; - let (ret, target) = destination.unwrap(); + let (ret, target) = match destination { + Some(dest) => dest, + None => return Err(EvalError::Unreachable), + }; self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; Ok(()) } diff --git a/tests/compile-fail/never_transmute_humans.rs b/tests/compile-fail/never_transmute_humans.rs new file mode 100644 index 00000000000..38406eeb3fe --- /dev/null +++ b/tests/compile-fail/never_transmute_humans.rs @@ -0,0 +1,14 @@ +#![feature(never_type)] +#![allow(unreachable_code)] +#![allow(unused_variables)] + +struct Human; + +fn main() { + let x: ! = unsafe { + std::mem::transmute::(Human) //~ ERROR entered unreachable code + }; + f(x) +} + +fn f(x: !) -> ! { x } From a58170a4c6286f16805ee474b4814f264b5964a2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 10:37:14 +0100 Subject: [PATCH 4/6] prevent intrinsics from creating uninhabited types --- src/eval_context.rs | 5 +++++ src/terminator/mod.rs | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 7a0264665e5..fe5beb2e5cf 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -10,6 +10,7 @@ use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::indexed_vec::Idx; +use rustc_data_structures::fx::FxHashSet; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; @@ -1468,3 +1469,7 @@ pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty:: let substituted = &f.ty(tcx, substs); tcx.normalize_associated_type(&substituted) } + +pub fn is_inhabited<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.uninhabited_from(&mut FxHashSet::default(), tcx).is_empty() +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4c02adbd07a..5cc640142c6 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -9,7 +9,7 @@ use syntax::codemap::{DUMMY_SP, Span}; use syntax::{ast, attr}; use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty}; +use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty, is_inhabited}; use lvalue::{Lvalue, LvalueExtra}; use memory::Pointer; use value::PrimVal; @@ -201,8 +201,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = fn_ty.sig.0.output(); let layout = self.type_layout(ty)?; let (ret, target) = match destination { - Some(dest) => dest, - None => return Err(EvalError::Unreachable), + Some(dest) if is_inhabited(self.tcx, ty) => dest, + _ => return Err(EvalError::Unreachable), }; self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; Ok(()) From 548a6baec0312b53832e0b382d904bd1d26b4c1f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 11:23:44 +0100 Subject: [PATCH 5/6] also test transmutes to empty enums --- tests/compile-fail/never_transmute_void.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/compile-fail/never_transmute_void.rs diff --git a/tests/compile-fail/never_transmute_void.rs b/tests/compile-fail/never_transmute_void.rs new file mode 100644 index 00000000000..3fffacc55ea --- /dev/null +++ b/tests/compile-fail/never_transmute_void.rs @@ -0,0 +1,16 @@ +#![feature(never_type)] +#![allow(unreachable_code)] +#![allow(unused_variables)] + +enum Void {} + +fn f(v: Void) -> ! { + match v {} +} + +fn main() { + let v: Void = unsafe { + std::mem::transmute::<(), Void>(()) //~ ERROR entered unreachable code + }; + f(v); +} From 0595f9546085ac53e57fe4776687794b0bf2bb7d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 11:27:04 +0100 Subject: [PATCH 6/6] remove old comment --- src/terminator/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 5cc640142c6..7107a99e8b5 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -235,7 +235,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(eddyb) Detect ADT constructors more efficiently. if let Some(adt_def) = fn_ty.sig.skip_binder().output().ty_adt_def() { if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) { - // technically they can diverge, but only if one of their arguments diverges, so it doesn't matter let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); let dest_ty = self.tcx.item_type(adt_def.did); let dest_layout = self.type_layout(dest_ty)?;