Merge pull request #100 from oli-obk/tuple_struct_constructor_as_fn
allow the use of tuple struct constructors as functions
This commit is contained in:
commit
f3752220af
@ -52,6 +52,7 @@ pub enum EvalError<'tcx> {
|
||||
ReallocatedFrozenMemory,
|
||||
DeallocatedFrozenMemory,
|
||||
Layout(layout::LayoutError<'tcx>),
|
||||
Unreachable,
|
||||
}
|
||||
|
||||
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
|
||||
@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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;
|
||||
@ -155,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
DropAndReplace { .. } => unimplemented!(),
|
||||
Resume => unimplemented!(),
|
||||
Unreachable => unimplemented!(),
|
||||
Unreachable => return Err(EvalError::Unreachable),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -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) if is_inhabited(self.tcx, ty) => dest,
|
||||
_ => return Err(EvalError::Unreachable),
|
||||
};
|
||||
self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -229,6 +232,33 @@ 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) {
|
||||
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)),
|
||||
|
12
tests/compile-fail/never_say_never.rs
Normal file
12
tests/compile-fail/never_say_never.rs
Normal file
@ -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 }
|
14
tests/compile-fail/never_transmute_humans.rs
Normal file
14
tests/compile-fail/never_transmute_humans.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#![feature(never_type)]
|
||||
#![allow(unreachable_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
struct Human;
|
||||
|
||||
fn main() {
|
||||
let x: ! = unsafe {
|
||||
std::mem::transmute::<Human, !>(Human) //~ ERROR entered unreachable code
|
||||
};
|
||||
f(x)
|
||||
}
|
||||
|
||||
fn f(x: !) -> ! { x }
|
16
tests/compile-fail/never_transmute_void.rs
Normal file
16
tests/compile-fail/never_transmute_void.rs
Normal file
@ -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);
|
||||
}
|
5
tests/run-pass/tuple_like_struct_constructor.rs
Normal file
5
tests/run-pass/tuple_like_struct_constructor.rs
Normal file
@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
struct A(i32);
|
||||
assert_eq!(Some(42).map(A), Some(A(42)));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user