From 0af3775dd2c93cdaf8902f83eb21037e474e058f Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 7 Feb 2017 22:46:21 +0100 Subject: [PATCH 01/11] translate tuple-variant constructors using MIR --- src/librustc/ty/mod.rs | 13 +++- src/librustc_metadata/encoder.rs | 4 +- src/librustc_mir/lib.rs | 2 + src/librustc_mir/mir_map.rs | 63 ++++++++++++++++++ src/librustc_mir/shim.rs | 109 +++++++++++++++++++++++++++++++ src/librustc_trans/base.rs | 78 ++-------------------- src/librustc_trans/callee.rs | 33 ---------- src/librustc_trans/collector.rs | 19 ++---- src/librustc_trans/common.rs | 10 ++- src/librustc_trans/mir/block.rs | 6 +- src/librustc_trans/trans_item.rs | 16 +++-- 11 files changed, 215 insertions(+), 138 deletions(-) create mode 100644 src/librustc_mir/shim.rs diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3c37c7353d683..360fa24bf3687 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1264,10 +1264,17 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> { def_id, ROOT_CODE_EXTENT) } - _ => { + Some(hir_map::NodeStructCtor(..)) | + Some(hir_map::NodeVariant(..)) => { + let def_id = tcx.hir.local_def_id(id); + tcx.construct_parameter_environment(tcx.hir.span(id), + def_id, + ROOT_CODE_EXTENT) + } + it => { bug!("ParameterEnvironment::from_item(): \ - `{}` is not an item", - tcx.hir.node_to_string(id)) + `{}` = {:?} is unsupported", + tcx.hir.node_to_string(id), it) } } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 0c31e30671dc2..044ed529ef74c 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -293,7 +293,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { predicates: Some(self.encode_predicates(def_id)), ast: None, - mir: None, + mir: self.encode_mir(def_id), } } @@ -426,7 +426,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { predicates: Some(self.encode_predicates(def_id)), ast: None, - mir: None, + mir: self.encode_mir(def_id), } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index f21f1881c832e..19028bfa531be 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(associated_consts)] #![feature(box_patterns)] +#![feature(box_syntax)] #![feature(i128_type)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] @@ -50,6 +51,7 @@ pub mod callgraph; pub mod def_use; pub mod graphviz; mod hair; +mod shim; pub mod mir_map; pub mod pretty; pub mod transform; diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 58f23a5c81bd7..3fa7131a2b6b0 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,6 +22,7 @@ use rustc::dep_graph::DepNode; use rustc::mir::Mir; use rustc::mir::transform::MirSource; use rustc::mir::visit::MutVisitor; +use shim; use pretty; use hair::cx::Cx; @@ -30,6 +31,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::subst::Substs; use rustc::hir; +use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use syntax::abi::Abi; use syntax::ast; use syntax_pos::Span; @@ -44,6 +46,31 @@ pub fn build_mir_for_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.visit_all_bodies_in_krate(|body_owner_def_id, _body_id| { tcx.item_mir(body_owner_def_id); }); + + // Tuple struct/variant constructors don't have a BodyId, so we need + // to build them separately. + struct GatherCtors<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx> + } + impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> { + fn visit_variant_data(&mut self, + v: &'tcx hir::VariantData, + _: ast::Name, + _: &'tcx hir::Generics, + _: ast::NodeId, + _: Span) { + if let hir::VariantData::Tuple(_, node_id) = *v { + self.tcx.item_mir(self.tcx.hir.local_def_id(node_id)); + } + intravisit::walk_struct_def(self, v) + } + fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, 'tcx> { + NestedVisitorMap::None + } + } + tcx.visit_all_item_likes_in_krate(DepNode::Mir, &mut GatherCtors { + tcx: tcx + }.as_deep_visitor()); } } @@ -95,6 +122,10 @@ fn build_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) _ => hir::BodyId { node_id: expr.id } } } + hir::map::NodeVariant(variant) => + return create_constructor_shim(tcx, id, &variant.node.data), + hir::map::NodeStructCtor(ctor) => + return create_constructor_shim(tcx, id, ctor), _ => unsupported() }; @@ -180,6 +211,38 @@ impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> { } } +fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + ctor_id: ast::NodeId, + v: &'tcx hir::VariantData) + -> &'tcx RefCell> +{ + let span = tcx.hir.span(ctor_id); + if let hir::VariantData::Tuple(ref fields, ctor_id) = *v { + let pe = ty::ParameterEnvironment::for_item(tcx, ctor_id); + tcx.infer_ctxt(pe, Reveal::UserFacing).enter(|infcx| { + let (mut mir, src) = + shim::build_adt_ctor(&infcx, ctor_id, fields, span); + + // Convert the Mir to global types. + let tcx = infcx.tcx.global_tcx(); + let mut globalizer = GlobalizeMir { + tcx: tcx, + span: mir.span + }; + globalizer.visit_mir(&mut mir); + let mir = unsafe { + mem::transmute::>(mir) + }; + + pretty::dump_mir(tcx, "mir_map", &0, src, &mir); + + tcx.alloc_mir(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/shim.rs b/src/librustc_mir/shim.rs new file mode 100644 index 0000000000000..3705a317715f4 --- /dev/null +++ b/src/librustc_mir/shim.rs @@ -0,0 +1,109 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir; +use rustc::infer; +use rustc::mir::*; +use rustc::mir::transform::MirSource; +use rustc::ty; + +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; + +use syntax::ast; +use syntax_pos::Span; + +use std::iter; + +fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) + -> IndexVec> +{ + iter::once(LocalDecl { + mutability: Mutability::Mut, + ty: sig.output(), + name: None, + source_info: None + }).chain(sig.inputs().iter().map(|ity| LocalDecl { + mutability: Mutability::Not, + ty: *ity, + name: None, + source_info: None, + })).collect() +} + +pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, + ctor_id: ast::NodeId, + fields: &[hir::StructField], + span: Span) + -> (Mir<'tcx>, MirSource) +{ + let tcx = infcx.tcx; + let def_id = tcx.hir.local_def_id(ctor_id); + let sig = match tcx.item_type(def_id).sty { + ty::TyFnDef(_, _, fty) => tcx.no_late_bound_regions(&fty) + .expect("LBR in ADT constructor signature"), + _ => bug!("unexpected type for ctor {:?}", def_id) + }; + let sig = tcx.erase_regions(&sig); + + let (adt_def, substs) = match sig.output().sty { + ty::TyAdt(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); + + let local_decls = local_decls_for_sig(&sig); + + let source_info = SourceInfo { + span: span, + scope: ARGUMENT_VISIBILITY_SCOPE + }; + + let variant_no = if adt_def.is_enum() { + adt_def.variant_index_with_id(def_id) + } else { + 0 + }; + + // return = ADT(arg0, arg1, ...); return + let start_block = BasicBlockData { + statements: vec![Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Aggregate( + AggregateKind::Adt(adt_def, variant_no, substs, None), + (1..sig.inputs().len()+1).map(|i| { + Operand::Consume(Lvalue::Local(Local::new(i))) + }).collect() + ) + ) + }], + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Return, + }), + is_cleanup: false + }; + + let mir = Mir::new( + IndexVec::from_elem_n(start_block, 1), + IndexVec::from_elem_n( + VisibilityScopeData { span: span, parent_scope: None }, 1 + ), + IndexVec::new(), + sig.output(), + local_decls, + sig.inputs().len(), + vec![], + span + ); + (mir, MirSource::Fn(ctor_id)) +} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 1b43491e73c8f..fe2b21895cce6 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -34,10 +34,8 @@ use back::linker::LinkerInfo; use back::symbol_export::{self, ExportedSymbols}; use llvm::{Linkage, ValueRef, Vector, get_param}; use llvm; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::def_id::LOCAL_CRATE; use middle::lang_items::StartFnLangItem; -use rustc::ty::subst::Substs; -use rustc::mir::tcx::LvalueTy; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; @@ -47,9 +45,8 @@ use rustc::util::common::time; use session::config::{self, NoDebugInfo}; use rustc_incremental::IncrementalHashesMap; use session::{self, DataTypeKind, Session}; -use abi::{self, FnType}; +use abi; use mir::lvalue::LvalueRef; -use adt; use attributes; use builder::Builder; use callee::{Callee}; @@ -65,7 +62,7 @@ use context::{SharedCrateContext, CrateContextList}; use debuginfo; use declare; use machine; -use machine::{llalign_of_min, llsize_of}; +use machine::llsize_of; use meth; use mir; use monomorphize::{self, Instance}; @@ -76,7 +73,6 @@ use trans_item::{TransItem, DefPathBasedNames}; use type_::Type; use type_of; use value::Value; -use Disr; use util::nodemap::{NodeSet, FxHashMap, FxHashSet}; use libc::c_uint; @@ -615,72 +611,6 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance mir::trans_mir(ccx, lldecl, &mir, instance, sig); } -pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - disr: Disr, - llfn: ValueRef) { - attributes::inline(llfn, attributes::InlineAttr::Hint); - attributes::set_frame_pointer_elimination(ccx, llfn); - - let ctor_ty = common::def_ty(ccx.shared(), def_id, substs); - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&ctor_ty.fn_sig()); - let fn_ty = FnType::new(ccx, sig, &[]); - - let bcx = Builder::new_block(ccx, llfn, "entry-block"); - if !fn_ty.ret.is_ignore() { - // But if there are no nested returns, we skip the indirection - // and have a single retslot - let dest = if fn_ty.ret.is_indirect() { - get_param(llfn, 0) - } else { - // We create an alloca to hold a pointer of type `ret.original_ty` - // which will hold the pointer to the right alloca which has the - // final ret value - bcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot") - }; - // Can return unsized value - let mut dest_val = LvalueRef::new_sized_ty(dest, sig.output(), Alignment::AbiAligned); - dest_val.ty = LvalueTy::Downcast { - adt_def: sig.output().ty_adt_def().unwrap(), - substs: substs, - variant_index: disr.0 as usize, - }; - let mut llarg_idx = fn_ty.ret.is_indirect() as usize; - let mut arg_idx = 0; - for (i, arg_ty) in sig.inputs().iter().enumerate() { - let (lldestptr, _) = dest_val.trans_field_ptr(&bcx, i); - let arg = &fn_ty.args[arg_idx]; - arg_idx += 1; - if common::type_is_fat_ptr(bcx.ccx, arg_ty) { - let meta = &fn_ty.args[arg_idx]; - arg_idx += 1; - arg.store_fn_arg(&bcx, &mut llarg_idx, get_dataptr(&bcx, lldestptr)); - meta.store_fn_arg(&bcx, &mut llarg_idx, get_meta(&bcx, lldestptr)); - } else { - arg.store_fn_arg(&bcx, &mut llarg_idx, lldestptr); - } - } - adt::trans_set_discr(&bcx, sig.output(), dest, disr); - - if fn_ty.ret.is_indirect() { - bcx.ret_void(); - return; - } - - if let Some(cast_ty) = fn_ty.ret.cast { - bcx.ret(bcx.load( - bcx.pointercast(dest, cast_ty.ptr_to()), - Some(llalign_of_min(ccx, fn_ty.ret.ty)) - )); - } else { - bcx.ret(bcx.load(dest, None)) - } - } else { - bcx.ret_void(); - } -} - pub fn llvm_linkage_by_name(name: &str) -> Option { // Use the names from src/llvm/docs/LangRef.rst here. Most types are only // applicable to variable declarations and may not really make sense for @@ -721,7 +651,7 @@ pub fn set_link_section(ccx: &CrateContext, } /// Create the `main` function which will initialise the rust runtime and call -/// users’ main function. +/// users main function. pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { let (main_def_id, span) = match *ccx.sess().entry_fn.borrow() { Some((id, span)) => { diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 762aaf1ce1d1b..a5b42a973cf27 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -22,7 +22,6 @@ use rustc::ty::subst::{Substs, Subst}; use rustc::traits; use abi::{Abi, FnType}; use attributes; -use base; use builder::Builder; use common::{self, CrateContext}; use cleanup::CleanupScope; @@ -35,7 +34,6 @@ use meth; use monomorphize::Instance; use trans_item::TransItem; use type_of; -use Disr; use rustc::ty::{self, Ty, TypeFoldable}; use rustc::hir; use std::iter; @@ -46,9 +44,6 @@ use mir::lvalue::Alignment; #[derive(Debug)] pub enum CalleeData { - /// Constructor for enum variant/tuple-like-struct. - NamedTupleConstructor(Disr), - /// Function pointer. Fn(ValueRef), @@ -92,16 +87,6 @@ impl<'tcx> Callee<'tcx> { } } - // FIXME(eddyb) Detect ADT constructors more efficiently. - if let Some(adt_def) = fn_ty.fn_ret().skip_binder().ty_adt_def() { - if let Some(i) = adt_def.variants.iter().position(|v| def_id == v.did) { - return Callee { - data: NamedTupleConstructor(Disr::for_variant(tcx, adt_def, i)), - ty: fn_ty - }; - } - } - let (llfn, ty) = get_fn(ccx, def_id, substs); Callee::ptr(llfn, ty) } @@ -185,24 +170,6 @@ impl<'tcx> Callee<'tcx> { match self.data { Fn(llfn) => llfn, Virtual(_) => meth::trans_object_shim(ccx, self), - NamedTupleConstructor(disr) => match self.ty.sty { - ty::TyFnDef(def_id, substs, _) => { - let instance = Instance::new(def_id, substs); - if let Some(&llfn) = ccx.instances().borrow().get(&instance) { - return llfn; - } - - let sym = ccx.symbol_map().get_or_compute(ccx.shared(), - TransItem::Fn(instance)); - assert!(!ccx.codegen_unit().contains_item(&TransItem::Fn(instance))); - let lldecl = declare::define_internal_fn(ccx, &sym, self.ty); - base::trans_ctor_shim(ccx, def_id, substs, disr, lldecl); - ccx.instances().borrow_mut().insert(instance, lldecl); - - lldecl - } - _ => bug!("expected fn item type, found {}", self.ty) - }, Intrinsic => bug!("intrinsic {} getting reified", self.ty) } } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 8d1db38999c6b..5e6b10f826ce0 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -630,14 +630,15 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { -> bool { match tcx.item_type(def_id).sty { ty::TyFnDef(def_id, _, _) => { - // Some constructors also have type TyFnDef but they are - // always instantiated inline and don't result in a - // translation item. Same for FFI functions. + // foreign items are linked from another library, not + // translated locally. if let Some(hir_map::NodeForeignItem(_)) = tcx.hir.get_if_local(def_id) { return false; } } - ty::TyClosure(..) => {} + ty::TyClosure(..) => { + // TODO: trans items for closures + } _ => return false } @@ -697,16 +698,6 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { - if let ty::TyFnDef(_, _, sig) = tcx.item_type(def_id).sty { - if let Some(adt_def) = sig.output().skip_binder().ty_adt_def() { - if adt_def.variants.iter().any(|v| def_id == v.did) { - // HACK: ADT constructors are translated in-place and - // do not have a trans-item. - return false; - } - } - } - if def_id.is_local() { true } else { diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index a509587f80fd0..0e536d58a56fb 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -37,6 +37,7 @@ use libc::{c_uint, c_char}; use std::iter; use syntax::ast; +use syntax::attr; use syntax::symbol::InternedString; use syntax_pos::Span; @@ -601,8 +602,13 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn is_closure(tcx: TyCtxt, def_id: DefId) -> bool { - tcx.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr +pub fn requests_inline(tcx: TyCtxt, def_id: DefId) -> bool { + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(..) | + DefPathData::ClosureExpr => true, + _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), + } } /// Given a DefId and some Substs, produces the monomorphic item type. diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 9d40419d338b8..2f1a2c9134c39 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -16,7 +16,7 @@ use rustc::ty::{self, layout, TypeFoldable}; use rustc::mir; use abi::{Abi, FnType, ArgType}; use base::{self, Lifetime}; -use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; +use callee::{Callee, CalleeData, Fn, Intrinsic, Virtual}; use builder::Builder; use common::{self, Funclet}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; @@ -491,10 +491,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } let fn_ptr = match callee.data { - NamedTupleConstructor(_) => { - // FIXME translate this like mir::Rvalue::Aggregate. - callee.reify(bcx.ccx) - } Intrinsic => { use intrinsic::trans_intrinsic_call; diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index d19f04b9554fb..5ec9c2a59957d 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -26,6 +26,7 @@ use monomorphize::Instance; use rustc::dep_graph::DepNode; use rustc::hir; use rustc::hir::def_id::DefId; +use rustc::hir::map::definitions::DefPathData; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::subst::Substs; use rustc_const_eval::fatal_const_eval_err; @@ -178,9 +179,14 @@ impl<'a, 'tcx> TransItem<'tcx> { llvm::SetUniqueComdat(ccx.llmod(), lldecl); } - if let ty::TyClosure(..) = mono_ty.sty { - // set an inline hint for all closures - attributes::inline(lldecl, attributes::InlineAttr::Hint); + debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); + match ccx.tcx().def_key(instance.def).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(..) | + DefPathData::ClosureExpr => { + attributes::inline(lldecl, attributes::InlineAttr::Hint); + } + _ => {} } attributes::from_fn_attrs(ccx, &attrs, lldecl); @@ -252,8 +258,8 @@ impl<'a, 'tcx> TransItem<'tcx> { match *self { TransItem::Fn(ref instance) => { if self.explicit_linkage(tcx).is_none() && - (common::is_closure(tcx, instance.def) || - attr::requests_inline(&tcx.get_attrs(instance.def)[..])) { + common::requests_inline(tcx, instance.def) + { InstantiationMode::LocalCopy } else { InstantiationMode::GloballyShared From ffee9566bbd7728e6411e6094105d6905373255d Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Feb 2017 18:31:03 +0100 Subject: [PATCH 02/11] move Instance to rustc and use it in the collector --- src/librustc/dep_graph/dep_node.rs | 5 + src/librustc/ty/instance.rs | 105 ++++++++ src/librustc/ty/maps.rs | 45 +++- src/librustc/ty/mod.rs | 13 + src/librustc/ty/util.rs | 12 + src/librustc_mir/lib.rs | 1 + src/librustc_mir/shim.rs | 17 ++ src/librustc_trans/back/symbol_export.rs | 7 +- src/librustc_trans/back/symbol_names.rs | 160 ++++++------ src/librustc_trans/base.rs | 18 +- src/librustc_trans/callee.rs | 46 ++-- src/librustc_trans/collector.rs | 300 +++++++++-------------- src/librustc_trans/common.rs | 30 ++- src/librustc_trans/consts.rs | 13 +- src/librustc_trans/context.rs | 12 +- src/librustc_trans/debuginfo/mod.rs | 16 +- src/librustc_trans/meth.rs | 3 +- src/librustc_trans/mir/constant.rs | 30 ++- src/librustc_trans/mir/rvalue.rs | 4 +- src/librustc_trans/monomorphize.rs | 59 ++--- src/librustc_trans/partitioning.rs | 20 +- src/librustc_trans/symbol_map.rs | 2 +- src/librustc_trans/symbol_names_test.rs | 6 +- src/librustc_trans/trans_item.rs | 30 +-- 24 files changed, 522 insertions(+), 432 deletions(-) create mode 100644 src/librustc/ty/instance.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 254cae61152b9..399af258e9251 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -89,6 +89,7 @@ pub enum DepNode { // things read/modify that MIR. MirKrate, Mir(D), + MirShim(Vec), BorrowCheckKrate, BorrowCheck(D), @@ -258,6 +259,10 @@ impl DepNode { IntrinsicCheck(ref d) => op(d).map(IntrinsicCheck), MatchCheck(ref d) => op(d).map(MatchCheck), Mir(ref d) => op(d).map(Mir), + MirShim(ref def_ids) => { + let def_ids: Option> = def_ids.iter().map(op).collect(); + def_ids.map(MirShim) + } BorrowCheck(ref d) => op(d).map(BorrowCheck), RvalueCheck(ref d) => op(d).map(RvalueCheck), StabilityCheck(ref d) => op(d).map(StabilityCheck), diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs new file mode 100644 index 0000000000000..fdcfb3ebd3ce5 --- /dev/null +++ b/src/librustc/ty/instance.rs @@ -0,0 +1,105 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dep_graph::DepNode; +use hir::def_id::DefId; +use ty::{self, Ty, TypeFoldable, Substs}; +use util::ppaux; + +use std::borrow::Cow; +use std::fmt; +use syntax::ast; + + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Instance<'tcx> { + pub def: InstanceDef<'tcx>, + pub substs: &'tcx Substs<'tcx>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum InstanceDef<'tcx> { + Item(DefId), + // ::call_* + FnPtrShim(DefId, Ty<'tcx>), +} + +impl<'tcx> InstanceDef<'tcx> { + #[inline] + pub fn def_id(&self) -> DefId { + match *self { + InstanceDef::Item(def_id) | + InstanceDef::FnPtrShim(def_id, _) + => def_id + } + } + + #[inline] + pub fn def_ty<'a>(&self, tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { + tcx.item_type(self.def_id()) + } + + #[inline] + pub fn attrs<'a>(&self, tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> Cow<'tcx, [ast::Attribute]> { + tcx.get_attrs(self.def_id()) + } + + pub(crate) fn dep_node(&self) -> DepNode { + // HACK: def-id binning, project-style; someone replace this with + // real on-demand. + let ty = match self { + &InstanceDef::FnPtrShim(_, ty) => Some(ty), + _ => None + }.into_iter(); + + DepNode::MirShim( + Some(self.def_id()).into_iter().chain( + ty.flat_map(|t| t.walk()).flat_map(|t| match t.sty { + ty::TyAdt(adt_def, _) => Some(adt_def.did), + ty::TyProjection(ref proj) => Some(proj.trait_ref.def_id), + _ => None, + }) + ).collect() + ) + } +} + +impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.def { + InstanceDef::Item(def) => { + ppaux::parameterized(f, self.substs, def, &[]) + } + InstanceDef::FnPtrShim(def, ty) => { + ppaux::parameterized(f, self.substs, def, &[])?; + write!(f, " - shim({:?})", ty) + } + } + } +} + +impl<'a, 'b, 'tcx> Instance<'tcx> { + pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) + -> Instance<'tcx> { + assert!(substs.is_normalized_for_trans() && !substs.has_escaping_regions(), + "substs of instance {:?} not normalized for trans: {:?}", + def_id, substs); + Instance { def: InstanceDef::Item(def_id), substs: substs } + } + + pub fn mono(tcx: ty::TyCtxt<'a, 'tcx, 'b>, def_id: DefId) -> Instance<'tcx> { + Instance::new(def_id, tcx.global_tcx().empty_substs_for_def_id(def_id)) + } + + #[inline] + pub fn def_id(&self) -> DefId { + self.def.def_id() + } +} diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index af05c0c43113b..ac8c38c7d5856 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -9,7 +9,7 @@ // except according to those terms. use dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig}; -use hir::def_id::{CrateNum, DefId}; +use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use middle::const_val::ConstVal; use mir; use ty::{self, Ty, TyCtxt}; @@ -24,6 +24,16 @@ trait Key { fn default_span(&self, tcx: TyCtxt) -> Span; } +impl<'tcx> Key for ty::InstanceDef<'tcx> { + fn map_crate(&self) -> CrateNum { + LOCAL_CRATE + } + + fn default_span(&self, tcx: TyCtxt) -> Span { + tcx.def_span(self.def_id()) + } +} + impl Key for CrateNum { fn map_crate(&self) -> CrateNum { *self @@ -83,9 +93,9 @@ impl<'tcx> Value<'tcx> for Ty<'tcx> { } } -pub struct CycleError<'a> { +pub struct CycleError<'a, 'tcx: 'a> { span: Span, - cycle: RefMut<'a, [(Span, Query)]>, + cycle: RefMut<'a, [(Span, Query<'tcx>)]>, } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { @@ -110,8 +120,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { err.emit(); } - fn cycle_check(self, span: Span, query: Query, compute: F) - -> Result> + fn cycle_check(self, span: Span, query: Query<'gcx>, compute: F) + -> Result> where F: FnOnce() -> R { { @@ -172,13 +182,20 @@ impl<'tcx> QueryDescription for queries::coherent_inherent_impls<'tcx> { } } +impl<'tcx> QueryDescription for queries::mir_shims<'tcx> { + fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String { + format!("generating MIR shim for `{}`", + tcx.item_path_str(def.def_id())) + } +} + macro_rules! define_maps { (<$tcx:tt> $($(#[$attr:meta])* pub $name:ident: $node:ident($K:ty) -> $V:ty),*) => { pub struct Maps<$tcx> { providers: IndexVec>, - query_stack: RefCell>, + query_stack: RefCell)>>, $($(#[$attr])* pub $name: RefCell>>),* } @@ -196,11 +213,11 @@ macro_rules! define_maps { #[allow(bad_style)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub enum Query { + pub enum Query<$tcx> { $($(#[$attr])* $name($K)),* } - impl Query { + impl<$tcx> Query<$tcx> { pub fn describe(&self, tcx: TyCtxt) -> String { match *self { $(Query::$name(key) => queries::$name::describe(tcx, key)),* @@ -233,7 +250,7 @@ macro_rules! define_maps { mut span: Span, key: $K, f: F) - -> Result> + -> Result> where F: FnOnce(&$V) -> R { if let Some(result) = tcx.maps.$name.borrow().get(&key) { @@ -256,7 +273,7 @@ macro_rules! define_maps { } pub fn try_get(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K) - -> Result<$V, CycleError<'a>> { + -> Result<$V, CycleError<'a, $tcx>> { Self::try_get_with(tcx, span, key, Clone::clone) } @@ -387,7 +404,9 @@ define_maps! { <'tcx> /// Results of evaluating monomorphic constants embedded in /// other items, such as enum variant explicit discriminants. - pub monomorphic_const_eval: MonomorphicConstEval(DefId) -> Result, ()> + pub monomorphic_const_eval: MonomorphicConstEval(DefId) -> Result, ()>, + + pub mir_shims: mir_shim(ty::InstanceDef<'tcx>) -> &'tcx RefCell> } fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode { @@ -397,3 +416,7 @@ fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode { fn coherent_inherent_impls_dep_node(_: CrateNum) -> DepNode { DepNode::Coherence } + +fn mir_shim(instance: ty::InstanceDef) -> DepNode { + instance.dep_node() +} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 360fa24bf3687..c4192ffc697ce 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -73,6 +73,8 @@ pub use self::contents::TypeContents; pub use self::context::{TyCtxt, GlobalArenas, tls}; pub use self::context::{Lift, TypeckTables}; +pub use self::instance::{Instance, InstanceDef}; + pub use self::trait_def::{TraitDef, TraitFlags}; pub use self::maps::queries; @@ -98,6 +100,7 @@ pub mod util; mod contents; mod context; mod flags; +mod instance; mod structural_impls; mod sty; @@ -2309,6 +2312,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { queries::mir::get(self, DUMMY_SP, did).borrow() } + /// Return the possibly-auto-generated MIR of a (DefId, Subst) pair. + pub fn instance_mir(self, instance: ty::InstanceDef<'gcx>) + -> Ref<'gcx, Mir<'gcx>> + { + match instance { + ty::InstanceDef::Item(did) if true => self.item_mir(did), + _ => queries::mir_shims::get(self, DUMMY_SP, instance).borrow(), + } + } + /// Given the DefId of an item, returns its MIR, borrowed immutably. /// Returns None if there is no MIR for the DefId pub fn maybe_item_mir(self, did: DefId) -> Option>> { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index fd95724990941..2344305fa9a7e 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -398,6 +398,18 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } def_id } + + /// Given the def-id of some item that has no type parameters, make + /// a suitable "empty substs" for it. + pub fn empty_substs_for_def_id(self, item_def_id: DefId) -> &'tcx ty::Substs<'tcx> { + ty::Substs::for_item(self, item_def_id, + |_, _| self.mk_region(ty::ReErased), + |_, _| { + bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) + }) + } + + } pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 19028bfa531be..9e6b77dbabdef 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -60,5 +60,6 @@ use rustc::ty::maps::Providers; pub fn provide(providers: &mut Providers) { mir_map::provide(providers); + shim::provide(providers); transform::qualify_consts::provide(providers); } \ No newline at end of file diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 3705a317715f4..0fbd488ffa360 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -13,14 +13,31 @@ use rustc::infer; use rustc::mir::*; use rustc::mir::transform::MirSource; use rustc::ty; +use rustc::ty::maps::Providers; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax::ast; use syntax_pos::Span; +use std::cell::RefCell; use std::iter; +pub fn provide(providers: &mut Providers) { + providers.mir_shims = make_shim; +} + +fn make_shim<'a, 'tcx>(_tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + instance: ty::InstanceDef<'tcx>) + -> &'tcx RefCell> +{ + match instance { + ty::InstanceDef::Item(..) => + bug!("item {:?} passed to make_shim", instance), + ty::InstanceDef::FnPtrShim(..) => unimplemented!() + } +} + fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) -> IndexVec> { diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs index bea3ca8df70e0..005fb3533ab0b 100644 --- a/src/librustc_trans/back/symbol_export.rs +++ b/src/librustc_trans/back/symbol_export.rs @@ -11,6 +11,7 @@ use context::SharedCrateContext; use monomorphize::Instance; use symbol_map::SymbolMap; +use back::symbol_names::symbol_name; use util::nodemap::FxHashMap; use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; use rustc::session::config; @@ -106,7 +107,7 @@ impl ExportedSymbols { .exported_symbols(cnum) .iter() .map(|&def_id| { - let name = Instance::mono(scx, def_id).symbol_name(scx); + let name = symbol_name(Instance::mono(scx.tcx(), def_id), scx); let export_level = if special_runtime_crate { // We can probably do better here by just ensuring that // it has hidden visibility rather than public @@ -218,9 +219,9 @@ fn symbol_for_def_id<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } - let instance = Instance::mono(scx, def_id); + let instance = Instance::mono(scx.tcx(), def_id); symbol_map.get(TransItem::Fn(instance)) .map(str::to_owned) - .unwrap_or_else(|| instance.symbol_name(scx)) + .unwrap_or_else(|| symbol_name(instance, scx)) } diff --git a/src/librustc_trans/back/symbol_names.rs b/src/librustc_trans/back/symbol_names.rs index fe58bc8f5f28b..518995dfedcc2 100644 --- a/src/librustc_trans/back/symbol_names.rs +++ b/src/librustc_trans/back/symbol_names.rs @@ -168,105 +168,105 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, format!("h{:016x}", hasher.finish()) } -impl<'a, 'tcx> Instance<'tcx> { - pub fn symbol_name(self, scx: &SharedCrateContext<'a, 'tcx>) -> String { - let Instance { def: def_id, substs } = self; +pub fn symbol_name<'a, 'tcx>(instance: Instance<'tcx>, + scx: &SharedCrateContext<'a, 'tcx>) -> String { + let def_id = instance.def_id(); + let substs = instance.substs; - debug!("symbol_name(def_id={:?}, substs={:?})", - def_id, substs); + debug!("symbol_name(def_id={:?}, substs={:?})", + def_id, substs); - let node_id = scx.tcx().hir.as_local_node_id(def_id); + let node_id = scx.tcx().hir.as_local_node_id(def_id); - if let Some(id) = node_id { - if scx.sess().plugin_registrar_fn.get() == Some(id) { - let svh = &scx.link_meta().crate_hash; - let idx = def_id.index; - return scx.sess().generate_plugin_registrar_symbol(svh, idx); - } - if scx.sess().derive_registrar_fn.get() == Some(id) { - let svh = &scx.link_meta().crate_hash; - let idx = def_id.index; - return scx.sess().generate_derive_registrar_symbol(svh, idx); - } + if let Some(id) = node_id { + if scx.sess().plugin_registrar_fn.get() == Some(id) { + let svh = &scx.link_meta().crate_hash; + let idx = def_id.index; + return scx.sess().generate_plugin_registrar_symbol(svh, idx); } - - // FIXME(eddyb) Precompute a custom symbol name based on attributes. - let attrs = scx.tcx().get_attrs(def_id); - let is_foreign = if let Some(id) = node_id { - match scx.tcx().hir.get(id) { - hir_map::NodeForeignItem(_) => true, - _ => false - } - } else { - scx.sess().cstore.is_foreign_item(def_id) - }; - - if let Some(name) = weak_lang_items::link_name(&attrs) { - return name.to_string(); + if scx.sess().derive_registrar_fn.get() == Some(id) { + let svh = &scx.link_meta().crate_hash; + let idx = def_id.index; + return scx.sess().generate_derive_registrar_symbol(svh, idx); } + } - if is_foreign { - if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { - return name.to_string(); - } - // Don't mangle foreign items. - return scx.tcx().item_name(def_id).as_str().to_string(); + // FIXME(eddyb) Precompute a custom symbol name based on attributes. + let attrs = scx.tcx().get_attrs(def_id); + let is_foreign = if let Some(id) = node_id { + match scx.tcx().hir.get(id) { + hir_map::NodeForeignItem(_) => true, + _ => false } + } else { + scx.sess().cstore.is_foreign_item(def_id) + }; - if let Some(name) = attr::find_export_name_attr(scx.sess().diagnostic(), &attrs) { - // Use provided name + if let Some(name) = weak_lang_items::link_name(&attrs) { + return name.to_string(); + } + + if is_foreign { + if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { return name.to_string(); } + // Don't mangle foreign items. + return scx.tcx().item_name(def_id).as_str().to_string(); + } - if attr::contains_name(&attrs, "no_mangle") { - // Don't mangle - return scx.tcx().item_name(def_id).as_str().to_string(); - } + if let Some(name) = attr::find_export_name_attr(scx.sess().diagnostic(), &attrs) { + // Use provided name + return name.to_string(); + } - let def_path = scx.tcx().def_path(def_id); - - // We want to compute the "type" of this item. Unfortunately, some - // kinds of items (e.g., closures) don't have an entry in the - // item-type array. So walk back up the find the closest parent - // that DOES have an entry. - let mut ty_def_id = def_id; - let instance_ty; - loop { - let key = scx.tcx().def_key(ty_def_id); - match key.disambiguated_data.data { - DefPathData::TypeNs(_) | - DefPathData::ValueNs(_) => { - instance_ty = scx.tcx().item_type(ty_def_id); - break; - } - _ => { - // if we're making a symbol for something, there ought - // to be a value or type-def or something in there - // *somewhere* - ty_def_id.index = key.parent.unwrap_or_else(|| { - bug!("finding type for {:?}, encountered def-id {:?} with no \ - parent", def_id, ty_def_id); - }); - } + if attr::contains_name(&attrs, "no_mangle") { + // Don't mangle + return scx.tcx().item_name(def_id).as_str().to_string(); + } + + let def_path = scx.tcx().def_path(def_id); + + // We want to compute the "type" of this item. Unfortunately, some + // kinds of items (e.g., closures) don't have an entry in the + // item-type array. So walk back up the find the closest parent + // that DOES have an entry. + let mut ty_def_id = def_id; + let instance_ty; + loop { + let key = scx.tcx().def_key(ty_def_id); + match key.disambiguated_data.data { + DefPathData::TypeNs(_) | + DefPathData::ValueNs(_) => { + instance_ty = scx.tcx().item_type(ty_def_id); + break; + } + _ => { + // if we're making a symbol for something, there ought + // to be a value or type-def or something in there + // *somewhere* + ty_def_id.index = key.parent.unwrap_or_else(|| { + bug!("finding type for {:?}, encountered def-id {:?} with no \ + parent", def_id, ty_def_id); + }); } } + } - // Erase regions because they may not be deterministic when hashed - // and should not matter anyhow. - let instance_ty = scx.tcx().erase_regions(&instance_ty); + // Erase regions because they may not be deterministic when hashed + // and should not matter anyhow. + let instance_ty = scx.tcx().erase_regions(&instance_ty); - let hash = get_symbol_hash(scx, &def_path, instance_ty, Some(substs)); + let hash = get_symbol_hash(scx, &def_path, instance_ty, Some(substs)); - let mut buffer = SymbolPathBuffer { - names: Vec::with_capacity(def_path.data.len()) - }; + let mut buffer = SymbolPathBuffer { + names: Vec::with_capacity(def_path.data.len()) + }; - item_path::with_forced_absolute_paths(|| { - scx.tcx().push_item_path(&mut buffer, def_id); - }); + item_path::with_forced_absolute_paths(|| { + scx.tcx().push_item_path(&mut buffer, def_id); + }); - mangle(buffer.names.into_iter(), &hash) - } + mangle(buffer.names.into_iter(), &hash) } struct SymbolPathBuffer { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index fe2b21895cce6..ce767468c012b 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -565,11 +565,11 @@ pub fn memcpy_ty<'a, 'tcx>( } pub fn call_memset<'a, 'tcx>(b: &Builder<'a, 'tcx>, - ptr: ValueRef, - fill_byte: ValueRef, - size: ValueRef, - align: ValueRef, - volatile: bool) -> ValueRef { + ptr: ValueRef, + fill_byte: ValueRef, + size: ValueRef, + align: ValueRef, + volatile: bool) -> ValueRef { let ptr_width = &b.ccx.sess().target.target.target_pointer_width[..]; let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); let llintrinsicfn = b.ccx.get_intrinsic(&intrinsic_key); @@ -581,7 +581,7 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance let _s = if ccx.sess().trans_stats() { let mut instance_name = String::new(); DefPathBasedNames::new(ccx.tcx(), true, true) - .push_def_path(instance.def, &mut instance_name); + .push_def_path(instance.def_id(), &mut instance_name); Some(StatRecorder::new(ccx, instance_name)) } else { None @@ -592,7 +592,7 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance // release builds. info!("trans_instance({})", instance); - let fn_ty = common::def_ty(ccx.shared(), instance.def, instance.substs); + let fn_ty = common::instance_ty(ccx.shared(), &instance); let sig = common::ty_fn_sig(ccx, fn_ty); let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); @@ -607,7 +607,7 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance attributes::emit_uwtable(lldecl, true); } - let mir = ccx.tcx().item_mir(instance.def); + let mir = ccx.tcx().instance_mir(instance.def); mir::trans_mir(ccx, lldecl, &mir, instance, sig); } @@ -668,7 +668,7 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { ccx.tcx().sess.span_fatal(span, "compilation successful"); } - let instance = Instance::mono(ccx.shared(), main_def_id); + let instance = Instance::mono(ccx.tcx(), main_def_id); if !ccx.codegen_unit().contains_item(&TransItem::Fn(instance)) { // We want to create the wrapper in the same codegen unit as Rust's main diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index a5b42a973cf27..19fc4e013fae1 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -27,11 +27,12 @@ use common::{self, CrateContext}; use cleanup::CleanupScope; use mir::lvalue::LvalueRef; use consts; -use common::def_ty; +use common::instance_ty; use declare; use value::Value; use meth; use monomorphize::Instance; +use back::symbol_names::symbol_name; use trans_item::TransItem; use type_of; use rustc::ty::{self, Ty, TypeFoldable}; @@ -77,7 +78,8 @@ impl<'tcx> Callee<'tcx> { return Callee::trait_method(ccx, trait_id, def_id, substs); } - let fn_ty = def_ty(ccx.shared(), def_id, substs); + let instance = ty::Instance::new(def_id, substs); + let fn_ty = instance_ty(ccx.shared(), &instance); if let ty::TyFnDef(.., f) = fn_ty.sty { if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { return Callee { @@ -87,7 +89,7 @@ impl<'tcx> Callee<'tcx> { } } - let (llfn, ty) = get_fn(ccx, def_id, substs); + let (llfn, ty) = get_fn(ccx, instance); Callee::ptr(llfn, ty) } @@ -104,13 +106,13 @@ impl<'tcx> Callee<'tcx> { match common::fulfill_obligation(ccx.shared(), DUMMY_SP, trait_ref) { traits::VtableImpl(vtable_impl) => { let name = tcx.item_name(def_id); - let (def_id, substs) = traits::find_method(tcx, name, substs, &vtable_impl); + let instance = common::find_method(tcx, name, substs, &vtable_impl); // Translate the function, bypassing Callee::def. // That is because default methods have the same ID as the // trait method used to look up the impl method that ended // up here, so calling Callee::def would infinitely recurse. - let (llfn, ty) = get_fn(ccx, def_id, substs); + let (llfn, ty) = get_fn(ccx, instance); Callee::ptr(llfn, ty) } traits::VtableClosure(vtable_closure) => { @@ -125,7 +127,7 @@ impl<'tcx> Callee<'tcx> { instance, trait_closure_kind); - let method_ty = def_ty(ccx.shared(), def_id, substs); + let method_ty = instance_ty(ccx.shared(), &instance); Callee::ptr(llfn, method_ty) } traits::VtableFnPointer(vtable_fn_pointer) => { @@ -135,13 +137,13 @@ impl<'tcx> Callee<'tcx> { trait_closure_kind, vtable_fn_pointer.fn_ty); - let method_ty = def_ty(ccx.shared(), def_id, substs); + let method_ty = instance_ty(ccx.shared(), &instance); Callee::ptr(llfn, method_ty) } traits::VtableObject(ref data) => { Callee { data: Virtual(tcx.get_vtable_index_of_object_method(data, def_id)), - ty: def_ty(ccx.shared(), def_id, substs) + ty: instance_ty(ccx.shared(), &Instance::new(def_id, substs)) } } vtable => { @@ -183,7 +185,7 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, -> ValueRef { // If this is a closure, redirect to it. - let (llfn, _) = get_fn(ccx, def_id, substs.substs); + let (llfn, _) = get_fn(ccx, Instance::new(def_id, substs.substs)); // If the closure is a Fn closure, but a FnOnce is needed (etc), // then adapt the self type @@ -292,7 +294,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let llonce_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig)); // Create the by-value helper. - let function_name = method_instance.symbol_name(ccx.shared()); + let function_name = symbol_name(method_instance, ccx.shared()); let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty); attributes::set_frame_pointer_elimination(ccx, lloncefn); @@ -438,7 +440,7 @@ fn trans_fn_pointer_shim<'a, 'tcx>( debug!("tuple_fn_ty: {:?}", tuple_fn_ty); // - let function_name = method_instance.symbol_name(ccx.shared()); + let function_name = symbol_name(method_instance, ccx.shared()); let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty); attributes::set_frame_pointer_elimination(ccx, llfn); // @@ -489,21 +491,17 @@ fn trans_fn_pointer_shim<'a, 'tcx>( /// - `def_id`: def id of the fn or method item being referenced /// - `substs`: values for each of the fn/method's parameters fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>) + instance: Instance<'tcx>) -> (ValueRef, Ty<'tcx>) { let tcx = ccx.tcx(); - debug!("get_fn(def_id={:?}, substs={:?})", def_id, substs); + debug!("get_fn(instance={:?})", instance); - assert!(!substs.needs_infer()); - assert!(!substs.has_escaping_regions()); - assert!(!substs.has_param_types()); - - let substs = tcx.normalize_associated_type(&substs); - let instance = Instance::new(def_id, substs); - let fn_ty = common::def_ty(ccx.shared(), def_id, substs); + assert!(!instance.substs.needs_infer()); + assert!(!instance.substs.has_escaping_regions()); + assert!(!instance.substs.has_param_types()); + let fn_ty = common::instance_ty(ccx.shared(), &instance); if let Some(&llfn) = ccx.instances().borrow().get(&instance) { return (llfn, fn_ty); } @@ -553,7 +551,7 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, assert_eq!(common::val_ty(llfn), llptrty); debug!("get_fn: not casting pointer!"); - let attrs = ccx.tcx().get_attrs(def_id); + let attrs = instance.def.attrs(ccx.tcx()); attributes::from_fn_attrs(ccx, &attrs, llfn); let is_local_def = ccx.shared().translation_items().borrow() @@ -565,7 +563,9 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage); } } - if ccx.use_dll_storage_attrs() && ccx.sess().cstore.is_dllimport_foreign_item(def_id) { + if ccx.use_dll_storage_attrs() && + ccx.sess().cstore.is_dllimport_foreign_item(instance.def_id()) + { unsafe { llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 5e6b10f826ce0..271f91f9adbdb 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -207,7 +207,7 @@ use syntax_pos::DUMMY_SP; use base::custom_coerce_unsize_info; use callee::needs_fn_once_adapter_shim; use context::SharedCrateContext; -use common::{def_ty, fulfill_obligation}; +use common::{def_ty, find_method, instance_ty, fulfill_obligation}; use glue::{self, DropGlueKind}; use monomorphize::{self, Instance}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; @@ -337,21 +337,22 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, } TransItem::Static(node_id) => { let def_id = scx.tcx().hir.local_def_id(node_id); + let instance = Instance::mono(scx.tcx(), def_id); // Sanity check whether this ended up being collected accidentally - debug_assert!(should_trans_locally(scx.tcx(), def_id)); + debug_assert!(should_trans_locally(scx.tcx(), &instance)); - let ty = def_ty(scx, def_id, Substs::empty()); + let ty = instance_ty(scx, &instance); let ty = glue::get_drop_glue_type(scx, ty); neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); recursion_depth_reset = None; - collect_neighbours(scx, Instance::mono(scx, def_id), &mut neighbors); + collect_neighbours(scx, instance, &mut neighbors); } TransItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally - debug_assert!(should_trans_locally(scx.tcx(), instance.def)); + debug_assert!(should_trans_locally(scx.tcx(), &instance)); // Keep track of the monomorphization recursion depth recursion_depth_reset = Some(check_recursion_limit(scx.tcx(), @@ -395,9 +396,8 @@ fn check_recursion_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, recursion_depths: &mut DefIdMap) -> (DefId, usize) { - let recursion_depth = recursion_depths.get(&instance.def) - .map(|x| *x) - .unwrap_or(0); + let def_id = instance.def_id(); + let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0); debug!(" => recursion depth={}", recursion_depth); // Code that needs to instantiate the same function recursively @@ -406,16 +406,16 @@ fn check_recursion_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if recursion_depth > tcx.sess.recursion_limit.get() { let error = format!("reached the recursion limit while instantiating `{}`", instance); - if let Some(node_id) = tcx.hir.as_local_node_id(instance.def) { + if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { tcx.sess.span_fatal(tcx.hir.span(node_id), &error); } else { tcx.sess.fatal(&error); } } - recursion_depths.insert(instance.def, recursion_depth + 1); + recursion_depths.insert(def_id, recursion_depth + 1); - (instance.def, recursion_depth) + (def_id, recursion_depth) } fn check_type_length_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -438,7 +438,7 @@ fn check_type_length_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let instance_name = instance.to_string(); let msg = format!("reached the type-length limit while instantiating `{:.64}...`", instance_name); - let mut diag = if let Some(node_id) = tcx.hir.as_local_node_id(instance.def) { + let mut diag = if let Some(node_id) = tcx.hir.as_local_node_id(instance.def_id()) { tcx.sess.struct_span_fatal(tcx.hir.span(node_id), &msg) } else { tcx.sess.struct_fatal(&msg) @@ -493,33 +493,24 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let source_ty = operand.ty(self.mir, self.scx.tcx()); match source_ty.sty { ty::TyClosure(def_id, substs) => { - let closure_trans_item = - create_fn_trans_item(self.scx, - def_id, - substs.substs, - self.param_substs); - self.output.push(closure_trans_item); + let substs = monomorphize::apply_param_substs( + self.scx, self.param_substs, &substs.substs); + self.output.push(create_fn_trans_item( + Instance::new(def_id, substs) + )); } _ => bug!(), } } mir::Rvalue::Box(..) => { - let exchange_malloc_fn_def_id = - self.scx - .tcx() - .lang_items - .require(ExchangeMallocFnLangItem) - .unwrap_or_else(|e| self.scx.sess().fatal(&e)); - - if should_trans_locally(self.scx.tcx(), exchange_malloc_fn_def_id) { - let empty_substs = self.scx.empty_substs_for_def_id(exchange_malloc_fn_def_id); - let exchange_malloc_fn_trans_item = - create_fn_trans_item(self.scx, - exchange_malloc_fn_def_id, - empty_substs, - self.param_substs); - - self.output.push(exchange_malloc_fn_trans_item); + let tcx = self.scx.tcx(); + let exchange_malloc_fn_def_id = tcx + .lang_items + .require(ExchangeMallocFnLangItem) + .unwrap_or_else(|e| self.scx.sess().fatal(&e)); + let instance = Instance::mono(tcx, exchange_malloc_fn_def_id); + if should_trans_locally(tcx, &instance) { + self.output.push(create_fn_trans_item(instance)); } } _ => { /* not interesting */ } @@ -564,8 +555,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let substs = monomorphize::apply_param_substs(self.scx, self.param_substs, &substs); - - let instance = Instance::new(def_id, substs).resolve_const(self.scx); + let instance = monomorphize::resolve_const(self.scx, def_id, substs); collect_neighbours(self.scx, instance, self.output); } @@ -586,28 +576,24 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { // // Calling do_static_dispatch() here will map the def_id of // `std::cmp::partial_cmp` to the def_id of `i32::partial_cmp` + + let callee_substs = monomorphize::apply_param_substs(self.scx, + self.param_substs, + &callee_substs); let dispatched = do_static_dispatch(self.scx, callee_def_id, - callee_substs, - self.param_substs); + callee_substs); if let StaticDispatchResult::Dispatched { - def_id: callee_def_id, - substs: callee_substs, - fn_once_adjustment, - } = dispatched { + instance, fn_once_adjustment + } = dispatched { // if we have a concrete impl (which we might not have // in the case of something compiler generated like an // object shim or a closure that is handled differently), // we check if the callee is something that will actually // result in a translation item ... - if can_result_in_trans_item(self.scx.tcx(), callee_def_id) { - // ... and create one if it does. - let trans_item = create_fn_trans_item(self.scx, - callee_def_id, - callee_substs, - self.param_substs); - self.output.push(trans_item); + if should_trans_locally(self.scx.tcx(), &instance) { + self.output.push(create_fn_trans_item(instance)); // This call will instantiate an FnOnce adapter, which drops // the closure environment. Therefore we need to make sure @@ -624,26 +610,6 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { } self.super_operand(operand, location); - - fn can_result_in_trans_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - match tcx.item_type(def_id).sty { - ty::TyFnDef(def_id, _, _) => { - // foreign items are linked from another library, not - // translated locally. - if let Some(hir_map::NodeForeignItem(_)) = tcx.hir.get_if_local(def_id) { - return false; - } - } - ty::TyClosure(..) => { - // TODO: trans items for closures - } - _ => return false - } - - should_trans_locally(tcx, def_id) - } } // This takes care of the "drop_in_place" intrinsic for which we otherwise @@ -695,22 +661,30 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { // Returns true if we should translate an instance in the local crate. // Returns false if we can just link to the upstream crate and therefore don't // need a translation item. -fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) +fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>) -> bool { - if def_id.is_local() { - true - } else { - if tcx.sess.cstore.is_exported_symbol(def_id) || - tcx.sess.cstore.is_foreign_item(def_id) { - // We can link to the item in question, no instance needed in this - // crate - false - } else { - if !tcx.sess.cstore.is_item_mir_available(def_id) { - bug!("Cannot create local trans-item for {:?}", def_id) + let def_id = match instance.def { + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::FnPtrShim(..) => return true + }; + match tcx.hir.get_if_local(def_id) { + Some(hir_map::NodeForeignItem(..)) => { + false // foreign items are linked against, not translated. + } + Some(_) => true, + None => { + if tcx.sess.cstore.is_exported_symbol(def_id) || + tcx.sess.cstore.is_foreign_item(def_id) + { + // We can link to the item in question, no instance needed + // in this crate + false + } else { + if !tcx.sess.cstore.is_item_mir_available(def_id) { + bug!("Cannot create local trans-item for {:?}", def_id) + } + true } - true } } } @@ -731,14 +705,14 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // Make sure the BoxFreeFn lang-item gets translated if there is a boxed value. if ty.is_box() { - let def_id = scx.tcx().require_lang_item(BoxFreeFnLangItem); - if should_trans_locally(scx.tcx(), def_id) { - let box_free_fn_trans_item = - create_fn_trans_item(scx, - def_id, - scx.tcx().mk_substs(iter::once(Kind::from(ty.boxed_ty()))), - scx.tcx().intern_substs(&[])); - output.push(box_free_fn_trans_item); + let tcx = scx.tcx(); + let def_id = tcx.require_lang_item(BoxFreeFnLangItem); + let box_free_instance = Instance::new( + def_id, + tcx.mk_substs(iter::once(Kind::from(ty.boxed_ty()))) + ); + if should_trans_locally(tcx, &box_free_instance) { + output.push(create_fn_trans_item(box_free_instance)); } } @@ -768,13 +742,9 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, traits::VtableImpl(data) => data.substs, _ => bug!() }; - - if should_trans_locally(scx.tcx(), destructor.did) { - let trans_item = create_fn_trans_item(scx, - destructor.did, - substs, - scx.tcx().intern_substs(&[])); - output.push(trans_item); + let instance = Instance::new(destructor.did, substs); + if should_trans_locally(scx.tcx(), &instance) { + output.push(create_fn_trans_item(instance)); } // This type has a Drop implementation, we'll need the contents-only @@ -847,72 +817,59 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } +enum StaticDispatchResult<'tcx> { + // The call could be resolved statically as going to the method with + // `instance`. + Dispatched { + instance: Instance<'tcx>, + // If this is a call to a closure that needs an FnOnce adjustment, + // this contains the new self type of the call (= type of the closure + // environment) + fn_once_adjustment: Option>, + }, + // This goes to somewhere that we don't know at compile-time + Unknown +} + fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn_def_id: DefId, - fn_substs: &'tcx Substs<'tcx>, - param_substs: &'tcx Substs<'tcx>) + fn_substs: &'tcx Substs<'tcx>) -> StaticDispatchResult<'tcx> { - debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?}, param_substs={:?})", + debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?})", def_id_to_string(scx.tcx(), fn_def_id), - fn_substs, - param_substs); - + fn_substs); if let Some(trait_def_id) = scx.tcx().trait_of_item(fn_def_id) { debug!(" => trait method, attempting to find impl"); do_static_trait_method_dispatch(scx, &scx.tcx().associated_item(fn_def_id), trait_def_id, - fn_substs, - param_substs) + fn_substs) } else { debug!(" => regular function"); // The function is not part of an impl or trait, no dispatching // to be done StaticDispatchResult::Dispatched { - def_id: fn_def_id, - substs: fn_substs, + instance: Instance::new(fn_def_id, fn_substs), fn_once_adjustment: None, } } } -enum StaticDispatchResult<'tcx> { - // The call could be resolved statically as going to the method with - // `def_id` and `substs`. - Dispatched { - def_id: DefId, - substs: &'tcx Substs<'tcx>, - - // If this is a call to a closure that needs an FnOnce adjustment, - // this contains the new self type of the call (= type of the closure - // environment) - fn_once_adjustment: Option>, - }, - // This goes to somewhere that we don't know at compile-time - Unknown -} - // Given a trait-method and substitution information, find out the actual // implementation of the trait method. fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, trait_method: &ty::AssociatedItem, trait_id: DefId, - callee_substs: &'tcx Substs<'tcx>, - param_substs: &'tcx Substs<'tcx>) + rcvr_substs: &'tcx Substs<'tcx>) -> StaticDispatchResult<'tcx> { let tcx = scx.tcx(); debug!("do_static_trait_method_dispatch(trait_method={}, \ trait_id={}, \ - callee_substs={:?}, \ - param_substs={:?}", + rcvr_substs={:?})", def_id_to_string(scx.tcx(), trait_method.def_id), def_id_to_string(scx.tcx(), trait_id), - callee_substs, - param_substs); + rcvr_substs); - let rcvr_substs = monomorphize::apply_param_substs(scx, - param_substs, - &callee_substs); let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); let vtbl = fulfill_obligation(scx, DUMMY_SP, ty::Binder(trait_ref)); @@ -920,13 +877,8 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // the actual function: match vtbl { traits::VtableImpl(impl_data) => { - let (def_id, substs) = traits::find_method(tcx, - trait_method.name, - rcvr_substs, - &impl_data); StaticDispatchResult::Dispatched { - def_id: def_id, - substs: substs, + instance: find_method(tcx, trait_method.name, rcvr_substs, &impl_data), fn_once_adjustment: None, } } @@ -950,8 +902,7 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, }; StaticDispatchResult::Dispatched { - def_id: closure_def_id, - substs: closure_data.substs.substs, + instance: Instance::new(closure_def_id, closure_data.substs.substs), fn_once_adjustment: fn_once_adjustment, } } @@ -961,7 +912,7 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, if let ty::TyFnDef(def_id, substs, _) = data.fn_ty.sty { // The destination of the pointer might be something that needs // further dispatching, such as a trait method, so we do that. - do_static_dispatch(scx, def_id, substs, param_substs) + do_static_dispatch(scx, def_id, substs) } else { StaticDispatchResult::Unknown } @@ -1066,28 +1017,9 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } -fn create_fn_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - def_id: DefId, - fn_substs: &'tcx Substs<'tcx>, - param_substs: &'tcx Substs<'tcx>) - -> TransItem<'tcx> { - let tcx = scx.tcx(); - - debug!("create_fn_trans_item(def_id={}, fn_substs={:?}, param_substs={:?})", - def_id_to_string(tcx, def_id), - fn_substs, - param_substs); - - // We only get here, if fn_def_id either designates a local item or - // an inlineable external item. Non-inlineable external items are - // ignored because we don't want to generate any code for them. - let concrete_substs = monomorphize::apply_param_substs(scx, - param_substs, - &fn_substs); - assert!(concrete_substs.is_normalized_for_trans(), - "concrete_substs not normalized for trans: {:?}", - concrete_substs); - TransItem::Fn(Instance::new(def_id, concrete_substs)) +fn create_fn_trans_item<'a, 'tcx>(instance: Instance<'tcx>) -> TransItem<'tcx> { + debug!("create_fn_trans_item(instance={})", instance); + TransItem::Fn(instance) } /// Creates a `TransItem` for each method that is referenced by the vtable for @@ -1102,8 +1034,6 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, if let ty::TyDynamic(ref trait_ty, ..) = trait_ty.sty { if let Some(principal) = trait_ty.principal() { let poly_trait_ref = principal.with_self_ty(scx.tcx(), impl_ty); - let param_substs = scx.tcx().intern_substs(&[]); - assert!(!poly_trait_ref.has_escaping_regions()); // Walk all methods of the trait, including those of its supertraits @@ -1111,19 +1041,18 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, let methods = methods.filter_map(|method| method) .filter_map(|(def_id, substs)| { if let StaticDispatchResult::Dispatched { - def_id, - substs, + instance, // We already add the drop-glue for the closure env // unconditionally below. fn_once_adjustment: _ , - } = do_static_dispatch(scx, def_id, substs, param_substs) { - Some((def_id, substs)) + } = do_static_dispatch(scx, def_id, substs) { + Some(instance) } else { None } }) - .filter(|&(def_id, _)| should_trans_locally(scx.tcx(), def_id)) - .map(|(def_id, substs)| create_fn_trans_item(scx, def_id, substs, param_substs)); + .filter(|&instance| should_trans_locally(scx.tcx(), &instance)) + .map(|instance| create_fn_trans_item(instance)); output.extend(methods); } // Also add the destructor @@ -1195,7 +1124,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { debug!("RootCollector: ItemFn({})", def_id_to_string(self.scx.tcx(), def_id)); - let instance = Instance::mono(self.scx, def_id); + let instance = Instance::mono(self.scx.tcx(), def_id); self.output.push(TransItem::Fn(instance)); } } @@ -1233,7 +1162,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { debug!("RootCollector: MethodImplItem({})", def_id_to_string(self.scx.tcx(), def_id)); - let instance = Instance::mono(self.scx, def_id); + let instance = Instance::mono(self.scx.tcx(), def_id); self.output.push(TransItem::Fn(instance)); } } @@ -1278,31 +1207,22 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(scx: &SharedCrateContext<'a, ' // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. - let impl_substs = Substs::for_item(tcx, impl_def_id, - |_, _| tcx.mk_region(ty::ReErased), - |_, _| tcx.types.err); + let impl_substs = tcx.empty_substs_for_def_id(impl_def_id); let impl_data = traits::VtableImplData { impl_def_id: impl_def_id, substs: impl_substs, nested: vec![] }; - let (def_id, substs) = traits::find_method(tcx, - method.name, - callee_substs, - &impl_data); + let instance = find_method(tcx, method.name, callee_substs, &impl_data); - let predicates = tcx.item_predicates(def_id).predicates - .subst(tcx, substs); + let predicates = tcx.item_predicates(instance.def_id()).predicates + .subst(tcx, impl_substs); if !traits::normalize_and_test_predicates(tcx, predicates) { continue; } - if should_trans_locally(tcx, method.def_id) { - let item = create_fn_trans_item(scx, - method.def_id, - callee_substs, - tcx.erase_regions(&substs)); - output.push(item); + if should_trans_locally(tcx, &instance) { + output.push(create_fn_trans_item(instance)); } } } @@ -1318,7 +1238,7 @@ fn collect_neighbours<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, instance: Instance<'tcx>, output: &mut Vec>) { - let mir = scx.tcx().item_mir(instance.def); + let mir = scx.tcx().instance_mir(instance.def); let mut visitor = MirNeighborCollector { scx: scx, diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 0e536d58a56fb..4389207cdf294 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -602,7 +602,14 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn requests_inline(tcx: TyCtxt, def_id: DefId) -> bool { +pub fn requests_inline<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx> +) -> bool { + let def_id = match instance.def { + ty::InstanceDef::Item(def_id) => def_id, + _ => return true + }; match tcx.def_key(def_id).disambiguated_data.data { DefPathData::StructCtor | DefPathData::EnumVariant(..) | @@ -610,7 +617,6 @@ pub fn requests_inline(tcx: TyCtxt, def_id: DefId) -> bool { _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), } } - /// Given a DefId and some Substs, produces the monomorphic item type. pub fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, def_id: DefId, @@ -620,3 +626,23 @@ pub fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, let ty = shared.tcx().item_type(def_id); monomorphize::apply_param_substs(shared, substs, &ty) } + +/// Return the substituted type of an instance. +pub fn instance_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, + instance: &ty::Instance<'tcx>) + -> Ty<'tcx> +{ + let ty = instance.def.def_ty(shared.tcx()); + monomorphize::apply_param_substs(shared, instance.substs, &ty) +} + +pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + name: ast::Name, + substs: &'tcx Substs<'tcx>, + impl_data: &traits::VtableImplData<'tcx, ()>) + -> ty::Instance<'tcx> +{ + let (def_id, substs) = traits::find_method(tcx, name, substs, impl_data); + let substs = tcx.erase_regions(&substs); + ty::Instance::new(def_id, substs) +} diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index bf1d9886ae7f0..0c3d211912add 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -9,6 +9,7 @@ // except according to those terms. +use back::symbol_names; use llvm; use llvm::{SetUnnamedAddr}; use llvm::{ValueRef, True}; @@ -24,7 +25,6 @@ use monomorphize::Instance; use type_::Type; use type_of; use rustc::ty; -use rustc::ty::subst::Substs; use rustc::hir; @@ -80,12 +80,12 @@ pub fn addr_of(ccx: &CrateContext, } pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { - let instance = Instance::mono(ccx.shared(), def_id); + let instance = Instance::mono(ccx.tcx(), def_id); if let Some(&g) = ccx.instances().borrow().get(&instance) { return g; } - let ty = common::def_ty(ccx.shared(), def_id, Substs::empty()); + let ty = common::instance_ty(ccx.shared(), &instance); let g = if let Some(id) = ccx.tcx().hir.as_local_node_id(def_id) { let llty = type_of::type_of(ccx, ty); @@ -114,7 +114,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { hir_map::NodeForeignItem(&hir::ForeignItem { ref attrs, span, node: hir::ForeignItemStatic(..), .. }) => { - let sym = instance.symbol_name(ccx.shared()); + let sym = symbol_names::symbol_name(instance, ccx.shared()); let g = if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "linkage") { // If this is a static with a linkage specified, then we need to handle @@ -174,7 +174,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { g } else { - let sym = instance.symbol_name(ccx.shared()); + let sym = symbol_names::symbol_name(instance, ccx.shared()); // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? // FIXME(nagisa): investigate whether it can be changed into define_global @@ -235,7 +235,8 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, v }; - let ty = common::def_ty(ccx.shared(), def_id, Substs::empty()); + let instance = Instance::mono(ccx.tcx(), def_id); + let ty = common::instance_ty(ccx.shared(), &instance); let llty = type_of::type_of(ccx, ty); let g = if val_llty == llty { g diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index 52851ea995d4b..b7381dd07dcfa 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -546,16 +546,6 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { &self.translation_items } - /// Given the def-id of some item that has no type parameters, make - /// a suitable "empty substs" for it. - pub fn empty_substs_for_def_id(&self, item_def_id: DefId) -> &'tcx Substs<'tcx> { - Substs::for_item(self.tcx(), item_def_id, - |_, _| self.tcx().mk_region(ty::ReErased), - |_, _| { - bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) - }) - } - pub fn metadata_symbol_name(&self) -> String { format!("rust_metadata_{}_{}", self.link_meta().crate_name, @@ -886,7 +876,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { /// Given the def-id of some item that has no type parameters, make /// a suitable "empty substs" for it. pub fn empty_substs_for_def_id(&self, item_def_id: DefId) -> &'tcx Substs<'tcx> { - self.shared().empty_substs_for_def_id(item_def_id) + self.tcx().empty_substs_for_def_id(item_def_id) } /// Generate a new symbol name with the given prefix. This symbol name must diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 1d4aebf135b9e..8e86b50b3f7dd 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -205,7 +205,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, return FunctionDebugContext::DebugInfoDisabled; } - for attr in cx.tcx().get_attrs(instance.def).iter() { + for attr in instance.def.attrs(cx.tcx()).iter() { if attr.check_name("no_debug") { return FunctionDebugContext::FunctionWithoutDebugInfo; } @@ -229,11 +229,11 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, }; // Find the enclosing function, in case this is a closure. - let def_key = cx.tcx().def_key(instance.def); + let def_key = cx.tcx().def_key(instance.def_id()); let mut name = def_key.disambiguated_data.data.to_string(); let name_len = name.len(); - let fn_def_id = cx.tcx().closure_base_def_id(instance.def); + let fn_def_id = cx.tcx().closure_base_def_id(instance.def_id()); // Get_template_parameters() will append a `<...>` clause to the function // name if necessary. @@ -246,11 +246,11 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, &mut name); // Build the linkage_name out of the item path and "template" parameters. - let linkage_name = mangled_name_of_item(cx, instance.def, &name[name_len..]); + let linkage_name = mangled_name_of_item(cx, instance.def_id(), &name[name_len..]); let scope_line = span_start(cx, span).line; - let local_id = cx.tcx().hir.as_local_node_id(instance.def); + let local_id = cx.tcx().hir.as_local_node_id(instance.def_id()); let is_local_to_unit = local_id.map_or(false, |id| is_node_local_to_unit(cx, id)); let function_name = CString::new(name).unwrap(); @@ -394,7 +394,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // First, let's see if this is a method within an inherent impl. Because // if yes, we want to make the result subroutine DIE a child of the // subroutine's self-type. - let self_type = cx.tcx().impl_of_method(instance.def).and_then(|impl_def_id| { + let self_type = cx.tcx().impl_of_method(instance.def_id()).and_then(|impl_def_id| { // If the method does *not* belong to a trait, proceed if cx.tcx().trait_id_of_impl(impl_def_id).is_none() { let impl_self_ty = @@ -417,9 +417,9 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, self_type.unwrap_or_else(|| { namespace::item_namespace(cx, DefId { - krate: instance.def.krate, + krate: instance.def_id().krate, index: cx.tcx() - .def_key(instance.def) + .def_key(instance.def_id()) .parent .expect("get_containing_scope: missing parent?") }) diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index a3f4168e96f2a..bfd9f69a92218 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -21,6 +21,7 @@ use machine; use monomorphize::Instance; use type_::Type; use type_of::*; +use back::symbol_names; use value::Value; use rustc::ty; @@ -70,7 +71,7 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, let function_name = match callee.ty.sty { ty::TyFnDef(def_id, substs, _) => { let instance = Instance::new(def_id, substs); - instance.symbol_name(ccx.shared()) + symbol_names::symbol_name(instance, ccx.shared()) } _ => bug!() }; diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index b6fcc990344ce..deb1073cf9aed 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -30,7 +30,7 @@ use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integra use common::{C_null, C_struct, C_str_slice, C_undef, C_uint, C_vector, is_undef}; use common::const_to_opt_u128; use consts; -use monomorphize::{self, Instance}; +use monomorphize; use type_of; use type_::Type; use value::Value; @@ -245,11 +245,12 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } fn trans_def(ccx: &'a CrateContext<'a, 'tcx>, - instance: Instance<'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>, args: IndexVec>) -> Result, ConstEvalErr<'tcx>> { - let instance = instance.resolve_const(ccx.shared()); - let mir = ccx.tcx().item_mir(instance.def); + let instance = monomorphize::resolve_const(ccx.shared(), def_id, substs); + let mir = ccx.tcx().instance_mir(instance.def); MirConstContext::new(ccx, &mir, instance.substs, args).trans() } @@ -332,10 +333,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => { let fn_ty = func.ty(self.mir, tcx); let fn_ty = self.monomorphize(&fn_ty); - let instance = match fn_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - Instance::new(def_id, substs) - } + let (def_id, substs) = match fn_ty.sty { + ty::TyFnDef(def_id, substs, _) => (def_id, substs), _ => span_bug!(span, "calling {:?} (of type {}) in constant", func, fn_ty) }; @@ -348,7 +347,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } } if let Some((ref dest, target)) = *destination { - match MirConstContext::trans_def(self.ccx, instance, const_args) { + match MirConstContext::trans_def(self.ccx, def_id, substs, const_args) { Ok(value) => self.store(dest, value, span), Err(err) => if failure.is_ok() { failure = Err(err); } } @@ -485,8 +484,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } let substs = self.monomorphize(&substs); - let instance = Instance::new(def_id, substs); - MirConstContext::trans_def(self.ccx, instance, IndexVec::new()) + MirConstContext::trans_def(self.ccx, def_id, substs, IndexVec::new()) } mir::Literal::Promoted { index } => { let mir = &self.mir.promoted[index]; @@ -588,7 +586,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { // Now create its substs [Closure, Tuple] let input = tcx.closure_type(def_id) .subst(tcx, substs.substs).input(0); - let substs = tcx.mk_substs([operand.ty, input.skip_binder()] + let input = tcx.erase_late_bound_regions_and_normalize(&input); + let substs = tcx.mk_substs([operand.ty, input] .iter().cloned().map(Kind::from)); Callee::def(self.ccx, call_once, substs) .reify(self.ccx) @@ -935,8 +934,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } let substs = self.monomorphize(&substs); - let instance = Instance::new(def_id, substs); - MirConstContext::trans_def(bcx.ccx, instance, IndexVec::new()) + MirConstContext::trans_def(bcx.ccx, def_id, substs, IndexVec::new()) } mir::Literal::Promoted { index } => { let mir = &self.mir.promoted[index]; @@ -964,8 +962,8 @@ pub fn trans_static_initializer<'a, 'tcx>( def_id: DefId) -> Result> { - let instance = Instance::mono(ccx.shared(), def_id); - MirConstContext::trans_def(ccx, instance, IndexVec::new()).map(|c| c.llval) + MirConstContext::trans_def(ccx, def_id, Substs::empty(), IndexVec::new()) + .map(|c| c.llval) } /// Construct a constant value, suitable for initializing a diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index b6af4e52e820b..bbaf0f9d35fa6 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -203,7 +203,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Now create its substs [Closure, Tuple] let input = bcx.tcx().closure_type(def_id) .subst(bcx.tcx(), substs.substs).input(0); - let substs = bcx.tcx().mk_substs([operand.ty, input.skip_binder()] + let input = + bcx.tcx().erase_late_bound_regions_and_normalize(&input); + let substs = bcx.tcx().mk_substs([operand.ty, input] .iter().cloned().map(Kind::from)); OperandValue::Immediate( Callee::def(bcx.ccx, call_once, substs) diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 4b31d5b7f88de..3b746af275a2a 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -15,54 +15,31 @@ use rustc::traits; use rustc::ty::fold::{TypeFolder, TypeFoldable}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::util::ppaux; use rustc::util::common::MemoizationMap; use syntax::codemap::DUMMY_SP; -use std::fmt; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct Instance<'tcx> { - pub def: DefId, - pub substs: &'tcx Substs<'tcx>, -} - -impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, &self.substs, self.def, &[]) - } -} - -impl<'a, 'tcx> Instance<'tcx> { - pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) - -> Instance<'tcx> { - assert!(substs.regions().all(|&r| r == ty::ReErased)); - Instance { def: def_id, substs: substs } - } - - pub fn mono(scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId) -> Instance<'tcx> { - Instance::new(def_id, scx.empty_substs_for_def_id(def_id)) - } - - /// For associated constants from traits, return the impl definition. - pub fn resolve_const(&self, scx: &SharedCrateContext<'a, 'tcx>) -> Self { - if let Some(trait_id) = scx.tcx().trait_of_item(self.def) { - let trait_ref = ty::TraitRef::new(trait_id, self.substs); - let trait_ref = ty::Binder(trait_ref); - let vtable = fulfill_obligation(scx, DUMMY_SP, trait_ref); - if let traits::VtableImpl(vtable_impl) = vtable { - let name = scx.tcx().item_name(self.def); - let ac = scx.tcx().associated_items(vtable_impl.impl_def_id) - .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); - if let Some(ac) = ac { - return Instance::new(ac.def_id, vtable_impl.substs); - } +pub use rustc::ty::Instance; + +/// For associated constants from traits, return the impl definition. +pub fn resolve_const<'a, 'tcx>( + scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx> +) -> Instance<'tcx> { + if let Some(trait_id) = scx.tcx().trait_of_item(def_id) { + let trait_ref = ty::TraitRef::new(trait_id, substs); + let trait_ref = ty::Binder(trait_ref); + let vtable = fulfill_obligation(scx, DUMMY_SP, trait_ref); + if let traits::VtableImpl(vtable_impl) = vtable { + let name = scx.tcx().item_name(def_id); + let ac = scx.tcx().associated_items(vtable_impl.impl_def_id) + .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); + if let Some(ac) = ac { + return Instance::new(ac.def_id, vtable_impl.substs); } } - - *self } + + Instance::new(def_id, substs) } /// Monomorphizes a type from the AST by first applying the in-scope diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index cc9fd8f46f6f0..1232c6cd28e52 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -110,7 +110,7 @@ use rustc::dep_graph::{DepNode, WorkProductId}; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; use rustc::session::config::NUMBERED_CODEGEN_UNIT_MARKER; -use rustc::ty::TyCtxt; +use rustc::ty::{self, TyCtxt}; use rustc::ty::item_path::characteristic_def_id_of_type; use rustc_incremental::IchHasher; use std::cmp::Ordering; @@ -186,7 +186,8 @@ impl<'tcx> CodegenUnit<'tcx> { symbol_name.hash(&mut state); let exported = match item { TransItem::Fn(ref instance) => { - let node_id = scx.tcx().hir.as_local_node_id(instance.def); + let node_id = + scx.tcx().hir.as_local_node_id(instance.def_id()); node_id.map(|node_id| exported_symbols.contains(&node_id)) .unwrap_or(false) } @@ -241,7 +242,7 @@ impl<'tcx> CodegenUnit<'tcx> { fn local_node_id(tcx: TyCtxt, trans_item: TransItem) -> Option { match trans_item { TransItem::Fn(instance) => { - tcx.hir.as_local_node_id(instance.def) + tcx.hir.as_local_node_id(instance.def_id()) } TransItem::Static(node_id) => Some(node_id), TransItem::DropGlue(_) => None, @@ -455,17 +456,22 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't let tcx = scx.tcx(); match trans_item { TransItem::Fn(instance) => { + let def_id = match instance.def { + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::FnPtrShim(..) => return None + }; + // If this is a method, we want to put it into the same module as // its self-type. If the self-type does not provide a characteristic // DefId, we use the location of the impl after all. - if tcx.trait_of_item(instance.def).is_some() { + if tcx.trait_of_item(def_id).is_some() { let self_ty = instance.substs.type_at(0); // This is an implementation of a trait method. - return characteristic_def_id_of_type(self_ty).or(Some(instance.def)); + return characteristic_def_id_of_type(self_ty).or(Some(def_id)); } - if let Some(impl_def_id) = tcx.impl_of_method(instance.def) { + if let Some(impl_def_id) = tcx.impl_of_method(def_id) { // This is a method within an inherent impl, find out what the // self-type is: let impl_self_ty = common::def_ty(scx, impl_def_id, instance.substs); @@ -474,7 +480,7 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't } } - Some(instance.def) + Some(def_id) } TransItem::DropGlue(dg) => characteristic_def_id_of_type(dg.ty()), TransItem::Static(node_id) => Some(tcx.hir.local_def_id(node_id)), diff --git a/src/librustc_trans/symbol_map.rs b/src/librustc_trans/symbol_map.rs index 880c65937e308..cd285bfaa6010 100644 --- a/src/librustc_trans/symbol_map.rs +++ b/src/librustc_trans/symbol_map.rs @@ -97,7 +97,7 @@ impl<'tcx> SymbolMap<'tcx> { trans_item: TransItem<'tcx>) -> Option { match trans_item { TransItem::Fn(Instance { def, .. }) => { - tcx.hir.as_local_node_id(def) + tcx.hir.as_local_node_id(def.def_id()) } TransItem::Static(node_id) => Some(node_id), TransItem::DropGlue(_) => None, diff --git a/src/librustc_trans/symbol_names_test.rs b/src/librustc_trans/symbol_names_test.rs index 02e1290b57783..fe551b06b3d95 100644 --- a/src/librustc_trans/symbol_names_test.rs +++ b/src/librustc_trans/symbol_names_test.rs @@ -14,6 +14,7 @@ //! item-path. This is used for unit testing the code that generates //! paths etc in all kinds of annoying scenarios. +use back::symbol_names; use rustc::hir; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use syntax::ast; @@ -51,8 +52,8 @@ impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> { for attr in tcx.get_attrs(def_id).iter() { if attr.check_name(SYMBOL_NAME) { // for now, can only use on monomorphic names - let instance = Instance::mono(self.scx, def_id); - let name = instance.symbol_name(self.scx); + let instance = Instance::mono(tcx, def_id); + let name = symbol_names::symbol_name(instance, self.scx); tcx.sess.span_err(attr.span, &format!("symbol-name({})", name)); } else if attr.check_name(ITEM_PATH) { let path = tcx.item_path_str(def_id); @@ -86,4 +87,3 @@ impl<'a, 'tcx> Visitor<'tcx> for SymbolNamesTest<'a, 'tcx> { intravisit::walk_impl_item(self, ii) } } - diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 5ec9c2a59957d..13af081e0b698 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -96,7 +96,7 @@ impl<'a, 'tcx> TransItem<'tcx> { } TransItem::Fn(instance) => { let _task = ccx.tcx().dep_graph.in_task( - DepNode::TransCrateItem(instance.def)); // (*) + DepNode::TransCrateItem(instance.def_id())); // (*) base::trans_instance(&ccx, instance); } @@ -147,7 +147,8 @@ impl<'a, 'tcx> TransItem<'tcx> { linkage: llvm::Linkage, symbol_name: &str) { let def_id = ccx.tcx().hir.local_def_id(node_id); - let ty = common::def_ty(ccx.shared(), def_id, Substs::empty()); + let instance = Instance::mono(ccx.tcx(), def_id); + let ty = common::instance_ty(ccx.shared(), &instance); let llty = type_of::type_of(ccx, ty); let g = declare::define_global(ccx, symbol_name, llty).unwrap_or_else(|| { @@ -157,7 +158,6 @@ impl<'a, 'tcx> TransItem<'tcx> { unsafe { llvm::LLVMRustSetLinkage(g, linkage) }; - let instance = Instance::mono(ccx.shared(), def_id); ccx.instances().borrow_mut().insert(instance, g); ccx.statics().borrow_mut().insert(g, def_id); } @@ -169,8 +169,8 @@ impl<'a, 'tcx> TransItem<'tcx> { assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types()); - let mono_ty = common::def_ty(ccx.shared(), instance.def, instance.substs); - let attrs = ccx.tcx().get_attrs(instance.def); + let mono_ty = common::instance_ty(ccx.shared(), &instance); + let attrs = instance.def.attrs(ccx.tcx()); let lldecl = declare::declare_fn(ccx, symbol_name, mono_ty); unsafe { llvm::LLVMRustSetLinkage(lldecl, linkage) }; base::set_link_section(ccx, lldecl, &attrs); @@ -180,7 +180,7 @@ impl<'a, 'tcx> TransItem<'tcx> { } debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); - match ccx.tcx().def_key(instance.def).disambiguated_data.data { + match ccx.tcx().def_key(instance.def_id()).disambiguated_data.data { DefPathData::StructCtor | DefPathData::EnumVariant(..) | DefPathData::ClosureExpr => { @@ -229,10 +229,10 @@ impl<'a, 'tcx> TransItem<'tcx> { pub fn compute_symbol_name(&self, scx: &SharedCrateContext<'a, 'tcx>) -> String { match *self { - TransItem::Fn(instance) => instance.symbol_name(scx), + TransItem::Fn(instance) => symbol_names::symbol_name(instance, scx), TransItem::Static(node_id) => { let def_id = scx.tcx().hir.local_def_id(node_id); - Instance::mono(scx, def_id).symbol_name(scx) + symbol_names::symbol_name(Instance::mono(scx.tcx(), def_id), scx) } TransItem::DropGlue(dg) => { let prefix = match dg { @@ -244,21 +244,13 @@ impl<'a, 'tcx> TransItem<'tcx> { } } - pub fn is_from_extern_crate(&self) -> bool { - match *self { - TransItem::Fn(ref instance) => !instance.def.is_local(), - TransItem::DropGlue(..) | - TransItem::Static(..) => false, - } - } - pub fn instantiation_mode(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> InstantiationMode { match *self { TransItem::Fn(ref instance) => { if self.explicit_linkage(tcx).is_none() && - common::requests_inline(tcx, instance.def) + common::requests_inline(tcx, instance) { InstantiationMode::LocalCopy } else { @@ -282,7 +274,7 @@ impl<'a, 'tcx> TransItem<'tcx> { pub fn explicit_linkage(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option { let def_id = match *self { - TransItem::Fn(ref instance) => instance.def, + TransItem::Fn(ref instance) => instance.def_id(), TransItem::Static(node_id) => tcx.hir.local_def_id(node_id), TransItem::DropGlue(..) => return None, }; @@ -587,7 +579,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { pub fn push_instance_as_string(&self, instance: Instance<'tcx>, output: &mut String) { - self.push_def_path(instance.def, output); + self.push_def_path(instance.def_id(), output); self.push_type_params(instance.substs, iter::empty(), output); } } From bf80fec326d0fa7b58882d6f2102808a3f220651 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 6 Mar 2017 12:58:51 +0200 Subject: [PATCH 03/11] translate function shims using MIR --- src/librustc_mir/shim.rs | 127 ++++++++++++++++- src/librustc_trans/callee.rs | 134 +----------------- src/librustc_trans/collector.rs | 14 +- .../item-collection/function-as-argument.rs | 2 + .../trait-method-as-argument.rs | 6 + src/test/run-pass/mir_calls_to_shims.rs | 56 ++++++++ 6 files changed, 199 insertions(+), 140 deletions(-) create mode 100644 src/test/run-pass/mir_calls_to_shims.rs diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 0fbd488ffa360..bf0b1796dffaf 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -12,30 +12,42 @@ use rustc::hir; use rustc::infer; use rustc::mir::*; use rustc::mir::transform::MirSource; -use rustc::ty; +use rustc::ty::{self, Ty}; use rustc::ty::maps::Providers; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use syntax::abi::Abi; use syntax::ast; +use syntax::codemap::DUMMY_SP; use syntax_pos::Span; use std::cell::RefCell; use std::iter; +use std::mem; pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } -fn make_shim<'a, 'tcx>(_tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, +fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx RefCell> { - match instance { + debug!("make_shim({:?})", instance); + let result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceDef::FnPtrShim(..) => unimplemented!() - } + ty::InstanceDef::FnPtrShim(_, ty) => { + build_fn_ptr_shim(tcx, ty, instance.def_ty(tcx)) + } + }; + debug!("make_shim({:?}) = {:?}", instance, result); + + let result = tcx.alloc_mir(result); + // Perma-borrow MIR from shims to prevent mutation. + mem::forget(result.borrow()); + result } fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) @@ -54,6 +66,111 @@ fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) })).collect() } + +fn build_fn_ptr_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + fn_ty: Ty<'tcx>, + sig_ty: Ty<'tcx>) + -> Mir<'tcx> +{ + debug!("build_fn_ptr_shim(fn_ty={:?}, sig_ty={:?})", fn_ty, sig_ty); + let trait_sig = match sig_ty.sty { + ty::TyFnDef(_, _, fty) => tcx.erase_late_bound_regions(&fty), + _ => bug!("unexpected type for shim {:?}", sig_ty) + }; + + let self_ty = match trait_sig.inputs()[0].sty { + ty::TyParam(..) => fn_ty, + ty::TyRef(r, mt) => tcx.mk_ref(r, ty::TypeAndMut { + ty: fn_ty, + mutbl: mt.mutbl + }), + _ => bug!("unexpected self_ty {:?}", trait_sig), + }; + + let fn_ptr_sig = match fn_ty.sty { + ty::TyFnPtr(fty) | + ty::TyFnDef(_, _, fty) => + tcx.erase_late_bound_regions_and_normalize(&fty), + _ => bug!("non-fn-ptr {:?} in build_fn_ptr_shim", fn_ty) + }; + + let sig = tcx.mk_fn_sig( + [ + self_ty, + tcx.intern_tup(fn_ptr_sig.inputs(), false) + ].iter().cloned(), + fn_ptr_sig.output(), + false, + hir::Unsafety::Normal, + Abi::RustCall, + ); + + let local_decls = local_decls_for_sig(&sig); + let source_info = SourceInfo { + span: DUMMY_SP, + scope: ARGUMENT_VISIBILITY_SCOPE + }; + + let fn_ptr = Lvalue::Local(Local::new(1+0)); + let fn_ptr = match trait_sig.inputs()[0].sty { + ty::TyParam(..) => fn_ptr, + ty::TyRef(..) => Lvalue::Projection(box Projection { + base: fn_ptr, elem: ProjectionElem::Deref + }), + _ => bug!("unexpected self_ty {:?}", trait_sig), + }; + let fn_args = Local::new(1+1); + + let return_block_id = BasicBlock::new(1); + + // return = ADT(arg0, arg1, ...); return + let start_block = BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Call { + func: Operand::Consume(fn_ptr), + args: fn_ptr_sig.inputs().iter().enumerate().map(|(i, ity)| { + Operand::Consume(Lvalue::Projection(box Projection { + base: Lvalue::Local(fn_args), + elem: ProjectionElem::Field( + Field::new(i), *ity + ) + })) + }).collect(), + // FIXME: can we pass a Some destination for an uninhabited ty? + destination: Some((Lvalue::Local(RETURN_POINTER), + return_block_id)), + cleanup: None + } + }), + is_cleanup: false + }; + let return_block = BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Return + }), + is_cleanup: false + }; + + let mut mir = Mir::new( + vec![start_block, return_block].into_iter().collect(), + IndexVec::from_elem_n( + VisibilityScopeData { span: DUMMY_SP, parent_scope: None }, 1 + ), + IndexVec::new(), + sig.output(), + local_decls, + sig.inputs().len(), + vec![], + DUMMY_SP + ); + mir.spread_arg = Some(fn_args); + mir +} + pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, ctor_id: ast::NodeId, fields: &[hir::StructField], diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 19fc4e013fae1..2294e8a0002e0 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -36,7 +36,6 @@ use back::symbol_names::symbol_name; use trans_item::TransItem; use type_of; use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::hir; use std::iter; use syntax_pos::DUMMY_SP; @@ -130,15 +129,14 @@ impl<'tcx> Callee<'tcx> { let method_ty = instance_ty(ccx.shared(), &instance); Callee::ptr(llfn, method_ty) } - traits::VtableFnPointer(vtable_fn_pointer) => { - let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - let instance = Instance::new(def_id, substs); - let llfn = trans_fn_pointer_shim(ccx, instance, - trait_closure_kind, - vtable_fn_pointer.fn_ty); + traits::VtableFnPointer(data) => { + let instance = ty::Instance { + def: ty::InstanceDef::FnPtrShim(def_id, data.fn_ty), + substs: substs, + }; - let method_ty = instance_ty(ccx.shared(), &instance); - Callee::ptr(llfn, method_ty) + let (llfn, ty) = get_fn(ccx, instance); + Callee::ptr(llfn, ty) } traits::VtableObject(ref data) => { Callee { @@ -363,124 +361,6 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( lloncefn } -/// Translates an adapter that implements the `Fn` trait for a fn -/// pointer. This is basically the equivalent of something like: -/// -/// ``` -/// impl<'a> Fn(&'a int) -> &'a int for fn(&int) -> &int { -/// extern "rust-abi" fn call(&self, args: (&'a int,)) -> &'a int { -/// (*self)(args.0) -/// } -/// } -/// ``` -/// -/// but for the bare function type given. -fn trans_fn_pointer_shim<'a, 'tcx>( - ccx: &'a CrateContext<'a, 'tcx>, - method_instance: Instance<'tcx>, - closure_kind: ty::ClosureKind, - bare_fn_ty: Ty<'tcx>) - -> ValueRef -{ - let tcx = ccx.tcx(); - - // Normalize the type for better caching. - let bare_fn_ty = tcx.normalize_associated_type(&bare_fn_ty); - - // If this is an impl of `Fn` or `FnMut` trait, the receiver is `&self`. - let is_by_ref = match closure_kind { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => true, - ty::ClosureKind::FnOnce => false, - }; - - let llfnpointer = match bare_fn_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - // Function definitions have to be turned into a pointer. - let llfn = Callee::def(ccx, def_id, substs).reify(ccx); - if !is_by_ref { - // A by-value fn item is ignored, so the shim has - // the same signature as the original function. - return llfn; - } - Some(llfn) - } - _ => None - }; - - let bare_fn_ty_maybe_ref = if is_by_ref { - tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), bare_fn_ty) - } else { - bare_fn_ty - }; - - // Check if we already trans'd this shim. - if let Some(&llval) = ccx.fn_pointer_shims().borrow().get(&bare_fn_ty_maybe_ref) { - return llval; - } - - debug!("trans_fn_pointer_shim(bare_fn_ty={:?})", - bare_fn_ty); - - // Construct the "tuply" version of `bare_fn_ty`. It takes two arguments: `self`, - // which is the fn pointer, and `args`, which is the arguments tuple. - let sig = bare_fn_ty.fn_sig(); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); - assert_eq!(sig.unsafety, hir::Unsafety::Normal); - assert_eq!(sig.abi, Abi::Rust); - let tuple_input_ty = tcx.intern_tup(sig.inputs(), false); - let sig = tcx.mk_fn_sig( - [bare_fn_ty_maybe_ref, tuple_input_ty].iter().cloned(), - sig.output(), - false, - hir::Unsafety::Normal, - Abi::RustCall - ); - let fn_ty = FnType::new(ccx, sig, &[]); - let tuple_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig)); - debug!("tuple_fn_ty: {:?}", tuple_fn_ty); - - // - let function_name = symbol_name(method_instance, ccx.shared()); - let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty); - attributes::set_frame_pointer_elimination(ccx, llfn); - // - let bcx = Builder::new_block(ccx, llfn, "entry-block"); - - let mut llargs = get_params(llfn); - - let self_arg = llargs.remove(fn_ty.ret.is_indirect() as usize); - let llfnpointer = llfnpointer.unwrap_or_else(|| { - // the first argument (`self`) will be ptr to the fn pointer - if is_by_ref { - bcx.load(self_arg, None) - } else { - self_arg - } - }); - - let callee = Callee { - data: Fn(llfnpointer), - ty: bare_fn_ty - }; - let fn_ret = callee.ty.fn_ret(); - let fn_ty = callee.direct_fn_type(ccx, &[]); - let llret = bcx.call(llfnpointer, &llargs, None); - fn_ty.apply_attrs_callsite(llret); - - if fn_ret.0.is_never() { - bcx.unreachable(); - } else { - if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { - bcx.ret_void(); - } else { - bcx.ret(llret); - } - } - - ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn); - - llfn -} /// Translates a reference to a fn/method item, monomorphizing and /// inlining as it goes. diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 271f91f9adbdb..c4239fa2ae65f 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -907,14 +907,12 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } traits::VtableFnPointer(ref data) => { - // If we know the destination of this fn-pointer, we'll have to make - // sure that this destination actually gets instantiated. - if let ty::TyFnDef(def_id, substs, _) = data.fn_ty.sty { - // The destination of the pointer might be something that needs - // further dispatching, such as a trait method, so we do that. - do_static_dispatch(scx, def_id, substs) - } else { - StaticDispatchResult::Unknown + StaticDispatchResult::Dispatched { + instance: Instance { + def: ty::InstanceDef::FnPtrShim(trait_method.def_id, data.fn_ty), + substs: trait_ref.substs + }, + fn_once_adjustment: None, } } // Trait object shims are always instantiated in-place, and as they are diff --git a/src/test/codegen-units/item-collection/function-as-argument.rs b/src/test/codegen-units/item-collection/function-as-argument.rs index 3a9d56c2a8bf7..51df38cabef30 100644 --- a/src/test/codegen-units/item-collection/function-as-argument.rs +++ b/src/test/codegen-units/item-collection/function-as-argument.rs @@ -28,10 +28,12 @@ fn main() { //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] //~ TRANS_ITEM fn function_as_argument::function[0] + //~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0] take_fn_once(function, 0u32, "abc"); //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] //~ TRANS_ITEM fn function_as_argument::function[0] + //~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0] take_fn_once(function, 'c', 0f64); //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] diff --git a/src/test/codegen-units/item-collection/trait-method-as-argument.rs b/src/test/codegen-units/item-collection/trait-method-as-argument.rs index e7006d73ef166..f7afd3f0891e3 100644 --- a/src/test/codegen-units/item-collection/trait-method-as-argument.rs +++ b/src/test/codegen-units/item-collection/trait-method-as-argument.rs @@ -40,22 +40,28 @@ fn take_foo_mut T>(mut f: F, arg: T) -> T { fn main() { //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] u32> //~ TRANS_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0] + //~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0] u32, (u32)> take_foo_once(Trait::foo, 0u32); //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] char> //~ TRANS_ITEM fn trait_method_as_argument::Trait[0]::foo[0] + //~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0] char, (char)> take_foo_once(Trait::foo, 'c'); //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] u32> + //~ TRANS_ITEM fn core::ops[0]::Fn[0]::call[0] u32, (u32)> take_foo(Trait::foo, 0u32); //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] char> + //~ TRANS_ITEM fn core::ops[0]::Fn[0]::call[0] char, (char)> take_foo(Trait::foo, 'c'); //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> + //~ TRANS_ITEM fn core::ops[0]::FnMut[0]::call_mut[0] char, (char)> take_foo_mut(Trait::foo, 0u32); //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] char> + //~ TRANS_ITEM fn core::ops[0]::FnMut[0]::call_mut[0] u32, (u32)> take_foo_mut(Trait::foo, 'c'); } diff --git a/src/test/run-pass/mir_calls_to_shims.rs b/src/test/run-pass/mir_calls_to_shims.rs new file mode 100644 index 0000000000000..7300a322ec4b7 --- /dev/null +++ b/src/test/run-pass/mir_calls_to_shims.rs @@ -0,0 +1,56 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(fn_traits)] +#![feature(never_type)] + +use std::panic; + +fn foo(x: u32, y: u32) -> u32 { x/y } +fn foo_diverges() -> ! { panic!() } + +fn test_fn_ptr(mut t: T) + where T: Fn(u32, u32) -> u32, +{ + let as_fn = >::call; + assert_eq!(as_fn(&t, (9, 3)), 3); + let as_fn_mut = >::call_mut; + assert_eq!(as_fn_mut(&mut t, (18, 3)), 6); + let as_fn_once = >::call_once; + assert_eq!(as_fn_once(t, (24, 3)), 8); +} + +fn assert_panics(f: F) where F: FnOnce() { + let f = panic::AssertUnwindSafe(f); + let result = panic::catch_unwind(move || { + f.0() + }); + if let Ok(..) = result { + panic!("diverging function returned"); + } +} + +fn test_fn_ptr_panic(mut t: T) + where T: Fn() -> ! +{ + let as_fn = >::call; + assert_panics(|| as_fn(&t, ())); + let as_fn_mut = >::call_mut; + assert_panics(|| as_fn_mut(&mut t, ())); + let as_fn_once = >::call_once; + assert_panics(|| as_fn_once(t, ())); +} + +fn main() { + test_fn_ptr(foo); + test_fn_ptr(foo as fn(u32, u32) -> u32); + test_fn_ptr_panic(foo_diverges); + test_fn_ptr_panic(foo_diverges as fn() -> !); +} From aac5ba5dab758803b06add74457eba03bbbd9930 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Mar 2017 01:41:26 +0200 Subject: [PATCH 04/11] resolve instances to ty::Instance directly This removes the duplication between collector, callee, and (eventually) MIRI. --- src/librustc/traits/mod.rs | 2 +- src/librustc/traits/specialize/mod.rs | 25 ++- src/librustc/ty/context.rs | 9 + src/librustc/ty/instance.rs | 34 +++- src/librustc/ty/util.rs | 2 - src/librustc_mir/shim.rs | 1 + src/librustc_trans/base.rs | 24 +-- src/librustc_trans/callee.rs | 186 ++++--------------- src/librustc_trans/collector.rs | 235 ++++++----------------- src/librustc_trans/common.rs | 82 +-------- src/librustc_trans/glue.rs | 20 +- src/librustc_trans/lib.rs | 1 + src/librustc_trans/mir/constant.rs | 2 +- src/librustc_trans/monomorphize.rs | 256 ++++++++++++++++++++++++-- src/librustc_trans/partitioning.rs | 5 +- 15 files changed, 389 insertions(+), 495 deletions(-) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 7e7d06e4b814e..c71fc28b4d6b3 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -40,7 +40,7 @@ pub use self::select::{EvaluationCache, SelectionContext, SelectionCache}; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants pub use self::specialize::{OverlapError, specialization_graph, specializes, translate_substs}; -pub use self::specialize::{SpecializesCache, find_method}; +pub use self::specialize::{SpecializesCache, find_associated_item}; pub use self::util::elaborate_predicates; pub use self::util::supertraits; pub use self::util::Supertraits; diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 2c99ee21b0f73..50a4d982832ac 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -29,8 +29,6 @@ use traits::{self, Reveal, ObligationCause}; use ty::{self, TyCtxt, TypeFoldable}; use syntax_pos::DUMMY_SP; -use syntax::ast; - pub mod specialization_graph; /// Information pertinent to an overlapping impl error. @@ -106,22 +104,23 @@ pub fn translate_substs<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, } /// Given a selected impl described by `impl_data`, returns the -/// definition and substitions for the method with the name `name`, -/// and trait method substitutions `substs`, in that impl, a less -/// specialized impl, or the trait default, whichever applies. -pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - name: ast::Name, - substs: &'tcx Substs<'tcx>, - impl_data: &super::VtableImplData<'tcx, ()>) - -> (DefId, &'tcx Substs<'tcx>) -{ +/// definition and substitions for the method with the name `name` +/// the kind `kind`, and trait method substitutions `substs`, in +/// that impl, a less specialized impl, or the trait default, +/// whichever applies. +pub fn find_associated_item<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + item: &ty::AssociatedItem, + substs: &'tcx Substs<'tcx>, + impl_data: &super::VtableImplData<'tcx, ()>, +) -> (DefId, &'tcx Substs<'tcx>) { assert!(!substs.needs_infer()); let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); let ancestors = trait_def.ancestors(impl_data.impl_def_id); - match ancestors.defs(tcx, name, ty::AssociatedKind::Method).next() { + match ancestors.defs(tcx, item.name, item.kind).next() { Some(node_item) => { let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs); @@ -137,7 +136,7 @@ pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (node_item.item.def_id, substs) } None => { - bug!("method {:?} not found in {:?}", name, impl_data.impl_def_id) + bug!("{:?} not found in {:?}", item, impl_data.impl_def_id) } } } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index a0aeb4107c156..5543223105b44 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1469,6 +1469,15 @@ impl InternIteratorElement for T { } } +impl<'a, T, R> InternIteratorElement for &'a T + where T: Clone + 'a +{ + type Output = R; + fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { + f(&iter.cloned().collect::>()) + } +} + impl InternIteratorElement for Result { type Output = Result; fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index fdcfb3ebd3ce5..d93482acbcb57 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -27,8 +27,17 @@ pub struct Instance<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum InstanceDef<'tcx> { Item(DefId), + Intrinsic(DefId), // ::call_* + // def-id is FnTrait::call_* FnPtrShim(DefId, Ty<'tcx>), + // ::fn + Virtual(DefId, usize), + // <[mut closure] as FnOnce>::call_once + ClosureOnceShim { + call_once: DefId, + closure_did: DefId + }, } impl<'tcx> InstanceDef<'tcx> { @@ -36,8 +45,12 @@ impl<'tcx> InstanceDef<'tcx> { pub fn def_id(&self) -> DefId { match *self { InstanceDef::Item(def_id) | - InstanceDef::FnPtrShim(def_id, _) - => def_id + InstanceDef::FnPtrShim(def_id, _) | + InstanceDef::Virtual(def_id, _) | + InstanceDef::Intrinsic(def_id, ) | + InstanceDef::ClosureOnceShim { + call_once: def_id, closure_did: _ + } => def_id } } @@ -73,14 +86,23 @@ impl<'tcx> InstanceDef<'tcx> { impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ppaux::parameterized(f, self.substs, self.def_id(), &[])?; match self.def { - InstanceDef::Item(def) => { - ppaux::parameterized(f, self.substs, def, &[]) + InstanceDef::Item(_) => Ok(()), + InstanceDef::Intrinsic(_) => { + write!(f, " - intrinsic") } - InstanceDef::FnPtrShim(def, ty) => { - ppaux::parameterized(f, self.substs, def, &[])?; + InstanceDef::Virtual(_, num) => { + write!(f, " - shim(#{})", num) + } + InstanceDef::FnPtrShim(_, ty) => { write!(f, " - shim({:?})", ty) } + InstanceDef::ClosureOnceShim { + call_once: _, closure_did + } => { + write!(f, " - shim({:?})", closure_did) + } } } } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 2344305fa9a7e..1d816d342c4fd 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -408,8 +408,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) }) } - - } pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> { diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index bf0b1796dffaf..2a053535e7e10 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -41,6 +41,7 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, ty::InstanceDef::FnPtrShim(_, ty) => { build_fn_ptr_shim(tcx, ty, instance.def_ty(tcx)) } + _ => bug!("unknown shim kind") }; debug!("make_shim({:?}) = {:?}", instance, result); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index ce767468c012b..10ab199671f64 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -36,9 +36,7 @@ use llvm::{Linkage, ValueRef, Vector, get_param}; use llvm; use rustc::hir::def_id::LOCAL_CRATE; use middle::lang_items::StartFnLangItem; -use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::dep_graph::{AssertDepGraphSafe, DepNode, WorkProduct}; use rustc::hir::map as hir_map; use rustc::util::common::time; @@ -54,7 +52,6 @@ use common::{C_bool, C_bytes_in_context, C_i32, C_uint}; use collector::{self, TransItemCollectionMode}; use common::{C_struct_in_context, C_u64, C_undef}; use common::CrateContext; -use common::{fulfill_obligation}; use common::{type_is_zero_size, val_ty}; use common; use consts; @@ -80,7 +77,7 @@ use std::ffi::{CStr, CString}; use std::rc::Rc; use std::str; use std::i32; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::Span; use syntax::attr; use rustc::hir; use rustc::ty::layout::{self, Layout}; @@ -313,25 +310,6 @@ pub fn coerce_unsized_into<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, } } -pub fn custom_coerce_unsize_info<'scx, 'tcx>(scx: &SharedCrateContext<'scx, 'tcx>, - source_ty: Ty<'tcx>, - target_ty: Ty<'tcx>) - -> CustomCoerceUnsized { - let trait_ref = ty::Binder(ty::TraitRef { - def_id: scx.tcx().lang_items.coerce_unsized_trait().unwrap(), - substs: scx.tcx().mk_substs_trait(source_ty, &[target_ty]) - }); - - match fulfill_obligation(scx, DUMMY_SP, trait_ref) { - traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { - scx.tcx().custom_coerce_unsized_kind(impl_def_id) - } - vtable => { - bug!("invalid CoerceUnsized vtable: {:?}", vtable); - } - } -} - pub fn cast_shift_expr_rhs( cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef ) -> ValueRef { diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 2294e8a0002e0..70da2a69ef425 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -19,13 +19,13 @@ pub use self::CalleeData::*; use llvm::{self, ValueRef, get_params}; use rustc::hir::def_id::DefId; use rustc::ty::subst::{Substs, Subst}; -use rustc::traits; use abi::{Abi, FnType}; use attributes; use builder::Builder; use common::{self, CrateContext}; use cleanup::CleanupScope; use mir::lvalue::LvalueRef; +use monomorphize; use consts; use common::instance_ty; use declare; @@ -38,8 +38,6 @@ use type_of; use rustc::ty::{self, Ty, TypeFoldable}; use std::iter; -use syntax_pos::DUMMY_SP; - use mir::lvalue::Alignment; #[derive(Debug)] @@ -60,94 +58,39 @@ pub struct Callee<'tcx> { } impl<'tcx> Callee<'tcx> { - /// Function pointer. - pub fn ptr(llfn: ValueRef, ty: Ty<'tcx>) -> Callee<'tcx> { - Callee { - data: Fn(llfn), - ty: ty - } - } - /// Function or method definition. pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Callee<'tcx> { - let tcx = ccx.tcx(); - - if let Some(trait_id) = tcx.trait_of_item(def_id) { - return Callee::trait_method(ccx, trait_id, def_id, substs); - } - - let instance = ty::Instance::new(def_id, substs); - let fn_ty = instance_ty(ccx.shared(), &instance); - if let ty::TyFnDef(.., f) = fn_ty.sty { - if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { - return Callee { - data: Intrinsic, - ty: fn_ty + let instance = monomorphize::resolve(ccx.shared(), def_id, substs); + let ty = instance_ty(ccx.shared(), &instance); + let data = match instance.def { + ty::InstanceDef::Intrinsic(_) => Intrinsic, + ty::InstanceDef::ClosureOnceShim { .. } => { + let closure_ty = instance.substs.type_at(0); + let (closure_def_id, closure_substs) = match closure_ty.sty { + ty::TyClosure(def_id, substs) => (def_id, substs), + _ => bug!("bad closure instance {:?}", instance) }; - } - } - - let (llfn, ty) = get_fn(ccx, instance); - Callee::ptr(llfn, ty) - } - /// Trait method, which has to be resolved to an impl method. - pub fn trait_method<'a>(ccx: &CrateContext<'a, 'tcx>, - trait_id: DefId, - def_id: DefId, - substs: &'tcx Substs<'tcx>) - -> Callee<'tcx> { - let tcx = ccx.tcx(); - - let trait_ref = ty::TraitRef::from_method(tcx, trait_id, substs); - let trait_ref = tcx.normalize_associated_type(&ty::Binder(trait_ref)); - match common::fulfill_obligation(ccx.shared(), DUMMY_SP, trait_ref) { - traits::VtableImpl(vtable_impl) => { - let name = tcx.item_name(def_id); - let instance = common::find_method(tcx, name, substs, &vtable_impl); - - // Translate the function, bypassing Callee::def. - // That is because default methods have the same ID as the - // trait method used to look up the impl method that ended - // up here, so calling Callee::def would infinitely recurse. - let (llfn, ty) = get_fn(ccx, instance); - Callee::ptr(llfn, ty) - } - traits::VtableClosure(vtable_closure) => { - // The substitutions should have no type parameters remaining - // after passing through fulfill_obligation - let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - let instance = Instance::new(def_id, substs); - let llfn = trans_closure_method( + Fn(trans_fn_once_adapter_shim( ccx, - vtable_closure.closure_def_id, - vtable_closure.substs, + closure_def_id, + closure_substs, instance, - trait_closure_kind); - - let method_ty = instance_ty(ccx.shared(), &instance); - Callee::ptr(llfn, method_ty) - } - traits::VtableFnPointer(data) => { - let instance = ty::Instance { - def: ty::InstanceDef::FnPtrShim(def_id, data.fn_ty), - substs: substs, - }; - - let (llfn, ty) = get_fn(ccx, instance); - Callee::ptr(llfn, ty) + get_fn( + ccx, + Instance::new(closure_def_id, closure_substs.substs) + ) + )) } - traits::VtableObject(ref data) => { - Callee { - data: Virtual(tcx.get_vtable_index_of_object_method(data, def_id)), - ty: instance_ty(ccx.shared(), &Instance::new(def_id, substs)) - } + ty::InstanceDef::Virtual(_, n) => Virtual(n), + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::Item(..) => { + Fn(get_fn(ccx, instance)) } - vtable => { - bug!("resolved vtable bad vtable {:?} in trans", vtable); - } - } + }; + + Callee { data, ty } } /// Get the abi::FnType for a direct call. Mainly deals with the fact @@ -155,7 +98,8 @@ impl<'tcx> Callee<'tcx> { /// The extra argument types are for variadic (extern "C") functions. pub fn direct_fn_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>, extra_args: &[Ty<'tcx>]) -> FnType { - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&self.ty.fn_sig()); + let sig = common::ty_fn_sig(ccx, self.ty); + let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); if let Virtual(_) = self.data { // Don't pass the vtable, it's not an argument of the virtual fn. @@ -175,72 +119,6 @@ impl<'tcx> Callee<'tcx> { } } -fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, - def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, - method_instance: Instance<'tcx>, - trait_closure_kind: ty::ClosureKind) - -> ValueRef -{ - // If this is a closure, redirect to it. - let (llfn, _) = get_fn(ccx, Instance::new(def_id, substs.substs)); - - // If the closure is a Fn closure, but a FnOnce is needed (etc), - // then adapt the self type - let llfn_closure_kind = ccx.tcx().closure_kind(def_id); - - debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \ - trait_closure_kind={:?}, llfn={:?})", - llfn_closure_kind, trait_closure_kind, Value(llfn)); - - match needs_fn_once_adapter_shim(llfn_closure_kind, trait_closure_kind) { - Ok(true) => trans_fn_once_adapter_shim(ccx, - def_id, - substs, - method_instance, - llfn), - Ok(false) => llfn, - Err(()) => { - bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}", - llfn_closure_kind, - trait_closure_kind); - } - } -} - -pub fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, - trait_closure_kind: ty::ClosureKind) - -> Result -{ - match (actual_closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | - (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { - // No adapter needed. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { - // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at trans time, these are - // basically the same thing, so we can just return llfn. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut - // self, ...)`. We want a `fn(self, ...)`. We can produce - // this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - Ok(true) - } - _ => Err(()), - } -} - fn trans_fn_once_adapter_shim<'a, 'tcx>( ccx: &'a CrateContext<'a, 'tcx>, def_id: DefId, @@ -307,7 +185,6 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // the first argument (`self`) will be the (by value) closure env. let mut llargs = get_params(lloncefn); - let fn_ret = callee.ty.fn_ret(); let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); let self_idx = fn_ty.ret.is_indirect() as usize; let env_arg = &orig_fn_ty.args[0]; @@ -344,7 +221,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( } fn_ty.apply_attrs_callsite(llret); - if fn_ret.0.is_never() { + if sig.output().is_never() { bcx.unreachable(); } else { self_scope.trans(&bcx); @@ -372,7 +249,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( /// - `substs`: values for each of the fn/method's parameters fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) - -> (ValueRef, Ty<'tcx>) { + -> ValueRef +{ let tcx = ccx.tcx(); debug!("get_fn(instance={:?})", instance); @@ -383,7 +261,7 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let fn_ty = common::instance_ty(ccx.shared(), &instance); if let Some(&llfn) = ccx.instances().borrow().get(&instance) { - return (llfn, fn_ty); + return llfn; } let sym = ccx.symbol_map().get_or_compute(ccx.shared(), @@ -455,5 +333,5 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.instances().borrow_mut().insert(instance, llfn); - (llfn, fn_ty) + llfn } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index c4239fa2ae65f..9476ffc944485 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -203,11 +203,8 @@ use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor as MirVisitor; use syntax::abi::Abi; -use syntax_pos::DUMMY_SP; -use base::custom_coerce_unsize_info; -use callee::needs_fn_once_adapter_shim; use context::SharedCrateContext; -use common::{def_ty, find_method, instance_ty, fulfill_obligation}; +use common::{def_ty, instance_ty}; use glue::{self, DropGlueKind}; use monomorphize::{self, Instance}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; @@ -555,7 +552,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let substs = monomorphize::apply_param_substs(self.scx, self.param_substs, &substs); - let instance = monomorphize::resolve_const(self.scx, def_id, substs); + let instance = monomorphize::resolve(self.scx, def_id, substs); collect_neighbours(self.scx, instance, self.output); } @@ -580,32 +577,23 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let callee_substs = monomorphize::apply_param_substs(self.scx, self.param_substs, &callee_substs); - let dispatched = do_static_dispatch(self.scx, - callee_def_id, - callee_substs); - - if let StaticDispatchResult::Dispatched { - instance, fn_once_adjustment - } = dispatched { - // if we have a concrete impl (which we might not have - // in the case of something compiler generated like an - // object shim or a closure that is handled differently), - // we check if the callee is something that will actually - // result in a translation item ... - if should_trans_locally(self.scx.tcx(), &instance) { - self.output.push(create_fn_trans_item(instance)); - - // This call will instantiate an FnOnce adapter, which drops - // the closure environment. Therefore we need to make sure - // that we collect the drop-glue for the environment type. - if let Some(env_ty) = fn_once_adjustment { - let env_ty = glue::get_drop_glue_type(self.scx, env_ty); - if self.scx.type_needs_drop(env_ty) { - let dg = DropGlueKind::Ty(env_ty); - self.output.push(TransItem::DropGlue(dg)); - } + let instance = + monomorphize::resolve(self.scx, callee_def_id, callee_substs); + if should_trans_locally(self.scx.tcx(), &instance) { + if let ty::InstanceDef::ClosureOnceShim { .. } = instance.def { + // This call will instantiate an FnOnce adapter, which + // drops the closure environment. Therefore we need to + // make sure that we collect the drop-glue for the + // environment type. + + let env_ty = instance.substs.type_at(0); + let env_ty = glue::get_drop_glue_type(self.scx, env_ty); + if self.scx.type_needs_drop(env_ty) { + let dg = DropGlueKind::Ty(env_ty); + self.output.push(TransItem::DropGlue(dg)); } } + self.output.push(create_fn_trans_item(instance)); } } @@ -664,8 +652,13 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>) -> bool { let def_id = match instance.def { - ty::InstanceDef::Item(def_id) => def_id, - ty::InstanceDef::FnPtrShim(..) => return true + ty::InstanceDef::Item(def_id) | + ty::InstanceDef::ClosureOnceShim { + call_once: _, closure_did: def_id + } => def_id, + ty::InstanceDef::FnPtrShim(..) => return true, + ty::InstanceDef::Virtual(..) | + ty::InstanceDef::Intrinsic(_) => return false }; match tcx.hir.get_if_local(def_id) { Some(hir_map::NodeForeignItem(..)) => { @@ -718,31 +711,21 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // If the type implements Drop, also add a translation item for the // monomorphized Drop::drop() implementation. - let destructor = match ty.sty { - ty::TyAdt(def, _) => def.destructor(scx.tcx()), - _ => None + let has_dtor = match ty.sty { + ty::TyAdt(def, _) => def.has_dtor(scx.tcx()), + _ => false }; - if let (Some(destructor), false) = (destructor, ty.is_box()) { - use rustc::ty::ToPolyTraitRef; - + if has_dtor && !ty.is_box() { let drop_trait_def_id = scx.tcx() .lang_items .drop_trait() .unwrap(); - - let self_type_substs = scx.tcx().mk_substs_trait(ty, &[]); - - let trait_ref = ty::TraitRef { - def_id: drop_trait_def_id, - substs: self_type_substs, - }.to_poly_trait_ref(); - - let substs = match fulfill_obligation(scx, DUMMY_SP, trait_ref) { - traits::VtableImpl(data) => data.substs, - _ => bug!() - }; - let instance = Instance::new(destructor.did, substs); + let drop_method = scx.tcx().associated_items(drop_trait_def_id) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let substs = scx.tcx().mk_substs_trait(ty, &[]); + let instance = monomorphize::resolve(scx, drop_method, substs); if should_trans_locally(scx.tcx(), &instance) { output.push(create_fn_trans_item(instance)); } @@ -817,115 +800,6 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } } -enum StaticDispatchResult<'tcx> { - // The call could be resolved statically as going to the method with - // `instance`. - Dispatched { - instance: Instance<'tcx>, - // If this is a call to a closure that needs an FnOnce adjustment, - // this contains the new self type of the call (= type of the closure - // environment) - fn_once_adjustment: Option>, - }, - // This goes to somewhere that we don't know at compile-time - Unknown -} - -fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - fn_def_id: DefId, - fn_substs: &'tcx Substs<'tcx>) - -> StaticDispatchResult<'tcx> { - debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?})", - def_id_to_string(scx.tcx(), fn_def_id), - fn_substs); - if let Some(trait_def_id) = scx.tcx().trait_of_item(fn_def_id) { - debug!(" => trait method, attempting to find impl"); - do_static_trait_method_dispatch(scx, - &scx.tcx().associated_item(fn_def_id), - trait_def_id, - fn_substs) - } else { - debug!(" => regular function"); - // The function is not part of an impl or trait, no dispatching - // to be done - StaticDispatchResult::Dispatched { - instance: Instance::new(fn_def_id, fn_substs), - fn_once_adjustment: None, - } - } -} - -// Given a trait-method and substitution information, find out the actual -// implementation of the trait method. -fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - trait_method: &ty::AssociatedItem, - trait_id: DefId, - rcvr_substs: &'tcx Substs<'tcx>) - -> StaticDispatchResult<'tcx> { - let tcx = scx.tcx(); - debug!("do_static_trait_method_dispatch(trait_method={}, \ - trait_id={}, \ - rcvr_substs={:?})", - def_id_to_string(scx.tcx(), trait_method.def_id), - def_id_to_string(scx.tcx(), trait_id), - rcvr_substs); - - let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = fulfill_obligation(scx, DUMMY_SP, ty::Binder(trait_ref)); - - // Now that we know which impl is being used, we can dispatch to - // the actual function: - match vtbl { - traits::VtableImpl(impl_data) => { - StaticDispatchResult::Dispatched { - instance: find_method(tcx, trait_method.name, rcvr_substs, &impl_data), - fn_once_adjustment: None, - } - } - traits::VtableClosure(closure_data) => { - let closure_def_id = closure_data.closure_def_id; - let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - let actual_closure_kind = tcx.closure_kind(closure_def_id); - - let needs_fn_once_adapter_shim = - match needs_fn_once_adapter_shim(actual_closure_kind, - trait_closure_kind) { - Ok(true) => true, - _ => false, - }; - - let fn_once_adjustment = if needs_fn_once_adapter_shim { - Some(tcx.mk_closure_from_closure_substs(closure_def_id, - closure_data.substs)) - } else { - None - }; - - StaticDispatchResult::Dispatched { - instance: Instance::new(closure_def_id, closure_data.substs.substs), - fn_once_adjustment: fn_once_adjustment, - } - } - traits::VtableFnPointer(ref data) => { - StaticDispatchResult::Dispatched { - instance: Instance { - def: ty::InstanceDef::FnPtrShim(trait_method.def_id, data.fn_ty), - substs: trait_ref.substs - }, - fn_once_adjustment: None, - } - } - // Trait object shims are always instantiated in-place, and as they are - // just an ABI-adjusting indirect call they do not have any dependencies. - traits::VtableObject(..) => { - StaticDispatchResult::Unknown - } - _ => { - bug!("static call to invalid vtable: {:?}", vtbl) - } - } -} - /// For given pair of source and target type that occur in an unsizing coercion, /// this function finds the pair of types that determines the vtable linking /// them. @@ -991,7 +865,8 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, &ty::TyAdt(target_adt_def, target_substs)) => { assert_eq!(source_adt_def, target_adt_def); - let kind = custom_coerce_unsize_info(scx, source_ty, target_ty); + let kind = + monomorphize::custom_coerce_unsize_info(scx, source_ty, target_ty); let coerce_index = match kind { CustomCoerceUnsized::Struct(i) => i @@ -1017,6 +892,20 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn create_fn_trans_item<'a, 'tcx>(instance: Instance<'tcx>) -> TransItem<'tcx> { debug!("create_fn_trans_item(instance={})", instance); + let instance = match instance.def { + ty::InstanceDef::ClosureOnceShim { .. } => { + // HACK: don't create ClosureOnce trans items for now + // have someone else generate the drop glue + let closure_ty = instance.substs.type_at(0); + match closure_ty.sty { + ty::TyClosure(def_id, substs) => { + Instance::new(def_id, substs.substs) + } + _ => bug!("bad closure instance {:?}", instance) + } + } + _ => instance + }; TransItem::Fn(instance) } @@ -1037,18 +926,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, // Walk all methods of the trait, including those of its supertraits let methods = traits::get_vtable_methods(scx.tcx(), poly_trait_ref); let methods = methods.filter_map(|method| method) - .filter_map(|(def_id, substs)| { - if let StaticDispatchResult::Dispatched { - instance, - // We already add the drop-glue for the closure env - // unconditionally below. - fn_once_adjustment: _ , - } = do_static_dispatch(scx, def_id, substs) { - Some(instance) - } else { - None - } - }) + .map(|(def_id, substs)| monomorphize::resolve(scx, def_id, substs)) .filter(|&instance| should_trans_locally(scx.tcx(), &instance)) .map(|instance| create_fn_trans_item(instance)); output.extend(methods); @@ -1203,18 +1081,11 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(scx: &SharedCrateContext<'a, ' continue; } - // The substitutions we have are on the impl, so we grab - // the method type from the impl to substitute into. - let impl_substs = tcx.empty_substs_for_def_id(impl_def_id); - let impl_data = traits::VtableImplData { - impl_def_id: impl_def_id, - substs: impl_substs, - nested: vec![] - }; - let instance = find_method(tcx, method.name, callee_substs, &impl_data); + let instance = + monomorphize::resolve(scx, method.def_id, callee_substs); let predicates = tcx.item_predicates(instance.def_id()).predicates - .subst(tcx, impl_substs); + .subst(tcx, instance.substs); if !traits::normalize_and_test_predicates(tcx, predicates) { continue; } diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 4389207cdf294..431325c5ec74e 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -17,7 +17,6 @@ use llvm::{ValueRef, ContextRef, TypeKind}; use llvm::{True, False, Bool, OperandBundleDef}; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; -use rustc::util::common::MemoizationMap; use middle::lang_items::LangItem; use base; use builder::Builder; @@ -30,13 +29,11 @@ use value::Value; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::layout::Layout; use rustc::ty::subst::{Subst, Substs}; -use rustc::traits::{self, SelectionContext, Reveal}; use rustc::hir; use libc::{c_uint, c_char}; use std::iter; -use syntax::ast; use syntax::attr; use syntax::symbol::InternedString; use syntax_pos::Span; @@ -427,73 +424,6 @@ pub fn is_null(val: ValueRef) -> bool { } } -/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we -/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should -/// guarantee to us that all nested obligations *could be* resolved if we wanted to. -pub fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - span: Span, - trait_ref: ty::PolyTraitRef<'tcx>) - -> traits::Vtable<'tcx, ()> -{ - let tcx = scx.tcx(); - - // Remove any references to regions; this helps improve caching. - let trait_ref = tcx.erase_regions(&trait_ref); - - scx.trait_cache().memoize(trait_ref, || { - debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", - trait_ref, trait_ref.def_id()); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - - let obligation_cause = traits::ObligationCause::misc(span, - ast::DUMMY_NODE_ID); - let obligation = traits::Obligation::new(obligation_cause, - trait_ref.to_poly_trait_predicate()); - - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongo type that never occurred - // statically -- this humongo type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - debug!("Encountered ambiguity selecting `{:?}` during trans, \ - presuming due to overflow", - trait_ref); - tcx.sess.span_fatal(span, - "reached the recursion limit during monomorphization \ - (selection ambiguity)"); - } - Err(e) => { - span_bug!(span, "Encountered error `{:?}` selecting `{:?}` during trans", - e, trait_ref) - } - }; - - debug!("fulfill_obligation: selection={:?}", selection); - - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = traits::FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); - - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); - vtable - }) - }) -} - pub fn langcall(tcx: TyCtxt, span: Option, msg: &str, @@ -617,6 +547,7 @@ pub fn requests_inline<'a, 'tcx>( _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), } } + /// Given a DefId and some Substs, produces the monomorphic item type. pub fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, def_id: DefId, @@ -635,14 +566,3 @@ pub fn instance_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>, let ty = instance.def.def_ty(shared.tcx()); monomorphize::apply_param_substs(shared, instance.substs, &ty) } - -pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - name: ast::Name, - substs: &'tcx Substs<'tcx>, - impl_data: &traits::VtableImplData<'tcx, ()>) - -> ty::Instance<'tcx> -{ - let (def_id, substs) = traits::find_method(tcx, name, substs, impl_data); - let substs = tcx.erase_regions(&substs); - ty::Instance::new(def_id, substs) -} diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 35ebd67b5f8c1..2eb94aa56ab50 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -39,7 +39,6 @@ use value::Value; use Disr; use builder::Builder; -use syntax_pos::DUMMY_SP; use mir::lvalue::Alignment; pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef<'tcx>) { @@ -241,8 +240,6 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi let shallow_drop = def.is_union(); let tcx = bcx.tcx(); - let def = t.ty_adt_def().unwrap(); - // Be sure to put the contents into a scope so we can use an invoke // instruction to call the user destructor but still call the field // destructors if the user destructor panics. @@ -256,17 +253,12 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi } else { CleanupScope::noop() }; - - let trait_ref = ty::Binder(ty::TraitRef { - def_id: tcx.lang_items.drop_trait().unwrap(), - substs: tcx.mk_substs_trait(t, &[]) - }); - let vtbl = match fulfill_obligation(bcx.ccx.shared(), DUMMY_SP, trait_ref) { - traits::VtableImpl(data) => data, - _ => bug!("dtor for {:?} is not an impl???", t) - }; - let dtor_did = def.destructor(tcx).unwrap().did; - let callee = Callee::def(bcx.ccx, dtor_did, vtbl.substs); + let drop_trait_def_id = tcx.lang_items.drop_trait().unwrap(); + let drop_method = tcx.associated_items(drop_trait_def_id) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let self_type_substs = tcx.mk_substs_trait(t, &[]); + let callee = Callee::def(bcx.ccx, drop_method, self_type_substs); let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); let llret; let args = &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize]; diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 1530fcda3d3ea..49cc0b5fad401 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -28,6 +28,7 @@ #![feature(box_syntax)] #![feature(const_fn)] #![feature(custom_attribute)] +#![cfg_attr(stage0, feature(field_init_shorthand))] #![allow(unused_attributes)] #![feature(i128_type)] #![feature(libc)] diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index deb1073cf9aed..69009ab356044 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -249,7 +249,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, args: IndexVec>) -> Result, ConstEvalErr<'tcx>> { - let instance = monomorphize::resolve_const(ccx.shared(), def_id, substs); + let instance = monomorphize::resolve(ccx.shared(), def_id, substs); let mir = ccx.tcx().instance_mir(instance.def); MirConstContext::new(ccx, &mir, instance.substs, args).trans() } diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 3b746af275a2a..bf073d8b97844 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -8,38 +8,260 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use abi::Abi; use common::*; + use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; -use rustc::traits; +use rustc::traits::{self, SelectionContext, Reveal}; +use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::ty::fold::{TypeFolder, TypeFoldable}; -use rustc::ty::subst::{Subst, Substs}; +use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::common::MemoizationMap; -use syntax::codemap::DUMMY_SP; +use syntax::ast; +use syntax::codemap::{Span, DUMMY_SP}; pub use rustc::ty::Instance; -/// For associated constants from traits, return the impl definition. -pub fn resolve_const<'a, 'tcx>( - scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx> +fn fn_once_adapter_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + closure_did: DefId, + substs: ty::ClosureSubsts<'tcx>, + ) -> Instance<'tcx> { + debug!("fn_once_adapter_shim({:?}, {:?})", + closure_did, + substs); + let fn_once = tcx.lang_items.fn_once_trait().unwrap(); + let call_once = tcx.associated_items(fn_once) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let def = ty::InstanceDef::ClosureOnceShim { call_once, closure_did }; + + let self_ty = tcx.mk_closure_from_closure_substs( + closure_did, substs); + + let sig = tcx.closure_type(closure_did).subst(tcx, substs.substs); + let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + assert_eq!(sig.inputs().len(), 1); + let substs = tcx.mk_substs([ + Kind::from(self_ty), + Kind::from(sig.inputs()[0]), + ].iter().cloned()); + + debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); + Instance { def, substs } +} + +/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we +/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should +/// guarantee to us that all nested obligations *could be* resolved if we wanted to. +fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + span: Span, + trait_ref: ty::PolyTraitRef<'tcx>) + -> traits::Vtable<'tcx, ()> +{ + let tcx = scx.tcx(); + + // Remove any references to regions; this helps improve caching. + let trait_ref = tcx.erase_regions(&trait_ref); + + scx.trait_cache().memoize(trait_ref, || { + debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", + trait_ref, trait_ref.def_id()); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + + let obligation_cause = traits::ObligationCause::misc(span, + ast::DUMMY_NODE_ID); + let obligation = traits::Obligation::new(obligation_cause, + trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + debug!("Encountered ambiguity selecting `{:?}` during trans, \ + presuming due to overflow", + trait_ref); + tcx.sess.span_fatal(span, + "reached the recursion limit during monomorphization \ + (selection ambiguity)"); + } + Err(e) => { + span_bug!(span, "Encountered error `{:?}` selecting `{:?}` during trans", + e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); + + info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + vtable + }) + }) +} + +fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind) + -> Result +{ + match (actual_closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { + // No adapter needed. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { + // The closure fn `llfn` is a `fn(&self, ...)`. We want a + // `fn(&mut self, ...)`. In fact, at trans time, these are + // basically the same thing, so we can just return llfn. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + Ok(true) + } + _ => Err(()), + } +} + +fn resolve_associated_item<'a, 'tcx>( + scx: &SharedCrateContext<'a, 'tcx>, + trait_item: &ty::AssociatedItem, + trait_id: DefId, + rcvr_substs: &'tcx Substs<'tcx> ) -> Instance<'tcx> { - if let Some(trait_id) = scx.tcx().trait_of_item(def_id) { - let trait_ref = ty::TraitRef::new(trait_id, substs); - let trait_ref = ty::Binder(trait_ref); - let vtable = fulfill_obligation(scx, DUMMY_SP, trait_ref); - if let traits::VtableImpl(vtable_impl) = vtable { - let name = scx.tcx().item_name(def_id); - let ac = scx.tcx().associated_items(vtable_impl.impl_def_id) - .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); - if let Some(ac) = ac { - return Instance::new(ac.def_id, vtable_impl.substs); + let tcx = scx.tcx(); + let def_id = trait_item.def_id; + debug!("resolve_associated_item(trait_item={:?}, \ + trait_id={:?}, \ + rcvr_substs={:?})", + def_id, trait_id, rcvr_substs); + + let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); + let vtbl = fulfill_obligation(scx, DUMMY_SP, ty::Binder(trait_ref)); + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + match vtbl { + traits::VtableImpl(impl_data) => { + let (def_id, substs) = traits::find_associated_item( + tcx, trait_item, rcvr_substs, &impl_data); + let substs = tcx.erase_regions(&substs); + ty::Instance::new(def_id, substs) + } + traits::VtableClosure(closure_data) => { + let closure_def_id = closure_data.closure_def_id; + let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + let actual_closure_kind = tcx.closure_kind(closure_def_id); + + match needs_fn_once_adapter_shim(actual_closure_kind, + trait_closure_kind) { + Ok(true) => fn_once_adapter_instance( + tcx, closure_def_id, closure_data.substs), + _ => Instance::new(closure_def_id, closure_data.substs.substs), + } + } + traits::VtableFnPointer(ref data) => { + Instance { + def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + substs: rcvr_substs + } + } + traits::VtableObject(ref data) => { + let index = tcx.get_vtable_index_of_object_method(data, def_id); + Instance { + def: ty::InstanceDef::Virtual(def_id, index), + substs: rcvr_substs } } + _ => { + bug!("static call to invalid vtable: {:?}", vtbl) + } } +} - Instance::new(def_id, substs) +/// The point where linking happens. Resolve a (def_id, substs) +/// pair to an instance. +pub fn resolve<'a, 'tcx>( + scx: &SharedCrateContext<'a, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx> +) -> Instance<'tcx> { + debug!("resolve(def_id={:?}, substs={:?})", + def_id, substs); + let result = if let Some(trait_def_id) = scx.tcx().trait_of_item(def_id) { + debug!(" => associated item, attempting to find impl"); + let item = scx.tcx().associated_item(def_id); + resolve_associated_item(scx, &item, trait_def_id, substs) + } else { + let item_type = def_ty(scx, def_id, substs); + let def = match item_type.sty { + ty::TyFnDef(_, _, f) if + f.abi() == Abi::RustIntrinsic || + f.abi() == Abi::PlatformIntrinsic => + { + debug!(" => intrinsic"); + ty::InstanceDef::Intrinsic(def_id) + } + _ => { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } + }; + Instance { def, substs } + }; + debug!("resolve(def_id={:?}, substs={:?}) = {}", + def_id, substs, result); + result +} + +pub fn custom_coerce_unsize_info<'scx, 'tcx>(scx: &SharedCrateContext<'scx, 'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>) + -> CustomCoerceUnsized { + let trait_ref = ty::Binder(ty::TraitRef { + def_id: scx.tcx().lang_items.coerce_unsized_trait().unwrap(), + substs: scx.tcx().mk_substs_trait(source_ty, &[target_ty]) + }); + + match fulfill_obligation(scx, DUMMY_SP, trait_ref) { + traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { + scx.tcx().custom_coerce_unsized_kind(impl_def_id) + } + vtable => { + bug!("invalid CoerceUnsized vtable: {:?}", vtable); + } + } } /// Monomorphizes a type from the AST by first applying the in-scope diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 1232c6cd28e52..d0cf32508d44c 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -458,7 +458,10 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't TransItem::Fn(instance) => { let def_id = match instance.def { ty::InstanceDef::Item(def_id) => def_id, - ty::InstanceDef::FnPtrShim(..) => return None + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::ClosureOnceShim { .. } | + ty::InstanceDef::Intrinsic(..) | + ty::InstanceDef::Virtual(..) => return None }; // If this is a method, we want to put it into the same module as From 65a4266f1f4ce9dac5a6ef588443b7cac911d265 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Mar 2017 18:33:21 +0200 Subject: [PATCH 05/11] refactor away callee::Callee and translate virtual calls through a MIR shim These changes are in the same commit to avoid needing to adapt meth::trans_object_shim to the new scheme. One codegen-units test is broken because we instantiate the shims even when they are not needed. This will be fixed in the next PR. --- src/librustc_mir/lib.rs | 1 + src/librustc_mir/mir_map.rs | 5 +- src/librustc_mir/shim.rs | 213 ++++++++++++++++++----------- src/librustc_trans/abi.rs | 31 ++++- src/librustc_trans/base.rs | 8 +- src/librustc_trans/callee.rs | 150 +++++++------------- src/librustc_trans/collector.rs | 2 +- src/librustc_trans/context.rs | 6 +- src/librustc_trans/glue.rs | 20 +-- src/librustc_trans/meth.rs | 78 +---------- src/librustc_trans/mir/block.rs | 156 +++++++++++---------- src/librustc_trans/mir/constant.rs | 8 +- src/librustc_trans/mir/rvalue.rs | 13 +- 13 files changed, 324 insertions(+), 367 deletions(-) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 9e6b77dbabdef..2718a0204a1ed 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -23,6 +23,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(associated_consts)] #![feature(box_patterns)] #![feature(box_syntax)] +#![cfg_attr(stage0, feature(field_init_shorthand))] #![feature(i128_type)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 3fa7131a2b6b0..2d2b90235ca3b 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -252,12 +252,9 @@ fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, -> Ty<'tcx> { let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_id); - // We're just hard-coding the idea that the signature will be - // &self or &mut self and hence will have a bound region with - // number 0, hokey. let region = ty::Region::ReFree(ty::FreeRegion { scope: tcx.region_maps.item_extent(body_id.node_id), - bound_region: ty::BoundRegion::BrAnon(0), + bound_region: ty::BoundRegion::BrEnv, }); let region = tcx.mk_region(region); diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 2a053535e7e10..97c83c7a42e28 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -9,17 +9,19 @@ // except according to those terms. use rustc::hir; +use rustc::hir::def_id::DefId; use rustc::infer; +use rustc::middle::region::ROOT_CODE_EXTENT; use rustc::mir::*; use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty}; +use rustc::ty::subst::Subst; use rustc::ty::maps::Providers; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax::abi::Abi; use syntax::ast; -use syntax::codemap::DUMMY_SP; use syntax_pos::Span; use std::cell::RefCell; @@ -35,11 +37,51 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, -> &'tcx RefCell> { debug!("make_shim({:?})", instance); + let did = instance.def_id(); + let span = tcx.def_span(did); + let param_env = + tcx.construct_parameter_environment(span, did, ROOT_CODE_EXTENT); + let result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceDef::FnPtrShim(_, ty) => { - build_fn_ptr_shim(tcx, ty, instance.def_ty(tcx)) + ty::InstanceDef::FnPtrShim(def_id, ty) => { + let trait_ = tcx.trait_of_item(def_id).unwrap(); + let adjustment = match tcx.lang_items.fn_trait_kind(trait_) { + Some(ty::ClosureKind::FnOnce) => Adjustment::Identity, + Some(ty::ClosureKind::FnMut) | + Some(ty::ClosureKind::Fn) => Adjustment::Deref, + None => bug!("fn pointer {:?} is not an fn", ty) + }; + // HACK: we need the "real" argument types for the MIR, + // but because our substs are (Self, Args), where Args + // is a tuple, we must include the *concrete* argument + // types in the MIR. They will be substituted again with + // the param-substs, but because they are concrete, this + // will not do any harm. + let sig = tcx.erase_late_bound_regions(&ty.fn_sig()); + let arg_tys = sig.inputs(); + + build_call_shim( + tcx, + ¶m_env, + def_id, + adjustment, + CallKind::Indirect, + Some(arg_tys) + ) + } + ty::InstanceDef::Virtual(def_id, _) => { + // We are translating a call back to our def-id, which + // trans::mir knows to turn to an actual virtual call. + build_call_shim( + tcx, + ¶m_env, + def_id, + Adjustment::Identity, + CallKind::Direct(def_id), + None + ) } _ => bug!("unknown shim kind") }; @@ -51,124 +93,135 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, result } +#[derive(Copy, Clone, Debug, PartialEq)] +enum Adjustment { + Identity, + Deref, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum CallKind { + Indirect, + Direct(DefId), +} + +fn temp_decl(mutability: Mutability, ty: Ty) -> LocalDecl { + LocalDecl { mutability, ty, name: None, source_info: None } +} + fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) -> IndexVec> { - iter::once(LocalDecl { - mutability: Mutability::Mut, - ty: sig.output(), - name: None, - source_info: None - }).chain(sig.inputs().iter().map(|ity| LocalDecl { - mutability: Mutability::Not, - ty: *ity, - name: None, - source_info: None, - })).collect() + iter::once(temp_decl(Mutability::Mut, sig.output())) + .chain(sig.inputs().iter().map( + |ity| temp_decl(Mutability::Not, ity))) + .collect() } - -fn build_fn_ptr_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, - fn_ty: Ty<'tcx>, - sig_ty: Ty<'tcx>) - -> Mir<'tcx> +/// Build a "call" shim for `def_id`. The shim calls the +/// function specified by `call_kind`, first adjusting its first +/// argument according to `rcvr_adjustment`. +/// +/// If `untuple_args` is a vec of types, the second argument of the +/// function will be untupled as these types. +fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + def_id: DefId, + rcvr_adjustment: Adjustment, + call_kind: CallKind, + untuple_args: Option<&[Ty<'tcx>]>) + -> Mir<'tcx> { - debug!("build_fn_ptr_shim(fn_ty={:?}, sig_ty={:?})", fn_ty, sig_ty); - let trait_sig = match sig_ty.sty { - ty::TyFnDef(_, _, fty) => tcx.erase_late_bound_regions(&fty), - _ => bug!("unexpected type for shim {:?}", sig_ty) - }; + debug!("build_call_shim(def_id={:?}, rcvr_adjustment={:?}, \ + call_kind={:?}, untuple_args={:?})", + def_id, rcvr_adjustment, call_kind, untuple_args); - let self_ty = match trait_sig.inputs()[0].sty { - ty::TyParam(..) => fn_ty, - ty::TyRef(r, mt) => tcx.mk_ref(r, ty::TypeAndMut { - ty: fn_ty, - mutbl: mt.mutbl - }), - _ => bug!("unexpected self_ty {:?}", trait_sig), - }; + let fn_ty = tcx.item_type(def_id).subst(tcx, param_env.free_substs); + // Not normalizing here without a param env. + let sig = tcx.erase_late_bound_regions(&fn_ty.fn_sig()); + let span = tcx.def_span(def_id); - let fn_ptr_sig = match fn_ty.sty { - ty::TyFnPtr(fty) | - ty::TyFnDef(_, _, fty) => - tcx.erase_late_bound_regions_and_normalize(&fty), - _ => bug!("non-fn-ptr {:?} in build_fn_ptr_shim", fn_ty) - }; - - let sig = tcx.mk_fn_sig( - [ - self_ty, - tcx.intern_tup(fn_ptr_sig.inputs(), false) - ].iter().cloned(), - fn_ptr_sig.output(), - false, - hir::Unsafety::Normal, - Abi::RustCall, - ); + debug!("build_call_shim: sig={:?}", sig); let local_decls = local_decls_for_sig(&sig); - let source_info = SourceInfo { - span: DUMMY_SP, - scope: ARGUMENT_VISIBILITY_SCOPE + let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; + + let rcvr_l = Lvalue::Local(Local::new(1+0)); + + let return_block_id = BasicBlock::new(1); + + let rcvr = match rcvr_adjustment { + Adjustment::Identity => Operand::Consume(rcvr_l), + Adjustment::Deref => Operand::Consume(Lvalue::Projection( + box Projection { base: rcvr_l, elem: ProjectionElem::Deref } + )) }; - let fn_ptr = Lvalue::Local(Local::new(1+0)); - let fn_ptr = match trait_sig.inputs()[0].sty { - ty::TyParam(..) => fn_ptr, - ty::TyRef(..) => Lvalue::Projection(box Projection { - base: fn_ptr, elem: ProjectionElem::Deref - }), - _ => bug!("unexpected self_ty {:?}", trait_sig), + let (callee, mut args) = match call_kind { + CallKind::Indirect => (rcvr, vec![]), + CallKind::Direct(def_id) => ( + Operand::Constant(Constant { + span: span, + ty: tcx.item_type(def_id).subst(tcx, param_env.free_substs), + literal: Literal::Item { def_id, substs: param_env.free_substs }, + }), + vec![rcvr] + ) }; - let fn_args = Local::new(1+1); - let return_block_id = BasicBlock::new(1); + if let Some(untuple_args) = untuple_args { + args.extend(untuple_args.iter().enumerate().map(|(i, ity)| { + let arg_lv = Lvalue::Local(Local::new(1+1)); + Operand::Consume(Lvalue::Projection(box Projection { + base: arg_lv, + elem: ProjectionElem::Field(Field::new(i), *ity) + })) + })); + } else { + args.extend((1..sig.inputs().len()).map(|i| { + Operand::Consume(Lvalue::Local(Local::new(1+i))) + })); + } - // return = ADT(arg0, arg1, ...); return - let start_block = BasicBlockData { + let mut blocks = IndexVec::new(); + blocks.push(BasicBlockData { statements: vec![], terminator: Some(Terminator { source_info: source_info, kind: TerminatorKind::Call { - func: Operand::Consume(fn_ptr), - args: fn_ptr_sig.inputs().iter().enumerate().map(|(i, ity)| { - Operand::Consume(Lvalue::Projection(box Projection { - base: Lvalue::Local(fn_args), - elem: ProjectionElem::Field( - Field::new(i), *ity - ) - })) - }).collect(), - // FIXME: can we pass a Some destination for an uninhabited ty? + func: callee, + args: args, destination: Some((Lvalue::Local(RETURN_POINTER), return_block_id)), cleanup: None } }), is_cleanup: false - }; - let return_block = BasicBlockData { + }); + blocks.push(BasicBlockData { statements: vec![], terminator: Some(Terminator { source_info: source_info, kind: TerminatorKind::Return }), is_cleanup: false - }; + }); let mut mir = Mir::new( - vec![start_block, return_block].into_iter().collect(), + blocks, IndexVec::from_elem_n( - VisibilityScopeData { span: DUMMY_SP, parent_scope: None }, 1 + VisibilityScopeData { span: span, parent_scope: None }, 1 ), IndexVec::new(), sig.output(), local_decls, sig.inputs().len(), vec![], - DUMMY_SP + span ); - mir.spread_arg = Some(fn_args); + if let Abi::RustCall = sig.abi { + mir.spread_arg = Some(Local::new(sig.inputs().len())); + } mir } diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 0bbe981f2f72c..710aa1fbbc6d9 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -11,7 +11,7 @@ use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace}; use base; use builder::Builder; -use common::{type_is_fat_ptr, C_uint}; +use common::{self, type_is_fat_ptr, C_uint}; use context::CrateContext; use cabi_x86; use cabi_x86_64; @@ -334,9 +334,30 @@ impl FnType { fn_ty } - pub fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + pub fn new_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> FnType { + let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); + // Don't pass the vtable, it's not an argument of the virtual fn. + fn_ty.args[1].ignore(); + fn_ty.adjust_for_abi(ccx, sig); + fn_ty + } + + pub fn from_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + instance: &ty::Instance<'tcx>, + extra_args: &[Ty<'tcx>]) -> FnType + { + let ity = common::instance_ty(ccx.shared(), instance); + let sig = common::ty_fn_sig(ccx, ity); + let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); + + Self::new(ccx, sig, extra_args) + } + + fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> FnType { use self::Abi::*; let cconv = match ccx.sess().target.target.adjust_abi(sig.abi) { RustIntrinsic | PlatformIntrinsic | @@ -532,9 +553,9 @@ impl FnType { } } - pub fn adjust_for_abi<'a, 'tcx>(&mut self, - ccx: &CrateContext<'a, 'tcx>, - sig: ty::FnSig<'tcx>) { + fn adjust_for_abi<'a, 'tcx>(&mut self, + ccx: &CrateContext<'a, 'tcx>, + sig: ty::FnSig<'tcx>) { let abi = sig.abi; if abi == Abi::Unadjusted { return } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 10ab199671f64..a58764878a318 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -47,7 +47,7 @@ use abi; use mir::lvalue::LvalueRef; use attributes; use builder::Builder; -use callee::{Callee}; +use callee; use common::{C_bool, C_bytes_in_context, C_i32, C_uint}; use collector::{self, TransItemCollectionMode}; use common::{C_struct_in_context, C_u64, C_undef}; @@ -654,7 +654,7 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { return; } - let main_llfn = Callee::def(ccx, main_def_id, instance.substs).reify(ccx); + let main_llfn = callee::get_fn(ccx, instance); let et = ccx.sess().entry_type.get().unwrap(); match et { @@ -688,8 +688,8 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { let (start_fn, args) = if use_start_lang_item { let start_def_id = ccx.tcx().require_lang_item(StartFnLangItem); - let empty_substs = ccx.tcx().intern_substs(&[]); - let start_fn = Callee::def(ccx, start_def_id, empty_substs).reify(ccx); + let start_instance = Instance::mono(ccx.tcx(), start_def_id); + let start_fn = callee::get_fn(ccx, start_instance); (start_fn, vec![bld.pointercast(rust_main, Type::i8p(ccx).ptr_to()), get_param(llfn, 0), get_param(llfn, 1)]) } else { diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 70da2a69ef425..5c7be56b56dad 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -14,8 +14,6 @@ //! and methods are represented as just a fn ptr and not a full //! closure. -pub use self::CalleeData::*; - use llvm::{self, ValueRef, get_params}; use rustc::hir::def_id::DefId; use rustc::ty::subst::{Substs, Subst}; @@ -27,98 +25,17 @@ use cleanup::CleanupScope; use mir::lvalue::LvalueRef; use monomorphize; use consts; -use common::instance_ty; use declare; use value::Value; -use meth; use monomorphize::Instance; use back::symbol_names::symbol_name; use trans_item::TransItem; use type_of; -use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::ty::{self, TypeFoldable}; use std::iter; use mir::lvalue::Alignment; -#[derive(Debug)] -pub enum CalleeData { - /// Function pointer. - Fn(ValueRef), - - Intrinsic, - - /// Trait object found in the vtable at that index. - Virtual(usize) -} - -#[derive(Debug)] -pub struct Callee<'tcx> { - pub data: CalleeData, - pub ty: Ty<'tcx> -} - -impl<'tcx> Callee<'tcx> { - /// Function or method definition. - pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>) - -> Callee<'tcx> { - let instance = monomorphize::resolve(ccx.shared(), def_id, substs); - let ty = instance_ty(ccx.shared(), &instance); - let data = match instance.def { - ty::InstanceDef::Intrinsic(_) => Intrinsic, - ty::InstanceDef::ClosureOnceShim { .. } => { - let closure_ty = instance.substs.type_at(0); - let (closure_def_id, closure_substs) = match closure_ty.sty { - ty::TyClosure(def_id, substs) => (def_id, substs), - _ => bug!("bad closure instance {:?}", instance) - }; - - Fn(trans_fn_once_adapter_shim( - ccx, - closure_def_id, - closure_substs, - instance, - get_fn( - ccx, - Instance::new(closure_def_id, closure_substs.substs) - ) - )) - } - ty::InstanceDef::Virtual(_, n) => Virtual(n), - ty::InstanceDef::FnPtrShim(..) | - ty::InstanceDef::Item(..) => { - Fn(get_fn(ccx, instance)) - } - }; - - Callee { data, ty } - } - - /// Get the abi::FnType for a direct call. Mainly deals with the fact - /// that a Virtual call doesn't take the vtable, like its shim does. - /// The extra argument types are for variadic (extern "C") functions. - pub fn direct_fn_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>, - extra_args: &[Ty<'tcx>]) -> FnType { - let sig = common::ty_fn_sig(ccx, self.ty); - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); - let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); - if let Virtual(_) = self.data { - // Don't pass the vtable, it's not an argument of the virtual fn. - fn_ty.args[1].ignore(); - } - fn_ty.adjust_for_abi(ccx, sig); - fn_ty - } - - /// Turn the callee into a function pointer. - pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef { - match self.data { - Fn(llfn) => llfn, - Virtual(_) => meth::trans_object_shim(ccx, self), - Intrinsic => bug!("intrinsic {} getting reified", self.ty) - } - } -} - fn trans_fn_once_adapter_shim<'a, 'tcx>( ccx: &'a CrateContext<'a, 'tcx>, def_id: DefId, @@ -145,13 +62,14 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let sig = tcx.closure_type(def_id).subst(tcx, substs.substs); let sig = tcx.erase_late_bound_regions_and_normalize(&sig); assert_eq!(sig.abi, Abi::RustCall); - let llref_fn_ty = tcx.mk_fn_ptr(ty::Binder(tcx.mk_fn_sig( + let llref_fn_sig = tcx.mk_fn_sig( iter::once(ref_closure_ty).chain(sig.inputs().iter().cloned()), sig.output(), sig.variadic, sig.unsafety, Abi::RustCall - ))); + ); + let llref_fn_ty = tcx.mk_fn_ptr(ty::Binder(llref_fn_sig)); debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}", llref_fn_ty); @@ -177,15 +95,10 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let orig_fn_ty = fn_ty; let mut bcx = Builder::new_block(ccx, lloncefn, "entry-block"); - let callee = Callee { - data: Fn(llreffn), - ty: llref_fn_ty - }; - // the first argument (`self`) will be the (by value) closure env. let mut llargs = get_params(lloncefn); - let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); + let fn_ty = FnType::new(ccx, llref_fn_sig, &[]); let self_idx = fn_ty.ret.is_indirect() as usize; let env_arg = &orig_fn_ty.args[0]; let env = if env_arg.is_indirect() { @@ -210,14 +123,13 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // to drop `self` when the body returns, or in case it unwinds. let self_scope = CleanupScope::schedule_drop_mem(&bcx, env); - let llfn = callee.reify(bcx.ccx); let llret; if let Some(landing_pad) = self_scope.landing_pad { let normal_bcx = bcx.build_sibling_block("normal-return"); - llret = bcx.invoke(llfn, &llargs[..], normal_bcx.llbb(), landing_pad, None); + llret = bcx.invoke(llreffn, &llargs[..], normal_bcx.llbb(), landing_pad, None); bcx = normal_bcx; } else { - llret = bcx.call(llfn, &llargs[..], None); + llret = bcx.call(llreffn, &llargs[..], None); } fn_ty.apply_attrs_callsite(llret); @@ -247,9 +159,9 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( /// - `ccx`: the crate context /// - `def_id`: def id of the fn or method item being referenced /// - `substs`: values for each of the fn/method's parameters -fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - instance: Instance<'tcx>) - -> ValueRef +fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + instance: Instance<'tcx>) + -> ValueRef { let tcx = ccx.tcx(); @@ -335,3 +247,45 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, llfn } + +pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + instance: Instance<'tcx>) + -> ValueRef +{ + match instance.def { + ty::InstanceDef::Intrinsic(_) => { + bug!("intrinsic {} getting reified", instance) + } + ty::InstanceDef::ClosureOnceShim { .. } => { + let closure_ty = instance.substs.type_at(0); + let (closure_def_id, closure_substs) = match closure_ty.sty { + ty::TyClosure(def_id, substs) => (def_id, substs), + _ => bug!("bad closure instance {:?}", instance) + }; + + trans_fn_once_adapter_shim( + ccx, + closure_def_id, + closure_substs, + instance, + do_get_fn( + ccx, + Instance::new(closure_def_id, closure_substs.substs) + ) + ) + } + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::Item(..) | + ty::InstanceDef::Virtual(..) => { + do_get_fn(ccx, instance) + } + } +} + +pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) + -> ValueRef +{ + get_fn(ccx, monomorphize::resolve(ccx.shared(), def_id, substs)) +} diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 9476ffc944485..2113b9b203ced 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -656,8 +656,8 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan ty::InstanceDef::ClosureOnceShim { call_once: _, closure_did: def_id } => def_id, - ty::InstanceDef::FnPtrShim(..) => return true, ty::InstanceDef::Virtual(..) | + ty::InstanceDef::FnPtrShim(..) => return true, ty::InstanceDef::Intrinsic(_) => return false }; match tcx.hir.get_if_local(def_id) { diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index b7381dd07dcfa..9297c0108468a 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -18,7 +18,7 @@ use rustc::hir::def::ExportMap; use rustc::hir::def_id::DefId; use rustc::traits; use debuginfo; -use callee::Callee; +use callee; use base; use declare; use glue::DropGlueKind; @@ -920,7 +920,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { let tcx = self.tcx(); let llfn = match tcx.lang_items.eh_personality() { Some(def_id) if !base::wants_msvc_seh(self.sess()) => { - Callee::def(self, def_id, tcx.intern_substs(&[])).reify(self) + callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])) } _ => { let name = if base::wants_msvc_seh(self.sess()) { @@ -948,7 +948,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { let tcx = self.tcx(); assert!(self.sess().target.target.options.custom_unwind_resume); if let Some(def_id) = tcx.lang_items.eh_unwind_resume() { - let llfn = Callee::def(self, def_id, tcx.intern_substs(&[])).reify(self); + let llfn = callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])); unwresume.set(Some(llfn)); return llfn; } diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 2eb94aa56ab50..9a639ed21f1ac 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -24,9 +24,10 @@ use rustc::ty::{self, layout, AdtDef, AdtKind, Ty, TypeFoldable}; use rustc::ty::subst::Kind; use rustc::mir::tcx::LvalueTy; use mir::lvalue::LvalueRef; +use abi::FnType; use adt; use base::*; -use callee::Callee; +use callee::get_fn; use cleanup::CleanupScope; use common::*; use machine::*; @@ -45,11 +46,10 @@ pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef< let content_ty = ptr.ty.to_ty(bcx.tcx()); let def_id = langcall(bcx.tcx(), None, "", BoxFreeFnLangItem); let substs = bcx.tcx().mk_substs(iter::once(Kind::from(content_ty))); - let callee = Callee::def(bcx.ccx, def_id, substs); + let instance = monomorphize::resolve(bcx.ccx.shared(), def_id, substs); - let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); - - let llret = bcx.call(callee.reify(bcx.ccx), + let fn_ty = FnType::from_instance(bcx.ccx, &instance, &[]); + let llret = bcx.call(get_fn(bcx.ccx, instance), &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize], None); fn_ty.apply_attrs_callsite(llret); } @@ -258,16 +258,18 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi .find(|it| it.kind == ty::AssociatedKind::Method) .unwrap().def_id; let self_type_substs = tcx.mk_substs_trait(t, &[]); - let callee = Callee::def(bcx.ccx, drop_method, self_type_substs); - let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); + let drop_instance = monomorphize::resolve( + bcx.ccx.shared(), drop_method, self_type_substs); + let fn_ty = FnType::from_instance(bcx.ccx, &drop_instance, &[]); + let llfn = get_fn(bcx.ccx, drop_instance); let llret; let args = &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize]; if let Some(landing_pad) = contents_scope.landing_pad { let normal_bcx = bcx.build_sibling_block("normal-return"); - llret = bcx.invoke(callee.reify(ccx), args, normal_bcx.llbb(), landing_pad, None); + llret = bcx.invoke(llfn, args, normal_bcx.llbb(), landing_pad, None); bcx = normal_bcx; } else { - llret = bcx.call(callee.reify(bcx.ccx), args, None); + llret = bcx.call(llfn, args, None); } fn_ty.apply_attrs_callsite(llret); contents_scope.trans(&bcx); diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index bfd9f69a92218..6b823756c8eab 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -8,20 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use attributes; -use llvm::{ValueRef, get_params}; +use llvm::ValueRef; use rustc::traits; -use callee::{Callee, CalleeData}; +use callee; use common::*; use builder::Builder; use consts; -use declare; use glue; use machine; -use monomorphize::Instance; use type_::Type; use type_of::*; -use back::symbol_names; use value::Value; use rustc::ty; @@ -42,74 +38,6 @@ pub fn get_virtual_method<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr } -/// Generate a shim function that allows an object type like `SomeTrait` to -/// implement the type `SomeTrait`. Imagine a trait definition: -/// -/// trait SomeTrait { fn get(&self) -> i32; ... } -/// -/// And a generic bit of code: -/// -/// fn foo(t: &T) { -/// let x = SomeTrait::get; -/// x(t) -/// } -/// -/// What is the value of `x` when `foo` is invoked with `T=SomeTrait`? -/// The answer is that it is a shim function generated by this routine: -/// -/// fn shim(t: &SomeTrait) -> i32 { -/// // ... call t.get() virtually ... -/// } -/// -/// In fact, all virtual calls can be thought of as normal trait calls -/// that go through this shim function. -pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, - callee: Callee<'tcx>) - -> ValueRef { - debug!("trans_object_shim({:?})", callee); - - let function_name = match callee.ty.sty { - ty::TyFnDef(def_id, substs, _) => { - let instance = Instance::new(def_id, substs); - symbol_names::symbol_name(instance, ccx.shared()) - } - _ => bug!() - }; - - let llfn = declare::define_internal_fn(ccx, &function_name, callee.ty); - attributes::set_frame_pointer_elimination(ccx, llfn); - - let bcx = Builder::new_block(ccx, llfn, "entry-block"); - - let mut llargs = get_params(llfn); - let fn_ret = callee.ty.fn_ret(); - let fn_ty = callee.direct_fn_type(ccx, &[]); - - let fn_ptr = match callee.data { - CalleeData::Virtual(idx) => { - let fn_ptr = get_virtual_method(&bcx, - llargs.remove(fn_ty.ret.is_indirect() as usize + 1), idx); - let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - bcx.pointercast(fn_ptr, llty) - }, - _ => bug!("trans_object_shim called with non-virtual callee"), - }; - let llret = bcx.call(fn_ptr, &llargs, None); - fn_ty.apply_attrs_callsite(llret); - - if fn_ret.0.is_never() { - bcx.unreachable(); - } else { - if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { - bcx.ret_void(); - } else { - bcx.ret(llret); - } - } - - llfn -} - /// Creates a dynamic vtable for the given type and vtable origin. /// This is used only for objects. /// @@ -150,7 +78,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let trait_ref = trait_ref.with_self_ty(tcx, ty); let methods = traits::get_vtable_methods(tcx, trait_ref).map(|opt_mth| { opt_mth.map_or(nullptr, |(def_id, substs)| { - Callee::def(ccx, def_id, substs).reify(ccx) + callee::resolve_and_get_fn(ccx, def_id, substs) }) }); components.extend(methods); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 2f1a2c9134c39..761f6b208e747 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -16,13 +16,14 @@ use rustc::ty::{self, layout, TypeFoldable}; use rustc::mir; use abi::{Abi, FnType, ArgType}; use base::{self, Lifetime}; -use callee::{Callee, CalleeData, Fn, Intrinsic, Virtual}; +use callee; use builder::Builder; use common::{self, Funclet}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; use consts; use machine::llalign_of_min; use meth; +use monomorphize; use type_of::{self, align_of}; use glue; use type_::Type; @@ -340,9 +341,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Obtain the panic entry point. let def_id = common::langcall(bcx.tcx(), Some(span), "", lang_item); - let callee = Callee::def(bcx.ccx, def_id, - bcx.ccx.empty_substs_for_def_id(def_id)); - let llfn = callee.reify(bcx.ccx); + let instance = ty::Instance::mono(bcx.tcx(), def_id); + let llfn = callee::get_fn(bcx.ccx, instance); // Translate the actual panic invoke/call. if let Some(unwind) = cleanup { @@ -365,30 +365,30 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.trans_operand(&bcx, func); - let (mut callee, sig) = match callee.ty.sty { + let (instance, mut llfn, sig) = match callee.ty.sty { ty::TyFnDef(def_id, substs, sig) => { - (Callee::def(bcx.ccx, def_id, substs), sig) + (Some(monomorphize::resolve(bcx.ccx.shared(), def_id, substs)), + None, + sig) } ty::TyFnPtr(sig) => { - (Callee { - data: Fn(callee.immediate()), - ty: callee.ty - }, sig) + (None, + Some(callee.immediate()), + sig) } _ => bug!("{} is not callable", callee.ty) }; - + let def = instance.map(|i| i.def); let sig = bcx.tcx().erase_late_bound_regions_and_normalize(&sig); let abi = sig.abi; // Handle intrinsics old trans wants Expr's for, ourselves. - let intrinsic = match (&callee.ty.sty, &callee.data) { - (&ty::TyFnDef(def_id, ..), &Intrinsic) => { - Some(bcx.tcx().item_name(def_id).as_str()) - } + let intrinsic = match def { + Some(ty::InstanceDef::Intrinsic(def_id)) + => Some(bcx.tcx().item_name(def_id).as_str()), _ => None }; - let mut intrinsic = intrinsic.as_ref().map(|s| &s[..]); + let intrinsic = intrinsic.as_ref().map(|s| &s[..]); if intrinsic == Some("move_val_init") { let &(_, target) = destination.as_ref().unwrap(); @@ -412,15 +412,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let op_ty = op_arg.ty(&self.mir, bcx.tcx()); self.monomorphize(&op_ty) }).collect::>(); - let fn_ty = callee.direct_fn_type(bcx.ccx, &extra_args); + + let fn_ty = match def { + Some(ty::InstanceDef::Virtual(..)) => { + FnType::new_vtable(bcx.ccx, sig, &extra_args) + } + _ => FnType::new(bcx.ccx, sig, &extra_args) + }; if intrinsic == Some("drop_in_place") { let &(_, target) = destination.as_ref().unwrap(); - let ty = if let ty::TyFnDef(_, substs, _) = callee.ty.sty { - substs.type_at(0) - } else { - bug!("Unexpected ty: {}", callee.ty); - }; + let ty = instance.unwrap().substs.type_at(0); // Double check for necessity to drop if !bcx.ccx.shared().type_needs_drop(ty) { @@ -430,8 +432,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let drop_fn = glue::get_drop_glue(bcx.ccx, ty); let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - callee.data = Fn(bcx.pointercast(drop_fn, llty)); - intrinsic = None; + llfn = Some(bcx.pointercast(drop_fn, llty)); } // The arguments we'll be passing. Plus one to account for outptr, if used. @@ -440,12 +441,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Prepare the return value destination let ret_dest = if let Some((ref dest, _)) = *destination { - let is_intrinsic = if let Intrinsic = callee.data { - true - } else { - false - }; - self.make_return_dest(&bcx, dest, &fn_ty.ret, &mut llargs, is_intrinsic) + let is_intrinsic = intrinsic.is_some(); + self.make_return_dest(&bcx, dest, &fn_ty.ret, &mut llargs, + is_intrinsic) } else { ReturnDest::Nothing }; @@ -483,52 +481,56 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let op = self.trans_operand(&bcx, arg); self.trans_argument(&bcx, op, &mut llargs, &fn_ty, - &mut idx, &mut callee.data); + &mut idx, &mut llfn, &def); } if let Some(tup) = untuple { self.trans_arguments_untupled(&bcx, tup, &mut llargs, &fn_ty, - &mut idx, &mut callee.data) + &mut idx, &mut llfn, &def) } - let fn_ptr = match callee.data { - Intrinsic => { - use intrinsic::trans_intrinsic_call; - - let (dest, llargs) = match ret_dest { - _ if fn_ty.ret.is_indirect() => { - (llargs[0], &llargs[1..]) - } - ReturnDest::Nothing => { - (C_undef(fn_ty.ret.original_ty.ptr_to()), &llargs[..]) - } - ReturnDest::IndirectOperand(dst, _) | - ReturnDest::Store(dst) => (dst, &llargs[..]), - ReturnDest::DirectOperand(_) => - bug!("Cannot use direct operand with an intrinsic call") - }; - - trans_intrinsic_call(&bcx, callee.ty, &fn_ty, &llargs, dest, - terminator.source_info.span); + if intrinsic.is_some() && intrinsic != Some("drop_in_place") { + use intrinsic::trans_intrinsic_call; - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - // Make a fake operand for store_return - let op = OperandRef { - val: Ref(dst, Alignment::AbiAligned), - ty: sig.output(), - }; - self.store_return(&bcx, ret_dest, fn_ty.ret, op); + let (dest, llargs) = match ret_dest { + _ if fn_ty.ret.is_indirect() => { + (llargs[0], &llargs[1..]) } - - if let Some((_, target)) = *destination { - funclet_br(self, bcx, target); - } else { - bcx.unreachable(); + ReturnDest::Nothing => { + (C_undef(fn_ty.ret.original_ty.ptr_to()), &llargs[..]) } + ReturnDest::IndirectOperand(dst, _) | + ReturnDest::Store(dst) => (dst, &llargs[..]), + ReturnDest::DirectOperand(_) => + bug!("Cannot use direct operand with an intrinsic call") + }; - return; + let callee_ty = common::instance_ty( + bcx.ccx.shared(), instance.as_ref().unwrap()); + trans_intrinsic_call(&bcx, callee_ty, &fn_ty, &llargs, dest, + terminator.source_info.span); + + if let ReturnDest::IndirectOperand(dst, _) = ret_dest { + // Make a fake operand for store_return + let op = OperandRef { + val: Ref(dst, Alignment::AbiAligned), + ty: sig.output(), + }; + self.store_return(&bcx, ret_dest, fn_ty.ret, op); + } + + if let Some((_, target)) = *destination { + funclet_br(self, bcx, target); + } else { + bcx.unreachable(); } - Fn(f) => f, - Virtual(_) => bug!("Virtual fn ptr not extracted") + + return; + } + + let fn_ptr = match (llfn, instance) { + (Some(llfn), _) => llfn, + (None, Some(instance)) => callee::get_fn(bcx.ccx, instance), + _ => span_bug!(span, "no llfn for call"), }; // Many different ways to call a function handled here @@ -578,16 +580,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { llargs: &mut Vec, fn_ty: &FnType, next_idx: &mut usize, - callee: &mut CalleeData) { + llfn: &mut Option, + def: &Option>) { if let Pair(a, b) = op.val { // Treat the values in a fat pointer separately. if common::type_is_fat_ptr(bcx.ccx, op.ty) { let (ptr, meta) = (a, b); if *next_idx == 0 { - if let Virtual(idx) = *callee { - let llfn = meth::get_virtual_method(bcx, meta, idx); + if let Some(ty::InstanceDef::Virtual(_, idx)) = *def { + let llmeth = meth::get_virtual_method(bcx, meta, idx); let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - *callee = Fn(bcx.pointercast(llfn, llty)); + *llfn = Some(bcx.pointercast(llmeth, llty)); } } @@ -596,8 +599,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // We won't be checking the type again. ty: bcx.tcx().types.err }; - self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, callee); - self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, llfn, def); + self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, llfn, def); return; } } @@ -660,7 +663,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { llargs: &mut Vec, fn_ty: &FnType, next_idx: &mut usize, - callee: &mut CalleeData) { + llfn: &mut Option, + def: &Option>) { let tuple = self.trans_operand(bcx, operand); let arg_types = match tuple.ty.sty { @@ -686,7 +690,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { val: val, ty: ty }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); } } @@ -708,7 +712,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { val: Immediate(elem), ty: ty }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); } } Pair(a, b) => { @@ -724,7 +728,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { val: Immediate(elem), ty: ty }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee); + self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); } } } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 69009ab356044..107b0982af982 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -23,7 +23,7 @@ use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::subst::{Kind, Substs, Subst}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use {abi, adt, base, Disr, machine}; -use callee::Callee; +use callee; use builder::Builder; use common::{self, CrateContext, const_get_elt, val_ty}; use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integral}; @@ -565,8 +565,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::CastKind::ReifyFnPointer => { match operand.ty.sty { ty::TyFnDef(def_id, substs, _) => { - Callee::def(self.ccx, def_id, substs) - .reify(self.ccx) + callee::resolve_and_get_fn(self.ccx, def_id, substs) } _ => { span_bug!(span, "{} cannot be reified to a fn ptr", @@ -589,8 +588,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { let input = tcx.erase_late_bound_regions_and_normalize(&input); let substs = tcx.mk_substs([operand.ty, input] .iter().cloned().map(Kind::from)); - Callee::def(self.ccx, call_once, substs) - .reify(self.ccx) + callee::resolve_and_get_fn(self.ccx, call_once, substs) } _ => { bug!("{} cannot be cast to a fn ptr", operand.ty) diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index bbaf0f9d35fa6..3832c21d10a49 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -19,7 +19,7 @@ use middle::lang_items::ExchangeMallocFnLangItem; use base; use builder::Builder; -use callee::Callee; +use callee; use common::{self, val_ty, C_bool, C_null, C_uint}; use common::{C_integral}; use adt; @@ -183,8 +183,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { match operand.ty.sty { ty::TyFnDef(def_id, substs, _) => { OperandValue::Immediate( - Callee::def(bcx.ccx, def_id, substs) - .reify(bcx.ccx)) + callee::resolve_and_get_fn(bcx.ccx, def_id, substs)) } _ => { bug!("{} cannot be reified to a fn ptr", operand.ty) @@ -208,8 +207,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let substs = bcx.tcx().mk_substs([operand.ty, input] .iter().cloned().map(Kind::from)); OperandValue::Immediate( - Callee::def(bcx.ccx, call_once, substs) - .reify(bcx.ccx)) + callee::resolve_and_get_fn(bcx.ccx, call_once, substs) + ) } _ => { bug!("{} cannot be cast to a fn ptr", operand.ty) @@ -463,8 +462,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx.sess().fatal(&format!("allocation of `{}` {}", box_ty, s)); } }; - let r = Callee::def(bcx.ccx, def_id, bcx.tcx().intern_substs(&[])) - .reify(bcx.ccx); + let instance = ty::Instance::mono(bcx.tcx(), def_id); + let r = callee::get_fn(bcx.ccx, instance); let val = bcx.pointercast(bcx.call(r, &[llsize, llalign], None), llty_ptr); let operand = OperandRef { From a5e3c3d5b85e415ad2094f476d9f1ac29a48e413 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Mar 2017 21:21:27 +0200 Subject: [PATCH 06/11] collector: collect functions when they are called/reified This avoids the creation of unneeded vtable shims. --- src/librustc_trans/collector.rs | 179 +++++++++++++++----------------- 1 file changed, 86 insertions(+), 93 deletions(-) diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 2113b9b203ced..40a89783d91cf 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -202,7 +202,6 @@ use rustc::mir::{self, Location}; use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor as MirVisitor; -use syntax::abi::Abi; use context::SharedCrateContext; use common::{def_ty, instance_ty}; use glue::{self, DropGlueKind}; @@ -486,6 +485,14 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.output); } } + mir::Rvalue::Cast(mir::CastKind::ReifyFnPointer, ref operand, _) => { + let fn_ty = operand.ty(self.mir, self.scx.tcx()); + let fn_ty = monomorphize::apply_param_substs( + self.scx, + self.param_substs, + &fn_ty); + visit_fn_use(self.scx, fn_ty, false, &mut self.output); + } mir::Rvalue::Cast(mir::CastKind::ClosureFnPointer, ref operand, _) => { let source_ty = operand.ty(self.mir, self.scx.tcx()); match source_ty.sty { @@ -537,111 +544,97 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.super_lvalue(lvalue, context, location); } - fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) { - debug!("visiting operand {:?}", *operand); - - let callee = match *operand { - mir::Operand::Constant(ref constant) => { - if let ty::TyFnDef(def_id, substs, _) = constant.ty.sty { - // This is something that can act as a callee, proceed - Some((def_id, substs)) - } else { - // This is not a callee, but we still have to look for - // references to `const` items - if let mir::Literal::Item { def_id, substs } = constant.literal { - let substs = monomorphize::apply_param_substs(self.scx, - self.param_substs, - &substs); - let instance = monomorphize::resolve(self.scx, def_id, substs); - collect_neighbours(self.scx, instance, self.output); - } + fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) { + debug!("visiting constant {:?} @ {:?}", *constant, location); - None - } - } - _ => None - }; - - if let Some((callee_def_id, callee_substs)) = callee { - debug!(" => operand is callable"); - - // `callee_def_id` might refer to a trait method instead of a - // concrete implementation, so we have to find the actual - // implementation. For example, the call might look like - // - // std::cmp::partial_cmp(0i32, 1i32) - // - // Calling do_static_dispatch() here will map the def_id of - // `std::cmp::partial_cmp` to the def_id of `i32::partial_cmp` + if let ty::TyFnDef(..) = constant.ty.sty { + // function definitions are zero-sized, and only generate + // IR when they are called/reified. + self.super_constant(constant, location); + return + } - let callee_substs = monomorphize::apply_param_substs(self.scx, - self.param_substs, - &callee_substs); - let instance = - monomorphize::resolve(self.scx, callee_def_id, callee_substs); - if should_trans_locally(self.scx.tcx(), &instance) { - if let ty::InstanceDef::ClosureOnceShim { .. } = instance.def { - // This call will instantiate an FnOnce adapter, which - // drops the closure environment. Therefore we need to - // make sure that we collect the drop-glue for the - // environment type. - - let env_ty = instance.substs.type_at(0); - let env_ty = glue::get_drop_glue_type(self.scx, env_ty); - if self.scx.type_needs_drop(env_ty) { - let dg = DropGlueKind::Ty(env_ty); - self.output.push(TransItem::DropGlue(dg)); - } - } - self.output.push(create_fn_trans_item(instance)); - } + if let mir::Literal::Item { def_id, substs } = constant.literal { + let substs = monomorphize::apply_param_substs(self.scx, + self.param_substs, + &substs); + let instance = monomorphize::resolve(self.scx, def_id, substs); + collect_neighbours(self.scx, instance, self.output); } - self.super_operand(operand, location); + self.super_constant(constant, location); } - // This takes care of the "drop_in_place" intrinsic for which we otherwise - // we would not register drop-glues. fn visit_terminator_kind(&mut self, block: mir::BasicBlock, kind: &mir::TerminatorKind<'tcx>, location: Location) { let tcx = self.scx.tcx(); - match *kind { - mir::TerminatorKind::Call { - func: mir::Operand::Constant(ref constant), - ref args, - .. - } => { - match constant.ty.sty { - ty::TyFnDef(def_id, _, bare_fn_ty) - if is_drop_in_place_intrinsic(tcx, def_id, bare_fn_ty) => { - let operand_ty = args[0].ty(self.mir, tcx); - if let ty::TyRawPtr(mt) = operand_ty.sty { - let operand_ty = monomorphize::apply_param_substs(self.scx, - self.param_substs, - &mt.ty); - let ty = glue::get_drop_glue_type(self.scx, operand_ty); - self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); - } else { - bug!("Has the drop_in_place() intrinsic's signature changed?") - } - } - _ => { /* Nothing to do. */ } - } - } - _ => { /* Nothing to do. */ } + if let mir::TerminatorKind::Call { + ref func, + .. + } = *kind { + let callee_ty = func.ty(self.mir, tcx); + let callee_ty = monomorphize::apply_param_substs( + self.scx, self.param_substs, &callee_ty); + visit_fn_use(self.scx, callee_ty, true, &mut self.output); } self.super_terminator_kind(block, kind, location); + } +} + +fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>, + is_direct_call: bool, + output: &mut Vec>) +{ + debug!("visit_fn_use({:?}, is_direct_call={:?})", ty, is_direct_call); + let (def_id, substs) = match ty.sty { + ty::TyFnDef(def_id, substs, _) => (def_id, substs), + _ => return + }; - fn is_drop_in_place_intrinsic<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - bare_fn_ty: ty::PolyFnSig<'tcx>) - -> bool { - (bare_fn_ty.abi() == Abi::RustIntrinsic || - bare_fn_ty.abi() == Abi::PlatformIntrinsic) && - tcx.item_name(def_id) == "drop_in_place" + let instance = monomorphize::resolve(scx, def_id, substs); + if !should_trans_locally(scx.tcx(), &instance) { + return + } + + match instance.def { + ty::InstanceDef::ClosureOnceShim { .. } => { + // This call will instantiate an FnOnce adapter, which + // drops the closure environment. Therefore we need to + // make sure that we collect the drop-glue for the + // environment type along with the instance. + + let env_ty = instance.substs.type_at(0); + let env_ty = glue::get_drop_glue_type(scx, env_ty); + if scx.type_needs_drop(env_ty) { + let dg = DropGlueKind::Ty(env_ty); + output.push(TransItem::DropGlue(dg)); + } + output.push(create_fn_trans_item(instance)); + } + ty::InstanceDef::Intrinsic(..) => { + if !is_direct_call { + bug!("intrinsic {:?} being reified", ty); + } + if scx.tcx().item_name(def_id) == "drop_in_place" { + // drop_in_place is a call to drop glue, need to instantiate + // that. + let ty = glue::get_drop_glue_type(scx, substs.type_at(0)); + output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); + } + } + ty::InstanceDef::Virtual(..) => { + // don't need to emit shim if we are calling directly. + if !is_direct_call { + output.push(create_fn_trans_item(instance)); + } + } + ty::InstanceDef::Item(..) | + ty::InstanceDef::FnPtrShim(..) => { + output.push(create_fn_trans_item(instance)); } } } @@ -657,8 +650,8 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan call_once: _, closure_did: def_id } => def_id, ty::InstanceDef::Virtual(..) | - ty::InstanceDef::FnPtrShim(..) => return true, - ty::InstanceDef::Intrinsic(_) => return false + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::Intrinsic(_) => return true }; match tcx.hir.get_if_local(def_id) { Some(hir_map::NodeForeignItem(..)) => { From e1f3c67cb4e2eb1595040888e897e48c64afb4be Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 8 Mar 2017 23:19:09 +0200 Subject: [PATCH 07/11] translate closure shims using MIR --- src/librustc/ty/instance.rs | 16 +-- src/librustc_mir/shim.rs | 121 +++++++++++++++----- src/librustc_trans/callee.rs | 173 ++--------------------------- src/librustc_trans/cleanup.rs | 18 --- src/librustc_trans/collector.rs | 43 +------ src/librustc_trans/mir/rvalue.rs | 21 +--- src/librustc_trans/monomorphize.rs | 95 ++++++++-------- src/test/run-pass/issue-29948.rs | 17 ++- 8 files changed, 183 insertions(+), 321 deletions(-) diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index d93482acbcb57..aeb1fe079ff25 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -34,10 +34,7 @@ pub enum InstanceDef<'tcx> { // ::fn Virtual(DefId, usize), // <[mut closure] as FnOnce>::call_once - ClosureOnceShim { - call_once: DefId, - closure_did: DefId - }, + ClosureOnceShim { call_once: DefId }, } impl<'tcx> InstanceDef<'tcx> { @@ -48,9 +45,8 @@ impl<'tcx> InstanceDef<'tcx> { InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id, ) | - InstanceDef::ClosureOnceShim { - call_once: def_id, closure_did: _ - } => def_id + InstanceDef::ClosureOnceShim { call_once: def_id } + => def_id } } @@ -98,10 +94,8 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::FnPtrShim(_, ty) => { write!(f, " - shim({:?})", ty) } - InstanceDef::ClosureOnceShim { - call_once: _, closure_did - } => { - write!(f, " - shim({:?})", closure_did) + InstanceDef::ClosureOnceShim { .. } => { + write!(f, " - shim") } } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 97c83c7a42e28..35ad296006dad 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -83,7 +83,25 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, None ) } - _ => bug!("unknown shim kind") + ty::InstanceDef::ClosureOnceShim { call_once } => { + let fn_mut = tcx.lang_items.fn_mut_trait().unwrap(); + let call_mut = tcx.global_tcx() + .associated_items(fn_mut) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + + build_call_shim( + tcx, + ¶m_env, + call_once, + Adjustment::RefMut, + CallKind::Direct(call_mut), + None + ) + } + ty::InstanceDef::Intrinsic(_) => { + bug!("creating shims from intrinsics ({:?}) is unsupported", instance) + } }; debug!("make_shim({:?}) = {:?}", instance, result); @@ -97,6 +115,7 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, enum Adjustment { Identity, Deref, + RefMut, } #[derive(Copy, Clone, Debug, PartialEq)] @@ -143,18 +162,37 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, debug!("build_call_shim: sig={:?}", sig); - let local_decls = local_decls_for_sig(&sig); + let mut local_decls = local_decls_for_sig(&sig); let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; - let rcvr_l = Lvalue::Local(Local::new(1+0)); - - let return_block_id = BasicBlock::new(1); + let rcvr_arg = Local::new(1+0); + let rcvr_l = Lvalue::Local(rcvr_arg); + let mut statements = vec![]; let rcvr = match rcvr_adjustment { Adjustment::Identity => Operand::Consume(rcvr_l), Adjustment::Deref => Operand::Consume(Lvalue::Projection( box Projection { base: rcvr_l, elem: ProjectionElem::Deref } - )) + )), + Adjustment::RefMut => { + // let rcvr = &mut rcvr; + let re_erased = tcx.mk_region(ty::ReErased); + let ref_rcvr = local_decls.push(temp_decl( + Mutability::Not, + tcx.mk_ref(re_erased, ty::TypeAndMut { + ty: sig.inputs()[0], + mutbl: hir::Mutability::MutMutable + }) + )); + statements.push(Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(ref_rcvr), + Rvalue::Ref(re_erased, BorrowKind::Mut, rcvr_l) + ) + }); + Operand::Consume(Lvalue::Local(ref_rcvr)) + } }; let (callee, mut args) = match call_kind { @@ -184,28 +222,57 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, } let mut blocks = IndexVec::new(); - blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: source_info, - kind: TerminatorKind::Call { - func: callee, - args: args, - destination: Some((Lvalue::Local(RETURN_POINTER), - return_block_id)), - cleanup: None + let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| { + blocks.push(BasicBlockData { + statements, + terminator: Some(Terminator { source_info, kind }), + is_cleanup + }) + }; + + let have_unwind = match (rcvr_adjustment, tcx.sess.no_landing_pads()) { + (Adjustment::RefMut, false) => true, + _ => false + }; + + // BB #0 + block(&mut blocks, statements, TerminatorKind::Call { + func: callee, + args: args, + destination: Some((Lvalue::Local(RETURN_POINTER), + BasicBlock::new(1))), + cleanup: if have_unwind { + Some(BasicBlock::new(3)) + } else { + None + } + }, false); + + if let Adjustment::RefMut = rcvr_adjustment { + // BB #1 - drop for Self + block(&mut blocks, vec![], TerminatorKind::Drop { + location: Lvalue::Local(rcvr_arg), + target: BasicBlock::new(2), + unwind: if have_unwind { + Some(BasicBlock::new(4)) + } else { + None } - }), - is_cleanup: false - }); - blocks.push(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: source_info, - kind: TerminatorKind::Return - }), - is_cleanup: false - }); + }, false); + } + // BB #1/#2 - return + block(&mut blocks, vec![], TerminatorKind::Return, false); + if have_unwind { + // BB #3 - drop if closure panics + block(&mut blocks, vec![], TerminatorKind::Drop { + location: Lvalue::Local(rcvr_arg), + target: BasicBlock::new(4), + unwind: None + }, true); + + // BB #4 - resume + block(&mut blocks, vec![], TerminatorKind::Resume, true); + } let mut mir = Mir::new( blocks, diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 5c7be56b56dad..4d0bd9fa201d8 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -14,142 +14,18 @@ //! and methods are represented as just a fn ptr and not a full //! closure. -use llvm::{self, ValueRef, get_params}; +use llvm::{self, ValueRef}; use rustc::hir::def_id::DefId; -use rustc::ty::subst::{Substs, Subst}; -use abi::{Abi, FnType}; +use rustc::ty::subst::Substs; use attributes; -use builder::Builder; use common::{self, CrateContext}; -use cleanup::CleanupScope; -use mir::lvalue::LvalueRef; use monomorphize; use consts; use declare; -use value::Value; use monomorphize::Instance; -use back::symbol_names::symbol_name; use trans_item::TransItem; use type_of; -use rustc::ty::{self, TypeFoldable}; -use std::iter; - -use mir::lvalue::Alignment; - -fn trans_fn_once_adapter_shim<'a, 'tcx>( - ccx: &'a CrateContext<'a, 'tcx>, - def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, - method_instance: Instance<'tcx>, - llreffn: ValueRef) - -> ValueRef -{ - if let Some(&llfn) = ccx.instances().borrow().get(&method_instance) { - return llfn; - } - - debug!("trans_fn_once_adapter_shim(def_id={:?}, substs={:?}, llreffn={:?})", - def_id, substs, Value(llreffn)); - - let tcx = ccx.tcx(); - - // Find a version of the closure type. Substitute static for the - // region since it doesn't really matter. - let closure_ty = tcx.mk_closure_from_closure_substs(def_id, substs); - let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty); - - // Make a version with the type of by-ref closure. - let sig = tcx.closure_type(def_id).subst(tcx, substs.substs); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); - assert_eq!(sig.abi, Abi::RustCall); - let llref_fn_sig = tcx.mk_fn_sig( - iter::once(ref_closure_ty).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.variadic, - sig.unsafety, - Abi::RustCall - ); - let llref_fn_ty = tcx.mk_fn_ptr(ty::Binder(llref_fn_sig)); - debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}", - llref_fn_ty); - - - // Make a version of the closure type with the same arguments, but - // with argument #0 being by value. - let sig = tcx.mk_fn_sig( - iter::once(closure_ty).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.variadic, - sig.unsafety, - Abi::RustCall - ); - - let fn_ty = FnType::new(ccx, sig, &[]); - let llonce_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig)); - - // Create the by-value helper. - let function_name = symbol_name(method_instance, ccx.shared()); - let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty); - attributes::set_frame_pointer_elimination(ccx, lloncefn); - - let orig_fn_ty = fn_ty; - let mut bcx = Builder::new_block(ccx, lloncefn, "entry-block"); - - // the first argument (`self`) will be the (by value) closure env. - - let mut llargs = get_params(lloncefn); - let fn_ty = FnType::new(ccx, llref_fn_sig, &[]); - let self_idx = fn_ty.ret.is_indirect() as usize; - let env_arg = &orig_fn_ty.args[0]; - let env = if env_arg.is_indirect() { - LvalueRef::new_sized_ty(llargs[self_idx], closure_ty, Alignment::AbiAligned) - } else { - let scratch = LvalueRef::alloca(&bcx, closure_ty, "self"); - let mut llarg_idx = self_idx; - env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch.llval); - scratch - }; - - debug!("trans_fn_once_adapter_shim: env={:?}", env); - // Adjust llargs such that llargs[self_idx..] has the call arguments. - // For zero-sized closures that means sneaking in a new argument. - if env_arg.is_ignore() { - llargs.insert(self_idx, env.llval); - } else { - llargs[self_idx] = env.llval; - } - - // Call the by-ref closure body with `self` in a cleanup scope, - // to drop `self` when the body returns, or in case it unwinds. - let self_scope = CleanupScope::schedule_drop_mem(&bcx, env); - - let llret; - if let Some(landing_pad) = self_scope.landing_pad { - let normal_bcx = bcx.build_sibling_block("normal-return"); - llret = bcx.invoke(llreffn, &llargs[..], normal_bcx.llbb(), landing_pad, None); - bcx = normal_bcx; - } else { - llret = bcx.call(llreffn, &llargs[..], None); - } - fn_ty.apply_attrs_callsite(llret); - - if sig.output().is_never() { - bcx.unreachable(); - } else { - self_scope.trans(&bcx); - - if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { - bcx.ret_void(); - } else { - bcx.ret(llret); - } - } - - ccx.instances().borrow_mut().insert(method_instance, lloncefn); - - lloncefn -} - +use rustc::ty::TypeFoldable; /// Translates a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -157,11 +33,10 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( /// # Parameters /// /// - `ccx`: the crate context -/// - `def_id`: def id of the fn or method item being referenced -/// - `substs`: values for each of the fn/method's parameters -fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - instance: Instance<'tcx>) - -> ValueRef +/// - `instance`: the instance to be instantiated +pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + instance: Instance<'tcx>) + -> ValueRef { let tcx = ccx.tcx(); @@ -248,40 +123,6 @@ fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, llfn } -pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - instance: Instance<'tcx>) - -> ValueRef -{ - match instance.def { - ty::InstanceDef::Intrinsic(_) => { - bug!("intrinsic {} getting reified", instance) - } - ty::InstanceDef::ClosureOnceShim { .. } => { - let closure_ty = instance.substs.type_at(0); - let (closure_def_id, closure_substs) = match closure_ty.sty { - ty::TyClosure(def_id, substs) => (def_id, substs), - _ => bug!("bad closure instance {:?}", instance) - }; - - trans_fn_once_adapter_shim( - ccx, - closure_def_id, - closure_substs, - instance, - do_get_fn( - ccx, - Instance::new(closure_def_id, closure_substs.substs) - ) - ) - } - ty::InstanceDef::FnPtrShim(..) | - ty::InstanceDef::Item(..) | - ty::InstanceDef::Virtual(..) => { - do_get_fn(ccx, instance) - } - } -} - pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>) diff --git a/src/librustc_trans/cleanup.rs b/src/librustc_trans/cleanup.rs index 5d89a67d3fd80..2b2e5e85ea50d 100644 --- a/src/librustc_trans/cleanup.rs +++ b/src/librustc_trans/cleanup.rs @@ -93,24 +93,6 @@ impl<'tcx> DropValue<'tcx> { } impl<'a, 'tcx> CleanupScope<'tcx> { - /// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty` - pub fn schedule_drop_mem( - bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx> - ) -> CleanupScope<'tcx> { - if let LvalueTy::Downcast { .. } = val.ty { - bug!("Cannot drop downcast ty yet"); - } - if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) { - return CleanupScope::noop(); - } - let drop = DropValue { - val: val, - skip_dtor: false, - }; - - CleanupScope::new(bcx, drop) - } - /// Issue #23611: Schedules a (deep) drop of the contents of /// `val`, which is a pointer to an instance of struct/enum type /// `ty`. The scheduled code handles extracting the discriminant diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 40a89783d91cf..f076fc4710222 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -497,11 +497,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let source_ty = operand.ty(self.mir, self.scx.tcx()); match source_ty.sty { ty::TyClosure(def_id, substs) => { - let substs = monomorphize::apply_param_substs( - self.scx, self.param_substs, &substs.substs); - self.output.push(create_fn_trans_item( - Instance::new(def_id, substs) - )); + let instance = monomorphize::resolve_closure( + self.scx, def_id, substs, ty::ClosureKind::FnOnce); + self.output.push(create_fn_trans_item(instance)); } _ => bug!(), } @@ -601,20 +599,6 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, } match instance.def { - ty::InstanceDef::ClosureOnceShim { .. } => { - // This call will instantiate an FnOnce adapter, which - // drops the closure environment. Therefore we need to - // make sure that we collect the drop-glue for the - // environment type along with the instance. - - let env_ty = instance.substs.type_at(0); - let env_ty = glue::get_drop_glue_type(scx, env_ty); - if scx.type_needs_drop(env_ty) { - let dg = DropGlueKind::Ty(env_ty); - output.push(TransItem::DropGlue(dg)); - } - output.push(create_fn_trans_item(instance)); - } ty::InstanceDef::Intrinsic(..) => { if !is_direct_call { bug!("intrinsic {:?} being reified", ty); @@ -632,6 +616,7 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, output.push(create_fn_trans_item(instance)); } } + ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) => { output.push(create_fn_trans_item(instance)); @@ -645,10 +630,8 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>) -> bool { let def_id = match instance.def { - ty::InstanceDef::Item(def_id) | - ty::InstanceDef::ClosureOnceShim { - call_once: _, closure_did: def_id - } => def_id, + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Virtual(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Intrinsic(_) => return true @@ -885,20 +868,6 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn create_fn_trans_item<'a, 'tcx>(instance: Instance<'tcx>) -> TransItem<'tcx> { debug!("create_fn_trans_item(instance={})", instance); - let instance = match instance.def { - ty::InstanceDef::ClosureOnceShim { .. } => { - // HACK: don't create ClosureOnce trans items for now - // have someone else generate the drop glue - let closure_ty = instance.substs.type_at(0); - match closure_ty.sty { - ty::TyClosure(def_id, substs) => { - Instance::new(def_id, substs.substs) - } - _ => bug!("bad closure instance {:?}", instance) - } - } - _ => instance - }; TransItem::Fn(instance) } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 3832c21d10a49..178347369c915 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -12,7 +12,6 @@ use llvm::{self, ValueRef}; use rustc::ty::{self, Ty}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::layout::Layout; -use rustc::ty::subst::{Kind, Subst}; use rustc::mir::tcx::LvalueTy; use rustc::mir; use middle::lang_items::ExchangeMallocFnLangItem; @@ -24,6 +23,7 @@ use common::{self, val_ty, C_bool, C_null, C_uint}; use common::{C_integral}; use adt; use machine; +use monomorphize; use type_::Type; use type_of; use tvec; @@ -193,22 +193,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::CastKind::ClosureFnPointer => { match operand.ty.sty { ty::TyClosure(def_id, substs) => { - // Get the def_id for FnOnce::call_once - let fn_once = bcx.tcx().lang_items.fn_once_trait().unwrap(); - let call_once = bcx.tcx() - .global_tcx().associated_items(fn_once) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - // Now create its substs [Closure, Tuple] - let input = bcx.tcx().closure_type(def_id) - .subst(bcx.tcx(), substs.substs).input(0); - let input = - bcx.tcx().erase_late_bound_regions_and_normalize(&input); - let substs = bcx.tcx().mk_substs([operand.ty, input] - .iter().cloned().map(Kind::from)); - OperandValue::Immediate( - callee::resolve_and_get_fn(bcx.ccx, call_once, substs) - ) + let instance = monomorphize::resolve_closure( + bcx.ccx.shared(), def_id, substs, ty::ClosureKind::FnOnce); + OperandValue::Immediate(callee::get_fn(bcx.ccx, instance)) } _ => { bug!("{} cannot be cast to a fn ptr", operand.ty) diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index bf073d8b97844..0d8aa0f4bda0e 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -37,7 +37,7 @@ fn fn_once_adapter_instance<'a, 'tcx>( let call_once = tcx.associated_items(fn_once) .find(|it| it.kind == ty::AssociatedKind::Method) .unwrap().def_id; - let def = ty::InstanceDef::ClosureOnceShim { call_once, closure_did }; + let def = ty::InstanceDef::ClosureOnceShim { call_once }; let self_ty = tcx.mk_closure_from_closure_substs( closure_did, substs); @@ -54,6 +54,54 @@ fn fn_once_adapter_instance<'a, 'tcx>( Instance { def, substs } } +fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind) + -> Result +{ + match (actual_closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { + // No adapter needed. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { + // The closure fn `llfn` is a `fn(&self, ...)`. We want a + // `fn(&mut self, ...)`. In fact, at trans time, these are + // basically the same thing, so we can just return llfn. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + Ok(true) + } + _ => Err(()), + } +} + +pub fn resolve_closure<'a, 'tcx> ( + scx: &SharedCrateContext<'a, 'tcx>, + def_id: DefId, + substs: ty::ClosureSubsts<'tcx>, + requested_kind: ty::ClosureKind) + -> Instance<'tcx> +{ + let actual_kind = scx.tcx().closure_kind(def_id); + + match needs_fn_once_adapter_shim(actual_kind, requested_kind) { + Ok(true) => fn_once_adapter_instance(scx.tcx(), def_id, substs), + _ => Instance::new(def_id, substs.substs) + } +} + /// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we /// do not (necessarily) resolve all nested obligations on the impl. Note that type check should /// guarantee to us that all nested obligations *could be* resolved if we wanted to. @@ -121,39 +169,6 @@ fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, }) } -fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, - trait_closure_kind: ty::ClosureKind) - -> Result -{ - match (actual_closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | - (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { - // No adapter needed. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { - // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at trans time, these are - // basically the same thing, so we can just return llfn. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut - // self, ...)`. We want a `fn(self, ...)`. We can produce - // this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - Ok(true) - } - _ => Err(()), - } -} - fn resolve_associated_item<'a, 'tcx>( scx: &SharedCrateContext<'a, 'tcx>, trait_item: &ty::AssociatedItem, @@ -180,16 +195,9 @@ fn resolve_associated_item<'a, 'tcx>( ty::Instance::new(def_id, substs) } traits::VtableClosure(closure_data) => { - let closure_def_id = closure_data.closure_def_id; let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - let actual_closure_kind = tcx.closure_kind(closure_def_id); - - match needs_fn_once_adapter_shim(actual_closure_kind, - trait_closure_kind) { - Ok(true) => fn_once_adapter_instance( - tcx, closure_def_id, closure_data.substs), - _ => Instance::new(closure_def_id, closure_data.substs.substs), - } + resolve_closure(scx, closure_data.closure_def_id, closure_data.substs, + trait_closure_kind) } traits::VtableFnPointer(ref data) => { Instance { @@ -279,7 +287,6 @@ pub fn apply_param_substs<'a, 'tcx, T>(scx: &SharedCrateContext<'a, 'tcx>, AssociatedTypeNormalizer::new(scx).fold(&substituted) } - /// Returns the normalized type of a struct field pub fn field_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_substs: &Substs<'tcx>, diff --git a/src/test/run-pass/issue-29948.rs b/src/test/run-pass/issue-29948.rs index ec2b53313faa7..281dde15bd336 100644 --- a/src/test/run-pass/issue-29948.rs +++ b/src/test/run-pass/issue-29948.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::panic; + +impl<'a> panic::UnwindSafe for Foo<'a> {} +impl<'a> panic::RefUnwindSafe for Foo<'a> {} + struct Foo<'a>(&'a mut bool); impl<'a> Drop for Foo<'a> { @@ -28,5 +33,15 @@ fn main() { f(x); } assert!(ran_drop); -} + let mut ran_drop = false; + { + let x = Foo(&mut ran_drop); + let result = panic::catch_unwind(move || { + let x = move || { let _ = x; panic!() }; + f(x); + }); + assert!(result.is_err()); + } + assert!(ran_drop); +} From 2b9fea1300b515e0f8929bb3a09d4fb6fef3f0ea Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 9 Mar 2017 20:10:05 +0200 Subject: [PATCH 08/11] move the drop expansion code to rustc_mir --- .../borrowck/mir/dataflow/graphviz.rs | 3 +- .../borrowck/mir/dataflow/impls.rs | 2 +- .../borrowck/mir/elaborate_drops.rs | 727 ++++-------------- src/librustc_borrowck/borrowck/mir/mod.rs | 17 +- src/librustc_driver/pretty.rs | 3 +- src/librustc_mir/lib.rs | 4 +- src/librustc_mir/mir_map.rs | 6 +- src/librustc_mir/transform/copy_prop.rs | 2 +- src/librustc_mir/transform/dump_mir.rs | 4 +- src/librustc_mir/{ => util}/def_use.rs | 0 src/librustc_mir/util/elaborate_drops.rs | 561 ++++++++++++++ src/librustc_mir/{ => util}/graphviz.rs | 0 src/librustc_mir/util/mod.rs | 20 + .../mir => librustc_mir/util}/patch.rs | 0 src/librustc_mir/{ => util}/pretty.rs | 0 15 files changed, 728 insertions(+), 621 deletions(-) rename src/librustc_mir/{ => util}/def_use.rs (100%) create mode 100644 src/librustc_mir/util/elaborate_drops.rs rename src/librustc_mir/{ => util}/graphviz.rs (100%) create mode 100644 src/librustc_mir/util/mod.rs rename src/{librustc_borrowck/borrowck/mir => librustc_mir/util}/patch.rs (100%) rename src/librustc_mir/{ => util}/pretty.rs (100%) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs index b15c1873f9bd8..7f95f07f48d4a 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs @@ -15,6 +15,7 @@ use rustc::mir::{BasicBlock, Mir}; use rustc_data_structures::bitslice::bits_to_string; use rustc_data_structures::indexed_set::{IdxSet}; use rustc_data_structures::indexed_vec::Idx; +use rustc_mir::util as mir_util; use dot; use dot::IntoCow; @@ -219,7 +220,7 @@ impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P> } Ok(()) } - ::rustc_mir::graphviz::write_node_label( + mir_util::write_graphviz_node_label( *n, self.mbcx.mir(), &mut v, 4, |w| { let flow = self.mbcx.flow_state(); diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs index 7888a56d39dfb..da8aa231ccf15 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -14,10 +14,10 @@ use rustc_data_structures::bitslice::BitSlice; // adds set_bit/get_bit to &[usiz use rustc_data_structures::bitslice::{BitwiseOperator}; use rustc_data_structures::indexed_set::{IdxSet}; use rustc_data_structures::indexed_vec::Idx; +use rustc_mir::util::elaborate_drops::DropFlagState; use super::super::gather_moves::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex}; use super::super::MoveDataParamEnv; -use super::super::DropFlagState; use super::super::drop_flag_effects_for_function_entry; use super::super::drop_flag_effects_for_location; use super::super::on_lookup_result_bits; diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index a71d23e7e1e7e..88ec86cc95d61 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -13,22 +13,20 @@ use super::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use super::dataflow::{DataflowResults}; use super::{drop_flag_effects_for_location, on_all_children_bits}; use super::on_lookup_result_bits; -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 super::MoveDataParamEnv; +use rustc::ty::{self, TyCtxt}; use rustc::mir::*; use rustc::mir::transform::{Pass, MirPass, MirSource}; use rustc::middle::const_val::ConstVal; -use rustc::middle::lang_items; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; +use rustc_mir::util::patch::MirPatch; +use rustc_mir::util::elaborate_drops::{DropFlagState, elaborate_drop}; +use rustc_mir::util::elaborate_drops::{DropElaborator, DropStyle, DropFlagMode}; use syntax_pos::Span; use std::fmt; -use std::iter; use std::u32; pub struct ElaborateDrops; @@ -109,12 +107,116 @@ impl InitializationData { } } -impl fmt::Debug for InitializationData { +struct Elaborator<'a, 'b: 'a, 'tcx: 'b> { + init_data: &'a InitializationData, + ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>, +} + +impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> { fn fmt(&self, _f: &mut fmt::Formatter) -> Result<(), fmt::Error> { Ok(()) } } +impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { + type Path = MovePathIndex; + + fn patch(&mut self) -> &mut MirPatch<'tcx> { + &mut self.ctxt.patch + } + + fn mir(&self) -> &'a Mir<'tcx> { + self.ctxt.mir + } + + fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> { + self.ctxt.tcx + } + + fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx> { + self.ctxt.param_env() + } + + fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { + let ((maybe_live, maybe_dead), multipart) = match mode { + DropFlagMode::Shallow => (self.init_data.state(path), false), + DropFlagMode::Deep => { + let mut some_live = false; + let mut some_dead = false; + let mut children_count = 0; + on_all_children_bits( + self.tcx(), self.mir(), self.ctxt.move_data(), + path, |child| { + if self.ctxt.path_needs_drop(child) { + let (live, dead) = self.init_data.state(child); + debug!("elaborate_drop: state({:?}) = {:?}", + child, (live, dead)); + some_live |= live; + some_dead |= dead; + children_count += 1; + } + }); + ((some_live, some_dead), children_count != 1) + } + }; + match (maybe_live, maybe_dead, multipart) { + (false, _, _) => DropStyle::Dead, + (true, false, _) => DropStyle::Static, + (true, true, false) => DropStyle::Conditional, + (true, true, true) => DropStyle::Open, + } + } + + fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) { + match mode { + DropFlagMode::Shallow => { + self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent); + } + DropFlagMode::Deep => { + on_all_children_bits( + self.tcx(), self.mir(), self.ctxt.move_data(), path, + |child| self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent) + ); + } + } + } + + fn field_subpath(&self, path: Self::Path, field: Field) -> Option { + super::move_path_children_matching(self.ctxt.move_data(), path, |p| { + match p { + &Projection { + elem: ProjectionElem::Field(idx, _), .. + } => idx == field, + _ => false + } + }) + } + + fn deref_subpath(&self, path: Self::Path) -> Option { + super::move_path_children_matching(self.ctxt.move_data(), path, |p| { + match p { + &Projection { elem: ProjectionElem::Deref, .. } => true, + _ => false + } + }) + } + + fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option { + super::move_path_children_matching(self.ctxt.move_data(), path, |p| { + match p { + &Projection { + elem: ProjectionElem::Downcast(_, idx), .. + } => idx == variant, + _ => false + } + }) + } + + fn get_drop_flag(&mut self, path: Self::Path) -> Option> { + self.ctxt.drop_flag(path).map(Operand::Consume) + } +} + struct ElaborateDropsCtxt<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, @@ -125,19 +227,6 @@ struct ElaborateDropsCtxt<'a, 'tcx: 'a> { patch: MirPatch<'tcx>, } -#[derive(Copy, Clone, Debug)] -struct DropCtxt<'a, 'tcx: 'a> { - source_info: SourceInfo, - is_cleanup: bool, - - init_data: &'a InitializationData, - - lvalue: &'a Lvalue<'tcx>, - path: MovePathIndex, - succ: BasicBlock, - unwind: Option -} - impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn move_data(&self) -> &'b MoveData<'tcx> { &self.env.move_data } fn param_env(&self) -> &'b ty::ParameterEnvironment<'tcx> { @@ -254,19 +343,22 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let init_data = self.initialization_data_at(loc); match self.move_data().rev_lookup.find(location) { LookupResult::Exact(path) => { - self.elaborate_drop(&DropCtxt { - source_info: terminator.source_info, - is_cleanup: data.is_cleanup, - init_data: &init_data, - lvalue: location, - path: path, - succ: target, - unwind: if data.is_cleanup { + elaborate_drop( + &mut Elaborator { + init_data: &init_data, + ctxt: self + }, + terminator.source_info, + data.is_cleanup, + location, + path, + target, + if data.is_cleanup { None } else { Some(Option::unwrap_or(unwind, resume_block)) - } - }, bb); + }, + bb) } LookupResult::Parent(..) => { span_bug!(terminator.source_info.span, @@ -343,15 +435,18 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); let init_data = self.initialization_data_at(loc); - self.elaborate_drop(&DropCtxt { - source_info: terminator.source_info, - is_cleanup: data.is_cleanup, - init_data: &init_data, - lvalue: location, - path: path, - succ: target, - unwind: Some(unwind) - }, bb); + elaborate_drop( + &mut Elaborator { + init_data: &init_data, + ctxt: self + }, + terminator.source_info, + data.is_cleanup, + location, + path, + target, + Some(unwind), + bb); on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { self.set_drop_flag(Location { block: target, statement_index: 0 }, child, DropFlagState::Present); @@ -372,547 +467,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } } - /// This elaborates a single drop instruction, located at `bb`, and - /// patches over it. - /// - /// The elaborated drop checks the drop flags to only drop what - /// is initialized. - /// - /// In addition, the relevant drop flags also need to be cleared - /// to avoid double-drops. However, in the middle of a complex - /// drop, one must avoid clearing some of the flags before they - /// are read, as that would cause a memory leak. - /// - /// In particular, when dropping an ADT, multiple fields may be - /// joined together under the `rest` subpath. They are all controlled - /// by the primary drop flag, but only the last rest-field dropped - /// should clear it (and it must also not clear anything else). - /// - /// FIXME: I think we should just control the flags externally - /// and then we do not need this machinery. - fn elaborate_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, bb: BasicBlock) { - debug!("elaborate_drop({:?})", c); - - let mut some_live = false; - let mut some_dead = false; - let mut children_count = 0; - on_all_children_bits( - self.tcx, self.mir, self.move_data(), - c.path, |child| { - if self.path_needs_drop(child) { - let (live, dead) = c.init_data.state(child); - debug!("elaborate_drop: state({:?}) = {:?}", - child, (live, dead)); - some_live |= live; - some_dead |= dead; - children_count += 1; - } - }); - - debug!("elaborate_drop({:?}): live - {:?}", c, - (some_live, some_dead)); - match (some_live, some_dead) { - (false, false) | (false, true) => { - // dead drop - patch it out - self.patch.patch_terminator(bb, TerminatorKind::Goto { - target: c.succ - }); - } - (true, false) => { - // static drop - just set the flag - self.patch.patch_terminator(bb, TerminatorKind::Drop { - location: c.lvalue.clone(), - target: c.succ, - unwind: c.unwind - }); - self.drop_flags_for_drop(c, bb); - } - (true, true) => { - // dynamic drop - let drop_bb = if children_count == 1 || self.must_complete_drop(c) { - self.conditional_drop(c) - } else { - self.open_drop(c) - }; - self.patch.patch_terminator(bb, TerminatorKind::Goto { - target: drop_bb - }); - } - } - } - - /// Return the lvalue and move path for each field of `variant`, - /// (the move path is `None` if the field is a rest field). - fn move_paths_for_fields(&self, - base_lv: &Lvalue<'tcx>, - variant_path: MovePathIndex, - variant: &'tcx ty::VariantDef, - substs: &'tcx Substs<'tcx>) - -> Vec<(Lvalue<'tcx>, Option)> - { - variant.fields.iter().enumerate().map(|(i, f)| { - let subpath = - super::move_path_children_matching(self.move_data(), variant_path, |p| { - match p { - &Projection { - elem: ProjectionElem::Field(idx, _), .. - } => idx.index() == i, - _ => false - } - }); - - let field_ty = - self.tcx.normalize_associated_type_in_env( - &f.ty(self.tcx, substs), - self.param_env() - ); - (base_lv.clone().field(Field::new(i), field_ty), subpath) - }).collect() - } - - /// Create one-half of the drop ladder for a list of fields, and return - /// the list of steps in it in reverse order. - /// - /// `unwind_ladder` is such a list of steps in reverse order, - /// which is called instead of the next step if the drop unwinds - /// (the first field is never reached). If it is `None`, all - /// unwind targets are left blank. - fn drop_halfladder<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - unwind_ladder: Option>, - succ: BasicBlock, - fields: &[(Lvalue<'tcx>, Option)], - is_cleanup: bool) - -> Vec - { - let mut unwind_succ = if is_cleanup { - None - } else { - c.unwind - }; - - let mut succ = self.new_block( - c, c.is_cleanup, TerminatorKind::Goto { target: succ } - ); - - // Always clear the "master" drop flag at the bottom of the - // ladder. This is needed because the "master" drop flag - // protects the ADT's discriminant, which is invalidated - // after the ADT is dropped. - self.set_drop_flag( - Location { block: succ, statement_index: 0 }, - c.path, - DropFlagState::Absent - ); - - fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| { - succ = if let Some(path) = path { - debug!("drop_ladder: for std field {} ({:?})", i, lv); - - self.elaborated_drop_block(&DropCtxt { - source_info: c.source_info, - is_cleanup: is_cleanup, - init_data: c.init_data, - lvalue: lv, - path: path, - succ: succ, - unwind: unwind_succ, - }) - } else { - debug!("drop_ladder: for rest field {} ({:?})", i, lv); - - self.complete_drop(&DropCtxt { - source_info: c.source_info, - is_cleanup: is_cleanup, - init_data: c.init_data, - lvalue: lv, - path: c.path, - succ: succ, - unwind: unwind_succ, - }, false) - }; - - unwind_succ = unwind_ladder.as_ref().map(|p| p[i]); - succ - }).collect() - } - - /// Create a full drop ladder, consisting of 2 connected half-drop-ladders - /// - /// For example, with 3 fields, the drop ladder is - /// - /// .d0: - /// ELAB(drop location.0 [target=.d1, unwind=.c1]) - /// .d1: - /// ELAB(drop location.1 [target=.d2, unwind=.c2]) - /// .d2: - /// ELAB(drop location.2 [target=`c.succ`, unwind=`c.unwind`]) - /// .c1: - /// ELAB(drop location.1 [target=.c2]) - /// .c2: - /// ELAB(drop location.2 [target=`c.unwind]) - fn drop_ladder<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - fields: Vec<(Lvalue<'tcx>, Option)>) - -> BasicBlock - { - debug!("drop_ladder({:?}, {:?})", c, fields); - - let mut fields = fields; - fields.retain(|&(ref lvalue, _)| { - let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - self.tcx.type_needs_drop_given_env(ty, self.param_env()) - }); - - debug!("drop_ladder - fields needing drop: {:?}", fields); - - let unwind_ladder = if c.is_cleanup { - None - } else { - Some(self.drop_halfladder(c, None, c.unwind.unwrap(), &fields, true)) - }; - - self.drop_halfladder(c, unwind_ladder, c.succ, &fields, c.is_cleanup) - .last().cloned().unwrap_or(c.succ) - } - - fn open_drop_for_tuple<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, tys: &[Ty<'tcx>]) - -> BasicBlock - { - debug!("open_drop_for_tuple({:?}, {:?})", c, tys); - - let fields = tys.iter().enumerate().map(|(i, &ty)| { - (c.lvalue.clone().field(Field::new(i), ty), - super::move_path_children_matching( - self.move_data(), c.path, |proj| match proj { - &Projection { - elem: ProjectionElem::Field(f, _), .. - } => f.index() == i, - _ => false - } - )) - }).collect(); - - self.drop_ladder(c, fields) - } - - fn open_drop_for_box<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, ty: Ty<'tcx>) - -> BasicBlock - { - debug!("open_drop_for_box({:?}, {:?})", c, ty); - - let interior_path = super::move_path_children_matching( - self.move_data(), c.path, |proj| match proj { - &Projection { elem: ProjectionElem::Deref, .. } => true, - _ => false - }).unwrap(); - - let interior = c.lvalue.clone().deref(); - let inner_c = DropCtxt { - lvalue: &interior, - unwind: c.unwind.map(|u| { - self.box_free_block(c, ty, u, true) - }), - succ: self.box_free_block(c, ty, c.succ, c.is_cleanup), - path: interior_path, - ..*c - }; - - self.elaborated_drop_block(&inner_c) - } - - 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); - - match adt.variants.len() { - 1 => { - let fields = self.move_paths_for_fields( - c.lvalue, - c.path, - &adt.variants[0], - substs - ); - self.drop_ladder(c, fields) - } - _ => { - let mut values = Vec::with_capacity(adt.variants.len()); - let mut blocks = Vec::with_capacity(adt.variants.len()); - let mut otherwise = None; - for (variant_index, discr) in adt.discriminants(self.tcx).enumerate() { - let subpath = super::move_path_children_matching( - self.move_data(), c.path, |proj| match proj { - &Projection { - elem: ProjectionElem::Downcast(_, idx), .. - } => idx == variant_index, - _ => false - }); - if let Some(variant_path) = subpath { - let base_lv = c.lvalue.clone().elem( - ProjectionElem::Downcast(adt, variant_index) - ); - let fields = self.move_paths_for_fields( - &base_lv, - variant_path, - &adt.variants[variant_index], - substs); - values.push(discr); - blocks.push(self.drop_ladder(c, fields)); - } else { - // variant not found - drop the entire enum - if let None = otherwise { - otherwise = Some(self.complete_drop(c, true)); - } - } - } - if let Some(block) = otherwise { - blocks.push(block); - } else { - values.pop(); - } - // If there are multiple variants, then if something - // is present within the enum the discriminant, tracked - // by the rest path, must be initialized. - // - // Additionally, we do not want to switch on the - // discriminant after it is free-ed, because that - // way lies only trouble. - let discr_ty = adt.repr.discr_type().to_ty(self.tcx); - let discr = Lvalue::Local(self.patch.new_temp(discr_ty)); - let switch_block = self.patch.new_block(BasicBlockData { - statements: vec![ - Statement { - source_info: c.source_info, - kind: StatementKind::Assign(discr.clone(), - Rvalue::Discriminant(c.lvalue.clone())) - } - ], - terminator: Some(Terminator { - source_info: c.source_info, - kind: TerminatorKind::SwitchInt { - discr: Operand::Consume(discr), - switch_ty: discr_ty, - values: From::from(values), - targets: blocks, - } - }), - is_cleanup: c.is_cleanup, - }); - self.drop_flag_test_block(c, switch_block) - } - } - } - - /// The slow-path - create an "open", elaborated drop for a type - /// which is moved-out-of only partially, and patch `bb` to a jump - /// to it. This must not be called on ADTs with a destructor, - /// as these can't be moved-out-of, except for `Box`, which is - /// special-cased. - /// - /// This creates a "drop ladder" that drops the needed fields of the - /// ADT, both in the success case or if one of the destructors fail. - fn open_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { - let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - match ty.sty { - ty::TyClosure(def_id, substs) => { - let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx).collect(); - self.open_drop_for_tuple(c, &tys) - } - ty::TyTuple(tys, _) => { - self.open_drop_for_tuple(c, tys) - } - ty::TyAdt(def, _) if def.is_box() => { - self.open_drop_for_box(c, ty.boxed_ty()) - } - ty::TyAdt(def, substs) => { - self.open_drop_for_adt(c, def, substs) - } - _ => bug!("open drop from non-ADT `{:?}`", ty) - } - } - - /// Return a basic block that drop an lvalue using the context - /// and path in `c`. If `update_drop_flag` is true, also - /// clear `c`. - /// - /// if FLAG(c.path) - /// if(update_drop_flag) FLAG(c.path) = false - /// drop(c.lv) - fn complete_drop<'a>( - &mut self, - c: &DropCtxt<'a, 'tcx>, - update_drop_flag: bool) - -> BasicBlock - { - debug!("complete_drop({:?},{:?})", c, update_drop_flag); - - let drop_block = self.drop_block(c); - if update_drop_flag { - self.set_drop_flag( - Location { block: drop_block, statement_index: 0 }, - c.path, - DropFlagState::Absent - ); - } - - self.drop_flag_test_block(c, drop_block) - } - - /// Create a simple conditional drop. - /// - /// if FLAG(c.lv) - /// FLAGS(c.lv) = false - /// drop(c.lv) - fn conditional_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) - -> BasicBlock - { - debug!("conditional_drop({:?})", c); - let drop_bb = self.drop_block(c); - self.drop_flags_for_drop(c, drop_bb); - - self.drop_flag_test_block(c, drop_bb) - } - - fn new_block<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - is_cleanup: bool, - k: TerminatorKind<'tcx>) - -> BasicBlock - { - self.patch.new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: c.source_info, kind: k - }), - is_cleanup: is_cleanup - }) - } - - fn elaborated_drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { - debug!("elaborated_drop_block({:?})", c); - let blk = self.drop_block(c); - self.elaborate_drop(c, blk); - blk - } - - fn drop_flag_test_block<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - on_set: BasicBlock) - -> BasicBlock { - self.drop_flag_test_block_with_succ(c, c.is_cleanup, on_set, c.succ) - } - - fn drop_flag_test_block_with_succ<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - is_cleanup: bool, - on_set: BasicBlock, - on_unset: BasicBlock) - -> BasicBlock - { - let (maybe_live, maybe_dead) = c.init_data.state(c.path); - debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}", - c, is_cleanup, on_set, (maybe_live, maybe_dead)); - - match (maybe_live, maybe_dead) { - (false, _) => on_unset, - (true, false) => on_set, - (true, true) => { - let flag = self.drop_flag(c.path).unwrap(); - let term = TerminatorKind::if_(self.tcx, Operand::Consume(flag), on_set, on_unset); - self.new_block(c, is_cleanup, term) - } - } - } - - fn drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { - self.new_block(c, c.is_cleanup, TerminatorKind::Drop { - location: c.lvalue.clone(), - target: c.succ, - unwind: c.unwind - }) - } - - fn box_free_block<'a>( - &mut self, - c: &DropCtxt<'a, 'tcx>, - ty: Ty<'tcx>, - target: BasicBlock, - is_cleanup: bool - ) -> BasicBlock { - let block = self.unelaborated_free_block(c, ty, target, is_cleanup); - self.drop_flag_test_block_with_succ(c, is_cleanup, block, target) - } - - fn unelaborated_free_block<'a>( - &mut self, - c: &DropCtxt<'a, 'tcx>, - ty: Ty<'tcx>, - target: BasicBlock, - is_cleanup: bool - ) -> BasicBlock { - let mut statements = vec![]; - if let Some(&flag) = self.drop_flags.get(&c.path) { - statements.push(Statement { - source_info: c.source_info, - kind: StatementKind::Assign( - Lvalue::Local(flag), - self.constant_bool(c.source_info.span, false) - ) - }); - } - - let tcx = self.tcx; - let unit_temp = Lvalue::Local(self.patch.new_temp(tcx.mk_nil())); - let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); - let substs = tcx.mk_substs(iter::once(Kind::from(ty))); - let fty = tcx.item_type(free_func).subst(tcx, substs); - - self.patch.new_block(BasicBlockData { - statements: statements, - terminator: Some(Terminator { - source_info: c.source_info, kind: TerminatorKind::Call { - func: Operand::Constant(Constant { - span: c.source_info.span, - ty: fty, - literal: Literal::Item { - def_id: free_func, - substs: substs - } - }), - args: vec![Operand::Consume(c.lvalue.clone())], - destination: Some((unit_temp, target)), - cleanup: None - } - }), - is_cleanup: is_cleanup - }) - } - - fn must_complete_drop<'a>(&self, c: &DropCtxt<'a, 'tcx>) -> bool { - // if we have a destuctor, we must *not* split the drop. - - // dataflow can create unneeded children in some cases - // - be sure to ignore them. - - let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - - match ty.sty { - ty::TyAdt(def, _) => { - if def.has_dtor(self.tcx) && !def.is_box() { - self.tcx.sess.span_warn( - c.source_info.span, - &format!("dataflow bug??? moving out of type with dtor {:?}", - c)); - true - } else { - false - } - } - _ => false - } - } - fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> { Rvalue::Use(Operand::Constant(Constant { span: span, @@ -1023,15 +577,4 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } } } - - fn drop_flags_for_drop<'a>(&mut self, - c: &DropCtxt<'a, 'tcx>, - bb: BasicBlock) - { - let loc = self.patch.terminator_loc(self.mir, bb); - on_all_children_bits( - self.tcx, self.mir, self.move_data(), c.path, - |child| self.set_drop_flag(loc, child, DropFlagState::Absent) - ); - } } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 1c9ee335699ae..9237bb31f6bd7 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -16,12 +16,12 @@ use syntax_pos::DUMMY_SP; use rustc::mir::{self, BasicBlock, BasicBlockData, Mir, Statement, Terminator, Location}; use rustc::session::Session; use rustc::ty::{self, TyCtxt}; +use rustc_mir::util::elaborate_drops::DropFlagState; mod abs_domain; pub mod elaborate_drops; mod dataflow; mod gather_moves; -mod patch; // mod graphviz; use self::dataflow::{BitDenotation}; @@ -183,21 +183,6 @@ impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -enum DropFlagState { - Present, // i.e. initialized - Absent, // i.e. deinitialized or "moved" -} - -impl DropFlagState { - fn value(self) -> bool { - match self { - DropFlagState::Present => true, - DropFlagState::Absent => false - } - } -} - fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>, path: MovePathIndex, mut cond: F) diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index b6978478085d5..6cd97e9559885 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -26,8 +26,7 @@ use rustc::session::config::Input; use rustc_borrowck as borrowck; use rustc_borrowck::graphviz as borrowck_dot; -use rustc_mir::pretty::write_mir_pretty; -use rustc_mir::graphviz::write_mir_graphviz; +use rustc_mir::util::{write_mir_pretty, write_mir_graphviz}; use syntax::ast::{self, BlockCheckMode}; use syntax::fold::{self, Folder}; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 2718a0204a1ed..590c6a430b98a 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -49,13 +49,11 @@ pub mod diagnostics; pub mod build; pub mod callgraph; -pub mod def_use; -pub mod graphviz; mod hair; mod shim; pub mod mir_map; -pub mod pretty; pub mod transform; +pub mod util; use rustc::ty::maps::Providers; diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 2d2b90235ca3b..8c138d779c106 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -23,8 +23,8 @@ use rustc::mir::Mir; use rustc::mir::transform::MirSource; use rustc::mir::visit::MutVisitor; use shim; -use pretty; use hair::cx::Cx; +use util as mir_util; use rustc::traits::Reveal; use rustc::ty::{self, Ty, TyCtxt}; @@ -175,7 +175,7 @@ fn build_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) mem::transmute::>(mir) }; - pretty::dump_mir(tcx, "mir_map", &0, src, &mir); + mir_util::dump_mir(tcx, "mir_map", &0, src, &mir); tcx.alloc_mir(mir) }) @@ -234,7 +234,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mem::transmute::>(mir) }; - pretty::dump_mir(tcx, "mir_map", &0, src, &mir); + mir_util::dump_mir(tcx, "mir_map", &0, src, &mir); tcx.alloc_mir(mir) }) diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs index 2194c20c2f442..5d127a5aed461 100644 --- a/src/librustc_mir/transform/copy_prop.rs +++ b/src/librustc_mir/transform/copy_prop.rs @@ -29,11 +29,11 @@ //! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the //! future. -use def_use::DefUseAnalysis; use rustc::mir::{Constant, Local, LocalKind, Location, Lvalue, Mir, Operand, Rvalue, StatementKind}; use rustc::mir::transform::{MirPass, MirSource, Pass}; use rustc::mir::visit::MutVisitor; use rustc::ty::TyCtxt; +use util::def_use::DefUseAnalysis; use transform::qualify_consts; pub struct CopyPropagation; diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs index 035f33de91aa5..f22a71636a98e 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -15,7 +15,7 @@ use std::fmt; use rustc::ty::TyCtxt; use rustc::mir::*; use rustc::mir::transform::{Pass, MirPass, MirPassHook, MirSource}; -use pretty; +use util as mir_util; pub struct Marker<'a>(pub &'a str); @@ -56,7 +56,7 @@ impl<'tcx> MirPassHook<'tcx> for DumpMir { pass: &Pass, is_after: bool) { - pretty::dump_mir( + mir_util::dump_mir( tcx, &*pass.name(), &Disambiguator { diff --git a/src/librustc_mir/def_use.rs b/src/librustc_mir/util/def_use.rs similarity index 100% rename from src/librustc_mir/def_use.rs rename to src/librustc_mir/util/def_use.rs diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs new file mode 100644 index 0000000000000..ce6debab9a4b8 --- /dev/null +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -0,0 +1,561 @@ +// 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. + +use std::fmt; +use rustc::mir::*; +use rustc::middle::lang_items; +use rustc::ty::{self, Ty}; +use rustc::ty::subst::{Kind, Subst, Substs}; +use rustc::ty::util::IntTypeExt; +use rustc_data_structures::indexed_vec::Idx; +use util::patch::MirPatch; + +use std::iter; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum DropFlagState { + Present, // i.e. initialized + Absent, // i.e. deinitialized or "moved" +} + +impl DropFlagState { + pub fn value(self) -> bool { + match self { + DropFlagState::Present => true, + DropFlagState::Absent => false + } + } +} + +#[derive(Debug)] +pub enum DropStyle { + Dead, + Static, + Conditional, + Open, +} + +#[derive(Debug)] +pub enum DropFlagMode { + Shallow, + Deep +} + +pub trait DropElaborator<'a, 'tcx: 'a> : fmt::Debug { + type Path : Copy + fmt::Debug; + + fn patch(&mut self) -> &mut MirPatch<'tcx>; + fn mir(&self) -> &'a Mir<'tcx>; + fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx>; + fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx>; + + fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; + fn get_drop_flag(&mut self, path: Self::Path) -> Option>; + fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); + + + fn field_subpath(&self, path: Self::Path, field: Field) -> Option; + fn deref_subpath(&self, path: Self::Path) -> Option; + fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option; +} + +#[derive(Debug)] +struct DropCtxt<'l, 'b: 'l, 'tcx: 'b, D> + where D : DropElaborator<'b, 'tcx> + 'l +{ + elaborator: &'l mut D, + + source_info: SourceInfo, + is_cleanup: bool, + + lvalue: &'l Lvalue<'tcx>, + path: D::Path, + succ: BasicBlock, + unwind: Option, +} + +pub fn elaborate_drop<'b, 'tcx, D>( + elaborator: &mut D, + source_info: SourceInfo, + is_cleanup: bool, + lvalue: &Lvalue<'tcx>, + path: D::Path, + succ: BasicBlock, + unwind: Option, + bb: BasicBlock) + where D: DropElaborator<'b, 'tcx> +{ + DropCtxt { + elaborator, source_info, is_cleanup, lvalue, path, succ, unwind + }.elaborate_drop(bb) +} + +impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> + where D: DropElaborator<'b, 'tcx> +{ + fn lvalue_ty(&self, lvalue: &Lvalue<'tcx>) -> Ty<'tcx> { + lvalue.ty(self.elaborator.mir(), self.tcx()).to_ty(self.tcx()) + } + + fn tcx(&self) -> ty::TyCtxt<'b, 'tcx, 'tcx> { + self.elaborator.tcx() + } + + /// This elaborates a single drop instruction, located at `bb`, and + /// patches over it. + /// + /// The elaborated drop checks the drop flags to only drop what + /// is initialized. + /// + /// In addition, the relevant drop flags also need to be cleared + /// to avoid double-drops. However, in the middle of a complex + /// drop, one must avoid clearing some of the flags before they + /// are read, as that would cause a memory leak. + /// + /// In particular, when dropping an ADT, multiple fields may be + /// joined together under the `rest` subpath. They are all controlled + /// by the primary drop flag, but only the last rest-field dropped + /// should clear it (and it must also not clear anything else). + /// + /// FIXME: I think we should just control the flags externally + /// and then we do not need this machinery. + pub fn elaborate_drop<'a>(&mut self, bb: BasicBlock) { + debug!("elaborate_drop({:?})", self); + let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep); + debug!("elaborate_drop({:?}): live - {:?}", self, style); + match style { + DropStyle::Dead => { + self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { + target: self.succ + }); + } + DropStyle::Static => { + let loc = self.terminator_loc(bb); + self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep); + self.elaborator.patch().patch_terminator(bb, TerminatorKind::Drop { + location: self.lvalue.clone(), + target: self.succ, + unwind: self.unwind + }); + } + DropStyle::Conditional => { + let drop_bb = self.complete_drop(Some(DropFlagMode::Deep)); + self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { + target: drop_bb + }); + } + DropStyle::Open => { + let drop_bb = self.open_drop(); + self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { + target: drop_bb + }); + } + } + } + + /// Return the lvalue and move path for each field of `variant`, + /// (the move path is `None` if the field is a rest field). + fn move_paths_for_fields(&self, + base_lv: &Lvalue<'tcx>, + variant_path: D::Path, + variant: &'tcx ty::VariantDef, + substs: &'tcx Substs<'tcx>) + -> Vec<(Lvalue<'tcx>, Option)> + { + variant.fields.iter().enumerate().map(|(i, f)| { + let field = Field::new(i); + let subpath = self.elaborator.field_subpath(variant_path, field); + + let field_ty = + self.tcx().normalize_associated_type_in_env( + &f.ty(self.tcx(), substs), + self.elaborator.param_env() + ); + (base_lv.clone().field(field, field_ty), subpath) + }).collect() + } + + fn drop_subpath(&mut self, + is_cleanup: bool, + lvalue: &Lvalue<'tcx>, + path: Option, + succ: BasicBlock, + unwind: Option) + -> BasicBlock + { + if let Some(path) = path { + debug!("drop_subpath: for std field {:?}", lvalue); + + DropCtxt { + elaborator: self.elaborator, + source_info: self.source_info, + path, lvalue, succ, unwind, is_cleanup + }.elaborated_drop_block() + } else { + debug!("drop_subpath: for rest field {:?}", lvalue); + + DropCtxt { + elaborator: self.elaborator, + source_info: self.source_info, + lvalue, succ, unwind, is_cleanup, + // Using `self.path` here to condition the drop on + // our own drop flag. + path: self.path + }.complete_drop(None) + } + } + + /// Create one-half of the drop ladder for a list of fields, and return + /// the list of steps in it in reverse order. + /// + /// `unwind_ladder` is such a list of steps in reverse order, + /// which is called instead of the next step if the drop unwinds + /// (the first field is never reached). If it is `None`, all + /// unwind targets are left blank. + fn drop_halfladder<'a>(&mut self, + unwind_ladder: Option>, + succ: BasicBlock, + fields: &[(Lvalue<'tcx>, Option)], + is_cleanup: bool) + -> Vec + { + let mut unwind_succ = if is_cleanup { + None + } else { + self.unwind + }; + + let goto = TerminatorKind::Goto { target: succ }; + let mut succ = self.new_block(is_cleanup, goto); + + // Always clear the "master" drop flag at the bottom of the + // ladder. This is needed because the "master" drop flag + // protects the ADT's discriminant, which is invalidated + // after the ADT is dropped. + let succ_loc = Location { block: succ, statement_index: 0 }; + self.elaborator.clear_drop_flag(succ_loc, self.path, DropFlagMode::Shallow); + + fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| { + succ = self.drop_subpath(is_cleanup, lv, path, succ, unwind_succ); + unwind_succ = unwind_ladder.as_ref().map(|p| p[i]); + succ + }).collect() + } + + /// Create a full drop ladder, consisting of 2 connected half-drop-ladders + /// + /// For example, with 3 fields, the drop ladder is + /// + /// .d0: + /// ELAB(drop location.0 [target=.d1, unwind=.c1]) + /// .d1: + /// ELAB(drop location.1 [target=.d2, unwind=.c2]) + /// .d2: + /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`]) + /// .c1: + /// ELAB(drop location.1 [target=.c2]) + /// .c2: + /// ELAB(drop location.2 [target=`self.unwind]) + fn drop_ladder<'a>(&mut self, + fields: Vec<(Lvalue<'tcx>, Option)>) + -> BasicBlock + { + debug!("drop_ladder({:?}, {:?})", self, fields); + + let mut fields = fields; + fields.retain(|&(ref lvalue, _)| { + self.tcx().type_needs_drop_given_env( + self.lvalue_ty(lvalue), self.elaborator.param_env()) + }); + + debug!("drop_ladder - fields needing drop: {:?}", fields); + + let unwind_ladder = if self.is_cleanup { + None + } else { + let unwind = self.unwind.unwrap(); // FIXME(#6393) + Some(self.drop_halfladder(None, unwind, &fields, true)) + }; + + let succ = self.succ; // FIXME(#6393) + let is_cleanup = self.is_cleanup; + self.drop_halfladder(unwind_ladder, succ, &fields, is_cleanup) + .last().cloned().unwrap_or(succ) + } + + fn open_drop_for_tuple<'a>(&mut self, tys: &[Ty<'tcx>]) + -> BasicBlock + { + debug!("open_drop_for_tuple({:?}, {:?})", self, tys); + + let fields = tys.iter().enumerate().map(|(i, &ty)| { + (self.lvalue.clone().field(Field::new(i), ty), + self.elaborator.field_subpath(self.path, Field::new(i))) + }).collect(); + + self.drop_ladder(fields) + } + + fn open_drop_for_box<'a>(&mut self, ty: Ty<'tcx>) -> BasicBlock + { + debug!("open_drop_for_box({:?}, {:?})", self, ty); + + let interior = self.lvalue.clone().deref(); + let interior_path = self.elaborator.deref_subpath(self.path); + + let succ = self.succ; // FIXME(#6393) + let is_cleanup = self.is_cleanup; + let succ = self.box_free_block(ty, succ, is_cleanup); + let unwind_succ = self.unwind.map(|u| { + self.box_free_block(ty, u, true) + }); + + self.drop_subpath(is_cleanup, &interior, interior_path, succ, unwind_succ) + } + + fn open_drop_for_adt<'a>(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>) + -> BasicBlock { + debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs); + + match adt.variants.len() { + 1 => { + let fields = self.move_paths_for_fields( + self.lvalue, + self.path, + &adt.variants[0], + substs + ); + self.drop_ladder(fields) + } + _ => { + let mut values = Vec::with_capacity(adt.variants.len()); + let mut blocks = Vec::with_capacity(adt.variants.len()); + let mut otherwise = None; + for (variant_index, discr) in adt.discriminants(self.tcx()).enumerate() { + let subpath = self.elaborator.downcast_subpath( + self.path, variant_index); + if let Some(variant_path) = subpath { + let base_lv = self.lvalue.clone().elem( + ProjectionElem::Downcast(adt, variant_index) + ); + let fields = self.move_paths_for_fields( + &base_lv, + variant_path, + &adt.variants[variant_index], + substs); + values.push(discr); + blocks.push(self.drop_ladder(fields)); + } else { + // variant not found - drop the entire enum + if let None = otherwise { + otherwise = + Some(self.complete_drop(Some(DropFlagMode::Shallow))); + } + } + } + if let Some(block) = otherwise { + blocks.push(block); + } else { + values.pop(); + } + // If there are multiple variants, then if something + // is present within the enum the discriminant, tracked + // by the rest path, must be initialized. + // + // Additionally, we do not want to switch on the + // discriminant after it is free-ed, because that + // way lies only trouble. + let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); + let discr = Lvalue::Local(self.new_temp(discr_ty)); + let discr_rv = Rvalue::Discriminant(self.lvalue.clone()); + let switch_block = self.elaborator.patch().new_block(BasicBlockData { + statements: vec![ + Statement { + source_info: self.source_info, + kind: StatementKind::Assign(discr.clone(), discr_rv), + } + ], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Consume(discr), + switch_ty: discr_ty, + values: From::from(values), + targets: blocks, + } + }), + is_cleanup: self.is_cleanup, + }); + self.drop_flag_test_block(switch_block) + } + } + } + + /// The slow-path - create an "open", elaborated drop for a type + /// which is moved-out-of only partially, and patch `bb` to a jump + /// to it. This must not be called on ADTs with a destructor, + /// as these can't be moved-out-of, except for `Box`, which is + /// special-cased. + /// + /// This creates a "drop ladder" that drops the needed fields of the + /// ADT, both in the success case or if one of the destructors fail. + fn open_drop<'a>(&mut self) -> BasicBlock { + let ty = self.lvalue_ty(self.lvalue); + match ty.sty { + ty::TyClosure(def_id, substs) => { + let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect(); + self.open_drop_for_tuple(&tys) + } + ty::TyTuple(tys, _) => { + self.open_drop_for_tuple(tys) + } + ty::TyAdt(def, _) if def.is_box() => { + self.open_drop_for_box(ty.boxed_ty()) + } + ty::TyAdt(def, substs) => { + self.open_drop_for_adt(def, substs) + } + _ => bug!("open drop from non-ADT `{:?}`", ty) + } + } + + /// Return a basic block that drop an lvalue using the context + /// and path in `c`. If `mode` is something, also clear `c` + /// according to it. + /// + /// if FLAG(self.path) + /// if let Some(mode) = mode: FLAG(self.path)[mode] = false + /// drop(self.lv) + fn complete_drop<'a>(&mut self, drop_mode: Option) -> BasicBlock + { + debug!("complete_drop({:?},{:?})", self, drop_mode); + + let drop_block = self.drop_block(); + if let Some(mode) = drop_mode { + let block_start = Location { block: drop_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, mode); + } + + self.drop_flag_test_block(drop_block) + } + + fn elaborated_drop_block<'a>(&mut self) -> BasicBlock { + debug!("elaborated_drop_block({:?})", self); + let blk = self.drop_block(); + self.elaborate_drop(blk); + blk + } + + fn box_free_block<'a>( + &mut self, + ty: Ty<'tcx>, + target: BasicBlock, + is_cleanup: bool + ) -> BasicBlock { + let block = self.unelaborated_free_block(ty, target, is_cleanup); + self.drop_flag_test_block_with_succ(is_cleanup, block, target) + } + + fn unelaborated_free_block<'a>( + &mut self, + ty: Ty<'tcx>, + target: BasicBlock, + is_cleanup: bool + ) -> BasicBlock { + let tcx = self.tcx(); + let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); + let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); + let substs = tcx.mk_substs(iter::once(Kind::from(ty))); + let fty = tcx.item_type(free_func).subst(tcx, substs); + + let free_block = self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, kind: TerminatorKind::Call { + func: Operand::Constant(Constant { + span: self.source_info.span, + ty: fty, + literal: Literal::Item { + def_id: free_func, + substs: substs + } + }), + args: vec![Operand::Consume(self.lvalue.clone())], + destination: Some((unit_temp, target)), + cleanup: None + } + }), + is_cleanup: is_cleanup + }); + let block_start = Location { block: free_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); + free_block + } + + fn drop_block<'a>(&mut self) -> BasicBlock { + let block = TerminatorKind::Drop { + location: self.lvalue.clone(), + target: self.succ, + unwind: self.unwind + }; + let is_cleanup = self.is_cleanup; // FIXME(#6393) + self.new_block(is_cleanup, block) + } + + fn drop_flag_test_block<'a>(&mut self, on_set: BasicBlock) -> BasicBlock { + let is_cleanup = self.is_cleanup; + let succ = self.succ; // FIXME(#6393) + self.drop_flag_test_block_with_succ(is_cleanup, on_set, succ) + } + + fn drop_flag_test_block_with_succ<'a>(&mut self, + is_cleanup: bool, + on_set: BasicBlock, + on_unset: BasicBlock) + -> BasicBlock + { + let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); + debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}", + self, is_cleanup, on_set, style); + + match style { + DropStyle::Dead => on_unset, + DropStyle::Static => on_set, + DropStyle::Conditional | DropStyle::Open => { + let flag = self.elaborator.get_drop_flag(self.path).unwrap(); + let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset); + self.new_block(is_cleanup, term) + } + } + } + + fn new_block<'a>(&mut self, + is_cleanup: bool, + k: TerminatorKind<'tcx>) + -> BasicBlock + { + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, kind: k + }), + is_cleanup: is_cleanup + }) + } + + fn new_temp(&mut self, ty: Ty<'tcx>) -> Local { + self.elaborator.patch().new_temp(ty) + } + + fn terminator_loc(&mut self, bb: BasicBlock) -> Location { + let mir = self.elaborator.mir(); + self.elaborator.patch().terminator_loc(mir, bb) + } +} diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/util/graphviz.rs similarity index 100% rename from src/librustc_mir/graphviz.rs rename to src/librustc_mir/util/graphviz.rs diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs new file mode 100644 index 0000000000000..cafc5bca76acd --- /dev/null +++ b/src/librustc_mir/util/mod.rs @@ -0,0 +1,20 @@ +// 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. + +pub mod elaborate_drops; +pub mod def_use; +pub mod patch; + +mod graphviz; +mod pretty; + +pub use self::pretty::{dump_mir, write_mir_pretty}; +pub use self::graphviz::{write_mir_graphviz}; +pub use self::graphviz::write_node_label as write_graphviz_node_label; diff --git a/src/librustc_borrowck/borrowck/mir/patch.rs b/src/librustc_mir/util/patch.rs similarity index 100% rename from src/librustc_borrowck/borrowck/mir/patch.rs rename to src/librustc_mir/util/patch.rs diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/util/pretty.rs similarity index 100% rename from src/librustc_mir/pretty.rs rename to src/librustc_mir/util/pretty.rs From 26df816f520d00fe5ddf332a0a08ad6e26a5698d Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 9 Mar 2017 20:36:01 +0200 Subject: [PATCH 09/11] apply pre-trans passes to Shim MIR --- src/librustc_mir/shim.rs | 23 +++---- src/librustc_mir/transform/add_call_guards.rs | 68 ++++++++++--------- src/librustc_mir/transform/no_landing_pads.rs | 10 ++- src/librustc_mir/transform/simplify.rs | 14 ++-- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 35ad296006dad..2d9ef124ad244 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -28,6 +28,8 @@ use std::cell::RefCell; use std::iter; use std::mem; +use transform::{add_call_guards, no_landing_pads, simplify}; + pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } @@ -42,7 +44,7 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, let param_env = tcx.construct_parameter_environment(span, did, ROOT_CODE_EXTENT); - let result = match instance { + let mut result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), ty::InstanceDef::FnPtrShim(def_id, ty) => { @@ -103,6 +105,10 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } }; + debug!("make_shim({:?}) = untransformed {:?}", instance, result); + no_landing_pads::no_landing_pads(tcx, &mut result); + simplify::simplify_cfg(&mut result); + add_call_guards::add_call_guards(&mut result); debug!("make_shim({:?}) = {:?}", instance, result); let result = tcx.alloc_mir(result); @@ -230,18 +236,13 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, }) }; - let have_unwind = match (rcvr_adjustment, tcx.sess.no_landing_pads()) { - (Adjustment::RefMut, false) => true, - _ => false - }; - // BB #0 block(&mut blocks, statements, TerminatorKind::Call { func: callee, args: args, destination: Some((Lvalue::Local(RETURN_POINTER), BasicBlock::new(1))), - cleanup: if have_unwind { + cleanup: if let Adjustment::RefMut = rcvr_adjustment { Some(BasicBlock::new(3)) } else { None @@ -253,16 +254,12 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, block(&mut blocks, vec![], TerminatorKind::Drop { location: Lvalue::Local(rcvr_arg), target: BasicBlock::new(2), - unwind: if have_unwind { - Some(BasicBlock::new(4)) - } else { - None - } + unwind: None }, false); } // BB #1/#2 - return block(&mut blocks, vec![], TerminatorKind::Return, false); - if have_unwind { + if let Adjustment::RefMut = rcvr_adjustment { // BB #3 - drop if closure panics block(&mut blocks, vec![], TerminatorKind::Drop { location: Lvalue::Local(rcvr_arg), diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs index 89e644e4fb077..80b17c6a008f5 100644 --- a/src/librustc_mir/transform/add_call_guards.rs +++ b/src/librustc_mir/transform/add_call_guards.rs @@ -37,46 +37,50 @@ pub struct AddCallGuards; impl<'tcx> MirPass<'tcx> for AddCallGuards { fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) { - let pred_count: IndexVec<_, _> = - mir.predecessors().iter().map(|ps| ps.len()).collect(); + add_call_guards(mir); + } +} + +pub fn add_call_guards(mir: &mut Mir) { + let pred_count: IndexVec<_, _> = + mir.predecessors().iter().map(|ps| ps.len()).collect(); - // We need a place to store the new blocks generated - let mut new_blocks = Vec::new(); + // We need a place to store the new blocks generated + let mut new_blocks = Vec::new(); - let cur_len = mir.basic_blocks().len(); + let cur_len = mir.basic_blocks().len(); - for block in mir.basic_blocks_mut() { - match block.terminator { - Some(Terminator { - kind: TerminatorKind::Call { - destination: Some((_, ref mut destination)), - cleanup: Some(_), - .. - }, source_info - }) if pred_count[*destination] > 1 => { - // It's a critical edge, break it - let call_guard = BasicBlockData { - statements: vec![], - is_cleanup: block.is_cleanup, - terminator: Some(Terminator { - source_info: source_info, - kind: TerminatorKind::Goto { target: *destination } - }) - }; + for block in mir.basic_blocks_mut() { + match block.terminator { + Some(Terminator { + kind: TerminatorKind::Call { + destination: Some((_, ref mut destination)), + cleanup: Some(_), + .. + }, source_info + }) if pred_count[*destination] > 1 => { + // It's a critical edge, break it + let call_guard = BasicBlockData { + statements: vec![], + is_cleanup: block.is_cleanup, + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Goto { target: *destination } + }) + }; - // Get the index it will be when inserted into the MIR - let idx = cur_len + new_blocks.len(); - new_blocks.push(call_guard); - *destination = BasicBlock::new(idx); - } - _ => {} + // Get the index it will be when inserted into the MIR + let idx = cur_len + new_blocks.len(); + new_blocks.push(call_guard); + *destination = BasicBlock::new(idx); } + _ => {} } + } - debug!("Broke {} N edges", new_blocks.len()); + debug!("Broke {} N edges", new_blocks.len()); - mir.basic_blocks_mut().extend(new_blocks); - } + mir.basic_blocks_mut().extend(new_blocks); } impl Pass for AddCallGuards {} diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index 55a26f4b37fe2..3654ae6940c52 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -42,12 +42,16 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { } } +pub fn no_landing_pads<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &mut Mir<'tcx>) { + if tcx.sess.no_landing_pads() { + NoLandingPads.visit_mir(mir); + } +} + impl<'tcx> MirPass<'tcx> for NoLandingPads { fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut Mir<'tcx>) { - if tcx.sess.no_landing_pads() { - self.visit_mir(mir); - } + no_landing_pads(tcx, mir) } } diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index a762507f35e7e..0a8f147b21410 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -53,14 +53,18 @@ impl<'a> SimplifyCfg<'a> { } } +pub fn simplify_cfg(mir: &mut Mir) { + CfgSimplifier::new(mir).simplify(); + remove_dead_blocks(mir); + + // FIXME: Should probably be moved into some kind of pass manager + mir.basic_blocks_mut().raw.shrink_to_fit(); +} + impl<'l, 'tcx> MirPass<'tcx> for SimplifyCfg<'l> { fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, mir); - CfgSimplifier::new(mir).simplify(); - remove_dead_blocks(mir); - - // FIXME: Should probably be moved into some kind of pass manager - mir.basic_blocks_mut().raw.shrink_to_fit(); + simplify_cfg(mir); } } From f2c7917402ebd858f5b517a3406bec8ef187bfb1 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 14 Mar 2017 01:08:21 +0200 Subject: [PATCH 10/11] translate drop glue using MIR Drop of arrays is now translated in trans::block in an ugly way that I should clean up in a later PR, and does not handle panics in the middle of an array drop, but this commit & PR are growing too big. --- src/libcore/intrinsics.rs | 8 +- src/libcore/ptr.rs | 29 ++ src/librustc/middle/lang_items.rs | 4 +- src/librustc/mir/mod.rs | 18 +- src/librustc/ty/instance.rs | 9 +- .../borrowck/mir/gather_moves.rs | 2 +- src/librustc_mir/shim.rs | 132 +++++- src/librustc_mir/util/elaborate_drops.rs | 301 +++++++++---- src/librustc_trans/abi.rs | 24 +- src/librustc_trans/adt.rs | 22 - src/librustc_trans/attributes.rs | 1 + src/librustc_trans/base.rs | 4 +- src/librustc_trans/callee.rs | 3 + src/librustc_trans/cleanup.rs | 144 ------- src/librustc_trans/collector.rs | 260 ++++------- src/librustc_trans/common.rs | 13 +- src/librustc_trans/context.rs | 16 +- src/librustc_trans/glue.rs | 403 +----------------- src/librustc_trans/lib.rs | 1 - src/librustc_trans/meth.rs | 52 ++- src/librustc_trans/mir/block.rs | 89 ++-- src/librustc_trans/mir/lvalue.rs | 58 +-- src/librustc_trans/mir/operand.rs | 24 +- src/librustc_trans/mir/rvalue.rs | 6 +- src/librustc_trans/monomorphize.rs | 27 +- src/librustc_trans/partitioning.rs | 5 +- src/librustc_trans/symbol_map.rs | 1 - src/librustc_trans/trans_item.rs | 80 +--- src/librustc_trans/tvec.rs | 7 +- .../cross-crate-generic-functions.rs | 2 - .../drop_in_place_intrinsic.rs | 7 +- .../item-collection/function-as-argument.rs | 2 - .../item-collection/generic-drop-glue.rs | 19 +- .../instantiation-through-vtable.rs | 3 +- .../items-within-generic-items.rs | 2 - .../item-collection/non-generic-drop-glue.rs | 8 +- .../item-collection/non-generic-functions.rs | 2 - .../item-collection/overloaded-operators.rs | 2 - .../item-collection/static-init.rs | 1 - .../item-collection/statics-and-consts.rs | 2 - .../item-collection/trait-implementations.rs | 2 - .../trait-method-as-argument.rs | 2 - .../trait-method-default-impl.rs | 2 - .../item-collection/transitive-drop-glue.rs | 21 +- .../item-collection/tuple-drop-glue.rs | 9 +- .../codegen-units/item-collection/unsizing.rs | 8 +- .../unused-traits-and-generics.rs | 1 - .../partitioning/extern-drop-glue.rs | 7 +- .../partitioning/extern-generic.rs | 2 - .../partitioning/local-drop-glue.rs | 9 +- .../partitioning/regular-modules.rs | 2 - .../codegen-units/partitioning/statics.rs | 2 - .../partitioning/vtable-through-const.rs | 2 +- 53 files changed, 721 insertions(+), 1141 deletions(-) delete mode 100644 src/librustc_trans/cleanup.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 12410c08f399b..e74e13d1a6b05 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -46,8 +46,13 @@ issue = "0")] #![allow(missing_docs)] -extern "rust-intrinsic" { +#[cfg(not(stage0))] +#[stable(feature = "drop_in_place", since = "1.8.0")] +#[rustc_deprecated(reason = "no longer an intrinsic - use `ptr::drop_in_place` directly", + since = "1.18.0")] +pub use ptr::drop_in_place; +extern "rust-intrinsic" { // NB: These intrinsics take raw pointers because they mutate aliased // memory, which is not valid for either `&` or `&mut`. @@ -622,6 +627,7 @@ extern "rust-intrinsic" { pub fn size_of_val(_: &T) -> usize; pub fn min_align_of_val(_: &T) -> usize; + #[cfg(stage0)] /// Executes the destructor (if any) of the pointed-to value. /// /// This has two use cases: diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 0b7aa4fa9117c..c4409d3cb5590 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -37,9 +37,38 @@ pub use intrinsics::copy; #[stable(feature = "rust1", since = "1.0.0")] pub use intrinsics::write_bytes; +#[cfg(stage0)] #[stable(feature = "drop_in_place", since = "1.8.0")] pub use intrinsics::drop_in_place; +#[cfg(not(stage0))] +/// Executes the destructor (if any) of the pointed-to value. +/// +/// This has two use cases: +/// +/// * It is *required* to use `drop_in_place` to drop unsized types like +/// trait objects, because they can't be read out onto the stack and +/// dropped normally. +/// +/// * It is friendlier to the optimizer to do this over `ptr::read` when +/// dropping manually allocated memory (e.g. when writing Box/Rc/Vec), +/// as the compiler doesn't need to prove that it's sound to elide the +/// copy. +/// +/// # Undefined Behavior +/// +/// This has all the same safety problems as `ptr::read` with respect to +/// invalid pointers, types, and double drops. +#[stable(feature = "drop_in_place", since = "1.8.0")] +#[lang="drop_in_place"] +#[inline] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(to_drop: *mut T) { + // Code here does not matter - this is replaced by the + // real drop glue by the compiler. + drop_in_place(to_drop); +} + /// Creates a null raw pointer. /// /// # Examples diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index b9f1611f62baf..81a415a2f5309 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -335,7 +335,7 @@ language_item_table! { ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn; BoxFreeFnLangItem, "box_free", box_free_fn; - StrDupUniqFnLangItem, "strdup_uniq", strdup_uniq_fn; + DropInPlaceFnLangItem, "drop_in_place", drop_in_place_fn; StartFnLangItem, "start", start_fn; @@ -355,8 +355,6 @@ language_item_table! { ContravariantLifetimeItem, "contravariant_lifetime", contravariant_lifetime; InvariantLifetimeItem, "invariant_lifetime", invariant_lifetime; - NoCopyItem, "no_copy_bound", no_copy_bound; - NonZeroItem, "non_zero", non_zero; DebugTraitLangItem, "debug_trait", debug_trait; diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index fea576f706780..33df648f172d6 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -17,7 +17,7 @@ use rustc_data_structures::control_flow_graph::{GraphPredecessors, GraphSuccesso use rustc_data_structures::control_flow_graph::ControlFlowGraph; use hir::def::CtorKind; use hir::def_id::DefId; -use ty::subst::Substs; +use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, ClosureSubsts, Region, Ty}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use util::ppaux; @@ -982,6 +982,22 @@ impl<'tcx> Debug for Operand<'tcx> { } } +impl<'tcx> Operand<'tcx> { + pub fn item<'a>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + span: Span) + -> Self + { + Operand::Constant(Constant { + span: span, + ty: tcx.item_type(def_id).subst(tcx, substs), + literal: Literal::Item { def_id, substs } + }) + } + +} + /////////////////////////////////////////////////////////////////////////// /// Rvalues diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index aeb1fe079ff25..67287f1b4ff72 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -35,6 +35,8 @@ pub enum InstanceDef<'tcx> { Virtual(DefId, usize), // <[mut closure] as FnOnce>::call_once ClosureOnceShim { call_once: DefId }, + // drop_in_place::; None for empty drop glue. + DropGlue(DefId, Option>), } impl<'tcx> InstanceDef<'tcx> { @@ -46,7 +48,8 @@ impl<'tcx> InstanceDef<'tcx> { InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id, ) | InstanceDef::ClosureOnceShim { call_once: def_id } - => def_id + => def_id, + InstanceDef::DropGlue(def_id, _) => def_id } } @@ -65,6 +68,7 @@ impl<'tcx> InstanceDef<'tcx> { // real on-demand. let ty = match self { &InstanceDef::FnPtrShim(_, ty) => Some(ty), + &InstanceDef::DropGlue(_, ty) => ty, _ => None }.into_iter(); @@ -97,6 +101,9 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::ClosureOnceShim { .. } => { write!(f, " - shim") } + InstanceDef::DropGlue(_, ty) => { + write!(f, " - shim({:?})", ty) + } } } } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 8d866676dbd18..81037fe40d9da 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -437,7 +437,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } Rvalue::Ref(..) | Rvalue::Discriminant(..) | - Rvalue::Len(..) => {} + Rvalue::Len(..) | Rvalue::Box(..) => { // This returns an rvalue with uninitialized contents. We can't // move out of it here because it is an rvalue - assignments always diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 2d9ef124ad244..26d5b7fd38ab0 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -15,7 +15,7 @@ use rustc::middle::region::ROOT_CODE_EXTENT; use rustc::mir::*; use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty}; -use rustc::ty::subst::Subst; +use rustc::ty::subst::{Kind, Subst}; use rustc::ty::maps::Providers; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -25,10 +25,13 @@ use syntax::ast; use syntax_pos::Span; use std::cell::RefCell; +use std::fmt; use std::iter; use std::mem; use transform::{add_call_guards, no_landing_pads, simplify}; +use util::elaborate_drops::{self, DropElaborator, DropStyle, DropFlagMode}; +use util::patch::MirPatch; pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; @@ -101,6 +104,9 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, None ) } + ty::InstanceDef::DropGlue(def_id, ty) => { + build_drop_shim(tcx, ¶m_env, def_id, ty) + } ty::InstanceDef::Intrinsic(_) => { bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } @@ -143,6 +149,129 @@ fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) .collect() } +fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + def_id: DefId, + ty: Option>) + -> Mir<'tcx> +{ + debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); + + let substs = if let Some(ty) = ty { + tcx.mk_substs(iter::once(Kind::from(ty))) + } else { + param_env.free_substs + }; + let fn_ty = tcx.item_type(def_id).subst(tcx, substs); + let sig = tcx.erase_late_bound_regions(&fn_ty.fn_sig()); + let span = tcx.def_span(def_id); + + let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; + + let return_block = BasicBlock::new(1); + let mut blocks = IndexVec::new(); + let block = |blocks: &mut IndexVec<_, _>, kind| { + blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { source_info, kind }), + is_cleanup: false + }) + }; + block(&mut blocks, TerminatorKind::Goto { target: return_block }); + block(&mut blocks, TerminatorKind::Return); + + let mut mir = Mir::new( + blocks, + IndexVec::from_elem_n( + VisibilityScopeData { span: span, parent_scope: None }, 1 + ), + IndexVec::new(), + sig.output(), + local_decls_for_sig(&sig), + sig.inputs().len(), + vec![], + span + ); + + if let Some(..) = ty { + let patch = { + let mut elaborator = DropShimElaborator { + mir: &mir, + patch: MirPatch::new(&mir), + tcx, param_env + }; + let dropee = Lvalue::Projection( + box Projection { + base: Lvalue::Local(Local::new(1+0)), + elem: ProjectionElem::Deref + } + ); + let resume_block = elaborator.patch.resume_block(); + elaborate_drops::elaborate_drop( + &mut elaborator, + source_info, + false, + &dropee, + (), + return_block, + Some(resume_block), + START_BLOCK + ); + elaborator.patch + }; + patch.apply(&mut mir); + } + + mir +} + +pub struct DropShimElaborator<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, + patch: MirPatch<'tcx>, + tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + param_env: &'a ty::ParameterEnvironment<'tcx>, +} + +impl<'a, 'tcx> fmt::Debug for DropShimElaborator<'a, 'tcx> { + fn fmt(&self, _f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + Ok(()) + } +} + +impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { + type Path = (); + + fn patch(&mut self) -> &mut MirPatch<'tcx> { &mut self.patch } + fn mir(&self) -> &'a Mir<'tcx> { self.mir } + fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> { self.tcx } + fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx> { self.param_env } + + fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle { + if let DropFlagMode::Shallow = mode { + DropStyle::Static + } else { + DropStyle::Open + } + } + + fn get_drop_flag(&mut self, _path: Self::Path) -> Option> { + None + } + + fn clear_drop_flag(&mut self, _location: Location, _path: Self::Path, _mode: DropFlagMode) { + } + + fn field_subpath(&self, _path: Self::Path, _field: Field) -> Option { + None + } + fn deref_subpath(&self, _path: Self::Path) -> Option { + None + } + fn downcast_subpath(&self, _path: Self::Path, _variant: usize) -> Option { + Some(()) + } +} + /// Build a "call" shim for `def_id`. The shim calls the /// function specified by `call_kind`, first adjusting its first /// argument according to `rcvr_adjustment`. @@ -162,7 +291,6 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, def_id, rcvr_adjustment, call_kind, untuple_args); let fn_ty = tcx.item_type(def_id).subst(tcx, param_env.free_substs); - // Not normalizing here without a param env. let sig = tcx.erase_late_bound_regions(&fn_ty.fn_sig()); let span = tcx.def_span(def_id); diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index ce6debab9a4b8..d0f142ad7d7a8 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -9,10 +9,12 @@ // except according to those terms. use std::fmt; +use rustc::hir; use rustc::mir::*; +use rustc::middle::const_val::ConstInt; use rustc::middle::lang_items; use rustc::ty::{self, Ty}; -use rustc::ty::subst::{Kind, Subst, Substs}; +use rustc::ty::subst::{Kind, Substs}; use rustc::ty::util::IntTypeExt; use rustc_data_structures::indexed_vec::Idx; use util::patch::MirPatch; @@ -92,6 +94,7 @@ pub fn elaborate_drop<'b, 'tcx, D>( bb: BasicBlock) where D: DropElaborator<'b, 'tcx> { + assert_eq!(unwind.is_none(), is_cleanup); DropCtxt { elaborator, source_info, is_cleanup, lvalue, path, succ, unwind }.elaborate_drop(bb) @@ -146,7 +149,10 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> }); } DropStyle::Conditional => { - let drop_bb = self.complete_drop(Some(DropFlagMode::Deep)); + let is_cleanup = self.is_cleanup; // FIXME(#6393) + let succ = self.succ; + let drop_bb = self.complete_drop( + is_cleanup, Some(DropFlagMode::Deep), succ); self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); @@ -208,7 +214,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> // Using `self.path` here to condition the drop on // our own drop flag. path: self.path - }.complete_drop(None) + }.complete_drop(is_cleanup, None, succ) } } @@ -220,7 +226,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// (the first field is never reached). If it is `None`, all /// unwind targets are left blank. fn drop_halfladder<'a>(&mut self, - unwind_ladder: Option>, + unwind_ladder: Option<&[BasicBlock]>, succ: BasicBlock, fields: &[(Lvalue<'tcx>, Option)], is_cleanup: bool) @@ -262,10 +268,10 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// .c1: /// ELAB(drop location.1 [target=.c2]) /// .c2: - /// ELAB(drop location.2 [target=`self.unwind]) + /// ELAB(drop location.2 [target=`self.unwind`]) fn drop_ladder<'a>(&mut self, fields: Vec<(Lvalue<'tcx>, Option)>) - -> BasicBlock + -> (BasicBlock, Option) { debug!("drop_ladder({:?}, {:?})", self, fields); @@ -286,8 +292,12 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let succ = self.succ; // FIXME(#6393) let is_cleanup = self.is_cleanup; - self.drop_halfladder(unwind_ladder, succ, &fields, is_cleanup) - .last().cloned().unwrap_or(succ) + let normal_ladder = + self.drop_halfladder(unwind_ladder.as_ref().map(|x| &**x), + succ, &fields, is_cleanup); + + (normal_ladder.last().cloned().unwrap_or(succ), + unwind_ladder.and_then(|l| l.last().cloned()).or(self.unwind)) } fn open_drop_for_tuple<'a>(&mut self, tys: &[Ty<'tcx>]) @@ -300,7 +310,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.elaborator.field_subpath(self.path, Field::new(i))) }).collect(); - self.drop_ladder(fields) + self.drop_ladder(fields).0 } fn open_drop_for_box<'a>(&mut self, ty: Ty<'tcx>) -> BasicBlock @@ -323,7 +333,33 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn open_drop_for_adt<'a>(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>) -> BasicBlock { debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs); + if adt.variants.len() == 0 { + return self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Unreachable + }), + is_cleanup: self.is_cleanup + }); + } + + let contents_drop = if adt.is_union() { + (self.succ, self.unwind) + } else { + self.open_drop_for_adt_contents(adt, substs) + }; + if adt.has_dtor(self.tcx()) { + self.destructor_call_block(contents_drop) + } else { + contents_drop.0 + } + } + + fn open_drop_for_adt_contents<'a>(&mut self, adt: &'tcx ty::AdtDef, + substs: &'tcx Substs<'tcx>) + -> (BasicBlock, Option) { match adt.variants.len() { 1 => { let fields = self.move_paths_for_fields( @@ -335,9 +371,19 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.drop_ladder(fields) } _ => { + let is_cleanup = self.is_cleanup; + let succ = self.succ; + let unwind = self.unwind; // FIXME(#6393) + let mut values = Vec::with_capacity(adt.variants.len()); - let mut blocks = Vec::with_capacity(adt.variants.len()); + let mut normal_blocks = Vec::with_capacity(adt.variants.len()); + let mut unwind_blocks = if is_cleanup { + None + } else { + Some(Vec::with_capacity(adt.variants.len())) + }; let mut otherwise = None; + let mut unwind_otherwise = None; for (variant_index, discr) in adt.discriminants(self.tcx()).enumerate() { let subpath = self.elaborator.downcast_subpath( self.path, variant_index); @@ -351,53 +397,146 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> &adt.variants[variant_index], substs); values.push(discr); - blocks.push(self.drop_ladder(fields)); + if let Some(ref mut unwind_blocks) = unwind_blocks { + // We can't use the half-ladder from the original + // drop ladder, because this breaks the + // "funclet can't have 2 successor funclets" + // requirement from MSVC: + // + // switch unwind-switch + // / \ / \ + // v1.0 v2.0 v2.0-unwind v1.0-unwind + // | | / | + // v1.1-unwind v2.1-unwind | + // ^ | + // \-------------------------------/ + // + // Create a duplicate half-ladder to avoid that. We + // could technically only do this on MSVC, but I + // I want to minimize the divergence between MSVC + // and non-MSVC. + + let unwind = unwind.unwrap(); + let halfladder = self.drop_halfladder( + None, unwind, &fields, true); + unwind_blocks.push( + halfladder.last().cloned().unwrap_or(unwind) + ); + } + let (normal, _) = self.drop_ladder(fields); + normal_blocks.push(normal); } else { // variant not found - drop the entire enum if let None = otherwise { - otherwise = - Some(self.complete_drop(Some(DropFlagMode::Shallow))); + otherwise = Some(self.complete_drop( + is_cleanup, + Some(DropFlagMode::Shallow), + succ)); + unwind_otherwise = unwind.map(|unwind| self.complete_drop( + true, + Some(DropFlagMode::Shallow), + unwind + )); } } } if let Some(block) = otherwise { - blocks.push(block); + normal_blocks.push(block); + if let Some(ref mut unwind_blocks) = unwind_blocks { + unwind_blocks.push(unwind_otherwise.unwrap()); + } } else { values.pop(); } - // If there are multiple variants, then if something - // is present within the enum the discriminant, tracked - // by the rest path, must be initialized. - // - // Additionally, we do not want to switch on the - // discriminant after it is free-ed, because that - // way lies only trouble. - let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); - let discr = Lvalue::Local(self.new_temp(discr_ty)); - let discr_rv = Rvalue::Discriminant(self.lvalue.clone()); - let switch_block = self.elaborator.patch().new_block(BasicBlockData { - statements: vec![ - Statement { - source_info: self.source_info, - kind: StatementKind::Assign(discr.clone(), discr_rv), - } - ], - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::SwitchInt { - discr: Operand::Consume(discr), - switch_ty: discr_ty, - values: From::from(values), - targets: blocks, - } - }), - is_cleanup: self.is_cleanup, - }); - self.drop_flag_test_block(switch_block) + + (self.adt_switch_block(is_cleanup, adt, normal_blocks, &values, succ), + unwind_blocks.map(|unwind_blocks| { + self.adt_switch_block( + is_cleanup, adt, unwind_blocks, &values, unwind.unwrap() + ) + })) } } } + fn adt_switch_block(&mut self, + is_cleanup: bool, + adt: &'tcx ty::AdtDef, + blocks: Vec, + values: &[ConstInt], + succ: BasicBlock) + -> BasicBlock { + // If there are multiple variants, then if something + // is present within the enum the discriminant, tracked + // by the rest path, must be initialized. + // + // Additionally, we do not want to switch on the + // discriminant after it is free-ed, because that + // way lies only trouble. + let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); + let discr = Lvalue::Local(self.new_temp(discr_ty)); + let discr_rv = Rvalue::Discriminant(self.lvalue.clone()); + let switch_block = self.elaborator.patch().new_block(BasicBlockData { + statements: vec![ + Statement { + source_info: self.source_info, + kind: StatementKind::Assign(discr.clone(), discr_rv), + } + ], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Consume(discr), + switch_ty: discr_ty, + values: From::from(values.to_owned()), + targets: blocks, + } + }), + is_cleanup: is_cleanup, + }); + self.drop_flag_test_block(is_cleanup, switch_block, succ) + } + + fn destructor_call_block<'a>(&mut self, (succ, unwind): (BasicBlock, Option)) + -> BasicBlock + { + debug!("destructor_call_block({:?}, {:?})", self, succ); + let tcx = self.tcx(); + let drop_trait = tcx.lang_items.drop_trait().unwrap(); + let drop_fn = tcx.associated_items(drop_trait).next().unwrap(); + let ty = self.lvalue_ty(self.lvalue); + let substs = tcx.mk_substs(iter::once(Kind::from(ty))); + + let re_erased = tcx.mk_region(ty::ReErased); + let ref_ty = tcx.mk_ref(re_erased, ty::TypeAndMut { + ty: ty, + mutbl: hir::Mutability::MutMutable + }); + let ref_lvalue = self.new_temp(ref_ty); + let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); + + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![Statement { + source_info: self.source_info, + kind: StatementKind::Assign( + Lvalue::Local(ref_lvalue), + Rvalue::Ref(re_erased, BorrowKind::Mut, self.lvalue.clone()) + ) + }], + terminator: Some(Terminator { + kind: TerminatorKind::Call { + func: Operand::item(tcx, drop_fn.def_id, substs, + self.source_info.span), + args: vec![Operand::Consume(Lvalue::Local(ref_lvalue))], + destination: Some((unit_temp, succ)), + cleanup: unwind, + }, + source_info: self.source_info + }), + is_cleanup: self.is_cleanup, + }) + } + /// The slow-path - create an "open", elaborated drop for a type /// which is moved-out-of only partially, and patch `bb` to a jump /// to it. This must not be called on ADTs with a destructor, @@ -408,6 +547,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// ADT, both in the success case or if one of the destructors fail. fn open_drop<'a>(&mut self) -> BasicBlock { let ty = self.lvalue_ty(self.lvalue); + let is_cleanup = self.is_cleanup; // FIXME(#6393) + let succ = self.succ; match ty.sty { ty::TyClosure(def_id, substs) => { let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect(); @@ -422,6 +563,14 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> ty::TyAdt(def, substs) => { self.open_drop_for_adt(def, substs) } + ty::TyDynamic(..) => { + self.complete_drop(is_cleanup, Some(DropFlagMode::Deep), succ) + } + ty::TyArray(..) | ty::TySlice(..) => { + // FIXME(#34708): handle partially-dropped + // array/slice elements. + self.complete_drop(is_cleanup, Some(DropFlagMode::Deep), succ) + } _ => bug!("open drop from non-ADT `{:?}`", ty) } } @@ -433,22 +582,27 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// if FLAG(self.path) /// if let Some(mode) = mode: FLAG(self.path)[mode] = false /// drop(self.lv) - fn complete_drop<'a>(&mut self, drop_mode: Option) -> BasicBlock + fn complete_drop<'a>(&mut self, + is_cleanup: bool, + drop_mode: Option, + succ: BasicBlock) -> BasicBlock { debug!("complete_drop({:?},{:?})", self, drop_mode); - let drop_block = self.drop_block(); + let drop_block = self.drop_block(is_cleanup, succ); if let Some(mode) = drop_mode { let block_start = Location { block: drop_block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, mode); } - self.drop_flag_test_block(drop_block) + self.drop_flag_test_block(is_cleanup, drop_block, succ) } fn elaborated_drop_block<'a>(&mut self) -> BasicBlock { debug!("elaborated_drop_block({:?})", self); - let blk = self.drop_block(); + let is_cleanup = self.is_cleanup; // FIXME(#6393) + let succ = self.succ; + let blk = self.drop_block(is_cleanup, succ); self.elaborate_drop(blk); blk } @@ -460,7 +614,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> is_cleanup: bool ) -> BasicBlock { let block = self.unelaborated_free_block(ty, target, is_cleanup); - self.drop_flag_test_block_with_succ(is_cleanup, block, target) + self.drop_flag_test_block(is_cleanup, block, target) } fn unelaborated_free_block<'a>( @@ -473,53 +627,34 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); let substs = tcx.mk_substs(iter::once(Kind::from(ty))); - let fty = tcx.item_type(free_func).subst(tcx, substs); - let free_block = self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: self.source_info, kind: TerminatorKind::Call { - func: Operand::Constant(Constant { - span: self.source_info.span, - ty: fty, - literal: Literal::Item { - def_id: free_func, - substs: substs - } - }), - args: vec![Operand::Consume(self.lvalue.clone())], - destination: Some((unit_temp, target)), - cleanup: None - } - }), - is_cleanup: is_cleanup - }); + let call = TerminatorKind::Call { + func: Operand::item(tcx, free_func, substs, self.source_info.span), + args: vec![Operand::Consume(self.lvalue.clone())], + destination: Some((unit_temp, target)), + cleanup: None + }; // FIXME(#6393) + let free_block = self.new_block(is_cleanup, call); + let block_start = Location { block: free_block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); free_block } - fn drop_block<'a>(&mut self) -> BasicBlock { + fn drop_block<'a>(&mut self, is_cleanup: bool, succ: BasicBlock) -> BasicBlock { let block = TerminatorKind::Drop { location: self.lvalue.clone(), - target: self.succ, - unwind: self.unwind + target: succ, + unwind: if is_cleanup { None } else { self.unwind } }; - let is_cleanup = self.is_cleanup; // FIXME(#6393) self.new_block(is_cleanup, block) } - fn drop_flag_test_block<'a>(&mut self, on_set: BasicBlock) -> BasicBlock { - let is_cleanup = self.is_cleanup; - let succ = self.succ; // FIXME(#6393) - self.drop_flag_test_block_with_succ(is_cleanup, on_set, succ) - } - - fn drop_flag_test_block_with_succ<'a>(&mut self, - is_cleanup: bool, - on_set: BasicBlock, - on_unset: BasicBlock) - -> BasicBlock + fn drop_flag_test_block(&mut self, + is_cleanup: bool, + on_set: BasicBlock, + on_unset: BasicBlock) + -> BasicBlock { let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}", diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 710aa1fbbc6d9..27a19d211c290 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -11,7 +11,7 @@ use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace}; use base; use builder::Builder; -use common::{self, type_is_fat_ptr, C_uint}; +use common::{type_is_fat_ptr, C_uint}; use context::CrateContext; use cabi_x86; use cabi_x86_64; @@ -59,6 +59,7 @@ enum ArgKind { pub use self::attr_impl::ArgAttribute; #[allow(non_upper_case_globals)] +#[allow(unused)] mod attr_impl { // The subset of llvm::Attribute needed for arguments, packed into a bitfield. bitflags! { @@ -223,16 +224,6 @@ impl ArgType { self.kind == ArgKind::Ignore } - /// Get the LLVM type for an lvalue of the original Rust type of - /// this argument/return, i.e. the result of `type_of::type_of`. - pub fn memory_ty(&self, ccx: &CrateContext) -> Type { - if self.original_ty == Type::i1(ccx) { - Type::i8(ccx) - } else { - self.original_ty - } - } - /// Store a direct/indirect value described by this ArgType into a /// lvalue for the original Rust type of this argument/return. /// Can be used for both storing formal arguments into Rust variables @@ -344,17 +335,6 @@ impl FnType { fn_ty } - pub fn from_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - instance: &ty::Instance<'tcx>, - extra_args: &[Ty<'tcx>]) -> FnType - { - let ity = common::instance_ty(ccx.shared(), instance); - let sig = common::ty_fn_sig(ccx, ity); - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); - - Self::new(ccx, sig, extra_args) - } - fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> FnType { diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 11d3fae823830..058f37f62dd82 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -363,28 +363,6 @@ fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef, } } -/// Yield information about how to dispatch a case of the -/// discriminant-like value returned by `trans_switch`. -/// -/// This should ideally be less tightly tied to `_match`. -pub fn trans_case<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -> ValueRef { - let l = bcx.ccx.layout_of(t); - match *l { - layout::CEnum { discr, .. } - | layout::General { discr, .. }=> { - C_integral(Type::from_integer(bcx.ccx, discr), value.0, true) - } - layout::RawNullablePointer { .. } | - layout::StructWrappedNullablePointer { .. } => { - assert!(value == Disr(0) || value == Disr(1)); - C_bool(bcx.ccx, value != Disr(0)) - } - _ => { - bug!("{} does not have a discriminant. Represented as {:#?}", t, l); - } - } -} - /// Set the discriminant for a new value of the given case of the given /// representation. pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr) { diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs index efdd1b736f0e7..6bef31ccf64a4 100644 --- a/src/librustc_trans/attributes.rs +++ b/src/librustc_trans/attributes.rs @@ -17,6 +17,7 @@ pub use syntax::attr::InlineAttr; use syntax::ast; use context::CrateContext; + /// Mark LLVM function to use provided inline heuristic. #[inline] pub fn inline(val: ValueRef, inline: InlineAttr) { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index a58764878a318..6593b8e68d425 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -403,7 +403,9 @@ pub fn load_ty<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, // a char is a Unicode codepoint, and so takes values from 0 // to 0x10FFFF inclusive only. b.load_range_assert(ptr, 0, 0x10FFFF + 1, llvm::False, alignment.to_align()) - } else if (t.is_region_ptr() || t.is_box()) && !common::type_is_fat_ptr(ccx, t) { + } else if (t.is_region_ptr() || t.is_box() || t.is_fn()) + && !common::type_is_fat_ptr(ccx, t) + { b.load_nonnull(ptr, alignment.to_align()) } else { b.load(ptr, alignment.to_align()) diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 4d0bd9fa201d8..aefee51191ac4 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -96,6 +96,9 @@ pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, assert_eq!(common::val_ty(llfn), llptrty); debug!("get_fn: not casting pointer!"); + if common::is_inline_instance(tcx, &instance) { + attributes::inline(llfn, attributes::InlineAttr::Hint); + } let attrs = instance.def.attrs(ccx.tcx()); attributes::from_fn_attrs(ccx, &attrs, llfn); diff --git a/src/librustc_trans/cleanup.rs b/src/librustc_trans/cleanup.rs deleted file mode 100644 index 2b2e5e85ea50d..0000000000000 --- a/src/librustc_trans/cleanup.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2013-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. - -//! ## The Cleanup module -//! -//! The cleanup module tracks what values need to be cleaned up as scopes -//! are exited, either via panic or just normal control flow. -//! -//! Cleanup items can be scheduled into any of the scopes on the stack. -//! Typically, when a scope is finished, we generate the cleanup code. This -//! corresponds to a normal exit from a block (for example, an expression -//! completing evaluation successfully without panic). - -use llvm::BasicBlockRef; -use base; -use mir::lvalue::LvalueRef; -use rustc::mir::tcx::LvalueTy; -use builder::Builder; -use common::Funclet; -use glue; -use type_::Type; - -pub struct CleanupScope<'tcx> { - // Cleanup to run upon scope exit. - cleanup: Option>, - - // Computed on creation if compiling with landing pads (!sess.no_landing_pads) - pub landing_pad: Option, -} - -#[derive(Copy, Clone)] -pub struct DropValue<'tcx> { - val: LvalueRef<'tcx>, - skip_dtor: bool, -} - -impl<'tcx> DropValue<'tcx> { - fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &Builder<'a, 'tcx>) { - glue::call_drop_glue(bcx, self.val, self.skip_dtor, funclet) - } - - /// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary - /// for an unwind and then `resume` to continue error propagation: - /// - /// landing_pad -> ... cleanups ... -> [resume] - /// - /// This should only be called once per function, as it creates an alloca for the landingpad. - fn get_landing_pad<'a>(&self, bcx: &Builder<'a, 'tcx>) -> BasicBlockRef { - debug!("get_landing_pad"); - let bcx = bcx.build_sibling_block("cleanup_unwind"); - let llpersonality = bcx.ccx.eh_personality(); - bcx.set_personality_fn(llpersonality); - - if base::wants_msvc_seh(bcx.sess()) { - let pad = bcx.cleanup_pad(None, &[]); - let funclet = Some(Funclet::new(pad)); - self.trans(funclet.as_ref(), &bcx); - - bcx.cleanup_ret(pad, None); - } else { - // The landing pad return type (the type being propagated). Not sure - // what this represents but it's determined by the personality - // function and this is what the EH proposal example uses. - let llretty = Type::struct_(bcx.ccx, &[Type::i8p(bcx.ccx), Type::i32(bcx.ccx)], false); - - // The only landing pad clause will be 'cleanup' - let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.llfn()); - - // The landing pad block is a cleanup - bcx.set_cleanup(llretval); - - // Insert cleanup instructions into the cleanup block - self.trans(None, &bcx); - - if !bcx.sess().target.target.options.custom_unwind_resume { - bcx.resume(llretval); - } else { - let exc_ptr = bcx.extract_value(llretval, 0); - bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], None); - bcx.unreachable(); - } - } - - bcx.llbb() - } -} - -impl<'a, 'tcx> CleanupScope<'tcx> { - /// Issue #23611: Schedules a (deep) drop of the contents of - /// `val`, which is a pointer to an instance of struct/enum type - /// `ty`. The scheduled code handles extracting the discriminant - /// and dropping the contents associated with that variant - /// *without* executing any associated drop implementation. - pub fn schedule_drop_adt_contents( - bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx> - ) -> CleanupScope<'tcx> { - if let LvalueTy::Downcast { .. } = val.ty { - bug!("Cannot drop downcast ty yet"); - } - // `if` below could be "!contents_needs_drop"; skipping drop - // is just an optimization, so sound to be conservative. - if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) { - return CleanupScope::noop(); - } - - let drop = DropValue { - val: val, - skip_dtor: true, - }; - - CleanupScope::new(bcx, drop) - } - - fn new(bcx: &Builder<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> { - CleanupScope { - cleanup: Some(drop_val), - landing_pad: if !bcx.sess().no_landing_pads() { - Some(drop_val.get_landing_pad(bcx)) - } else { - None - }, - } - } - - pub fn noop() -> CleanupScope<'tcx> { - CleanupScope { - cleanup: None, - landing_pad: None, - } - } - - pub fn trans(self, bcx: &'a Builder<'a, 'tcx>) { - if let Some(cleanup) = self.cleanup { - cleanup.trans(None, &bcx); - } - } -} diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index f076fc4710222..500802a4135d0 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -193,25 +193,21 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; -use rustc::middle::lang_items::{BoxFreeFnLangItem, ExchangeMallocFnLangItem}; +use rustc::middle::lang_items::{ExchangeMallocFnLangItem}; use rustc::traits; -use rustc::ty::subst::{Kind, Substs, Subst}; +use rustc::ty::subst::{Substs, Subst}; use rustc::ty::{self, TypeFoldable, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::mir::{self, Location}; -use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor as MirVisitor; use context::SharedCrateContext; use common::{def_ty, instance_ty}; -use glue::{self, DropGlueKind}; use monomorphize::{self, Instance}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; use trans_item::{TransItem, DefPathBasedNames, InstantiationMode}; -use std::iter; - #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub enum TransItemCollectionMode { Eager, @@ -327,10 +323,6 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, let recursion_depth_reset; match starting_point { - TransItem::DropGlue(t) => { - find_drop_glue_neighbors(scx, t, &mut neighbors); - recursion_depth_reset = None; - } TransItem::Static(node_id) => { let def_id = scx.tcx().hir.local_def_id(node_id); let instance = Instance::mono(scx.tcx(), def_id); @@ -339,8 +331,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, debug_assert!(should_trans_locally(scx.tcx(), &instance)); let ty = instance_ty(scx, &instance); - let ty = glue::get_drop_glue_type(scx, ty); - neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); + visit_drop_use(scx, ty, true, &mut neighbors); recursion_depth_reset = None; @@ -396,6 +387,14 @@ fn check_recursion_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0); debug!(" => recursion depth={}", recursion_depth); + let recursion_depth = if Some(def_id) == tcx.lang_items.drop_in_place_fn() { + // HACK: drop_in_place creates tight monomorphization loops. Give + // it more margin. + recursion_depth / 4 + } else { + recursion_depth + }; + // Code that needs to instantiate the same function recursively // more than the recursion limit is assumed to be causing an // infinite expansion. @@ -521,27 +520,6 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.super_rvalue(rvalue, location); } - fn visit_lvalue(&mut self, - lvalue: &mir::Lvalue<'tcx>, - context: mir_visit::LvalueContext<'tcx>, - location: Location) { - debug!("visiting lvalue {:?}", *lvalue); - - if let mir_visit::LvalueContext::Drop = context { - let ty = lvalue.ty(self.mir, self.scx.tcx()) - .to_ty(self.scx.tcx()); - - let ty = monomorphize::apply_param_substs(self.scx, - self.param_substs, - &ty); - assert!(ty.is_normalized_for_trans()); - let ty = glue::get_drop_glue_type(self.scx, ty); - self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); - } - - self.super_lvalue(lvalue, context, location); - } - fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) { debug!("visiting constant {:?} @ {:?}", *constant, location); @@ -568,54 +546,90 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { kind: &mir::TerminatorKind<'tcx>, location: Location) { let tcx = self.scx.tcx(); - if let mir::TerminatorKind::Call { - ref func, - .. - } = *kind { - let callee_ty = func.ty(self.mir, tcx); - let callee_ty = monomorphize::apply_param_substs( - self.scx, self.param_substs, &callee_ty); - visit_fn_use(self.scx, callee_ty, true, &mut self.output); + match *kind { + mir::TerminatorKind::Call { ref func, .. } => { + let callee_ty = func.ty(self.mir, tcx); + let callee_ty = monomorphize::apply_param_substs( + self.scx, self.param_substs, &callee_ty); + visit_fn_use(self.scx, callee_ty, true, &mut self.output); + } + mir::TerminatorKind::Drop { ref location, .. } | + mir::TerminatorKind::DropAndReplace { ref location, .. } => { + let ty = location.ty(self.mir, self.scx.tcx()) + .to_ty(self.scx.tcx()); + let ty = monomorphize::apply_param_substs(self.scx, + self.param_substs, + &ty); + visit_drop_use(self.scx, ty, true, self.output); + } + mir::TerminatorKind::Goto { .. } | + mir::TerminatorKind::SwitchInt { .. } | + mir::TerminatorKind::Resume | + mir::TerminatorKind::Return | + mir::TerminatorKind::Unreachable | + mir::TerminatorKind::Assert { .. } => {} } self.super_terminator_kind(block, kind, location); } } +fn visit_drop_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>, + is_direct_call: bool, + output: &mut Vec>) +{ + let instance = monomorphize::resolve_drop_in_place(scx, ty); + visit_instance_use(scx, instance, is_direct_call, output); +} + fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, is_direct_call: bool, output: &mut Vec>) { - debug!("visit_fn_use({:?}, is_direct_call={:?})", ty, is_direct_call); - let (def_id, substs) = match ty.sty { - ty::TyFnDef(def_id, substs, _) => (def_id, substs), - _ => return - }; + if let ty::TyFnDef(def_id, substs, _) = ty.sty { + let instance = monomorphize::resolve(scx, def_id, substs); + visit_instance_use(scx, instance, is_direct_call, output); + } +} - let instance = monomorphize::resolve(scx, def_id, substs); +fn visit_instance_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + instance: ty::Instance<'tcx>, + is_direct_call: bool, + output: &mut Vec>) +{ + debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); if !should_trans_locally(scx.tcx(), &instance) { return } match instance.def { - ty::InstanceDef::Intrinsic(..) => { + ty::InstanceDef::Intrinsic(def_id) => { if !is_direct_call { - bug!("intrinsic {:?} being reified", ty); - } - if scx.tcx().item_name(def_id) == "drop_in_place" { - // drop_in_place is a call to drop glue, need to instantiate - // that. - let ty = glue::get_drop_glue_type(scx, substs.type_at(0)); - output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); + bug!("intrinsic {:?} being reified", def_id); } } - ty::InstanceDef::Virtual(..) => { + ty::InstanceDef::Virtual(..) | + ty::InstanceDef::DropGlue(_, None) => { // don't need to emit shim if we are calling directly. if !is_direct_call { output.push(create_fn_trans_item(instance)); } } + ty::InstanceDef::DropGlue(_, Some(ty)) => { + match ty.sty { + ty::TyArray(ety, _) | + ty::TySlice(ety) + if is_direct_call => + { + // drop of arrays/slices is translated in-line. + visit_drop_use(scx, ety, false, output); + } + _ => {} + }; + output.push(create_fn_trans_item(instance)); + } ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) => { @@ -634,6 +648,7 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Virtual(..) | ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Intrinsic(_) => return true }; match tcx.hir.get_if_local(def_id) { @@ -658,124 +673,6 @@ fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instan } } -fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - dg: DropGlueKind<'tcx>, - output: &mut Vec>) { - let ty = match dg { - DropGlueKind::Ty(ty) => ty, - DropGlueKind::TyContents(_) => { - // We already collected the neighbors of this item via the - // DropGlueKind::Ty variant. - return - } - }; - - debug!("find_drop_glue_neighbors: {}", type_to_string(scx.tcx(), ty)); - - // Make sure the BoxFreeFn lang-item gets translated if there is a boxed value. - if ty.is_box() { - let tcx = scx.tcx(); - let def_id = tcx.require_lang_item(BoxFreeFnLangItem); - let box_free_instance = Instance::new( - def_id, - tcx.mk_substs(iter::once(Kind::from(ty.boxed_ty()))) - ); - if should_trans_locally(tcx, &box_free_instance) { - output.push(create_fn_trans_item(box_free_instance)); - } - } - - // If the type implements Drop, also add a translation item for the - // monomorphized Drop::drop() implementation. - let has_dtor = match ty.sty { - ty::TyAdt(def, _) => def.has_dtor(scx.tcx()), - _ => false - }; - - if has_dtor && !ty.is_box() { - let drop_trait_def_id = scx.tcx() - .lang_items - .drop_trait() - .unwrap(); - let drop_method = scx.tcx().associated_items(drop_trait_def_id) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - let substs = scx.tcx().mk_substs_trait(ty, &[]); - let instance = monomorphize::resolve(scx, drop_method, substs); - if should_trans_locally(scx.tcx(), &instance) { - output.push(create_fn_trans_item(instance)); - } - - // This type has a Drop implementation, we'll need the contents-only - // version of the glue too. - output.push(TransItem::DropGlue(DropGlueKind::TyContents(ty))); - } - - // Finally add the types of nested values - match ty.sty { - ty::TyBool | - ty::TyChar | - ty::TyInt(_) | - ty::TyUint(_) | - ty::TyStr | - ty::TyFloat(_) | - ty::TyRawPtr(_) | - ty::TyRef(..) | - ty::TyFnDef(..) | - ty::TyFnPtr(_) | - ty::TyNever | - ty::TyDynamic(..) => { - /* nothing to do */ - } - ty::TyAdt(def, _) if def.is_box() => { - let inner_type = glue::get_drop_glue_type(scx, ty.boxed_ty()); - if scx.type_needs_drop(inner_type) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(inner_type))); - } - } - ty::TyAdt(def, substs) => { - for field in def.all_fields() { - let field_type = def_ty(scx, field.did, substs); - let field_type = glue::get_drop_glue_type(scx, field_type); - - if scx.type_needs_drop(field_type) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(field_type))); - } - } - } - ty::TyClosure(def_id, substs) => { - for upvar_ty in substs.upvar_tys(def_id, scx.tcx()) { - let upvar_ty = glue::get_drop_glue_type(scx, upvar_ty); - if scx.type_needs_drop(upvar_ty) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(upvar_ty))); - } - } - } - ty::TySlice(inner_type) | - ty::TyArray(inner_type, _) => { - let inner_type = glue::get_drop_glue_type(scx, inner_type); - if scx.type_needs_drop(inner_type) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(inner_type))); - } - } - ty::TyTuple(args, _) => { - for arg in args { - let arg = glue::get_drop_glue_type(scx, arg); - if scx.type_needs_drop(arg) { - output.push(TransItem::DropGlue(DropGlueKind::Ty(arg))); - } - } - } - ty::TyProjection(_) | - ty::TyParam(_) | - ty::TyInfer(_) | - ty::TyAnon(..) | - ty::TyError => { - bug!("encountered unexpected type"); - } - } -} - /// For given pair of source and target type that occur in an unsizing coercion, /// this function finds the pair of types that determines the vtable linking /// them. @@ -894,8 +791,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, output.extend(methods); } // Also add the destructor - let dg_type = glue::get_drop_glue_type(scx, impl_ty); - output.push(TransItem::DropGlue(DropGlueKind::Ty(dg_type))); + visit_drop_use(scx, impl_ty, false, output); } } @@ -940,8 +836,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { def_id_to_string(self.scx.tcx(), def_id)); let ty = def_ty(self.scx, def_id, Substs::empty()); - let ty = glue::get_drop_glue_type(self.scx, ty); - self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); + visit_drop_use(self.scx, ty, true, self.output); } } } @@ -1093,12 +988,3 @@ fn def_id_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, printer.push_def_path(def_id, &mut output); output } - -fn type_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: ty::Ty<'tcx>) - -> String { - let mut output = String::new(); - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_type_name(ty, &mut output); - output -} diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 431325c5ec74e..a0906bb02f5a3 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -535,16 +535,27 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn requests_inline<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &ty::Instance<'tcx> +) -> bool { + if is_inline_instance(tcx, instance) { + return true + } + attr::requests_inline(&instance.def.attrs(tcx)[..]) +} + +pub fn is_inline_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx> ) -> bool { let def_id = match instance.def { ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::DropGlue(_, Some(_)) => return false, _ => return true }; match tcx.def_key(def_id).disambiguated_data.data { DefPathData::StructCtor | DefPathData::EnumVariant(..) | DefPathData::ClosureExpr => true, - _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), + _ => false } } diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index 9297c0108468a..1c1395f1b7762 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -21,7 +21,6 @@ use debuginfo; use callee; use base; use declare; -use glue::DropGlueKind; use monomorphize::Instance; use partitioning::CodegenUnit; @@ -46,7 +45,7 @@ use std::str; use syntax::ast; use syntax::symbol::InternedString; use syntax_pos::DUMMY_SP; -use abi::{Abi, FnType}; +use abi::Abi; pub struct Stats { pub n_glues_created: Cell, @@ -94,8 +93,6 @@ pub struct LocalCrateContext<'tcx> { previous_work_product: Option, codegen_unit: CodegenUnit<'tcx>, needs_unwind_cleanup_cache: RefCell, bool>>, - fn_pointer_shims: RefCell, ValueRef>>, - drop_glues: RefCell, (ValueRef, FnType)>>, /// Cache instances of monomorphic and polymorphic items instances: RefCell, ValueRef>>, /// Cache generated vtables @@ -587,8 +584,6 @@ impl<'tcx> LocalCrateContext<'tcx> { previous_work_product: previous_work_product, codegen_unit: codegen_unit, needs_unwind_cleanup_cache: RefCell::new(FxHashMap()), - fn_pointer_shims: RefCell::new(FxHashMap()), - drop_glues: RefCell::new(FxHashMap()), instances: RefCell::new(FxHashMap()), vtables: RefCell::new(FxHashMap()), const_cstr_cache: RefCell::new(FxHashMap()), @@ -723,15 +718,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local().needs_unwind_cleanup_cache } - pub fn fn_pointer_shims(&self) -> &RefCell, ValueRef>> { - &self.local().fn_pointer_shims - } - - pub fn drop_glues<'a>(&'a self) - -> &'a RefCell, (ValueRef, FnType)>> { - &self.local().drop_glues - } - pub fn instances<'a>(&'a self) -> &'a RefCell, ValueRef>> { &self.local().instances } diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 9a639ed21f1ac..41a9ab2842dcd 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -13,69 +13,35 @@ // Code relating to drop glue. use std; -use std::iter; use llvm; -use llvm::{ValueRef, get_param}; -use middle::lang_items::BoxFreeFnLangItem; -use rustc::ty::subst::{Substs}; +use llvm::{ValueRef}; use rustc::traits; -use rustc::ty::{self, layout, AdtDef, AdtKind, Ty, TypeFoldable}; -use rustc::ty::subst::Kind; -use rustc::mir::tcx::LvalueTy; -use mir::lvalue::LvalueRef; -use abi::FnType; -use adt; -use base::*; -use callee::get_fn; -use cleanup::CleanupScope; +use rustc::ty::{self, Ty, TypeFoldable}; use common::*; use machine::*; +use meth; use monomorphize; -use trans_item::TransItem; -use tvec; -use type_of::{type_of, sizing_type_of, align_of}; -use type_::Type; +use type_of::{sizing_type_of, align_of}; use value::Value; -use Disr; use builder::Builder; -use mir::lvalue::Alignment; - -pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef<'tcx>) { - let content_ty = ptr.ty.to_ty(bcx.tcx()); - let def_id = langcall(bcx.tcx(), None, "", BoxFreeFnLangItem); - let substs = bcx.tcx().mk_substs(iter::once(Kind::from(content_ty))); - let instance = monomorphize::resolve(bcx.ccx.shared(), def_id, substs); - - let fn_ty = FnType::from_instance(bcx.ccx, &instance, &[]); - let llret = bcx.call(get_fn(bcx.ccx, instance), - &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize], None); - fn_ty.apply_attrs_callsite(llret); -} - -pub fn get_drop_glue_type<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Ty<'tcx> { +pub fn needs_drop_glue<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'tcx>) -> bool { assert!(t.is_normalized_for_trans()); let t = scx.tcx().erase_regions(&t); - // Even if there is no dtor for t, there might be one deeper down and we - // might need to pass in the vtable ptr. - if !scx.type_is_sized(t) { - return t; - } - // FIXME (#22815): note that type_needs_drop conservatively // approximates in some cases and may say a type expression // requires drop glue when it actually does not. // // (In this case it is not clear whether any harm is done, i.e. - // erroneously returning `t` in some cases where we could have - // returned `tcx.types.i8` does not appear unsound. The impact on + // erroneously returning `true` in some cases where we could have + // returned `false` does not appear unsound. The impact on // code quality is unknown at this time.) if !scx.type_needs_drop(t) { - return scx.tcx().types.i8; + return false; } match t.sty { ty::TyAdt(def, _) if def.is_box() => { @@ -85,210 +51,19 @@ pub fn get_drop_glue_type<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'t let layout = t.layout(&infcx).unwrap(); if layout.size(&scx.tcx().data_layout).bytes() == 0 { // `Box` does not allocate. - scx.tcx().types.i8 + false } else { - t + true } }) } else { - t + true } } - _ => t - } -} - -fn drop_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, args: LvalueRef<'tcx>) { - call_drop_glue(bcx, args, false, None) -} - -pub fn call_drop_glue<'a, 'tcx>( - bcx: &Builder<'a, 'tcx>, - mut args: LvalueRef<'tcx>, - skip_dtor: bool, - funclet: Option<&'a Funclet>, -) { - let t = args.ty.to_ty(bcx.tcx()); - // NB: v is an *alias* of type t here, not a direct value. - debug!("call_drop_glue(t={:?}, skip_dtor={})", t, skip_dtor); - if bcx.ccx.shared().type_needs_drop(t) { - let ccx = bcx.ccx; - let g = if skip_dtor { - DropGlueKind::TyContents(t) - } else { - DropGlueKind::Ty(t) - }; - let glue = get_drop_glue_core(ccx, g); - let glue_type = get_drop_glue_type(ccx.shared(), t); - if glue_type != t { - args.llval = bcx.pointercast(args.llval, type_of(ccx, glue_type).ptr_to()); - } - - // No drop-hint ==> call standard drop glue - bcx.call(glue, &[args.llval, args.llextra][..1 + args.has_extra() as usize], - funclet.map(|b| b.bundle())); + _ => true } } -pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ValueRef { - get_drop_glue_core(ccx, DropGlueKind::Ty(t)) -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum DropGlueKind<'tcx> { - /// The normal path; runs the dtor, and then recurs on the contents - Ty(Ty<'tcx>), - /// Skips the dtor, if any, for ty; drops the contents directly. - /// Note that the dtor is only skipped at the most *shallow* - /// level, namely, an `impl Drop for Ty` itself. So, for example, - /// if Ty is Newtype(S) then only the Drop impl for Newtype itself - /// will be skipped, while the Drop impl for S, if any, will be - /// invoked. - TyContents(Ty<'tcx>), -} - -impl<'tcx> DropGlueKind<'tcx> { - pub fn ty(&self) -> Ty<'tcx> { - match *self { DropGlueKind::Ty(t) | DropGlueKind::TyContents(t) => t } - } - - pub fn map_ty(&self, mut f: F) -> DropGlueKind<'tcx> where F: FnMut(Ty<'tcx>) -> Ty<'tcx> - { - match *self { - DropGlueKind::Ty(t) => DropGlueKind::Ty(f(t)), - DropGlueKind::TyContents(t) => DropGlueKind::TyContents(f(t)), - } - } -} - -fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKind<'tcx>) -> ValueRef { - let g = g.map_ty(|t| get_drop_glue_type(ccx.shared(), t)); - match ccx.drop_glues().borrow().get(&g) { - Some(&(glue, _)) => glue, - None => { - bug!("Could not find drop glue for {:?} -- {} -- {}.", - g, - TransItem::DropGlue(g).to_raw_string(), - ccx.codegen_unit().name()); - } - } -} - -pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKind<'tcx>) { - assert_eq!(g.ty(), get_drop_glue_type(ccx.shared(), g.ty())); - let (llfn, _) = ccx.drop_glues().borrow().get(&g).unwrap().clone(); - - let mut bcx = Builder::new_block(ccx, llfn, "entry-block"); - - ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1); - // All glue functions take values passed *by alias*; this is a - // requirement since in many contexts glue is invoked indirectly and - // the caller has no idea if it's dealing with something that can be - // passed by value. - // - // llfn is expected be declared to take a parameter of the appropriate - // type, so we don't need to explicitly cast the function parameter. - - // NB: v0 is an *alias* of type t here, not a direct value. - // Only drop the value when it ... well, we used to check for - // non-null, (and maybe we need to continue doing so), but we now - // must definitely check for special bit-patterns corresponding to - // the special dtor markings. - let t = g.ty(); - - let value = get_param(llfn, 0); - let ptr = if ccx.shared().type_is_sized(t) { - LvalueRef::new_sized_ty(value, t, Alignment::AbiAligned) - } else { - LvalueRef::new_unsized_ty(value, get_param(llfn, 1), t, Alignment::AbiAligned) - }; - - let skip_dtor = match g { - DropGlueKind::Ty(_) => false, - DropGlueKind::TyContents(_) => true - }; - - let bcx = match t.sty { - ty::TyAdt(def, _) if def.is_box() => { - // Support for Box is built-in as yet and its drop glue is special - // despite having a dummy Drop impl in the library. - assert!(!skip_dtor); - let content_ty = t.boxed_ty(); - let ptr = if !bcx.ccx.shared().type_is_sized(content_ty) { - let llbox = bcx.load(get_dataptr(&bcx, ptr.llval), None); - let info = bcx.load(get_meta(&bcx, ptr.llval), None); - LvalueRef::new_unsized_ty(llbox, info, content_ty, Alignment::AbiAligned) - } else { - LvalueRef::new_sized_ty( - bcx.load(ptr.llval, None), - content_ty, Alignment::AbiAligned) - }; - drop_ty(&bcx, ptr); - trans_exchange_free_ty(&bcx, ptr); - bcx - } - ty::TyDynamic(..) => { - // No support in vtable for distinguishing destroying with - // versus without calling Drop::drop. Assert caller is - // okay with always calling the Drop impl, if any. - assert!(!skip_dtor); - let dtor = bcx.load(ptr.llextra, None); - bcx.call(dtor, &[ptr.llval], None); - bcx - } - ty::TyAdt(def, ..) if def.has_dtor(bcx.tcx()) && !skip_dtor => { - let shallow_drop = def.is_union(); - let tcx = bcx.tcx(); - - // Be sure to put the contents into a scope so we can use an invoke - // instruction to call the user destructor but still call the field - // destructors if the user destructor panics. - // - // FIXME (#14875) panic-in-drop semantics might be unsupported; we - // might well consider changing below to more direct code. - // Issue #23611: schedule cleanup of contents, re-inspecting the - // discriminant (if any) in case of variant swap in drop code. - let contents_scope = if !shallow_drop { - CleanupScope::schedule_drop_adt_contents(&bcx, ptr) - } else { - CleanupScope::noop() - }; - let drop_trait_def_id = tcx.lang_items.drop_trait().unwrap(); - let drop_method = tcx.associated_items(drop_trait_def_id) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - let self_type_substs = tcx.mk_substs_trait(t, &[]); - let drop_instance = monomorphize::resolve( - bcx.ccx.shared(), drop_method, self_type_substs); - let fn_ty = FnType::from_instance(bcx.ccx, &drop_instance, &[]); - let llfn = get_fn(bcx.ccx, drop_instance); - let llret; - let args = &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize]; - if let Some(landing_pad) = contents_scope.landing_pad { - let normal_bcx = bcx.build_sibling_block("normal-return"); - llret = bcx.invoke(llfn, args, normal_bcx.llbb(), landing_pad, None); - bcx = normal_bcx; - } else { - llret = bcx.call(llfn, args, None); - } - fn_ty.apply_attrs_callsite(llret); - contents_scope.trans(&bcx); - bcx - } - ty::TyAdt(def, ..) if def.is_union() => { - bcx - } - _ => { - if bcx.ccx.shared().type_needs_drop(t) { - drop_structural_ty(bcx, ptr) - } else { - bcx - } - } - }; - bcx.ret_void(); -} - pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef) -> (ValueRef, ValueRef) { debug!("calculate size of DST: {}; with lost info: {:?}", @@ -375,20 +150,8 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf (size, align) } ty::TyDynamic(..) => { - // info points to the vtable and the second entry in the vtable is the - // dynamic size of the object. - let info = bcx.pointercast(info, Type::int(bcx.ccx).ptr_to()); - let size_ptr = bcx.gepi(info, &[1]); - let align_ptr = bcx.gepi(info, &[2]); - - let size = bcx.load(size_ptr, None); - let align = bcx.load(align_ptr, None); - - // Vtable loads are invariant - bcx.set_invariant_load(size); - bcx.set_invariant_load(align); - - (size, align) + // load size/align from vtable + (meth::SIZE.get_usize(bcx, info), meth::ALIGN.get_usize(bcx, info)) } ty::TySlice(_) | ty::TyStr => { let unit_ty = t.sequence_element_type(bcx.tcx()); @@ -403,141 +166,3 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf _ => bug!("Unexpected unsized type, found {}", t) } } - -// Iterates through the elements of a structural type, dropping them. -fn drop_structural_ty<'a, 'tcx>( - cx: Builder<'a, 'tcx>, - mut ptr: LvalueRef<'tcx> -) -> Builder<'a, 'tcx> { - fn iter_variant_fields<'a, 'tcx>( - cx: &'a Builder<'a, 'tcx>, - av: LvalueRef<'tcx>, - adt_def: &'tcx AdtDef, - variant_index: usize, - substs: &'tcx Substs<'tcx> - ) { - let variant = &adt_def.variants[variant_index]; - let tcx = cx.tcx(); - for (i, field) in variant.fields.iter().enumerate() { - let arg = monomorphize::field_ty(tcx, substs, field); - let (field_ptr, align) = av.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(field_ptr, arg, align)); - } - } - - let mut cx = cx; - let t = ptr.ty.to_ty(cx.tcx()); - match t.sty { - ty::TyClosure(def_id, substs) => { - for (i, upvar_ty) in substs.upvar_tys(def_id, cx.tcx()).enumerate() { - let (llupvar, align) = ptr.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(llupvar, upvar_ty, align)); - } - } - ty::TyArray(_, n) => { - let base = get_dataptr(&cx, ptr.llval); - let len = C_uint(cx.ccx, n); - let unit_ty = t.sequence_element_type(cx.tcx()); - cx = tvec::slice_for_each(&cx, base, unit_ty, len, - |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty, ptr.alignment))); - } - ty::TySlice(_) | ty::TyStr => { - let unit_ty = t.sequence_element_type(cx.tcx()); - cx = tvec::slice_for_each(&cx, ptr.llval, unit_ty, ptr.llextra, - |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty, ptr.alignment))); - } - ty::TyTuple(ref args, _) => { - for (i, arg) in args.iter().enumerate() { - let (llfld_a, align) = ptr.trans_field_ptr(&cx, i); - drop_ty(&cx, LvalueRef::new_sized_ty(llfld_a, *arg, align)); - } - } - ty::TyAdt(adt, substs) => match adt.adt_kind() { - AdtKind::Struct => { - for (i, field) in adt.variants[0].fields.iter().enumerate() { - let field_ty = monomorphize::field_ty(cx.tcx(), substs, field); - let (llval, align) = ptr.trans_field_ptr(&cx, i); - let field_ptr = if cx.ccx.shared().type_is_sized(field_ty) { - LvalueRef::new_sized_ty(llval, field_ty, align) - } else { - LvalueRef::new_unsized_ty(llval, ptr.llextra, field_ty, align) - }; - drop_ty(&cx, field_ptr); - } - } - AdtKind::Union => { - bug!("Union in `glue::drop_structural_ty`"); - } - AdtKind::Enum => { - let n_variants = adt.variants.len(); - - // NB: we must hit the discriminant first so that structural - // comparison know not to proceed when the discriminants differ. - - // Obtain a representation of the discriminant sufficient to translate - // destructuring; this may or may not involve the actual discriminant. - let l = cx.ccx.layout_of(t); - match *l { - layout::Univariant { .. } | - layout::UntaggedUnion { .. } => { - if n_variants != 0 { - assert!(n_variants == 1); - ptr.ty = LvalueTy::Downcast { - adt_def: adt, - substs: substs, - variant_index: 0, - }; - iter_variant_fields(&cx, ptr, &adt, 0, substs); - } - } - layout::CEnum { .. } | - layout::General { .. } | - layout::RawNullablePointer { .. } | - layout::StructWrappedNullablePointer { .. } => { - let lldiscrim_a = adt::trans_get_discr( - &cx, t, ptr.llval, ptr.alignment, None, false); - - // Create a fall-through basic block for the "else" case of - // the switch instruction we're about to generate. Note that - // we do **not** use an Unreachable instruction here, even - // though most of the time this basic block will never be hit. - // - // When an enum is dropped it's contents are currently - // overwritten to DTOR_DONE, which means the discriminant - // could have changed value to something not within the actual - // range of the discriminant. Currently this function is only - // used for drop glue so in this case we just return quickly - // from the outer function, and any other use case will only - // call this for an already-valid enum in which case the `ret - // void` will never be hit. - let ret_void_cx = cx.build_sibling_block("enum-iter-ret-void"); - ret_void_cx.ret_void(); - let llswitch = cx.switch(lldiscrim_a, ret_void_cx.llbb(), n_variants); - let next_cx = cx.build_sibling_block("enum-iter-next"); - - for (i, discr) in adt.discriminants(cx.tcx()).enumerate() { - let variant_cx_name = format!("enum-iter-variant-{}", i); - let variant_cx = cx.build_sibling_block(&variant_cx_name); - let case_val = adt::trans_case(&cx, t, Disr::from(discr)); - variant_cx.add_case(llswitch, case_val, variant_cx.llbb()); - ptr.ty = LvalueTy::Downcast { - adt_def: adt, - substs: substs, - variant_index: i, - }; - iter_variant_fields(&variant_cx, ptr, &adt, i, substs); - variant_cx.br(next_cx.llbb()); - } - cx = next_cx; - } - _ => bug!("{} is not an enum.", t), - } - } - }, - - _ => { - cx.sess().unimpl(&format!("type in drop_structural_ty: {}", t)) - } - } - return cx; -} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 49cc0b5fad401..f3e30ed4839ae 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -113,7 +113,6 @@ mod cabi_x86; mod cabi_x86_64; mod cabi_x86_win64; mod callee; -mod cleanup; mod collector; mod common; mod consts; diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index 6b823756c8eab..75ab407614050 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -14,28 +14,45 @@ use callee; use common::*; use builder::Builder; use consts; -use glue; use machine; +use monomorphize; use type_::Type; use type_of::*; use value::Value; use rustc::ty; -// drop_glue pointer, size, align. -const VTABLE_OFFSET: usize = 3; - -/// Extracts a method from a trait object's vtable, at the specified index. -pub fn get_virtual_method<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, - llvtable: ValueRef, - vtable_index: usize) -> ValueRef { - // Load the data pointer from the object. - debug!("get_virtual_method(vtable_index={}, llvtable={:?})", - vtable_index, Value(llvtable)); - - let ptr = bcx.load_nonnull(bcx.gepi(llvtable, &[vtable_index + VTABLE_OFFSET]), None); - // Vtable loads are invariant - bcx.set_invariant_load(ptr); - ptr +#[derive(Copy, Clone, Debug)] +pub struct VirtualIndex(usize); + +pub const DESTRUCTOR: VirtualIndex = VirtualIndex(0); +pub const SIZE: VirtualIndex = VirtualIndex(1); +pub const ALIGN: VirtualIndex = VirtualIndex(2); + +impl<'a, 'tcx> VirtualIndex { + pub fn from_index(index: usize) -> Self { + VirtualIndex(index + 3) + } + + pub fn get_fn(self, bcx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { + // Load the data pointer from the object. + debug!("get_fn({:?}, {:?})", Value(llvtable), self); + + let ptr = bcx.load_nonnull(bcx.gepi(llvtable, &[self.0]), None); + // Vtable loads are invariant + bcx.set_invariant_load(ptr); + ptr + } + + pub fn get_usize(self, bcx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { + // Load the data pointer from the object. + debug!("get_int({:?}, {:?})", Value(llvtable), self); + + let llvtable = bcx.pointercast(llvtable, Type::int(bcx.ccx).ptr_to()); + let ptr = bcx.load(bcx.gepi(llvtable, &[self.0]), None); + // Vtable loads are invariant + bcx.set_invariant_load(ptr); + ptr + } } /// Creates a dynamic vtable for the given type and vtable origin. @@ -68,8 +85,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let align = align_of(ccx, ty); let mut components: Vec<_> = [ - // Generate a destructor for the vtable. - glue::get_drop_glue(ccx, ty), + callee::get_fn(ccx, monomorphize::resolve_drop_in_place(ccx.shared(), ty)), C_uint(ccx, size), C_uint(ccx, align) ].iter().cloned().collect(); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 761f6b208e747..226d40948c4dc 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -19,13 +19,13 @@ use base::{self, Lifetime}; use callee; use builder::Builder; use common::{self, Funclet}; -use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; +use common::{C_bool, C_str_slice, C_struct, C_u32, C_uint, C_undef}; use consts; use machine::llalign_of_min; use meth; use monomorphize; +use tvec; use type_of::{self, align_of}; -use glue; use type_::Type; use rustc_data_structures::indexed_vec::IndexVec; @@ -209,21 +209,49 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::TerminatorKind::Drop { ref location, target, unwind } => { let ty = location.ty(&self.mir, bcx.tcx()).to_ty(bcx.tcx()); let ty = self.monomorphize(&ty); + let drop_fn = monomorphize::resolve_drop_in_place(bcx.ccx.shared(), ty); - // Double check for necessity to drop - if !bcx.ccx.shared().type_needs_drop(ty) { + if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + // we don't actually need to drop anything. funclet_br(self, bcx, target); - return; + return } - let mut lvalue = self.trans_lvalue(&bcx, location); - let drop_fn = glue::get_drop_glue(bcx.ccx, ty); - let drop_ty = glue::get_drop_glue_type(bcx.ccx.shared(), ty); - if bcx.ccx.shared().type_is_sized(ty) && drop_ty != ty { - lvalue.llval = bcx.pointercast( - lvalue.llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to()); - } - let args = &[lvalue.llval, lvalue.llextra][..1 + lvalue.has_extra() as usize]; + let lvalue = self.trans_lvalue(&bcx, location); + let (drop_fn, need_extra) = match ty.sty { + ty::TyDynamic(..) => (meth::DESTRUCTOR.get_fn(&bcx, lvalue.llextra), + false), + ty::TyArray(ety, _) | ty::TySlice(ety) => { + // FIXME: handle panics + let drop_fn = monomorphize::resolve_drop_in_place( + bcx.ccx.shared(), ety); + let drop_fn = callee::get_fn(bcx.ccx, drop_fn); + let bcx = tvec::slice_for_each( + &bcx, + lvalue.project_index(&bcx, C_uint(bcx.ccx, 0u64)), + ety, + lvalue.len(bcx.ccx), + |bcx, llval, loop_bb| { + self.set_debug_loc(&bcx, terminator.source_info); + if let Some(unwind) = unwind { + bcx.invoke( + drop_fn, + &[llval], + loop_bb, + llblock(self, unwind), + cleanup_bundle + ); + } else { + bcx.call(drop_fn, &[llval], cleanup_bundle); + bcx.br(loop_bb); + } + }); + funclet_br(self, bcx, target); + return + } + _ => (callee::get_fn(bcx.ccx, drop_fn), lvalue.has_extra()) + }; + let args = &[lvalue.llval, lvalue.llextra][..1 + need_extra as usize]; if let Some(unwind) = unwind { bcx.invoke( drop_fn, @@ -417,23 +445,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { Some(ty::InstanceDef::Virtual(..)) => { FnType::new_vtable(bcx.ccx, sig, &extra_args) } - _ => FnType::new(bcx.ccx, sig, &extra_args) - }; - - if intrinsic == Some("drop_in_place") { - let &(_, target) = destination.as_ref().unwrap(); - let ty = instance.unwrap().substs.type_at(0); - - // Double check for necessity to drop - if !bcx.ccx.shared().type_needs_drop(ty) { + Some(ty::InstanceDef::DropGlue(_, None)) => { + // empty drop glue - a nop. + let &(_, target) = destination.as_ref().unwrap(); funclet_br(self, bcx, target); return; } - - let drop_fn = glue::get_drop_glue(bcx.ccx, ty); - let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - llfn = Some(bcx.pointercast(drop_fn, llty)); - } + _ => FnType::new(bcx.ccx, sig, &extra_args) + }; // The arguments we'll be passing. Plus one to account for outptr, if used. let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; @@ -588,7 +607,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (ptr, meta) = (a, b); if *next_idx == 0 { if let Some(ty::InstanceDef::Virtual(_, idx)) = *def { - let llmeth = meth::get_virtual_method(bcx, meta, idx); + let llmeth = meth::VirtualIndex::from_index(idx).get_fn(bcx, meta); let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); *llfn = Some(bcx.pointercast(llmeth, llty)); } @@ -756,14 +775,18 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return block; } + let block = self.blocks[target_bb]; + let landing_pad = self.landing_pad_uncached(block); + self.landing_pads[target_bb] = Some(landing_pad); + landing_pad + } + + fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef { if base::wants_msvc_seh(self.ccx.sess()) { - return self.blocks[target_bb]; + return target_bb; } - let target = self.get_builder(target_bb); - let bcx = self.new_block("cleanup"); - self.landing_pads[target_bb] = Some(bcx.llbb()); let ccx = bcx.ccx; let llpersonality = self.ccx.eh_personality(); @@ -772,7 +795,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx.set_cleanup(llretval); let slot = self.get_personality_slot(&bcx); bcx.store(llretval, slot, None); - bcx.br(target.llbb()); + bcx.br(target_bb); bcx.llbb() } diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index 49e1e3855571b..dd8c1d0e1f031 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -27,7 +27,6 @@ use std::ptr; use std::ops; use super::{MirContext, LocalRef}; -use super::operand::OperandValue; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Alignment { @@ -95,16 +94,6 @@ impl<'a, 'tcx> LvalueRef<'tcx> { LvalueRef::new_sized(llval, LvalueTy::from_ty(ty), alignment) } - pub fn new_unsized_ty(llval: ValueRef, llextra: ValueRef, ty: Ty<'tcx>, alignment: Alignment) - -> LvalueRef<'tcx> { - LvalueRef { - llval: llval, - llextra: llextra, - ty: LvalueTy::from_ty(ty), - alignment: alignment, - } - } - pub fn alloca(bcx: &Builder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> LvalueRef<'tcx> { debug!("alloca({:?}: {:?})", name, ty); let tmp = bcx.alloca(type_of::type_of(bcx.ccx, ty), name); @@ -279,6 +268,16 @@ impl<'a, 'tcx> LvalueRef<'tcx> { _ => bug!("element access in type without elements: {} represented as {:#?}", t, l) } } + + pub fn project_index(&self, bcx: &Builder<'a, 'tcx>, llindex: ValueRef) -> ValueRef { + if let ty::TySlice(_) = self.ty.to_ty(bcx.tcx()).sty { + // Slices already point to the array element type. + bcx.inbounds_gep(self.llval, &[llindex]) + } else { + let zero = common::C_uint(bcx.ccx, 0u64); + bcx.inbounds_gep(self.llval, &[zero, llindex]) + } + } } impl<'a, 'tcx> MirContext<'a, 'tcx> { @@ -314,21 +313,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { elem: mir::ProjectionElem::Deref }) => { // Load the pointer from its location. - let ptr = self.trans_consume(bcx, base); - let projected_ty = LvalueTy::from_ty(ptr.ty) - .projection_ty(tcx, &mir::ProjectionElem::Deref); - let projected_ty = self.monomorphize(&projected_ty); - let (llptr, llextra) = match ptr.val { - OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), - OperandValue::Pair(llptr, llextra) => (llptr, llextra), - OperandValue::Ref(..) => bug!("Deref of by-Ref type {:?}", ptr.ty) - }; - LvalueRef { - llval: llptr, - llextra: llextra, - ty: projected_ty, - alignment: Alignment::AbiAligned, - } + self.trans_consume(bcx, base).deref() } mir::Lvalue::Projection(ref projection) => { let tr_base = self.trans_lvalue(bcx, &projection.base); @@ -336,17 +321,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let projected_ty = self.monomorphize(&projected_ty); let align = tr_base.alignment; - let project_index = |llindex| { - let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty { - // Slices already point to the array element type. - bcx.inbounds_gep(tr_base.llval, &[llindex]) - } else { - let zero = common::C_uint(bcx.ccx, 0u64); - bcx.inbounds_gep(tr_base.llval, &[zero, llindex]) - }; - (element, align) - }; - let ((llprojected, align), llextra) = match projection.elem { mir::ProjectionElem::Deref => bug!(), mir::ProjectionElem::Field(ref field, _) => { @@ -359,13 +333,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::ProjectionElem::Index(ref index) => { let index = self.trans_operand(bcx, index); - (project_index(self.prepare_index(bcx, index.immediate())), ptr::null_mut()) + let llindex = self.prepare_index(bcx, index.immediate()); + ((tr_base.project_index(bcx, llindex), align), ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: false, min_length: _ } => { let lloffset = C_uint(bcx.ccx, offset); - (project_index(lloffset), ptr::null_mut()) + ((tr_base.project_index(bcx, lloffset), align), ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: true, @@ -373,11 +348,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let lloffset = C_uint(bcx.ccx, offset); let lllen = tr_base.len(bcx.ccx); let llindex = bcx.sub(lllen, lloffset); - (project_index(llindex), ptr::null_mut()) + ((tr_base.project_index(bcx, llindex), align), ptr::null_mut()) } mir::ProjectionElem::Subslice { from, to } => { - let llindex = C_uint(bcx.ccx, from); - let (llbase, align) = project_index(llindex); + let llbase = tr_base.project_index(bcx, C_uint(bcx.ccx, from)); let base_ty = tr_base.ty.to_ty(bcx.tcx()); match base_ty.sty { diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 3f29545ecf45a..da24c03fdc2a0 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -9,9 +9,10 @@ // except according to those terms. use llvm::ValueRef; -use rustc::ty::Ty; +use rustc::ty::{self, Ty}; use rustc::ty::layout::Layout; use rustc::mir; +use rustc::mir::tcx::LvalueTy; use rustc_data_structures::indexed_vec::Idx; use base; @@ -22,9 +23,10 @@ use type_of; use type_::Type; use std::fmt; +use std::ptr; use super::{MirContext, LocalRef}; -use super::lvalue::Alignment; +use super::lvalue::{Alignment, LvalueRef}; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -86,6 +88,22 @@ impl<'a, 'tcx> OperandRef<'tcx> { } } + pub fn deref(self) -> LvalueRef<'tcx> { + let projected_ty = self.ty.builtin_deref(true, ty::NoPreference) + .unwrap().ty; + let (llptr, llextra) = match self.val { + OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), + OperandValue::Pair(llptr, llextra) => (llptr, llextra), + OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self) + }; + LvalueRef { + llval: llptr, + llextra: llextra, + ty: LvalueTy::from_ty(projected_ty), + alignment: Alignment::AbiAligned, + } + } + /// If this operand is a Pair, we return an /// Immediate aggregate with the two values. pub fn pack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { @@ -236,7 +254,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::Operand::Constant(ref constant) => { - let val = self.trans_constant(bcx, constant); + let val = self.trans_constant(&bcx, constant); let operand = val.to_operand(bcx.ccx); if let OperandValue::Ref(ptr, align) = operand.val { // If this is a OperandValue::Ref to an immediate constant, load it. diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 178347369c915..d487aa6cd5be6 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -98,8 +98,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let size = count.as_u64(bcx.tcx().sess.target.uint_type); let size = C_uint(bcx.ccx, size); let base = base::get_dataptr(&bcx, dest.llval); - tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot| { + tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot, loop_bb| { self.store_operand(bcx, llslot, dest.alignment.to_align(), tr_elem); + bcx.br(loop_bb); }) } @@ -459,7 +460,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; (bcx, operand) } - mir::Rvalue::Use(ref operand) => { let operand = self.trans_operand(&bcx, operand); (bcx, operand) @@ -662,7 +662,7 @@ pub fn rvalue_creates_operand(rvalue: &mir::Rvalue) -> bool { mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) | mir::Rvalue::Box(..) | - mir::Rvalue::Use(..) => + mir::Rvalue::Use(..) => // (*) true, mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 0d8aa0f4bda0e..fcf6937d4b6d5 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -10,9 +10,11 @@ use abi::Abi; use common::*; +use glue; use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; +use rustc::middle::lang_items::DropInPlaceFnLangItem; use rustc::traits::{self, SelectionContext, Reveal}; use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::ty::fold::{TypeFolder, TypeFoldable}; @@ -242,8 +244,19 @@ pub fn resolve<'a, 'tcx>( ty::InstanceDef::Intrinsic(def_id) } _ => { - debug!(" => free item"); - ty::InstanceDef::Item(def_id) + if Some(def_id) == scx.tcx().lang_items.drop_in_place_fn() { + let ty = substs.type_at(0); + if glue::needs_drop_glue(scx, ty) { + debug!(" => nontrivial drop glue"); + ty::InstanceDef::DropGlue(def_id, Some(ty)) + } else { + debug!(" => trivial drop glue"); + ty::InstanceDef::DropGlue(def_id, None) + } + } else { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } } }; Instance { def, substs } @@ -253,6 +266,16 @@ pub fn resolve<'a, 'tcx>( result } +pub fn resolve_drop_in_place<'a, 'tcx>( + scx: &SharedCrateContext<'a, 'tcx>, + ty: Ty<'tcx>) + -> ty::Instance<'tcx> +{ + let def_id = scx.tcx().require_lang_item(DropInPlaceFnLangItem); + let substs = scx.tcx().intern_substs(&[Kind::from(ty)]); + resolve(scx, def_id, substs) +} + pub fn custom_coerce_unsize_info<'scx, 'tcx>(scx: &SharedCrateContext<'scx, 'tcx>, source_ty: Ty<'tcx>, target_ty: Ty<'tcx>) diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index d0cf32508d44c..90ce40cfbcf8f 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -194,7 +194,6 @@ impl<'tcx> CodegenUnit<'tcx> { TransItem::Static(node_id) => { exported_symbols.contains(&node_id) } - TransItem::DropGlue(..) => false, }; exported.hash(&mut state); } @@ -245,7 +244,6 @@ impl<'tcx> CodegenUnit<'tcx> { tcx.hir.as_local_node_id(instance.def_id()) } TransItem::Static(node_id) => Some(node_id), - TransItem::DropGlue(_) => None, } } } @@ -341,7 +339,6 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>, match trans_item { TransItem::Fn(..) | TransItem::Static(..) => llvm::ExternalLinkage, - TransItem::DropGlue(..) => unreachable!(), } } }; @@ -461,6 +458,7 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Intrinsic(..) | + ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) => return None }; @@ -485,7 +483,6 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(scx: &SharedCrateContext<'a, 't Some(def_id) } - TransItem::DropGlue(dg) => characteristic_def_id_of_type(dg.ty()), TransItem::Static(node_id) => Some(tcx.hir.local_def_id(node_id)), } } diff --git a/src/librustc_trans/symbol_map.rs b/src/librustc_trans/symbol_map.rs index cd285bfaa6010..1b48e131b720a 100644 --- a/src/librustc_trans/symbol_map.rs +++ b/src/librustc_trans/symbol_map.rs @@ -100,7 +100,6 @@ impl<'tcx> SymbolMap<'tcx> { tcx.hir.as_local_node_id(def.def_id()) } TransItem::Static(node_id) => Some(node_id), - TransItem::DropGlue(_) => None, }.map(|node_id| { tcx.hir.span(node_id) }) diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 13af081e0b698..410e3f30be731 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -20,28 +20,23 @@ use consts; use context::{CrateContext, SharedCrateContext}; use common; use declare; -use glue::DropGlueKind; use llvm; use monomorphize::Instance; use rustc::dep_graph::DepNode; use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::hir::map::definitions::DefPathData; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::subst::Substs; use rustc_const_eval::fatal_const_eval_err; use syntax::ast::{self, NodeId}; use syntax::attr; use type_of; -use glue; -use abi::{Abi, FnType}; use back::symbol_names; use std::fmt::Write; use std::iter; #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub enum TransItem<'tcx> { - DropGlue(DropGlueKind<'tcx>), Fn(Instance<'tcx>), Static(NodeId) } @@ -100,9 +95,6 @@ impl<'a, 'tcx> TransItem<'tcx> { base::trans_instance(&ccx, instance); } - TransItem::DropGlue(dg) => { - glue::implement_drop_glue(&ccx, dg); - } } debug!("END IMPLEMENTING '{} ({})' in cgu {}", @@ -131,9 +123,6 @@ impl<'a, 'tcx> TransItem<'tcx> { TransItem::Fn(instance) => { TransItem::predefine_fn(ccx, instance, linkage, &symbol_name); } - TransItem::DropGlue(dg) => { - TransItem::predefine_drop_glue(ccx, dg, linkage, &symbol_name); - } } debug!("END PREDEFINING '{} ({})' in cgu {}", @@ -180,52 +169,14 @@ impl<'a, 'tcx> TransItem<'tcx> { } debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); - match ccx.tcx().def_key(instance.def_id()).disambiguated_data.data { - DefPathData::StructCtor | - DefPathData::EnumVariant(..) | - DefPathData::ClosureExpr => { - attributes::inline(lldecl, attributes::InlineAttr::Hint); - } - _ => {} + if common::is_inline_instance(ccx.tcx(), &instance) { + attributes::inline(lldecl, attributes::InlineAttr::Hint); } - attributes::from_fn_attrs(ccx, &attrs, lldecl); ccx.instances().borrow_mut().insert(instance, lldecl); } - fn predefine_drop_glue(ccx: &CrateContext<'a, 'tcx>, - dg: glue::DropGlueKind<'tcx>, - linkage: llvm::Linkage, - symbol_name: &str) { - let tcx = ccx.tcx(); - assert_eq!(dg.ty(), glue::get_drop_glue_type(ccx.shared(), dg.ty())); - let t = dg.ty(); - - let sig = tcx.mk_fn_sig( - iter::once(tcx.mk_mut_ptr(t)), - tcx.mk_nil(), - false, - hir::Unsafety::Normal, - Abi::Rust - ); - - debug!("predefine_drop_glue: sig={}", sig); - - let fn_ty = FnType::new(ccx, sig, &[]); - let llfnty = fn_ty.llvm_type(ccx); - - assert!(declare::get_defined_value(ccx, symbol_name).is_none()); - let llfn = declare::declare_cfn(ccx, symbol_name, llfnty); - unsafe { llvm::LLVMRustSetLinkage(llfn, linkage) }; - if linkage == llvm::Linkage::LinkOnceODRLinkage || - linkage == llvm::Linkage::WeakODRLinkage { - llvm::SetUniqueComdat(ccx.llmod(), llfn); - } - attributes::set_frame_pointer_elimination(ccx, llfn); - ccx.drop_glues().borrow_mut().insert(dg, (llfn, fn_ty)); - } - pub fn compute_symbol_name(&self, scx: &SharedCrateContext<'a, 'tcx>) -> String { match *self { @@ -234,13 +185,6 @@ impl<'a, 'tcx> TransItem<'tcx> { let def_id = scx.tcx().hir.local_def_id(node_id); symbol_names::symbol_name(Instance::mono(scx.tcx(), def_id), scx) } - TransItem::DropGlue(dg) => { - let prefix = match dg { - DropGlueKind::Ty(_) => "drop", - DropGlueKind::TyContents(_) => "drop_contents", - }; - symbol_names::exported_name_from_type_and_prefix(scx, dg.ty(), prefix) - } } } @@ -257,7 +201,6 @@ impl<'a, 'tcx> TransItem<'tcx> { InstantiationMode::GloballyShared } } - TransItem::DropGlue(..) => InstantiationMode::LocalCopy, TransItem::Static(..) => InstantiationMode::GloballyShared, } } @@ -267,7 +210,6 @@ impl<'a, 'tcx> TransItem<'tcx> { TransItem::Fn(ref instance) => { instance.substs.types().next().is_some() } - TransItem::DropGlue(..) | TransItem::Static(..) => false, } } @@ -276,7 +218,6 @@ impl<'a, 'tcx> TransItem<'tcx> { let def_id = match *self { TransItem::Fn(ref instance) => instance.def_id(), TransItem::Static(node_id) => tcx.hir.local_def_id(node_id), - TransItem::DropGlue(..) => return None, }; let attributes = tcx.get_attrs(def_id); @@ -300,16 +241,6 @@ impl<'a, 'tcx> TransItem<'tcx> { let hir_map = &tcx.hir; return match *self { - TransItem::DropGlue(dg) => { - let mut s = String::with_capacity(32); - match dg { - DropGlueKind::Ty(_) => s.push_str("drop-glue "), - DropGlueKind::TyContents(_) => s.push_str("drop-glue-contents "), - }; - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_type_name(dg.ty(), &mut s); - s - } TransItem::Fn(instance) => { to_string_internal(tcx, "fn ", instance) }, @@ -334,13 +265,6 @@ impl<'a, 'tcx> TransItem<'tcx> { pub fn to_raw_string(&self) -> String { match *self { - TransItem::DropGlue(dg) => { - let prefix = match dg { - DropGlueKind::Ty(_) => "Ty", - DropGlueKind::TyContents(_) => "TyContents", - }; - format!("DropGlue({}: {})", prefix, dg.ty() as *const _ as usize) - } TransItem::Fn(instance) => { format!("Fn({:?}, {})", instance.def, diff --git a/src/librustc_trans/tvec.rs b/src/librustc_trans/tvec.rs index cbcbb02bdc890..4216a73a8dd85 100644 --- a/src/librustc_trans/tvec.rs +++ b/src/librustc_trans/tvec.rs @@ -10,7 +10,7 @@ use llvm; use builder::Builder; -use llvm::ValueRef; +use llvm::{BasicBlockRef, ValueRef}; use common::*; use rustc::ty::Ty; @@ -20,7 +20,7 @@ pub fn slice_for_each<'a, 'tcx, F>( unit_ty: Ty<'tcx>, len: ValueRef, f: F -) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef) { +) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef, BasicBlockRef) { // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890) let zst = type_is_zero_size(bcx.ccx, unit_ty); let add = |bcx: &Builder, a, b| if zst { @@ -46,9 +46,8 @@ pub fn slice_for_each<'a, 'tcx, F>( let keep_going = header_bcx.icmp(llvm::IntNE, current, end); header_bcx.cond_br(keep_going, body_bcx.llbb(), next_bcx.llbb()); - f(&body_bcx, if zst { data_ptr } else { current }); let next = add(&body_bcx, current, C_uint(bcx.ccx, 1usize)); + f(&body_bcx, if zst { data_ptr } else { current }, header_bcx.llbb()); header_bcx.add_incoming_to_phi(current, next, body_bcx.llbb()); - body_bcx.br(header_bcx.llbb()); next_bcx } diff --git a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs index ada1234b852a1..eb4f9e8e28e2d 100644 --- a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs +++ b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs @@ -30,5 +30,3 @@ fn main() // This should not introduce a codegen item let _ = cgu_generic_function::exported_but_not_generic(3); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs index db940b680473a..d8e6028b799fb 100644 --- a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -11,8 +11,7 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager -//~ TRANS_ITEM drop-glue drop_in_place_intrinsic::StructWithDtor[0] -//~ TRANS_ITEM drop-glue-contents drop_in_place_intrinsic::StructWithDtor[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic.cgu-0[Internal] struct StructWithDtor(u32); impl Drop for StructWithDtor { @@ -23,7 +22,7 @@ impl Drop for StructWithDtor { //~ TRANS_ITEM fn drop_in_place_intrinsic::main[0] fn main() { - //~ TRANS_ITEM drop-glue [drop_in_place_intrinsic::StructWithDtor[0]; 2] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic.cgu-0[Internal] let x = [StructWithDtor(0), StructWithDtor(1)]; drop_slice_in_place(&x); @@ -35,7 +34,7 @@ fn drop_slice_in_place(x: &[StructWithDtor]) { // This is the interesting thing in this test case: Normally we would // not have drop-glue for the unsized [StructWithDtor]. This has to be // generated though when the drop_in_place() intrinsic is used. - //~ TRANS_ITEM drop-glue [drop_in_place_intrinsic::StructWithDtor[0]] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic.cgu-0[Internal] ::std::ptr::drop_in_place(x as *const _ as *mut [StructWithDtor]); } } diff --git a/src/test/codegen-units/item-collection/function-as-argument.rs b/src/test/codegen-units/item-collection/function-as-argument.rs index 51df38cabef30..c4aed7465bcb0 100644 --- a/src/test/codegen-units/item-collection/function-as-argument.rs +++ b/src/test/codegen-units/item-collection/function-as-argument.rs @@ -44,5 +44,3 @@ fn main() { //~ TRANS_ITEM fn function_as_argument::function[0] take_fn_pointer(function, 0f32, 0i64); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/generic-drop-glue.rs b/src/test/codegen-units/item-collection/generic-drop-glue.rs index 6da8154540574..06e02b100152e 100644 --- a/src/test/codegen-units/item-collection/generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/generic-drop-glue.rs @@ -45,8 +45,7 @@ enum EnumNoDrop { struct NonGenericNoDrop(i32); struct NonGenericWithDrop(i32); -//~ TRANS_ITEM drop-glue generic_drop_glue::NonGenericWithDrop[0] -//~ TRANS_ITEM drop-glue-contents generic_drop_glue::NonGenericWithDrop[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue.cgu-0[Internal] impl Drop for NonGenericWithDrop { //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] @@ -55,13 +54,11 @@ impl Drop for NonGenericWithDrop { //~ TRANS_ITEM fn generic_drop_glue::main[0] fn main() { - //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0] - //~ TRANS_ITEM drop-glue-contents generic_drop_glue::StructWithDrop[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0] let _ = StructWithDrop { x: 0i8, y: 'a' }.x; - //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> - //~ TRANS_ITEM drop-glue-contents generic_drop_glue::StructWithDrop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; @@ -70,19 +67,17 @@ fn main() { // This is supposed to generate drop-glue because it contains a field that // needs to be dropped. - //~ TRANS_ITEM drop-glue generic_drop_glue::StructNoDrop[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; - //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0] - //~ TRANS_ITEM drop-glue-contents generic_drop_glue::EnumWithDrop[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::A::(0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as i32 }; - //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0] - //~ TRANS_ITEM drop-glue-contents generic_drop_glue::EnumWithDrop[0] + //~TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::B::(1.0) { EnumWithDrop::A(x) => x, @@ -99,5 +94,3 @@ fn main() { EnumNoDrop::B(x) => x as f64 }; } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs index ad466671cf79b..9c6bdb6624eae 100644 --- a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs @@ -31,12 +31,13 @@ impl Trait for Struct { fn main() { let s1 = Struct { _a: 0u32 }; - //~ TRANS_ITEM drop-glue i8 + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable.cgu-0[Internal] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; let s1 = Struct { _a: 0u64 }; + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable.cgu-0[Internal] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; diff --git a/src/test/codegen-units/item-collection/items-within-generic-items.rs b/src/test/codegen-units/item-collection/items-within-generic-items.rs index a2dcd81b6750c..75d842d3c0bfc 100644 --- a/src/test/codegen-units/item-collection/items-within-generic-items.rs +++ b/src/test/codegen-units/item-collection/items-within-generic-items.rs @@ -40,5 +40,3 @@ fn main() { //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] let _ = generic_fn(0i8); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs index 91be81a0b8996..5f70ff396ddd5 100644 --- a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs @@ -13,8 +13,7 @@ #![deny(dead_code)] -//~ TRANS_ITEM drop-glue non_generic_drop_glue::StructWithDrop[0] -//~ TRANS_ITEM drop-glue-contents non_generic_drop_glue::StructWithDrop[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue.cgu-0[Internal] struct StructWithDrop { x: i32 } @@ -28,8 +27,7 @@ struct StructNoDrop { x: i32 } -//~ TRANS_ITEM drop-glue non_generic_drop_glue::EnumWithDrop[0] -//~ TRANS_ITEM drop-glue-contents non_generic_drop_glue::EnumWithDrop[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue.cgu-0[Internal] enum EnumWithDrop { A(i32) } @@ -54,5 +52,3 @@ fn main() { EnumNoDrop::A(x) => x }; } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/non-generic-functions.rs b/src/test/codegen-units/item-collection/non-generic-functions.rs index 4e2a7c8508468..26f9eb11876cb 100644 --- a/src/test/codegen-units/item-collection/non-generic-functions.rs +++ b/src/test/codegen-units/item-collection/non-generic-functions.rs @@ -77,5 +77,3 @@ fn main() { let x = Struct { _x: 0 }; x.bar(); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/overloaded-operators.rs b/src/test/codegen-units/item-collection/overloaded-operators.rs index 0295311334b6b..05848a727e951 100644 --- a/src/test/codegen-units/item-collection/overloaded-operators.rs +++ b/src/test/codegen-units/item-collection/overloaded-operators.rs @@ -68,5 +68,3 @@ impl Deref for Equatable { &self.0 } } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/static-init.rs b/src/test/codegen-units/item-collection/static-init.rs index 41c0f46f80bfb..3c9dcf32e0c78 100644 --- a/src/test/codegen-units/item-collection/static-init.rs +++ b/src/test/codegen-units/item-collection/static-init.rs @@ -20,4 +20,3 @@ pub fn foo() { } fn main() { } //~ TRANS_ITEM fn static_init::main[0] -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/statics-and-consts.rs b/src/test/codegen-units/item-collection/statics-and-consts.rs index 7c8b2b117ef7c..89bc620b7c552 100644 --- a/src/test/codegen-units/item-collection/statics-and-consts.rs +++ b/src/test/codegen-units/item-collection/statics-and-consts.rs @@ -60,5 +60,3 @@ fn main() { //~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[2] //~ TRANS_ITEM fn statics_and_consts::main[0] - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/trait-implementations.rs b/src/test/codegen-units/item-collection/trait-implementations.rs index 2eb2212f0cacd..e8a7d8f25b22c 100644 --- a/src/test/codegen-units/item-collection/trait-implementations.rs +++ b/src/test/codegen-units/item-collection/trait-implementations.rs @@ -78,5 +78,3 @@ fn main() { //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<&str, &str> 0f32.bar("&str", "&str"); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/trait-method-as-argument.rs b/src/test/codegen-units/item-collection/trait-method-as-argument.rs index f7afd3f0891e3..f095b637a84e4 100644 --- a/src/test/codegen-units/item-collection/trait-method-as-argument.rs +++ b/src/test/codegen-units/item-collection/trait-method-as-argument.rs @@ -64,5 +64,3 @@ fn main() { //~ TRANS_ITEM fn core::ops[0]::FnMut[0]::call_mut[0] u32, (u32)> take_foo_mut(Trait::foo, 'c'); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/trait-method-default-impl.rs b/src/test/codegen-units/item-collection/trait-method-default-impl.rs index 47892781902ea..5b24a219f354b 100644 --- a/src/test/codegen-units/item-collection/trait-method-default-impl.rs +++ b/src/test/codegen-units/item-collection/trait-method-default-impl.rs @@ -66,5 +66,3 @@ fn main() { //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0u32.bar(0i16, ()); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/transitive-drop-glue.rs b/src/test/codegen-units/item-collection/transitive-drop-glue.rs index 81a7059fe209f..e41cb34eec6ab 100644 --- a/src/test/codegen-units/item-collection/transitive-drop-glue.rs +++ b/src/test/codegen-units/item-collection/transitive-drop-glue.rs @@ -13,12 +13,11 @@ #![deny(dead_code)] -//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] struct Root(Intermediate); -//~ TRANS_ITEM drop-glue transitive_drop_glue::Intermediate[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] struct Intermediate(Leaf); -//~ TRANS_ITEM drop-glue transitive_drop_glue::Leaf[0] -//~ TRANS_ITEM drop-glue-contents transitive_drop_glue::Leaf[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] struct Leaf; impl Drop for Leaf { @@ -39,17 +38,15 @@ fn main() { let _ = Root(Intermediate(Leaf)); - //~ TRANS_ITEM drop-glue transitive_drop_glue::RootGen[0] - //~ TRANS_ITEM drop-glue transitive_drop_glue::IntermediateGen[0] - //~ TRANS_ITEM drop-glue transitive_drop_glue::LeafGen[0] - //~ TRANS_ITEM drop-glue-contents transitive_drop_glue::LeafGen[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0u32))); - //~ TRANS_ITEM drop-glue transitive_drop_glue::RootGen[0] - //~ TRANS_ITEM drop-glue transitive_drop_glue::IntermediateGen[0] - //~ TRANS_ITEM drop-glue transitive_drop_glue::LeafGen[0] - //~ TRANS_ITEM drop-glue-contents transitive_drop_glue::LeafGen[0] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0i16))); } diff --git a/src/test/codegen-units/item-collection/tuple-drop-glue.rs b/src/test/codegen-units/item-collection/tuple-drop-glue.rs index ef4bc1dca594c..39043cf87cbec 100644 --- a/src/test/codegen-units/item-collection/tuple-drop-glue.rs +++ b/src/test/codegen-units/item-collection/tuple-drop-glue.rs @@ -13,8 +13,7 @@ #![deny(dead_code)] -//~ TRANS_ITEM drop-glue tuple_drop_glue::Dropped[0] -//~ TRANS_ITEM drop-glue-contents tuple_drop_glue::Dropped[0] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue.cgu-0[Internal] struct Dropped; impl Drop for Dropped { @@ -24,10 +23,10 @@ impl Drop for Dropped { //~ TRANS_ITEM fn tuple_drop_glue::main[0] fn main() { - //~ TRANS_ITEM drop-glue (u32, tuple_drop_glue::Dropped[0]) + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue.cgu-0[Internal] let x = (0u32, Dropped); - //~ TRANS_ITEM drop-glue (i16, (tuple_drop_glue::Dropped[0], bool)) - //~ TRANS_ITEM drop-glue (tuple_drop_glue::Dropped[0], bool) + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue.cgu-0[Internal] let x = (0i16, (Dropped, true)); } diff --git a/src/test/codegen-units/item-collection/unsizing.rs b/src/test/codegen-units/item-collection/unsizing.rs index cd4cc258f7a68..de7613741b27b 100644 --- a/src/test/codegen-units/item-collection/unsizing.rs +++ b/src/test/codegen-units/item-collection/unsizing.rs @@ -57,11 +57,13 @@ fn main() { // simple case let bool_sized = &true; - //~ TRANS_ITEM drop-glue i8 + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[0]::foo[0] let _bool_unsized = bool_sized as &Trait; - let char_sized = &true; + let char_sized = &'a'; + + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[1]::foo[0] let _char_unsized = char_sized as &Trait; @@ -71,11 +73,13 @@ fn main() _b: 2, _c: 3.0f64 }; + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[2]::foo[0] let _struct_unsized = struct_sized as &Struct; // custom coercion let wrapper_sized = Wrapper(&0u32); + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[3]::foo[0] let _wrapper_sized = wrapper_sized as Wrapper; } diff --git a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs index 8689beb3fb77e..ce85c4fc13ce2 100644 --- a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs +++ b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs @@ -86,4 +86,3 @@ impl NonGeneric { // Only the non-generic methods should be instantiated: //~ TRANS_ITEM fn unused_traits_and_generics::{{impl}}[3]::foo[0] -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/extern-drop-glue.rs b/src/test/codegen-units/partitioning/extern-drop-glue.rs index 910ffd2959ed0..f28c4872111c9 100644 --- a/src/test/codegen-units/partitioning/extern-drop-glue.rs +++ b/src/test/codegen-units/partitioning/extern-drop-glue.rs @@ -20,15 +20,14 @@ // aux-build:cgu_extern_drop_glue.rs extern crate cgu_extern_drop_glue; -//~ TRANS_ITEM drop-glue cgu_extern_drop_glue::Struct[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] -//~ TRANS_ITEM drop-glue-contents cgu_extern_drop_glue::Struct[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] struct LocalStruct(cgu_extern_drop_glue::Struct); //~ TRANS_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[External] fn user() { - //~ TRANS_ITEM drop-glue extern_drop_glue::LocalStruct[0] @@ extern_drop_glue[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } @@ -40,7 +39,7 @@ mod mod1 { //~ TRANS_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[External] fn user() { - //~ TRANS_ITEM drop-glue extern_drop_glue::mod1[0]::LocalStruct[0] @@ extern_drop_glue-mod1[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-mod1[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } } diff --git a/src/test/codegen-units/partitioning/extern-generic.rs b/src/test/codegen-units/partitioning/extern-generic.rs index db36b50702a43..e32c946f8554f 100644 --- a/src/test/codegen-units/partitioning/extern-generic.rs +++ b/src/test/codegen-units/partitioning/extern-generic.rs @@ -60,5 +60,3 @@ mod mod3 { // once for the current crate //~ TRANS_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function.volatile[External] //~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[External] - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/local-drop-glue.rs b/src/test/codegen-units/partitioning/local-drop-glue.rs index f61e3fe12931e..64f4f854c2d7f 100644 --- a/src/test/codegen-units/partitioning/local-drop-glue.rs +++ b/src/test/codegen-units/partitioning/local-drop-glue.rs @@ -16,8 +16,7 @@ #![allow(dead_code)] #![crate_type="lib"] -//~ TRANS_ITEM drop-glue local_drop_glue::Struct[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] -//~ TRANS_ITEM drop-glue-contents local_drop_glue::Struct[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] struct Struct { _a: u32 } @@ -27,7 +26,7 @@ impl Drop for Struct { fn drop(&mut self) {} } -//~ TRANS_ITEM drop-glue local_drop_glue::Outer[0] @@ local_drop_glue[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] struct Outer { _a: Struct } @@ -46,10 +45,10 @@ mod mod1 { use super::Struct; - //~ TRANS_ITEM drop-glue local_drop_glue::mod1[0]::Struct2[0] @@ local_drop_glue-mod1[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-mod1[Internal] struct Struct2 { _a: Struct, - //~ TRANS_ITEM drop-glue (u32, local_drop_glue::Struct[0]) @@ local_drop_glue-mod1[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, local_drop_glue::Struct[0])> @@ local_drop_glue-mod1[Internal] _b: (u32, Struct), } diff --git a/src/test/codegen-units/partitioning/regular-modules.rs b/src/test/codegen-units/partitioning/regular-modules.rs index 4da6411032168..07c341203f9e2 100644 --- a/src/test/codegen-units/partitioning/regular-modules.rs +++ b/src/test/codegen-units/partitioning/regular-modules.rs @@ -80,5 +80,3 @@ mod mod2 { static BAZ: u64 = 0; } } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/statics.rs b/src/test/codegen-units/partitioning/statics.rs index ffe1ec278b8dd..d06b3ac407a7f 100644 --- a/src/test/codegen-units/partitioning/statics.rs +++ b/src/test/codegen-units/partitioning/statics.rs @@ -46,5 +46,3 @@ mod mod1 { static BAR: u32 = 0; } } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/vtable-through-const.rs b/src/test/codegen-units/partitioning/vtable-through-const.rs index 7a0217072f32c..c4594bb547ef5 100644 --- a/src/test/codegen-units/partitioning/vtable-through-const.rs +++ b/src/test/codegen-units/partitioning/vtable-through-const.rs @@ -69,7 +69,7 @@ mod mod1 { //~ TRANS_ITEM fn vtable_through_const::main[0] @@ vtable_through_const[External] fn main() { - //~ TRANS_ITEM drop-glue i8 @@ vtable_through_const[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ vtable_through_const[Internal] // Since Trait1::do_something() is instantiated via its default implementation, // it is considered a generic and is instantiated here only because it is From 5dc8548050514b0781af7b21a4706552da18dd50 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 19 Mar 2017 17:51:27 +0200 Subject: [PATCH 11/11] update LLVM pick up a fix to LLVM PR29151. --- src/llvm | 2 +- src/rustllvm/llvm-rebuild-trigger | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm b/src/llvm index 859fb26936462..d5ef27a79661d 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit 859fb269364623b17e092efaba3f94e70ce97c5e +Subproject commit d5ef27a79661d4f0d57d7b7d2cdbe9204f790a4a diff --git a/src/rustllvm/llvm-rebuild-trigger b/src/rustllvm/llvm-rebuild-trigger index aeabf4a1dd376..be6d535dc732b 100644 --- a/src/rustllvm/llvm-rebuild-trigger +++ b/src/rustllvm/llvm-rebuild-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be (optionally) cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. -2017-03-04 +2017-03-19