From 1baabdbcc38a9809d380a59a5039b3195a6d6d8d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 14 May 2021 22:02:55 +0200 Subject: [PATCH 1/2] Prepare for type erasure. --- .../rustc_codegen_cranelift/src/abi/mod.rs | 9 ++++- compiler/rustc_codegen_cranelift/src/base.rs | 10 +++++- compiler/rustc_codegen_ssa/src/mir/block.rs | 18 ++++++---- compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_middle/src/mir/mod.rs | 5 +++ compiler/rustc_middle/src/mir/mono.rs | 1 + compiler/rustc_middle/src/mir/terminator.rs | 5 +-- .../rustc_middle/src/mir/type_foldable.rs | 3 +- compiler/rustc_middle/src/mir/visit.rs | 4 ++- compiler/rustc_middle/src/ty/fold.rs | 6 ++++ compiler/rustc_middle/src/ty/instance.rs | 23 +++++++++++++ compiler/rustc_middle/src/ty/layout.rs | 33 +++++++++++++++---- compiler/rustc_middle/src/ty/mod.rs | 1 + .../rustc_middle/src/ty/structural_impls.rs | 10 ++++-- .../src/borrow_check/invalidation.rs | 1 + compiler/rustc_mir/src/borrow_check/mod.rs | 1 + .../src/dataflow/framework/direction.rs | 10 +++++- .../src/dataflow/move_paths/builder.rs | 1 + .../rustc_mir/src/interpret/terminator.rs | 13 +++++++- .../rustc_mir/src/monomorphize/collector.rs | 18 +++++++--- .../src/monomorphize/partitioning/default.rs | 2 ++ compiler/rustc_mir/src/shim.rs | 7 ++++ compiler/rustc_mir/src/transform/dest_prop.rs | 1 + .../src/transform/function_item_references.rs | 1 + compiler/rustc_mir/src/transform/generator.rs | 1 + compiler/rustc_mir/src/transform/inline.rs | 1 + .../rustc_mir/src/transform/inline/cycle.rs | 1 + .../rustc_mir/src/transform/promote_consts.rs | 1 + .../rustc_mir/src/util/elaborate_drops.rs | 2 ++ .../rustc_mir_build/src/build/expr/into.rs | 1 + .../rustc_mir_build/src/build/matches/test.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/const_evaluatable.rs | 1 + .../clippy_utils/src/qualify_min_const_fn.rs | 1 + 34 files changed, 166 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 54c8fb0e7b80b..8889ecb3af5bc 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -298,6 +298,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( func: &Operand<'tcx>, args: &[Operand<'tcx>], destination: Option<(Place<'tcx>, BasicBlock)>, + erased: bool, ) { let fn_ty = fx.monomorphize(func.ty(fx.mir, fx.tcx)); let fn_sig = @@ -306,7 +307,13 @@ pub(crate) fn codegen_terminator_call<'tcx>( let destination = destination.map(|(place, bb)| (codegen_place(fx, place), bb)); // Handle special calls like instrinsics and empty drop glue. - let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() { + let instance = if erased { + if let ty::FnDef(def_id, substs) = *fn_ty.kind() { + Some(ty::Instance::resolve_erased(fx.tcx, def_id, substs)) + } else { + bug!() + } + } else if let ty::FnDef(def_id, substs) = *fn_ty.kind() { let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs) .unwrap() .unwrap() diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 3ec5c14ff17a2..055cc0f099746 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -354,9 +354,17 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { fn_span, cleanup: _, from_hir_call: _, + erased, } => { fx.tcx.sess.time("codegen call", || { - crate::abi::codegen_terminator_call(fx, *fn_span, func, args, *destination) + crate::abi::codegen_terminator_call( + fx, + *fn_span, + func, + args, + *destination, + *erased, + ) }); } TerminatorKind::InlineAsm { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 72e9163b88e21..6be79f3d2a12b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -498,6 +498,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, cleanup: Option, fn_span: Span, + erased: bool, ) { let source_info = terminator.source_info; let span = source_info.span; @@ -506,15 +507,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let callee = self.codegen_operand(&mut bx, func); let (instance, mut llfn) = match *callee.layout.ty.kind() { - ty::FnDef(def_id, substs) => ( - Some( + ty::FnDef(def_id, substs) => { + let instance = if erased { + ty::Instance::resolve_erased(bx.tcx(), def_id, substs) + } else { ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs) .unwrap() .unwrap() - .polymorphize(bx.tcx()), - ), - None, - ), + .polymorphize(bx.tcx()) + }; + (Some(instance), None) + } + _ if erased => bug!("{} is not erasable", callee.layout.ty), ty::FnPtr(_) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), }; @@ -971,6 +975,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cleanup, from_hir_call: _, fn_span, + erased, } => { self.codegen_call_terminator( helper, @@ -981,6 +986,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { destination, cleanup, fn_span, + erased, ); } mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } => { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a8719be84c2a4..928655223972a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -555,6 +555,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes, Testing: // ========================================================================== + rustc_attr!(TEST, rustc_dyn, AssumedUsed, template!(Word)), rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 1cc7f235d7d21..0fcbac6af07df 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -185,6 +185,9 @@ pub struct Body<'tcx> { pub generator: Option>>, + /// Erased version for dyn_erased. + pub dyn_erased_body: Option>>, + /// Declarations of locals. /// /// The first local is the return value pointer, followed by `arg_count` @@ -273,6 +276,7 @@ impl<'tcx> Body<'tcx> { generator_kind, }) }), + dyn_erased_body: None, local_decls, user_type_annotations, arg_count, @@ -300,6 +304,7 @@ impl<'tcx> Body<'tcx> { basic_blocks, source_scopes: IndexVec::new(), generator: None, + dyn_erased_body: None, local_decls: IndexVec::new(), user_type_annotations: IndexVec::new(), arg_count: 0, diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 67440e6e0edf0..c0d16d658f756 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -353,6 +353,7 @@ impl<'tcx> CodegenUnit<'tcx> { .map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)), InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) + | InstanceDef::ErasedShim(..) | InstanceDef::Intrinsic(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index c8db4aeb449b8..634ef2b1c2cbc 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -190,6 +190,7 @@ pub enum TerminatorKind<'tcx> { /// This `Span` is the span of the function, without the dot and receiver /// (e.g. `foo(a, b)` in `x.foo(a, b)` fn_span: Span, + erased: bool, }, /// Jump to the target if the condition has the expected value, @@ -470,11 +471,11 @@ impl<'tcx> TerminatorKind<'tcx> { DropAndReplace { place, value, .. } => { write!(fmt, "replace({:?} <- {:?})", place, value) } - Call { func, args, destination, .. } => { + Call { func, args, destination, erased, .. } => { if let Some((destination, _)) = destination { write!(fmt, "{:?} = ", destination)?; } - write!(fmt, "{:?}(", func)?; + write!(fmt, "{:?}{}(", func, if *erased { "[erased]" } else { "" })?; for (index, arg) in args.iter().enumerate() { if index > 0 { write!(fmt, ", ")?; diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index f3124e5bf424e..bf535743663e8 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -41,7 +41,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { resume_arg: resume_arg.fold_with(folder), drop, }, - Call { func, args, destination, cleanup, from_hir_call, fn_span } => { + Call { func, args, destination, cleanup, from_hir_call, fn_span, erased } => { let dest = destination.map(|(loc, dest)| (loc.fold_with(folder), dest)); Call { @@ -51,6 +51,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { cleanup, from_hir_call, fn_span, + erased, } } Assert { cond, expected, msg, target, cleanup } => { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 5516a045c1db0..38038ce6092f8 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -347,6 +347,7 @@ macro_rules! make_mir_visitor { ty::InstanceDef::Intrinsic(_def_id) | ty::InstanceDef::VtableShim(_def_id) | ty::InstanceDef::ReifyShim(_def_id) | + ty::InstanceDef::ErasedShim(_def_id) | ty::InstanceDef::Virtual(_def_id, _) | ty::InstanceDef::ClosureOnceShim { call_once: _def_id } | ty::InstanceDef::DropGlue(_def_id, None) => {} @@ -535,7 +536,8 @@ macro_rules! make_mir_visitor { destination, cleanup: _, from_hir_call: _, - fn_span: _ + fn_span: _, + erased: _ } => { self.visit_operand(func, location); for arg in args { diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index eb6d163312c83..78874219578a6 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -86,6 +86,12 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn references_error(&self) -> bool { self.has_type_flags(TypeFlags::HAS_ERROR) } + fn has_param_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_PARAM) + } + fn has_param_consts(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_CT_PARAM) + } fn has_param_types_or_consts(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM) } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 41d953216e0dd..764ff3a7d1da7 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -60,6 +60,9 @@ pub enum InstanceDef<'tcx> { /// (the definition of the function itself). ReifyShim(DefId), + /// Compiler-generated untyped version of an item. + ErasedShim(DefId), + /// `::call_*` (generated `FnTrait` implementation for `fn()` pointers). /// /// `DefId` is `FnTrait::call_*`. @@ -143,6 +146,7 @@ impl<'tcx> InstanceDef<'tcx> { InstanceDef::Item(def) => def.did, InstanceDef::VtableShim(def_id) | InstanceDef::ReifyShim(def_id) + | InstanceDef::ErasedShim(def_id) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) @@ -158,6 +162,7 @@ impl<'tcx> InstanceDef<'tcx> { InstanceDef::Item(def) => def, InstanceDef::VtableShim(def_id) | InstanceDef::ReifyShim(def_id) + | InstanceDef::ErasedShim(def_id) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) @@ -244,6 +249,7 @@ impl<'tcx> InstanceDef<'tcx> { match *self { InstanceDef::CloneShim(..) | InstanceDef::FnPtrShim(..) + | InstanceDef::ErasedShim(..) | InstanceDef::DropGlue(_, Some(_)) => false, InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) @@ -269,6 +275,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::Item(_) => Ok(()), InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), + InstanceDef::ErasedShim(_) => write!(f, " - shim(erased)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty), @@ -436,6 +443,22 @@ impl<'tcx> Instance<'tcx> { Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() } + pub fn resolve_erased( + tcx: TyCtxt<'tcx>, + def_id: DefId, + _substs: SubstsRef<'tcx>, + ) -> Instance<'tcx> { + let substs = InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } => tcx.types.u8.into(), + ty::GenericParamDefKind::Const { .. } => { + panic!("Unsupported type-erased const params") + } + }); + let def = ty::InstanceDef::ErasedShim(def_id); + ty::Instance { def, substs } + } + pub fn fn_once_adapter_instance( tcx: TyCtxt<'tcx>, closure_did: DefId, diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index c2e9dba6c8e8a..90cd924fcf9b3 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2455,18 +2455,26 @@ impl<'tcx> ty::Instance<'tcx> { // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function. let ty = self.ty(tcx, ty::ParamEnv::reveal_all()); match *ty.kind() { - ty::FnDef(..) => { + ty::FnDef(def_id, substs) => { // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering // parameters unused if they show up in the signature, but not in the `mir::Body` // (i.e. due to being inside a projection that got normalized, see // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping // track of a polymorphization `ParamEnv` to allow normalizing later. - let mut sig = match *ty.kind() { - ty::FnDef(def_id, substs) => tcx - .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) - .subst(tcx, substs), - _ => unreachable!(), - }; + let mut sig = tcx.fn_sig(def_id); + if let ty::InstanceDef::ErasedShim(..) = self.def { + let mir = tcx.optimized_mir(def_id); + let inner_mir = &mir.dyn_erased_body.as_ref().unwrap(); + sig = sig.map_bound(|mut sig| { + let mut inputs_and_output: Vec<_> = + inner_mir.args_iter().map(|l| inner_mir.local_decls[l].ty).collect(); + inputs_and_output.push(inner_mir.return_ty()); + sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output[..]); + sig + }); + } + let mut sig = + tcx.normalize_erasing_regions(tcx.param_env(def_id), sig).subst(tcx, substs); if let ty::InstanceDef::VtableShim(..) = self.def { // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. @@ -2665,6 +2673,17 @@ where None }; + let extra_args = if extra_args.is_empty() { + extra_args + } else if let ty::InstanceDef::ErasedShim(def_id) = instance.def { + // Remove extra arguments between Item and ErasedShim. + let raw_sig = cx.tcx().fn_sig(def_id); + let additions = sig.inputs().skip_binder().len() - raw_sig.inputs().skip_binder().len(); + &extra_args[additions..] + } else { + extra_args + }; + let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()).flags; call::FnAbi::new_internal( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index af49533753fae..4bd65217a6c24 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1788,6 +1788,7 @@ impl<'tcx> TyCtxt<'tcx> { }, ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::ErasedShim(..) | ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Virtual(..) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 7290c41d615df..9b9901707c6b1 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -626,6 +626,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), + ty::InstanceDef::ErasedShim(def_id) => Some(ty::InstanceDef::ErasedShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::FnPtrShim(def_id, ty) => { Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) @@ -814,6 +815,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { Item(def) => Item(def.fold_with(folder)), VtableShim(did) => VtableShim(did.fold_with(folder)), ReifyShim(did) => ReifyShim(did.fold_with(folder)), + ErasedShim(did) => ErasedShim(did.fold_with(folder)), Intrinsic(did) => Intrinsic(did.fold_with(folder)), FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), Virtual(did, i) => Virtual(did.fold_with(folder), i), @@ -831,9 +833,11 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { self.substs.visit_with(visitor)?; match self.def { Item(def) => def.visit_with(visitor), - VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { - did.visit_with(visitor) - } + VtableShim(did) + | ReifyShim(did) + | ErasedShim(did) + | Intrinsic(did) + | Virtual(did, _) => did.visit_with(visitor), FnPtrShim(did, ty) | CloneShim(did, ty) => { did.visit_with(visitor)?; ty.visit_with(visitor) diff --git a/compiler/rustc_mir/src/borrow_check/invalidation.rs b/compiler/rustc_mir/src/borrow_check/invalidation.rs index e621bafb671bb..49c2454a298db 100644 --- a/compiler/rustc_mir/src/borrow_check/invalidation.rs +++ b/compiler/rustc_mir/src/borrow_check/invalidation.rs @@ -154,6 +154,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { cleanup: _, from_hir_call: _, fn_span: _, + erased: _, } => { self.consume_operand(location, func); for arg in args { diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 4c35be39a3d33..33adff7777a65 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -698,6 +698,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc cleanup: _, from_hir_call: _, fn_span: _, + erased: _, } => { self.consume_operand(loc, (func, span), flow_state); for arg in args { diff --git a/compiler/rustc_mir/src/dataflow/framework/direction.rs b/compiler/rustc_mir/src/dataflow/framework/direction.rs index 8a9ced91eb376..9835d997e79d3 100644 --- a/compiler/rustc_mir/src/dataflow/framework/direction.rs +++ b/compiler/rustc_mir/src/dataflow/framework/direction.rs @@ -467,7 +467,15 @@ impl Direction for Forward { propagate(target, exit_state); } - Call { cleanup, destination, ref func, ref args, from_hir_call: _, fn_span: _ } => { + Call { + cleanup, + destination, + ref func, + ref args, + from_hir_call: _, + fn_span: _, + erased: _, + } => { if let Some(unwind) = cleanup { if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) { propagate(unwind, exit_state); diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs index 994b403abf3bc..02b9da6a12f91 100644 --- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs +++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs @@ -405,6 +405,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { cleanup: _, from_hir_call: _, fn_span: _, + erased: _, } => { self.gather_operand(func); for arg in args { diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index 4aa1360d53539..d2ad30521c65d 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -51,7 +51,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.go_to_block(target_block); } - Call { ref func, ref args, destination, ref cleanup, from_hir_call: _, fn_span: _ } => { + Call { + ref func, + ref args, + destination, + ref cleanup, + from_hir_call: _, + fn_span: _, + erased: _, + } => { let old_stack = self.frame_idx(); let old_loc = self.frame().loc; let func = self.eval_operand(func, None)?; @@ -425,6 +433,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // recurse with concrete function self.eval_fn_call(drop_fn, caller_abi, &args, ret, unwind) } + ty::InstanceDef::ErasedShim(_) => { + bug!("Interpreting type-erased functions is not implemented.") + } } } diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index fdefc89067477..1ea18e75f1655 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -597,7 +597,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { ) => { let fn_ty = operand.ty(self.body, self.tcx); let fn_ty = self.monomorphize(fn_ty); - visit_fn_use(self.tcx, fn_ty, false, span, &mut self.output); + visit_fn_use(self.tcx, fn_ty, false, false, span, &mut self.output); } mir::Rvalue::Cast( mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)), @@ -710,10 +710,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let tcx = self.tcx; match terminator.kind { - mir::TerminatorKind::Call { ref func, .. } => { + mir::TerminatorKind::Call { ref func, erased, .. } => { let callee_ty = func.ty(self.body, tcx); let callee_ty = self.monomorphize(callee_ty); - visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output); + visit_fn_use(self.tcx, callee_ty, true, erased, source, &mut self.output); } mir::TerminatorKind::Drop { ref place, .. } | mir::TerminatorKind::DropAndReplace { ref place, .. } => { @@ -726,7 +726,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { match *op { mir::InlineAsmOperand::SymFn { ref value } => { let fn_ty = self.monomorphize(value.literal.ty()); - visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output); + visit_fn_use(self.tcx, fn_ty, false, false, source, &mut self.output); } mir::InlineAsmOperand::SymStatic { def_id } => { let instance = Instance::mono(self.tcx, def_id); @@ -815,15 +815,20 @@ fn visit_drop_use<'tcx>( visit_instance_use(tcx, instance, is_direct_call, source, output); } +#[instrument(skip(tcx))] fn visit_fn_use<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, is_direct_call: bool, + erased: bool, source: Span, output: &mut Vec>>, ) { if let ty::FnDef(def_id, substs) = *ty.kind() { - let instance = if is_direct_call { + let instance = if erased { + debug_assert!(is_direct_call); + ty::Instance::resolve_erased(tcx, def_id, substs) + } else if is_direct_call { ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() } else { ty::Instance::resolve_for_fn_ptr(tcx, ty::ParamEnv::reveal_all(), def_id, substs) @@ -833,6 +838,7 @@ fn visit_fn_use<'tcx>( } } +#[instrument(skip(tcx))] fn visit_instance_use<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, @@ -860,6 +866,7 @@ fn visit_instance_use<'tcx>( ty::InstanceDef::DropGlue(_, Some(_)) | ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::ErasedShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) @@ -878,6 +885,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> ty::InstanceDef::DropGlue(def_id, Some(_)) => def_id, ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::ErasedShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Virtual(..) | ty::InstanceDef::FnPtrShim(..) diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs index edd46310f20d4..3ea0684df55c7 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs @@ -273,6 +273,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( ty::InstanceDef::Item(def) => def.did, ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::ErasedShim(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Intrinsic(..) @@ -422,6 +423,7 @@ fn mono_item_visibility( // These are all compiler glue and such, never exported, always hidden. InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) + | InstanceDef::ErasedShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::Intrinsic(..) diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs index 796d024771d7f..e3e02e06b7d03 100644 --- a/compiler/rustc_mir/src/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -53,6 +53,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::ReifyShim(def_id) => { build_call_shim(tcx, instance, None, CallKind::Direct(def_id)) } + ty::InstanceDef::ErasedShim(def_id) => { + let orig_mir = tcx.optimized_mir(def_id); + let sub_mir = orig_mir.dyn_erased_body.as_deref().unwrap(); + sub_mir.clone() + } ty::InstanceDef::ClosureOnceShim { call_once: _ } => { let fn_mut = tcx.require_lang_item(LangItem::FnMut, None); let call_mut = tcx @@ -445,6 +450,7 @@ impl CloneShimBuilder<'tcx> { cleanup: Some(cleanup), from_hir_call: true, fn_span: self.span, + erased: false, }, false, ); @@ -823,6 +829,7 @@ fn build_call_shim<'tcx>( }, from_hir_call: true, fn_span: span, + erased: false, }, false, ); diff --git a/compiler/rustc_mir/src/transform/dest_prop.rs b/compiler/rustc_mir/src/transform/dest_prop.rs index 29df86ca6cdb7..b3abf2383685f 100644 --- a/compiler/rustc_mir/src/transform/dest_prop.rs +++ b/compiler/rustc_mir/src/transform/dest_prop.rs @@ -628,6 +628,7 @@ impl Conflicts<'a> { cleanup: _, from_hir_call: _, fn_span: _, + erased: _, } => { // No arguments may overlap with the destination. for arg in args.iter().chain(Some(func)) { diff --git a/compiler/rustc_mir/src/transform/function_item_references.rs b/compiler/rustc_mir/src/transform/function_item_references.rs index 8d02ac6d9b774..d400fdbe42e71 100644 --- a/compiler/rustc_mir/src/transform/function_item_references.rs +++ b/compiler/rustc_mir/src/transform/function_item_references.rs @@ -39,6 +39,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> { cleanup: _, from_hir_call: _, fn_span: _, + erased: _, } = &terminator.kind { let source_info = *self.body.source_info(location); diff --git a/compiler/rustc_mir/src/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs index 003003a8abbea..b46929e04d68f 100644 --- a/compiler/rustc_mir/src/transform/generator.rs +++ b/compiler/rustc_mir/src/transform/generator.rs @@ -1471,6 +1471,7 @@ impl Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { cleanup: _, from_hir_call: _, fn_span: _, + erased: _, } => { self.check_assigned_place(*dest, |this| { this.visit_operand(func, location); diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index b6f80763bc8c4..78c95877c23cd 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -193,6 +193,7 @@ impl Inliner<'tcx> { // inlining a second time. InstanceDef::VtableShim(_) | InstanceDef::ReifyShim(_) + | InstanceDef::ErasedShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) diff --git a/compiler/rustc_mir/src/transform/inline/cycle.rs b/compiler/rustc_mir/src/transform/inline/cycle.rs index 295f3ec70dc78..cc981e5b3697c 100644 --- a/compiler/rustc_mir/src/transform/inline/cycle.rs +++ b/compiler/rustc_mir/src/transform/inline/cycle.rs @@ -80,6 +80,7 @@ crate fn mir_callgraph_reachable( // a cycle that way InstanceDef::VtableShim(_) | InstanceDef::ReifyShim(_) + | InstanceDef::ErasedShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::CloneShim(..) => {} diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index 1bbaf833c4fd9..a68019b35c164 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -936,6 +936,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { destination: Some((Place::from(new_temp), new_target)), from_hir_call, fn_span, + erased: false, }, source_info: SourceInfo::outermost(terminator.source_info.span), ..terminator diff --git a/compiler/rustc_mir/src/util/elaborate_drops.rs b/compiler/rustc_mir/src/util/elaborate_drops.rs index e9190d7ebef8b..4dfdd24c1baeb 100644 --- a/compiler/rustc_mir/src/util/elaborate_drops.rs +++ b/compiler/rustc_mir/src/util/elaborate_drops.rs @@ -635,6 +635,7 @@ where cleanup: unwind.into_option(), from_hir_call: true, fn_span: self.source_info.span, + erased: false, }, source_info: self.source_info, }), @@ -972,6 +973,7 @@ where cleanup: None, from_hir_call: false, fn_span: self.source_info.span, + erased: false, }; // FIXME(#43234) let free_block = self.new_block(unwind, call); diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 1e7ed3d95d236..9b1c6435d103e 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -223,6 +223,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, from_hir_call, fn_span, + erased: false, }, ); this.diverge_from(block); diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index b082169cd63c1..ebbd2b7470e4b 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -445,6 +445,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { cleanup: None, from_hir_call: false, fn_span: source_info.span, + erased: false, }, ); self.diverge_from(block); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4c80b84e3d275..13f0f301c230e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1009,6 +1009,7 @@ symbols! { rustc_dump_env_program_clauses, rustc_dump_program_clauses, rustc_dump_user_substs, + rustc_dyn, rustc_error, rustc_expected_cgu_reuse, rustc_if_this_changed, diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 8961cdaebf345..bf7ac62315ffc 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -448,6 +448,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { // This is currently fairly irrelevant as it requires `const Trait`s. from_hir_call: true, fn_span, + erased: _, } => { let local = self.place_to_local(fn_span, place)?; let func = self.operand_to_node(fn_span, func)?; diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index a08dcf19e5b51..c23a85d61167f 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -313,6 +313,7 @@ fn check_terminator( destination: _, cleanup: _, fn_span: _, + erased: _, } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { From 25370adae6c361918aa2130d3d35c8e359b8c98b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 14 May 2021 23:37:46 +0200 Subject: [PATCH 2/2] Add dyn_erased pass. --- .../rustc_mir/src/transform/dyn_erased.rs | 667 ++++++++++++++++++ compiler/rustc_mir/src/transform/mod.rs | 2 + ...dyn_erased.assoc_const.DynErased.after.mir | 24 + ...erased.assoc_const.DynErasedBody.after.mir | 10 + .../dyn_erased.coreturn.DynErased.after.mir | 34 + ...yn_erased.coreturn.DynErasedBody.after.mir | 32 + .../dyn_erased.dropping.DynErased.after.mir | 29 + ...yn_erased.dropping.DynErasedBody.after.mir | 21 + .../dyn_erased.good_niche.DynErased.after.mir | 32 + ..._erased.good_niche.DynErasedBody.after.mir | 33 + .../dyn_erased.normal.DynErased.after.mir | 36 + .../dyn_erased.normal.DynErasedBody.after.mir | 51 ++ src/test/mir-opt/dyn_erased.rs | 81 +++ src/test/ui/dyn_erased.rs | 32 + src/test/ui/dyn_erased.stderr | 20 + 15 files changed, 1104 insertions(+) create mode 100644 compiler/rustc_mir/src/transform/dyn_erased.rs create mode 100644 src/test/mir-opt/dyn_erased.assoc_const.DynErased.after.mir create mode 100644 src/test/mir-opt/dyn_erased.assoc_const.DynErasedBody.after.mir create mode 100644 src/test/mir-opt/dyn_erased.coreturn.DynErased.after.mir create mode 100644 src/test/mir-opt/dyn_erased.coreturn.DynErasedBody.after.mir create mode 100644 src/test/mir-opt/dyn_erased.dropping.DynErased.after.mir create mode 100644 src/test/mir-opt/dyn_erased.dropping.DynErasedBody.after.mir create mode 100644 src/test/mir-opt/dyn_erased.good_niche.DynErased.after.mir create mode 100644 src/test/mir-opt/dyn_erased.good_niche.DynErasedBody.after.mir create mode 100644 src/test/mir-opt/dyn_erased.normal.DynErased.after.mir create mode 100644 src/test/mir-opt/dyn_erased.normal.DynErasedBody.after.mir create mode 100644 src/test/mir-opt/dyn_erased.rs create mode 100644 src/test/ui/dyn_erased.rs create mode 100644 src/test/ui/dyn_erased.stderr diff --git a/compiler/rustc_mir/src/transform/dyn_erased.rs b/compiler/rustc_mir/src/transform/dyn_erased.rs new file mode 100644 index 0000000000000..8803b8cfa70da --- /dev/null +++ b/compiler/rustc_mir/src/transform/dyn_erased.rs @@ -0,0 +1,667 @@ +//! A pass that propagates the unreachable terminator of a block to its predecessors +//! when all of their successors are unreachable. This is achieved through a +//! post-order traversal of the blocks. + +use crate::transform::MirPass; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def::DefKind; +use rustc_hir::lang_items::LangItem; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::visit::*; +use rustc_middle::mir::*; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_span::def_id::DefId; +use rustc_span::sym; +use rustc_span::DUMMY_SP; +use rustc_target::spec::abi::Abi; +use std::ops::Range; + +pub struct DynErased; + +impl MirPass<'_> for DynErased { + fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let def_id = body.source.def_id(); + if !tcx.has_attr(def_id, sym::rustc_dyn) { + return; + } + if body.arg_count == 0 { + // Do not run for constants. + let span = tcx.def_span(def_id); + tcx.sess.span_err(span, "rustc_dyn: constants are not allowed"); + return; + } + if !body.is_polymorphic { + // There is not point in running. + let span = tcx.def_span(def_id); + tcx.sess.span_err(span, "rustc_dyn: function must be polymorphic"); + return; + } + if let DefKind::Closure | DefKind::Generator = tcx.def_kind(body.source.instance.def_id()) { + // Skip closures and generators. + let span = tcx.def_span(def_id); + tcx.sess.span_err(span, "rustc_dyn: closures and generators are not handled"); + return; + } + if body.has_param_consts() { + // We do not handle this yet. + let span = tcx.def_span(def_id); + tcx.sess.span_err(span, "rustc_dyn: const generics are not handled"); + return; + } + + assert!(body.dyn_erased_body.is_none()); + + let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); + let types = match gather_types(tcx, param_env, &*body) { + Ok(types) => types, + Err(()) => return, + }; + + // We start modifying the body from this point on. + let locals = gather_constants(tcx, body); + + // Insert a block at the beginning to translate arguments. + // Insert a block after return to translate output. + let trampoline = build_trampoline( + tcx, + param_env, + &locals, + &*body, + types.get(body.local_decls[RETURN_PLACE].ty).copied(), + ); + + // We do not need the original declarations to be in order any more. + // We can fully overwrite and reorder them. + let mut erased_mir = std::mem::replace(body, trampoline); + type_erase_body(tcx, locals, &types, &mut erased_mir); + + super::dump_mir::on_mir_pass(tcx, &"-------", "DynErasedBody", &mut erased_mir, true); + + super::validate::Validator { + when: "after dyn_erased: trampoline".to_owned(), + mir_phase: super::MirPhase::Optimization, + } + .run_pass(tcx, body); + super::validate::Validator { + when: "after dyn_erased: erased".to_owned(), + mir_phase: super::MirPhase::Optimization, + } + .run_pass(tcx, &mut erased_mir); + + body.dyn_erased_body = Some(box erased_mir); + } +} + +fn build_trampoline( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + locals: &NewLocals<'tcx>, + body: &Body<'tcx>, + erased_ret_ty: Option>, +) -> Body<'tcx> { + let source_info = SourceInfo::outermost(body.span); + let def_id = body.source.instance.def_id(); + let ret_ty = body.local_decls[RETURN_PLACE].ty; + let erased_ret_ty = erased_ret_ty.unwrap_or(ret_ty); + + let mut blocks = IndexVec::with_capacity(4); + let init_bb = blocks.push(BasicBlockData::new(None)); + let finish_bb = blocks.push(BasicBlockData::new(None)); + let cleanup_bb = blocks.push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { source_info, kind: TerminatorKind::Resume }), + is_cleanup: true, + }); + + // Restrict locals to arguments. + let mut local_decls: IndexVec<_, _> = + body.local_decls.iter().take(1 + body.arg_count).cloned().collect(); + let var_debug_info: Vec<_> = body + .var_debug_info + .iter() + .filter(|debug_info| match debug_info.value { + VarDebugInfoContents::Place(p) => p.local.index() < 1 + body.arg_count, + VarDebugInfoContents::Const(_) => true, + }) + .cloned() + .collect(); + + // Reify all the function calls. + let mut reifier = FnReifier { tcx, param_env }; + + // Declare and assign new locals. + let mut stmts = vec![]; + for (decl, operand) in locals.decls.iter() { + let local = local_decls.push(decl.clone()); + reifier.visit_local_decl(local, &mut local_decls[local]); + let place = Place::from(local); + let mut init_stmt = Statement { + source_info, + kind: StatementKind::Assign(Box::new((place, Rvalue::Use(operand.clone())))), + }; + let location = Location { block: init_bb, statement_index: stmts.len() }; + reifier.visit_statement(&mut init_stmt, location); + stmts.push(init_stmt); + } + + let args = local_decls.indices().skip(1).map(|l| Operand::Move(Place::from(l))).collect(); + + // Fake return place to be transmuted later. + let ret_place = + local_decls.push(LocalDecl { ty: erased_ret_ty, ..body.local_decls[RETURN_PLACE].clone() }); + + // Build erased function call. + let func = { + //let substs = InternalSubsts::identity_for_item(tcx, def_id); + let substs = InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } => tcx.mk_ty_param(param.index, param.name).into(), + ty::GenericParamDefKind::Const { .. } => { + tcx.mk_const_param(param.index, param.name, tcx.type_of(param.def_id)).into() + } + }); + let ty = tcx.mk_fn_def(def_id, substs); + Operand::Constant(box Constant { + span: DUMMY_SP, + user_ty: None, + literal: ConstantKind::Ty(ty::Const::zero_sized(tcx, ty)), + }) + }; + let func = TerminatorKind::Call { + func, + args, + destination: Some((Place::from(ret_place), finish_bb)), + cleanup: Some(cleanup_bb), + fn_span: DUMMY_SP, + from_hir_call: false, + erased: true, + }; + blocks[init_bb].statements = stmts; + blocks[init_bb].terminator = Some(Terminator { source_info, kind: func }); + + if ret_ty != erased_ret_ty { + // Insert a transmute block so LLVM knows it should insert a bitcast. + let return_bb = blocks.push(BasicBlockData::new(Some(Terminator { + source_info, + kind: TerminatorKind::Return, + }))); + let transmute = { + let def_id = tcx.get_diagnostic_item(sym::transmute).unwrap(); + debug_assert_eq!( + tcx.layout_of(param_env.and(ret_ty)).unwrap().layout, + tcx.layout_of(param_env.and(erased_ret_ty)).unwrap().layout, + "Mismatch in layout for {:?} and erased {:?}", + ret_ty, + erased_ret_ty, + ); + let substs = tcx.intern_substs(&[ret_ty.into(), erased_ret_ty.into()]); + let ty = tcx.mk_fn_def(def_id, substs); + Operand::Constant(box Constant { + span: DUMMY_SP, + user_ty: None, + literal: ConstantKind::Ty(ty::Const::zero_sized(tcx, ty)), + }) + }; + let transmute = TerminatorKind::Call { + func: transmute, + args: vec![Operand::Move(Place::from(ret_place))], + destination: Some((Place::return_place(), return_bb)), + cleanup: Some(cleanup_bb), + fn_span: DUMMY_SP, + from_hir_call: false, + erased: false, + }; + blocks[finish_bb].terminator = Some(Terminator { source_info, kind: transmute }); + } else { + blocks[finish_bb].statements.push(Statement { + source_info, + kind: StatementKind::Assign(box ( + Place::return_place(), + Rvalue::Use(Operand::Move(Place::from(ret_place))), + )), + }); + blocks[finish_bb].terminator = + Some(Terminator { source_info, kind: TerminatorKind::Return }); + } + + Body::new( + body.source, + blocks, + body.source_scopes.clone(), + local_decls, + body.user_type_annotations.clone(), + body.arg_count, + var_debug_info, + body.span, + None, + ) +} + +/// Gather all types in the body, and check they can be erased. +fn gather_types( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + body: &Body<'tcx>, +) -> Result, Ty<'tcx>>, ()> { + // Translate all the function calls. + let mut calls = TypeGatherer { tcx, decls: &body.local_decls, types: FxHashSet::default() }; + calls.visit_body(body); + let TypeGatherer { types, .. } = calls; + + let mut type_map = FxHashMap::default(); + let mut eraser = TypeEraser { tcx }; + + let mut types: Vec<_> = types.into_iter().collect(); + while let Some(ty) = types.pop() { + if type_map.get(&ty).is_some() { + continue; + } + if !ty.has_param_types_or_consts() { + type_map.insert(ty, ty); + continue; + } + + let layout = tcx.layout_of(param_env.and(ty)); + if let Err(err) = layout { + let span = tcx.def_span(body.source.def_id()); + tcx.sess.span_err(span, &format!("rustc_dyn: unknown layout for {:?}: {:?}", ty, err)); + return Err(()); + } + + if let ty::FnDef(def_id, substs) = ty.kind() { + let f = ty.fn_sig(tcx); + if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { + // We do not support intrinsics yet. + let span = tcx.def_span(body.source.def_id()); + tcx.sess.span_err(span, &format!("rustc_dyn: unhandled intrinsic {:?}", ty)); + return Err(()); + } + + let new_ty = mk_fn_ptr(tcx, param_env, *def_id, substs); + let new_ty = new_ty.fold_with(&mut eraser); + type_map.insert(ty, new_ty); + + for t in f.inputs_and_output().skip_binder().iter() { + types.push(t); + } + } + + let new_ty = ty.fold_with(&mut eraser); + debug_assert!(!new_ty.has_param_types_or_consts()); + type_map.insert(ty, new_ty); + } + + Ok(type_map) +} + +/// Gather all constants that depend on generics. +fn gather_constants(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> NewLocals<'tcx> { + // Translate all the function calls. + let (bb, decls) = body.basic_blocks_and_local_decls_mut(); + + for data in bb.iter_mut() { + if let Some(terminator) = &mut data.terminator { + if let TerminatorKind::Drop { place, target, unwind } = terminator.kind { + let place_ty = place.ty(decls, tcx).ty; + if !place_ty.has_param_types_or_consts() { + continue; + } + let source_info = terminator.source_info; + + // Replace Drop by a call to drop_in_place that we can erase. + let addr_local = decls.push(LocalDecl { + mutability: Mutability::Not, + internal: true, + local_info: None, + is_block_tail: None, + ty: tcx.mk_mut_ptr(place_ty), + user_ty: None, + source_info, + }); + let addr_local = Place::from(addr_local); + let ret_local = decls.push(LocalDecl { + mutability: Mutability::Not, + internal: true, + local_info: None, + is_block_tail: None, + ty: tcx.types.unit, + user_ty: None, + source_info, + }); + data.statements.push(Statement { + source_info, + kind: StatementKind::Assign(box ( + addr_local, + Rvalue::AddressOf(Mutability::Mut, place), + )), + }); + let drop_fn = Operand::Constant(box Constant { + span: source_info.span, + user_ty: None, + literal: ConstantKind::Ty(ty::Const::zero_sized( + tcx, + mk_drop_in_place(tcx, place_ty), + )), + }); + terminator.kind = TerminatorKind::Call { + func: drop_fn, + args: vec![Operand::Move(addr_local)], + destination: Some((Place::from(ret_local), target)), + cleanup: unwind, + fn_span: DUMMY_SP, + from_hir_call: false, + erased: false, + }; + } + } + } + + let locals = NewLocals::new(body.local_decls.len()); + let mut calls = ConstantGatherer { tcx, locals }; + calls.visit_body(body); + calls.locals +} + +fn type_erase_body( + tcx: TyCtxt<'tcx>, + locals: NewLocals<'tcx>, + types: &FxHashMap, Ty<'tcx>>, + body: &mut Body<'tcx>, +) { + let NewLocals { range, decls } = locals; + let num_new_locals = decls.len(); + + // Insert locals to make new constants arguments. + let first_arg = body.arg_count + 1; + body.local_decls + .raw + .splice(first_arg..first_arg, decls.into_iter().map(|(decl, _)| decl)) + .for_each(|_| unreachable!()); + + // Reify all the function calls and reorder arguments. + let mut eraser = BodyEraser { tcx, range, types, first_arg: first_arg as u32 }; + eraser.visit_body(body); + body.required_consts.retain(|c| !c.has_param_types_or_consts()); + + body.is_polymorphic = body.has_param_types_or_consts(); + debug_assert!(!body.is_polymorphic, "{:#?}", body); + + // Reorder locals to have parameters first. + body.arg_count += num_new_locals; +} + +struct NewLocals<'tcx> { + range: Range, + decls: Vec<(LocalDecl<'tcx>, Operand<'tcx>)>, +} + +impl<'tcx> NewLocals<'tcx> { + fn new(nl: usize) -> Self { + let nl = nl as u32; + Self { range: nl..nl, decls: vec![] } + } + + fn push(&mut self, decl: impl FnOnce(Local) -> (LocalDecl<'tcx>, Operand<'tcx>)) -> Local { + let next = self.range.end; + self.range.end = next + 1; + let next = Local::from_u32(next); + self.decls.push(decl(next)); + next + } +} + +struct TypeGatherer<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + decls: &'a LocalDecls<'tcx>, + types: FxHashSet>, +} + +impl<'tcx> Visitor<'tcx> for TypeGatherer<'tcx, '_> { + fn visit_ty(&mut self, ty: Ty<'tcx>, _: TyContext) { + self.super_ty(ty); + self.types.insert(ty); + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.super_place(place, context, location); + let mut ty = PlaceTy::from_ty(self.decls[place.local].ty); + self.types.insert(ty.ty); + for elem in place.projection.iter() { + ty = ty.projection_ty(self.tcx, elem); + self.types.insert(ty.ty); + } + } + + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + self.super_operand(operand, location); + if let Operand::Constant(ref constant) = &*operand { + let const_ty = constant.literal.ty(); + self.types.insert(const_ty); + } + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + let tcx = self.tcx; + if let TerminatorKind::Drop { place, .. } = terminator.kind { + let place_ty = place.ty(self.decls, tcx).ty; + if place_ty.has_param_types_or_consts() { + // Replace Drop by an erased call to drop_in_place. + let drop_fn_ty = mk_drop_in_place(tcx, place_ty); + self.types.insert(drop_fn_ty); + } + } + self.super_terminator(terminator, location); + } +} + +struct ConstantGatherer<'tcx> { + tcx: TyCtxt<'tcx>, + locals: NewLocals<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for ConstantGatherer<'tcx> { + fn tcx(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { + self.super_operand(operand, location); + if let Operand::Constant(ref constant) = &*operand { + if constant.has_param_types_or_consts() { + let source_info = SourceInfo::outermost(constant.span); + let const_ty = constant.literal.ty(); + self.locals.push(&mut |local| { + let decl = LocalDecl { + mutability: Mutability::Not, + internal: true, + local_info: None, + is_block_tail: None, + ty: const_ty, + user_ty: None, + source_info, + }; + let place = Place::from(local); + let operand = std::mem::replace(operand, Operand::Move(place)); + (decl, operand) + }); + } + } + } +} + +fn mk_drop_in_place(tcx: TyCtxt<'tcx>, place_ty: Ty<'tcx>) -> Ty<'tcx> { + let def_id = tcx.require_lang_item(LangItem::DropInPlace, None); + let substs = tcx.intern_substs(&[place_ty.into()]); + tcx.mk_fn_def(def_id, substs) +} + +fn mk_fn_ptr( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, +) -> Ty<'tcx> { + let substs = tcx.normalize_erasing_regions(param_env, substs); + let fn_sig = tcx.fn_sig(def_id).subst(tcx, substs); + let fn_sig = tcx.mk_fn_ptr(fn_sig); + let fn_sig = tcx.erase_regions(fn_sig); + fn_sig +} + +struct FnReifier<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for FnReifier<'tcx> { + fn tcx(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) { + if let ty::FnDef(def_id, substs) = *ty.kind() { + let new_ty = mk_fn_ptr(self.tcx, self.param_env, def_id, substs); + *ty = new_ty; + } + } + + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { + if let Rvalue::Use(Operand::Constant(constant)) = &*rvalue { + let ty = constant.literal.ty(); + if let ty::FnDef(def_id, substs) = ty.kind() { + let f = self.tcx.fn_sig(*def_id); + // Intrinsics cannot be made into function pointers. + if f.abi() != Abi::RustIntrinsic && f.abi() != Abi::PlatformIntrinsic { + let ty = mk_fn_ptr(self.tcx, self.param_env, *def_id, substs); + let new_rvalue = Rvalue::Cast( + CastKind::Pointer(PointerCast::ReifyFnPointer), + Operand::Constant(constant.clone()), + ty, + ); + *rvalue = new_rvalue; + } + } + } + self.super_rvalue(rvalue, location); + } +} + +struct BodyEraser<'tcx, 'll> { + tcx: TyCtxt<'tcx>, + range: Range, + first_arg: u32, + types: &'ll FxHashMap, Ty<'tcx>>, +} + +impl<'tcx> MutVisitor<'tcx> for BodyEraser<'tcx, '_> { + fn tcx(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) { + self.super_ty(ty); + if let Some(new_ty) = self.types.get(&*ty) { + *ty = new_ty; + } else { + debug_assert!(!ty.has_param_types_or_consts()); + } + } + + fn visit_source_scope_data(&mut self, scope_data: &mut SourceScopeData<'tcx>) { + self.super_source_scope_data(scope_data); + *scope_data = scope_data.clone().fold_with(&mut TypeEraser { tcx: self.tcx }) + } + + fn process_projection_elem( + &mut self, + elem: PlaceElem<'tcx>, + _: Location, + ) -> Option> { + match elem { + PlaceElem::Field(field, ty) => { + if let Some(new_ty) = self.types.get(&ty) { + Some(PlaceElem::Field(field, new_ty)) + } else { + debug_assert!(!ty.has_param_types_or_consts()); + None + } + } + PlaceElem::Index(..) + | PlaceElem::Deref + | PlaceElem::ConstantIndex { .. } + | PlaceElem::Subslice { .. } + | PlaceElem::Downcast(..) => None, + } + } + + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { + // Avoid double casts. + if let Rvalue::Cast( + CastKind::Pointer(PointerCast::ReifyFnPointer), + Operand::Move(place), + _ty, + ) = rvalue + { + if self.range.contains(&place.local.as_u32()) { + let new_rvalue = Rvalue::Use(Operand::Move(*place)); + *rvalue = new_rvalue; + } + } + self.super_rvalue(rvalue, location); + debug_assert!(!rvalue.has_param_types_or_consts()); + } + + fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { + // Swap the two ranges `self.range` and `first_arg..`. + let idx = local.as_u32(); + if self.range.contains(&idx) { + let shift = idx - self.range.start; + *local = Local::from_u32(self.first_arg + shift); + } else if idx >= self.first_arg { + let shift = idx - self.first_arg; + let start = self.first_arg + self.range.len() as u32; + *local = Local::from_u32(start + shift); + } + } +} + +struct TypeEraser<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl TypeFolder<'tcx> for TypeEraser<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_param_types_or_consts() { + return ty; + } + if let ty::Projection(..) | ty::Param(..) = ty.kind() { + return self.tcx.types.u8; + } + let ty = ty.super_fold_with(self); + assert!(!ty.has_param_types_or_consts()); + ty + } + + fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + T: TypeFoldable<'tcx>, + { + let u = self.tcx.anonymize_late_bound_regions(t); + u.super_fold_with(self) + } + + fn fold_region(&mut self, _r: ty::Region<'tcx>) -> ty::Region<'tcx> { + self.tcx.lifetimes.re_erased + } + + fn fold_mir_const(&mut self, c: ConstantKind<'tcx>) -> ConstantKind<'tcx> { + c.super_fold_with(self) + } +} diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index 5c49ee69edc51..93c707ee40712 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -29,6 +29,7 @@ pub mod deaggregator; pub mod deduplicate_blocks; pub mod dest_prop; pub mod dump_mir; +pub mod dyn_erased; pub mod early_otherwise_branch; pub mod elaborate_drops; pub mod function_item_references; @@ -526,6 +527,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Some cleanup necessary at least for LLVM and potentially other codegen backends. let pre_codegen_cleanup: &[&dyn MirPass<'tcx>] = &[ &add_call_guards::CriticalCallEdges, + &dyn_erased::DynErased, // Dump the end result for testing and debugging purposes. &dump_mir::Marker("PreCodegen"), ]; diff --git a/src/test/mir-opt/dyn_erased.assoc_const.DynErased.after.mir b/src/test/mir-opt/dyn_erased.assoc_const.DynErased.after.mir new file mode 100644 index 0000000000000..cb00d4280aac8 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.assoc_const.DynErased.after.mir @@ -0,0 +1,24 @@ +// MIR for `assoc_const` after DynErased + +fn assoc_const(_1: &T) -> usize { + let mut _0: usize; // return place in scope 0 at $DIR/dyn_erased.rs:54:34: 54:39 + let _2: usize; // in scope 0 at $DIR/dyn_erased.rs:55:5: 55:9 + let mut _3: usize; // in scope 0 at $DIR/dyn_erased.rs:54:34: 54:39 + + bb0: { + _2 = const ::C; // scope 0 at $DIR/dyn_erased.rs:54:1: 56:2 + _3 = assoc_const::[erased](move _1, move _2) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/dyn_erased.rs:54:1: 56:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:1:1: 1:1 + // + literal: Const { ty: for<'r> fn(&'r T) -> usize {assoc_const::}, val: Value(Scalar()) } + } + + bb1: { + _0 = move _3; // scope 0 at $DIR/dyn_erased.rs:54:1: 56:2 + return; // scope 0 at $DIR/dyn_erased.rs:54:1: 56:2 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/dyn_erased.rs:54:1: 56:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.assoc_const.DynErasedBody.after.mir b/src/test/mir-opt/dyn_erased.assoc_const.DynErasedBody.after.mir new file mode 100644 index 0000000000000..70a5613d5e387 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.assoc_const.DynErasedBody.after.mir @@ -0,0 +1,10 @@ +// MIR for `assoc_const` after DynErasedBody + +fn assoc_const(_1: &u8, _2: usize) -> usize { + let mut _0: usize; // return place in scope 0 at $DIR/dyn_erased.rs:54:34: 54:39 + + bb0: { + _0 = move _2; // scope 0 at $DIR/dyn_erased.rs:55:5: 55:9 + return; // scope 0 at $DIR/dyn_erased.rs:56:2: 56:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.coreturn.DynErased.after.mir b/src/test/mir-opt/dyn_erased.coreturn.DynErased.after.mir new file mode 100644 index 0000000000000..bb148aea0d961 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.coreturn.DynErased.after.mir @@ -0,0 +1,34 @@ +// MIR for `coreturn` after DynErased + +fn coreturn(_1: &T) -> &T { + debug f => _1; // in scope 0 at $DIR/dyn_erased.rs:30:25: 30:26 + let mut _0: &T; // return place in scope 0 at $DIR/dyn_erased.rs:30:35: 30:37 + let _2: for<'r> fn(&'r T); // in scope 0 at $DIR/dyn_erased.rs:31:5: 31:12 + let mut _3: &u8; // in scope 0 at $DIR/dyn_erased.rs:30:35: 30:37 + + bb0: { + _2 = generic:: as for<'r> fn(&'r T) (Pointer(ReifyFnPointer)); // scope 0 at $DIR/dyn_erased.rs:30:1: 34:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:31:5: 31:12 + // + literal: Const { ty: for<'r> fn(&'r T) {generic::}, val: Value(Scalar()) } + _3 = coreturn::[erased](move _1, move _2) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/dyn_erased.rs:30:1: 34:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:1:1: 1:1 + // + literal: Const { ty: for<'r> fn(&'r T) -> &'r T {coreturn::}, val: Value(Scalar()) } + } + + bb1: { + _0 = transmute::<&T, &u8>(move _3) -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/dyn_erased.rs:30:1: 34:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:1:1: 1:1 + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&T) -> &u8 {std::intrinsics::transmute::<&T, &u8>}, val: Value(Scalar()) } + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/dyn_erased.rs:30:1: 34:2 + } + + bb3: { + return; // scope 0 at $DIR/dyn_erased.rs:30:1: 34:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.coreturn.DynErasedBody.after.mir b/src/test/mir-opt/dyn_erased.coreturn.DynErasedBody.after.mir new file mode 100644 index 0000000000000..ada813e76bd39 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.coreturn.DynErasedBody.after.mir @@ -0,0 +1,32 @@ +// MIR for `coreturn` after DynErasedBody + +fn coreturn(_1: &u8, _2: for<'r> fn(&'r u8) {generic::}) -> &u8 { + debug f => _1; // in scope 0 at $DIR/dyn_erased.rs:30:25: 30:26 + let mut _0: &u8; // return place in scope 0 at $DIR/dyn_erased.rs:30:35: 30:37 + let _3: (); // in scope 0 at $DIR/dyn_erased.rs:31:5: 31:15 + let mut _4: &u8; // in scope 0 at $DIR/dyn_erased.rs:31:13: 31:14 + let _5: (); // in scope 0 at $DIR/dyn_erased.rs:32:5: 32:19 + + bb0: { + StorageLive(_3); // scope 0 at $DIR/dyn_erased.rs:31:5: 31:15 + StorageLive(_4); // scope 0 at $DIR/dyn_erased.rs:31:13: 31:14 + _4 = _1; // scope 0 at $DIR/dyn_erased.rs:31:13: 31:14 + _3 = move _2(move _4) -> bb1; // scope 0 at $DIR/dyn_erased.rs:31:5: 31:15 + } + + bb1: { + StorageDead(_4); // scope 0 at $DIR/dyn_erased.rs:31:14: 31:15 + StorageDead(_3); // scope 0 at $DIR/dyn_erased.rs:31:15: 31:16 + StorageLive(_5); // scope 0 at $DIR/dyn_erased.rs:32:5: 32:19 + _5 = non_generic(const 5_u32) -> bb2; // scope 0 at $DIR/dyn_erased.rs:32:5: 32:19 + // mir::Constant + // + span: $DIR/dyn_erased.rs:32:5: 32:16 + // + literal: Const { ty: fn(u32) {non_generic}, val: Value(Scalar()) } + } + + bb2: { + StorageDead(_5); // scope 0 at $DIR/dyn_erased.rs:32:19: 32:20 + _0 = _1; // scope 0 at $DIR/dyn_erased.rs:33:5: 33:6 + return; // scope 0 at $DIR/dyn_erased.rs:34:2: 34:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.dropping.DynErased.after.mir b/src/test/mir-opt/dyn_erased.dropping.DynErased.after.mir new file mode 100644 index 0000000000000..07eb70d1d6f60 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.dropping.DynErased.after.mir @@ -0,0 +1,29 @@ +// MIR for `dropping` after DynErased + +fn dropping(_1: &T) -> () { + let mut _0: (); // return place in scope 0 at $DIR/dyn_erased.rs:70:31: 70:33 + let _2: unsafe fn(*mut Droppy); // in scope 0 at $DIR/dyn_erased.rs:71:43: 71:44 + let mut _3: (); // in scope 0 at $DIR/dyn_erased.rs:70:31: 70:33 + scope 1 { + } + + bb0: { + _2 = std::ptr::drop_in_place::> as unsafe fn(*mut Droppy) (Pointer(ReifyFnPointer)); // scope 0 at $DIR/dyn_erased.rs:70:1: 72:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:71:43: 71:44 + // + literal: Const { ty: unsafe fn(*mut Droppy) {std::ptr::drop_in_place::>}, val: Value(Scalar()) } + _3 = dropping::[erased](move _1, move _2) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/dyn_erased.rs:70:1: 72:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:1:1: 1:1 + // + literal: Const { ty: for<'r> fn(&'r T) {dropping::}, val: Value(Scalar()) } + } + + bb1: { + _0 = move _3; // scope 0 at $DIR/dyn_erased.rs:70:1: 72:2 + return; // scope 0 at $DIR/dyn_erased.rs:70:1: 72:2 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/dyn_erased.rs:70:1: 72:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.dropping.DynErasedBody.after.mir b/src/test/mir-opt/dyn_erased.dropping.DynErasedBody.after.mir new file mode 100644 index 0000000000000..a2e36e20472f2 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.dropping.DynErasedBody.after.mir @@ -0,0 +1,21 @@ +// MIR for `dropping` after DynErasedBody + +fn dropping(_1: &u8, _2: unsafe fn(*mut Droppy) {std::ptr::drop_in_place::>}) -> () { + let mut _0: (); // return place in scope 0 at $DIR/dyn_erased.rs:70:31: 70:33 + let mut _3: Droppy; // in scope 0 at $DIR/dyn_erased.rs:71:24: 71:43 + let _4: *mut Droppy; // in scope 0 at $DIR/dyn_erased.rs:71:43: 71:44 + let _5: (); // in scope 0 at $DIR/dyn_erased.rs:71:43: 71:44 + scope 1 { + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/dyn_erased.rs:71:24: 71:43 + _4 = &raw mut _3; // scope 0 at $DIR/dyn_erased.rs:71:43: 71:44 + _5 = move _2(move _4) -> bb1; // scope 0 at $DIR/dyn_erased.rs:71:43: 71:44 + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/dyn_erased.rs:71:43: 71:44 + return; // scope 0 at $DIR/dyn_erased.rs:72:2: 72:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.good_niche.DynErased.after.mir b/src/test/mir-opt/dyn_erased.good_niche.DynErased.after.mir new file mode 100644 index 0000000000000..d4eac4f562341 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.good_niche.DynErased.after.mir @@ -0,0 +1,32 @@ +// MIR for `good_niche` after DynErased + +fn good_niche(_1: Option<&T>) -> &T { + debug f => _1; // in scope 0 at $DIR/dyn_erased.rs:39:18: 39:19 + let mut _0: &T; // return place in scope 0 at $DIR/dyn_erased.rs:40:17: 40:18 + let mut _2: &u8; // in scope 0 at $DIR/dyn_erased.rs:40:17: 40:18 + scope 1 { + debug f => _0; // in scope 1 at $DIR/dyn_erased.rs:40:17: 40:18 + } + + bb0: { + _2 = good_niche::[erased](move _1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/dyn_erased.rs:39:1: 41:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:1:1: 1:1 + // + literal: Const { ty: for<'r> fn(std::option::Option<&'r T>) -> &'r T {good_niche::}, val: Value(Scalar()) } + } + + bb1: { + _0 = transmute::<&T, &u8>(move _2) -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/dyn_erased.rs:39:1: 41:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:1:1: 1:1 + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&T) -> &u8 {std::intrinsics::transmute::<&T, &u8>}, val: Value(Scalar()) } + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/dyn_erased.rs:39:1: 41:2 + } + + bb3: { + return; // scope 0 at $DIR/dyn_erased.rs:39:1: 41:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.good_niche.DynErasedBody.after.mir b/src/test/mir-opt/dyn_erased.good_niche.DynErasedBody.after.mir new file mode 100644 index 0000000000000..b0d238d956693 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.good_niche.DynErasedBody.after.mir @@ -0,0 +1,33 @@ +// MIR for `good_niche` after DynErasedBody + +fn good_niche(_1: Option<&u8>) -> &u8 { + debug f => _1; // in scope 0 at $DIR/dyn_erased.rs:39:18: 39:19 + let mut _0: &u8; // return place in scope 0 at $DIR/dyn_erased.rs:40:17: 40:18 + let mut _2: isize; // in scope 0 at $DIR/dyn_erased.rs:40:12: 40:19 + scope 1 { + debug f => _0; // in scope 1 at $DIR/dyn_erased.rs:40:17: 40:18 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/dyn_erased.rs:40:12: 40:19 + switchInt(move _2) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/dyn_erased.rs:40:12: 40:19 + } + + bb1: { + begin_panic::<&str>(const "explicit panic"); // scope 0 at $SRC_DIR/std/src/panic.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/std/src/panic.rs:LL:COL + // + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar()) } + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) + // mir::Constant + // + span: $SRC_DIR/std/src/panic.rs:LL:COL + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } + } + + bb2: { + _0 = ((_1 as Some).0: &u8); // scope 0 at $DIR/dyn_erased.rs:40:17: 40:18 + return; // scope 0 at $DIR/dyn_erased.rs:41:2: 41:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.normal.DynErased.after.mir b/src/test/mir-opt/dyn_erased.normal.DynErased.after.mir new file mode 100644 index 0000000000000..ee8ef7a81813f --- /dev/null +++ b/src/test/mir-opt/dyn_erased.normal.DynErased.after.mir @@ -0,0 +1,36 @@ +// MIR for `normal` after DynErased + +fn normal(_1: &T) -> () { + debug f => _1; // in scope 0 at $DIR/dyn_erased.rs:21:23: 21:24 + let mut _0: (); // return place in scope 0 at $DIR/dyn_erased.rs:21:30: 21:30 + let _2: for<'r> fn(&'r T); // in scope 0 at $DIR/dyn_erased.rs:22:5: 22:12 + let _3: &&str; // in scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + let mut _4: (); // in scope 0 at $DIR/dyn_erased.rs:21:30: 21:30 + + bb0: { + _2 = generic:: as for<'r> fn(&'r T) (Pointer(ReifyFnPointer)); // scope 0 at $DIR/dyn_erased.rs:21:1: 25:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:22:5: 22:12 + // + literal: Const { ty: for<'r> fn(&'r T) {generic::}, val: Value(Scalar()) } + _3 = const normal::::promoted[0]; // scope 0 at $DIR/dyn_erased.rs:21:1: 25:2 + // ty::Const + // + ty: &&str + // + val: Unevaluated(normal, [T], Some(promoted[0])) + // mir::Constant + // + span: $DIR/dyn_erased.rs:23:13: 23:19 + // + literal: Const { ty: &&str, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:12 ~ dyn_erased[317d]::normal), const_param_did: None }, substs: [T], promoted: Some(promoted[0]) }) } + _4 = normal::[erased](move _1, move _2, move _3) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/dyn_erased.rs:21:1: 25:2 + // mir::Constant + // + span: $DIR/dyn_erased.rs:1:1: 1:1 + // + literal: Const { ty: for<'r> fn(&'r T) {normal::}, val: Value(Scalar()) } + } + + bb1: { + _0 = move _4; // scope 0 at $DIR/dyn_erased.rs:21:1: 25:2 + return; // scope 0 at $DIR/dyn_erased.rs:21:1: 25:2 + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/dyn_erased.rs:21:1: 25:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.normal.DynErasedBody.after.mir b/src/test/mir-opt/dyn_erased.normal.DynErasedBody.after.mir new file mode 100644 index 0000000000000..ac6c96f136d4a --- /dev/null +++ b/src/test/mir-opt/dyn_erased.normal.DynErasedBody.after.mir @@ -0,0 +1,51 @@ +// MIR for `normal` after DynErasedBody + +fn normal(_1: &u8, _2: for<'r> fn(&'r u8) {generic::}, _3: &&str) -> () { + debug f => _1; // in scope 0 at $DIR/dyn_erased.rs:21:23: 21:24 + let mut _0: (); // return place in scope 0 at $DIR/dyn_erased.rs:21:30: 21:30 + let _4: (); // in scope 0 at $DIR/dyn_erased.rs:22:5: 22:15 + let mut _5: &u8; // in scope 0 at $DIR/dyn_erased.rs:22:13: 22:14 + let _6: (); // in scope 0 at $DIR/dyn_erased.rs:23:5: 23:20 + let mut _7: &&str; // in scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + let _8: &&str; // in scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + let _9: (); // in scope 0 at $DIR/dyn_erased.rs:24:5: 24:19 + let mut _10: &&str; // in scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + + bb0: { + StorageLive(_4); // scope 0 at $DIR/dyn_erased.rs:22:5: 22:15 + StorageLive(_5); // scope 0 at $DIR/dyn_erased.rs:22:13: 22:14 + _5 = _1; // scope 0 at $DIR/dyn_erased.rs:22:13: 22:14 + _4 = move _2(move _5) -> bb1; // scope 0 at $DIR/dyn_erased.rs:22:5: 22:15 + } + + bb1: { + StorageDead(_5); // scope 0 at $DIR/dyn_erased.rs:22:14: 22:15 + StorageDead(_4); // scope 0 at $DIR/dyn_erased.rs:22:15: 22:16 + StorageLive(_6); // scope 0 at $DIR/dyn_erased.rs:23:5: 23:20 + StorageLive(_7); // scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + StorageLive(_8); // scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + _10 = move _3; // scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + _8 = _10; // scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + _7 = _8; // scope 0 at $DIR/dyn_erased.rs:23:13: 23:19 + _6 = generic::<&str>(move _7) -> bb2; // scope 0 at $DIR/dyn_erased.rs:23:5: 23:20 + // mir::Constant + // + span: $DIR/dyn_erased.rs:23:5: 23:12 + // + literal: Const { ty: for<'r> fn(&'r &str) {generic::<&str>}, val: Value(Scalar()) } + } + + bb2: { + StorageDead(_7); // scope 0 at $DIR/dyn_erased.rs:23:19: 23:20 + StorageDead(_8); // scope 0 at $DIR/dyn_erased.rs:23:20: 23:21 + StorageDead(_6); // scope 0 at $DIR/dyn_erased.rs:23:20: 23:21 + StorageLive(_9); // scope 0 at $DIR/dyn_erased.rs:24:5: 24:19 + _9 = non_generic(const 5_u32) -> bb3; // scope 0 at $DIR/dyn_erased.rs:24:5: 24:19 + // mir::Constant + // + span: $DIR/dyn_erased.rs:24:5: 24:16 + // + literal: Const { ty: fn(u32) {non_generic}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_9); // scope 0 at $DIR/dyn_erased.rs:24:19: 24:20 + return; // scope 0 at $DIR/dyn_erased.rs:25:2: 25:2 + } +} diff --git a/src/test/mir-opt/dyn_erased.rs b/src/test/mir-opt/dyn_erased.rs new file mode 100644 index 0000000000000..3c6587ad296b9 --- /dev/null +++ b/src/test/mir-opt/dyn_erased.rs @@ -0,0 +1,81 @@ +// compile-flags: -Zinline-mir=no + +#![feature(rustc_attrs)] + +use std::fmt::Display; +use std::marker::PhantomData; + +#[inline(never)] +fn generic(a: &T) { + println!("{}", a) +} + +#[inline(never)] +fn non_generic(a: u32) { + println!("{}", a) +} + +// EMIT_MIR dyn_erased.normal.DynErased.after.mir +// EMIT_MIR dyn_erased.normal.DynErasedBody.after.mir +#[rustc_dyn] +fn normal(f: &T) { + generic(f); + generic(&"foo"); // Check this remaine a direct call. + non_generic(5); // Check this is a direct call. +} + +// EMIT_MIR dyn_erased.coreturn.DynErased.after.mir +// EMIT_MIR dyn_erased.coreturn.DynErasedBody.after.mir +#[rustc_dyn] +fn coreturn(f: &T) -> &T { + generic(f); + non_generic(5); // Check this is a direct call. + f // Check we transmute this return type for LLVM. +} + +// EMIT_MIR dyn_erased.good_niche.DynErased.after.mir +// EMIT_MIR dyn_erased.good_niche.DynErasedBody.after.mir +#[rustc_dyn] +fn good_niche(f: Option<&T>) -> &T { + if let Some(f) = f { f } else { panic!() } +} + +trait Foo { + const C: usize; +} + +impl Foo for u32 { + const C: usize = 13; +} + +// EMIT_MIR dyn_erased.assoc_const.DynErased.after.mir +// EMIT_MIR dyn_erased.assoc_const.DynErasedBody.after.mir +#[rustc_dyn] +fn assoc_const(_: &T) -> usize { + T::C +} + +struct Droppy(PhantomData); + +impl Drop for Droppy { + #[inline(never)] + fn drop(&mut self) { + println!("drop!"); + } +} + +// EMIT_MIR dyn_erased.dropping.DynErased.after.mir +// EMIT_MIR dyn_erased.dropping.DynErasedBody.after.mir +#[rustc_dyn] +fn dropping(_: &T) -> () { + let _: Droppy = Droppy(PhantomData); +} + +// Use a main function to verify that monomorphizations are collected correctly. +fn main() { + normal(&42); + assert!(coreturn(&42) == &42); + good_niche(Some(&42)); + assoc_const(&42); + dropping(&42); +} diff --git a/src/test/ui/dyn_erased.rs b/src/test/ui/dyn_erased.rs new file mode 100644 index 0000000000000..1fd06dc313470 --- /dev/null +++ b/src/test/ui/dyn_erased.rs @@ -0,0 +1,32 @@ +// build-fail +// compile-flags: -Zmir-opt-level=1 + +#![feature(rustc_attrs)] +#![feature(core_intrinsics)] + +use std::fmt::Display; + +#[rustc_dyn] +fn intrinsic(f: *mut T) -> *const T { + //~^ ERROR rustc_dyn: unhandled intrinsic + let f = unsafe { std::intrinsics::offset(f, 5) }; + f as *const _ +} + +#[rustc_dyn] +fn bad_niche(f: &Option) -> &T { + //~^ ERROR rustc_dyn: unknown layout + if let Some(f) = f { f } else { panic!() } +} + +#[rustc_dyn] +fn fat_ptr(f: &T) -> &T { + //~^ ERROR rustc_dyn: unknown layout + f +} + +fn main() { + intrinsic(&mut 42 as _); + bad_niche(&Some(42)); + fat_ptr(&42); +} diff --git a/src/test/ui/dyn_erased.stderr b/src/test/ui/dyn_erased.stderr new file mode 100644 index 0000000000000..51db10eee23ae --- /dev/null +++ b/src/test/ui/dyn_erased.stderr @@ -0,0 +1,20 @@ +error: rustc_dyn: unhandled intrinsic unsafe extern "rust-intrinsic" fn(*const T, isize) -> *const T {std::intrinsics::offset::} + --> $DIR/dyn_erased.rs:10:1 + | +LL | fn intrinsic(f: *mut T) -> *const T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: rustc_dyn: unknown layout for T: Unknown(T) + --> $DIR/dyn_erased.rs:17:1 + | +LL | fn bad_niche(f: &Option) -> &T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: rustc_dyn: unknown layout for &T: Unknown(T) + --> $DIR/dyn_erased.rs:23:1 + | +LL | fn fat_ptr(f: &T) -> &T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors +