diff options
9 files changed, 118 insertions, 31 deletions
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 345a272895d..542e212e1bf 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -2,6 +2,7 @@ pub(super) mod structural_traits; +use std::cell::Cell; use std::ops::ControlFlow; use derive_where::derive_where; @@ -117,24 +118,24 @@ where ) -> Result<Candidate<I>, NoSolution> { Self::fast_reject_assumption(ecx, goal, assumption)?; - ecx.probe(|candidate: &Result<Candidate<I>, NoSolution>| match candidate { - Ok(candidate) => inspect::ProbeKind::TraitCandidate { - source: candidate.source, - result: Ok(candidate.result), - }, - Err(NoSolution) => inspect::ProbeKind::TraitCandidate { - source: CandidateSource::ParamEnv(ParamEnvSource::Global), - result: Err(NoSolution), - }, + // Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily + // check whether the candidate is global while considering normalization. + // + // We need to write into `source` inside of `match_assumption`, but need to access it + // in `probe` even if the candidate does not apply before we get there. We handle this + // by using a `Cell` here. We only ever write into it inside of `match_assumption`. + let source = Cell::new(CandidateSource::ParamEnv(ParamEnvSource::Global)); + ecx.probe(|result: &QueryResult<I>| inspect::ProbeKind::TraitCandidate { + source: source.get(), + result: *result, }) .enter(|ecx| { - Self::match_assumption(ecx, goal, assumption)?; - let source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?; - Ok(Candidate { - source, - result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?, + Self::match_assumption(ecx, goal, assumption, |ecx| { + source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) }) + .map(|result| Candidate { source: source.get(), result }) } /// Try equating an assumption predicate against a goal's predicate. If it @@ -150,10 +151,8 @@ where ) -> Result<Candidate<I>, NoSolution> { Self::fast_reject_assumption(ecx, goal, assumption)?; - ecx.probe_trait_candidate(source).enter(|ecx| { - Self::match_assumption(ecx, goal, assumption)?; - then(ecx) - }) + ecx.probe_trait_candidate(source) + .enter(|ecx| Self::match_assumption(ecx, goal, assumption, then)) } /// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`] @@ -169,7 +168,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, assumption: I::Clause, - ) -> Result<(), NoSolution>; + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>, + ) -> QueryResult<I>; fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, 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 84a83d79cf0..8413c2abbb9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -61,13 +61,14 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>, + ) -> QueryResult<I> { let host_clause = assumption.as_host_effect_clause().unwrap(); let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause); ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; - Ok(()) + then(ecx) } /// Register additional assumptions for aliases corresponding to `~const` item bounds. 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 b90e34e7810..2fddc0044cb 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 @@ -129,7 +129,40 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>, + ) -> QueryResult<I> { + let cx = ecx.cx(); + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. + // + // If this type is a GAT with currently unconstrained arguments, we do not + // want to normalize it via a candidate which only applies for a specific + // instantiation. We could otherwise keep the GAT as rigid and succeed this way. + // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. + // + // This only avoids normalization if the GAT arguments are fully unconstrained. + // This is quite arbitrary but fixing it causes some ambiguity, see #125196. + match goal.predicate.alias.kind(cx) { + ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { + for arg in goal.predicate.alias.own_args(cx).iter() { + let Some(term) = arg.as_term() else { + continue; + }; + let term = ecx.structurally_normalize_term(goal.param_env, term)?; + if term.is_infer() { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + } + } + ty::AliasTermKind::OpaqueTy + | ty::AliasTermKind::InherentTy + | ty::AliasTermKind::InherentConst + | ty::AliasTermKind::FreeTy + | ty::AliasTermKind::FreeConst + | ty::AliasTermKind::UnevaluatedConst => {} + } + let projection_pred = assumption.as_projection_clause().unwrap(); let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); @@ -139,7 +172,6 @@ where // Add GAT where clauses from the trait's definition // FIXME: We don't need these, since these are the type's own WF obligations. - let cx = ecx.cx(); ecx.add_goals( GoalSource::AliasWellFormed, cx.own_predicates_of(goal.predicate.def_id()) @@ -147,7 +179,7 @@ where .map(|pred| goal.with(cx, pred)), ); - Ok(()) + then(ecx) } fn consider_additional_alias_assumptions( 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 e3addf8bf93..966d5422fbb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - NoSolution, ParamEnvSource, + NoSolution, ParamEnvSource, QueryResult, }; impl<D, I> assembly::GoalKind<D> for TraitPredicate<I> @@ -150,13 +150,14 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>, + ) -> QueryResult<I> { let trait_clause = assumption.as_trait_clause().unwrap(); let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; - Ok(()) + then(ecx) } fn consider_auto_trait_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4ce37db4280..77c24adabe3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1760,12 +1760,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if is_match { let generics = self.tcx().generics_of(obligation.predicate.def_id); - // FIXME(generic-associated-types): Addresses aggressive inference in #92917. + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. // If this type is a GAT, and of the GAT args resolve to something new, // that means that we must have newly inferred something about the GAT. // We should give up in that case. - // FIXME(generic-associated-types): This only detects one layer of inference, - // which is probably not what we actually want, but fixing it causes some ambiguity: + // + // This only detects one layer of inference, which is probably not what we actually + // want, but fixing it causes some ambiguity: // <https://github.com/rust-lang/rust/issues/125196>. if !generics.is_own_empty() && obligation.predicate.args[generics.parent_count..].iter().any(|&p| { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index e6e6466766b..ee4a8096462 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -298,6 +298,14 @@ pub trait GenericArg<I: Interner<GenericArg = Self>>: + From<I::Region> + From<I::Const> { + fn as_term(&self) -> Option<I::Term> { + match self.kind() { + ty::GenericArgKind::Lifetime(_) => None, + ty::GenericArgKind::Type(ty) => Some(ty.into()), + ty::GenericArgKind::Const(ct) => Some(ct.into()), + } + } + fn as_type(&self) -> Option<I::Ty> { if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None } } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index b59495b93c8..f02d9c988c8 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -682,6 +682,13 @@ impl<I: Interner> AliasTerm<I> { pub fn trait_ref(self, interner: I) -> TraitRef<I> { self.trait_ref_and_own_args(interner).0 } + + /// Extract the own args from this projection. + /// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`, + /// then this function would return the slice `['a]` as the own args. + pub fn own_args(self, interner: I) -> I::GenericArgsSlice { + self.trait_ref_and_own_args(interner).1 + } } /// The following methods work only with inherent associated term projections. diff --git a/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs b/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs index 96a0f2f40bf..82ffa0221b9 100644 --- a/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs +++ b/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs @@ -1,5 +1,9 @@ -// Fix for <https://github.com/rust-lang/rust/issues/125196>. //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Fix for <https://github.com/rust-lang/rust/issues/125196>. trait Tr { type Gat<T>; diff --git a/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs b/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs new file mode 100644 index 00000000000..0c25c64224b --- /dev/null +++ b/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs @@ -0,0 +1,33 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#202. We have +// to make sure we don't constrain ambiguous GAT args when normalizing +// via where bounds or item bounds. + +trait Trait { + type Assoc<U>; +} + +fn ret<T: Trait, U>(x: U) -> <T as Trait>::Assoc<U> { + loop {} +} + +fn where_bound<T: Trait<Assoc<u32> = u32>>() { + let inf = Default::default(); + let x = ret::<T, _>(inf); + let _: i32 = inf; +} + +trait ItemBound { + type Bound: Trait<Assoc<u32> = u32>; +} +fn item_bound<T: ItemBound>() { + let inf = Default::default(); + let x = ret::<T::Bound, _>(inf); + let _: i32 = inf; +} + +fn main() {} |
