diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index f583f601726..11919db479c 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -14,6 +14,7 @@ use std::rc::Rc; use hir::def_id::DefId; use rustc_const_math::*; use self::ConstVal::*; +pub use rustc_const_math::ConstInt; use std::collections::BTreeMap; @@ -48,4 +49,14 @@ impl ConstVal { Char(..) => "char", } } + + pub fn to_const_int(&self) -> Option { + match *self { + ConstVal::Integral(i) => Some(i), + ConstVal::Bool(true) => Some(ConstInt::Infer(1)), + ConstVal::Bool(false) => Some(ConstInt::Infer(0)), + ConstVal::Char(ch) => Some(ConstInt::U32(ch as u32)), + _ => None + } + } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index cbb7b2710f5..d8212807eb2 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -453,36 +453,30 @@ pub enum TerminatorKind<'tcx> { target: BasicBlock, }, - /// jump to branch 0 if this lvalue evaluates to true - If { - cond: Operand<'tcx>, - targets: (BasicBlock, BasicBlock), - }, - - /// lvalue evaluates to some enum; jump depending on the branch - Switch { - discr: Lvalue<'tcx>, - adt_def: &'tcx AdtDef, - targets: Vec, - }, - /// operand evaluates to an integer; jump depending on its value /// to one of the targets, and otherwise fallback to `otherwise` SwitchInt { /// discriminant value being tested - discr: Lvalue<'tcx>, + discr: Operand<'tcx>, /// type of value being tested switch_ty: Ty<'tcx>, /// Possible values. The locations to branch to in each case /// are found in the corresponding indices from the `targets` vector. - values: Vec, + values: Cow<'tcx, [ConstInt]>, - /// Possible branch sites. The length of this vector should be - /// equal to the length of the `values` vector plus 1 -- the - /// extra item is the block to branch to if none of the values - /// fit. + /// Possible branch sites. The last element of this vector is used + /// for the otherwise branch, so values.len() == targets.len() + 1 + /// should hold. + // This invariant is quite non-obvious and also could be improved. + // One way to make this invariant is to have something like this instead: + // + // branches: Vec<(ConstInt, BasicBlock)>, + // otherwise: Option // exhaustive if None + // + // However we’ve decided to keep this as-is until we figure a case + // where some other approach seems to be strictly better than other. targets: Vec, }, @@ -546,12 +540,21 @@ impl<'tcx> Terminator<'tcx> { } impl<'tcx> TerminatorKind<'tcx> { + pub fn if_<'a, 'gcx>(tcx: ty::TyCtxt<'a, 'gcx, 'tcx>, cond: Operand<'tcx>, + t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> { + static BOOL_SWITCH_FALSE: &'static [ConstInt] = &[ConstInt::Infer(0)]; + TerminatorKind::SwitchInt { + discr: cond, + switch_ty: tcx.types.bool, + values: From::from(BOOL_SWITCH_FALSE), + targets: vec![f, t], + } + } + pub fn successors(&self) -> Cow<[BasicBlock]> { use self::TerminatorKind::*; match *self { Goto { target: ref b } => slice::ref_slice(b).into_cow(), - If { targets: (b1, b2), .. } => vec![b1, b2].into_cow(), - Switch { targets: ref b, .. } => b[..].into_cow(), SwitchInt { targets: ref b, .. } => b[..].into_cow(), Resume => (&[]).into_cow(), Return => (&[]).into_cow(), @@ -580,8 +583,6 @@ impl<'tcx> TerminatorKind<'tcx> { use self::TerminatorKind::*; match *self { Goto { target: ref mut b } => vec![b], - If { targets: (ref mut b1, ref mut b2), .. } => vec![b1, b2], - Switch { targets: ref mut b, .. } => b.iter_mut().collect(), SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(), Resume => Vec::new(), Return => Vec::new(), @@ -659,8 +660,6 @@ impl<'tcx> TerminatorKind<'tcx> { use self::TerminatorKind::*; match *self { Goto { .. } => write!(fmt, "goto"), - If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv), - Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv), SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), Return => write!(fmt, "return"), Resume => write!(fmt, "resume"), @@ -710,18 +709,11 @@ impl<'tcx> TerminatorKind<'tcx> { match *self { Return | Resume | Unreachable => vec![], Goto { .. } => vec!["".into()], - If { .. } => vec!["true".into(), "false".into()], - Switch { ref adt_def, .. } => { - adt_def.variants - .iter() - .map(|variant| variant.name.to_string().into()) - .collect() - } SwitchInt { ref values, .. } => { values.iter() .map(|const_val| { let mut buf = String::new(); - fmt_const_val(&mut buf, const_val).unwrap(); + fmt_const_val(&mut buf, &ConstVal::Integral(*const_val)).unwrap(); buf.into() }) .chain(iter::once(String::from("otherwise").into())) @@ -997,6 +989,12 @@ pub enum Rvalue<'tcx> { UnaryOp(UnOp, Operand<'tcx>), + /// Read the discriminant of an ADT. + /// + /// Undefined (i.e. no effort is made to make it defined, but there’s no reason why it cannot + /// be defined to return, say, a 0) if ADT is not an enum. + Discriminant(Lvalue<'tcx>), + /// Creates an *uninitialized* Box Box(Ty<'tcx>), @@ -1111,6 +1109,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) } UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), + Discriminant(ref lval) => write!(fmt, "discriminant({:?})", lval), Box(ref t) => write!(fmt, "Box({:?})", t), InlineAsm { ref asm, ref outputs, ref inputs } => { write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs) diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index f1268521d67..7b0863b4c42 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -18,6 +18,7 @@ use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use hir; +use ty::util::IntTypeExt; #[derive(Copy, Clone, Debug)] pub enum LvalueTy<'tcx> { @@ -135,15 +136,15 @@ impl<'tcx> Lvalue<'tcx> { impl<'tcx> Rvalue<'tcx> { pub fn ty<'a, 'gcx>(&self, mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option> { - match self { - &Rvalue::Use(ref operand) => Some(operand.ty(mir, tcx)), - &Rvalue::Repeat(ref operand, ref count) => { + match *self { + Rvalue::Use(ref operand) => Some(operand.ty(mir, tcx)), + Rvalue::Repeat(ref operand, ref count) => { let op_ty = operand.ty(mir, tcx); let count = count.value.as_u64(tcx.sess.target.uint_type); assert_eq!(count as usize as u64, count); Some(tcx.mk_array(op_ty, count as usize)) } - &Rvalue::Ref(reg, bk, ref lv) => { + Rvalue::Ref(reg, bk, ref lv) => { let lv_ty = lv.ty(mir, tcx).to_ty(tcx); Some(tcx.mk_ref(reg, ty::TypeAndMut { @@ -152,27 +153,37 @@ impl<'tcx> Rvalue<'tcx> { } )) } - &Rvalue::Len(..) => Some(tcx.types.usize), - &Rvalue::Cast(.., ty) => Some(ty), - &Rvalue::BinaryOp(op, ref lhs, ref rhs) => { + Rvalue::Len(..) => Some(tcx.types.usize), + Rvalue::Cast(.., ty) => Some(ty), + Rvalue::BinaryOp(op, ref lhs, ref rhs) => { let lhs_ty = lhs.ty(mir, tcx); let rhs_ty = rhs.ty(mir, tcx); Some(op.ty(tcx, lhs_ty, rhs_ty)) } - &Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { let lhs_ty = lhs.ty(mir, tcx); let rhs_ty = rhs.ty(mir, tcx); let ty = op.ty(tcx, lhs_ty, rhs_ty); let ty = tcx.intern_tup(&[ty, tcx.types.bool], false); Some(ty) } - &Rvalue::UnaryOp(_, ref operand) => { + Rvalue::UnaryOp(_, ref operand) => { Some(operand.ty(mir, tcx)) } - &Rvalue::Box(t) => { + Rvalue::Discriminant(ref lval) => { + let ty = lval.ty(mir, tcx).to_ty(tcx); + if let ty::TyAdt(adt_def, _) = ty.sty { + Some(adt_def.discr_ty.to_ty(tcx)) + } else { + // Undefined behaviour, bug for now; may want to return something for + // the `discriminant` intrinsic later. + bug!("Rvalue::Discriminant on Lvalue of type {:?}", ty); + } + } + Rvalue::Box(t) => { Some(tcx.mk_box(t)) } - &Rvalue::Aggregate(ref ak, ref ops) => { + Rvalue::Aggregate(ref ak, ref ops) => { match *ak { AggregateKind::Array => { if let Some(operand) = ops.get(0) { @@ -196,7 +207,7 @@ impl<'tcx> Rvalue<'tcx> { } } } - &Rvalue::InlineAsm { .. } => None + Rvalue::InlineAsm { .. } => None } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index b5da304a109..be3c43db7ba 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -14,7 +14,6 @@ use ty::subst::Substs; use ty::{ClosureSubsts, Region, Ty}; use mir::*; use rustc_const_math::ConstUsize; -use rustc_data_structures::tuple_slice::TupleSlice; use rustc_data_structures::indexed_vec::Idx; use syntax_pos::Span; @@ -224,6 +223,12 @@ macro_rules! make_mir_visitor { self.super_const_val(const_val); } + fn visit_const_int(&mut self, + const_int: &ConstInt, + _: Location) { + self.super_const_int(const_int); + } + fn visit_const_usize(&mut self, const_usize: & $($mutability)* ConstUsize, _: Location) { @@ -363,31 +368,14 @@ macro_rules! make_mir_visitor { self.visit_branch(block, target); } - TerminatorKind::If { ref $($mutability)* cond, - ref $($mutability)* targets } => { - self.visit_operand(cond, source_location); - for &target in targets.as_slice() { - self.visit_branch(block, target); - } - } - - TerminatorKind::Switch { ref $($mutability)* discr, - adt_def: _, - ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect, source_location); - for &target in targets { - self.visit_branch(block, target); - } - } - TerminatorKind::SwitchInt { ref $($mutability)* discr, ref $($mutability)* switch_ty, - ref $($mutability)* values, + ref values, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect, source_location); + self.visit_operand(discr, source_location); self.visit_ty(switch_ty); - for value in values { - self.visit_const_val(value, source_location); + for value in &values[..] { + self.visit_const_int(value, source_location); } for &target in targets { self.visit_branch(block, target); @@ -506,6 +494,10 @@ macro_rules! make_mir_visitor { self.visit_operand(op, location); } + Rvalue::Discriminant(ref $($mutability)* lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Inspect, location); + } + Rvalue::Box(ref $($mutability)* ty) => { self.visit_ty(ty); } @@ -712,10 +704,13 @@ macro_rules! make_mir_visitor { _substs: & $($mutability)* ClosureSubsts<'tcx>) { } - fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) { + fn super_const_val(&mut self, _const_val: & $($mutability)* ConstVal) { } - fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) { + fn super_const_int(&mut self, _const_int: &ConstInt) { + } + + fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) { } // Convenience methods diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 6203679a510..19bb8a63aa2 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -673,10 +673,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn alloc_adt_def(self, did: DefId, kind: AdtKind, + discr_ty: Option, variants: Vec, repr: ReprOptions) -> &'gcx ty::AdtDef { - let def = ty::AdtDef::new(self, did, kind, variants, repr); + let discr_ty = discr_ty.unwrap_or(attr::UnsignedInt(ast::UintTy::U8)); + let def = ty::AdtDef::new(self, did, kind, discr_ty, variants, repr); self.global_arenas.adt_def.alloc(def) } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 3a463e981a6..f429053d8bb 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -20,7 +20,6 @@ use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions}; use syntax::ast::{FloatTy, IntTy, UintTy}; use syntax::attr; use syntax_pos::DUMMY_SP; -use rustc_const_math::ConstInt; use std::cmp; use std::fmt; @@ -1183,11 +1182,7 @@ impl<'a, 'gcx, 'tcx> Layout { i64::min_value(), true); for v in &def.variants { - let x = match v.disr_val.erase_type() { - ConstInt::InferSigned(i) => i as i64, - ConstInt::Infer(i) => i as u64 as i64, - _ => bug!() - }; + let x = v.disr_val as i128 as i64; if x == 0 { non_zero = false; } if x < min { min = x; } if x > max { max = x; } @@ -1195,9 +1190,7 @@ impl<'a, 'gcx, 'tcx> Layout { // FIXME: should handle i128? signed-value based impl is weird and hard to // grok. - let (discr, signed) = Integer::repr_discr(tcx, ty, &def.repr, - min, - max); + let (discr, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); return success(CEnum { discr: discr, signed: signed, @@ -1247,7 +1240,7 @@ impl<'a, 'gcx, 'tcx> Layout { // non-empty body, explicit discriminants should have // been rejected by a checker before this point. for (i, v) in def.variants.iter().enumerate() { - if i as u128 != v.disr_val.to_u128_unchecked() { + if i as u128 != v.disr_val { bug!("non-C-like enum {} with specified discriminants", tcx.item_path_str(def.did)); } @@ -1315,7 +1308,6 @@ impl<'a, 'gcx, 'tcx> Layout { let discr_max = (variants.len() - 1) as i64; assert!(discr_max >= 0); let (min_ity, _) = Integer::repr_discr(tcx, ty, &def.repr, 0, discr_max); - let mut align = dl.aggregate_align; let mut size = Size::from_bytes(0); @@ -1356,6 +1348,23 @@ impl<'a, 'gcx, 'tcx> Layout { return Err(LayoutError::SizeOverflow(ty)); } + let typeck_ity = Integer::from_attr(dl, def.discr_ty); + if typeck_ity < min_ity { + // It is a bug if Layout decided on a greater discriminant size than typeck for + // some reason at this point (based on values discriminant can take on). Mostly + // because this discriminant will be loaded, and then stored into variable of + // type calculated by typeck. Consider such case (a bug): typeck decided on + // byte-sized discriminant, but layout thinks we need a 16-bit to store all + // discriminant values. That would be a bug, because then, in trans, in order + // to store this 16-bit discriminant into 8-bit sized temporary some of the + // space necessary to represent would have to be discarded (or layout is wrong + // on thinking it needs 16 bits) + bug!("layout decided on a larger discriminant type ({:?}) than typeck ({:?})", + min_ity, typeck_ity); + // However, it is fine to make discr type however large (as an optimisation) + // after this point – we’ll just truncate the value we load in trans. + } + // Check to see if we should use a different type for the // discriminant. We can safely use a type with the same size // as the alignment of the first field of each variant. diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index beb286108a0..411e14531fa 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -45,7 +45,6 @@ use syntax::attr; use syntax::symbol::{Symbol, InternedString}; use syntax_pos::{DUMMY_SP, Span}; -use rustc_const_math::ConstInt; use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter; use hir; @@ -96,7 +95,7 @@ mod flags; mod structural_impls; mod sty; -pub type Disr = ConstInt; +pub type Disr = u128; // Data types @@ -1325,6 +1324,12 @@ pub struct FieldDef { /// table. pub struct AdtDef { pub did: DefId, + /// Type of the discriminant + /// + /// Note, that this is the type specified in `repr()` or a default type of some sort, and might + /// not match the actual type that layout algorithm decides to use when translating this type + /// into LLVM. That being said, layout algorithm may not use a type larger than specified here. + pub discr_ty: attr::IntType, pub variants: Vec, destructor: Cell>, flags: Cell, @@ -1387,6 +1392,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, did: DefId, kind: AdtKind, + discr_ty: attr::IntType, variants: Vec, repr: ReprOptions) -> Self { let mut flags = AdtFlags::NO_ADT_FLAGS; @@ -1410,6 +1416,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } AdtDef { did: did, + discr_ty: discr_ty, variants: variants, flags: Cell::new(flags), destructor: Cell::new(None), diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index b01b77bbcf8..16492de6c3d 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -23,7 +23,6 @@ use ty::TypeVariants::*; use util::nodemap::FxHashMap; use middle::lang_items; -use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult}; use std::cell::RefCell; @@ -37,84 +36,20 @@ use syntax_pos::Span; use hir; pub trait IntTypeExt { - fn to_ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx>; - fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option; - fn assert_ty_matches(&self, val: Disr); - fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr; + fn to_ty<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>; + fn initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr; } impl IntTypeExt for attr::IntType { - fn to_ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { - match *self { - SignedInt(ast::IntTy::I8) => tcx.types.i8, - SignedInt(ast::IntTy::I16) => tcx.types.i16, - SignedInt(ast::IntTy::I32) => tcx.types.i32, - SignedInt(ast::IntTy::I64) => tcx.types.i64, - SignedInt(ast::IntTy::I128) => tcx.types.i128, - SignedInt(ast::IntTy::Is) => tcx.types.isize, - UnsignedInt(ast::UintTy::U8) => tcx.types.u8, - UnsignedInt(ast::UintTy::U16) => tcx.types.u16, - UnsignedInt(ast::UintTy::U32) => tcx.types.u32, - UnsignedInt(ast::UintTy::U64) => tcx.types.u64, - UnsignedInt(ast::UintTy::U128) => tcx.types.u128, - UnsignedInt(ast::UintTy::Us) => tcx.types.usize, + fn to_ty<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { + match self { + SignedInt(i) => tcx.mk_mach_int(i), + UnsignedInt(i) => tcx.mk_mach_uint(i), } } - fn initial_discriminant<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Disr { - match *self { - SignedInt(ast::IntTy::I8) => ConstInt::I8(0), - SignedInt(ast::IntTy::I16) => ConstInt::I16(0), - SignedInt(ast::IntTy::I32) => ConstInt::I32(0), - SignedInt(ast::IntTy::I64) => ConstInt::I64(0), - SignedInt(ast::IntTy::I128) => ConstInt::I128(0), - SignedInt(ast::IntTy::Is) => match tcx.sess.target.int_type { - ast::IntTy::I16 => ConstInt::Isize(ConstIsize::Is16(0)), - ast::IntTy::I32 => ConstInt::Isize(ConstIsize::Is32(0)), - ast::IntTy::I64 => ConstInt::Isize(ConstIsize::Is64(0)), - _ => bug!(), - }, - UnsignedInt(ast::UintTy::U8) => ConstInt::U8(0), - UnsignedInt(ast::UintTy::U16) => ConstInt::U16(0), - UnsignedInt(ast::UintTy::U32) => ConstInt::U32(0), - UnsignedInt(ast::UintTy::U64) => ConstInt::U64(0), - UnsignedInt(ast::UintTy::U128) => ConstInt::U128(0), - UnsignedInt(ast::UintTy::Us) => match tcx.sess.target.uint_type { - ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16(0)), - ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32(0)), - ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64(0)), - _ => bug!(), - }, - } - } - - fn assert_ty_matches(&self, val: Disr) { - match (*self, val) { - (SignedInt(ast::IntTy::I8), ConstInt::I8(_)) => {}, - (SignedInt(ast::IntTy::I16), ConstInt::I16(_)) => {}, - (SignedInt(ast::IntTy::I32), ConstInt::I32(_)) => {}, - (SignedInt(ast::IntTy::I64), ConstInt::I64(_)) => {}, - (SignedInt(ast::IntTy::I128), ConstInt::I128(_)) => {}, - (SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) => {}, - (UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) => {}, - (UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) => {}, - (UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) => {}, - (UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) => {}, - (UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) => {}, - (UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => {}, - _ => bug!("disr type mismatch: {:?} vs {:?}", self, val), - } - } - - fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option { - if let Some(val) = val { - self.assert_ty_matches(val); - (val + ConstInt::Infer(1)).ok() - } else { - Some(self.initial_discriminant(tcx)) - } + fn initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr { + 0 } } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index f11cf90834d..8b246105f61 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -454,11 +454,6 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> self.propagate_bits_into_entry_set_for(in_out, changed, target); self.propagate_bits_into_entry_set_for(in_out, changed, unwind); } - mir::TerminatorKind::If { ref targets, .. } => { - self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0); - self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1); - } - mir::TerminatorKind::Switch { ref targets, .. } | mir::TerminatorKind::SwitchInt { ref targets, .. } => { for target in targets { self.propagate_bits_into_entry_set_for(in_out, changed, target); diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index d2f744bde2d..5899c9f31d1 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -17,9 +17,10 @@ use super::{DropFlagState, MoveDataParamEnv}; use super::patch::MirPatch; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Subst, Substs}; +use rustc::ty::util::IntTypeExt; use rustc::mir::*; use rustc::mir::transform::{Pass, MirPass, MirSource}; -use rustc::middle::const_val::ConstVal; +use rustc::middle::const_val::{ConstVal, ConstInt}; use rustc::middle::lang_items; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_set::IdxSetBuf; @@ -619,48 +620,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { self.elaborated_drop_block(&inner_c) } - fn open_drop_for_variant<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - drop_block: &mut Option, - adt: &'tcx ty::AdtDef, - substs: &'tcx Substs<'tcx>, - variant_index: usize) - -> BasicBlock - { - let subpath = super::move_path_children_matching( - self.move_data(), c.path, |proj| match proj { - &Projection { - elem: ProjectionElem::Downcast(_, idx), .. - } => idx == variant_index, - _ => false - }); - - if let Some(variant_path) = subpath { - let base_lv = c.lvalue.clone().elem( - ProjectionElem::Downcast(adt, variant_index) - ); - let fields = self.move_paths_for_fields( - &base_lv, - variant_path, - &adt.variants[variant_index], - substs); - self.drop_ladder(c, fields) - } else { - // variant not found - drop the entire enum - if let None = *drop_block { - *drop_block = Some(self.complete_drop(c, true)); - } - return drop_block.unwrap(); - } - } - fn open_drop_for_adt<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>) -> BasicBlock { debug!("open_drop_for_adt({:?}, {:?}, {:?})", c, adt, substs); - let mut drop_block = None; - match adt.variants.len() { 1 => { let fields = self.move_paths_for_fields( @@ -672,12 +636,43 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { self.drop_ladder(c, fields) } _ => { - let variant_drops : Vec = - (0..adt.variants.len()).map(|i| { - self.open_drop_for_variant(c, &mut drop_block, - adt, substs, i) - }).collect(); - + let mut values = Vec::with_capacity(adt.variants.len()); + let mut blocks = Vec::with_capacity(adt.variants.len()); + let mut otherwise = None; + for (variant_index, variant) in adt.variants.iter().enumerate() { + let discr = ConstInt::new_inttype(variant.disr_val, adt.discr_ty, + self.tcx.sess.target.uint_type, + self.tcx.sess.target.int_type).unwrap(); + let subpath = super::move_path_children_matching( + self.move_data(), c.path, |proj| match proj { + &Projection { + elem: ProjectionElem::Downcast(_, idx), .. + } => idx == variant_index, + _ => false + }); + if let Some(variant_path) = subpath { + let base_lv = c.lvalue.clone().elem( + ProjectionElem::Downcast(adt, variant_index) + ); + let fields = self.move_paths_for_fields( + &base_lv, + variant_path, + &adt.variants[variant_index], + substs); + values.push(discr); + blocks.push(self.drop_ladder(c, fields)); + } else { + // variant not found - drop the entire enum + if let None = otherwise { + otherwise = Some(self.complete_drop(c, true)); + } + } + } + if let Some(block) = otherwise { + blocks.push(block); + } else { + values.pop(); + } // If there are multiple variants, then if something // is present within the enum the discriminant, tracked // by the rest path, must be initialized. @@ -685,14 +680,27 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { // Additionally, we do not want to switch on the // discriminant after it is free-ed, because that // way lies only trouble. - - let switch_block = self.new_block( - c, c.is_cleanup, TerminatorKind::Switch { - discr: c.lvalue.clone(), - adt_def: adt, - targets: variant_drops - }); - + let discr_ty = adt.discr_ty.to_ty(self.tcx); + let discr = Lvalue::Local(self.patch.new_temp(discr_ty)); + let switch_block = self.patch.new_block(BasicBlockData { + statements: vec![ + Statement { + source_info: c.source_info, + kind: StatementKind::Assign(discr.clone(), + Rvalue::Discriminant(c.lvalue.clone())) + } + ], + terminator: Some(Terminator { + source_info: c.source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Consume(discr), + switch_ty: discr_ty, + values: From::from(values), + targets: blocks, + } + }), + is_cleanup: c.is_cleanup, + }); self.drop_flag_test_block(c, switch_block) } } @@ -813,10 +821,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { (true, false) => on_set, (true, true) => { let flag = self.drop_flag(c.path).unwrap(); - self.new_block(c, is_cleanup, TerminatorKind::If { - cond: Operand::Consume(flag), - targets: (on_set, on_unset) - }) + let term = TerminatorKind::if_(self.tcx, Operand::Consume(flag), on_set, on_unset); + self.new_block(c, is_cleanup, term) } } } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 7cf6ab2999c..0c7e922c48a 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -435,6 +435,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } Rvalue::Ref(..) | + Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::InlineAsm { .. } => {} Rvalue::Box(..) => { @@ -463,10 +464,8 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { self.gather_move(loc, &Lvalue::Local(RETURN_POINTER)); } - TerminatorKind::If { .. } | TerminatorKind::Assert { .. } | - TerminatorKind::SwitchInt { .. } | - TerminatorKind::Switch { .. } => { + TerminatorKind::SwitchInt { .. } => { // branching terminators - these don't move anything } diff --git a/src/librustc_const_math/int.rs b/src/librustc_const_math/int.rs index 17714f2fb2d..bc3809db1c6 100644 --- a/src/librustc_const_math/int.rs +++ b/src/librustc_const_math/int.rs @@ -77,6 +77,14 @@ mod ibounds { } impl ConstInt { + pub fn new_inttype(val: u128, ty: IntType, usize_ty: UintTy, isize_ty: IntTy) + -> Option { + match ty { + IntType::SignedInt(i) => ConstInt::new_signed(val as i128, i, isize_ty), + IntType::UnsignedInt(i) => ConstInt::new_unsigned(val, i, usize_ty), + } + } + /// Creates a new unsigned ConstInt with matching type while also checking that overflow does /// not happen. pub fn new_unsigned(val: u128, ty: UintTy, usize_ty: UintTy) -> Option { diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index 3700d46c346..ffcd25a4cdd 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -30,6 +30,10 @@ impl BitVector { } } + pub fn count(&self) -> usize { + self.data.iter().map(|e| e.count_ones() as usize).sum() + } + #[inline] pub fn contains(&self, bit: usize) -> bool { let (word, mask) = word_mask(bit); diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index 8a958cf1ac1..adf8067987e 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -1084,11 +1084,11 @@ extern "C" { DestTy: TypeRef, Name: *const c_char) -> ValueRef; - pub fn LLVMBuildIntCast(B: BuilderRef, - Val: ValueRef, - DestTy: TypeRef, - Name: *const c_char) - -> ValueRef; + pub fn LLVMRustBuildIntCast(B: BuilderRef, + Val: ValueRef, + DestTy: TypeRef, + IsSized: bool) + -> ValueRef; pub fn LLVMBuildFPCast(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index bb99be20f64..abc3ffcf86b 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -25,8 +25,6 @@ use rustc::session::Session; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; -use rustc_const_math::ConstInt; - use rustc::mir::Mir; use std::borrow::Cow; @@ -435,7 +433,7 @@ impl<'tcx> EntryKind<'tcx> { EntryKind::Mod(_) => Def::Mod(did), EntryKind::Variant(_) => Def::Variant(did), EntryKind::Trait(_) => Def::Trait(did), - EntryKind::Enum(_) => Def::Enum(did), + EntryKind::Enum(..) => Def::Enum(did), EntryKind::MacroDef(_) => Def::Macro(did), EntryKind::ForeignMod | @@ -535,7 +533,7 @@ impl<'a, 'tcx> CrateMetadata { vis: f.visibility.decode(self) } }).collect(), - disr_val: ConstInt::Infer(data.disr), + disr_val: data.disr, ctor_kind: data.ctor_kind, }, data.struct_ctor) } @@ -546,8 +544,14 @@ impl<'a, 'tcx> CrateMetadata { -> &'tcx ty::AdtDef { let item = self.entry(item_id); let did = self.local_def_id(item_id); + let (kind, ty) = match item.kind { + EntryKind::Enum(dt, _) => (ty::AdtKind::Enum, Some(dt.decode(self))), + EntryKind::Struct(_, _) => (ty::AdtKind::Struct, None), + EntryKind::Union(_, _) => (ty::AdtKind::Union, None), + _ => bug!("get_adt_def called on a non-ADT {:?}", did), + }; let mut ctor_index = None; - let variants = if let EntryKind::Enum(_) = item.kind { + let variants = if let ty::AdtKind::Enum = kind { item.children .decode(self) .map(|index| { @@ -562,13 +566,13 @@ impl<'a, 'tcx> CrateMetadata { vec![variant] }; let (kind, repr) = match item.kind { - EntryKind::Enum(repr) => (ty::AdtKind::Enum, repr), + EntryKind::Enum(_, repr) => (ty::AdtKind::Enum, repr), EntryKind::Struct(_, repr) => (ty::AdtKind::Struct, repr), EntryKind::Union(_, repr) => (ty::AdtKind::Union, repr), _ => bug!("get_adt_def called on a non-ADT {:?}", did), }; - let adt = tcx.alloc_adt_def(did, kind, variants, repr); + let adt = tcx.alloc_adt_def(did, kind, ty, variants, repr); if let Some(ctor_index) = ctor_index { // Make adt definition available through constructor id as well. tcx.adt_defs.borrow_mut().insert(self.local_def_id(ctor_index), adt); diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index f4ff5f4626f..0f9491aaf15 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -261,7 +261,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let data = VariantData { ctor_kind: variant.ctor_kind, - disr: variant.disr_val.to_u128_unchecked(), + disr: variant.disr_val, struct_ctor: None, }; @@ -388,7 +388,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let data = VariantData { ctor_kind: variant.ctor_kind, - disr: variant.disr_val.to_u128_unchecked(), + disr: variant.disr_val, struct_ctor: Some(def_id.index), }; @@ -661,7 +661,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } hir::ItemForeignMod(_) => EntryKind::ForeignMod, hir::ItemTy(..) => EntryKind::Type, - hir::ItemEnum(..) => EntryKind::Enum(get_repr_options(&tcx, def_id)), + hir::ItemEnum(..) => EntryKind::Enum(self.lazy(&tcx.lookup_adt_def(def_id).discr_ty), + get_repr_options(&tcx, def_id)), hir::ItemStruct(ref struct_def, _) => { let variant = tcx.lookup_adt_def(def_id).struct_variant(); @@ -678,7 +679,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { EntryKind::Struct(self.lazy(&VariantData { ctor_kind: variant.ctor_kind, - disr: variant.disr_val.to_u128_unchecked(), + disr: variant.disr_val, struct_ctor: struct_ctor, }), repr_options) } @@ -688,7 +689,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { EntryKind::Union(self.lazy(&VariantData { ctor_kind: variant.ctor_kind, - disr: variant.disr_val.to_u128_unchecked(), + disr: variant.disr_val, struct_ctor: None, }), repr_options) } diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 10aa4784aa2..777af02772e 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -228,7 +228,7 @@ pub enum EntryKind<'tcx> { ForeignMutStatic, ForeignMod, Type, - Enum(ReprOptions), + Enum(Lazy, ReprOptions), Field, Variant(Lazy), Struct(Lazy, ReprOptions), diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 3d4af259ec9..35841c2cbdf 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -69,10 +69,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let mut then_block = this.cfg.start_new_block(); let mut else_block = this.cfg.start_new_block(); - this.cfg.terminate(block, source_info, TerminatorKind::If { - cond: operand, - targets: (then_block, else_block) - }); + let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block); + this.cfg.terminate(block, source_info, term); unpack!(then_block = this.into(destination, then_block, then_expr)); else_block = if let Some(else_expr) = else_expr { @@ -114,14 +112,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { LogicalOp::And => (else_block, false_block), LogicalOp::Or => (true_block, else_block), }; - this.cfg.terminate(block, source_info, - TerminatorKind::If { cond: lhs, targets: blocks }); + let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1); + this.cfg.terminate(block, source_info, term); let rhs = unpack!(else_block = this.as_operand(else_block, rhs)); - this.cfg.terminate(else_block, source_info, TerminatorKind::If { - cond: rhs, - targets: (true_block, false_block) - }); + let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block); + this.cfg.terminate(else_block, source_info, term); this.cfg.push_assign_constant( true_block, source_info, destination, @@ -179,11 +175,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let cond = unpack!( loop_block_end = this.as_operand(loop_block, cond_expr)); body_block = this.cfg.start_new_block(); - this.cfg.terminate(loop_block_end, source_info, - TerminatorKind::If { - cond: cond, - targets: (body_block, exit_block) - }); + let term = TerminatorKind::if_(this.hir.tcx(), cond, + body_block, exit_block); + this.cfg.terminate(loop_block_end, source_info, term); // if the test is false, there's no `break` to assign `destination`, so // we have to do it; this overwrites any `break`-assigned value but it's diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 5c3d93ffda9..a28bc5d6ce3 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -673,8 +673,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let cond = unpack!(block = self.as_operand(block, guard)); let otherwise = self.cfg.start_new_block(); self.cfg.terminate(block, source_info, - TerminatorKind::If { cond: cond, - targets: (arm_block, otherwise)}); + TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise)); Some(otherwise) } else { let source_info = self.source_info(candidate.span); diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index a35b2925ae2..01c0433112b 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -20,8 +20,9 @@ use build::matches::{Candidate, MatchPair, Test, TestKind}; use hair::*; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::bitvec::BitVector; -use rustc::middle::const_val::ConstVal; +use rustc::middle::const_val::{ConstVal, ConstInt}; use rustc::ty::{self, Ty}; +use rustc::ty::util::IntTypeExt; use rustc::mir::*; use rustc::hir::RangeEnd; use syntax_pos::Span; @@ -182,74 +183,82 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let source_info = self.source_info(test.span); match test.kind { TestKind::Switch { adt_def, ref variants } => { + // Variants is a BitVec of indexes into adt_def.variants. let num_enum_variants = self.hir.num_variants(adt_def); + let used_variants = variants.count(); let mut otherwise_block = None; - let target_blocks: Vec<_> = (0..num_enum_variants).map(|i| { - if variants.contains(i) { - self.cfg.start_new_block() + let mut target_blocks = Vec::with_capacity(num_enum_variants); + let mut targets = Vec::with_capacity(used_variants + 1); + let mut values = Vec::with_capacity(used_variants); + let tcx = self.hir.tcx(); + for (idx, variant) in adt_def.variants.iter().enumerate() { + target_blocks.place_back() <- if variants.contains(idx) { + let discr = ConstInt::new_inttype(variant.disr_val, adt_def.discr_ty, + tcx.sess.target.uint_type, + tcx.sess.target.int_type).unwrap(); + values.push(discr); + *(targets.place_back() <- self.cfg.start_new_block()) } else { if otherwise_block.is_none() { otherwise_block = Some(self.cfg.start_new_block()); } otherwise_block.unwrap() - } - }).collect(); - debug!("num_enum_variants: {}, num tested variants: {}, variants: {:?}", - num_enum_variants, variants.iter().count(), variants); - self.cfg.terminate(block, source_info, TerminatorKind::Switch { - discr: lvalue.clone(), - adt_def: adt_def, - targets: target_blocks.clone() + }; + } + if let Some(otherwise_block) = otherwise_block { + targets.push(otherwise_block); + } else { + values.pop(); + } + debug!("num_enum_variants: {}, tested variants: {:?}, variants: {:?}", + num_enum_variants, values, variants); + let discr_ty = adt_def.discr_ty.to_ty(tcx); + let discr = self.temp(discr_ty); + self.cfg.push_assign(block, source_info, &discr, + Rvalue::Discriminant(lvalue.clone())); + assert_eq!(values.len() + 1, targets.len()); + self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { + discr: Operand::Consume(discr), + switch_ty: discr_ty, + values: From::from(values), + targets: targets }); target_blocks } TestKind::SwitchInt { switch_ty, ref options, indices: _ } => { - let (targets, term) = match switch_ty.sty { - // If we're matching on boolean we can - // use the If TerminatorKind instead - ty::TyBool => { - assert!(options.len() > 0 && options.len() <= 2); - - let (true_bb, else_bb) = - (self.cfg.start_new_block(), - self.cfg.start_new_block()); - - let targets = match &options[0] { - &ConstVal::Bool(true) => vec![true_bb, else_bb], - &ConstVal::Bool(false) => vec![else_bb, true_bb], - v => span_bug!(test.span, "expected boolean value but got {:?}", v) - }; - - (targets, - TerminatorKind::If { - cond: Operand::Consume(lvalue.clone()), - targets: (true_bb, else_bb) - }) - - } - _ => { - // The switch may be inexhaustive so we - // add a catch all block - let otherwise = self.cfg.start_new_block(); - let targets: Vec<_> = - options.iter() - .map(|_| self.cfg.start_new_block()) - .chain(Some(otherwise)) - .collect(); - - (targets.clone(), - TerminatorKind::SwitchInt { - discr: lvalue.clone(), - switch_ty: switch_ty, - values: options.clone(), - targets: targets - }) - } + let (ret, terminator) = if switch_ty.sty == ty::TyBool { + assert!(options.len() > 0 && options.len() <= 2); + let (true_bb, false_bb) = (self.cfg.start_new_block(), + self.cfg.start_new_block()); + let ret = match &options[0] { + &ConstVal::Bool(true) => vec![true_bb, false_bb], + &ConstVal::Bool(false) => vec![false_bb, true_bb], + v => span_bug!(test.span, "expected boolean value but got {:?}", v) + }; + (ret, TerminatorKind::if_(self.hir.tcx(), Operand::Consume(lvalue.clone()), + true_bb, false_bb)) + } else { + // The switch may be inexhaustive so we + // add a catch all block + let otherwise = self.cfg.start_new_block(); + let targets: Vec<_> = + options.iter() + .map(|_| self.cfg.start_new_block()) + .chain(Some(otherwise)) + .collect(); + let values: Vec<_> = options.iter().map(|v| + v.to_const_int().expect("switching on integral") + ).collect(); + (targets.clone(), TerminatorKind::SwitchInt { + discr: Operand::Consume(lvalue.clone()), + switch_ty: switch_ty, + values: From::from(values), + targets: targets, + }) }; - - self.cfg.terminate(block, source_info, term); - targets + self.cfg.terminate(block, source_info, terminator); + ret } TestKind::Eq { ref value, mut ty } => { @@ -314,11 +323,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // check the result let block = self.cfg.start_new_block(); - self.cfg.terminate(eq_block, source_info, TerminatorKind::If { - cond: Operand::Consume(eq_result), - targets: (block, fail), - }); - + self.cfg.terminate(eq_block, source_info, + TerminatorKind::if_(self.hir.tcx(), + Operand::Consume(eq_result), + block, fail)); vec![block, fail] } else { let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val); @@ -360,14 +368,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Operand::Consume(expected))); // branch based on result - let target_blocks: Vec<_> = vec![self.cfg.start_new_block(), - self.cfg.start_new_block()]; - self.cfg.terminate(block, source_info, TerminatorKind::If { - cond: Operand::Consume(result), - targets: (target_blocks[0], target_blocks[1]) - }); - - target_blocks + let (false_bb, true_bb) = (self.cfg.start_new_block(), + self.cfg.start_new_block()); + self.cfg.terminate(block, source_info, + TerminatorKind::if_(self.hir.tcx(), Operand::Consume(result), + true_bb, false_bb)); + vec![true_bb, false_bb] } } } @@ -389,11 +395,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // branch based on result let target_block = self.cfg.start_new_block(); - self.cfg.terminate(block, source_info, TerminatorKind::If { - cond: Operand::Consume(result), - targets: (target_block, fail_block) - }); - + self.cfg.terminate(block, source_info, + TerminatorKind::if_(self.hir.tcx(), Operand::Consume(result), + target_block, fail_block)); target_block } diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 01dc01c5ecf..4ac67cfb2fc 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -134,7 +134,8 @@ pub enum ExprKind<'tcx> { op: LogicalOp, lhs: ExprRef<'tcx>, rhs: ExprRef<'tcx>, - }, + }, // NOT overloaded! + // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands. Unary { op: UnOp, arg: ExprRef<'tcx>, diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 61ba9d90fef..9a8fb1099d0 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -26,6 +26,8 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] #![feature(staged_api)] +#![feature(placement_in_syntax)] +#![feature(collection_placement)] #[macro_use] extern crate log; extern crate graphviz as dot; diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index 6ef5720b330..55a26f4b37f 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -28,8 +28,6 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::Unreachable | - TerminatorKind::If { .. } | - TerminatorKind::Switch { .. } | TerminatorKind::SwitchInt { .. } => { /* nothing to do */ }, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 9c1107344f2..922521726c6 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -394,8 +394,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { return Qualif::empty(); } - TerminatorKind::If {..} | - TerminatorKind::Switch {..} | TerminatorKind::SwitchInt {..} | TerminatorKind::DropAndReplace { .. } | TerminatorKind::Resume | @@ -739,6 +737,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } + Rvalue::Discriminant(..) => { + // FIXME discriminant + self.add(Qualif::NOT_CONST); + if self.mode != Mode::Fn { + bug!("implement discriminant const qualify"); + } + } + Rvalue::Box(_) => { self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index d5fc90289e2..e93a412dc74 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -209,8 +209,6 @@ impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> { // turn a branch with all successors identical to a goto fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool { match terminator.kind { - TerminatorKind::If { .. } | - TerminatorKind::Switch { .. } | TerminatorKind::SwitchInt { .. } => {}, _ => return false }; diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index 8759a340d7e..3d5106c4b06 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -30,26 +30,30 @@ impl<'l, 'tcx> MirPass<'tcx> for SimplifyBranches<'l> { for block in mir.basic_blocks_mut() { let terminator = block.terminator_mut(); terminator.kind = match terminator.kind { - TerminatorKind::If { ref targets, cond: Operand::Constant(Constant { - literal: Literal::Value { - value: ConstVal::Bool(cond) - }, .. - }) } => { - if cond { - TerminatorKind::Goto { target: targets.0 } + TerminatorKind::SwitchInt { discr: Operand::Constant(Constant { + literal: Literal::Value { ref value }, .. + }), ref values, ref targets, .. } => { + if let Some(ref constint) = value.to_const_int() { + let (otherwise, targets) = targets.split_last().unwrap(); + let mut ret = TerminatorKind::Goto { target: *otherwise }; + for (v, t) in values.iter().zip(targets.iter()) { + if v == constint { + ret = TerminatorKind::Goto { target: *t }; + break; + } + } + ret } else { - TerminatorKind::Goto { target: targets.1 } + continue } - } - + }, TerminatorKind::Assert { target, cond: Operand::Constant(Constant { literal: Literal::Value { value: ConstVal::Bool(cond) }, .. }), expected, .. } if cond == expected => { TerminatorKind::Goto { target: target } - } - + }, _ => continue }; } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 529fe564af0..8ede7aaab5f 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -423,18 +423,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { lv_ty, rv_ty, terr); } } - - TerminatorKind::If { ref cond, .. } => { - let cond_ty = cond.ty(mir, tcx); - match cond_ty.sty { - ty::TyBool => {} - _ => { - span_mirbug!(self, term, "bad If ({:?}, not bool", cond_ty); - } - } - } TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { - let discr_ty = discr.ty(mir, tcx).to_ty(tcx); + let discr_ty = discr.ty(mir, tcx); if let Err(terr) = self.sub_types(discr_ty, switch_ty) { span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", switch_ty, discr_ty, terr); @@ -446,19 +436,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } // FIXME: check the values } - TerminatorKind::Switch { ref discr, adt_def, ref targets } => { - let discr_ty = discr.ty(mir, tcx).to_ty(tcx); - match discr_ty.sty { - ty::TyAdt(def, _) if def.is_enum() && - def == adt_def && - adt_def.variants.len() == targets.len() - => {}, - _ => { - span_mirbug!(self, term, "bad Switch ({:?} on {:?})", - adt_def, discr_ty); - } - } - } TerminatorKind::Call { ref func, ref args, ref destination, .. } => { let func_ty = func.ty(mir, tcx); debug!("check_terminator: call, func_ty={:?}", func_ty); @@ -603,11 +580,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { match block.terminator().kind { TerminatorKind::Goto { target } => self.assert_iscleanup(mir, block, target, is_cleanup), - TerminatorKind::If { targets: (on_true, on_false), .. } => { - self.assert_iscleanup(mir, block, on_true, is_cleanup); - self.assert_iscleanup(mir, block, on_false, is_cleanup); - } - TerminatorKind::Switch { ref targets, .. } | TerminatorKind::SwitchInt { ref targets, .. } => { for target in targets { self.assert_iscleanup(mir, block, *target, is_cleanup); diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index cec1c20519b..517a4720563 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -148,8 +148,6 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { self.record("TerminatorKind", kind); self.record(match *kind { TerminatorKind::Goto { .. } => "TerminatorKind::Goto", - TerminatorKind::If { .. } => "TerminatorKind::If", - TerminatorKind::Switch { .. } => "TerminatorKind::Switch", TerminatorKind::SwitchInt { .. } => "TerminatorKind::SwitchInt", TerminatorKind::Resume => "TerminatorKind::Resume", TerminatorKind::Return => "TerminatorKind::Return", @@ -186,6 +184,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { Rvalue::BinaryOp(..) => "Rvalue::BinaryOp", Rvalue::CheckedBinaryOp(..) => "Rvalue::CheckedBinaryOp", Rvalue::UnaryOp(..) => "Rvalue::UnaryOp", + Rvalue::Discriminant(..) => "Rvalue::Discriminant", Rvalue::Box(..) => "Rvalue::Box", Rvalue::Aggregate(ref kind, ref _operands) => { // AggregateKind is not distinguished by visit API, so diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 59f2104ec14..11d3fae8238 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -318,7 +318,7 @@ pub fn trans_get_discr<'a, 'tcx>( }; match cast_to { None => val, - Some(llty) => if is_discr_signed(&l) { bcx.sext(val, llty) } else { bcx.zext(val, llty) } + Some(llty) => bcx.intcast(val, llty, is_discr_signed(&l)) } } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index ce02c9725d1..41c0eaa52a7 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -536,7 +536,7 @@ pub fn call_memcpy<'a, 'tcx>(b: &Builder<'a, 'tcx>, let memcpy = ccx.get_intrinsic(&key); let src_ptr = b.pointercast(src, Type::i8p(ccx)); let dst_ptr = b.pointercast(dst, Type::i8p(ccx)); - let size = b.intcast(n_bytes, ccx.int_type()); + let size = b.intcast(n_bytes, ccx.int_type(), false); let align = C_i32(ccx, align as i32); let volatile = C_bool(ccx, false); b.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index 89aaa8b6630..f64e581c177 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -780,10 +780,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn intcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + pub fn intcast(&self, val: ValueRef, dest_ty: Type, is_signed: bool) -> ValueRef { self.count_insn("intcast"); unsafe { - llvm::LLVMBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + llvm::LLVMRustBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), is_signed) } } diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index c453d9bbd05..11c0bf852f7 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -1476,7 +1476,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, DIB(cx), name.as_ptr(), // FIXME: what if enumeration has i128 discriminant? - v.disr_val.to_u128_unchecked() as u64) + v.disr_val as u64) } }) .collect(); diff --git a/src/librustc_trans/disr.rs b/src/librustc_trans/disr.rs index c5737c6e5f1..f3a62bc85b8 100644 --- a/src/librustc_trans/disr.rs +++ b/src/librustc_trans/disr.rs @@ -27,7 +27,7 @@ impl ::std::ops::BitAnd for Disr { impl From<::rustc::ty::Disr> for Disr { fn from(i: ::rustc::ty::Disr) -> Disr { // FIXME: what if discr has 128 bit discr? - Disr(i.to_u128_unchecked() as u64) + Disr(i as u64) } } diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 2a1ab10d74e..2c3b479c7dd 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -156,10 +156,10 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { LvalueContext::StorageLive | LvalueContext::StorageDead | + LvalueContext::Inspect | LvalueContext::Consume => {} LvalueContext::Store | - LvalueContext::Inspect | LvalueContext::Borrow { .. } | LvalueContext::Projection(..) => { self.mark_as_lvalue(index); @@ -204,8 +204,6 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec { /* nothing to do */ } diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index f04f7ab9ed1..651d0066b12 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -11,17 +11,16 @@ use llvm::{self, ValueRef, BasicBlockRef}; use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err}; use rustc::middle::lang_items; +use rustc::middle::const_val::ConstInt; use rustc::ty::{self, layout, TypeFoldable}; use rustc::mir; use abi::{Abi, FnType, ArgType}; -use adt; use base::{self, Lifetime}; use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; use builder::Builder; use common::{self, Funclet}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; use consts; -use Disr; use machine::{llalign_of_min, llbitsize_of_real}; use meth; use type_of::{self, align_of}; @@ -29,7 +28,6 @@ use glue; use type_::Type; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_data_structures::fx::FxHashMap; use syntax::symbol::Symbol; use std::cmp; @@ -136,59 +134,25 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { funclet_br(self, bcx, target); } - mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => { - let cond = self.trans_operand(&bcx, cond); - - let lltrue = llblock(self, true_bb); - let llfalse = llblock(self, false_bb); - bcx.cond_br(cond.immediate(), lltrue, llfalse); - } - - mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => { - let discr_lvalue = self.trans_lvalue(&bcx, discr); - let ty = discr_lvalue.ty.to_ty(bcx.tcx()); - let discr = adt::trans_get_discr( - &bcx, ty, discr_lvalue.llval, discr_lvalue.alignment, - None, true); - - let mut bb_hist = FxHashMap(); - for target in targets { - *bb_hist.entry(target).or_insert(0) += 1; - } - let (default_bb, default_blk) = match bb_hist.iter().max_by_key(|&(_, c)| c) { - // If a single target basic blocks is predominant, promote that to be the - // default case for the switch instruction to reduce the size of the generated - // code. This is especially helpful in cases like an if-let on a huge enum. - // Note: This optimization is only valid for exhaustive matches. - Some((&&bb, &c)) if c > targets.len() / 2 => { - (Some(bb), llblock(self, bb)) - } - // We're generating an exhaustive switch, so the else branch - // can't be hit. Branching to an unreachable instruction - // lets LLVM know this - _ => (None, self.unreachable_block()) - }; - let switch = bcx.switch(discr, default_blk, targets.len()); - assert_eq!(adt_def.variants.len(), targets.len()); - for (adt_variant, &target) in adt_def.variants.iter().zip(targets) { - if default_bb != Some(target) { - let llbb = llblock(self, target); - let llval = adt::trans_case(&bcx, ty, Disr::from(adt_variant.disr_val)); - bcx.add_case(switch, llval, llbb) - } - } - } - mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { - let (otherwise, targets) = targets.split_last().unwrap(); - let lv = self.trans_lvalue(&bcx, discr); - let discr = bcx.load(lv.llval, lv.alignment.to_align()); - let discr = base::to_immediate(&bcx, discr, switch_ty); - let switch = bcx.switch(discr, llblock(self, *otherwise), values.len()); - for (value, target) in values.iter().zip(targets) { - let val = Const::from_constval(bcx.ccx, value.clone(), switch_ty); - let llbb = llblock(self, *target); - bcx.add_case(switch, val.llval, llbb) + let discr = self.trans_operand(&bcx, discr); + if switch_ty == bcx.tcx().types.bool { + let lltrue = llblock(self, targets[0]); + let llfalse = llblock(self, targets[1]); + if let [ConstInt::Infer(0)] = values[..] { + bcx.cond_br(discr.immediate(), llfalse, lltrue); + } else { + bcx.cond_br(discr.immediate(), lltrue, llfalse); + } + } else { + let (otherwise, targets) = targets.split_last().unwrap(); + let switch = bcx.switch(discr.immediate(), + llblock(self, *otherwise), values.len()); + for (value, target) in values.iter().zip(targets) { + let val = Const::from_constint(bcx.ccx, value); + let llbb = llblock(self, *target); + bcx.add_case(switch, val.llval, llbb) + } } } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 11668e792e3..19139301bb0 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -61,6 +61,33 @@ impl<'tcx> Const<'tcx> { } } + pub fn from_constint<'a>(ccx: &CrateContext<'a, 'tcx>, ci: &ConstInt) + -> Const<'tcx> { + let tcx = ccx.tcx(); + let (llval, ty) = match *ci { + I8(v) => (C_integral(Type::i8(ccx), v as u64, true), tcx.types.i8), + I16(v) => (C_integral(Type::i16(ccx), v as u64, true), tcx.types.i16), + I32(v) => (C_integral(Type::i32(ccx), v as u64, true), tcx.types.i32), + I64(v) => (C_integral(Type::i64(ccx), v as u64, true), tcx.types.i64), + I128(v) => (C_big_integral(Type::i128(ccx), v as u128), tcx.types.i128), + Isize(v) => { + let i = v.as_i64(ccx.tcx().sess.target.int_type); + (C_integral(Type::int(ccx), i as u64, true), tcx.types.isize) + }, + U8(v) => (C_integral(Type::i8(ccx), v as u64, false), tcx.types.u8), + U16(v) => (C_integral(Type::i16(ccx), v as u64, false), tcx.types.u16), + U32(v) => (C_integral(Type::i32(ccx), v as u64, false), tcx.types.u32), + U64(v) => (C_integral(Type::i64(ccx), v, false), tcx.types.u64), + U128(v) => (C_big_integral(Type::i128(ccx), v), tcx.types.u128), + Usize(v) => { + let u = v.as_u64(ccx.tcx().sess.target.uint_type); + (C_integral(Type::int(ccx), u, false), tcx.types.usize) + }, + Infer(_) | InferSigned(_) => bug!("MIR must not use `{:?}`", ci), + }; + Const { llval: llval, ty: ty } + } + /// Translate ConstVal into a LLVM constant value. pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>, cv: ConstVal, @@ -72,26 +99,7 @@ impl<'tcx> Const<'tcx> { ConstVal::Float(F64(v)) => C_floating_f64(v, llty), ConstVal::Float(FInfer {..}) => bug!("MIR must not use `{:?}`", cv), ConstVal::Bool(v) => C_bool(ccx, v), - ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true), - ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true), - ConstVal::Integral(I32(v)) => C_integral(Type::i32(ccx), v as u64, true), - ConstVal::Integral(I64(v)) => C_integral(Type::i64(ccx), v as u64, true), - ConstVal::Integral(I128(v)) => C_big_integral(Type::i128(ccx), v as u128), - ConstVal::Integral(Isize(v)) => { - let i = v.as_i64(ccx.tcx().sess.target.int_type); - C_integral(Type::int(ccx), i as u64, true) - }, - ConstVal::Integral(U8(v)) => C_integral(Type::i8(ccx), v as u64, false), - ConstVal::Integral(U16(v)) => C_integral(Type::i16(ccx), v as u64, false), - ConstVal::Integral(U32(v)) => C_integral(Type::i32(ccx), v as u64, false), - ConstVal::Integral(U64(v)) => C_integral(Type::i64(ccx), v, false), - ConstVal::Integral(U128(v)) => C_big_integral(Type::i128(ccx), v), - ConstVal::Integral(Usize(v)) => { - let u = v.as_u64(ccx.tcx().sess.target.uint_type); - C_integral(Type::int(ccx), u, false) - }, - ConstVal::Integral(Infer(_)) | - ConstVal::Integral(InferSigned(_)) => bug!("MIR must not use `{:?}`", cv), + ConstVal::Integral(ref i) => return Const::from_constint(ccx, i), ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"), ConstVal::Struct(_) | ConstVal::Tuple(_) | diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 0a8e676b078..38ee67796c6 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -286,17 +286,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let newval = match (r_t_in, r_t_out) { (CastTy::Int(_), CastTy::Int(_)) => { - let srcsz = ll_t_in.int_width(); - let dstsz = ll_t_out.int_width(); - if srcsz == dstsz { - bcx.bitcast(llval, ll_t_out) - } else if srcsz > dstsz { - bcx.trunc(llval, ll_t_out) - } else if signed { - bcx.sext(llval, ll_t_out) - } else { - bcx.zext(llval, ll_t_out) - } + bcx.intcast(llval, ll_t_out, signed) } (CastTy::Float, CastTy::Float) => { let srcsz = ll_t_in.float_width(); @@ -433,6 +423,19 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }) } + mir::Rvalue::Discriminant(ref lvalue) => { + let discr_lvalue = self.trans_lvalue(&bcx, lvalue); + let enum_ty = discr_lvalue.ty.to_ty(bcx.tcx()); + let discr_ty = rvalue.ty(&*self.mir, bcx.tcx()).unwrap(); + let discr_type = type_of::immediate_type_of(bcx.ccx, discr_ty); + let discr = adt::trans_get_discr(&bcx, enum_ty, discr_lvalue.llval, + discr_lvalue.alignment, Some(discr_type), true); + (bcx, OperandRef { + val: OperandValue::Immediate(discr), + ty: discr_ty + }) + } + mir::Rvalue::Box(content_ty) => { let content_ty: Ty<'tcx> = self.monomorphize(&content_ty); let llty = type_of::type_of(bcx.ccx, content_ty); @@ -661,6 +664,7 @@ pub fn rvalue_creates_operand(rvalue: &mir::Rvalue) -> bool { mir::Rvalue::BinaryOp(..) | mir::Rvalue::CheckedBinaryOp(..) | mir::Rvalue::UnaryOp(..) | + mir::Rvalue::Discriminant(..) | mir::Rvalue::Box(..) | mir::Rvalue::Use(..) => true, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7936db65c44..217405a81ec 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1004,9 +1004,8 @@ fn convert_struct_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let did = ccx.tcx.hir.local_def_id(it.id); // Use separate constructor id for unit/tuple structs and reuse did for braced structs. let ctor_id = if !def.is_struct() { Some(ccx.tcx.hir.local_def_id(def.id())) } else { None }; - let variants = vec![convert_struct_variant(ccx, ctor_id.unwrap_or(did), it.name, - ConstInt::Infer(0), def)]; - let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Struct, variants, + let variants = vec![convert_struct_variant(ccx, ctor_id.unwrap_or(did), it.name, 0, def)]; + let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Struct, None, variants, ReprOptions::new(&ccx.tcx, did)); if let Some(ctor_id) = ctor_id { // Make adt definition available through constructor id as well. @@ -1023,63 +1022,65 @@ fn convert_union_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, -> &'tcx ty::AdtDef { let did = ccx.tcx.hir.local_def_id(it.id); - let variants = vec![convert_struct_variant(ccx, did, it.name, ConstInt::Infer(0), def)]; - - let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Union, variants, ReprOptions::new(&ccx.tcx, did)); + let variants = vec![convert_struct_variant(ccx, did, it.name, 0, def)]; + let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Union, None, variants, + ReprOptions::new(&ccx.tcx, did)); ccx.tcx.adt_defs.borrow_mut().insert(did, adt); adt } - fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId) - -> Option { - let e = &ccx.tcx.hir.body(body).value; - debug!("disr expr, checking {}", ccx.tcx.hir.node_to_pretty_string(e.id)); +fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId) + -> Option { + let e = &ccx.tcx.hir.body(body).value; + debug!("disr expr, checking {}", ccx.tcx.hir.node_to_pretty_string(e.id)); - let ty_hint = repr_ty.to_ty(ccx.tcx); - let print_err = |cv: ConstVal| { - struct_span_err!(ccx.tcx.sess, e.span, E0079, "mismatched types") - .note_expected_found(&"type", &ty_hint, &format!("{}", cv.description())) - .span_label(e.span, &format!("expected '{}' type", ty_hint)) - .emit(); - }; + let ty_hint = repr_ty.to_ty(ccx.tcx); + let print_err = |cv: ConstVal| { + struct_span_err!(ccx.tcx.sess, e.span, E0079, "mismatched types") + .note_expected_found(&"type", &ty_hint, &format!("{}", cv.description())) + .span_label(e.span, &format!("expected '{}' type", ty_hint)) + .emit(); + }; - let hint = UncheckedExprHint(ty_hint); - match ConstContext::new(ccx.tcx, body).eval(e, hint) { - Ok(ConstVal::Integral(i)) => { - // FIXME: eval should return an error if the hint is wrong - match (repr_ty, i) { - (attr::SignedInt(ast::IntTy::I8), ConstInt::I8(_)) | - (attr::SignedInt(ast::IntTy::I16), ConstInt::I16(_)) | - (attr::SignedInt(ast::IntTy::I32), ConstInt::I32(_)) | - (attr::SignedInt(ast::IntTy::I64), ConstInt::I64(_)) | - (attr::SignedInt(ast::IntTy::I128), ConstInt::I128(_)) | - (attr::SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) | - (attr::UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) | - (attr::UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) | - (attr::UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) | - (attr::UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) | - (attr::UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) | - (attr::UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => Some(i), - (_, i) => { - print_err(ConstVal::Integral(i)); - None - }, - } - }, - Ok(cv) => { - print_err(cv); - None - }, - // enum variant evaluation happens before the global constant check - // so we need to report the real error - Err(err) => { - let mut diag = report_const_eval_err( - ccx.tcx, &err, e.span, "enum discriminant"); - diag.emit(); - None + let hint = UncheckedExprHint(ty_hint); + match ConstContext::new(ccx.tcx, body).eval(e, hint) { + Ok(ConstVal::Integral(i)) => { + // FIXME: eval should return an error if the hint does not match the type of the body. + // i.e. eventually the match below would not exist. + match (repr_ty, i) { + (attr::SignedInt(ast::IntTy::I8), ConstInt::I8(_)) | + (attr::SignedInt(ast::IntTy::I16), ConstInt::I16(_)) | + (attr::SignedInt(ast::IntTy::I32), ConstInt::I32(_)) | + (attr::SignedInt(ast::IntTy::I64), ConstInt::I64(_)) | + (attr::SignedInt(ast::IntTy::I128), ConstInt::I128(_)) | + (attr::SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) | + (attr::UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) | + (attr::UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) | + (attr::UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) | + (attr::UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) | + (attr::UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) | + (attr::UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => + Some(i), + (_, i) => { + print_err(ConstVal::Integral(i)); + None + }, } + }, + Ok(cv) => { + print_err(cv); + None + }, + // enum variant evaluation happens before the global constant check + // so we need to report the real error + Err(err) => { + let mut diag = report_const_eval_err( + ccx.tcx, &err, e.span, "enum discriminant"); + diag.emit(); + None } } +} fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, it: &hir::Item, @@ -1090,13 +1091,17 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let did = tcx.hir.local_def_id(it.id); let repr_hints = tcx.lookup_repr_hints(did); let repr_type = tcx.enum_repr_type(repr_hints.get(0)); - let initial = repr_type.initial_discriminant(tcx); - let mut prev_disr = None::; + let initial = ConstInt::new_inttype(repr_type.initial_discriminant(tcx), repr_type, + tcx.sess.target.uint_type, tcx.sess.target.int_type) + .unwrap(); + let mut prev_disr = None::; let variants = def.variants.iter().map(|v| { let wrapped_disr = prev_disr.map_or(initial, |d| d.wrap_incr()); let disr = if let Some(e) = v.node.disr_expr { + // FIXME: i128 discriminants evaluate_disr_expr(ccx, repr_type, e) - } else if let Some(disr) = repr_type.disr_incr(tcx, prev_disr) { + } else if let Some(disr) = prev_disr.map_or(Some(initial), + |v| (v + ConstInt::Infer(1)).ok()) { Some(disr) } else { struct_span_err!(tcx.sess, v.span, E0370, @@ -1108,12 +1113,11 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, None }.unwrap_or(wrapped_disr); prev_disr = Some(disr); - let did = tcx.hir.local_def_id(v.node.data.id()); - convert_struct_variant(ccx, did, v.node.name, disr, &v.node.data) + convert_struct_variant(ccx, did, v.node.name, disr.to_u128_unchecked(), &v.node.data) }).collect(); - - let adt = tcx.alloc_adt_def(did, AdtKind::Enum, variants, ReprOptions::new(&ccx.tcx, did)); + let adt = tcx.alloc_adt_def(did, AdtKind::Enum, Some(repr_type), variants, + ReprOptions::new(&ccx.tcx, did)); tcx.adt_defs.borrow_mut().insert(did, adt); adt } diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index ba39fcdec6f..c6847249803 100644 --- a/src/libserialize/serialize.rs +++ b/src/libserialize/serialize.rs @@ -567,6 +567,34 @@ impl Decodable for Vec { } } +impl<'a, T:Encodable> Encodable for Cow<'a, [T]> +where [T]: ToOwned> +{ + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))? + } + Ok(()) + }) + } +} + +impl Decodable for Cow<'static, [T]> +where [T]: ToOwned> +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut v = Vec::with_capacity(len); + for i in 0..len { + v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(Cow::Owned(v)) + }) + } +} + + impl Encodable for Option { fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_option(|s| { diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 40432735911..e89f48b4105 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -1316,6 +1316,12 @@ extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) { return toRust(LLVMGetVisibility(V)); } +// Oh hey, a binding that makes sense for once? (because LLVM’s own do not) +extern "C" LLVMValueRef LLVMRustBuildIntCast(LLVMBuilderRef B, LLVMValueRef Val, + LLVMTypeRef DestTy, bool isSigned) { + return wrap(unwrap(B)->CreateIntCast(unwrap(Val), unwrap(DestTy), isSigned, "")); +} + extern "C" void LLVMRustSetVisibility(LLVMValueRef V, LLVMRustVisibility RustVisibility) { LLVMSetVisibility(V, fromRust(RustVisibility)); diff --git a/src/test/codegen/match.rs b/src/test/codegen/match.rs index ac47f6082e3..aa100da6013 100644 --- a/src/test/codegen/match.rs +++ b/src/test/codegen/match.rs @@ -20,9 +20,13 @@ pub enum E { // CHECK-LABEL: @exhaustive_match #[no_mangle] pub fn exhaustive_match(e: E) { -// CHECK: switch{{.*}}, label %[[DEFAULT:[a-zA-Z0-9_]+]] -// CHECK: [[DEFAULT]]: -// CHECK-NEXT: unreachable +// CHECK: switch{{.*}}, label %[[OTHERWISE:[a-zA-Z0-9_]+]] [ +// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[TRUE:[a-zA-Z0-9_]+]] +// CHECK-NEXT: ] +// CHECK: [[TRUE]]: +// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] +// CHECK: [[OTHERWISE]]: +// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] match e { E::A => (), E::B => (), diff --git a/src/test/compile-fail/E0081.rs b/src/test/compile-fail/E0081.rs index 9911e093a89..e12eff72c7f 100644 --- a/src/test/compile-fail/E0081.rs +++ b/src/test/compile-fail/E0081.rs @@ -9,10 +9,10 @@ // except according to those terms. enum Enum { - P = 3, //~ NOTE first use of `3isize` + P = 3, //~ NOTE first use of `3` X = 3, - //~^ ERROR discriminant value `3isize` already exists - //~| NOTE enum already has `3isize` + //~^ ERROR discriminant value `3` already exists + //~| NOTE enum already has `3` Y = 5 } diff --git a/src/test/compile-fail/issue-15524.rs b/src/test/compile-fail/issue-15524.rs index 658a0c1546b..0d5f5fd75eb 100644 --- a/src/test/compile-fail/issue-15524.rs +++ b/src/test/compile-fail/issue-15524.rs @@ -12,20 +12,20 @@ const N: isize = 1; enum Foo { A = 1, - //~^ NOTE first use of `1isize` - //~| NOTE first use of `1isize` - //~| NOTE first use of `1isize` + //~^ NOTE first use of `1` + //~| NOTE first use of `1` + //~| NOTE first use of `1` B = 1, - //~^ ERROR discriminant value `1isize` already exists - //~| NOTE enum already has `1isize` + //~^ ERROR discriminant value `1` already exists + //~| NOTE enum already has `1` C = 0, D, - //~^ ERROR discriminant value `1isize` already exists - //~| NOTE enum already has `1isize` + //~^ ERROR discriminant value `1` already exists + //~| NOTE enum already has `1` E = N, - //~^ ERROR discriminant value `1isize` already exists - //~| NOTE enum already has `1isize` + //~^ ERROR discriminant value `1` already exists + //~| NOTE enum already has `1` } diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs index 7239e32357b..0e8971269b0 100644 --- a/src/test/mir-opt/simplify_if.rs +++ b/src/test/mir-opt/simplify_if.rs @@ -17,7 +17,7 @@ fn main() { // END RUST SOURCE // START rustc.node4.SimplifyBranches.initial-before.mir // bb0: { -// if(const false) -> [true: bb1, false: bb2]; +// switchInt(const false) -> [0: bb2, otherwise: bb1]; // } // END rustc.node4.SimplifyBranches.initial-before.mir // START rustc.node4.SimplifyBranches.initial-after.mir diff --git a/src/test/ui/custom-derive/issue-36935.rs b/src/test/ui/custom-derive/issue-36935.rs index 22d603563de..2231c3c2422 100644 --- a/src/test/ui/custom-derive/issue-36935.rs +++ b/src/test/ui/custom-derive/issue-36935.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:plugin.rs +// ignore-stage1 #![feature(proc_macro)] diff --git a/src/test/ui/custom-derive/issue-36935.stderr b/src/test/ui/custom-derive/issue-36935.stderr index 9a5e2de14e3..46cc7a42b04 100644 --- a/src/test/ui/custom-derive/issue-36935.stderr +++ b/src/test/ui/custom-derive/issue-36935.stderr @@ -1,7 +1,7 @@ error: proc-macro derive panicked - --> $DIR/issue-36935.rs:17:15 + --> $DIR/issue-36935.rs:18:15 | -17 | #[derive(Foo, Bar)] +18 | #[derive(Foo, Bar)] | ^^^ | = help: message: lolnope