about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-11-16 20:18:13 +0000
committerMichael Goulet <michael@errs.io>2024-12-10 16:52:20 +0000
commita7fa4cbcb498b80b126a954b5944f19a11e28dec (patch)
tree462d7ced92d08e35a6cae86caeb804c8dbefb635
parent3b057796264e637b31c28809322028a32d22ae6f (diff)
downloadrust-a7fa4cbcb498b80b126a954b5944f19a11e28dec.tar.gz
rust-a7fa4cbcb498b80b126a954b5944f19a11e28dec.zip
Implement projection and shim for AFIDT
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs37
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs1
-rw-r--r--compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs95
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs56
-rw-r--r--compiler/rustc_monomorphize/src/lib.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs58
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs74
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs26
-rw-r--r--tests/ui/async-await/dyn/auxiliary/block-on.rs20
-rw-r--r--tests/ui/async-await/dyn/mut-is-pointer-like.rs40
-rw-r--r--tests/ui/async-await/dyn/mut-is-pointer-like.run.stdout1
-rw-r--r--tests/ui/async-await/dyn/mut-is-pointer-like.stderr11
-rw-r--r--tests/ui/async-await/dyn/works.rs32
-rw-r--r--tests/ui/async-await/dyn/works.run.stdout1
-rw-r--r--tests/ui/async-await/dyn/works.stderr11
-rw-r--r--tests/ui/async-await/dyn/wrong-size.rs23
-rw-r--r--tests/ui/async-await/dyn/wrong-size.stderr21
17 files changed, 490 insertions, 22 deletions
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 65c909e70f6..1dd564d9798 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -677,23 +677,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 70e0568b202..80b11892a42 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -146,6 +146,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 00000000000..21c605f8296
--- /dev/null
+++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs
@@ -0,0 +1,95 @@
+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);
+
+        // Check if the trait returns an RPITIT.
+        let ty::Alias(ty::Projection, ty::AliasTy { def_id, .. }) =
+            *sig.skip_binder().skip_binder().output().kind()
+        else {
+            return None;
+        };
+        if !self.is_impl_trait_in_trait(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((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<ty::PolyExistentialPredicate<'tcx>> {
+        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),
+                            ))
+                        }
+                        ty::ClauseKind::TypeOutlives(_) => {
+                            // Type outlives bounds don't really turn into anything,
+                            // since we must use an intersection region for the `dyn*`'s
+                            // region anyways.
+                            None
+                        }
+                        _ => unreachable!("unexpected clause in item bounds: {clause:?}"),
+                    })
+                    .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 b8383e734e2..722da3c420d 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));
 
@@ -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 caae54cd559..714b64b3a23 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -42,7 +42,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_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 49c34550f8e..9b8317fda75 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};
 use rustc_middle::traits::select::OverflowError;
 use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData};
@@ -18,6 +18,7 @@ use rustc_middle::ty::visit::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::{
@@ -61,6 +62,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>),
 }
@@ -827,6 +831,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(
@@ -1247,6 +1262,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
@@ -2034,6 +2051,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 2fbe2e1e323..3664121ac4b 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -19,6 +19,7 @@ use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
 use rustc_middle::ty::{self, GenericArgsRef, ToPolyTraitRef, Ty, TyCtxt, Upcast};
 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, *};
@@ -624,6 +625,12 @@ 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.features().async_fn_in_dyn_trait() && tcx.is_impl_trait_in_trait(assoc_type) {
+                continue;
+            }
+
             if !defs.own_params.is_empty() {
                 tcx.dcx().span_delayed_bug(
                     obligation.cause.span,
@@ -1175,6 +1182,33 @@ 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 pointer_like_goal = pointer_like_goal_for_rpitit(
+                                tcx,
+                                supertrait,
+                                assoc_item,
+                                &obligation.cause,
+                            );
+
+                            nested.push(predicate_to_obligation(pointer_like_goal.upcast(tcx)));
+                        }
+                    }
+                }
+
                 ImplSource::Builtin(BuiltinImplSource::Misc, nested)
             }
 
@@ -1280,3 +1314,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         })
     }
 }
+
+/// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures)
+/// implements the `PointerLike` trait, which is a requirement for the RPITIT to be
+/// coercible to `dyn* Future`, which is itself a requirement for the RPITIT's parent
+/// trait to be coercible to `dyn Trait`.
+///
+/// We do this given a supertrait's substitutions, and then augment the substitutions
+/// with bound variables to compute the goal universally. Given that `PointerLike` has
+/// no region requirements (at least for the built-in pointer types), this shouldn't
+/// *really* matter, but it is the best choice for soundness.
+fn pointer_like_goal_for_rpitit<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    supertrait: ty::PolyTraitRef<'tcx>,
+    rpitit_item: DefId,
+    cause: &ObligationCause<'tcx>,
+) -> ty::PolyTraitRef<'tcx> {
+    let mut bound_vars = supertrait.bound_vars().to_vec();
+
+    let args = supertrait.skip_binder().args.extend_to(tcx, rpitit_item, |arg, _| match arg.kind {
+        ty::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()
+        }
+        ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => {
+            unreachable!()
+        }
+    });
+
+    ty::Binder::bind_with_vars(
+        ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)), [
+            Ty::new_projection_from_args(tcx, rpitit_item, args),
+        ]),
+        tcx.mk_bound_variable_kinds(&bound_vars),
+    )
+}
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index ae6d697794f..b63534880d1 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -48,12 +48,38 @@ fn fn_sig_for_fn_abi<'tcx>(
             let mut sig = tcx
                 .instantiate_bound_regions_with_erased(tcx.fn_sig(def_id).instantiate(tcx, args));
 
