//! Code which is used by built-in goals that match "structurally", such a auto //! traits, `Copy`/`Clone`. use derive_where::derive_where; use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{ self as ty, FallibleTypeFolder, Interner, Movability, Mutability, TypeFoldable, TypeSuperFoldable, Upcast as _, elaborate, }; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; use crate::delegate::SolverDelegate; use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution}; // Calculates the constituent types of a type for `auto trait` purposes. #[instrument(level = "trace", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait( ecx: &EvalCtxt<'_, D>, ty: I::Ty, ) -> Result>, NoSolution> where D: SolverDelegate, I: Interner, { let cx = ecx.cx(); match ty.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Float(_) | ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::Never | ty::Char => Ok(ty::Binder::dummy(vec![])), // This branch is only for `experimental_default_bounds`. // Other foreign types were rejected earlier in // `disqualify_auto_trait_candidate_due_to_possible_impl`. ty::Foreign(..) => Ok(ty::Binder::dummy(vec![])), // Treat `str` like it's defined as `struct str([u8]);` ty::Str => Ok(ty::Binder::dummy(vec![Ty::new_slice(cx, Ty::new_u8(cx))])), ty::Dynamic(..) | ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => { panic!("unexpected type `{ty:?}`") } ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => { Ok(ty::Binder::dummy(vec![element_ty])) } ty::Pat(element_ty, _) | ty::Array(element_ty, _) | ty::Slice(element_ty) => { Ok(ty::Binder::dummy(vec![element_ty])) } ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet Ok(ty::Binder::dummy(tys.to_vec())) } ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])), ty::CoroutineClosure(_, args) => { Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()])) } ty::Coroutine(def_id, args) => Ok(ty::Binder::dummy(vec![ args.as_coroutine().tupled_upvars_ty(), Ty::new_coroutine_witness_for_coroutine(ecx.cx(), def_id, args), ])), ty::CoroutineWitness(def_id, args) => Ok(ecx .cx() .coroutine_hidden_types(def_id) .instantiate(cx, args) .map_bound(|bound| bound.types.to_vec())), ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])), // For `PhantomData`, we pass `T`. ty::Adt(def, args) if def.is_phantom_data() => Ok(ty::Binder::dummy(vec![args.type_at(0)])), ty::Adt(def, args) => { Ok(ty::Binder::dummy(def.all_field_tys(cx).iter_instantiated(cx, args).collect())) } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. Ok(ty::Binder::dummy(vec![cx.type_of(def_id).instantiate(cx, args)])) } } } #[instrument(level = "trace", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_sizedness_trait( ecx: &EvalCtxt<'_, D>, sizedness: SizedTraitKind, ty: I::Ty, ) -> Result>, NoSolution> where D: SolverDelegate, I: Interner, { match ty.kind() { // impl {Meta,}Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char // impl {Meta,}Sized for &mut? T, [T; N], dyn* Trait, !, Coroutine, CoroutineWitness // impl {Meta,}Sized for Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Float(_) | ty::FnDef(..) | ty::FnPtr(..) | ty::RawPtr(..) | ty::Char | ty::Ref(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Array(..) | ty::Pat(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), // impl {Meta,}Sized for str, [T], dyn Trait ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { SizedTraitKind::Sized => Err(NoSolution), SizedTraitKind::MetaSized => Ok(ty::Binder::dummy(vec![])), }, // impl {} for extern type ty::Foreign(..) => Err(NoSolution), ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => Err(NoSolution), ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { panic!("unexpected type `{ty:?}`") } ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])), // impl {Meta,}Sized for () // impl {Meta,}Sized for (T1, T2, .., Tn) where Tn: {Meta,}Sized if n >= 1 ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.last().map_or_else(Vec::new, |ty| vec![ty]))), // impl {Meta,}Sized for Adt // where {meta,pointee,}sized_constraint(Adt): {Meta,}Sized // // `{meta,pointee,}sized_constraint(Adt)` is the deepest struct trail that can be // determined by the definition of `Adt`, independent of the generic args. // // impl {Meta,}Sized for Adt // if {meta,pointee,}sized_constraint(Adt) == None // // As a performance optimization, `{meta,pointee,}sized_constraint(Adt)` can return `None` // if the ADTs definition implies that it is {meta,}sized by for all possible args. // In this case, the builtin impl will have no nested subgoals. This is a // "best effort" optimization and `{meta,pointee,}sized_constraint` may return `Some`, // even if the ADT is {meta,pointee,}sized for all possible args. ty::Adt(def, args) => { if let Some(crit) = def.sizedness_constraint(ecx.cx(), sizedness) { Ok(ty::Binder::dummy(vec![crit.instantiate(ecx.cx(), args)])) } else { Ok(ty::Binder::dummy(vec![])) } } } } #[instrument(level = "trace", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait( ecx: &EvalCtxt<'_, D>, ty: I::Ty, ) -> Result>, NoSolution> where D: SolverDelegate, I: Interner, { match ty.kind() { // impl Copy/Clone for FnDef, FnPtr ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), // Implementations are provided in core ty::Uint(_) | ty::Int(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Bool | ty::Float(_) | ty::Char | ty::RawPtr(..) | ty::Never | ty::Ref(_, _, Mutability::Not) | ty::Array(..) => Err(NoSolution), // Cannot implement in core, as we can't be generic over patterns yet, // so we'd have to list all patterns and type combinations. ty::Pat(ty, ..) => Ok(ty::Binder::dummy(vec![ty])), ty::Dynamic(..) | ty::Str | ty::Slice(_) | ty::Foreign(..) | ty::Ref(_, _, Mutability::Mut) | ty::Adt(_, _) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => Err(NoSolution), ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { panic!("unexpected type `{ty:?}`") } // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())), // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])), // impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone ty::CoroutineClosure(_, args) => { Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()])) } // only when `coroutine_clone` is enabled and the coroutine is movable // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) ty::Coroutine(def_id, args) => match ecx.cx().coroutine_movability(def_id) { Movability::Static => Err(NoSolution), Movability::Movable => { if ecx.cx().features().coroutine_clone() { Ok(ty::Binder::dummy(vec![ args.as_coroutine().tupled_upvars_ty(), Ty::new_coroutine_witness_for_coroutine(ecx.cx(), def_id, args), ])) } else { Err(NoSolution) } } }, ty::UnsafeBinder(_) => Err(NoSolution), // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types ty::CoroutineWitness(def_id, args) => Ok(ecx .cx() .coroutine_hidden_types(def_id) .instantiate(ecx.cx(), args) .map_bound(|bound| bound.types.to_vec())), } } // Returns a binder of the tupled inputs types and output type from a builtin callable type. pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( cx: I, self_ty: I::Ty, goal_kind: ty::ClosureKind, ) -> Result>, NoSolution> { match self_ty.kind() { // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. ty::FnDef(def_id, args) => { let sig = cx.fn_sig(def_id); if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) { Ok(Some( sig.instantiate(cx, args) .map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())), )) } else { Err(NoSolution) } } // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. ty::FnPtr(sig_tys, hdr) => { let sig = sig_tys.with(hdr); if sig.is_fn_trait_compatible() { Ok(Some( sig.map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())), )) } else { Err(NoSolution) } } ty::Closure(_, args) => { let closure_args = args.as_closure(); match closure_args.kind_ty().to_opt_closure_kind() { // If the closure's kind doesn't extend the goal kind, // then the closure doesn't implement the trait. Some(closure_kind) => { if !closure_kind.extends(goal_kind) { return Err(NoSolution); } } // Closure kind is not yet determined, so we return ambiguity unless // the expected kind is `FnOnce` as that is always implemented. None => { if goal_kind != ty::ClosureKind::FnOnce { return Ok(None); } } } Ok(Some( closure_args.sig().map_bound(|sig| (sig.inputs().get(0).unwrap(), sig.output())), )) } // Coroutine-closures don't implement `Fn` traits the normal way. // Instead, they always implement `FnOnce`, but only implement // `FnMut`/`Fn` if they capture no upvars, since those may borrow // from the closure. ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); let kind_ty = args.kind_ty(); let sig = args.coroutine_closure_sig().skip_binder(); let coroutine_ty = if let Some(kind) = kind_ty.to_opt_closure_kind() && !args.tupled_upvars_ty().is_ty_var() { if !kind.extends(goal_kind) { return Err(NoSolution); } // A coroutine-closure implements `FnOnce` *always*, since it may // always be called once. It additionally implements `Fn`/`FnMut` // only if it has no upvars referencing the closure-env lifetime, // and if the closure kind permits it. if goal_kind != ty::ClosureKind::FnOnce && args.has_self_borrows() { return Err(NoSolution); } coroutine_closure_to_certain_coroutine( cx, goal_kind, // No captures by ref, so this doesn't matter. Region::new_static(cx), def_id, args, sig, ) } else { // Closure kind is not yet determined, so we return ambiguity unless // the expected kind is `FnOnce` as that is always implemented. if goal_kind != ty::ClosureKind::FnOnce { return Ok(None); } coroutine_closure_to_ambiguous_coroutine( cx, goal_kind, // No captures by ref, so this doesn't matter. Region::new_static(cx), def_id, args, sig, ) }; Ok(Some(args.coroutine_closure_sig().rebind((sig.tupled_inputs_ty, coroutine_ty)))) } ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(_) | ty::Pat(_, _) | ty::UnsafeBinder(_) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Error(_) => Err(NoSolution), ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { panic!("unexpected type `{self_ty:?}`") } } } /// Relevant types for an async callable, including its inputs, output, /// and the return type you get from awaiting the output. #[derive_where(Clone, Copy, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub(in crate::solve) struct AsyncCallableRelevantTypes { pub tupled_inputs_ty: I::Ty, /// Type returned by calling the closure /// i.e. `f()`. pub output_coroutine_ty: I::Ty, /// Type returned by `await`ing the output /// i.e. `f().await`. pub coroutine_return_ty: I::Ty, } // Returns a binder of the tupled inputs types, output type, and coroutine type // from a builtin coroutine-closure type. If we don't yet know the closure kind of // the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper` // which enforces the closure is actually callable with the given trait. When we // know the kind already, we can short-circuit this check. pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable( cx: I, self_ty: I::Ty, goal_kind: ty::ClosureKind, env_region: I::Region, ) -> Result<(ty::Binder>, Vec), NoSolution> { match self_ty.kind() { ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); let kind_ty = args.kind_ty(); let sig = args.coroutine_closure_sig().skip_binder(); let mut nested = vec![]; let coroutine_ty = if let Some(kind) = kind_ty.to_opt_closure_kind() && !args.tupled_upvars_ty().is_ty_var() { if !kind.extends(goal_kind) { return Err(NoSolution); } coroutine_closure_to_certain_coroutine(cx, goal_kind, env_region, def_id, args, sig) } else { // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait // goal functions similarly to the old `ClosureKind` predicate, and ensures that // the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars` // will project to the right upvars for the generator, appending the inputs and // coroutine upvars respecting the closure kind. nested.push( ty::TraitRef::new( cx, cx.require_trait_lang_item(SolverTraitLangItem::AsyncFnKindHelper), [kind_ty, Ty::from_closure_kind(cx, goal_kind)], ) .upcast(cx), ); coroutine_closure_to_ambiguous_coroutine( cx, goal_kind, env_region, def_id, args, sig, ) }; Ok(( args.coroutine_closure_sig().rebind(AsyncCallableRelevantTypes { tupled_inputs_ty: sig.tupled_inputs_ty, output_coroutine_ty: coroutine_ty, coroutine_return_ty: sig.return_ty, }), nested, )) } ty::FnDef(def_id, _) => { let sig = self_ty.fn_sig(cx); if sig.is_fn_trait_compatible() && !cx.has_target_features(def_id) { fn_item_to_async_callable(cx, sig) } else { Err(NoSolution) } } ty::FnPtr(..) => { let sig = self_ty.fn_sig(cx); if sig.is_fn_trait_compatible() { fn_item_to_async_callable(cx, sig) } else { Err(NoSolution) } } ty::Closure(_, args) => { let args = args.as_closure(); let bound_sig = args.sig(); let sig = bound_sig.skip_binder(); let future_trait_def_id = cx.require_trait_lang_item(SolverTraitLangItem::Future); // `Closure`s only implement `AsyncFn*` when their return type // implements `Future`. let mut nested = vec![ bound_sig .rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])) .upcast(cx), ]; // Additionally, we need to check that the closure kind // is still compatible. let kind_ty = args.kind_ty(); if let Some(closure_kind) = kind_ty.to_opt_closure_kind() { if !closure_kind.extends(goal_kind) { return Err(NoSolution); } } else { let async_fn_kind_trait_def_id = cx.require_trait_lang_item(SolverTraitLangItem::AsyncFnKindHelper); // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait // goal functions similarly to the old `ClosureKind` predicate, and ensures that // the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars` // will project to the right upvars for the generator, appending the inputs and // coroutine upvars respecting the closure kind. nested.push( ty::TraitRef::new( cx, async_fn_kind_trait_def_id, [kind_ty, Ty::from_closure_kind(cx, goal_kind)], ) .upcast(cx), ); } let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput); let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { tupled_inputs_ty: sig.inputs().get(0).unwrap(), output_coroutine_ty: sig.output(), coroutine_return_ty: future_output_ty, }), nested, )) } ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) | ty::Pat(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) | ty::Tuple(_) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Error(_) => Err(NoSolution), ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { panic!("unexpected type `{self_ty:?}`") } } } fn fn_item_to_async_callable( cx: I, bound_sig: ty::Binder>, ) -> Result<(ty::Binder>, Vec), NoSolution> { let sig = bound_sig.skip_binder(); let future_trait_def_id = cx.require_trait_lang_item(SolverTraitLangItem::Future); // `FnDef` and `FnPtr` only implement `AsyncFn*` when their // return type implements `Future`. let nested = vec![ bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), ]; let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput); let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), output_coroutine_ty: sig.output(), coroutine_return_ty: future_output_ty, }), nested, )) } /// Given a coroutine-closure, project to its returned coroutine when we are *certain* /// that the closure's kind is compatible with the goal. fn coroutine_closure_to_certain_coroutine( cx: I, goal_kind: ty::ClosureKind, goal_region: I::Region, def_id: I::CoroutineClosureId, args: ty::CoroutineClosureArgs, sig: ty::CoroutineClosureSignature, ) -> I::Ty { sig.to_coroutine_given_kind_and_upvars( cx, args.parent_args(), cx.coroutine_for_closure(def_id), goal_kind, goal_region, args.tupled_upvars_ty(), args.coroutine_captures_by_ref_ty(), ) } /// Given a coroutine-closure, project to its returned coroutine when we are *not certain* /// that the closure's kind is compatible with the goal, and therefore also don't know /// yet what the closure's upvars are. /// /// Note that we do not also push a `AsyncFnKindHelper` goal here. fn coroutine_closure_to_ambiguous_coroutine( cx: I, goal_kind: ty::ClosureKind, goal_region: I::Region, def_id: I::CoroutineClosureId, args: ty::CoroutineClosureArgs, sig: ty::CoroutineClosureSignature, ) -> I::Ty { let upvars_projection_def_id = cx.require_lang_item(SolverLangItem::AsyncFnKindUpvars); let tupled_upvars_ty = Ty::new_projection( cx, upvars_projection_def_id, [ I::GenericArg::from(args.kind_ty()), Ty::from_closure_kind(cx, goal_kind).into(), goal_region.into(), sig.tupled_inputs_ty.into(), args.tupled_upvars_ty().into(), args.coroutine_captures_by_ref_ty().into(), ], ); sig.to_coroutine( cx, args.parent_args(), Ty::from_closure_kind(cx, goal_kind), cx.coroutine_for_closure(def_id), tupled_upvars_ty, ) } /// This duplicates `extract_tupled_inputs_and_output_from_callable` but needs /// to return different information (namely, the def id and args) so that we can /// create const conditions. /// /// Doing so on all calls to `extract_tupled_inputs_and_output_from_callable` /// would be wasteful. pub(in crate::solve) fn extract_fn_def_from_const_callable( cx: I, self_ty: I::Ty, ) -> Result<(ty::Binder, I::FunctionId, I::GenericArgs), NoSolution> { match self_ty.kind() { ty::FnDef(def_id, args) => { let sig = cx.fn_sig(def_id); if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) && cx.fn_is_const(def_id) { Ok(( sig.instantiate(cx, args) .map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())), def_id, args, )) } else { return Err(NoSolution); } } // `FnPtr`s are not const for now. ty::FnPtr(..) => { return Err(NoSolution); } // `Closure`s are not const for now. ty::Closure(..) => { return Err(NoSolution); } // `CoroutineClosure`s are not const for now. ty::CoroutineClosure(..) => { return Err(NoSolution); } ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Adt(_, _) | ty::Foreign(_) | ty::Str | ty::Array(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(_) | ty::Pat(_, _) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Error(_) | ty::UnsafeBinder(_) => return Err(NoSolution), ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { panic!("unexpected type `{self_ty:?}`") } } } // NOTE: Keep this in sync with `evaluate_host_effect_for_destruct_goal` in // the old solver, for as long as that exists. pub(in crate::solve) fn const_conditions_for_destruct( cx: I, self_ty: I::Ty, ) -> Result>, NoSolution> { let destruct_def_id = cx.require_trait_lang_item(SolverTraitLangItem::Destruct); match self_ty.kind() { // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it. ty::Adt(adt_def, _) if adt_def.is_manually_drop() => Ok(vec![]), // An ADT is `[const] Destruct` only if all of the fields are, // *and* if there is a `Drop` impl, that `Drop` impl is also `[const]`. ty::Adt(adt_def, args) => { let mut const_conditions: Vec<_> = adt_def .all_field_tys(cx) .iter_instantiated(cx, args) .map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty])) .collect(); match adt_def.destructor(cx) { // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`. Some(AdtDestructorKind::NotConst) => return Err(NoSolution), // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold. Some(AdtDestructorKind::Const) => { let drop_def_id = cx.require_trait_lang_item(SolverTraitLangItem::Drop); let drop_trait_ref = ty::TraitRef::new(cx, drop_def_id, [self_ty]); const_conditions.push(drop_trait_ref); } // No `Drop` impl, no need to require anything else. None => {} } Ok(const_conditions) } ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => { Ok(vec![ty::TraitRef::new(cx, destruct_def_id, [ty])]) } ty::Tuple(tys) => Ok(tys .iter() .map(|field_ty| ty::TraitRef::new(cx, destruct_def_id, [field_ty])) .collect()), // Trivially implement `[const] Destruct` ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Never | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) | ty::Error(_) => Ok(vec![]), // Coroutines and closures could implement `[const] Drop`, // but they don't really need to right now. ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(_, _) => Err(NoSolution), // FIXME(unsafe_binders): Unsafe binders could implement `[const] Drop` // if their inner type implements it. ty::UnsafeBinder(_) => Err(NoSolution), ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => { Err(NoSolution) } ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { panic!("unexpected type `{self_ty:?}`") } } } /// Assemble a list of predicates that would be present on a theoretical /// user impl for an object type. These predicates must be checked any time /// we assemble a built-in object candidate for an object type, since they /// are not implied by the well-formedness of the type. /// /// For example, given the following traits: /// /// ```rust,ignore (theoretical code) /// trait Foo: Baz { /// type Bar: Copy; /// } /// /// trait Baz {} /// ``` /// /// For the dyn type `dyn Foo`, we can imagine there being a /// pair of theoretical impls: /// /// ```rust,ignore (theoretical code) /// impl Foo for dyn Foo /// where /// Self: Baz, /// ::Bar: Copy, /// { /// type Bar = Ty; /// } /// /// impl Baz for dyn Foo {} /// ``` /// /// However, in order to make such impls non-cyclical, we need to do an /// additional step of eagerly folding the associated types in the where /// clauses of the impl. In this example, that means replacing /// `::Bar` with `Ty` in the first impl. pub(in crate::solve) fn predicates_for_object_candidate( ecx: &mut EvalCtxt<'_, D>, param_env: I::ParamEnv, trait_ref: ty::TraitRef, object_bounds: I::BoundExistentialPredicates, ) -> Result>, Ambiguous> where D: SolverDelegate, I: Interner, { let cx = ecx.cx(); let mut requirements = vec![]; // Elaborating all supertrait outlives obligations here is not soundness critical, // since if we just used the unelaborated set, then the transitive supertraits would // be reachable when proving the former. However, since we elaborate all supertrait // outlives obligations when confirming impls, we would end up with a different set // of outlives obligations here if we didn't do the same, leading to ambiguity. // FIXME(-Znext-solver=coinductive): Adding supertraits here can be removed once we // make impls coinductive always, since they'll always need to prove their supertraits. requirements.extend(elaborate::elaborate( cx, cx.explicit_super_predicates_of(trait_ref.def_id) .iter_instantiated(cx, trait_ref.args) .map(|(pred, _)| pred), )); // FIXME(associated_const_equality): Also add associated consts to // the requirements here. for associated_type_def_id in cx.associated_type_def_ids(trait_ref.def_id.into()) { // associated types that require `Self: Sized` do not show up in the built-in // implementation of `Trait for dyn Trait`, and can be dropped here. if cx.generics_require_sized_self(associated_type_def_id) { continue; } requirements .extend(cx.item_bounds(associated_type_def_id).iter_instantiated(cx, trait_ref.args)); } let mut replace_projection_with: HashMap<_, Vec<_>> = HashMap::default(); for bound in object_bounds.iter() { if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() { // FIXME: We *probably* should replace this with a dummy placeholder, // b/c don't want to replace literal instances of this dyn type that // show up in the bounds, but just ones that come from substituting // `Self` with the dyn type. let proj = proj.with_self_ty(cx, trait_ref.self_ty()); replace_projection_with.entry(proj.def_id()).or_default().push(bound.rebind(proj)); } } let mut folder = ReplaceProjectionWith { ecx, param_env, self_ty: trait_ref.self_ty(), mapping: &replace_projection_with, nested: vec![], }; let requirements = requirements.try_fold_with(&mut folder)?; Ok(folder .nested .into_iter() .chain(requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause))) .collect()) } struct ReplaceProjectionWith<'a, 'b, I: Interner, D: SolverDelegate> { ecx: &'a mut EvalCtxt<'b, D>, param_env: I::ParamEnv, self_ty: I::Ty, mapping: &'a HashMap>>>, nested: Vec>, } impl ReplaceProjectionWith<'_, '_, I, D> where D: SolverDelegate, I: Interner, { fn projection_may_match( &mut self, source_projection: ty::Binder>, target_projection: ty::AliasTerm, ) -> bool { source_projection.item_def_id() == target_projection.def_id && self .ecx .probe(|_| ProbeKind::ProjectionCompatibility) .enter(|ecx| -> Result<_, NoSolution> { let source_projection = ecx.instantiate_binder_with_infer(source_projection); ecx.eq(self.param_env, source_projection.projection_term, target_projection)?; ecx.try_evaluate_added_goals() }) .is_ok() } /// Try to replace an alias with the term present in the projection bounds of the self type. /// Returns `Ok` if this alias is not eligible to be replaced, or bail with /// `Err(Ambiguous)` if it's uncertain which projection bound to replace the term with due /// to multiple bounds applying. fn try_eagerly_replace_alias( &mut self, alias_term: ty::AliasTerm, ) -> Result, Ambiguous> { if alias_term.self_ty() != self.self_ty { return Ok(None); } let Some(replacements) = self.mapping.get(&alias_term.def_id) else { return Ok(None); }; // This is quite similar to the `projection_may_match` we use in unsizing, // but here we want to unify a projection predicate against an alias term // so we can replace it with the projection predicate's term. let mut matching_projections = replacements .iter() .filter(|source_projection| self.projection_may_match(**source_projection, alias_term)); let Some(replacement) = matching_projections.next() else { // This shouldn't happen. panic!("could not replace {alias_term:?} with term from from {:?}", self.self_ty); }; // FIXME: This *may* have issues with duplicated projections. if matching_projections.next().is_some() { // If there's more than one projection that we can unify here, then we // need to stall until inference constrains things so that there's only // one choice. return Err(Ambiguous); } let replacement = self.ecx.instantiate_binder_with_infer(*replacement); self.nested.extend( self.ecx .eq_and_get_goals(self.param_env, alias_term, replacement.projection_term) .expect("expected to be able to unify goal projection with dyn's projection"), ); Ok(Some(replacement.term)) } } /// Marker for bailing with ambiguity. pub(crate) struct Ambiguous; impl FallibleTypeFolder for ReplaceProjectionWith<'_, '_, I, D> where D: SolverDelegate, I: Interner, { type Error = Ambiguous; fn cx(&self) -> I { self.ecx.cx() } fn try_fold_ty(&mut self, ty: I::Ty) -> Result { if let ty::Alias(ty::Projection, alias_ty) = ty.kind() && let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())? { Ok(term.expect_ty()) } else { ty.try_super_fold_with(self) } } }