#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum StatementKind<'tcx> {
Assign(Lvalue<'tcx>, Rvalue<'tcx>),
+ SetDiscriminant{ lvalue: Lvalue<'tcx>, variant_index: usize },
}
impl<'tcx> Debug for Statement<'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
use self::StatementKind::*;
match self.kind {
- Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv)
+ Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
+ SetDiscriminant{lvalue: ref lv, variant_index: index} => {
+ write!(fmt, "discriminant({:?}) = {:?}", lv, index)
+ }
}
}
}
ref $($mutability)* rvalue) => {
self.visit_assign(block, lvalue, rvalue);
}
+ StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
+ self.visit_lvalue(lvalue, LvalueContext::Store);
+ }
}
}
}
let bits_per_block = self.bits_per_block(ctxt);
match stmt.kind {
+ repr::StatementKind::SetDiscriminant { .. } => {
+ span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
+ }
repr::StatementKind::Assign(ref lvalue, _) => {
// assigning into this `lvalue` kills all
// MoveOuts from it, and *also* all MoveOuts
repr::StatementKind::Assign(ref lvalue, ref rvalue) => {
(lvalue, rvalue)
}
+ repr::StatementKind::SetDiscriminant{ .. } =>
+ span_bug!(stmt.source_info.span,
+ "sanity_check should run before Deaggregator inserts SetDiscriminant"),
};
if lvalue == peek_arg_lval {
Rvalue::InlineAsm { .. } => {}
}
}
+ StatementKind::SetDiscriminant{ .. } => {
+ span_bug!(stmt.source_info.span,
+ "SetDiscriminant should not exist during borrowck");
+ }
}
}
let block = &mir[loc.block];
match block.statements.get(loc.index) {
Some(stmt) => match stmt.kind {
+ repr::StatementKind::SetDiscriminant{ .. } => {
+ span_bug!(stmt.source_info.span, "SetDiscrimant should not exist during borrowck");
+ }
repr::StatementKind::Assign(ref lvalue, _) => {
debug!("drop_flag_effects: assignment {:?}", stmt);
on_all_children_bits(tcx, mir, move_data,
let mut curr: usize = 0;
for bb in mir.basic_blocks_mut() {
- let idx = match get_aggregate_statement(curr, &bb.statements) {
+ let idx = match get_aggregate_statement_index(curr, &bb.statements) {
Some(idx) => idx,
None => continue,
};
let src_info = bb.statements[idx].source_info;
let suffix_stmts = bb.statements.split_off(idx+1);
let orig_stmt = bb.statements.pop().unwrap();
- let StatementKind::Assign(ref lhs, ref rhs) = orig_stmt.kind;
+ let (lhs, rhs) = match orig_stmt.kind {
+ StatementKind::Assign(ref lhs, ref rhs) => (lhs, rhs),
+ StatementKind::SetDiscriminant{ .. } =>
+ span_bug!(src_info.span, "expected aggregate, not {:?}", orig_stmt.kind),
+ };
let (agg_kind, operands) = match rhs {
&Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands),
_ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
let ty = variant_def.fields[i].ty(tcx, substs);
let rhs = Rvalue::Use(op.clone());
- // since we don't handle enums, we don't need a cast
- let lhs_cast = lhs.clone();
-
- // FIXME we cannot deaggregate enums issue: #35186
+ let lhs_cast = if adt_def.variants.len() > 1 {
+ Lvalue::Projection(Box::new(LvalueProjection {
+ base: lhs.clone(),
+ elem: ProjectionElem::Downcast(adt_def, variant),
+ }))
+ } else {
+ lhs.clone()
+ };
let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
base: lhs_cast,
debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
bb.statements.push(new_statement);
}
+
+ // if the aggregate was an enum, we need to set the discriminant
+ if adt_def.variants.len() > 1 {
+ let set_discriminant = Statement {
+ kind: StatementKind::SetDiscriminant {
+ lvalue: lhs.clone(),
+ variant_index: variant,
+ },
+ source_info: src_info,
+ };
+ bb.statements.push(set_discriminant);
+ };
+
curr = bb.statements.len();
bb.statements.extend(suffix_stmts);
}
}
}
-fn get_aggregate_statement<'a, 'tcx, 'b>(curr: usize,
+fn get_aggregate_statement_index<'a, 'tcx, 'b>(start: usize,
statements: &Vec<Statement<'tcx>>)
-> Option<usize> {
- for i in curr..statements.len() {
+ for i in start..statements.len() {
let ref statement = statements[i];
- let StatementKind::Assign(_, ref rhs) = statement.kind;
+ let rhs = match statement.kind {
+ StatementKind::Assign(_, ref rhs) => rhs,
+ StatementKind::SetDiscriminant{ .. } => continue,
+ };
let (kind, operands) = match rhs {
&Rvalue::Aggregate(ref kind, ref operands) => (kind, operands),
_ => continue,
&AggregateKind::Adt(adt_def, variant, _) => (adt_def, variant),
_ => continue,
};
- if operands.len() == 0 || adt_def.variants.len() > 1 {
+ if operands.len() == 0 {
// don't deaggregate ()
- // don't deaggregate enums ... for now
continue;
}
debug!("getting variant {:?}", variant);
let (mut rvalue, mut call) = (None, None);
let source_info = if stmt_idx < no_stmts {
let statement = &mut self.source[bb].statements[stmt_idx];
- let StatementKind::Assign(_, ref mut rhs) = statement.kind;
+ let mut rhs = match statement.kind {
+ StatementKind::Assign(_, ref mut rhs) => rhs,
+ StatementKind::SetDiscriminant{ .. } =>
+ span_bug!(statement.source_info.span,
+ "cannot promote SetDiscriminant {:?}",
+ statement),
+ };
if self.keep_original {
rvalue = Some(rhs.clone());
} else {
});
let mut rvalue = match candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
- match self.source[bb].statements[stmt_idx].kind {
+ let ref mut statement = self.source[bb].statements[stmt_idx];
+ match statement.kind {
StatementKind::Assign(_, ref mut rvalue) => {
mem::replace(rvalue, Rvalue::Use(new_operand))
}
+ StatementKind::SetDiscriminant{ .. } => {
+ span_bug!(statement.source_info.span,
+ "cannot promote SetDiscriminant {:?}",
+ statement);
+ }
}
}
Candidate::ShuffleIndices(bb) => {
let (span, ty) = match candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
let statement = &mir[bb].statements[stmt_idx];
- let StatementKind::Assign(ref dest, _) = statement.kind;
+ let dest = match statement.kind {
+ StatementKind::Assign(ref dest, _) => dest,
+ StatementKind::SetDiscriminant{ .. } =>
+ panic!("cannot promote SetDiscriminant"),
+ };
if let Lvalue::Temp(index) = *dest {
if temps[index] == TempState::PromotedOut {
// Already promoted.
use rustc::infer::{self, InferCtxt, InferOk};
use rustc::traits::{self, Reveal};
use rustc::ty::fold::TypeFoldable;
-use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::{self, Ty, TyCtxt, TypeVariants};
use rustc::mir::repr::*;
use rustc::mir::tcx::LvalueTy;
use rustc::mir::transform::{MirPass, MirSource, Pass};
span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
lv_ty, rv_ty, terr);
}
- }
-
// FIXME: rvalue with undeterminable type - e.g. inline
// asm.
+ }
+ }
+ StatementKind::SetDiscriminant{ ref lvalue, variant_index } => {
+ let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx);
+ let adt = match lvalue_type.sty {
+ TypeVariants::TyEnum(adt, _) => adt,
+ _ => {
+ span_bug!(stmt.source_info.span,
+ "bad set discriminant ({:?} = {:?}): lhs is not an enum",
+ lvalue,
+ variant_index);
+ }
+ };
+ if variant_index >= adt.variants.len() {
+ span_bug!(stmt.source_info.span,
+ "bad set discriminant ({:?} = {:?}): value of of range",
+ lvalue,
+ variant_index);
+ };
}
}
}
Err(err) => if failure.is_ok() { failure = Err(err); }
}
}
+ mir::StatementKind::SetDiscriminant{ .. } => {
+ span_bug!(span, "SetDiscriminant should not appear in constants?");
+ }
}
}
use super::MirContext;
use super::LocalRef;
+use super::super::adt;
+use super::super::disr::Disr;
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
pub fn trans_statement(&mut self,
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
}
}
+ mir::StatementKind::SetDiscriminant{ref lvalue, variant_index} => {
+ let ty = self.monomorphized_lvalue_ty(lvalue);
+ let repr = adt::represent_type(bcx.ccx(), ty);
+ let lvalue_transed = self.trans_lvalue(&bcx, lvalue);
+ bcx.with_block(|bcx|
+ adt::trans_set_discr(bcx,
+ &repr,
+ lvalue_transed.llval,
+ Disr::from(variant_index))
+ );
+ bcx
+ }
}
}
}
--- /dev/null
+// Copyright 2016 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Baz {
+ Empty,
+ Foo { x: usize },
+}
+
+fn bar(a: usize) -> Baz {
+ Baz::Foo { x: a }
+}
+
+fn main() {
+ let x = bar(10);
+ match x {
+ Baz::Empty => println!("empty"),
+ Baz::Foo { x } => println!("{}", x),
+ };
+}
+
+// END RUST SOURCE
+// START rustc.node10.Deaggregator.before.mir
+// bb0: {
+// var0 = arg0; // scope 0 at main.rs:7:8: 7:9
+// tmp0 = var0; // scope 1 at main.rs:8:19: 8:20
+// return = Baz::Foo { x: tmp0 }; // scope 1 at main.rs:8:5: 8:21
+// goto -> bb1; // scope 1 at main.rs:7:1: 9:2
+// }
+// END rustc.node10.Deaggregator.before.mir
+// START rustc.node10.Deaggregator.after.mir
+// bb0: {
+// var0 = arg0; // scope 0 at main.rs:7:8: 7:9
+// tmp0 = var0; // scope 1 at main.rs:8:19: 8:20
+// ((return as Foo).0: usize) = tmp0; // scope 1 at main.rs:8:5: 8:21
+// discriminant(return) = 1; // scope 1 at main.rs:8:5: 8:21
+// goto -> bb1; // scope 1 at main.rs:7:1: 9:2
+// }
+// END rustc.node10.Deaggregator.after.mir
\ No newline at end of file