diff --git a/mk/cfg/aarch64-unknown-freebsd.mk b/mk/cfg/aarch64-unknown-freebsd.mk deleted file mode 100644 index 34aee77ae2107..0000000000000 --- a/mk/cfg/aarch64-unknown-freebsd.mk +++ /dev/null @@ -1 +0,0 @@ -# rustbuild-only target diff --git a/mk/cfg/i686-unknown-netbsd.mk b/mk/cfg/i686-unknown-netbsd.mk deleted file mode 100644 index 34aee77ae2107..0000000000000 --- a/mk/cfg/i686-unknown-netbsd.mk +++ /dev/null @@ -1 +0,0 @@ -# rustbuild-only target diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 4e67a14345b9e..5858e077173ff 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -149,6 +149,7 @@ struct Build { python: Option, full_bootstrap: Option, extended: Option, + verbose: Option, } /// TOML representation of various global install decisions. @@ -294,6 +295,7 @@ impl Config { set(&mut config.vendor, build.vendor); set(&mut config.full_bootstrap, build.full_bootstrap); set(&mut config.extended, build.extended); + set(&mut config.verbose, build.verbose); if let Some(ref install) = toml.install { config.prefix = install.prefix.clone().map(PathBuf::from); diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example index a53419ad7fd78..42bc20c24e569 100644 --- a/src/bootstrap/config.toml.example +++ b/src/bootstrap/config.toml.example @@ -124,6 +124,9 @@ # disabled by default. #extended = false +# Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose +#verbose = 0 + # ============================================================================= # General install configuration options # ============================================================================= diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 1c3901bf2a143..2da2892150b4f 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -515,9 +515,7 @@ pub fn cargo(build: &Build, stage: u32, target: &str) { let branch = match &build.config.channel[..] { "stable" | - "beta" => { - build.release.split(".").take(2).collect::>().join(".") - } + "beta" => format!("rust-{}", build.release_num), _ => "master".to_string(), }; diff --git a/src/doc/book/functions.md b/src/doc/book/functions.md index b453936fe00d5..15b6db828145a 100644 --- a/src/doc/book/functions.md +++ b/src/doc/book/functions.md @@ -230,6 +230,19 @@ If you want more information, you can get a backtrace by setting the ```text $ RUST_BACKTRACE=1 ./diverges thread 'main' panicked at 'This function never returns!', hello.rs:2 +Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. +stack backtrace: + hello::diverges + at ./hello.rs:2 + hello::main + at ./hello.rs:6 +``` + +If you want the complete backtrace and filenames: + +```text +$ RUST_BACKTRACE=full ./diverges +thread 'main' panicked at 'This function never returns!', hello.rs:2 stack backtrace: 1: 0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r 2: 0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w @@ -262,7 +275,7 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace. `RUST_BACKTRACE` also works with Cargo’s `run` command: ```text -$ RUST_BACKTRACE=1 cargo run +$ RUST_BACKTRACE=full cargo run Running `target/debug/diverges` thread 'main' panicked at 'This function never returns!', hello.rs:2 stack backtrace: diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index f583f601726ed..11919db479c1a 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 cbb7b2710f506..d8212807eb277 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, - - /// 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. + values: Cow<'tcx, [ConstInt]>, + + /// 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 f1268521d6708..7b0863b4c42bc 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 b5da304a10986..be3c43db7badd 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_int(&mut self, _const_int: &ConstInt) { } - fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) { + 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 a0eae33c4402b..633b6c1747c0c 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -672,9 +672,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn alloc_adt_def(self, did: DefId, kind: AdtKind, + discr_ty: Option, variants: Vec) -> &'gcx ty::AdtDef { - let def = ty::AdtDef::new(self, did, kind, variants); + let discr_ty = discr_ty.unwrap_or(attr::UnsignedInt(ast::UintTy::U8)); + let def = ty::AdtDef::new(self, did, kind, discr_ty, variants); self.global_arenas.adt_def.alloc(def) } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index ff3ac3586a787..b21e6492dbce5 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -20,7 +20,6 @@ use ty::{self, Ty, TyCtxt, TypeFoldable}; 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; @@ -1207,11 +1206,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; } @@ -1271,7 +1266,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)); } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4d9514b1473c7..122624f751602 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 @@ -1360,6 +1365,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, did: DefId, kind: AdtKind, + discr_ty: attr::IntType, variants: Vec) -> Self { let mut flags = AdtFlags::NO_ADT_FLAGS; let attrs = tcx.get_attrs(did); @@ -1382,6 +1388,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 b01b77bbcf8a5..16492de6c3d27 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 f11cf90834dd9..8b246105f6169 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 d2f744bde2d63..5899c9f31d14d 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 7cf6ab2999c05..0c7e922c48ab4 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 17714f2fb2d6c..bc3809db1c63a 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 3700d46c3462a..ffcd25a4cdd39 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_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index f4a35ea5fd0cf..39bbff08f5b93 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| { @@ -561,14 +565,8 @@ impl<'a, 'tcx> CrateMetadata { ctor_index = struct_ctor; vec![variant] }; - let kind = match item.kind { - EntryKind::Enum => ty::AdtKind::Enum, - EntryKind::Struct(_) => ty::AdtKind::Struct, - EntryKind::Union(_) => ty::AdtKind::Union, - _ => bug!("get_adt_def called on a non-ADT {:?}", did), - }; - let adt = tcx.alloc_adt_def(did, kind, variants); + let adt = tcx.alloc_adt_def(did, kind, ty, variants); 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 69e1bbd77662b..6670be64c26ec 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), }; @@ -659,7 +659,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } hir::ItemForeignMod(_) => EntryKind::ForeignMod, hir::ItemTy(..) => EntryKind::Type, - hir::ItemEnum(..) => EntryKind::Enum, + hir::ItemEnum(..) => EntryKind::Enum(self.lazy(&tcx.lookup_adt_def(def_id).discr_ty)), hir::ItemStruct(ref struct_def, _) => { let variant = tcx.lookup_adt_def(def_id).struct_variant(); @@ -673,7 +673,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, })) } @@ -682,7 +682,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, })) } diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index d13628e9ce7a3..0ee42aa1bb2c8 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, + Enum(Lazy), Field, Variant(Lazy), Struct(Lazy), diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 3d4af259ec9f7..35841c2cbdf01 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 5c3d93ffda9c1..a28bc5d6ce36d 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 a35b2925ae271..01c0433112bf3 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 01dc01c5ecfd4..4ac67cfb2fca1 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 61ba9d90fef38..9a8fb1099d04b 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 6ef5720b330c9..55a26f4b37fe2 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 9c1107344f241..922521726c626 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 d5fc90289e2cc..e93a412dc744f 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 8759a340d7e3c..3d5106c4b06f7 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 529fe564af02b..8ede7aaab5f68 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 cec1c20519bd6..517a472056334 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/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 7578cc74dbf34..9ae83991962a5 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -1473,7 +1473,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 c5737c6e5f12c..f3a62bc85b8d9 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 2a1ab10d74e16..2c3b479c7dd0f 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 027779aca63e4..6b3081f610060 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}; 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,28 @@ 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, 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)) + mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { + 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); } - // 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) + } 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) } } } - mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { - let (otherwise, targets) = targets.split_last().unwrap(); - let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval); - 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) - } - } - mir::TerminatorKind::Return => { let ret = self.fn_ty.ret; if ret.is_ignore() || ret.is_indirect() { diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index f92faaa0508a6..199188bf0ab3c 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -60,6 +60,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, @@ -71,26 +98,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 81b241b485175..6874a0245b0b9 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -429,6 +429,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, + 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); @@ -657,6 +670,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 1981e7c3a3d12..60f4870a7aa12 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); if let Some(ctor_id) = ctor_id { // Make adt definition available through constructor id as well. ccx.tcx.adt_defs.borrow_mut().insert(ctor_id, adt); @@ -1022,63 +1021,64 @@ 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); + let variants = vec![convert_struct_variant(ccx, did, it.name, 0, def)]; + let adt = ccx.tcx.alloc_adt_def(did, AdtKind::Union, None, variants); 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)); - - 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(); - }; +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 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, @@ -1089,13 +1089,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, @@ -1107,12 +1111,10 @@ 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); + let adt = tcx.alloc_adt_def(did, AdtKind::Enum, Some(repr_type), variants); tcx.adt_defs.borrow_mut().insert(did, adt); adt } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 60dae19d876c9..c591c09bf20e2 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -671,9 +671,11 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt: } _ => { if f.alternate() { - write!(f, "&{}{}{:#}", lt, m, **ty) + write!(f, "&{}{}", lt, m)?; + fmt_type(&ty, f, use_absolute) } else { - write!(f, "&{}{}{}", lt, m, **ty) + write!(f, "&{}{}", lt, m)?; + fmt_type(&ty, f, use_absolute) } } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 40eb7e5ab78c3..6234d89024441 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2132,10 +2132,23 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
    ")?; if let Some(implementors) = cache.implementors.get(&it.def_id) { - let mut implementor_count: FxHashMap<&str, usize> = FxHashMap(); + // The DefId is for the first Type found with that name. The bool is + // if any Types with the same name but different DefId have been found. + let mut implementor_dups: FxHashMap<&str, (DefId, bool)> = FxHashMap(); for implementor in implementors { - if let clean::Type::ResolvedPath {ref path, ..} = implementor.impl_.for_ { - *implementor_count.entry(path.last_name()).or_insert(0) += 1; + match implementor.impl_.for_ { + clean::ResolvedPath { ref path, did, is_generic: false, .. } | + clean::BorrowedRef { + type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. }, + .. + } => { + let &mut (prev_did, ref mut has_duplicates) = + implementor_dups.entry(path.last_name()).or_insert((did, false)); + if prev_did != did { + *has_duplicates = true; + } + } + _ => {} } } @@ -2143,12 +2156,13 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, write!(w, "
  • ")?; // If there's already another implementor that has the same abbridged name, use the // full path, for example in `std::iter::ExactSizeIterator` - let use_absolute = if let clean::Type::ResolvedPath { - ref path, .. - } = implementor.impl_.for_ { - implementor_count[path.last_name()] > 1 - } else { - false + let use_absolute = match implementor.impl_.for_ { + clean::ResolvedPath { ref path, is_generic: false, .. } | + clean::BorrowedRef { + type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, + .. + } => implementor_dups[path.last_name()].1, + _ => false, }; fmt_impl_for_trait_page(&implementor.impl_, w, use_absolute)?; writeln!(w, "
  • ")?; diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index ba39fcdec6f88..c6847249803e3 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/libstd/panicking.rs b/src/libstd/panicking.rs index d76e8816ca45f..6428fdf13c2ae 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -320,7 +320,11 @@ fn default_hook(info: &PanicInfo) { let log_backtrace = { let panics = update_panic_count(0); - panics >= 2 || backtrace::log_enabled() + if panics >= 2 { + Some(backtrace::PrintFormat::Full) + } else { + backtrace::log_enabled() + } }; let file = info.location.file; @@ -347,8 +351,8 @@ fn default_hook(info: &PanicInfo) { static FIRST_PANIC: AtomicBool = AtomicBool::new(true); - if log_backtrace { - let _ = backtrace::write(err); + if let Some(format) = log_backtrace { + let _ = backtrace::print(err, format); } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) { let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` for a backtrace."); } diff --git a/src/libstd/sys/redox/backtrace.rs b/src/libstd/sys/redox/backtrace.rs index 6f53841502ad2..961148fb6b4a8 100644 --- a/src/libstd/sys/redox/backtrace.rs +++ b/src/libstd/sys/redox/backtrace.rs @@ -10,9 +10,14 @@ use libc; use io; -use sys_common::backtrace::output; +use sys_common::backtrace::Frame; + +pub use sys_common::gnu::libbacktrace::*; +pub struct BacktraceContext; #[inline(never)] -pub fn write(w: &mut io::Write) -> io::Result<()> { - output(w, 0, 0 as *mut libc::c_void, None) +pub fn unwind_backtrace(frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + Ok((0, BacktraceContext)) } diff --git a/src/libstd/sys/unix/backtrace/mod.rs b/src/libstd/sys/unix/backtrace/mod.rs index 1eef89bf66f74..29d4012dcdf98 100644 --- a/src/libstd/sys/unix/backtrace/mod.rs +++ b/src/libstd/sys/unix/backtrace/mod.rs @@ -83,7 +83,8 @@ /// to symbols. This is a bit of a hokey implementation as-is, but it works for /// all unix platforms we support right now, so it at least gets the job done. -pub use self::tracing::write; +pub use self::tracing::unwind_backtrace; +pub use self::printing::{foreach_symbol_fileline, resolve_symname}; // tracing impls: mod tracing; @@ -100,3 +101,5 @@ pub mod gnu { Err(io::Error::new(io::ErrorKind::Other, "Not implemented")) } } + +pub struct BacktraceContext; diff --git a/src/libstd/sys/unix/backtrace/printing/dladdr.rs b/src/libstd/sys/unix/backtrace/printing/dladdr.rs index d9b759dc67394..f15d6500478ef 100644 --- a/src/libstd/sys/unix/backtrace/printing/dladdr.rs +++ b/src/libstd/sys/unix/backtrace/printing/dladdr.rs @@ -11,10 +11,11 @@ use io; use io::prelude::*; use libc; +use sys_common::backtrace::Frame; -pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, - _symaddr: *mut libc::c_void) -> io::Result<()> { - use sys_common::backtrace::{output}; +pub fn resolve_symname(frame: Frame, callback: F) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ use intrinsics; use ffi::CStr; @@ -31,11 +32,20 @@ pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, } let mut info: Dl_info = unsafe { intrinsics::init() }; - if unsafe { dladdr(addr, &mut info) == 0 } { - output(w, idx,addr, None) + let symname = if unsafe { dladdr(frame.exact_position, &mut info) == 0 } { + None } else { - output(w, idx, addr, Some(unsafe { - CStr::from_ptr(info.dli_sname).to_bytes() - })) - } + unsafe { + CStr::from_ptr(info.dli_sname).to_str().ok() + } + }; + callback(symname) +} + +pub fn foreach_symbol_fileline(_symbol_addr: Frame, + mut _f: F + _: &BacktraceContext) -> io::Result + where F: FnMut(&[u8], libc::c_int) -> io::Result<()> +{ + Ok(()) } diff --git a/src/libstd/sys/unix/backtrace/printing/mod.rs b/src/libstd/sys/unix/backtrace/printing/mod.rs index 02e53854727f7..1ae82e0110016 100644 --- a/src/libstd/sys/unix/backtrace/printing/mod.rs +++ b/src/libstd/sys/unix/backtrace/printing/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::imp::print; +pub use self::imp::{foreach_symbol_fileline, resolve_symname}; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "emscripten"))] @@ -17,5 +17,6 @@ mod imp; #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "emscripten")))] -#[path = "gnu.rs"] -mod imp; +mod imp { + pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname}; +} diff --git a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs index ca2e70b5003a1..4710de9ff7f54 100644 --- a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs +++ b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs @@ -22,35 +22,26 @@ use io::prelude::*; use io; use libc; use mem; -use sys::mutex::Mutex; +use sys::backtrace::BacktraceContext; +use sys_common::backtrace::Frame; -use super::super::printing::print; - -#[inline(never)] -pub fn write(w: &mut Write) -> io::Result<()> { - extern { - fn backtrace(buf: *mut *mut libc::c_void, - sz: libc::c_int) -> libc::c_int; - } - - // while it doesn't requires lock for work as everything is - // local, it still displays much nicer backtraces when a - // couple of threads panic simultaneously - static LOCK: Mutex = Mutex::new(); - unsafe { - LOCK.lock(); - - writeln!(w, "stack backtrace:")?; - // 100 lines should be enough - const SIZE: usize = 100; - let mut buf: [*mut libc::c_void; SIZE] = mem::zeroed(); - let cnt = backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as usize; - - // skipping the first one as it is write itself - for i in 1..cnt { - print(w, i as isize, buf[i], buf[i])? - } - LOCK.unlock(); +#[inline(never)] // if we know this is a function call, we can skip it when + // tracing +pub fn unwind_backtrace(frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + const FRAME_LEN = 100; + assert!(FRAME_LEN >= frames); + let mut raw_frames = [0; FRAME_LEN]; + let nb_frames = backtrace(frames.as_mut_ptr(), + frames.len() as libc::c_int); + for (from, to) in raw_frames[..nb_frames].iter().zip(frames.iter_mut()) { + *to = Frame { + exact_position: *from, + symbol_addr: *from, + }; } - Ok(()) + (nb_frames as usize, BacktraceContext) } + +extern fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int; diff --git a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs index c1b45620ab04a..8691fe55e7ceb 100644 --- a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs +++ b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs @@ -8,102 +8,97 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use error::Error; use io; -use io::prelude::*; use libc; -use mem; -use sys_common::mutex::Mutex; +use sys::backtrace::BacktraceContext; +use sys_common::backtrace::Frame; -use super::super::printing::print; use unwind as uw; -#[inline(never)] // if we know this is a function call, we can skip it when - // tracing -pub fn write(w: &mut Write) -> io::Result<()> { - struct Context<'a> { - idx: isize, - writer: &'a mut (Write+'a), - last_error: Option, - } +struct Context<'a> { + idx: usize, + frames: &'a mut [Frame], +} - // When using libbacktrace, we use some necessary global state, so we - // need to prevent more than one thread from entering this block. This - // is semi-reasonable in terms of printing anyway, and we know that all - // I/O done here is blocking I/O, not green I/O, so we don't have to - // worry about this being a native vs green mutex. - static LOCK: Mutex = Mutex::new(); - unsafe { - LOCK.lock(); +#[derive(Debug)] +struct UnwindError(uw::_Unwind_Reason_Code); - writeln!(w, "stack backtrace:")?; +impl Error for UnwindError { + fn description(&self) -> &'static str { + "unexpected return value while unwinding" + } +} - let mut cx = Context { writer: w, last_error: None, idx: 0 }; - let ret = match { - uw::_Unwind_Backtrace(trace_fn, - &mut cx as *mut Context as *mut libc::c_void) - } { - uw::_URC_NO_REASON => { - match cx.last_error { - Some(err) => Err(err), - None => Ok(()) - } - } - _ => Ok(()), - }; - LOCK.unlock(); - return ret +impl ::fmt::Display for UnwindError { + fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { + write!(f, "{}: {:?}", self.description(), self.0) } +} - extern fn trace_fn(ctx: *mut uw::_Unwind_Context, - arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { - let cx: &mut Context = unsafe { mem::transmute(arg) }; - let mut ip_before_insn = 0; - let mut ip = unsafe { - uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void - }; - if !ip.is_null() && ip_before_insn == 0 { - // this is a non-signaling frame, so `ip` refers to the address - // after the calling instruction. account for that. - ip = (ip as usize - 1) as *mut _; +#[inline(never)] // if we know this is a function call, we can skip it when + // tracing +pub fn unwind_backtrace(frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + let mut cx = Context { + idx: 0, + frames: frames, + }; + let result_unwind = unsafe { + uw::_Unwind_Backtrace(trace_fn, + &mut cx as *mut Context + as *mut libc::c_void) + }; + // See libunwind:src/unwind/Backtrace.c for the return values. + // No, there is no doc. + match result_unwind { + uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR => { + Ok((cx.idx, BacktraceContext)) } - - // dladdr() on osx gets whiny when we use FindEnclosingFunction, and - // it appears to work fine without it, so we only use - // FindEnclosingFunction on non-osx platforms. In doing so, we get a - // slightly more accurate stack trace in the process. - // - // This is often because panic involves the last instruction of a - // function being "call std::rt::begin_unwind", with no ret - // instructions after it. This means that the return instruction - // pointer points *outside* of the calling function, and by - // unwinding it we go back to the original function. - let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { - ip - } else { - unsafe { uw::_Unwind_FindEnclosingFunction(ip) } - }; - - // Don't print out the first few frames (they're not user frames) - cx.idx += 1; - if cx.idx <= 0 { return uw::_URC_NO_REASON } - // Don't print ginormous backtraces - if cx.idx > 100 { - match write!(cx.writer, " ... \n") { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - return uw::_URC_FAILURE + _ => { + Err(io::Error::new(io::ErrorKind::Other, + UnwindError(result_unwind))) } + } +} - // Once we hit an error, stop trying to print more frames - if cx.last_error.is_some() { return uw::_URC_FAILURE } +extern fn trace_fn(ctx: *mut uw::_Unwind_Context, + arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { + let cx = unsafe { &mut *(arg as *mut Context) }; + let mut ip_before_insn = 0; + let mut ip = unsafe { + uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void + }; + if !ip.is_null() && ip_before_insn == 0 { + // this is a non-signaling frame, so `ip` refers to the address + // after the calling instruction. account for that. + ip = (ip as usize - 1) as *mut _; + } - match print(cx.writer, cx.idx, ip, symaddr) { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } + // dladdr() on osx gets whiny when we use FindEnclosingFunction, and + // it appears to work fine without it, so we only use + // FindEnclosingFunction on non-osx platforms. In doing so, we get a + // slightly more accurate stack trace in the process. + // + // This is often because panic involves the last instruction of a + // function being "call std::rt::begin_unwind", with no ret + // instructions after it. This means that the return instruction + // pointer points *outside* of the calling function, and by + // unwinding it we go back to the original function. + let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { + ip + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(ip) } + }; - // keep going - uw::_URC_NO_REASON + if cx.idx < cx.frames.len() { + cx.frames[cx.idx] = Frame { + symbol_addr: symaddr, + exact_position: ip, + }; + cx.idx += 1; } + + uw::_URC_NO_REASON } diff --git a/src/libstd/sys/windows/backtrace_gnu.rs b/src/libstd/sys/windows/backtrace/backtrace_gnu.rs similarity index 100% rename from src/libstd/sys/windows/backtrace_gnu.rs rename to src/libstd/sys/windows/backtrace/backtrace_gnu.rs diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace/mod.rs similarity index 77% rename from src/libstd/sys/windows/backtrace.rs rename to src/libstd/sys/windows/backtrace/mod.rs index 94aaf439f3d57..ab3407b52b547 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace/mod.rs @@ -32,7 +32,7 @@ use mem; use ptr; use sys::c; use sys::dynamic_lib::DynamicLibrary; -use sys::mutex::Mutex; +use sys_common::backtrace::Frame; macro_rules! sym { ($lib:expr, $e:expr, $t:ident) => ( @@ -43,18 +43,69 @@ macro_rules! sym { ) } -#[cfg(target_env = "msvc")] -#[path = "printing/msvc.rs"] -mod printing; - -#[cfg(target_env = "gnu")] -#[path = "printing/gnu.rs"] mod printing; #[cfg(target_env = "gnu")] #[path = "backtrace_gnu.rs"] pub mod gnu; +pub use printing::{resolve_symname, foreach_symbol_fileline}; + +pub fn unwind_backtrace(frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + let dbghelp = DynamicLibrary::open("dbghelp.dll")?; + + // Fetch the symbols necessary from dbghelp.dll + let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn); + let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn); + let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn); + + // Allocate necessary structures for doing the stack walk + let process = c::GetCurrentProcess(); + let thread = c::GetCurrentThread(); + let mut context: c::CONTEXT = mem::zeroed(); + c::RtlCaptureContext(&mut context); + let mut frame: c::STACKFRAME64 = mem::zeroed(); + let image = init_frame(&mut frame, &context); + + let backtrace_context = BacktraceContext { + handle: process, + SymCleanup: SymCleanup, + dbghelp: dbghelp, + }; + + // Initialize this process's symbols + let ret = SymInitialize(process, ptr::null_mut(), c::TRUE); + if ret != c::TRUE { + return Ok((0, backtrace_context)) + } + + // And now that we're done with all the setup, do the stack walking! + // Start from -1 to avoid printing this stack frame, which will + // always be exactly the same. + let mut i = 0; + while i < frames.len() && + StackWalk64(image, process, thread, &mut frame, &mut context, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut()) == c::TRUE + { + let addr = frame.AddrPC.Offset; + if addr == frame.AddrReturn.Offset || addr == 0 || + frame.AddrReturn.Offset == 0 { break } + + frames[i] = Frame { + symbol_addr: addr - 1, + exact_position: addr - 1, + } + i += 1; + } + + Ok((i, backtrace_context)) +} + type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL; @@ -68,8 +119,8 @@ type StackWalk64Fn = *mut c_void, *mut c_void) -> c::BOOL; #[cfg(target_arch = "x86")] -pub fn init_frame(frame: &mut c::STACKFRAME64, - ctx: &c::CONTEXT) -> c::DWORD { +fn init_frame(frame: &mut c::STACKFRAME64, + ctx: &c::CONTEXT) -> c::DWORD { frame.AddrPC.Offset = ctx.Eip as u64; frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; frame.AddrStack.Offset = ctx.Esp as u64; @@ -80,8 +131,8 @@ pub fn init_frame(frame: &mut c::STACKFRAME64, } #[cfg(target_arch = "x86_64")] -pub fn init_frame(frame: &mut c::STACKFRAME64, - ctx: &c::CONTEXT) -> c::DWORD { +fn init_frame(frame: &mut c::STACKFRAME64, + ctx: &c::CONTEXT) -> c::DWORD { frame.AddrPC.Offset = ctx.Rip as u64; frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; frame.AddrStack.Offset = ctx.Rsp as u64; @@ -91,73 +142,14 @@ pub fn init_frame(frame: &mut c::STACKFRAME64, c::IMAGE_FILE_MACHINE_AMD64 } -struct Cleanup { +pub struct BacktraceContext { handle: c::HANDLE, SymCleanup: SymCleanupFn, + dbghelp: DynamicLibrary, } -impl Drop for Cleanup { +impl Drop for BacktraceContext { fn drop(&mut self) { unsafe { (self.SymCleanup)(self.handle); } } } - -pub fn write(w: &mut Write) -> io::Result<()> { - // According to windows documentation, all dbghelp functions are - // single-threaded. - static LOCK: Mutex = Mutex::new(); - unsafe { - LOCK.lock(); - let res = _write(w); - LOCK.unlock(); - return res - } -} - -unsafe fn _write(w: &mut Write) -> io::Result<()> { - let dbghelp = match DynamicLibrary::open("dbghelp.dll") { - Ok(lib) => lib, - Err(..) => return Ok(()), - }; - - // Fetch the symbols necessary from dbghelp.dll - let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn); - let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn); - let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn); - - // Allocate necessary structures for doing the stack walk - let process = c::GetCurrentProcess(); - let thread = c::GetCurrentThread(); - let mut context: c::CONTEXT = mem::zeroed(); - c::RtlCaptureContext(&mut context); - let mut frame: c::STACKFRAME64 = mem::zeroed(); - let image = init_frame(&mut frame, &context); - - // Initialize this process's symbols - let ret = SymInitialize(process, ptr::null_mut(), c::TRUE); - if ret != c::TRUE { return Ok(()) } - let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; - - // And now that we're done with all the setup, do the stack walking! - // Start from -1 to avoid printing this stack frame, which will - // always be exactly the same. - let mut i = -1; - write!(w, "stack backtrace:\n")?; - while StackWalk64(image, process, thread, &mut frame, &mut context, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut()) == c::TRUE { - let addr = frame.AddrPC.Offset; - if addr == frame.AddrReturn.Offset || addr == 0 || - frame.AddrReturn.Offset == 0 { break } - - i += 1; - - if i >= 0 { - printing::print(w, i, addr - 1, process, &dbghelp)?; - } - } - - Ok(()) -} diff --git a/src/libstd/sys/unix/backtrace/printing/gnu.rs b/src/libstd/sys/windows/backtrace/printing/mod.rs similarity index 64% rename from src/libstd/sys/unix/backtrace/printing/gnu.rs rename to src/libstd/sys/windows/backtrace/printing/mod.rs index fb06fbedaf57b..c66ef3b2a8826 100644 --- a/src/libstd/sys/unix/backtrace/printing/gnu.rs +++ b/src/libstd/sys/windows/backtrace/printing/mod.rs @@ -8,4 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use sys_common::gnu::libbacktrace::print; +#[cfg(target_env = "msvc")] +#[path = "msvc.rs"] +mod printing; + +#[cfg(target_env = "gnu")] +mod printing { + pub use sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname}; +} + +pub use printing::{foreach_symbol_fileline, resolve_symname}; diff --git a/src/libstd/sys/windows/printing/msvc.rs b/src/libstd/sys/windows/backtrace/printing/msvc.rs similarity index 57% rename from src/libstd/sys/windows/printing/msvc.rs rename to src/libstd/sys/windows/backtrace/printing/msvc.rs index 9c29ac4082a6b..e0e0dc73cc1e2 100644 --- a/src/libstd/sys/windows/printing/msvc.rs +++ b/src/libstd/sys/windows/backtrace/printing/msvc.rs @@ -15,6 +15,7 @@ use libc::{c_ulong, c_int, c_char, c_void}; use mem; use sys::c; use sys::dynamic_lib::DynamicLibrary; +use sys::backtrace::BacktraceContext; use sys_common::backtrace::{output, output_fileline}; type SymFromAddrFn = @@ -24,17 +25,12 @@ type SymGetLineFromAddr64Fn = unsafe extern "system" fn(c::HANDLE, u64, *mut u32, *mut c::IMAGEHLP_LINE64) -> c::BOOL; -pub fn print(w: &mut Write, - i: isize, - addr: u64, - process: c::HANDLE, - dbghelp: &DynamicLibrary) - -> io::Result<()> { +/// Converts a pointer to symbol to its string value. +pub fn resolve_symname(symaddr: Frame, callback: F) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ unsafe { - let SymFromAddr = sym!(dbghelp, "SymFromAddr", SymFromAddrFn); - let SymGetLineFromAddr64 = sym!(dbghelp, - "SymGetLineFromAddr64", - SymGetLineFromAddr64Fn); + let SymFromAddr = sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn); let mut info: c::SYMBOL_INFO = mem::zeroed(); info.MaxNameLen = c::MAX_SYM_NAME as c_ulong; @@ -44,30 +40,44 @@ pub fn print(w: &mut Write, info.SizeOfStruct = 88; let mut displacement = 0u64; - let ret = SymFromAddr(process, addr, &mut displacement, &mut info); + let ret = SymFromAddr(context.process, + symbol_addr as u64, + &mut displacement, + &mut info); - let name = if ret == c::TRUE { + let symname = if ret == c::TRUE { let ptr = info.Name.as_ptr() as *const c_char; - Some(CStr::from_ptr(ptr).to_bytes()) + CStr::from_ptr(ptr).to_str().ok() } else { None - }; + } + callback(symname) + } +} - output(w, i, addr as usize as *mut c_void, name)?; +pub fn foreach_symbol_fileline(symbol_addr: Frame, + mut f: F, + context: &BacktraceContext) + -> io::Result + where F: FnMut(&[u8], libc::c_int) -> io::Result<()> +{ + unsafe { + let SymGetLineFromAddr64 = sym!(&context.dbghelp, + "SymGetLineFromAddr64", + SymGetLineFromAddr64Fn); - // Now find out the filename and line number let mut line: c::IMAGEHLP_LINE64 = mem::zeroed(); line.SizeOfStruct = ::mem::size_of::() as u32; let mut displacement = 0u32; - let ret = SymGetLineFromAddr64(process, addr, &mut displacement, &mut line); + let ret = SymGetLineFromAddr64(context.process, + symbol_addr, + &mut displacement, + &mut line); if ret == c::TRUE { - output_fileline(w, - CStr::from_ptr(line.Filename).to_bytes(), - line.LineNumber as c_int, - false) - } else { - Ok(()) + let name = CStr::from_ptr(line.Filename).to_bytes(); + f(name, line.LineNumber as libc::c_int)?; } + Ok(false) } } diff --git a/src/libstd/sys/windows/printing/gnu.rs b/src/libstd/sys/windows/printing/gnu.rs deleted file mode 100644 index be2d5273c0728..0000000000000 --- a/src/libstd/sys/windows/printing/gnu.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use io::prelude::*; -use io; -use libc::c_void; -use sys::c; -use sys::dynamic_lib::DynamicLibrary; -use sys_common::gnu::libbacktrace; - -pub fn print(w: &mut Write, - i: isize, - addr: u64, - _process: c::HANDLE, - _dbghelp: &DynamicLibrary) - -> io::Result<()> { - let addr = addr as usize as *mut c_void; - libbacktrace::print(w, i, addr, addr) -} diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs index a8540fed9286f..0b7c2694368b8 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -10,14 +10,25 @@ #![cfg_attr(target_os = "nacl", allow(dead_code))] +/// Common code for printing the backtrace in the same way across the different +/// supported platforms. + use env; use io::prelude::*; use io; use libc; use str; use sync::atomic::{self, Ordering}; +use path::Path; +use sys::mutex::Mutex; +use ptr; -pub use sys::backtrace::write; +pub use sys::backtrace::{ + unwind_backtrace, + resolve_symname, + foreach_symbol_fileline, + BacktraceContext +}; #[cfg(target_pointer_width = "64")] pub const HEX_WIDTH: usize = 18; @@ -25,45 +36,214 @@ pub const HEX_WIDTH: usize = 18; #[cfg(target_pointer_width = "32")] pub const HEX_WIDTH: usize = 10; +/// Represents an item in the backtrace list. See `unwind_backtrace` for how +/// it is created. +#[derive(Debug, Copy, Clone)] +pub struct Frame { + /// Exact address of the call that failed. + pub exact_position: *const libc::c_void, + /// Address of the enclosing function. + pub symbol_addr: *const libc::c_void, +} + +/// Max number of frames to print. +const MAX_NB_FRAMES: usize = 100; + +/// Prints the current backtrace. +pub fn print(w: &mut Write, format: PrintFormat) -> io::Result<()> { + static LOCK: Mutex = Mutex::new(); + + // Use a lock to prevent mixed output in multithreading context. + // Some platforms also requires it, like `SymFromAddr` on Windows. + unsafe { + LOCK.lock(); + let res = _print(w, format); + LOCK.unlock(); + res + } +} + +fn _print(w: &mut Write, format: PrintFormat) -> io::Result<()> { + let mut frames = [Frame { + exact_position: ptr::null(), + symbol_addr: ptr::null(), + }; MAX_NB_FRAMES]; + let (nb_frames, context) = unwind_backtrace(&mut frames)?; + let (skipped_before, skipped_after) = + filter_frames(&frames[..nb_frames], format); + if format == PrintFormat::Short { + writeln!(w, "note: Some details are omitted, \ + run with `RUST_BACKTRACE=full` for a verbose backtrace.")?; + } + writeln!(w, "stack backtrace:")?; + + let filtered_frames = &frames[..nb_frames - skipped_after]; + for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() { + resolve_symname(*frame, |symname| { + output(w, index, *frame, symname, format) + })?; + let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| { + output_fileline(w, file, line, format) + }, &context)?; + if has_more_filenames { + w.write_all(b" <... and possibly more>")?; + } + } + + Ok(()) +} + +fn filter_frames(frames: &[Frame], format: PrintFormat) -> (usize, usize) { + if format == PrintFormat::Full { + return (0, 0); + } + + let mut skipped_before = 0; + for (i, frame) in frames.iter().enumerate() { + skipped_before = i; + let mut skip = false; + + let _ = resolve_symname(*frame, |symname| { + if let Some(mangled_symbol_name) = symname { + let magics_begin = [ + "_ZN3std3sys3imp9backtrace", + "_ZN3std10sys_common9backtrace", + "_ZN3std9panicking", + "_ZN4core9panicking", + "rust_begin_unwind", + "_ZN4core6result13unwrap_failed", + ]; + if !magics_begin.iter().any(|s| mangled_symbol_name.starts_with(s)) { + skip = true; + } + } + Ok(()) + }); + + if skip { + break; + } + } + + let mut skipped_after = 0; + for (i, frame) in frames.iter().rev().enumerate() { + let _ = resolve_symname(*frame, |symname| { + if let Some(mangled_symbol_name) = symname { + let magics_end = [ + "_ZN3std9panicking3try7do_call", + "__rust_maybe_catch_panic", + "__libc_start_main", + "__rust_try", + "_start", + ]; + if magics_end.iter().any(|s| mangled_symbol_name.starts_with(s)) { + skipped_after = i + 1; + } + } + Ok(()) + }); + } + + (skipped_before, skipped_after) +} + +/// Controls how the backtrace should be formated. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum PrintFormat { + /// Show all the frames with absolute path for files. + Full = 2, + /// Show only relevant data from the backtrace. + Short = 3, +} + // For now logging is turned off by default, and this function checks to see // whether the magical environment variable is present to see if it's turned on. -pub fn log_enabled() -> bool { +pub fn log_enabled() -> Option { static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0); match ENABLED.load(Ordering::SeqCst) { - 1 => return false, - 2 => return true, - _ => {} + 0 => {}, + 1 => return None, + 2 => return Some(PrintFormat::Full), + 3 => return Some(PrintFormat::Short), + _ => unreachable!(), } let val = match env::var_os("RUST_BACKTRACE") { - Some(x) => if &x == "0" { 1 } else { 2 }, - None => 1, + Some(x) => if &x == "0" { + None + } else if &x == "full" { + Some(PrintFormat::Full) + } else { + Some(PrintFormat::Short) + }, + None => None, }; - ENABLED.store(val, Ordering::SeqCst); - val == 2 + ENABLED.store(match val { + Some(v) => v as isize, + None => 1, + }, Ordering::SeqCst); + val } -// These output functions should now be used everywhere to ensure consistency. -pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void, - s: Option<&[u8]>) -> io::Result<()> { - write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)?; - match s.and_then(|s| str::from_utf8(s).ok()) { - Some(string) => demangle(w, string)?, - None => write!(w, "")?, +/// Print the symbol of the backtrace frame. +/// +/// These output functions should now be used everywhere to ensure consistency. +/// You may want to also use `output_fileline`. +fn output(w: &mut Write, idx: usize, frame: Frame, + s: Option<&str>, format: PrintFormat) -> io::Result<()> { + // Remove the `17: 0x0 - ` line. + if format == PrintFormat::Short && frame.exact_position == ptr::null() { + return Ok(()); + } + match format { + PrintFormat::Full => write!(w, + " {:2}: {:2$?} - ", + idx, + frame.exact_position, + HEX_WIDTH)?, + PrintFormat::Short => write!(w, " {:2}: ", idx)?, } - w.write_all(&['\n' as u8]) + match s { + Some(string) => demangle(w, string, format)?, + None => w.write_all(b"")?, + } + w.write_all(b"\n") } +/// Print the filename and line number of the backtrace frame. +/// +/// See also `output`. #[allow(dead_code)] -pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, - more: bool) -> io::Result<()> { - let file = str::from_utf8(file).unwrap_or(""); +fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, + format: PrintFormat) -> io::Result<()> { // prior line: " ##: {:2$} - func" - write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH)?; - if more { - write!(w, " <... and possibly more>")?; + w.write_all(b"")?; + match format { + PrintFormat::Full => write!(w, + " {:1$}", + "", + HEX_WIDTH)?, + PrintFormat::Short => write!(w, " ")?, } - w.write_all(&['\n' as u8]) + + let file = str::from_utf8(file).unwrap_or(""); + let file_path = Path::new(file); + let mut already_printed = false; + if format == PrintFormat::Short && file_path.is_absolute() { + if let Ok(cwd) = env::current_dir() { + if let Ok(stripped) = file_path.strip_prefix(&cwd) { + if let Some(s) = stripped.to_str() { + write!(w, " at ./{}:{}", s, line)?; + already_printed = true; + } + } + } + } + if !already_printed { + write!(w, " at {}:{}", file, line)?; + } + + w.write_all(b"\n") } @@ -84,7 +264,7 @@ pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, // Note that this demangler isn't quite as fancy as it could be. We have lots // of other information in our symbols like hashes, version, type information, // etc. Additionally, this doesn't handle glue symbols at all. -pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> { +pub fn demangle(writer: &mut Write, s: &str, format: PrintFormat) -> io::Result<()> { // First validate the symbol. If it doesn't look like anything we're // expecting, we just print it literally. Note that we must handle non-rust // symbols because we could have any function in the backtrace. @@ -123,6 +303,22 @@ pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> { if !valid { writer.write_all(s.as_bytes())?; } else { + // remove the `::hfc2edb670e5eda97` part at the end of the symbol. + if format == PrintFormat::Short { + // The symbol in still mangled. + let mut split = inner.rsplitn(2, "17h"); + match (split.next(), split.next()) { + (Some(addr), rest) => { + if addr.len() == 16 && + addr.chars().all(|c| c.is_digit(16)) + { + inner = rest.unwrap_or(""); + } + } + _ => (), + } + } + let mut first = true; while !inner.is_empty() { if !first { @@ -208,7 +404,9 @@ mod tests { use sys_common; macro_rules! t { ($a:expr, $b:expr) => ({ let mut m = Vec::new(); - sys_common::backtrace::demangle(&mut m, $a).unwrap(); + sys_common::backtrace::demangle(&mut m, + $a, + super::PrintFormat::Full).unwrap(); assert_eq!(String::from_utf8(m).unwrap(), $b); }) } diff --git a/src/libstd/sys_common/gnu/libbacktrace.rs b/src/libstd/sys_common/gnu/libbacktrace.rs index 0bdbeddb11212..fe834e20b023f 100644 --- a/src/libstd/sys_common/gnu/libbacktrace.rs +++ b/src/libstd/sys_common/gnu/libbacktrace.rs @@ -8,186 +8,202 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use io; -use io::prelude::*; use libc; -use sys_common::backtrace::{output, output_fileline}; - -pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, - symaddr: *mut libc::c_void) -> io::Result<()> { - use ffi::CStr; - use mem; - use ptr; - - //////////////////////////////////////////////////////////////////////// - // libbacktrace.h API - //////////////////////////////////////////////////////////////////////// - type backtrace_syminfo_callback = - extern "C" fn(data: *mut libc::c_void, - pc: libc::uintptr_t, - symname: *const libc::c_char, - symval: libc::uintptr_t, - symsize: libc::uintptr_t); - type backtrace_full_callback = - extern "C" fn(data: *mut libc::c_void, - pc: libc::uintptr_t, - filename: *const libc::c_char, - lineno: libc::c_int, - function: *const libc::c_char) -> libc::c_int; - type backtrace_error_callback = - extern "C" fn(data: *mut libc::c_void, - msg: *const libc::c_char, - errnum: libc::c_int); - enum backtrace_state {} - - extern { - fn backtrace_create_state(filename: *const libc::c_char, - threaded: libc::c_int, - error: backtrace_error_callback, - data: *mut libc::c_void) - -> *mut backtrace_state; - fn backtrace_syminfo(state: *mut backtrace_state, - addr: libc::uintptr_t, - cb: backtrace_syminfo_callback, - error: backtrace_error_callback, - data: *mut libc::c_void) -> libc::c_int; - fn backtrace_pcinfo(state: *mut backtrace_state, - addr: libc::uintptr_t, - cb: backtrace_full_callback, - error: backtrace_error_callback, - data: *mut libc::c_void) -> libc::c_int; - } - - //////////////////////////////////////////////////////////////////////// - // helper callbacks - //////////////////////////////////////////////////////////////////////// - - type FileLine = (*const libc::c_char, libc::c_int); - - extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, - _errnum: libc::c_int) { - // do nothing for now - } - extern fn syminfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - symname: *const libc::c_char, - _symval: libc::uintptr_t, - _symsize: libc::uintptr_t) { - let slot = data as *mut *const libc::c_char; - unsafe { *slot = symname; } - } - extern fn pcinfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - filename: *const libc::c_char, - lineno: libc::c_int, - _function: *const libc::c_char) -> libc::c_int { - if !filename.is_null() { - let slot = data as *mut &mut [FileLine]; - let buffer = unsafe {ptr::read(slot)}; - - // if the buffer is not full, add file:line to the buffer - // and adjust the buffer for next possible calls to pcinfo_cb. - if !buffer.is_empty() { - buffer[0] = (filename, lineno); - unsafe { ptr::write(slot, &mut buffer[1..]); } - } - } - - 0 - } - - // The libbacktrace API supports creating a state, but it does not - // support destroying a state. I personally take this to mean that a - // state is meant to be created and then live forever. - // - // I would love to register an at_exit() handler which cleans up this - // state, but libbacktrace provides no way to do so. - // - // With these constraints, this function has a statically cached state - // that is calculated the first time this is requested. Remember that - // backtracing all happens serially (one global lock). - // - // Things don't work so well on not-Linux since libbacktrace can't track - // down that executable this is. We at one point used env::current_exe but - // it turns out that there are some serious security issues with that - // approach. - // - // Specifically, on certain platforms like BSDs, a malicious actor can cause - // an arbitrary file to be placed at the path returned by current_exe. - // libbacktrace does not behave defensively in the presence of ill-formed - // DWARF information, and has been demonstrated to segfault in at least one - // case. There is no evidence at the moment to suggest that a more carefully - // constructed file can't cause arbitrary code execution. As a result of all - // of this, we don't hint libbacktrace with the path to the current process. - unsafe fn init_state() -> *mut backtrace_state { - static mut STATE: *mut backtrace_state = ptr::null_mut(); - if !STATE.is_null() { return STATE } - - let filename = match ::sys::backtrace::gnu::get_executable_filename() { - Ok((filename, file)) => { - // filename is purposely leaked here since libbacktrace requires - // it to stay allocated permanently, file is also leaked so that - // the file stays locked - let filename_ptr = filename.as_ptr(); - mem::forget(filename); - mem::forget(file); - filename_ptr - }, - Err(_) => ptr::null(), - }; - - STATE = backtrace_create_state(filename, 0, error_cb, - ptr::null_mut()); - STATE - } - - //////////////////////////////////////////////////////////////////////// - // translation - //////////////////////////////////////////////////////////////////////// - - // backtrace errors are currently swept under the rug, only I/O - // errors are reported - let state = unsafe { init_state() }; - if state.is_null() { - return output(w, idx, addr, None) - } - let mut data = ptr::null(); - let data_addr = &mut data as *mut *const libc::c_char; - let ret = unsafe { - backtrace_syminfo(state, symaddr as libc::uintptr_t, - syminfo_cb, error_cb, - data_addr as *mut libc::c_void) - }; - if ret == 0 || data.is_null() { - output(w, idx, addr, None)?; - } else { - output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))?; - } +use ffi::CStr; +use io; +use mem; +use ptr; +use sys::backtrace::BacktraceContext; +use sys_common::backtrace::Frame; + +pub fn foreach_symbol_fileline(frame: Frame, + mut f: F, + _: &BacktraceContext) -> io::Result +where F: FnMut(&[u8], libc::c_int) -> io::Result<()> +{ // pcinfo may return an arbitrary number of file:line pairs, // in the order of stack trace (i.e. inlined calls first). // in order to avoid allocation, we stack-allocate a fixed size of entries. const FILELINE_SIZE: usize = 32; let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE]; let ret; - let fileline_count; - { + let fileline_count = { + let state = unsafe { init_state() }; let mut fileline_win: &mut [FileLine] = &mut fileline_buf; let fileline_addr = &mut fileline_win as *mut &mut [FileLine]; ret = unsafe { - backtrace_pcinfo(state, addr as libc::uintptr_t, - pcinfo_cb, error_cb, + backtrace_pcinfo(state, + frame.exact_position as libc::uintptr_t, + pcinfo_cb, + error_cb, fileline_addr as *mut libc::c_void) }; - fileline_count = FILELINE_SIZE - fileline_win.len(); - } + FILELINE_SIZE - fileline_win.len() + }; if ret == 0 { - for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() { + for &(file, line) in &fileline_buf[..fileline_count] { if file.is_null() { continue; } // just to be sure let file = unsafe { CStr::from_ptr(file).to_bytes() }; - output_fileline(w, file, line, i == FILELINE_SIZE - 1)?; + f(file, line)?; + } + Ok(fileline_count == FILELINE_SIZE) + } else { + Ok(false) + } +} + +/// Converts a pointer to symbol to its string value. +pub fn resolve_symname(frame: Frame, callback: F) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ + let symname = { + let state = unsafe { init_state() }; + if state.is_null() { + None + } else { + let mut data = ptr::null(); + let data_addr = &mut data as *mut *const libc::c_char; + let ret = unsafe { + backtrace_syminfo(state, + frame.symbol_addr as libc::uintptr_t, + syminfo_cb, + error_cb, + data_addr as *mut libc::c_void) + }; + if ret == 0 || data.is_null() { + None + } else { + unsafe { + CStr::from_ptr(data).to_str().ok() + } + } + } + }; + callback(symname) +} + +//////////////////////////////////////////////////////////////////////// +// libbacktrace.h API +//////////////////////////////////////////////////////////////////////// +type backtrace_syminfo_callback = +extern "C" fn(data: *mut libc::c_void, + pc: libc::uintptr_t, + symname: *const libc::c_char, + symval: libc::uintptr_t, + symsize: libc::uintptr_t); +type backtrace_full_callback = +extern "C" fn(data: *mut libc::c_void, + pc: libc::uintptr_t, + filename: *const libc::c_char, + lineno: libc::c_int, + function: *const libc::c_char) -> libc::c_int; +type backtrace_error_callback = +extern "C" fn(data: *mut libc::c_void, + msg: *const libc::c_char, + errnum: libc::c_int); +enum backtrace_state {} +#[link(name = "backtrace", kind = "static")] +#[cfg(all(not(test), not(cargobuild)))] +extern {} + +extern { + fn backtrace_create_state(filename: *const libc::c_char, + threaded: libc::c_int, + error: backtrace_error_callback, + data: *mut libc::c_void) + -> *mut backtrace_state; + fn backtrace_syminfo(state: *mut backtrace_state, + addr: libc::uintptr_t, + cb: backtrace_syminfo_callback, + error: backtrace_error_callback, + data: *mut libc::c_void) -> libc::c_int; + fn backtrace_pcinfo(state: *mut backtrace_state, + addr: libc::uintptr_t, + cb: backtrace_full_callback, + error: backtrace_error_callback, + data: *mut libc::c_void) -> libc::c_int; +} + +//////////////////////////////////////////////////////////////////////// +// helper callbacks +//////////////////////////////////////////////////////////////////////// + +type FileLine = (*const libc::c_char, libc::c_int); + +extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, + _errnum: libc::c_int) { + // do nothing for now +} +extern fn syminfo_cb(data: *mut libc::c_void, + _pc: libc::uintptr_t, + symname: *const libc::c_char, + _symval: libc::uintptr_t, + _symsize: libc::uintptr_t) { + let slot = data as *mut *const libc::c_char; + unsafe { *slot = symname; } +} +extern fn pcinfo_cb(data: *mut libc::c_void, + _pc: libc::uintptr_t, + filename: *const libc::c_char, + lineno: libc::c_int, + _function: *const libc::c_char) -> libc::c_int { + if !filename.is_null() { + let slot = data as *mut &mut [FileLine]; + let buffer = unsafe {ptr::read(slot)}; + + // if the buffer is not full, add file:line to the buffer + // and adjust the buffer for next possible calls to pcinfo_cb. + if !buffer.is_empty() { + buffer[0] = (filename, lineno); + unsafe { ptr::write(slot, &mut buffer[1..]); } } } - Ok(()) + 0 +} + +// The libbacktrace API supports creating a state, but it does not +// support destroying a state. I personally take this to mean that a +// state is meant to be created and then live forever. +// +// I would love to register an at_exit() handler which cleans up this +// state, but libbacktrace provides no way to do so. +// +// With these constraints, this function has a statically cached state +// that is calculated the first time this is requested. Remember that +// backtracing all happens serially (one global lock). +// +// Things don't work so well on not-Linux since libbacktrace can't track +// down that executable this is. We at one point used env::current_exe but +// it turns out that there are some serious security issues with that +// approach. +// +// Specifically, on certain platforms like BSDs, a malicious actor can cause +// an arbitrary file to be placed at the path returned by current_exe. +// libbacktrace does not behave defensively in the presence of ill-formed +// DWARF information, and has been demonstrated to segfault in at least one +// case. There is no evidence at the moment to suggest that a more carefully +// constructed file can't cause arbitrary code execution. As a result of all +// of this, we don't hint libbacktrace with the path to the current process. +unsafe fn init_state() -> *mut backtrace_state { + static mut STATE: *mut backtrace_state = ptr::null_mut(); + if !STATE.is_null() { return STATE } + + let filename = match ::sys::backtrace::gnu::get_executable_filename() { + Ok((filename, file)) => { + // filename is purposely leaked here since libbacktrace requires + // it to stay allocated permanently, file is also leaked so that + // the file stays locked + let filename_ptr = filename.as_ptr(); + mem::forget(filename); + mem::forget(file); + filename_ptr + }, + Err(_) => ptr::null(), + }; + + STATE = backtrace_create_state(filename, 0, error_cb, + ptr::null_mut()); + STATE } diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs index 7fb58373251a5..e22f670267298 100644 --- a/src/libunwind/libunwind.rs +++ b/src/libunwind/libunwind.rs @@ -18,7 +18,7 @@ macro_rules! cfg_if { use libc::{c_int, c_void, uintptr_t}; #[repr(C)] -#[derive(Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum _Unwind_Reason_Code { _URC_NO_REASON = 0, _URC_FOREIGN_EXCEPTION_CAUGHT = 1, diff --git a/src/test/codegen/match.rs b/src/test/codegen/match.rs index ac47f6082e3e3..aa100da60132f 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 9911e093a8980..e12eff72c7f41 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 658a0c1546b9f..0d5f5fd75eba8 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 7239e32357b95..0e8971269b007 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/run-pass/backtrace-debuginfo.rs b/src/test/run-pass/backtrace-debuginfo.rs index 626eccfc9ec86..88fee9ed25b8d 100644 --- a/src/test/run-pass/backtrace-debuginfo.rs +++ b/src/test/run-pass/backtrace-debuginfo.rs @@ -141,12 +141,12 @@ fn run_test(me: &str) { use std::process::Command; let mut template = Command::new(me); - template.env("RUST_BACKTRACE", "1"); + template.env("RUST_BACKTRACE", "full"); let mut i = 0; loop { let out = Command::new(me) - .env("RUST_BACKTRACE", "1") + .env("RUST_BACKTRACE", "full") .arg(i.to_string()).output().unwrap(); let output = str::from_utf8(&out.stdout).unwrap(); let error = str::from_utf8(&out.stderr).unwrap(); diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs index 834ce984e6632..dcdf82682f3a5 100644 --- a/src/test/run-pass/backtrace.rs +++ b/src/test/run-pass/backtrace.rs @@ -47,7 +47,7 @@ fn template(me: &str) -> Command { } fn expected(fn_name: &str) -> String { - format!(" - backtrace::{}", fn_name) + format!(" backtrace::{}", fn_name) } fn runtest(me: &str) { @@ -59,6 +59,53 @@ fn runtest(me: &str) { assert!(s.contains("stack backtrace") && s.contains(&expected("foo")), "bad output: {}", s); + // Make sure than the short version cleans the backtrace. + let p = template(me).arg("fail").env("RUST_BACKTRACE", "1").spawn().unwrap(); + let out = p.wait_with_output().unwrap(); + assert!(!out.status.success()); + let s = str::from_utf8(&out.stderr).unwrap(); + let removed_symbols = &[ + "std::sys::imp::backtrace", + "std::sys_common::backtrace", + "std::panicking", + "core::panicking", + "rust_begin_unwind", + "code::result::unwrap_failed", + "std::panicking::try::do_call", + "__rust_maybe_catch_panic", + "__libc_start_main", + "__rust_try", + "_start", + ]; + for symbol in removed_symbols { + assert!(!s.contains(symbol), + "{} should be removed from the backtrace", + symbol); + } + assert!(s.contains(" 0:"), "the frame number should start at 0"); + + // Only on linux for _start and __libc_start_main + #[cfg(target_os="linux")] + { + // Make sure than the short version cleans the backtrace. + let p = template(me).arg("fail").env("RUST_BACKTRACE", "full").spawn().unwrap(); + let out = p.wait_with_output().unwrap(); + assert!(!out.status.success()); + let s = str::from_utf8(&out.stderr).unwrap(); + let should_be_present = &[ + "std::panicking", + "__rust_maybe_catch_panic", + "__libc_start_main", + "_start", + ]; + for symbol in should_be_present { + // May give false positive due to inlining. + assert!(s.contains(symbol), + "the full version of the backtrace should contain {}", + symbol); + } + } + // Make sure the stack trace is *not* printed // (Remove RUST_BACKTRACE from our own environment, in case developer // is running `make check` with it on.) diff --git a/src/test/rustdoc/impl-disambiguation.rs b/src/test/rustdoc/impl-disambiguation.rs new file mode 100644 index 0000000000000..afe1daf5983a2 --- /dev/null +++ b/src/test/rustdoc/impl-disambiguation.rs @@ -0,0 +1,40 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +pub trait Foo {} + +pub struct Bar { field: T } + +// @has foo/trait.Foo.html '//*[@class="item-list"]//code' \ +// "impl Foo for Bar" +impl Foo for Bar {} +// @has foo/trait.Foo.html '//*[@class="item-list"]//code' \ +// "impl Foo for Bar" +impl Foo for Bar {} +// @has foo/trait.Foo.html '//*[@class="item-list"]//code' \ +// "impl<'a> Foo for &'a Bar" +impl<'a> Foo for &'a Bar {} + +pub mod mod1 { + pub struct Baz {} +} + +pub mod mod2 { + pub enum Baz {} +} + +// @has foo/trait.Foo.html '//*[@class="item-list"]//code' \ +// "impl Foo for foo::mod1::Baz" +impl Foo for mod1::Baz {} +// @has foo/trait.Foo.html '//*[@class="item-list"]//code' \ +// "impl<'a> Foo for &'a foo::mod2::Baz" +impl<'a> Foo for &'a mod2::Baz {} diff --git a/src/test/ui/custom-derive/issue-36935.rs b/src/test/ui/custom-derive/issue-36935.rs index 22d603563de17..2231c3c242285 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 9a5e2de14e3b0..46cc7a42b0429 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