+            // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
             if let ty::InstanceKind::VTableShim(..) = instance.def {
                 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) => {
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 00000000000..dcb710fc97c
--- /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<T>(fut: impl Future<Output = T>) -> 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/mut-is-pointer-like.rs b/tests/ui/async-await/dyn/mut-is-pointer-like.rs
new file mode 100644
index 00000000000..93e8281164c
--- /dev/null
+++ b/tests/ui/async-await/dyn/mut-is-pointer-like.rs
@@ -0,0 +1,40 @@
+//@ aux-build:block-on.rs
+//@ edition: 2021
+//@ run-pass
+//@ check-run-results
+
+#![allow(refining_impl_trait)]
+#![feature(async_fn_in_dyn_trait)]
+//~^ WARN the feature `async_fn_in_dyn_trait` is incomplete
+
+extern crate block_on;
+
+use std::future::Future;
+use std::pin::Pin;
+
+trait AsyncTrait {
+    type Output;
+
+    async fn async_dispatch(self: Pin<&mut Self>) -> Self::Output;
+}
+
+impl<F> AsyncTrait for F
+where
+    F: Future,
+{
+    type Output = F::Output;
+
+    fn async_dispatch(self: Pin<&mut Self>) -> Pin<&mut Self> {
+        self
+    }
+}
+
+fn main() {
+    block_on::block_on(async {
+        let f = std::pin::pin!(async {
+            println!("hello, world");
+        });
+        let x: Pin<&mut dyn AsyncTrait<Output = ()>> = f;
+        x.async_dispatch().await;
+    });
+}
diff --git a/tests/ui/async-await/dyn/mut-is-pointer-like.run.stdout b/tests/ui/async-await/dyn/mut-is-pointer-like.run.stdout
new file mode 100644
index 00000000000..4b5fa63702d
--- /dev/null
+++ b/tests/ui/async-await/dyn/mut-is-pointer-like.run.stdout
@@ -0,0 +1 @@
+hello, world
diff --git a/tests/ui/async-await/dyn/mut-is-pointer-like.stderr b/tests/ui/async-await/dyn/mut-is-pointer-like.stderr
new file mode 100644
index 00000000000..7c72ce43cf0
--- /dev/null
+++ b/tests/ui/async-await/dyn/mut-is-pointer-like.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/mut-is-pointer-like.rs:7:12
+   |
+LL | #![feature(async_fn_in_dyn_trait)]
+   |            ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133119 <https://github.com/rust-lang/rust/issues/133119> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/async-await/dyn/works.rs b/tests/ui/async-await/dyn/works.rs
new file mode 100644
index 00000000000..0732a3ee2f2
--- /dev/null
+++ b/tests/ui/async-await/dyn/works.rs
@@ -0,0 +1,32 @@
+//@ aux-build:block-on.rs
+//@ edition: 2021
+//@ run-pass
+//@ check-run-results
+
+#![allow(refining_impl_trait)]
+#![feature(async_fn_in_dyn_trait)]
+//~^ WARN the feature `async_fn_in_dyn_trait` is incomplete
+
+extern crate block_on;
+
+use std::pin::Pin;
+use std::future::Future;
+
+trait AsyncTrait {
+    async fn async_dispatch(&self);
+}
+
+impl AsyncTrait for &'static str {
+    fn async_dispatch(&self) -> Pin<Box<impl Future<Output = ()>>> {
+        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 00000000000..7b45a504e60
--- /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 00000000000..2c7db7c32f5
--- /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:7:12
+   |
+LL | #![feature(async_fn_in_dyn_trait)]
+   |            ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #133119 <https://github.com/rust-lang/rust/issues/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 00000000000..ac15dd26067
--- /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<Output = ()> {
+        async move {
+            // The implementor must box the future...
+        }
+    }
+}
+
+fn main() {
+    let x: &dyn AsyncTrait = &"hello, world!";
+    //~^ ERROR `impl Future<Output = ()>` 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 00000000000..0202b5f2409
--- /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 <https://github.com/rust-lang/rust/issues/133119> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0277]: `impl Future<Output = ()>` needs to have the same ABI as a pointer
+  --> $DIR/wrong-size.rs:21:30
+   |
+LL |     let x: &dyn AsyncTrait = &"hello, world!";
+   |                              ^^^^^^^^^^^^^^^^ `impl Future<Output = ()>` needs to be a pointer-like type
+   |
+   = help: the trait `for<'a> PointerLike` is not implemented for `impl Future<Output = ()>`
+   = 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`.