diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a67a5776449d7..aa54bc436638f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -384,6 +384,8 @@ declare_features! ( (unstable, associated_type_defaults, "1.2.0", Some(29661)), /// Allows `async || body` closures. (unstable, async_closure, "1.37.0", Some(62290)), + /// Allows async functions to be called from `dyn Trait`. + (incomplete, async_fn_in_dyn_trait, "CURRENT_RUSTC_VERSION", Some(133119)), /// Allows `#[track_caller]` on async functions. (unstable, async_fn_track_caller, "1.73.0", Some(110011)), /// Allows `for await` loops. diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index d42b6be4787d9..96ce21ede2072 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -675,23 +675,26 @@ impl<'tcx> Instance<'tcx> { // // 1) The underlying method expects a caller location parameter // in the ABI - if resolved.def.requires_caller_location(tcx) - // 2) The caller location parameter comes from having `#[track_caller]` - // on the implementation, and *not* on the trait method. - && !tcx.should_inherit_track_caller(def) - // If the method implementation comes from the trait definition itself - // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`), - // then we don't need to generate a shim. This check is needed because - // `should_inherit_track_caller` returns `false` if our method - // implementation comes from the trait block, and not an impl block - && !matches!( - tcx.opt_associated_item(def), - Some(ty::AssocItem { - container: ty::AssocItemContainer::Trait, - .. - }) - ) - { + let needs_track_caller_shim = resolved.def.requires_caller_location(tcx) + // 2) The caller location parameter comes from having `#[track_caller]` + // on the implementation, and *not* on the trait method. + && !tcx.should_inherit_track_caller(def) + // If the method implementation comes from the trait definition itself + // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`), + // then we don't need to generate a shim. This check is needed because + // `should_inherit_track_caller` returns `false` if our method + // implementation comes from the trait block, and not an impl block + && !matches!( + tcx.opt_associated_item(def), + Some(ty::AssocItem { + container: ty::AssocItemContainer::Trait, + .. + }) + ); + // We also need to generate a shim if this is an AFIT. + let needs_rpitit_shim = + tcx.return_position_impl_trait_in_trait_shim_data(def).is_some(); + if needs_track_caller_shim || needs_rpitit_shim { if tcx.is_closure_like(def) { debug!( " => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}", diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index cddd6110c23c2..5cc83bac60fed 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -148,6 +148,7 @@ mod opaque_types; mod parameterized; mod predicate; mod region; +mod return_position_impl_trait_in_trait; mod rvalue_scopes; mod structural_impls; #[allow(hidden_glob_reexports)] diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs new file mode 100644 index 0000000000000..58f35a974d2c1 --- /dev/null +++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs @@ -0,0 +1,88 @@ +use rustc_hir::def_id::DefId; + +use crate::ty::{self, ExistentialPredicateStableCmpExt, TyCtxt}; + +impl<'tcx> TyCtxt<'tcx> { + /// Given a `def_id` of a trait or impl method, compute whether that method needs to + /// have an RPITIT shim applied to it for it to be object safe. If so, return the + /// `def_id` of the RPITIT, and also the args of trait method that returns the RPITIT. + /// + /// NOTE that these args are not, in general, the same as than the RPITIT's args. They + /// are a subset of those args, since they do not include the late-bound lifetimes of + /// the RPITIT. Depending on the context, these will need to be dealt with in different + /// ways -- in codegen, it's okay to fill them with ReErased. + pub fn return_position_impl_trait_in_trait_shim_data( + self, + def_id: DefId, + ) -> Option<(DefId, ty::EarlyBinder<'tcx, ty::GenericArgsRef<'tcx>>)> { + let assoc_item = self.opt_associated_item(def_id)?; + + let (trait_item_def_id, opt_impl_def_id) = match assoc_item.container { + ty::AssocItemContainer::Impl => { + (assoc_item.trait_item_def_id?, Some(self.parent(def_id))) + } + ty::AssocItemContainer::Trait => (def_id, None), + }; + + let sig = self.fn_sig(trait_item_def_id); + + let ty::Alias(ty::Projection, alias_ty) = *sig.skip_binder().skip_binder().output().kind() + else { + return None; + }; + + if !self.is_impl_trait_in_trait(alias_ty.def_id) { + return None; + } + + let args = if let Some(impl_def_id) = opt_impl_def_id { + // Rebase the args from the RPITIT onto the impl trait ref, so we can later + // substitute them with the method args of the *impl* method, since that's + // the instance we're building a vtable shim for. + ty::GenericArgs::identity_for_item(self, trait_item_def_id).rebase_onto( + self, + self.parent(trait_item_def_id), + self.impl_trait_ref(impl_def_id) + .expect("expected impl trait ref from parent of impl item") + .instantiate_identity() + .args, + ) + } else { + // This is when we have a default trait implementation. + ty::GenericArgs::identity_for_item(self, trait_item_def_id) + }; + + Some((alias_ty.def_id, ty::EarlyBinder::bind(args))) + } + + /// Given a `DefId` of an RPITIT and its args, return the existential predicates + /// that corresponds to the RPITIT's bounds with the self type erased. + pub fn item_bounds_to_existential_predicates( + self, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> &'tcx ty::List> { + let mut bounds: Vec<_> = self + .item_super_predicates(def_id) + .iter_instantiated(self, args) + .filter_map(|clause| { + clause + .kind() + .map_bound(|clause| match clause { + ty::ClauseKind::Trait(trait_pred) => Some(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(self, trait_pred.trait_ref), + )), + ty::ClauseKind::Projection(projection_pred) => { + Some(ty::ExistentialPredicate::Projection( + ty::ExistentialProjection::erase_self_ty(self, projection_pred), + )) + } + _ => None, + }) + .transpose() + }) + .collect(); + bounds.sort_by(|a, b| a.skip_binder().stable_cmp(self, &b.skip_binder())); + self.mk_poly_existential_predicates(&bounds) + } +} diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index f16cde7cd4eb6..0d4f8f536fe73 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -9,6 +9,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::query::Providers; +use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{ self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, GenericArgs, Ty, TyCtxt, }; @@ -710,6 +711,13 @@ fn build_call_shim<'tcx>( }; let def_id = instance.def_id(); + + let rpitit_shim = if let ty::InstanceKind::ReifyShim(..) = instance { + tcx.return_position_impl_trait_in_trait_shim_data(def_id) + } else { + None + }; + let sig = tcx.fn_sig(def_id); let sig = sig.map_bound(|sig| tcx.instantiate_bound_regions_with_erased(sig)); @@ -747,8 +755,8 @@ fn build_call_shim<'tcx>( sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } - // FIXME(eddyb) avoid having this snippet both here and in - // `Instance::fn_sig` (introduce `InstanceKind::fn_sig`?). + // FIXME: Avoid having to adjust the signature both here and in + // `fn_sig_for_fn_abi`. if let ty::InstanceKind::VTableShim(..) = instance { // Modify fn(self, ...) to fn(self: *mut Self, ...) let mut inputs_and_output = sig.inputs_and_output.to_vec(); @@ -765,9 +773,34 @@ fn build_call_shim<'tcx>( let mut local_decls = local_decls_for_sig(&sig, span); let source_info = SourceInfo::outermost(span); + let mut destination = Place::return_place(); + if let Some((rpitit_def_id, fn_args)) = rpitit_shim { + let rpitit_args = + fn_args.instantiate_identity().extend_to(tcx, rpitit_def_id, |param, _| { + match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => { + unreachable!("rpitit should have no addition ty/ct") + } + } + }); + let dyn_star_ty = Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates(rpitit_def_id, rpitit_args), + tcx.lifetimes.re_erased, + ty::DynStar, + ); + destination = local_decls.push(local_decls[RETURN_PLACE].clone()).into(); + local_decls[RETURN_PLACE].ty = dyn_star_ty; + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + *inputs_and_output.last_mut().unwrap() = dyn_star_ty; + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); + } + let rcvr_place = || { assert!(rcvr_adjustment.is_some()); - Place::from(Local::new(1 + 0)) + Place::from(Local::new(1)) }; let mut statements = vec![]; @@ -854,7 +887,7 @@ fn build_call_shim<'tcx>( TerminatorKind::Call { func: callee, args, - destination: Place::return_place(), + destination, target: Some(BasicBlock::new(1)), unwind: if let Some(Adjustment::RefMut) = rcvr_adjustment { UnwindAction::Cleanup(BasicBlock::new(3)) @@ -882,7 +915,24 @@ fn build_call_shim<'tcx>( ); } // BB #1/#2 - return - block(&mut blocks, vec![], TerminatorKind::Return, false); + // NOTE: If this is an RPITIT in dyn, we also want to coerce + // the return type of the function into a `dyn*`. + let stmts = if rpitit_shim.is_some() { + vec![Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Cast( + CastKind::PointerCoercion(PointerCoercion::DynStar, CoercionSource::Implicit), + Operand::Move(destination), + sig.output(), + ), + ))), + }] + } else { + vec![] + }; + block(&mut blocks, stmts, TerminatorKind::Return, false); if let Some(Adjustment::RefMut) = rcvr_adjustment { // BB #3 - drop if closure panics block( diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 0f08930fb4c86..1e01c52e25cb1 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -43,7 +43,10 @@ fn custom_coerce_unsize_info<'tcx>( .. })) => Ok(tcx.coerce_unsized_info(impl_def_id)?.custom_kind.unwrap()), impl_source => { - bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); + bug!( + "invalid `CoerceUnsized` from {source_ty} to {target_ty}: impl_source: {:?}", + impl_source + ); } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a562d34abdee6..102d0a116772c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -453,6 +453,7 @@ symbols! { async_drop_slice, async_drop_surface_drop_in_place, async_fn, + async_fn_in_dyn_trait, async_fn_in_trait, async_fn_kind_helper, async_fn_kind_upvars, diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index cf63f14fb937d..7887478f8b480 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -11,6 +11,7 @@ use rustc_abi::BackendRepr; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt, @@ -905,23 +906,56 @@ fn contains_illegal_impl_trait_in_trait<'tcx>( fn_def_id: DefId, ty: ty::Binder<'tcx, Ty<'tcx>>, ) -> Option { - // This would be caught below, but rendering the error as a separate - // `async-specific` message is better. + let ty = tcx.liberate_late_bound_regions(fn_def_id, ty); + if tcx.asyncness(fn_def_id).is_async() { - return Some(MethodViolationCode::AsyncFn); + if tcx.features().async_fn_in_dyn_trait() { + let ty::Alias(ty::Projection, proj) = *ty.kind() else { + bug!("expected async fn in trait to return an RPITIT"); + }; + assert!(tcx.is_impl_trait_in_trait(proj.def_id)); + + // FIXME(async_fn_in_dyn_trait): We should check that this bound is legal too, + // and stop relying on `async fn` in the definition. + for bound in tcx.item_bounds(proj.def_id).instantiate(tcx, proj.args) { + if let Some(violation) = bound + .visit_with(&mut IllegalRpititVisitor { tcx, allowed: Some(proj) }) + .break_value() + { + return Some(violation); + } + } + + None + } else { + // Rendering the error as a separate `async-specific` message is better. + Some(MethodViolationCode::AsyncFn) + } + } else { + ty.visit_with(&mut IllegalRpititVisitor { tcx, allowed: None }).break_value() } +} + +struct IllegalRpititVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + allowed: Option>, +} + +impl<'tcx> TypeVisitor> for IllegalRpititVisitor<'tcx> { + type Result = ControlFlow; - // FIXME(RPITIT): Perhaps we should use a visitor here? - ty.skip_binder().walk().find_map(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Projection, proj) = ty.kind() - && tcx.is_impl_trait_in_trait(proj.def_id) + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::Alias(ty::Projection, proj) = *ty.kind() + && Some(proj) != self.allowed + && self.tcx.is_impl_trait_in_trait(proj.def_id) { - Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.def_id))) + ControlFlow::Break(MethodViolationCode::ReferencesImplTraitInTrait( + self.tcx.def_span(proj.def_id), + )) } else { - None + ty.super_visit_with(self) } - }) + } } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index aab854e9caf91..67762f6f34cbf 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -7,8 +7,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::infer::{DefineOpaqueTypes, RegionVariableOrigin}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; pub use rustc_middle::traits::Reveal; use rustc_middle::traits::select::OverflowError; @@ -19,6 +19,7 @@ use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; +use thin_vec::thin_vec; use tracing::{debug, instrument}; use super::{ @@ -62,6 +63,9 @@ enum ProjectionCandidate<'tcx> { /// Bounds specified on an object type Object(ty::PolyProjectionPredicate<'tcx>), + /// Built-in bound for a dyn async fn in trait + ObjectRpitit, + /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), } @@ -852,6 +856,17 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( env_predicates, false, ); + + // `dyn Trait` automagically project their AFITs to `dyn* Future`. + if tcx.is_impl_trait_in_trait(obligation.predicate.def_id) + && let Some(out_trait_def_id) = data.principal_def_id() + && let rpitit_trait_def_id = tcx.parent(obligation.predicate.def_id) + && tcx + .supertrait_def_ids(out_trait_def_id) + .any(|trait_def_id| trait_def_id == rpitit_trait_def_id) + { + candidate_set.push_candidate(ProjectionCandidate::ObjectRpitit); + } } #[instrument( @@ -1270,6 +1285,8 @@ fn confirm_candidate<'cx, 'tcx>( ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } + + ProjectionCandidate::ObjectRpitit => confirm_object_rpitit_candidate(selcx, obligation), }; // When checking for cycle during evaluation, we compare predicates with @@ -2057,6 +2074,45 @@ fn confirm_impl_candidate<'cx, 'tcx>( } } +fn confirm_object_rpitit_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTermObligation<'tcx>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + let mut obligations = thin_vec![]; + + // Compute an intersection lifetime for all the input components of this GAT. + let intersection = + selcx.infcx.next_region_var(RegionVariableOrigin::MiscVariable(obligation.cause.span)); + for component in obligation.predicate.args { + match component.unpack() { + ty::GenericArgKind::Lifetime(lt) => { + obligations.push(obligation.with(tcx, ty::OutlivesPredicate(lt, intersection))); + } + ty::GenericArgKind::Type(ty) => { + obligations.push(obligation.with(tcx, ty::OutlivesPredicate(ty, intersection))); + } + ty::GenericArgKind::Const(_ct) => { + // Consts have no outlives... + } + } + } + + Progress { + term: Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates( + obligation.predicate.def_id, + obligation.predicate.args, + ), + intersection, + ty::DynStar, + ) + .into(), + obligations, + } +} + // Get obligations corresponding to the predicates from the where-clause of the // associated type itself. fn assoc_ty_own_obligations<'cx, 'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 712856e6a8f2d..64140d7a9452e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -21,6 +21,7 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; +use rustc_type_ir::elaborate; use tracing::{debug, instrument}; use super::SelectionCandidate::{self, *}; @@ -626,6 +627,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for assoc_type in assoc_types { let defs: &ty::Generics = tcx.generics_of(assoc_type); + // When `async_fn_in_dyn_trait` is enabled, we don't need to check the + // RPITIT for compatibility, since it's not provided by the user. + if tcx.is_impl_trait_in_trait(assoc_type) { + if tcx.features().async_fn_in_dyn_trait() { + continue; + } else { + tcx.dcx().span_delayed_bug( + obligation.cause.span, + "RPITIT in trait object shouldn't have been considered", + ); + return Err(SelectionError::TraitDynIncompatible( + trait_predicate.trait_ref.def_id, + )); + } + } + if !defs.own_params.is_empty() && !tcx.features().generic_associated_types_extended() { tcx.dcx().span_delayed_bug( obligation.cause.span, @@ -1225,6 +1242,61 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::ClauseKind::TypeOutlives(outlives).upcast(tcx), )); + // Require that all AFIT will return something that can be coerced into `dyn*` + // -- a shim will be responsible for doing the actual coercion to `dyn*`. + if let Some(principal) = data.principal() { + for supertrait in + elaborate::supertraits(tcx, principal.with_self_ty(tcx, source)) + { + if tcx.is_trait_alias(supertrait.def_id()) { + continue; + } + + for &assoc_item in tcx.associated_item_def_ids(supertrait.def_id()) { + if !tcx.is_impl_trait_in_trait(assoc_item) { + continue; + } + + let mut bound_vars = supertrait.bound_vars().to_vec(); + let args = supertrait.skip_binder().args.extend_to( + tcx, + assoc_item, + |arg, _| match arg.kind { + GenericParamDefKind::Lifetime => { + let kind = ty::BoundRegionKind::Named( + arg.def_id, + tcx.item_name(arg.def_id), + ); + bound_vars.push(ty::BoundVariableKind::Region(kind)); + ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind, + }) + .into() + } + GenericParamDefKind::Type { .. } + | GenericParamDefKind::Const { .. } => unreachable!(), + }, + ); + + nested.push(predicate_to_obligation( + ty::Binder::bind_with_vars( + ty::TraitRef::new( + tcx, + tcx.require_lang_item( + LangItem::PointerLike, + Some(obligation.cause.span), + ), + [Ty::new_projection_from_args(tcx, assoc_item, args)], + ), + tcx.mk_bound_variable_kinds(&bound_vars), + ) + .upcast(tcx), + )); + } + } + } + ImplSource::Builtin(BuiltinImplSource::Misc, nested) } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index e2283383196bc..c958828300b22 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -31,20 +31,20 @@ fn fn_sig_for_fn_abi<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, typing_env: ty::TypingEnv<'tcx>, -) -> ty::PolyFnSig<'tcx> { +) -> ty::FnSig<'tcx> { if let InstanceKind::ThreadLocalShim(..) = instance.def { - return ty::Binder::dummy(tcx.mk_fn_sig( + return tcx.mk_fn_sig( [], tcx.thread_local_ptr_ty(instance.def_id()), false, hir::Safety::Safe, rustc_abi::ExternAbi::Unadjusted, - )); + ); } let ty = instance.ty(tcx, typing_env); match *ty.kind() { - ty::FnDef(..) => { + ty::FnDef(def_id, args) => { // 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 @@ -52,9 +52,8 @@ fn fn_sig_for_fn_abi<'tcx>( // track of a polymorphization `ParamEnv` to allow normalizing later. // // We normalize the `fn_sig` again after instantiating at a later point. - let mut sig = match *ty.kind() { - ty::FnDef(def_id, args) => tcx - .fn_sig(def_id) + let mut sig = tcx.instantiate_bound_regions_with_erased( + tcx.fn_sig(def_id) .map_bound(|fn_sig| { tcx.normalize_erasing_regions( ty::TypingEnv::non_body_analysis(tcx, def_id), @@ -62,62 +61,62 @@ fn fn_sig_for_fn_abi<'tcx>( ) }) .instantiate(tcx, args), - _ => unreachable!(), - }; + ); + // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. if let ty::InstanceKind::VTableShim(..) = instance.def { - // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. - sig = sig.map_bound(|mut sig| { - let mut inputs_and_output = sig.inputs_and_output.to_vec(); - inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); - sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); - sig - }); + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); + } + + // Modify `fn() -> impl Future` to `fn() -> dyn* Future`. + if let ty::InstanceKind::ReifyShim(def_id, _) = instance.def + && let Some((rpitit_def_id, fn_args)) = + tcx.return_position_impl_trait_in_trait_shim_data(def_id) + { + let fn_args = fn_args.instantiate(tcx, args); + let rpitit_args = + fn_args.extend_to(tcx, rpitit_def_id, |param, _| match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => { + unreachable!("rpitit should have no addition ty/ct") + } + }); + let dyn_star_ty = Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates(rpitit_def_id, rpitit_args), + tcx.lifetimes.re_erased, + ty::DynStar, + ); + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + *inputs_and_output.last_mut().unwrap() = dyn_star_ty; + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } + sig } ty::Closure(def_id, args) => { - let sig = args.as_closure().sig(); - - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - ))); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let sig = tcx.instantiate_bound_regions_with_erased(args.as_closure().sig()); let env_ty = tcx.closure_env_ty( Ty::new_closure(tcx, def_id, args), args.as_closure().kind(), - env_region, + tcx.lifetimes.re_erased, ); - let sig = sig.skip_binder(); - ty::Binder::bind_with_vars( - tcx.mk_fn_sig( - iter::once(env_ty).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.c_variadic, - sig.safety, - sig.abi, - ), - bound_vars, + tcx.mk_fn_sig( + iter::once(env_ty).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.c_variadic, + sig.safety, + sig.abi, ) } ty::CoroutineClosure(def_id, args) => { let coroutine_ty = Ty::new_coroutine_closure(tcx, def_id, args); let sig = args.as_coroutine_closure().coroutine_closure_sig(); - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - ))); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + // When this `CoroutineClosure` comes from a `ConstructCoroutineInClosureShim`, // make sure we respect the `target_kind` in that shim. // FIXME(async_closures): This shouldn't be needed, and we should be populating @@ -138,42 +137,32 @@ fn fn_sig_for_fn_abi<'tcx>( coroutine_ty } } else { - tcx.closure_env_ty(coroutine_ty, coroutine_kind, env_region) + tcx.closure_env_ty(coroutine_ty, coroutine_kind, tcx.lifetimes.re_erased) }; - let sig = sig.skip_binder(); - ty::Binder::bind_with_vars( - tcx.mk_fn_sig( - iter::once(env_ty).chain([sig.tupled_inputs_ty]), - sig.to_coroutine_given_kind_and_upvars( - tcx, - args.as_coroutine_closure().parent_args(), - tcx.coroutine_for_closure(def_id), - coroutine_kind, - env_region, - args.as_coroutine_closure().tupled_upvars_ty(), - args.as_coroutine_closure().coroutine_captures_by_ref_ty(), - ), - sig.c_variadic, - sig.safety, - sig.abi, + let sig = tcx.instantiate_bound_regions_with_erased(sig); + + tcx.mk_fn_sig( + iter::once(env_ty).chain([sig.tupled_inputs_ty]), + sig.to_coroutine_given_kind_and_upvars( + tcx, + args.as_coroutine_closure().parent_args(), + tcx.coroutine_for_closure(def_id), + coroutine_kind, + tcx.lifetimes.re_erased, + args.as_coroutine_closure().tupled_upvars_ty(), + args.as_coroutine_closure().coroutine_captures_by_ref_ty(), ), - bound_vars, + sig.c_variadic, + sig.safety, + sig.abi, ) } ty::Coroutine(did, args) => { let coroutine_kind = tcx.coroutine_kind(did).unwrap(); let sig = args.as_coroutine().sig(); - let bound_vars = tcx.mk_bound_variable_kinds_from_iter(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - )); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - - let env_ty = Ty::new_mut_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), ty); + let env_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty); let pin_did = tcx.require_lang_item(LangItem::Pin, None); let pin_adt_ref = tcx.adt_def(pin_did); @@ -268,7 +257,7 @@ fn fn_sig_for_fn_abi<'tcx>( } }; - let fn_sig = if let Some(resume_ty) = resume_ty { + if let Some(resume_ty) = resume_ty { tcx.mk_fn_sig( [env_ty, resume_ty], ret_ty, @@ -285,8 +274,7 @@ fn fn_sig_for_fn_abi<'tcx>( hir::Safety::Safe, rustc_abi::ExternAbi::Rust, ) - }; - ty::Binder::bind_with_vars(fn_sig, bound_vars) + } } _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), } @@ -335,8 +323,16 @@ fn fn_abi_of_fn_ptr<'tcx>( query: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query; + let cx = LayoutCx::new(tcx, typing_env); - fn_abi_new_uncached(&cx, sig, extra_args, None, None, false) + fn_abi_new_uncached( + &cx, + tcx.instantiate_bound_regions_with_erased(sig), + extra_args, + None, + None, + false, + ) } fn fn_abi_of_instance<'tcx>( @@ -567,7 +563,7 @@ fn fn_abi_sanity_check<'tcx>( #[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))] fn fn_abi_new_uncached<'tcx>( cx: &LayoutCx<'tcx>, - sig: ty::PolyFnSig<'tcx>, + sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>], caller_location: Option>, fn_def_id: Option, @@ -575,7 +571,8 @@ fn fn_abi_new_uncached<'tcx>( force_thin_self_ptr: bool, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let tcx = cx.tcx(); - let sig = tcx.normalize_erasing_late_bound_regions(cx.typing_env, sig); + // FIXME: is this needed? + let sig = tcx.normalize_erasing_regions(cx.typing_env, sig); let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic); diff --git a/tests/crashes/131538.rs b/tests/crashes/131538.rs deleted file mode 100644 index f971d8b7791e4..0000000000000 --- a/tests/crashes/131538.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #131538 -#![feature(generic_associated_types_extended)] -#![feature(trivial_bounds)] - -trait HealthCheck { - async fn check(); -} - -fn do_health_check_par() -where - HealthCheck: HealthCheck, -{ -} diff --git a/tests/ui/async-await/dyn/auxiliary/block-on.rs b/tests/ui/async-await/dyn/auxiliary/block-on.rs new file mode 100644 index 0000000000000..dcb710fc97c97 --- /dev/null +++ b/tests/ui/async-await/dyn/auxiliary/block-on.rs @@ -0,0 +1,20 @@ +//@ edition: 2021 + +#![feature(async_closure, noop_waker)] + +use std::future::Future; +use std::pin::pin; +use std::task::*; + +pub fn block_on(fut: impl Future) -> T { + let mut fut = pin!(fut); + // Poll loop, just to test the future... + let ctx = &mut Context::from_waker(Waker::noop()); + + loop { + match unsafe { fut.as_mut().poll(ctx) } { + Poll::Pending => {} + Poll::Ready(t) => break t, + } + } +} diff --git a/tests/ui/async-await/dyn/gat-extended-bad-afit.no.stderr b/tests/ui/async-await/dyn/gat-extended-bad-afit.no.stderr new file mode 100644 index 0000000000000..036955d06771a --- /dev/null +++ b/tests/ui/async-await/dyn/gat-extended-bad-afit.no.stderr @@ -0,0 +1,30 @@ +warning: the feature `generic_associated_types_extended` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/gat-extended-bad-afit.rs:9:12 + | +LL | #![feature(generic_associated_types_extended)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95451 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0038]: the trait `HealthCheck` cannot be made into an object + --> $DIR/gat-extended-bad-afit.rs:19:22 + | +LL | dyn HealthCheck: HealthCheck, + | ^^^^^^^^^^^ `HealthCheck` cannot be made into an object + | +note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/gat-extended-bad-afit.rs:14:14 + | +LL | trait HealthCheck { + | ----------- this trait cannot be made into an object... +LL | async fn check(&self); + | ^^^^^ + | | + | ...because method `check` is `async` + | ...because method `check` has generic type parameters + = help: consider moving `check` to another trait + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/async-await/dyn/gat-extended-bad-afit.rs b/tests/ui/async-await/dyn/gat-extended-bad-afit.rs new file mode 100644 index 0000000000000..05b14efc312eb --- /dev/null +++ b/tests/ui/async-await/dyn/gat-extended-bad-afit.rs @@ -0,0 +1,24 @@ +//@ edition: 2021 +//@ revisions: yes no + +// Ice regression test for #131538 +// Make sure that we don't crash when the GATE feature gate is enabled. + +#![cfg_attr(yes, feature(async_fn_in_dyn_trait))] +//[yes]~^ WARN the feature `async_fn_in_dyn_trait` is incomplete +#![feature(generic_associated_types_extended)] +//~^ WARN the feature `generic_associated_types_extended` is incomplete +#![feature(trivial_bounds)] + +trait HealthCheck { + async fn check(&self); +} + +fn do_health_check_par() +where + dyn HealthCheck: HealthCheck, + //~^ ERROR the trait `HealthCheck` cannot be made into an object +{ +} + +fn main() {} diff --git a/tests/ui/async-await/dyn/gat-extended-bad-afit.yes.stderr b/tests/ui/async-await/dyn/gat-extended-bad-afit.yes.stderr new file mode 100644 index 0000000000000..7c59b5ae84eca --- /dev/null +++ b/tests/ui/async-await/dyn/gat-extended-bad-afit.yes.stderr @@ -0,0 +1,35 @@ +warning: the feature `async_fn_in_dyn_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/gat-extended-bad-afit.rs:7:26 + | +LL | #![cfg_attr(yes, feature(async_fn_in_dyn_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133119 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `generic_associated_types_extended` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/gat-extended-bad-afit.rs:9:12 + | +LL | #![feature(generic_associated_types_extended)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95451 for more information + +error[E0038]: the trait `HealthCheck` cannot be made into an object + --> $DIR/gat-extended-bad-afit.rs:19:22 + | +LL | dyn HealthCheck: HealthCheck, + | ^^^^^^^^^^^ `HealthCheck` cannot be made into an object + | +note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/gat-extended-bad-afit.rs:14:14 + | +LL | trait HealthCheck { + | ----------- this trait cannot be made into an object... +LL | async fn check(&self); + | ^^^^^ ...because method `check` has generic type parameters + = help: consider moving `check` to another trait + +error: aborting due to 1 previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/async-await/dyn/works.rs b/tests/ui/async-await/dyn/works.rs new file mode 100644 index 0000000000000..e52d793f4f1ae --- /dev/null +++ b/tests/ui/async-await/dyn/works.rs @@ -0,0 +1,30 @@ +//@ aux-build:block-on.rs +//@ edition: 2021 +//@ run-pass +//@ check-run-results + +#![feature(async_fn_in_dyn_trait)] +//~^ WARN the feature `async_fn_in_dyn_trait` is incomplete + +extern crate block_on; + +use std::future::Future; + +trait AsyncTrait { + async fn async_dispatch(&self); +} + +impl AsyncTrait for &'static str { + fn async_dispatch(&self) -> impl Future { + Box::pin(async move { + println!("message from the aether: {self}"); + }) + } +} + +fn main() { + block_on::block_on(async { + let x: &dyn AsyncTrait = &"hello, world!"; + x.async_dispatch().await; + }); +} diff --git a/tests/ui/async-await/dyn/works.run.stdout b/tests/ui/async-await/dyn/works.run.stdout new file mode 100644 index 0000000000000..7b45a504e60c7 --- /dev/null +++ b/tests/ui/async-await/dyn/works.run.stdout @@ -0,0 +1 @@ +message from the aether: hello, world! diff --git a/tests/ui/async-await/dyn/works.stderr b/tests/ui/async-await/dyn/works.stderr new file mode 100644 index 0000000000000..850a47a2bd0a1 --- /dev/null +++ b/tests/ui/async-await/dyn/works.stderr @@ -0,0 +1,11 @@ +warning: the feature `async_fn_in_dyn_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/works.rs:6:12 + | +LL | #![feature(async_fn_in_dyn_trait)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133119 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/async-await/dyn/wrong-size.rs b/tests/ui/async-await/dyn/wrong-size.rs new file mode 100644 index 0000000000000..ac15dd2606767 --- /dev/null +++ b/tests/ui/async-await/dyn/wrong-size.rs @@ -0,0 +1,23 @@ +//@ edition: 2021 + +#![feature(async_fn_in_dyn_trait)] +//~^ WARN the feature `async_fn_in_dyn_trait` is incomplete + +use std::future::Future; + +trait AsyncTrait { + async fn async_dispatch(&self); +} + +impl AsyncTrait for &'static str { + fn async_dispatch(&self) -> impl Future { + async move { + // The implementor must box the future... + } + } +} + +fn main() { + let x: &dyn AsyncTrait = &"hello, world!"; + //~^ ERROR `impl Future` needs to have the same ABI as a pointer +} diff --git a/tests/ui/async-await/dyn/wrong-size.stderr b/tests/ui/async-await/dyn/wrong-size.stderr new file mode 100644 index 0000000000000..0202b5f240977 --- /dev/null +++ b/tests/ui/async-await/dyn/wrong-size.stderr @@ -0,0 +1,21 @@ +warning: the feature `async_fn_in_dyn_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/wrong-size.rs:3:12 + | +LL | #![feature(async_fn_in_dyn_trait)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133119 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: `impl Future` needs to have the same ABI as a pointer + --> $DIR/wrong-size.rs:21:30 + | +LL | let x: &dyn AsyncTrait = &"hello, world!"; + | ^^^^^^^^^^^^^^^^ `impl Future` needs to be a pointer-like type + | + = help: the trait `for<'a> PointerLike` is not implemented for `impl Future` + = note: required for the cast from `&&'static str` to `&dyn AsyncTrait` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs new file mode 100644 index 0000000000000..d9ff45f57ecba --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs @@ -0,0 +1,14 @@ +//@ edition: 2021 + +trait Foo { + async fn bar(&self); +} + +async fn takes_dyn_trait(x: &dyn Foo) { + //~^ ERROR the trait `Foo` cannot be made into an object + x.bar().await; + //~^ ERROR the trait `Foo` cannot be made into an object + //~| ERROR the trait `Foo` cannot be made into an object +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr new file mode 100644 index 0000000000000..f78fc422410be --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr @@ -0,0 +1,48 @@ +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:7:30 + | +LL | async fn takes_dyn_trait(x: &dyn Foo) { + | ^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | async fn bar(&self); + | ^^^ ...because method `bar` is `async` + = help: consider moving `bar` to another trait + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:9:7 + | +LL | x.bar().await; + | ^^^ `Foo` cannot be made into an object + | +note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | async fn bar(&self); + | ^^^ ...because method `bar` is `async` + = help: consider moving `bar` to another trait + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:9:5 + | +LL | x.bar().await; + | ^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | async fn bar(&self); + | ^^^ ...because method `bar` is `async` + = help: consider moving `bar` to another trait + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0038`.