mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 11:07:42 +00:00
add SetDiscriminant StatementKind to enable deaggregation of enums
This commit is contained in:
parent
42001edc99
commit
d77a136437
@ -689,13 +689,17 @@ pub struct Statement<'tcx> {
|
|||||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
||||||
pub enum StatementKind<'tcx> {
|
pub enum StatementKind<'tcx> {
|
||||||
Assign(Lvalue<'tcx>, Rvalue<'tcx>),
|
Assign(Lvalue<'tcx>, Rvalue<'tcx>),
|
||||||
|
SetDiscriminant{ lvalue: Lvalue<'tcx>, variant_index: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Debug for Statement<'tcx> {
|
impl<'tcx> Debug for Statement<'tcx> {
|
||||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||||
use self::StatementKind::*;
|
use self::StatementKind::*;
|
||||||
match self.kind {
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,6 +323,9 @@ macro_rules! make_mir_visitor {
|
|||||||
ref $($mutability)* rvalue) => {
|
ref $($mutability)* rvalue) => {
|
||||||
self.visit_assign(block, lvalue, rvalue);
|
self.visit_assign(block, lvalue, rvalue);
|
||||||
}
|
}
|
||||||
|
StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
|
||||||
|
self.visit_lvalue(lvalue, LvalueContext::Store);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,6 +442,9 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
let bits_per_block = self.bits_per_block(ctxt);
|
let bits_per_block = self.bits_per_block(ctxt);
|
||||||
match stmt.kind {
|
match stmt.kind {
|
||||||
|
repr::StatementKind::SetDiscriminant { .. } => {
|
||||||
|
span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
|
||||||
|
}
|
||||||
repr::StatementKind::Assign(ref lvalue, _) => {
|
repr::StatementKind::Assign(ref lvalue, _) => {
|
||||||
// assigning into this `lvalue` kills all
|
// assigning into this `lvalue` kills all
|
||||||
// MoveOuts from it, and *also* all MoveOuts
|
// MoveOuts from it, and *also* all MoveOuts
|
||||||
|
@ -104,6 +104,9 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||||||
repr::StatementKind::Assign(ref lvalue, ref rvalue) => {
|
repr::StatementKind::Assign(ref lvalue, ref rvalue) => {
|
||||||
(lvalue, 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 {
|
if lvalue == peek_arg_lval {
|
||||||
|
@ -616,6 +616,10 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
|
|||||||
Rvalue::InlineAsm { .. } => {}
|
Rvalue::InlineAsm { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
StatementKind::SetDiscriminant{ .. } => {
|
||||||
|
span_bug!(stmt.source_info.span,
|
||||||
|
"SetDiscriminant should not exist during borrowck");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,6 +369,9 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
|
|||||||
let block = &mir[loc.block];
|
let block = &mir[loc.block];
|
||||||
match block.statements.get(loc.index) {
|
match block.statements.get(loc.index) {
|
||||||
Some(stmt) => match stmt.kind {
|
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, _) => {
|
repr::StatementKind::Assign(ref lvalue, _) => {
|
||||||
debug!("drop_flag_effects: assignment {:?}", stmt);
|
debug!("drop_flag_effects: assignment {:?}", stmt);
|
||||||
on_all_children_bits(tcx, mir, move_data,
|
on_all_children_bits(tcx, mir, move_data,
|
||||||
|
@ -39,7 +39,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
|
|||||||
|
|
||||||
let mut curr: usize = 0;
|
let mut curr: usize = 0;
|
||||||
for bb in mir.basic_blocks_mut() {
|
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,
|
Some(idx) => idx,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
@ -48,7 +48,11 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
|
|||||||
let src_info = bb.statements[idx].source_info;
|
let src_info = bb.statements[idx].source_info;
|
||||||
let suffix_stmts = bb.statements.split_off(idx+1);
|
let suffix_stmts = bb.statements.split_off(idx+1);
|
||||||
let orig_stmt = bb.statements.pop().unwrap();
|
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 {
|
let (agg_kind, operands) = match rhs {
|
||||||
&Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands),
|
&Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands),
|
||||||
_ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
|
_ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
|
||||||
@ -64,10 +68,14 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
|
|||||||
let ty = variant_def.fields[i].ty(tcx, substs);
|
let ty = variant_def.fields[i].ty(tcx, substs);
|
||||||
let rhs = Rvalue::Use(op.clone());
|
let rhs = Rvalue::Use(op.clone());
|
||||||
|
|
||||||
// since we don't handle enums, we don't need a cast
|
let lhs_cast = if adt_def.variants.len() > 1 {
|
||||||
let lhs_cast = lhs.clone();
|
Lvalue::Projection(Box::new(LvalueProjection {
|
||||||
|
base: lhs.clone(),
|
||||||
// FIXME we cannot deaggregate enums issue: #35186
|
elem: ProjectionElem::Downcast(adt_def, variant),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
lhs.clone()
|
||||||
|
};
|
||||||
|
|
||||||
let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
|
let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
|
||||||
base: lhs_cast,
|
base: lhs_cast,
|
||||||
@ -80,18 +88,34 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
|
|||||||
debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
|
debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
|
||||||
bb.statements.push(new_statement);
|
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();
|
curr = bb.statements.len();
|
||||||
bb.statements.extend(suffix_stmts);
|
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>>)
|
statements: &Vec<Statement<'tcx>>)
|
||||||
-> Option<usize> {
|
-> Option<usize> {
|
||||||
for i in curr..statements.len() {
|
for i in start..statements.len() {
|
||||||
let ref statement = statements[i];
|
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 {
|
let (kind, operands) = match rhs {
|
||||||
&Rvalue::Aggregate(ref kind, ref operands) => (kind, operands),
|
&Rvalue::Aggregate(ref kind, ref operands) => (kind, operands),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
@ -100,9 +124,8 @@ fn get_aggregate_statement<'a, 'tcx, 'b>(curr: usize,
|
|||||||
&AggregateKind::Adt(adt_def, variant, _) => (adt_def, variant),
|
&AggregateKind::Adt(adt_def, variant, _) => (adt_def, variant),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
if operands.len() == 0 || adt_def.variants.len() > 1 {
|
if operands.len() == 0 {
|
||||||
// don't deaggregate ()
|
// don't deaggregate ()
|
||||||
// don't deaggregate enums ... for now
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
debug!("getting variant {:?}", variant);
|
debug!("getting variant {:?}", variant);
|
||||||
|
@ -219,7 +219,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
|
|||||||
let (mut rvalue, mut call) = (None, None);
|
let (mut rvalue, mut call) = (None, None);
|
||||||
let source_info = if stmt_idx < no_stmts {
|
let source_info = if stmt_idx < no_stmts {
|
||||||
let statement = &mut self.source[bb].statements[stmt_idx];
|
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 {
|
if self.keep_original {
|
||||||
rvalue = Some(rhs.clone());
|
rvalue = Some(rhs.clone());
|
||||||
} else {
|
} else {
|
||||||
@ -300,10 +306,16 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
|
|||||||
});
|
});
|
||||||
let mut rvalue = match candidate {
|
let mut rvalue = match candidate {
|
||||||
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
|
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) => {
|
StatementKind::Assign(_, ref mut rvalue) => {
|
||||||
mem::replace(rvalue, Rvalue::Use(new_operand))
|
mem::replace(rvalue, Rvalue::Use(new_operand))
|
||||||
}
|
}
|
||||||
|
StatementKind::SetDiscriminant{ .. } => {
|
||||||
|
span_bug!(statement.source_info.span,
|
||||||
|
"cannot promote SetDiscriminant {:?}",
|
||||||
|
statement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Candidate::ShuffleIndices(bb) => {
|
Candidate::ShuffleIndices(bb) => {
|
||||||
@ -340,7 +352,11 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
|
|||||||
let (span, ty) = match candidate {
|
let (span, ty) = match candidate {
|
||||||
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
|
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
|
||||||
let statement = &mir[bb].statements[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 let Lvalue::Temp(index) = *dest {
|
||||||
if temps[index] == TempState::PromotedOut {
|
if temps[index] == TempState::PromotedOut {
|
||||||
// Already promoted.
|
// Already promoted.
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
use rustc::infer::{self, InferCtxt, InferOk};
|
use rustc::infer::{self, InferCtxt, InferOk};
|
||||||
use rustc::traits::{self, ProjectionMode};
|
use rustc::traits::{self, ProjectionMode};
|
||||||
use rustc::ty::fold::TypeFoldable;
|
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::repr::*;
|
||||||
use rustc::mir::tcx::LvalueTy;
|
use rustc::mir::tcx::LvalueTy;
|
||||||
use rustc::mir::transform::{MirPass, MirSource, Pass};
|
use rustc::mir::transform::{MirPass, MirSource, Pass};
|
||||||
@ -360,10 +360,27 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||||||
span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
|
span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
|
||||||
lv_ty, rv_ty, terr);
|
lv_ty, rv_ty, terr);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: rvalue with undeterminable type - e.g. inline
|
// FIXME: rvalue with undeterminable type - e.g. inline
|
||||||
// asm.
|
// 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);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,6 +285,9 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mir::StatementKind::SetDiscriminant{ .. } => {
|
||||||
|
span_bug!(span, "SetDiscriminant should not appear in constants?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ use common::{self, BlockAndBuilder};
|
|||||||
|
|
||||||
use super::MirContext;
|
use super::MirContext;
|
||||||
use super::LocalRef;
|
use super::LocalRef;
|
||||||
|
use super::super::adt;
|
||||||
|
use super::super::disr::Disr;
|
||||||
|
|
||||||
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||||
pub fn trans_statement(&mut self,
|
pub fn trans_statement(&mut self,
|
||||||
@ -57,6 +59,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||||||
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
src/test/mir-opt/deaggregator_test_enum.rs
Normal file
45
src/test/mir-opt/deaggregator_test_enum.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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
|
Loading…
Reference in New Issue
Block a user