diff options
| author | Michael Goulet <michael@errs.io> | 2023-07-20 18:36:34 +0000 |
|---|---|---|
| committer | Michael Goulet <michael@errs.io> | 2023-07-25 15:15:25 +0000 |
| commit | de81007d1310580e6060f2356f1938e45cafb92c (patch) | |
| tree | bd45f9fbceb054827520e7ebbc22aecdea534c09 /compiler | |
| parent | c02d1a65534244532aba0f42a6719d87084865d2 (diff) | |
| download | rust-de81007d1310580e6060f2356f1938e45cafb92c.tar.gz rust-de81007d1310580e6060f2356f1938e45cafb92c.zip | |
Consolidate trait upcasting and unsize into one normalization
Diffstat (limited to 'compiler')
4 files changed, 237 insertions, 197 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 6920e790e71..942f4088472 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -94,6 +94,7 @@ pub(super) enum CandidateSource { #[derive(Debug, Clone, Copy)] pub(super) enum BuiltinImplSource { TraitUpcasting, + TupleUnsize, Object, Misc, Ambiguity, @@ -281,20 +282,19 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + /// Consider (possibly several) goals to upcast or unsize a type to another + /// type. + /// /// The most common forms of unsizing are array to slice, and concrete (Sized) /// type into a `dyn Trait`. ADTs and Tuples can also have their final field /// unsized if it's generic. - fn consider_builtin_unsize_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; - + /// /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or /// if `Trait2` is a (transitive) supertrait of `Trait2`. - fn consider_builtin_dyn_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + fn consider_builtin_unsize_and_upcast_candidates( + _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec<CanonicalResponse<'tcx>>; + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, @@ -610,8 +610,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_future_candidate(self, goal) } else if lang_items.gen_trait() == Some(trait_def_id) { G::consider_builtin_generator_candidate(self, goal) - } else if lang_items.unsize_trait() == Some(trait_def_id) { - G::consider_builtin_unsize_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { G::consider_builtin_discriminant_kind_candidate(self, goal) } else if lang_items.destruct_trait() == Some(trait_def_id) { @@ -633,11 +631,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>` if lang_items.unsize_trait() == Some(trait_def_id) { - for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { - candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting), - result, - }); + for (result, source) in G::consider_builtin_unsize_and_upcast_candidates(self, goal) { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result }); } } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index e2f91d7668d..cdfbaedb8c2 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -116,10 +116,19 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { ), ) => rematch_object(self, goal, nested_obligations), - (Certainty::Maybe(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) + ( + Certainty::Maybe(_), + CandidateSource::BuiltinImpl( + BuiltinImplSource::Misc | BuiltinImplSource::TupleUnsize, + ), + ) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { + rematch_unsize(self, goal, nested_obligations) + } + + (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::TupleUnsize)) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { - rematch_unsize(self, goal, nested_obligations) + Ok(Some(ImplSource::TupleUnsizing(nested_obligations))) } // Technically some builtin impls have nested obligations, but if diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 222ed9939ba..0f4fec50427 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,6 +1,6 @@ use crate::traits::specialization_graph; -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, BuiltinImplSource}; use super::EvalCtxt; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -502,17 +502,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidate( + fn consider_builtin_unsize_and_upcast_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Unsize` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_dyn_upcast_candidates( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec<CanonicalResponse<'tcx>> { + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { bug!("`Unsize` does not have an associated type: {:?}", goal); } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 69b30f0a1a8..7f813c58c84 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,6 +1,6 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, BuiltinImplSource}; use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; @@ -8,7 +8,7 @@ use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::Reveal; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -367,7 +367,59 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidate( + fn consider_builtin_unsize_and_upcast_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return vec![]; + } + + ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { + let a_ty = goal.predicate.self_ty(); + // We need to normalize the b_ty since it's matched structurally + // in the other functions below. + let b_ty = match ecx + .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) + { + Ok(Some(b_ty)) if !b_ty.is_ty_var() => b_ty, + Ok(_) => { + return vec![( + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .unwrap(), + BuiltinImplSource::Ambiguity, + )]; + } + Err(_) => return vec![], + }; + + let mut results = vec![]; + results.extend( + ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty) + .into_iter() + .map(|resp| (resp, BuiltinImplSource::TraitUpcasting)), + ); + results.extend( + ecx.consider_builtin_unsize_candidate(goal.param_env, a_ty, b_ty).into_iter().map( + |resp| { + // If we're unsizing from tuple -> tuple, detect + let source = + if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..))) + { + BuiltinImplSource::TupleUnsize + } else { + BuiltinImplSource::Misc + }; + (resp, source) + }, + ), + ); + + results + }) + } + + fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { @@ -375,20 +427,72 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); } - let tcx = ecx.tcx(); - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); + // `DiscriminantKind` is automatically implemented for every type. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } - ecx.probe_candidate("builtin unsize").enter(|ecx| { - let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( - MaybeCause::Overflow, - )); - }; + fn consider_builtin_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + if !goal.param_env.is_const() { + // `Destruct` is automatically implemented for every type in + // non-const environments. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver + Err(NoSolution) + } + } + + fn consider_builtin_transmute_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + // `rustc_transmute` does not have support for type or const params + if goal.has_non_region_placeholders() { + return Err(NoSolution); + } + + // Erase regions because we compute layouts in `rustc_transmute`, + // which will ICE for region vars. + let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); + + let Some(assume) = + rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3)) + else { + return Err(NoSolution); + }; + let certainty = ecx.is_transmutable( + rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, + args.type_at(2), + assume, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + } +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + fn consider_builtin_unsize_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, + ) -> QueryResult<'tcx> { + self.probe_candidate("builtin unsize").enter(|ecx| { + let tcx = ecx.tcx(); match (a_ty.kind(), b_ty.kind()) { - (_, ty::Infer(ty::TyVar(_))) => { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => { + bug!("unexpected type variable in unsize goal") } // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { @@ -413,20 +517,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Check that the type implements all of the predicates of the def-id. // (i.e. the principal, all of the associated types match, and any auto traits) ecx.add_goals( - data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), + data.iter() + .map(|pred| Goal::new(tcx, param_env, pred.with_self_ty(tcx, a_ty))), ); // The type must be Sized to be unsized. - ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty]))); + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::TraitRef::new(tcx, sized_def_id, [a_ty]), + )); // The type must outlive the lifetime of the `dyn` we're unsizing into. - ecx.add_goal( - goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))), - ); + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), + )); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // `[T; n]` -> `[T]` unsizing (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { // We just require that the element type stays the same - ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?; + ecx.eq(param_env, a_elem_ty, b_elem_ty)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>` @@ -457,10 +568,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Finally, we require that `TailA: Unsize<TailB>` for the tail field // types. - ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; - ecx.add_goal(goal.with( + ecx.eq(param_env, unsized_a_ty, b_ty)?; + ecx.add_goal(Goal::new( tcx, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), + param_env, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_tail_ty, b_tail_ty], + ), )); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } @@ -474,12 +590,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Substitute just the tail field of B., and require that they're equal. let unsized_a_ty = Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); - ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; + ecx.eq(param_env, unsized_a_ty, b_ty)?; // Similar to ADTs, require that the rest of the fields are equal. - ecx.add_goal(goal.with( + ecx.add_goal(Goal::new( tcx, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), + param_env, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [*a_last_ty, *b_last_ty], + ), )); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } @@ -489,164 +610,86 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_dyn_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + &mut self, + param_env: ty::ParamEnv<'tcx>, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, ) -> Vec<CanonicalResponse<'tcx>> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return vec![]; + if a_ty.is_ty_var() || b_ty.is_ty_var() { + bug!("unexpected type variable in unsize goal") } - let tcx = ecx.tcx(); - - // Need to wrap in a probe since `normalize_non_self_ty` has side-effects. - ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { - return vec![]; - }; + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + return vec![]; + }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + return vec![]; + }; - // We don't care about `ty::Infer` here or errors here, since we'll - // register an ambiguous/error response in the other unsize candidate - // assembly function. - let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else { - return vec![]; - }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { - return vec![]; - }; + let tcx = self.tcx(); + // All of a's auto traits need to be in b's auto traits. + let auto_traits_compatible = + b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); + if !auto_traits_compatible { + return vec![]; + } - // All of a's auto traits need to be in b's auto traits. - let auto_traits_compatible = - b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); - if !auto_traits_compatible { - return vec![]; - } + let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| { + self.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - let mut unsize_dyn_to_principal = |principal: Option< - ty::PolyExistentialTraitRef<'tcx>, - >| { - ecx.probe_candidate("upcast dyn to principle").enter( - |ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - ) - }; + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(param_env, new_a_ty, b_ty)?; + ecx.add_goal(Goal::new( + tcx, + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }; - let mut responses = vec![]; - // If the principal def ids match (or are both none), then we're not doing - // trait upcasting. We're just removing auto traits (or shortening the lifetime). - if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { - responses.push(response); + let mut responses = vec![]; + // If the principal def ids match (or are both none), then we're not doing + // trait upcasting. We're just removing auto traits (or shortening the lifetime). + if a_data.principal_def_id() == b_data.principal_def_id() { + if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { + responses.push(response); + } + } else if let Some(a_principal) = a_data.principal() + && let Some(b_principal) = b_data.principal() + { + for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { + if super_trait_ref.def_id() != b_principal.def_id() { + continue; } - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() - { - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { - if super_trait_ref.def_id() != b_principal.def_id() { - continue; - } - let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| { - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) - }); - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { - responses.push(response); - } + let erased_trait_ref = super_trait_ref + .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { + responses.push(response); } } - - responses - }) - } - - fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); - } - - // `DiscriminantKind` is automatically implemented for every type. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - - fn consider_builtin_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); } - if !goal.param_env.is_const() { - // `Destruct` is automatically implemented for every type in - // non-const environments. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver - Err(NoSolution) - } + responses } - fn consider_builtin_transmute_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); - } - - // `rustc_transmute` does not have support for type or const params - if goal.has_non_region_placeholders() { - return Err(NoSolution); - } - - // Erase regions because we compute layouts in `rustc_transmute`, - // which will ICE for region vars. - let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); - - let Some(assume) = - rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3)) - else { - return Err(NoSolution); - }; - - let certainty = ecx.is_transmutable( - rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, - args.type_at(2), - assume, - )?; - ecx.evaluate_added_goals_and_make_canonical_response(certainty) - } -} - -impl<'tcx> EvalCtxt<'_, 'tcx> { // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering |
