diff options
Diffstat (limited to 'compiler/rustc_next_trait_solver/src')
8 files changed, 314 insertions, 191 deletions
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 7eeed721d5a..9cae7f27947 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use rustc_type_ir::data_structures::HashMap; +use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack}; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::solve::{Goal, QueryInput}; @@ -389,7 +389,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { | ty::Alias(_, _) | ty::Bound(_, _) | ty::Error(_) => { - return t.super_fold_with(self); + return ensure_sufficient_stack(|| t.super_fold_with(self)); } }; diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index d8c1dc8b4e9..0fc313e33b3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -34,7 +34,17 @@ where ) -> QueryResult<I> { let cx = self.cx(); let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; - debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some()); + + // Check that the alias-relate goal is reasonable. Writeback for + // `coroutine_stalled_predicates` can replace alias terms with + // `{type error}` if the alias still contains infer vars, so we also + // accept alias-relate goals where one of the terms is an error. + debug_assert!( + lhs.to_alias_term().is_some() + || rhs.to_alias_term().is_some() + || lhs.is_error() + || rhs.is_error() + ); // Structurally normalize the lhs. let lhs = if let Some(alias) = lhs.to_alias_term() { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index d0b01b14d63..bfb590e8767 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -6,7 +6,6 @@ use derive_where::derive_where; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::solve::inspect; use rustc_type_ir::visit::TypeVisitableExt as _; use rustc_type_ir::{self as ty, Interner, TypingMode, Upcast as _, elaborate}; use tracing::{debug, instrument}; @@ -267,6 +266,11 @@ where goal: Goal<I, Self>, ) -> Result<Candidate<I>, NoSolution>; + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution>; + /// Consider (possibly several) candidates to upcast or unsize a type to another /// type, excluding the coercion of a sized type into a `dyn Trait`. /// @@ -292,25 +296,6 @@ where let Ok(normalized_self_ty) = self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty()) else { - // FIXME: We register a fake candidate when normalization fails so that - // we can point at the reason for *why*. I'm tempted to say that this - // is the wrong way to do this, though. - let result = - self.probe(|&result| inspect::ProbeKind::RigidAlias { result }).enter(|this| { - let normalized_ty = this.next_ty_infer(); - let alias_relate_goal = Goal::new( - this.cx(), - goal.param_env, - ty::PredicateKind::AliasRelate( - goal.predicate.self_ty().into(), - normalized_ty.into(), - ty::AliasRelationDirection::Equate, - ), - ); - this.add_goal(GoalSource::AliasWellFormed, alias_relate_goal); - this.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - }); - assert_eq!(result, Err(NoSolution)); return vec![]; }; @@ -478,6 +463,9 @@ where Some(TraitSolverLangItem::TransmuteTrait) => { G::consider_builtin_transmute_candidate(self, goal) } + Some(TraitSolverLangItem::BikeshedGuaranteedNoDrop) => { + G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) + } _ => Err(NoSolution), } }; @@ -789,11 +777,12 @@ where /// treat the alias as rigid. /// /// See trait-system-refactor-initiative#124 for more details. - #[instrument(level = "debug", skip(self), ret)] + #[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)] pub(super) fn merge_candidates( &mut self, proven_via: Option<TraitGoalProvenVia>, candidates: Vec<Candidate<I>>, + inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>, ) -> QueryResult<I> { let Some(proven_via) = proven_via else { // We don't care about overflow. If proving the trait goal overflowed, then @@ -802,7 +791,7 @@ where return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Ambiguity)); }; - let responses: Vec<_> = match proven_via { + match proven_via { // Even when a trait bound has been proven using a where-bound, we // still need to consider alias-bounds for normalization, see // tests/ui/next-solver/alias-bound-shadowed-by-env.rs. @@ -810,16 +799,51 @@ where // FIXME(const_trait_impl): should this behavior also be used by // constness checking. Doing so is *at least theoretically* breaking, // see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754 - TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => candidates - .iter() - .filter(|c| { - matches!(c.source, CandidateSource::AliasBound | CandidateSource::ParamEnv(_)) - }) - .map(|c| c.result) - .collect(), - TraitGoalProvenVia::Misc => candidates.iter().map(|c| c.result).collect(), - }; + TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => { + let mut candidates_from_env_and_bounds: Vec<_> = candidates + .iter() + .filter(|c| { + matches!( + c.source, + CandidateSource::AliasBound | CandidateSource::ParamEnv(_) + ) + }) + .map(|c| c.result) + .collect(); + + // If the trait goal has been proven by using the environment, we want to treat + // aliases as rigid if there are no applicable projection bounds in the environment. + if candidates_from_env_and_bounds.is_empty() { + if let Ok(response) = inject_normalize_to_rigid_candidate(self) { + candidates_from_env_and_bounds.push(response); + } + } - self.try_merge_responses(&responses).map_or_else(|| self.flounder(&responses), Ok) + if let Some(response) = self.try_merge_responses(&candidates_from_env_and_bounds) { + Ok(response) + } else { + self.flounder(&candidates_from_env_and_bounds) + } + } + TraitGoalProvenVia::Misc => { + // Prefer "orphaned" param-env normalization predicates, which are used + // (for example, and ideally only) when proving item bounds for an impl. + let candidates_from_env: Vec<_> = candidates + .iter() + .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + .map(|c| c.result) + .collect(); + if let Some(response) = self.try_merge_responses(&candidates_from_env) { + return Ok(response); + } + + let responses: Vec<_> = candidates.iter().map(|c| c.result).collect(); + if let Some(response) = self.try_merge_responses(&responses) { + Ok(response) + } else { + self.flounder(&responses) + } + } + } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 082c356cc5f..dc0f4c4483e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -2,12 +2,11 @@ //! traits, `Copy`/`Clone`. use derive_where::derive_where; -use rustc_ast_ir::{Movability, Mutability}; use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::{self as ty, Interner, Upcast as _, elaborate}; +use rustc_type_ir::{self as ty, Interner, Movability, Mutability, Upcast as _, elaborate}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index fb53e778e60..0b61c368d8e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -374,6 +374,13 @@ where unreachable!("TransmuteFrom is not const") } + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("BikeshedGuaranteedNoDrop is not const"); + } + fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal<I, Self>, @@ -398,6 +405,6 @@ where goal.with(ecx.cx(), goal.predicate.trait_ref); ecx.compute_trait_goal(trait_goal) })?; - self.merge_candidates(proven_via, candidates) + self.merge_candidates(proven_via, candidates, |_ecx| Err(NoSolution)) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index c8ea0b119ff..88002e1a88a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -30,75 +30,26 @@ where ) -> QueryResult<I> { self.set_is_normalizes_to_goal(); debug_assert!(self.term_is_fully_unconstrained(goal)); - let normalize_result = self - .probe(|&result| ProbeKind::TryNormalizeNonRigid { result }) - .enter(|this| this.normalize_at_least_one_step(goal)); - - match normalize_result { - Ok(res) => Ok(res), - Err(NoSolution) => { - self.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { - let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal; - this.add_rigid_constraints(param_env, alias)?; - this.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?; - this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - } - } - - /// Register any obligations that are used to validate that an alias should be - /// treated as rigid. - /// - /// An alias may be considered rigid if it fails normalization, but we also don't - /// want to consider aliases that are not well-formed to be rigid simply because - /// they fail normalization. - /// - /// For example, some `<T as Trait>::Assoc` where `T: Trait` does not hold, or an - /// opaque type whose hidden type doesn't actually satisfy the opaque item bounds. - fn add_rigid_constraints( - &mut self, - param_env: I::ParamEnv, - rigid_alias: ty::AliasTerm<I>, - ) -> Result<(), NoSolution> { - let cx = self.cx(); - match rigid_alias.kind(cx) { - // Projections are rigid only if their trait ref holds, - // and the GAT where-clauses hold. - ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { - let trait_ref = rigid_alias.trait_ref(cx); - self.add_goal(GoalSource::AliasWellFormed, Goal::new(cx, param_env, trait_ref)); - Ok(()) - } - ty::AliasTermKind::OpaqueTy => { - if self.opaque_type_is_rigid(rigid_alias.def_id) { - Ok(()) - } else { - Err(NoSolution) - } - } - // FIXME(generic_const_exprs): we would need to support generic consts here - ty::AliasTermKind::UnevaluatedConst => Err(NoSolution), - // Inherent and weak types are never rigid. This type must not be well-formed. - ty::AliasTermKind::WeakTy | ty::AliasTermKind::InherentTy => Err(NoSolution), - } - } - - /// Normalize the given alias by at least one step. If the alias is rigid, this - /// returns `NoSolution`. - #[instrument(level = "trace", skip(self), ret)] - fn normalize_at_least_one_step(&mut self, goal: Goal<I, NormalizesTo<I>>) -> QueryResult<I> { let cx = self.cx(); match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { let candidates = self.assemble_and_evaluate_candidates(goal); + let trait_ref = goal.predicate.alias.trait_ref(cx); let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { - let trait_goal: Goal<I, ty::TraitPredicate<I>> = - goal.with(cx, goal.predicate.alias.trait_ref(cx)); + let trait_goal: Goal<I, ty::TraitPredicate<I>> = goal.with(cx, trait_ref); ecx.compute_trait_goal(trait_goal) })?; - self.merge_candidates(proven_via, candidates) + self.merge_candidates(proven_via, candidates, |ecx| { + ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.add_goal(GoalSource::AliasWellFormed, goal.with(cx, trait_ref)); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }) } ty::AliasTermKind::InherentTy => self.normalize_inherent_associated_type(goal), ty::AliasTermKind::OpaqueTy => self.normalize_opaque_type(goal), @@ -120,6 +71,17 @@ where self.eq(goal.param_env, goal.predicate.term, term) .expect("expected goal term to be fully unconstrained"); } + + /// Unlike `instantiate_normalizes_to_term` this instantiates the expected term + /// with a rigid alias. Using this is pretty much always wrong. + pub fn structurally_instantiate_normalizes_to_term( + &mut self, + goal: Goal<I, NormalizesTo<I>>, + term: ty::AliasTerm<I>, + ) { + self.relate_rigid_alias_non_alias(goal.param_env, term, ty::Invariant, goal.predicate.term) + .expect("expected goal term to be fully unconstrained"); + } } impl<D, I> assembly::GoalKind<D> for NormalizesTo<I> @@ -576,80 +538,92 @@ where let cx = ecx.cx(); let metadata_def_id = cx.require_lang_item(TraitSolverLangItem::Metadata); assert_eq!(metadata_def_id, goal.predicate.def_id()); - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - let metadata_ty = match goal.predicate.self_ty().kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Array(..) - | ty::Pat(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never - | ty::Foreign(..) - | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(cx), - - ty::Error(e) => Ty::new_error(cx, e), - - ty::Str | ty::Slice(_) => Ty::new_usize(cx), - - ty::Dynamic(_, _, ty::Dyn) => { - let dyn_metadata = cx.require_lang_item(TraitSolverLangItem::DynMetadata); - cx.type_of(dyn_metadata) - .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) - } + let metadata_ty = match goal.predicate.self_ty().kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::Pat(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Foreign(..) + | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(cx), - ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - // This is the "fallback impl" for type parameters, unnormalizable projections - // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. - // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't - // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`. - let sized_predicate = ty::TraitRef::new( - cx, - cx.require_lang_item(TraitSolverLangItem::Sized), - [I::GenericArg::from(goal.predicate.self_ty())], - ); - // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? - ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate)); - Ty::new_unit(cx) - } + ty::Error(e) => Ty::new_error(cx, e), - ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(cx) { - None => Ty::new_unit(cx), - Some(tail_ty) => { - Ty::new_projection(cx, metadata_def_id, [tail_ty.instantiate(cx, args)]) - } - }, - ty::Adt(_, _) => Ty::new_unit(cx), + ty::Str | ty::Slice(_) => Ty::new_usize(cx), - ty::Tuple(elements) => match elements.last() { - None => Ty::new_unit(cx), - Some(tail_ty) => Ty::new_projection(cx, metadata_def_id, [tail_ty]), - }, + ty::Dynamic(_, _, ty::Dyn) => { + let dyn_metadata = cx.require_lang_item(TraitSolverLangItem::DynMetadata); + cx.type_of(dyn_metadata) + .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) + } - ty::UnsafeBinder(_) => { - // FIXME(unsafe_binder): Figure out how to handle pointee for unsafe binders. - todo!() + ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + // This is the "fallback impl" for type parameters, unnormalizable projections + // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. + // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't + // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`. + let alias_bound_result = + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let sized_predicate = ty::TraitRef::new( + cx, + cx.require_lang_item(TraitSolverLangItem::Sized), + [I::GenericArg::from(goal.predicate.self_ty())], + ); + ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate)); + ecx.instantiate_normalizes_to_term(goal, Ty::new_unit(cx).into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); + // In case the dummy alias-bound candidate does not apply, we instead treat this projection + // as rigid. + return alias_bound_result.or_else(|NoSolution| { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }); + } + + ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(cx) { + None => Ty::new_unit(cx), + Some(tail_ty) => { + Ty::new_projection(cx, metadata_def_id, [tail_ty.instantiate(cx, args)]) } + }, + ty::Adt(_, _) => Ty::new_unit(cx), - ty::Infer( - ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), - ) - | ty::Bound(..) => panic!( - "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`", - goal.predicate.self_ty() - ), - }; + ty::Tuple(elements) => match elements.last() { + None => Ty::new_unit(cx), + Some(tail_ty) => Ty::new_projection(cx, metadata_def_id, [tail_ty]), + }, + + ty::UnsafeBinder(_) => { + // FIXME(unsafe_binder): Figure out how to handle pointee for unsafe binders. + todo!() + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Bound(..) => panic!( + "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`", + goal.predicate.self_ty() + ), + }; + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.instantiate_normalizes_to_term(goal, metadata_ty.into()); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -850,12 +824,14 @@ where todo!("discr subgoal...") } - // We do not call `Ty::discriminant_ty` on alias, param, or placeholder - // types, which return `<self_ty as DiscriminantKind>::Discriminant` - // (or ICE in the case of placeholders). Projecting a type to itself - // is never really productive. + // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid + // alias. In case there's a where-bound further constraining this alias it is preferred over + // this impl candidate anyways. It's still a bit scuffed. ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - return Err(NoSolution); + return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); } ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) @@ -902,12 +878,14 @@ where todo!() } - // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder - // types, which return `<self_ty as AsyncDestruct>::AsyncDestructor` - // (or ICE in the case of placeholders). Projecting a type to itself - // is never really productive. + // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid + // alias. In case there's a where-bound further constraining this alias it is preferred over + // this impl candidate anyways. It's still a bit scuffed. ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - return Err(NoSolution); + return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); } ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) @@ -942,6 +920,13 @@ where ) -> Result<Candidate<I>, NoSolution> { panic!("`TransmuteFrom` does not have an associated type: {:?}", goal) } + + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + _ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) + } } impl<D, I> EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 26a8a22d77e..60c20762a30 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -35,14 +35,15 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } TypingMode::Analysis { defining_opaque_types } => { - let Some(def_id) = opaque_ty.def_id.as_local() else { - return Err(NoSolution); + let Some(def_id) = opaque_ty + .def_id + .as_local() + .filter(|&def_id| defining_opaque_types.contains(&def_id)) + else { + self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); }; - if !defining_opaque_types.contains(&def_id) { - return Err(NoSolution); - } - // FIXME: This may have issues when the args contain aliases... match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) { Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => { @@ -97,15 +98,16 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } TypingMode::PostBorrowckAnalysis { defined_opaque_types } => { - let Some(def_id) = opaque_ty.def_id.as_local() else { - return Err(NoSolution); + let Some(def_id) = opaque_ty + .def_id + .as_local() + .filter(|&def_id| defined_opaque_types.contains(&def_id)) + else { + self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); }; - if !defined_opaque_types.contains(&def_id) { - return Err(NoSolution); - } - - let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args); + let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args); // FIXME: Actually use a proper binder here instead of relying on `ReErased`. // // This is also probably unsound or sth :shrug: diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 46fae5c6fa4..1665dbb3018 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1,13 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use rustc_ast_ir::Movability; use rustc_type_ir::data_structures::IndexSet; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::CanonicalResponse; use rustc_type_ir::visit::TypeVisitableExt as _; -use rustc_type_ir::{self as ty, Interner, TraitPredicate, TypingMode, Upcast as _, elaborate}; +use rustc_type_ir::{ + self as ty, Interner, Movability, TraitPredicate, TypingMode, Upcast as _, elaborate, +}; use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; @@ -625,6 +626,101 @@ where }) } + /// NOTE: This is implemented as a built-in goal and not a set of impls like: + /// + /// ```rust,ignore (illustrative) + /// impl<T> BikeshedGuaranteedNoDrop for T where T: Copy {} + /// impl<T> BikeshedGuaranteedNoDrop for ManuallyDrop<T> {} + /// ``` + /// + /// because these impls overlap, and I'd rather not build a coherence hack for + /// this harmless overlap. + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + let cx = ecx.cx(); + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let ty = goal.predicate.self_ty(); + match ty.kind() { + // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`. + ty::Ref(..) => {} + // `ManuallyDrop<T>` always implements `BikeshedGuaranteedNoDrop`. + ty::Adt(def, _) if def.is_manually_drop() => {} + // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if + // their constituent types implement `BikeshedGuaranteedNoDrop`. + ty::Tuple(tys) => { + ecx.add_goals( + GoalSource::ImplWhereBound, + tys.iter().map(|elem_ty| { + goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])) + }), + ); + } + ty::Array(elem_ty, _) => { + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])), + ); + } + + // All other types implement `BikeshedGuaranteedNoDrop` only if + // they implement `Copy`. We could be smart here and short-circuit + // some trivially `Copy`/`!Copy` types, but there's no benefit. + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) => { + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with( + cx, + ty::TraitRef::new( + cx, + cx.require_lang_item(TraitSolverLangItem::Copy), + [ty], + ), + ), + ); + } + + ty::Bound(..) + | ty::Infer( + ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), + ) => { + panic!("unexpected type `{ty:?}`") + } + } + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + /// ```ignore (builtin impl example) /// trait Trait { /// fn foo(&self); |
