From 6c9a018b60d1222217645ef4015764fd33b14a3f Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 26 May 2019 09:55:50 +0100 Subject: [PATCH] Don't run MIR passes on constructor shims --- src/librustc_mir/borrow_check/mod.rs | 28 -------- src/librustc_mir/build/mod.rs | 35 --------- src/librustc_mir/lib.rs | 1 + src/librustc_mir/shim.rs | 82 ++++++++++++++-------- src/librustc_mir/transform/deaggregator.rs | 68 +++--------------- src/librustc_mir/transform/mod.rs | 12 +++- src/librustc_mir/util/aggregate.rs | 76 ++++++++++++++++++++ src/librustc_mir/util/mod.rs | 2 + src/test/mir-opt/unusual-item-types.rs | 3 +- 9 files changed, 152 insertions(+), 155 deletions(-) create mode 100644 src/librustc_mir/util/aggregate.rs diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 4ae4d039d6034..74b2faa7a4ce8 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -91,34 +91,6 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> BorrowC let input_mir = tcx.mir_validated(def_id); debug!("run query mir_borrowck: {}", tcx.def_path_str(def_id)); - // We are not borrow checking the automatically generated struct/variant constructors - // because we want to accept structs such as this (taken from the `linked-hash-map` - // crate): - // ```rust - // struct Qey(Q); - // ``` - // MIR of this struct constructor looks something like this: - // ```rust - // fn Qey(_1: Q) -> Qey{ - // let mut _0: Qey; // return place - // - // bb0: { - // (_0.0: Q) = move _1; // bb0[0]: scope 0 at src/main.rs:1:1: 1:26 - // return; // bb0[1]: scope 0 at src/main.rs:1:1: 1:26 - // } - // } - // ``` - // The problem here is that `(_0.0: Q) = move _1;` is valid only if `Q` is - // of statically known size, which is not known to be true because of the - // `Q: ?Sized` constraint. However, it is true because the constructor can be - // called only when `Q` is of statically known size. - if tcx.is_constructor(def_id) { - return BorrowCheckResult { - closure_requirements: None, - used_mut_upvars: SmallVec::new(), - }; - } - let opt_closure_req = tcx.infer_ctxt().enter(|infcx| { let input_mir: &Body<'_> = &input_mir.borrow(); do_mir_borrowck(&infcx, input_mir, def_id) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index c8a31ecffb84d..6bde349390ee8 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -2,7 +2,6 @@ use crate::build; use crate::build::scope::DropKind; use crate::hair::cx::Cx; use crate::hair::{LintLevel, BindingMode, PatternKind}; -use crate::shim; use crate::transform::MirSource; use crate::util as mir_util; use rustc::hir; @@ -31,8 +30,6 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Body<' // Figure out what primary body this item has. let (body_id, return_ty_span) = match tcx.hir().get_by_hir_id(id) { - Node::Ctor(ctor) => return create_constructor_shim(tcx, id, ctor), - Node::Expr(hir::Expr { node: hir::ExprKind::Closure(_, decl, body_id, _, _), .. }) | Node::Item(hir::Item { node: hir::ItemKind::Fn(decl, _, _, body_id), .. }) | Node::ImplItem( @@ -234,38 +231,6 @@ impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> { } } -fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ctor_id: hir::HirId, - v: &'tcx hir::VariantData) - -> Body<'tcx> -{ - let span = tcx.hir().span_by_hir_id(ctor_id); - if let hir::VariantData::Tuple(ref fields, ctor_id) = *v { - tcx.infer_ctxt().enter(|infcx| { - let mut mir = shim::build_adt_ctor(&infcx, ctor_id, fields, span); - - // Convert the `mir::Body` to global types. - let tcx = infcx.tcx.global_tcx(); - let mut globalizer = GlobalizeMir { - tcx, - span: mir.span - }; - globalizer.visit_body(&mut mir); - let mir = unsafe { - mem::transmute::, Body<'tcx>>(mir) - }; - - mir_util::dump_mir(tcx, None, "mir_map", &0, - MirSource::item(tcx.hir().local_def_id_from_hir_id(ctor_id)), - &mir, |_, _| Ok(()) ); - - mir - }) - } else { - span_bug!(span, "attempting to create MIR for non-tuple variant {:?}", v); - } -} - /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 9213a009ea740..53302810b4052 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -22,6 +22,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(unicode_internals)] #![feature(step_trait)] #![feature(slice_concat_ext)] +#![feature(trusted_len)] #![feature(try_blocks)] #![recursion_limit="256"] diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 0cefc8c3a92ab..169e426c1d3c6 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -1,6 +1,5 @@ use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::infer; use rustc::mir::*; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::layout::VariantIdx; @@ -21,6 +20,7 @@ use crate::transform::{ }; use crate::util::elaborate_drops::{self, DropElaborator, DropStyle, DropFlagMode}; use crate::util::patch::MirPatch; +use crate::util::expand_aggregate; pub fn provide(providers: &mut Providers<'_>) { providers.mir_shims = make_shim; @@ -842,29 +842,26 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir } -pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, - ctor_id: hir::HirId, - fields: &[hir::StructField], - span: Span) - -> Body<'tcx> -{ - let tcx = infcx.tcx; - let gcx = tcx.global_tcx(); - let def_id = tcx.hir().local_def_id_from_hir_id(ctor_id); - let param_env = gcx.param_env(def_id); +pub fn build_adt_ctor<'gcx>(tcx: TyCtxt<'_, 'gcx, 'gcx>, ctor_id: DefId) -> &'gcx Body<'gcx> { + debug_assert!(tcx.is_constructor(ctor_id)); + + let span = tcx.hir().span_if_local(ctor_id) + .unwrap_or_else(|| bug!("no span for ctor {:?}", ctor_id)); + + let param_env = tcx.param_env(ctor_id); // Normalize the sig. - let sig = gcx.fn_sig(def_id) + let sig = tcx.fn_sig(ctor_id) .no_bound_vars() .expect("LBR in ADT constructor signature"); - let sig = gcx.normalize_erasing_regions(param_env, sig); + let sig = tcx.normalize_erasing_regions(param_env, sig); let (adt_def, substs) = match sig.output().sty { ty::Adt(adt_def, substs) => (adt_def, substs), _ => bug!("unexpected type for ADT ctor {:?}", sig.output()) }; - debug!("build_ctor: def_id={:?} sig={:?} fields={:?}", def_id, sig, fields); + debug!("build_ctor: ctor_id={:?} sig={:?}", ctor_id, sig); let local_decls = local_decls_for_sig(&sig, span); @@ -873,26 +870,37 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, scope: OUTERMOST_SOURCE_SCOPE }; - let variant_no = if adt_def.is_enum() { - adt_def.variant_index_with_ctor_id(def_id) + let variant_index = if adt_def.is_enum() { + adt_def.variant_index_with_ctor_id(ctor_id) } else { VariantIdx::new(0) }; - // return = ADT(arg0, arg1, ...); return + // Generate the following MIR: + // + // (return as Variant).field0 = arg0; + // (return as Variant).field1 = arg1; + // + // return; + debug!("build_ctor: variant_index={:?}", variant_index); + + let statements = expand_aggregate( + Place::RETURN_PLACE, + adt_def + .variants[variant_index] + .fields + .iter() + .enumerate() + .map(|(idx, field_def)| ( + Operand::Move(Place::Base(PlaceBase::Local(Local::new(idx + 1)))), + field_def.ty(tcx, substs), + )), + AggregateKind::Adt(adt_def, variant_index, substs, None, None), + source_info, + ).collect(); + let start_block = BasicBlockData { - statements: vec![Statement { - source_info, - kind: StatementKind::Assign( - Place::RETURN_PLACE, - box Rvalue::Aggregate( - box AggregateKind::Adt(adt_def, variant_no, substs, None, None), - (1..sig.inputs().len()+1).map(|i| { - Operand::Move(Place::Base(PlaceBase::Local(Local::new(i)))) - }).collect() - ) - ) - }], + statements, terminator: Some(Terminator { source_info, kind: TerminatorKind::Return, @@ -900,7 +908,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, is_cleanup: false }; - Body::new( + let body = Body::new( IndexVec::from_elem_n(start_block, 1), IndexVec::from_elem_n( SourceScopeData { span: span, parent_scope: None }, 1 @@ -914,5 +922,17 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, vec![], span, vec![], - ) + ); + + crate::util::dump_mir( + tcx, + None, + "mir_map", + &0, + crate::transform::MirSource::item(ctor_id), + &body, + |_, _| Ok(()), + ); + + tcx.arena.alloc(body) } diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index 7da37f956cedd..286c412622dd5 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -1,8 +1,7 @@ use rustc::mir::*; use rustc::ty::TyCtxt; -use rustc::ty::layout::VariantIdx; -use rustc_data_structures::indexed_vec::Idx; use crate::transform::{MirPass, MirSource}; +use crate::util::expand_aggregate; pub struct Deaggregator; @@ -31,7 +30,7 @@ impl MirPass for Deaggregator { let stmt = stmt.replace_nop(); let source_info = stmt.source_info; - let (mut lhs, kind, operands) = match stmt.kind { + let (lhs, kind, operands) = match stmt.kind { StatementKind::Assign(lhs, box rvalue) => { match rvalue { Rvalue::Aggregate(kind, operands) => (lhs, kind, operands), @@ -41,62 +40,15 @@ impl MirPass for Deaggregator { _ => bug!() }; - let mut set_discriminant = None; - let active_field_index = match *kind { - AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => { - if adt_def.is_enum() { - set_discriminant = Some(Statement { - kind: StatementKind::SetDiscriminant { - place: lhs.clone(), - variant_index, - }, - source_info, - }); - lhs = lhs.downcast(adt_def, variant_index); - } - active_field_index - } - AggregateKind::Generator(..) => { - // Right now we only support initializing generators to - // variant 0 (Unresumed). - let variant_index = VariantIdx::new(0); - set_discriminant = Some(Statement { - kind: StatementKind::SetDiscriminant { - place: lhs.clone(), - variant_index, - }, - source_info, - }); - - // Operands are upvars stored on the base place, so no - // downcast is necessary. - - None - } - _ => None - }; - - Some(operands.into_iter().enumerate().map(move |(i, op)| { - let lhs_field = if let AggregateKind::Array(_) = *kind { - // FIXME(eddyb) `offset` should be u64. - let offset = i as u32; - assert_eq!(offset as usize, i); - lhs.clone().elem(ProjectionElem::ConstantIndex { - offset, - // FIXME(eddyb) `min_length` doesn't appear to be used. - min_length: offset + 1, - from_end: false - }) - } else { + Some(expand_aggregate( + lhs, + operands.into_iter().map(|op| { let ty = op.ty(local_decls, tcx); - let field = Field::new(active_field_index.unwrap_or(i)); - lhs.clone().field(field, ty) - }; - Statement { - source_info, - kind: StatementKind::Assign(lhs_field, box Rvalue::Use(op)), - } - }).chain(set_discriminant)) + (op, ty) + }), + *kind, + source_info, + )) }); } } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 82193d98655d6..cc8aaa1c97fb3 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -1,4 +1,4 @@ -use crate::build; +use crate::{build, shim}; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::mir::{Body, MirPhase, Promoted}; use rustc::ty::{TyCtxt, InstanceDef}; @@ -228,7 +228,15 @@ fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx } fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Body<'tcx> { - // `mir_borrowck` uses `mir_validated`, so we have to force it to + if tcx.is_constructor(def_id) { + // There's no reason to run all of the MIR passes on constructors when + // we can just output the MIR we want directly. This also saves const + // qualification and borrow checking the trouble of special casing + // constructors. + return shim::build_adt_ctor(tcx, def_id); + } + + // (Mir-)Borrowck uses `mir_validated`, so we have to force it to // execute before we can steal. tcx.ensure().mir_borrowck(def_id); diff --git a/src/librustc_mir/util/aggregate.rs b/src/librustc_mir/util/aggregate.rs new file mode 100644 index 0000000000000..98e70671ab715 --- /dev/null +++ b/src/librustc_mir/util/aggregate.rs @@ -0,0 +1,76 @@ +use rustc::mir::*; +use rustc::ty::Ty; +use rustc::ty::layout::VariantIdx; +use rustc_data_structures::indexed_vec::Idx; + +use std::iter::TrustedLen; + +/// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields. +/// +/// Produces something like +/// +/// (lhs as Variant).field0 = arg0; // We only have a downcast if this is an enum +/// (lhs as Variant).field1 = arg1; +/// discriminant(lhs) = variant_index; // If lhs is an enum or generator. +pub fn expand_aggregate<'tcx>( + mut lhs: Place<'tcx>, + operands: impl Iterator, Ty<'tcx>)> + TrustedLen, + kind: AggregateKind<'tcx>, + source_info: SourceInfo, +) -> impl Iterator> + TrustedLen { + let mut set_discriminant = None; + let active_field_index = match kind { + AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => { + if adt_def.is_enum() { + set_discriminant = Some(Statement { + kind: StatementKind::SetDiscriminant { + place: lhs.clone(), + variant_index, + }, + source_info, + }); + lhs = lhs.downcast(adt_def, variant_index); + } + active_field_index + } + AggregateKind::Generator(..) => { + // Right now we only support initializing generators to + // variant 0 (Unresumed). + let variant_index = VariantIdx::new(0); + set_discriminant = Some(Statement { + kind: StatementKind::SetDiscriminant { + place: lhs.clone(), + variant_index, + }, + source_info, + }); + + // Operands are upvars stored on the base place, so no + // downcast is necessary. + + None + } + _ => None + }; + + operands.into_iter().enumerate().map(move |(i, (op, ty))| { + let lhs_field = if let AggregateKind::Array(_) = kind { + // FIXME(eddyb) `offset` should be u64. + let offset = i as u32; + assert_eq!(offset as usize, i); + lhs.clone().elem(ProjectionElem::ConstantIndex { + offset, + // FIXME(eddyb) `min_length` doesn't appear to be used. + min_length: offset + 1, + from_end: false + }) + } else { + let field = Field::new(active_field_index.unwrap_or(i)); + lhs.clone().field(field, ty) + }; + Statement { + source_info, + kind: StatementKind::Assign(lhs_field, box Rvalue::Use(op)), + } + }).chain(set_discriminant) +} diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index 0e7f473a3e70d..e340029434d81 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -2,6 +2,7 @@ use core::unicode::property::Pattern_White_Space; use rustc::ty::TyCtxt; use syntax_pos::Span; +pub mod aggregate; pub mod borrowck_errors; pub mod elaborate_drops; pub mod def_use; @@ -13,6 +14,7 @@ pub(crate) mod pretty; pub mod liveness; pub mod collect_writes; +pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz}; diff --git a/src/test/mir-opt/unusual-item-types.rs b/src/test/mir-opt/unusual-item-types.rs index 67a55101d829f..f4d848dfc7ad1 100644 --- a/src/test/mir-opt/unusual-item-types.rs +++ b/src/test/mir-opt/unusual-item-types.rs @@ -78,7 +78,8 @@ fn main() { // let mut _0: Test; // // bb0: { -// _0 = Test::X(move _1,); +// ((_0 as X).0: usize) = move _1; +// discriminant(_0) = 0; // return; // } // }