From c68e9632db0bac369dea6cd2206b9119aab83d2e Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 31 Jan 2017 01:10:54 +0200 Subject: [PATCH 01/25] Add Rvalue::Discriminant to retrieve discriminant --- src/librustc/mir/mod.rs | 7 ++++ src/librustc/mir/tcx.rs | 32 ++++++++++++------- src/librustc/mir/visit.rs | 4 +++ .../borrowck/mir/gather_moves.rs | 1 + src/librustc_mir/transform/qualify_consts.rs | 8 +++++ src/librustc_passes/mir_stats.rs | 1 + src/librustc_trans/mir/rvalue.rs | 19 +++++++++++ 7 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 6e9091cf31728..64642249f965d 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -997,6 +997,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 +1117,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..68fbadd5d6065 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -135,15 +135,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 +152,35 @@ 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) => { + if let ty::TyAdt(_, _) = lval.ty(mir, tcx).to_ty(tcx).sty { + // TODO + None + } else { + None + } + } + 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 +204,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..921b4e78b32c9 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -506,6 +506,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); } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 7cf6ab2999c05..806395c857a88 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(..) => { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 9c1107344f241..16371be576b05 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -739,6 +739,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_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index cec1c20519bd6..e29febdb712d3 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -186,6 +186,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/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 81b241b485175..74df3428b99e3 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -429,6 +429,24 @@ 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); + // FIXME: inline this + let discr = adt::trans_get_discr(&bcx, enum_ty, discr_lvalue.llval, None, true); + let discr = if common::val_ty(discr) == Type::i1(bcx.ccx) { + bcx.zext(discr, discr_type) + } else { + bcx.trunc(discr, discr_type) + }; + (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 +675,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, From caf9f95f4699b7c1a83b7567b01870365e52e20c Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 31 Jan 2017 05:32:08 +0200 Subject: [PATCH 02/25] If is now always a SwitchInt in MIR --- src/librustc/mir/mod.rs | 12 +----- src/librustc/mir/visit.rs | 11 +----- .../borrowck/mir/dataflow/mod.rs | 4 -- .../borrowck/mir/elaborate_drops.rs | 9 +++-- .../borrowck/mir/gather_moves.rs | 1 - src/librustc_mir/build/expr/into.rs | 37 ++++++++++++------- src/librustc_mir/build/matches/mod.rs | 9 +++-- src/librustc_mir/build/matches/test.rs | 35 +++++++++++------- src/librustc_mir/hair/mod.rs | 3 +- src/librustc_mir/transform/no_landing_pads.rs | 1 - src/librustc_mir/transform/qualify_consts.rs | 1 - src/librustc_mir/transform/simplify.rs | 1 - .../transform/simplify_branches.rs | 22 +++++------ src/librustc_mir/transform/type_check.rs | 16 +------- src/librustc_passes/mir_stats.rs | 1 - src/librustc_trans/mir/analyze.rs | 1 - src/librustc_trans/mir/block.rs | 12 +----- src/test/ui/custom-derive/issue-36935.rs | 1 + 18 files changed, 76 insertions(+), 101 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 64642249f965d..80ef6fdd95214 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -453,12 +453,6 @@ 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>, @@ -470,7 +464,7 @@ pub enum TerminatorKind<'tcx> { /// 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>, @@ -550,7 +544,6 @@ impl<'tcx> TerminatorKind<'tcx> { 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(), @@ -580,7 +573,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(), @@ -659,7 +651,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"), @@ -710,7 +701,6 @@ 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() diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 921b4e78b32c9..1e27a02287fd1 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; @@ -363,14 +362,6 @@ 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 } => { @@ -384,7 +375,7 @@ macro_rules! make_mir_visitor { ref $($mutability)* switch_ty, ref $($mutability)* 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); diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index f11cf90834dd9..8dd591fa2e778 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -454,10 +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 { diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index d2f744bde2d63..45f534767e4e7 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -813,9 +813,12 @@ 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 boolty = self.tcx.types.bool; + self.new_block(c, is_cleanup, TerminatorKind::SwitchInt { + discr: Operand::Consume(flag), + switch_ty: boolty, + values: vec![ConstVal::Bool(true)], + targets: vec![on_set, on_unset], }) } } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 806395c857a88..9e7e5ec9ee861 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -464,7 +464,6 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { self.gather_move(loc, &Lvalue::Local(RETURN_POINTER)); } - TerminatorKind::If { .. } | TerminatorKind::Assert { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Switch { .. } => { diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 3d4af259ec9f7..2b4336ba66f07 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -15,6 +15,7 @@ use build::expr::category::{Category, RvalueFunc}; use hair::*; use rustc::ty; use rustc::mir::*; +use rustc::middle::const_val::ConstVal; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr`, storing the result into `destination`, which @@ -69,9 +70,11 @@ 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) + this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { + discr: operand, + switch_ty: this.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: vec![then_block, else_block], }); unpack!(then_block = this.into(destination, then_block, then_expr)); @@ -111,16 +114,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let lhs = unpack!(block = this.as_operand(block, lhs)); let blocks = match op { - LogicalOp::And => (else_block, false_block), - LogicalOp::Or => (true_block, else_block), + LogicalOp::And => vec![else_block, false_block], + LogicalOp::Or => vec![true_block, else_block], }; - this.cfg.terminate(block, source_info, - TerminatorKind::If { cond: lhs, targets: blocks }); + this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { + discr: lhs, + switch_ty: this.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: blocks, + }); 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) + this.cfg.terminate(else_block, source_info, TerminatorKind::SwitchInt { + discr: rhs, + switch_ty: this.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: vec![true_block, false_block], }); this.cfg.push_assign_constant( @@ -180,9 +189,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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) + TerminatorKind::SwitchInt { + discr: cond, + switch_ty: this.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: vec![body_block, exit_block], }); // if the test is false, there's no `break` to assign `destination`, so diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 5c3d93ffda9c1..0898d06d2e470 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -672,9 +672,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let source_info = self.source_info(guard.span); 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)}); + self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { + discr: cond, + switch_ty: self.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: vec![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..291bd65d57775 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -221,10 +221,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { v => span_bug!(test.span, "expected boolean value but got {:?}", v) }; - (targets, - TerminatorKind::If { - cond: Operand::Consume(lvalue.clone()), - targets: (true_bb, else_bb) + (targets, TerminatorKind::SwitchInt { + discr: Operand::Consume(lvalue.clone()), + switch_ty: self.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: vec![true_bb, else_bb] }) } @@ -240,7 +241,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { (targets.clone(), TerminatorKind::SwitchInt { - discr: lvalue.clone(), + discr: Operand::Consume(lvalue.clone()), switch_ty: switch_ty, values: options.clone(), targets: targets @@ -314,9 +315,11 @@ 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::SwitchInt { + discr: Operand::Consume(eq_result), + switch_ty: self.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: vec![block, fail], }); vec![block, fail] @@ -362,9 +365,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // 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]) + self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { + discr: Operand::Consume(result), + switch_ty: self.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: target_blocks.clone(), }); target_blocks @@ -389,9 +394,11 @@ 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::SwitchInt { + discr: Operand::Consume(result), + switch_ty: self.hir.bool_ty(), + values: vec![ConstVal::Bool(true)], + targets: vec![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/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index 6ef5720b330c9..425df65659c6f 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -28,7 +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 16371be576b05..bda4c94625f8b 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -394,7 +394,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { return Qualif::empty(); } - TerminatorKind::If {..} | TerminatorKind::Switch {..} | TerminatorKind::SwitchInt {..} | TerminatorKind::DropAndReplace { .. } | diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index d5fc90289e2cc..1127f50fe508c 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -209,7 +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..424250586b162 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -30,17 +30,17 @@ 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 } - } else { - TerminatorKind::Goto { target: targets.1 } - } - } + // TerminatorKind::If { ref targets, cond: Operand::Constant(Constant { + // literal: Literal::Value { + // value: ConstVal::Bool(cond) + // }, .. + // }) } => { + // if cond { + // TerminatorKind::Goto { target: targets.0 } + // } else { + // TerminatorKind::Goto { target: targets.1 } + // } + // } TerminatorKind::Assert { target, cond: Operand::Constant(Constant { literal: Literal::Value { diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 529fe564af02b..9d2ad31438658 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); @@ -603,10 +593,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 { diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index e29febdb712d3..fef61128d04e5 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -148,7 +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", diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 2a1ab10d74e16..97118a7206299 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -204,7 +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..773e2bc871026 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -136,14 +136,6 @@ 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()); @@ -178,9 +170,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { + // TODO: cond_br if only 1 value 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 discr = self.trans_operand(&bcx, discr).immediate(); 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); 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)] From 6849985babf731d82e1f41790828b8b86e016b48 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Wed, 1 Feb 2017 03:38:28 +0200 Subject: [PATCH 03/25] Move type of discriminant to AdtDef Previously AdtDef variants contained ConstInt for each discriminant, which did not really reflect the actual type of the discriminants. Moving the type into AdtDef allows to easily put the type into metadata and also saves bytes from ConstVal overhead for each discriminant. Also arguably the code is cleaner now :) --- src/librustc/ty/context.rs | 4 +- src/librustc/ty/layout.rs | 13 +-- src/librustc/ty/mod.rs | 8 +- src/librustc/ty/util.rs | 61 +++---------- src/librustc_metadata/decoder.rs | 22 +++-- src/librustc_metadata/encoder.rs | 10 +-- src/librustc_metadata/schema.rs | 2 +- src/librustc_trans/debuginfo/metadata.rs | 2 +- src/librustc_trans/disr.rs | 2 +- src/librustc_typeck/collect.rs | 107 +++++++++++------------ 10 files changed, 99 insertions(+), 132 deletions(-) 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..4697726841589 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,10 +1206,12 @@ 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 = match def.discr_ty { + attr::IntType::SignedInt(IntTy::I128) | + attr::IntType::UnsignedInt(UintTy::U128) => + bug!("128-bit discriminants not yet supported"), + attr::IntType::SignedInt(_) => v.disr_val as i64, + attr::IntType::UnsignedInt(_) => v.disr_val as u64 as i64, }; if x == 0 { non_zero = false; } if x < min { min = x; } @@ -1271,7 +1272,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 c9ae3b3df028c..faadae4fec205 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; @@ -72,6 +71,8 @@ pub use self::context::{Lift, TypeckTables}; pub use self::trait_def::{TraitDef, TraitFlags}; +use rustc_i128::u128; + pub mod adjustment; pub mod cast; pub mod error; @@ -96,7 +97,7 @@ mod flags; mod structural_impls; mod sty; -pub type Disr = ConstInt; +pub type Disr = u128; // Data types @@ -1325,6 +1326,7 @@ pub struct FieldDef { /// table. pub struct AdtDef { pub did: DefId, + pub discr_ty: attr::IntType, // Type of the discriminant pub variants: Vec, destructor: Cell>, flags: Cell @@ -1360,6 +1362,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 +1385,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..1df87da42274c 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -23,7 +23,7 @@ use ty::TypeVariants::*; use util::nodemap::FxHashMap; use middle::lang_items; -use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; +use rustc_const_math::ConstInt; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult}; use std::cell::RefCell; @@ -34,14 +34,15 @@ use syntax::ast::{self, Name}; use syntax::attr::{self, SignedInt, UnsignedInt}; use syntax_pos::Span; +use rustc_i128::i128; + 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 initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr; } impl IntTypeExt for attr::IntType { @@ -62,56 +63,18 @@ impl IntTypeExt for attr::IntType { } } - 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 initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr { + 0 } + /// None = overflow fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option { + -> Option { if let Some(val) = val { - self.assert_ty_matches(val); - (val + ConstInt::Infer(1)).ok() + match *self { + SignedInt(it) => ConstInt::new_signed(val as i128, it, tcx.sess.target.int_type), + UnsignedInt(it) => ConstInt::new_unsigned(val, it, tcx.sess.target.uint_type), + }.and_then(|l| (l + ConstInt::Infer(1)).ok()).map(|v| v.to_u128_unchecked()) } else { Some(self.initial_discriminant(tcx)) } 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_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index c83e2f4854bf5..1adfebe712363 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_typeck/collect.rs b/src/librustc_typeck/collect.rs index 266975994ec3a..a0c852ad531ec 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,63 @@ 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 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.to_u128_unchecked()), + (_, 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, @@ -1092,7 +1091,7 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let initial = repr_type.initial_discriminant(tcx); 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 wrapped_disr = prev_disr.map_or(initial, |d| d.wrapping_add(1)); let disr = if let Some(e) = v.node.disr_expr { evaluate_disr_expr(ccx, repr_type, e) } else if let Some(disr) = repr_type.disr_incr(tcx, prev_disr) { @@ -1112,7 +1111,7 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, convert_struct_variant(ccx, did, v.node.name, disr, &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 } From a8075a440c4154afd278bae095bd45173357332b Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Wed, 1 Feb 2017 05:10:35 +0200 Subject: [PATCH 04/25] AdtDef now contains discr_ty same as layouted --- src/Cargo.lock | 1 + src/librustc/ty/layout.rs | 40 ++++++++++++++++++++++------------ src/librustc_typeck/Cargo.toml | 1 + src/librustc_typeck/collect.rs | 12 +++++++--- src/librustc_typeck/lib.rs | 2 ++ 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index 06cf32ad0f6b5..1189bc40faca4 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -529,6 +529,7 @@ dependencies = [ "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", + "rustc_i128 0.0.0", "rustc_platform_intrinsics 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 4697726841589..8ff0be4856f4f 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -382,6 +382,23 @@ impl Integer { } } + pub fn to_attr(&self, signed: bool) -> attr::IntType { + match (*self, signed) { + (I1, false) => attr::IntType::UnsignedInt(UintTy::U8), + (I8, false) => attr::IntType::UnsignedInt(UintTy::U8), + (I16, false) => attr::IntType::UnsignedInt(UintTy::U16), + (I32, false) => attr::IntType::UnsignedInt(UintTy::U32), + (I64, false) => attr::IntType::UnsignedInt(UintTy::U64), + (I128, false) => attr::IntType::UnsignedInt(UintTy::U128), + (I1, true) => attr::IntType::SignedInt(IntTy::I8), + (I8, true) => attr::IntType::SignedInt(IntTy::I8), + (I16, true) => attr::IntType::SignedInt(IntTy::I16), + (I32, true) => attr::IntType::SignedInt(IntTy::I32), + (I64, true) => attr::IntType::SignedInt(IntTy::I64), + (I128, true) => attr::IntType::SignedInt(IntTy::I128), + } + } + /// Find the smallest Integer type which can represent the signed value. pub fn fit_signed(x: i64) -> Integer { match x { @@ -436,13 +453,13 @@ impl Integer { /// signed discriminant range and #[repr] attribute. /// N.B.: u64 values above i64::MAX will be treated as signed, but /// that shouldn't affect anything, other than maybe debuginfo. - fn repr_discr(tcx: TyCtxt, ty: Ty, hints: &[attr::ReprAttr], min: i64, max: i64) - -> (Integer, bool) { + pub fn repr_discr(tcx: TyCtxt, hints: &[attr::ReprAttr], min: i128, max: i128) + -> (Integer, bool) { // Theoretically, negative values could be larger in unsigned representation // than the unsigned representation of the signed minimum. However, if there // are any negative values, the only valid unsigned representation is u64 // which can fit all i64 values, so the result remains unaffected. - let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u64, max as u64)); + let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); let mut min_from_extern = None; @@ -455,7 +472,7 @@ impl Integer { let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; if discr < fit { bug!("Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum `{}", ty) + discriminant range of enum") } return (discr, ity.is_signed()); } @@ -471,16 +488,15 @@ impl Integer { } attr::ReprAny => {}, attr::ReprPacked => { - bug!("Integer::repr_discr: found #[repr(packed)] on enum `{}", ty); + bug!("Integer::repr_discr: found #[repr(packed)] on enum"); } attr::ReprSimd => { - bug!("Integer::repr_discr: found #[repr(simd)] on enum `{}", ty); + bug!("Integer::repr_discr: found #[repr(simd)] on enum"); } } } let at_least = min_from_extern.unwrap_or(min_default); - // If there are no negative values, we can use the unsigned fit. if min >= 0 { (cmp::max(unsigned_fit, at_least), false) @@ -1220,9 +1236,8 @@ impl<'a, 'gcx, 'tcx> Layout { // FIXME: should handle i128? signed-value based impl is weird and hard to // grok. - let (discr, signed) = Integer::repr_discr(tcx, ty, &hints[..], - min, - max); + let discr = Integer::from_attr(&tcx.data_layout, def.discr_ty); + let signed = def.discr_ty.is_signed(); return success(CEnum { discr: discr, signed: signed, @@ -1337,10 +1352,7 @@ impl<'a, 'gcx, 'tcx> Layout { } // The general case. - let discr_max = (variants.len() - 1) as i64; - assert!(discr_max >= 0); - let (min_ity, _) = Integer::repr_discr(tcx, ty, &hints[..], 0, discr_max); - + let min_ity = Integer::from_attr(&tcx.data_layout, def.discr_ty); let mut align = dl.aggregate_align; let mut size = Size::from_bytes(0); diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index f08d26373e50e..067893552427b 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -22,3 +22,4 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } syntax_pos = { path = "../libsyntax_pos" } rustc_errors = { path = "../librustc_errors" } +rustc_i128 = { path = "../librustc_i128" } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index a0c852ad531ec..a8062ed845ff8 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -66,7 +66,7 @@ use rustc_const_eval::EvalHint::UncheckedExprHint; use rustc_const_eval::{ConstContext, report_const_eval_err}; use rustc::ty::subst::Substs; use rustc::ty::{ToPredicate, ImplContainer, AssociatedItemContainer, TraitContainer}; -use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; +use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt, layout}; use rustc::ty::util::IntTypeExt; use rustc::dep_graph::DepNode; use util::common::{ErrorReported, MemoizationMap}; @@ -86,6 +86,8 @@ use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; +use rustc_i128::i128; + /////////////////////////////////////////////////////////////////////////// // Main entry point @@ -1090,9 +1092,11 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let repr_type = tcx.enum_repr_type(repr_hints.get(0)); let initial = repr_type.initial_discriminant(tcx); let mut prev_disr = None::; + let (mut min, mut max) = (i128::max_value(), i128::min_value()); let variants = def.variants.iter().map(|v| { let wrapped_disr = prev_disr.map_or(initial, |d| d.wrapping_add(1)); 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) { Some(disr) @@ -1106,12 +1110,14 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, None }.unwrap_or(wrapped_disr); prev_disr = Some(disr); - + if (disr as i128) < min { min = disr as i128; } + if (disr as i128) > max { max = disr as i128; } let did = tcx.hir.local_def_id(v.node.data.id()); convert_struct_variant(ccx, did, v.node.name, disr, &v.node.data) }).collect(); - let adt = tcx.alloc_adt_def(did, AdtKind::Enum, Some(repr_type), variants); + let (repr_int, signed) = layout::Integer::repr_discr(tcx, &repr_hints[..], min, max); + let adt = tcx.alloc_adt_def(did, AdtKind::Enum, Some(repr_int.to_attr(signed)), variants); tcx.adt_defs.borrow_mut().insert(did, adt); adt } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index f19a59a5d38ae..aa2695b9553e7 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -98,6 +98,8 @@ extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors as errors; +extern crate rustc_i128; + pub use rustc::dep_graph; pub use rustc::hir; pub use rustc::lint; From 1355a575447e36b7a5c512a622066a2412552f7b Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 2 Feb 2017 02:05:56 +0200 Subject: [PATCH 05/25] SwitchInt over Switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes another special case of Switch by replacing it with the more general SwitchInt. While this is more clunky currently, there’s no reason we can’t make it nice (and efficient) to use. --- src/librustc/middle/const_val.rs | 1 + src/librustc/mir/mod.rs | 17 +----- src/librustc/mir/tcx.rs | 12 +++-- src/librustc/mir/visit.rs | 9 ---- src/librustc/ty/util.rs | 20 ++----- .../borrowck/mir/dataflow/mod.rs | 1 - .../borrowck/mir/elaborate_drops.rs | 49 +++++++++++------ .../borrowck/mir/gather_moves.rs | 3 +- src/librustc_const_math/int.rs | 8 +++ src/librustc_data_structures/bitvec.rs | 4 ++ src/librustc_mir/build/matches/test.rs | 52 ++++++++++++++----- src/librustc_mir/lib.rs | 2 + src/librustc_mir/transform/no_landing_pads.rs | 1 - src/librustc_mir/transform/qualify_consts.rs | 1 - src/librustc_mir/transform/simplify.rs | 1 - src/librustc_mir/transform/type_check.rs | 14 ----- src/librustc_passes/mir_stats.rs | 1 - src/librustc_trans/mir/analyze.rs | 1 - src/librustc_trans/mir/block.rs | 36 ------------- 19 files changed, 105 insertions(+), 128 deletions(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index f583f601726ed..c4e3827fef289 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; diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 80ef6fdd95214..e9769efb36beb 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -453,13 +453,6 @@ pub enum TerminatorKind<'tcx> { target: 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 { @@ -471,6 +464,7 @@ pub enum TerminatorKind<'tcx> { /// Possible values. The locations to branch to in each case /// are found in the corresponding indices from the `targets` vector. + // FIXME: ConstVal doesn’t quite make any sense here? Its a Switch*Int*. values: Vec, /// Possible branch sites. The length of this vector should be @@ -544,7 +538,6 @@ impl<'tcx> TerminatorKind<'tcx> { use self::TerminatorKind::*; match *self { Goto { target: ref b } => slice::ref_slice(b).into_cow(), - Switch { targets: ref b, .. } => b[..].into_cow(), SwitchInt { targets: ref b, .. } => b[..].into_cow(), Resume => (&[]).into_cow(), Return => (&[]).into_cow(), @@ -573,7 +566,6 @@ impl<'tcx> TerminatorKind<'tcx> { use self::TerminatorKind::*; match *self { Goto { target: ref mut b } => vec![b], - Switch { targets: ref mut b, .. } => b.iter_mut().collect(), SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(), Resume => Vec::new(), Return => Vec::new(), @@ -651,7 +643,6 @@ impl<'tcx> TerminatorKind<'tcx> { use self::TerminatorKind::*; match *self { Goto { .. } => write!(fmt, "goto"), - Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv), SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), Return => write!(fmt, "return"), Resume => write!(fmt, "resume"), @@ -701,12 +692,6 @@ impl<'tcx> TerminatorKind<'tcx> { match *self { Return | Resume | Unreachable => vec![], Goto { .. } => vec!["".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| { diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 68fbadd5d6065..fcfd1c5767216 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -17,6 +17,7 @@ use mir::*; use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use syntax::attr; use hir; #[derive(Copy, Clone, Debug)] @@ -170,9 +171,14 @@ impl<'tcx> Rvalue<'tcx> { Some(operand.ty(mir, tcx)) } Rvalue::Discriminant(ref lval) => { - if let ty::TyAdt(_, _) = lval.ty(mir, tcx).to_ty(tcx).sty { - // TODO - None + if let ty::TyAdt(adt_def, _) = lval.ty(mir, tcx).to_ty(tcx).sty { + // FIXME: Why this does not work? + // Some(adt_def.discr_ty.to_ty(tcx)) + let ty = match adt_def.discr_ty { + attr::SignedInt(i) => tcx.mk_mach_int(i), + attr::UnsignedInt(i) => tcx.mk_mach_uint(i), + }; + Some(ty) } else { None } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 1e27a02287fd1..ca20cf6236b58 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -362,15 +362,6 @@ macro_rules! make_mir_visitor { 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, diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 1df87da42274c..fe4b6dad30e04 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -39,27 +39,17 @@ use rustc_i128::i128; use hir; pub trait IntTypeExt { - fn to_ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx>; + 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 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, 'tcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { + match self { + SignedInt(i) => tcx.mk_mach_int(i), + UnsignedInt(i) => tcx.mk_mach_uint(i), } } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 8dd591fa2e778..8b246105f6169 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -454,7 +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::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 45f534767e4e7..8f3dcd0b8d673 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; @@ -672,12 +673,15 @@ 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() + 1); + for (idx, 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(); + values.push(ConstVal::Integral(discr)); + blocks.push(self.open_drop_for_variant(c, &mut drop_block, adt, substs, idx)); + } // 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 +689,29 @@ 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: values, + targets: blocks, + // adt_def: adt, + // targets: variant_drops + } + }), + is_cleanup: c.is_cleanup, + }); self.drop_flag_test_block(c, switch_block) } } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 9e7e5ec9ee861..0c7e922c48ab4 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -465,8 +465,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } 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_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 291bd65d57775..528e37e73c4f3 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -20,11 +20,12 @@ 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::mir::*; use rustc::hir::RangeEnd; use syntax_pos::Span; +use syntax::attr; use std::cmp::Ordering; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -182,24 +183,51 @@ 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(ConstVal::Integral(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); + // FIXME: WHY THIS DOES NOT WORK?! + // let discr_ty = adt_def.discr_ty.to_ty(tcx); + let discr_ty = match adt_def.discr_ty { + attr::SignedInt(i) => tcx.mk_mach_int(i), + attr::UnsignedInt(i) => tcx.mk_mach_uint(i), + }; + + 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: values, + targets: targets }); target_blocks } 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 425df65659c6f..55a26f4b37fe2 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -28,7 +28,6 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::Unreachable | - 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 bda4c94625f8b..922521726c626 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -394,7 +394,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { return Qualif::empty(); } - TerminatorKind::Switch {..} | TerminatorKind::SwitchInt {..} | TerminatorKind::DropAndReplace { .. } | TerminatorKind::Resume | diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 1127f50fe508c..e93a412dc744f 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -209,7 +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::Switch { .. } | TerminatorKind::SwitchInt { .. } => {}, _ => return false }; diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 9d2ad31438658..8ede7aaab5f68 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -436,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); @@ -593,7 +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::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 fef61128d04e5..517a472056334 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -148,7 +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::Switch { .. } => "TerminatorKind::Switch", TerminatorKind::SwitchInt { .. } => "TerminatorKind::SwitchInt", TerminatorKind::Resume => "TerminatorKind::Resume", TerminatorKind::Return => "TerminatorKind::Return", diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 97118a7206299..37725bfa2de84 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -204,7 +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 773e2bc871026..a56c10b565652 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -14,14 +14,12 @@ use rustc::middle::lang_items; 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 +27,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,39 +133,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { funclet_br(self, bcx, target); } - 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)) - } - // We're generating an exhaustive switch, so the else branch - // can't be hit. Branching to an unreachable instruction - // lets LLVM know this - _ => (None, self.unreachable_block()) - }; - let switch = bcx.switch(discr, default_blk, targets.len()); - assert_eq!(adt_def.variants.len(), targets.len()); - for (adt_variant, &target) in adt_def.variants.iter().zip(targets) { - if default_bb != Some(target) { - let llbb = llblock(self, target); - let llval = adt::trans_case(&bcx, ty, Disr::from(adt_variant.disr_val)); - bcx.add_case(switch, llval, llbb) - } - } - } - mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { // TODO: cond_br if only 1 value let (otherwise, targets) = targets.split_last().unwrap(); From 94e587ef296492acf767cf2d39a22d3138f00a25 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 2 Feb 2017 06:44:30 +0200 Subject: [PATCH 06/25] Only SwitchInt over integers, not all consts Also use a Cow to avoid full Vec for all SwitchInts --- src/librustc/middle/const_val.rs | 13 +++ src/librustc/mir/mod.rs | 8 +- src/librustc/mir/visit.rs | 19 +++- .../borrowck/mir/elaborate_drops.rs | 6 +- src/librustc_mir/build/expr/into.rs | 9 +- src/librustc_mir/build/matches/mod.rs | 2 +- src/librustc_mir/build/matches/test.rs | 95 ++++++++----------- src/librustc_trans/mir/block.rs | 27 ++++-- src/librustc_trans/mir/constant.rs | 48 ++++++---- src/libserialize/serialize.rs | 28 ++++++ 10 files changed, 154 insertions(+), 101 deletions(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index c4e3827fef289..f885a6d95693f 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -11,6 +11,7 @@ use syntax::symbol::InternedString; use syntax::ast; use std::rc::Rc; +use std::borrow::Cow; use hir::def_id::DefId; use rustc_const_math::*; use self::ConstVal::*; @@ -18,6 +19,8 @@ pub use rustc_const_math::ConstInt; use std::collections::BTreeMap; +pub static BOOL_SWITCH_TRUE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(1)]); + #[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)] pub enum ConstVal { Float(ConstFloat), @@ -49,4 +52,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 e9769efb36beb..9957f8c5e9e28 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -446,6 +446,9 @@ pub struct Terminator<'tcx> { pub kind: TerminatorKind<'tcx> } +/// For use in SwitchInt, for switching on bools. +pub static BOOL_SWITCH_TRUE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(1)]); + #[derive(Clone, RustcEncodable, RustcDecodable)] pub enum TerminatorKind<'tcx> { /// block should have one successor in the graph; we jump there @@ -464,8 +467,7 @@ pub enum TerminatorKind<'tcx> { /// Possible values. The locations to branch to in each case /// are found in the corresponding indices from the `targets` vector. - // FIXME: ConstVal doesn’t quite make any sense here? Its a Switch*Int*. - values: Vec, + values: Cow<'tcx, [ConstInt]>, /// Possible branch sites. The length of this vector should be /// equal to the length of the `values` vector plus 1 -- the @@ -696,7 +698,7 @@ impl<'tcx> TerminatorKind<'tcx> { 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())) diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index ca20cf6236b58..be3c43db7badd 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -223,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) { @@ -364,12 +370,12 @@ macro_rules! make_mir_visitor { TerminatorKind::SwitchInt { ref $($mutability)* discr, ref $($mutability)* switch_ty, - ref $($mutability)* values, + ref values, ref targets } => { 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); @@ -698,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_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 8f3dcd0b8d673..53e84f1fb7117 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -679,7 +679,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let discr = ConstInt::new_inttype(variant.disr_val, adt.discr_ty, self.tcx.sess.target.uint_type, self.tcx.sess.target.int_type).unwrap(); - values.push(ConstVal::Integral(discr)); + values.push(discr); blocks.push(self.open_drop_for_variant(c, &mut drop_block, adt, substs, idx)); } // If there are multiple variants, then if something @@ -704,7 +704,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { kind: TerminatorKind::SwitchInt { discr: Operand::Consume(discr), switch_ty: discr_ty, - values: values, + values: From::from(values), targets: blocks, // adt_def: adt, // targets: variant_drops @@ -836,7 +836,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { self.new_block(c, is_cleanup, TerminatorKind::SwitchInt { discr: Operand::Consume(flag), switch_ty: boolty, - values: vec![ConstVal::Bool(true)], + values: BOOL_SWITCH_TRUE.clone(), targets: vec![on_set, on_unset], }) } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 2b4336ba66f07..537daa4a15ba4 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -15,7 +15,6 @@ use build::expr::category::{Category, RvalueFunc}; use hair::*; use rustc::ty; use rustc::mir::*; -use rustc::middle::const_val::ConstVal; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr`, storing the result into `destination`, which @@ -73,7 +72,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: operand, switch_ty: this.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], + values: BOOL_SWITCH_TRUE.clone(), targets: vec![then_block, else_block], }); @@ -120,7 +119,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: lhs, switch_ty: this.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], + values: BOOL_SWITCH_TRUE.clone(), targets: blocks, }); @@ -128,7 +127,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.terminate(else_block, source_info, TerminatorKind::SwitchInt { discr: rhs, switch_ty: this.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], + values: BOOL_SWITCH_TRUE.clone(), targets: vec![true_block, false_block], }); @@ -192,7 +191,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { TerminatorKind::SwitchInt { discr: cond, switch_ty: this.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], + values: BOOL_SWITCH_TRUE.clone(), targets: vec![body_block, exit_block], }); diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 0898d06d2e470..885965da1f924 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -675,7 +675,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: cond, switch_ty: self.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], + values: BOOL_SWITCH_TRUE.clone(), targets: vec![arm_block, otherwise], }); Some(otherwise) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 528e37e73c4f3..f2d48f65fef2f 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -196,7 +196,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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(ConstVal::Integral(discr)); + values.push(discr); *(targets.place_back() <- self.cfg.start_new_block()) } else { if otherwise_block.is_none() { @@ -226,59 +226,45 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: Operand::Consume(discr), switch_ty: discr_ty, - values: values, + 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::SwitchInt { - discr: Operand::Consume(lvalue.clone()), - switch_ty: self.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], - targets: vec![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: Operand::Consume(lvalue.clone()), - switch_ty: switch_ty, - values: options.clone(), - targets: targets - }) - } + let (values, targets, ret) = 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) + }; + (BOOL_SWITCH_TRUE.clone(), vec![true_bb, false_bb], ret) + } 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(); + (From::from(values), targets.clone(), targets) }; - self.cfg.terminate(block, source_info, term); - targets + self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { + discr: Operand::Consume(lvalue.clone()), + switch_ty: switch_ty, + values: values, + targets: targets.clone(), + }); + ret } TestKind::Eq { ref value, mut ty } => { @@ -346,10 +332,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.terminate(eq_block, source_info, TerminatorKind::SwitchInt { discr: Operand::Consume(eq_result), switch_ty: self.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], + values: BOOL_SWITCH_TRUE.clone(), targets: vec![block, fail], }); - vec![block, fail] } else { let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val); @@ -391,16 +376,15 @@ 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()]; + let (false_bb, true_bb) = (self.cfg.start_new_block(), + self.cfg.start_new_block()); self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: Operand::Consume(result), switch_ty: self.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], - targets: target_blocks.clone(), + values: BOOL_SWITCH_TRUE.clone(), + targets: vec![true_bb, false_bb], }); - - target_blocks + vec![true_bb, false_bb] } } } @@ -425,10 +409,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: Operand::Consume(result), switch_ty: self.hir.bool_ty(), - values: vec![ConstVal::Bool(true)], + values: BOOL_SWITCH_TRUE.clone(), targets: vec![target_block, fail_block] }); - target_block } diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index a56c10b565652..6b3081f610060 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -11,6 +11,7 @@ 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}; @@ -134,14 +135,24 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { - // TODO: cond_br if only 1 value - let (otherwise, targets) = targets.split_last().unwrap(); - let discr = self.trans_operand(&bcx, discr).immediate(); - let switch = bcx.switch(discr, llblock(self, *otherwise), values.len()); - for (value, target) in values.iter().zip(targets) { - let val = Const::from_constval(bcx.ccx, value.clone(), switch_ty); - let llbb = llblock(self, *target); - bcx.add_case(switch, val.llval, llbb) + let discr = self.trans_operand(&bcx, discr); + if switch_ty == bcx.tcx().types.bool { + let lltrue = llblock(self, targets[0]); + let llfalse = llblock(self, targets[1]); + if let [ConstInt::Infer(0)] = values[..] { + bcx.cond_br(discr.immediate(), llfalse, lltrue); + } else { + bcx.cond_br(discr.immediate(), lltrue, llfalse); + } + } else { + let (otherwise, targets) = targets.split_last().unwrap(); + let switch = bcx.switch(discr.immediate(), + llblock(self, *otherwise), values.len()); + for (value, target) in values.iter().zip(targets) { + let val = Const::from_constint(bcx.ccx, value); + let llbb = llblock(self, *target); + bcx.add_case(switch, val.llval, llbb) + } } } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 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/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| { From 339358416e8458b96f158d47e7686a979b47d46e Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 2 Feb 2017 07:25:29 +0200 Subject: [PATCH 07/25] Fix build on further stages --- src/librustc/ty/layout.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 8ff0be4856f4f..a18152e75e938 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -400,7 +400,7 @@ impl Integer { } /// Find the smallest Integer type which can represent the signed value. - pub fn fit_signed(x: i64) -> Integer { + pub fn fit_signed(x: i128) -> Integer { match x { -0x0000_0000_0000_0001...0x0000_0000_0000_0000 => I1, -0x0000_0000_0000_0080...0x0000_0000_0000_007f => I8, @@ -412,7 +412,7 @@ impl Integer { } /// Find the smallest Integer type which can represent the unsigned value. - pub fn fit_unsigned(x: u64) -> Integer { + pub fn fit_unsigned(x: u128) -> Integer { match x { 0...0x0000_0000_0000_0001 => I1, 0...0x0000_0000_0000_00ff => I8, From 76bc6c31aa3c73669b8664094059fb829b47f789 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 2 Feb 2017 08:41:01 +0200 Subject: [PATCH 08/25] Reimplement simplify_cfg for SwitchInt First example of optimisation that applies to many more cases than originally. --- .../transform/simplify_branches.rs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index 424250586b162..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 } - // } else { - // TerminatorKind::Goto { target: targets.1 } - // } - // } - + 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 { + continue + } + }, TerminatorKind::Assert { target, cond: Operand::Constant(Constant { literal: Literal::Value { value: ConstVal::Bool(cond) }, .. }), expected, .. } if cond == expected => { TerminatorKind::Goto { target: target } - } - + }, _ => continue }; } From cd4147942dd2da5dbe3c962428b06d525f5fab4f Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 2 Feb 2017 19:53:44 +0200 Subject: [PATCH 09/25] Fix the IntTypeExt::to_ty() lifetime bounds --- src/librustc/mir/tcx.rs | 10 ++-------- src/librustc/ty/layout.rs | 2 +- src/librustc/ty/util.rs | 4 ++-- src/librustc_borrowck/borrowck/mir/elaborate_drops.rs | 2 -- src/librustc_mir/build/matches/test.rs | 10 ++-------- src/librustc_trans/mir/rvalue.rs | 1 - src/librustc_typeck/collect.rs | 3 ++- 7 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index fcfd1c5767216..6863468ec0d5b 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -17,8 +17,8 @@ use mir::*; use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use syntax::attr; use hir; +use ty::util::IntTypeExt; #[derive(Copy, Clone, Debug)] pub enum LvalueTy<'tcx> { @@ -172,13 +172,7 @@ impl<'tcx> Rvalue<'tcx> { } Rvalue::Discriminant(ref lval) => { if let ty::TyAdt(adt_def, _) = lval.ty(mir, tcx).to_ty(tcx).sty { - // FIXME: Why this does not work? - // Some(adt_def.discr_ty.to_ty(tcx)) - let ty = match adt_def.discr_ty { - attr::SignedInt(i) => tcx.mk_mach_int(i), - attr::UnsignedInt(i) => tcx.mk_mach_uint(i), - }; - Some(ty) + Some(adt_def.discr_ty.to_ty(tcx)) } else { None } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index a18152e75e938..6e25ce200720b 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -454,7 +454,7 @@ impl Integer { /// N.B.: u64 values above i64::MAX will be treated as signed, but /// that shouldn't affect anything, other than maybe debuginfo. pub fn repr_discr(tcx: TyCtxt, hints: &[attr::ReprAttr], min: i128, max: i128) - -> (Integer, bool) { + -> (Integer, bool) { // Theoretically, negative values could be larger in unsigned representation // than the unsigned representation of the signed minimum. However, if there // are any negative values, the only valid unsigned representation is u64 diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index fe4b6dad30e04..0281e53427d6d 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -39,14 +39,14 @@ use rustc_i128::i128; use hir; pub trait IntTypeExt { - fn to_ty<'a, 'tcx>(self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx>; + fn to_ty<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>; fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) -> Option; fn initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr; } impl IntTypeExt for attr::IntType { - fn to_ty<'a, 'gcx, 'tcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { + 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), diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 53e84f1fb7117..7521b750d5a09 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -706,8 +706,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { switch_ty: discr_ty, values: From::from(values), targets: blocks, - // adt_def: adt, - // targets: variant_drops } }), is_cleanup: c.is_cleanup, diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index f2d48f65fef2f..d9c2e6bb09071 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -22,10 +22,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::bitvec::BitVector; 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; -use syntax::attr; use std::cmp::Ordering; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -212,13 +212,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } debug!("num_enum_variants: {}, tested variants: {:?}, variants: {:?}", num_enum_variants, values, variants); - // FIXME: WHY THIS DOES NOT WORK?! - // let discr_ty = adt_def.discr_ty.to_ty(tcx); - let discr_ty = match adt_def.discr_ty { - attr::SignedInt(i) => tcx.mk_mach_int(i), - attr::UnsignedInt(i) => tcx.mk_mach_uint(i), - }; - + 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())); diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 74df3428b99e3..259f02a044c12 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -434,7 +434,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { 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); - // FIXME: inline this let discr = adt::trans_get_discr(&bcx, enum_ty, discr_lvalue.llval, None, true); let discr = if common::val_ty(discr) == Type::i1(bcx.ccx) { bcx.zext(discr, discr_type) diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index a8062ed845ff8..d1041f89b723d 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1045,7 +1045,8 @@ fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId 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 + // 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(_)) | From ff167a6a2ac34ca174c48cf3c249894991d1e065 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 2 Feb 2017 20:35:54 +0200 Subject: [PATCH 10/25] Fix SwitchInt building in ElaborateDrops pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously it used to build a switch in a way that didn’t preserve the invariat of SwitchInt. Now it builds it in an optimal way too, where otherwise branch becomes all the branches which did not have partial variant drops. --- src/librustc/mir/mod.rs | 15 ++++++++---- src/librustc/mir/tcx.rs | 7 ++++-- .../borrowck/mir/elaborate_drops.rs | 24 ++++++++++++++----- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 9957f8c5e9e28..bcd34eaf54e8c 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -469,10 +469,17 @@ pub enum TerminatorKind<'tcx> { /// are found in the corresponding indices from the `targets` vector. values: Cow<'tcx, [ConstInt]>, - /// Possible branch sites. The length of this vector should be - /// equal to the length of the `values` vector plus 1 -- the - /// extra item is the block to branch to if none of the values - /// fit. + /// Possible branch sites. The last element of this vector is used + /// for the otherwise branch, so values.len() == targets.len() + 1 + /// should hold. + // This invariant is quite non-obvious and also could be improved. + // One way to make this invariant is to have something like this instead: + // + // branches: Vec<(ConstInt, BasicBlock)>, + // otherwise: Option // exhaustive if None + // + // However we’ve decided to keep this as-is until we figure a case + // where some other approach seems to be strictly better than other. targets: Vec, }, diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 6863468ec0d5b..7b0863b4c42bc 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -171,10 +171,13 @@ impl<'tcx> Rvalue<'tcx> { Some(operand.ty(mir, tcx)) } Rvalue::Discriminant(ref lval) => { - if let ty::TyAdt(adt_def, _) = lval.ty(mir, tcx).to_ty(tcx).sty { + 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 { - None + // 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) => { diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 7521b750d5a09..144b0c2203ab7 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -626,7 +626,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>, variant_index: usize) - -> BasicBlock + -> (BasicBlock, bool) { let subpath = super::move_path_children_matching( self.move_data(), c.path, |proj| match proj { @@ -645,13 +645,13 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { variant_path, &adt.variants[variant_index], substs); - self.drop_ladder(c, fields) + (self.drop_ladder(c, fields), true) } 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(); + (drop_block.unwrap(), false) } } @@ -674,13 +674,25 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } _ => { let mut values = Vec::with_capacity(adt.variants.len()); - let mut blocks = Vec::with_capacity(adt.variants.len() + 1); + let mut blocks = Vec::with_capacity(adt.variants.len()); + let mut otherwise = None; for (idx, 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(); - values.push(discr); - blocks.push(self.open_drop_for_variant(c, &mut drop_block, adt, substs, idx)); + let (blk, is_ladder) = self.open_drop_for_variant(c, &mut drop_block, adt, + substs, idx); + if is_ladder { + values.push(discr); + blocks.push(blk); + } else { + otherwise = Some(blk) + } + } + 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 From 4e7770e4c2dc0985a85de7f8877bdb284a89bc64 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 2 Feb 2017 22:03:23 +0200 Subject: [PATCH 11/25] Prefer switching on false for boolean switches This ends up not really mattering because we generate a plain conditional branch in LLVM either way. --- src/librustc/middle/const_val.rs | 3 --- src/librustc/mir/mod.rs | 2 +- .../borrowck/mir/elaborate_drops.rs | 4 ++-- src/librustc_mir/build/expr/into.rs | 18 +++++++++--------- src/librustc_mir/build/matches/mod.rs | 4 ++-- src/librustc_mir/build/matches/test.rs | 14 +++++++------- 6 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index f885a6d95693f..11919db479c1a 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -11,7 +11,6 @@ use syntax::symbol::InternedString; use syntax::ast; use std::rc::Rc; -use std::borrow::Cow; use hir::def_id::DefId; use rustc_const_math::*; use self::ConstVal::*; @@ -19,8 +18,6 @@ pub use rustc_const_math::ConstInt; use std::collections::BTreeMap; -pub static BOOL_SWITCH_TRUE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(1)]); - #[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)] pub enum ConstVal { Float(ConstFloat), diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index bcd34eaf54e8c..19a6682b1c7be 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -447,7 +447,7 @@ pub struct Terminator<'tcx> { } /// For use in SwitchInt, for switching on bools. -pub static BOOL_SWITCH_TRUE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(1)]); +pub static BOOL_SWITCH_FALSE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(0)]); #[derive(Clone, RustcEncodable, RustcDecodable)] pub enum TerminatorKind<'tcx> { diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 144b0c2203ab7..44b85c31d8644 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -846,8 +846,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { self.new_block(c, is_cleanup, TerminatorKind::SwitchInt { discr: Operand::Consume(flag), switch_ty: boolty, - values: BOOL_SWITCH_TRUE.clone(), - targets: vec![on_set, on_unset], + values: BOOL_SWITCH_FALSE.clone(), + targets: vec![on_unset, on_set], }) } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 537daa4a15ba4..f61b4a6607722 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -72,8 +72,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: operand, switch_ty: this.hir.bool_ty(), - values: BOOL_SWITCH_TRUE.clone(), - targets: vec![then_block, else_block], + values: BOOL_SWITCH_FALSE.clone(), + targets: vec![else_block, then_block], }); unpack!(then_block = this.into(destination, then_block, then_expr)); @@ -113,13 +113,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let lhs = unpack!(block = this.as_operand(block, lhs)); let blocks = match op { - LogicalOp::And => vec![else_block, false_block], - LogicalOp::Or => vec![true_block, else_block], + LogicalOp::And => vec![false_block, else_block], + LogicalOp::Or => vec![else_block, true_block], }; this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: lhs, switch_ty: this.hir.bool_ty(), - values: BOOL_SWITCH_TRUE.clone(), + values: BOOL_SWITCH_FALSE.clone(), targets: blocks, }); @@ -127,8 +127,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.terminate(else_block, source_info, TerminatorKind::SwitchInt { discr: rhs, switch_ty: this.hir.bool_ty(), - values: BOOL_SWITCH_TRUE.clone(), - targets: vec![true_block, false_block], + values: BOOL_SWITCH_FALSE.clone(), + targets: vec![false_block, true_block], }); this.cfg.push_assign_constant( @@ -191,8 +191,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { TerminatorKind::SwitchInt { discr: cond, switch_ty: this.hir.bool_ty(), - values: BOOL_SWITCH_TRUE.clone(), - targets: vec![body_block, exit_block], + values: BOOL_SWITCH_FALSE.clone(), + targets: vec![exit_block, body_block], }); // if the test is false, there's no `break` to assign `destination`, so diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 885965da1f924..812900b6becc7 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -675,8 +675,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: cond, switch_ty: self.hir.bool_ty(), - values: BOOL_SWITCH_TRUE.clone(), - targets: vec![arm_block, otherwise], + values: BOOL_SWITCH_FALSE.clone(), + targets: vec![otherwise, arm_block], }); Some(otherwise) } else { diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index d9c2e6bb09071..f268eda4c15a5 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -236,7 +236,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { &ConstVal::Bool(false) => vec![false_bb, true_bb], v => span_bug!(test.span, "expected boolean value but got {:?}", v) }; - (BOOL_SWITCH_TRUE.clone(), vec![true_bb, false_bb], ret) + (BOOL_SWITCH_FALSE.clone(), vec![false_bb, true_bb], ret) } else { // The switch may be inexhaustive so we // add a catch all block @@ -326,8 +326,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.terminate(eq_block, source_info, TerminatorKind::SwitchInt { discr: Operand::Consume(eq_result), switch_ty: self.hir.bool_ty(), - values: BOOL_SWITCH_TRUE.clone(), - targets: vec![block, fail], + values: BOOL_SWITCH_FALSE.clone(), + targets: vec![fail, block], }); vec![block, fail] } else { @@ -375,8 +375,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: Operand::Consume(result), switch_ty: self.hir.bool_ty(), - values: BOOL_SWITCH_TRUE.clone(), - targets: vec![true_bb, false_bb], + values: BOOL_SWITCH_FALSE.clone(), + targets: vec![false_bb, true_bb], }); vec![true_bb, false_bb] } @@ -403,8 +403,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { discr: Operand::Consume(result), switch_ty: self.hir.bool_ty(), - values: BOOL_SWITCH_TRUE.clone(), - targets: vec![target_block, fail_block] + values: BOOL_SWITCH_FALSE.clone(), + targets: vec![fail_block, target_block] }); target_block } From d3f704a3bee191b095d7f35cb171c17576fbceb3 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Thu, 2 Feb 2017 22:53:21 +0200 Subject: [PATCH 12/25] Fix codegen test --- src/test/codegen/match.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/codegen/match.rs b/src/test/codegen/match.rs index ac47f6082e3e3..c35206e6e0a45 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: i8 [[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 => (), From 5e465d52e603b8486c2a497dc3baffb0cd272993 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 3 Feb 2017 00:07:31 +0200 Subject: [PATCH 13/25] Inspect now does not force on-stack Lvalue --- src/librustc_trans/mir/analyze.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 37725bfa2de84..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); From 1fee7221050642d8ed82b1d192c12847f3311071 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 3 Feb 2017 02:34:29 +0200 Subject: [PATCH 14/25] Revert use of layout code in typeck::collect --- src/librustc/ty/layout.rs | 13 +++++++------ src/librustc/ty/mod.rs | 7 ++++++- src/librustc/ty/util.rs | 18 ------------------ src/librustc_trans/mir/rvalue.rs | 8 ++------ src/librustc_typeck/collect.rs | 28 ++++++++++++---------------- 5 files changed, 27 insertions(+), 47 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 6e25ce200720b..02da7529ed39a 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -453,7 +453,7 @@ impl Integer { /// signed discriminant range and #[repr] attribute. /// N.B.: u64 values above i64::MAX will be treated as signed, but /// that shouldn't affect anything, other than maybe debuginfo. - pub fn repr_discr(tcx: TyCtxt, hints: &[attr::ReprAttr], min: i128, max: i128) + pub fn repr_discr(tcx: TyCtxt, ty: Ty, hints: &[attr::ReprAttr], min: i128, max: i128) -> (Integer, bool) { // Theoretically, negative values could be larger in unsigned representation // than the unsigned representation of the signed minimum. However, if there @@ -488,10 +488,10 @@ impl Integer { } attr::ReprAny => {}, attr::ReprPacked => { - bug!("Integer::repr_discr: found #[repr(packed)] on enum"); + bug!("Integer::repr_discr: found #[repr(packed)] on enum {}", ty); } attr::ReprSimd => { - bug!("Integer::repr_discr: found #[repr(simd)] on enum"); + bug!("Integer::repr_discr: found #[repr(simd)] on enum {}", ty); } } } @@ -1236,8 +1236,7 @@ impl<'a, 'gcx, 'tcx> Layout { // FIXME: should handle i128? signed-value based impl is weird and hard to // grok. - let discr = Integer::from_attr(&tcx.data_layout, def.discr_ty); - let signed = def.discr_ty.is_signed(); + let (discr, signed) = Integer::repr_discr(tcx, ty, hints, min, max); return success(CEnum { discr: discr, signed: signed, @@ -1352,7 +1351,9 @@ impl<'a, 'gcx, 'tcx> Layout { } // The general case. - let min_ity = Integer::from_attr(&tcx.data_layout, def.discr_ty); + let discr_max = (variants.len() - 1) as i64; + assert!(discr_max >= 0); + let (min_ity, _) = Integer::repr_discr(tcx, ty, &hints[..], 0, discr_max); let mut align = dl.aggregate_align; let mut size = Size::from_bytes(0); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index faadae4fec205..195d7e960eb5d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1326,7 +1326,12 @@ pub struct FieldDef { /// table. pub struct AdtDef { pub did: DefId, - pub discr_ty: attr::IntType, // Type of the discriminant + /// 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 diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 0281e53427d6d..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; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult}; use std::cell::RefCell; @@ -34,14 +33,10 @@ use syntax::ast::{self, Name}; use syntax::attr::{self, SignedInt, UnsignedInt}; use syntax_pos::Span; -use rustc_i128::i128; - use hir; pub trait IntTypeExt { fn to_ty<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>; - fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option; fn initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr; } @@ -56,19 +51,6 @@ impl IntTypeExt for attr::IntType { fn initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr { 0 } - - /// None = overflow - fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option) - -> Option { - if let Some(val) = val { - match *self { - SignedInt(it) => ConstInt::new_signed(val as i128, it, tcx.sess.target.int_type), - UnsignedInt(it) => ConstInt::new_unsigned(val, it, tcx.sess.target.uint_type), - }.and_then(|l| (l + ConstInt::Infer(1)).ok()).map(|v| v.to_u128_unchecked()) - } else { - Some(self.initial_discriminant(tcx)) - } - } } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 259f02a044c12..6874a0245b0b9 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -434,12 +434,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { 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, None, true); - let discr = if common::val_ty(discr) == Type::i1(bcx.ccx) { - bcx.zext(discr, discr_type) - } else { - bcx.trunc(discr, discr_type) - }; + 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 diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index d1041f89b723d..31f2af9b15c6b 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -66,7 +66,7 @@ use rustc_const_eval::EvalHint::UncheckedExprHint; use rustc_const_eval::{ConstContext, report_const_eval_err}; use rustc::ty::subst::Substs; use rustc::ty::{ToPredicate, ImplContainer, AssociatedItemContainer, TraitContainer}; -use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt, layout}; +use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::util::IntTypeExt; use rustc::dep_graph::DepNode; use util::common::{ErrorReported, MemoizationMap}; @@ -86,8 +86,6 @@ use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; -use rustc_i128::i128; - /////////////////////////////////////////////////////////////////////////// // Main entry point @@ -1030,7 +1028,7 @@ fn convert_union_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId) - -> Option { + -> Option { let e = &ccx.tcx.hir.body(body).value; debug!("disr expr, checking {}", ccx.tcx.hir.node_to_pretty_string(e.id)); @@ -1060,7 +1058,7 @@ fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId (attr::UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) | (attr::UnsignedInt(ast::UintTy::U128), ConstInt::U128(_)) | (attr::UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => - Some(i.to_u128_unchecked()), + Some(i), (_, i) => { print_err(ConstVal::Integral(i)); None @@ -1091,15 +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 (mut min, mut max) = (i128::max_value(), i128::min_value()); + 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.wrapping_add(1)); + 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, @@ -1111,14 +1111,10 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, None }.unwrap_or(wrapped_disr); prev_disr = Some(disr); - if (disr as i128) < min { min = disr as i128; } - if (disr as i128) > max { max = disr as i128; } 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 (repr_int, signed) = layout::Integer::repr_discr(tcx, &repr_hints[..], min, max); - let adt = tcx.alloc_adt_def(did, AdtKind::Enum, Some(repr_int.to_attr(signed)), variants); + let adt = tcx.alloc_adt_def(did, AdtKind::Enum, Some(repr_type), variants); tcx.adt_defs.borrow_mut().insert(did, adt); adt } From 832d7e07b099c12c507d8476784170a95d1c5f59 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 3 Feb 2017 03:36:32 +0200 Subject: [PATCH 15/25] Add TerminatorKind::if_ convenience constructor Constructs a TerminatorKind::SwitchInt for an equivalent conditional true-false branch. --- src/librustc/mir/mod.rs | 14 +++++-- .../borrowck/mir/elaborate_drops.rs | 9 +---- src/librustc_mir/build/expr/into.rs | 38 ++++++------------- src/librustc_mir/build/matches/mod.rs | 8 +--- src/librustc_mir/build/matches/test.rs | 31 ++++++--------- 5 files changed, 38 insertions(+), 62 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 19a6682b1c7be..9127d96ad4272 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -446,9 +446,6 @@ pub struct Terminator<'tcx> { pub kind: TerminatorKind<'tcx> } -/// For use in SwitchInt, for switching on bools. -pub static BOOL_SWITCH_FALSE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(0)]); - #[derive(Clone, RustcEncodable, RustcDecodable)] pub enum TerminatorKind<'tcx> { /// block should have one successor in the graph; we jump there @@ -543,6 +540,17 @@ 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 { diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 44b85c31d8644..d7ffe538c2456 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -842,13 +842,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { (true, false) => on_set, (true, true) => { let flag = self.drop_flag(c.path).unwrap(); - let boolty = self.tcx.types.bool; - self.new_block(c, is_cleanup, TerminatorKind::SwitchInt { - discr: Operand::Consume(flag), - switch_ty: boolty, - values: BOOL_SWITCH_FALSE.clone(), - targets: vec![on_unset, on_set], - }) + 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_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index f61b4a6607722..35841c2cbdf01 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -69,12 +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::SwitchInt { - discr: operand, - switch_ty: this.hir.bool_ty(), - values: BOOL_SWITCH_FALSE.clone(), - targets: vec![else_block, then_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 { @@ -113,23 +109,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let lhs = unpack!(block = this.as_operand(block, lhs)); let blocks = match op { - LogicalOp::And => vec![false_block, else_block], - LogicalOp::Or => vec![else_block, true_block], + LogicalOp::And => (else_block, false_block), + LogicalOp::Or => (true_block, else_block), }; - this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { - discr: lhs, - switch_ty: this.hir.bool_ty(), - values: BOOL_SWITCH_FALSE.clone(), - 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::SwitchInt { - discr: rhs, - switch_ty: this.hir.bool_ty(), - values: BOOL_SWITCH_FALSE.clone(), - targets: vec![false_block, true_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, @@ -187,13 +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::SwitchInt { - discr: cond, - switch_ty: this.hir.bool_ty(), - values: BOOL_SWITCH_FALSE.clone(), - targets: vec![exit_block, body_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 812900b6becc7..a28bc5d6ce36d 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -672,12 +672,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let source_info = self.source_info(guard.span); let cond = unpack!(block = self.as_operand(block, guard)); let otherwise = self.cfg.start_new_block(); - self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { - discr: cond, - switch_ty: self.hir.bool_ty(), - values: BOOL_SWITCH_FALSE.clone(), - targets: vec![otherwise, arm_block], - }); + self.cfg.terminate(block, source_info, + 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 f268eda4c15a5..7e47e173c51c5 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -228,6 +228,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { TestKind::SwitchInt { switch_ty, ref options, indices: _ } => { let (values, targets, ret) = if switch_ty.sty == ty::TyBool { + static BOOL_SWITCH_FALSE: &'static [ConstInt] = &[ConstInt::Infer(0)]; assert!(options.len() > 0 && options.len() <= 2); let (true_bb, false_bb) = (self.cfg.start_new_block(), self.cfg.start_new_block()); @@ -236,7 +237,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { &ConstVal::Bool(false) => vec![false_bb, true_bb], v => span_bug!(test.span, "expected boolean value but got {:?}", v) }; - (BOOL_SWITCH_FALSE.clone(), vec![false_bb, true_bb], ret) + (From::from(BOOL_SWITCH_FALSE), vec![false_bb, true_bb], ret) } else { // The switch may be inexhaustive so we // add a catch all block @@ -323,12 +324,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::SwitchInt { - discr: Operand::Consume(eq_result), - switch_ty: self.hir.bool_ty(), - values: BOOL_SWITCH_FALSE.clone(), - targets: vec![fail, block], - }); + 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); @@ -372,12 +371,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // branch based on result let (false_bb, true_bb) = (self.cfg.start_new_block(), self.cfg.start_new_block()); - self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { - discr: Operand::Consume(result), - switch_ty: self.hir.bool_ty(), - values: BOOL_SWITCH_FALSE.clone(), - targets: vec![false_bb, true_bb], - }); + self.cfg.terminate(block, source_info, + TerminatorKind::if_(self.hir.tcx(), Operand::Consume(result), + true_bb, false_bb)); vec![true_bb, false_bb] } } @@ -400,12 +396,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::SwitchInt { - discr: Operand::Consume(result), - switch_ty: self.hir.bool_ty(), - values: BOOL_SWITCH_FALSE.clone(), - targets: vec![fail_block, target_block] - }); + self.cfg.terminate(block, source_info, + TerminatorKind::if_(self.hir.tcx(), Operand::Consume(result), + target_block, fail_block)); target_block } From 4e29e816a3f5eddae2f168ef569d56b3646bbdc9 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 3 Feb 2017 05:34:46 +0200 Subject: [PATCH 16/25] Fix build Additionally, revert unnecessary changes to ty::layout --- src/librustc/ty/layout.rs | 45 +++++++++++---------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 02da7529ed39a..b21e6492dbce5 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -382,25 +382,8 @@ impl Integer { } } - pub fn to_attr(&self, signed: bool) -> attr::IntType { - match (*self, signed) { - (I1, false) => attr::IntType::UnsignedInt(UintTy::U8), - (I8, false) => attr::IntType::UnsignedInt(UintTy::U8), - (I16, false) => attr::IntType::UnsignedInt(UintTy::U16), - (I32, false) => attr::IntType::UnsignedInt(UintTy::U32), - (I64, false) => attr::IntType::UnsignedInt(UintTy::U64), - (I128, false) => attr::IntType::UnsignedInt(UintTy::U128), - (I1, true) => attr::IntType::SignedInt(IntTy::I8), - (I8, true) => attr::IntType::SignedInt(IntTy::I8), - (I16, true) => attr::IntType::SignedInt(IntTy::I16), - (I32, true) => attr::IntType::SignedInt(IntTy::I32), - (I64, true) => attr::IntType::SignedInt(IntTy::I64), - (I128, true) => attr::IntType::SignedInt(IntTy::I128), - } - } - /// Find the smallest Integer type which can represent the signed value. - pub fn fit_signed(x: i128) -> Integer { + pub fn fit_signed(x: i64) -> Integer { match x { -0x0000_0000_0000_0001...0x0000_0000_0000_0000 => I1, -0x0000_0000_0000_0080...0x0000_0000_0000_007f => I8, @@ -412,7 +395,7 @@ impl Integer { } /// Find the smallest Integer type which can represent the unsigned value. - pub fn fit_unsigned(x: u128) -> Integer { + pub fn fit_unsigned(x: u64) -> Integer { match x { 0...0x0000_0000_0000_0001 => I1, 0...0x0000_0000_0000_00ff => I8, @@ -453,13 +436,13 @@ impl Integer { /// signed discriminant range and #[repr] attribute. /// N.B.: u64 values above i64::MAX will be treated as signed, but /// that shouldn't affect anything, other than maybe debuginfo. - pub fn repr_discr(tcx: TyCtxt, ty: Ty, hints: &[attr::ReprAttr], min: i128, max: i128) + fn repr_discr(tcx: TyCtxt, ty: Ty, hints: &[attr::ReprAttr], min: i64, max: i64) -> (Integer, bool) { // Theoretically, negative values could be larger in unsigned representation // than the unsigned representation of the signed minimum. However, if there // are any negative values, the only valid unsigned representation is u64 // which can fit all i64 values, so the result remains unaffected. - let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); + let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u64, max as u64)); let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); let mut min_from_extern = None; @@ -472,7 +455,7 @@ impl Integer { let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; if discr < fit { bug!("Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum") + discriminant range of enum `{}", ty) } return (discr, ity.is_signed()); } @@ -488,15 +471,16 @@ impl Integer { } attr::ReprAny => {}, attr::ReprPacked => { - bug!("Integer::repr_discr: found #[repr(packed)] on enum {}", ty); + bug!("Integer::repr_discr: found #[repr(packed)] on enum `{}", ty); } attr::ReprSimd => { - bug!("Integer::repr_discr: found #[repr(simd)] on enum {}", ty); + bug!("Integer::repr_discr: found #[repr(simd)] on enum `{}", ty); } } } let at_least = min_from_extern.unwrap_or(min_default); + // If there are no negative values, we can use the unsigned fit. if min >= 0 { (cmp::max(unsigned_fit, at_least), false) @@ -1222,13 +1206,7 @@ impl<'a, 'gcx, 'tcx> Layout { i64::min_value(), true); for v in &def.variants { - let x = match def.discr_ty { - attr::IntType::SignedInt(IntTy::I128) | - attr::IntType::UnsignedInt(UintTy::U128) => - bug!("128-bit discriminants not yet supported"), - attr::IntType::SignedInt(_) => v.disr_val as i64, - attr::IntType::UnsignedInt(_) => v.disr_val as u64 as i64, - }; + 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; } @@ -1236,7 +1214,9 @@ impl<'a, 'gcx, 'tcx> Layout { // FIXME: should handle i128? signed-value based impl is weird and hard to // grok. - let (discr, signed) = Integer::repr_discr(tcx, ty, hints, min, max); + let (discr, signed) = Integer::repr_discr(tcx, ty, &hints[..], + min, + max); return success(CEnum { discr: discr, signed: signed, @@ -1354,6 +1334,7 @@ impl<'a, 'gcx, 'tcx> Layout { let discr_max = (variants.len() - 1) as i64; assert!(discr_max >= 0); let (min_ity, _) = Integer::repr_discr(tcx, ty, &hints[..], 0, discr_max); + let mut align = dl.aggregate_align; let mut size = Size::from_bytes(0); From cdd4aadb8a987ce6854a9af1225e4d35e41a89d6 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 3 Feb 2017 22:03:36 +0200 Subject: [PATCH 17/25] Fix tests --- src/test/compile-fail/E0081.rs | 6 +++--- src/test/compile-fail/issue-15524.rs | 18 +++++++++--------- src/test/ui/custom-derive/issue-36935.stderr | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) 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/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 From 1e8c855de6453521579a87994d3a6484dfd04b6c Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 4 Feb 2017 03:55:56 +0200 Subject: [PATCH 18/25] Rebase fixups --- src/Cargo.lock | 1 - src/librustc/ty/mod.rs | 2 -- src/librustc_typeck/Cargo.toml | 1 - src/librustc_typeck/lib.rs | 2 -- src/test/codegen/match.rs | 2 +- src/test/mir-opt/simplify_if.rs | 2 +- 6 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index 1189bc40faca4..06cf32ad0f6b5 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -529,7 +529,6 @@ dependencies = [ "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", - "rustc_i128 0.0.0", "rustc_platform_intrinsics 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 195d7e960eb5d..a292d7c032d55 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -71,8 +71,6 @@ pub use self::context::{Lift, TypeckTables}; pub use self::trait_def::{TraitDef, TraitFlags}; -use rustc_i128::u128; - pub mod adjustment; pub mod cast; pub mod error; diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index 067893552427b..f08d26373e50e 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -22,4 +22,3 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } syntax_pos = { path = "../libsyntax_pos" } rustc_errors = { path = "../librustc_errors" } -rustc_i128 = { path = "../librustc_i128" } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index aa2695b9553e7..f19a59a5d38ae 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -98,8 +98,6 @@ extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors as errors; -extern crate rustc_i128; - pub use rustc::dep_graph; pub use rustc::hir; pub use rustc::lint; diff --git a/src/test/codegen/match.rs b/src/test/codegen/match.rs index c35206e6e0a45..aa100da60132f 100644 --- a/src/test/codegen/match.rs +++ b/src/test/codegen/match.rs @@ -21,7 +21,7 @@ pub enum E { #[no_mangle] pub fn exhaustive_match(e: E) { // CHECK: switch{{.*}}, label %[[OTHERWISE:[a-zA-Z0-9_]+]] [ -// CHECK-NEXT: i8 [[DISCR:[0-9]+]], label %[[TRUE:[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_]+]] 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 From 1246779f743d2df9d1f1619dc789e79035a6280d Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 5 Feb 2017 01:09:55 +0200 Subject: [PATCH 19/25] Inline open_drop_for_variant & clean matches::test --- .../borrowck/mir/elaborate_drops.rs | 65 +++++++------------ src/librustc_mir/build/matches/test.rs | 21 +++--- 2 files changed, 32 insertions(+), 54 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index d7ffe538c2456..5899c9f31d14d 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -620,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, bool) - { - 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), true) - } else { - // variant not found - drop the entire enum - if let None = *drop_block { - *drop_block = Some(self.complete_drop(c, true)); - } - (drop_block.unwrap(), false) - } - } - 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( @@ -676,17 +639,33 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let mut values = Vec::with_capacity(adt.variants.len()); let mut blocks = Vec::with_capacity(adt.variants.len()); let mut otherwise = None; - for (idx, variant) in adt.variants.iter().enumerate() { + 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 (blk, is_ladder) = self.open_drop_for_variant(c, &mut drop_block, adt, - substs, idx); - if is_ladder { + 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(blk); + blocks.push(self.drop_ladder(c, fields)); } else { - otherwise = Some(blk) + // variant not found - drop the entire enum + if let None = otherwise { + otherwise = Some(self.complete_drop(c, true)); + } } } if let Some(block) = otherwise { diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 7e47e173c51c5..01c0433112bf3 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -227,8 +227,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } TestKind::SwitchInt { switch_ty, ref options, indices: _ } => { - let (values, targets, ret) = if switch_ty.sty == ty::TyBool { - static BOOL_SWITCH_FALSE: &'static [ConstInt] = &[ConstInt::Infer(0)]; + 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()); @@ -237,7 +236,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { &ConstVal::Bool(false) => vec![false_bb, true_bb], v => span_bug!(test.span, "expected boolean value but got {:?}", v) }; - (From::from(BOOL_SWITCH_FALSE), vec![false_bb, true_bb], ret) + (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 @@ -250,15 +250,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let values: Vec<_> = options.iter().map(|v| v.to_const_int().expect("switching on integral") ).collect(); - (From::from(values), targets.clone(), targets) + (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, TerminatorKind::SwitchInt { - discr: Operand::Consume(lvalue.clone()), - switch_ty: switch_ty, - values: values, - targets: targets.clone(), - }); + self.cfg.terminate(block, source_info, terminator); ret } From 7c2752a95be8878ccda118e4536c2d14363d45e4 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Mon, 6 Feb 2017 18:03:26 +0100 Subject: [PATCH 20/25] rustbuild: support setting verbosity in config.toml Signed-off-by: Marc-Antoine Perennou --- src/bootstrap/config.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 6e077691b3a05..aa2bc38143d5c 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -148,6 +148,7 @@ struct Build { python: Option, full_bootstrap: Option, extended: Option, + verbose: Option, } /// TOML representation of various global install decisions. @@ -292,6 +293,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); From 05eef36fa5ff9235ea8124a6396c7973015b4b8b Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Mon, 6 Feb 2017 17:50:30 +0000 Subject: [PATCH 21/25] rustdoc: Improve impl disambiguation * Don't disambiguate if there are multiple impls for the same type. * Disambiguate for impls of &Foo and &mut Foo. * Don't try to disambiguate generic types. --- src/librustdoc/html/format.rs | 6 ++-- src/librustdoc/html/render.rs | 32 ++++++++++++++------ src/test/rustdoc/impl-disambiguation.rs | 40 +++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 src/test/rustdoc/impl-disambiguation.rs 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/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 {} From 4268872807cf8bc5c8c435794d1c82d21899d67b Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Mon, 6 Feb 2017 20:47:04 +0100 Subject: [PATCH 22/25] rustbuild: add verbose to config.toml.example Signed-off-by: Marc-Antoine Perennou --- src/bootstrap/config.toml.example | 3 +++ 1 file changed, 3 insertions(+) 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 # ============================================================================= From 19bbd855ef73c28b381fd98553aedb0a7423efc4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 4 Feb 2017 16:53:17 -0800 Subject: [PATCH 23/25] Fix branch name Cargo's downloaded from This landed on beta in #39546 and this is bringing the patch back to master. --- src/bootstrap/dist.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 9327cc0cf7faf..9878d1c08bac1 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -517,9 +517,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(), }; From 52a887e12b2d5eaaedfee6843826960f4eee42a8 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 8 Feb 2017 10:56:42 +0100 Subject: [PATCH 24/25] Remove some leftover makefiles. --- mk/cfg/aarch64-unknown-freebsd.mk | 1 - mk/cfg/i686-unknown-netbsd.mk | 1 - 2 files changed, 2 deletions(-) delete mode 100644 mk/cfg/aarch64-unknown-freebsd.mk delete mode 100644 mk/cfg/i686-unknown-netbsd.mk 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 From fdd8c349ef5314d286a6227518cbda0036dfff3d Mon Sep 17 00:00:00 2001 From: Yamakaky Date: Sun, 4 Dec 2016 16:38:27 -0500 Subject: [PATCH 25/25] Improve backtrace formating while panicking. - `RUST_BACKTRACE=full` prints all the informations (old behaviour) - `RUST_BACKTRACE=(0|no)` disables the backtrace. - `RUST_BACKTRACE=` (including `1`) shows a simplified backtrace, without the function addresses and with cleaned filenames and symbols. Also removes some unneded frames at the beginning and the end. Fixes #37783. PR is #38165. --- src/doc/book/functions.md | 15 +- src/libstd/panicking.rs | 10 +- src/libstd/sys/redox/backtrace.rs | 11 +- src/libstd/sys/unix/backtrace/mod.rs | 5 +- .../sys/unix/backtrace/printing/dladdr.rs | 28 +- src/libstd/sys/unix/backtrace/printing/mod.rs | 7 +- .../unix/backtrace/tracing/backtrace_fn.rs | 49 +-- .../sys/unix/backtrace/tracing/gcc_s.rs | 159 ++++---- .../windows/{ => backtrace}/backtrace_gnu.rs | 0 .../{backtrace.rs => backtrace/mod.rs} | 138 ++++--- .../backtrace/printing/mod.rs} | 11 +- .../windows/{ => backtrace}/printing/msvc.rs | 56 +-- src/libstd/sys/windows/printing/gnu.rs | 26 -- src/libstd/sys_common/backtrace.rs | 250 +++++++++++-- src/libstd/sys_common/gnu/libbacktrace.rs | 342 +++++++++--------- src/libunwind/libunwind.rs | 2 +- src/test/run-pass/backtrace-debuginfo.rs | 4 +- src/test/run-pass/backtrace.rs | 49 ++- 18 files changed, 715 insertions(+), 447 deletions(-) rename src/libstd/sys/windows/{ => backtrace}/backtrace_gnu.rs (100%) rename src/libstd/sys/windows/{backtrace.rs => backtrace/mod.rs} (77%) rename src/libstd/sys/{unix/backtrace/printing/gnu.rs => windows/backtrace/printing/mod.rs} (64%) rename src/libstd/sys/windows/{ => backtrace}/printing/msvc.rs (57%) delete mode 100644 src/libstd/sys/windows/printing/gnu.rs 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/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/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.)