Skip to content

Commit f375222

Browse files
authored
Merge pull request rust-lang#100 from oli-obk/tuple_struct_constructor_as_fn
allow the use of tuple struct constructors as functions
2 parents 0daa3b2 + 0595f95 commit f375222

File tree

7 files changed

+88
-3
lines changed

7 files changed

+88
-3
lines changed

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub enum EvalError<'tcx> {
5252
ReallocatedFrozenMemory,
5353
DeallocatedFrozenMemory,
5454
Layout(layout::LayoutError<'tcx>),
55+
Unreachable,
5556
}
5657

5758
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
@@ -122,6 +123,8 @@ impl<'tcx> Error for EvalError<'tcx> {
122123
"rustc layout computation failed",
123124
EvalError::UnterminatedCString(_) =>
124125
"attempted to get length of a null terminated string, but no null found before end of allocation",
126+
EvalError::Unreachable =>
127+
"entered unreachable code",
125128
}
126129
}
127130

src/eval_context.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc::ty::layout::{self, Layout, Size};
1010
use rustc::ty::subst::{self, Subst, Substs};
1111
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
1212
use rustc_data_structures::indexed_vec::Idx;
13+
use rustc_data_structures::fx::FxHashSet;
1314
use syntax::codemap::{self, DUMMY_SP};
1415

1516
use error::{EvalError, EvalResult};
@@ -1468,3 +1469,7 @@ pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty::
14681469
let substituted = &f.ty(tcx, substs);
14691470
tcx.normalize_associated_type(&substituted)
14701471
}
1472+
1473+
pub fn is_inhabited<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
1474+
ty.uninhabited_from(&mut FxHashSet::default(), tcx).is_empty()
1475+
}

src/terminator/mod.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use syntax::codemap::{DUMMY_SP, Span};
99
use syntax::{ast, attr};
1010

1111
use error::{EvalError, EvalResult};
12-
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty};
12+
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty, is_inhabited};
1313
use lvalue::{Lvalue, LvalueExtra};
1414
use memory::Pointer;
1515
use value::PrimVal;
@@ -155,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
155155

156156
DropAndReplace { .. } => unimplemented!(),
157157
Resume => unimplemented!(),
158-
Unreachable => unimplemented!(),
158+
Unreachable => return Err(EvalError::Unreachable),
159159
}
160160

161161
Ok(())
@@ -200,7 +200,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
200200
Abi::RustIntrinsic => {
201201
let ty = fn_ty.sig.0.output();
202202
let layout = self.type_layout(ty)?;
203-
let (ret, target) = destination.unwrap();
203+
let (ret, target) = match destination {
204+
Some(dest) if is_inhabited(self.tcx, ty) => dest,
205+
_ => return Err(EvalError::Unreachable),
206+
};
204207
self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?;
205208
Ok(())
206209
}
@@ -229,6 +232,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
229232
(def_id, substs, Vec::new())
230233
};
231234

235+
// FIXME(eddyb) Detect ADT constructors more efficiently.
236+
if let Some(adt_def) = fn_ty.sig.skip_binder().output().ty_adt_def() {
237+
if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) {
238+
let (lvalue, target) = destination.expect("tuple struct constructors can't diverge");
239+
let dest_ty = self.tcx.item_type(adt_def.did);
240+
let dest_layout = self.type_layout(dest_ty)?;
241+
match *dest_layout {
242+
Layout::Univariant { ref variant, .. } => {
243+
assert_eq!(v.disr_val.to_u128_unchecked(), 0);
244+
let offsets = variant.offsets.iter().map(|s| s.bytes());
245+
246+
// FIXME: don't allocate for single or dual field structs
247+
let dest = self.force_allocation(lvalue)?.to_ptr();
248+
249+
for (offset, (value, value_ty)) in offsets.into_iter().zip(args) {
250+
let field_dest = dest.offset(offset);
251+
self.write_value_to_ptr(value, field_dest, value_ty)?;
252+
}
253+
},
254+
// FIXME: enum variant constructors
255+
_ => bug!("bad layout for tuple struct constructor: {:?}", dest_layout),
256+
}
257+
self.goto_block(target);
258+
return Ok(());
259+
}
260+
}
261+
232262
let mir = self.load_mir(resolved_def_id)?;
233263
let (return_lvalue, return_to_block) = match destination {
234264
Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),

tests/compile-fail/never_say_never.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(never_type)]
2+
#![allow(unreachable_code)]
3+
4+
fn main() {
5+
let y = &5;
6+
let x: ! = unsafe {
7+
*(y as *const _ as *const !) //~ ERROR entered unreachable code
8+
};
9+
f(x)
10+
}
11+
12+
fn f(x: !) -> ! { x }
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(never_type)]
2+
#![allow(unreachable_code)]
3+
#![allow(unused_variables)]
4+
5+
struct Human;
6+
7+
fn main() {
8+
let x: ! = unsafe {
9+
std::mem::transmute::<Human, !>(Human) //~ ERROR entered unreachable code
10+
};
11+
f(x)
12+
}
13+
14+
fn f(x: !) -> ! { x }
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![feature(never_type)]
2+
#![allow(unreachable_code)]
3+
#![allow(unused_variables)]
4+
5+
enum Void {}
6+
7+
fn f(v: Void) -> ! {
8+
match v {}
9+
}
10+
11+
fn main() {
12+
let v: Void = unsafe {
13+
std::mem::transmute::<(), Void>(()) //~ ERROR entered unreachable code
14+
};
15+
f(v);
16+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
#[derive(PartialEq, Eq, Debug)]
3+
struct A(i32);
4+
assert_eq!(Some(42).map(A), Some(A(42)));
5+
}

0 commit comments

Comments
 (0)