normalization: avoid incompletely constraining GAT args
This commit is contained in:
@@ -117,6 +117,8 @@ where
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
Self::fast_reject_assumption(ecx, goal, assumption)?;
|
||||
|
||||
// Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily
|
||||
// check whether the candidate is global.
|
||||
ecx.probe(|candidate: &Result<Candidate<I>, NoSolution>| match candidate {
|
||||
Ok(candidate) => inspect::ProbeKind::TraitCandidate {
|
||||
source: candidate.source,
|
||||
@@ -128,12 +130,12 @@ where
|
||||
},
|
||||
})
|
||||
.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)?,
|
||||
})
|
||||
let mut source = CandidateSource::ParamEnv(ParamEnvSource::Global);
|
||||
let result = Self::match_assumption(ecx, goal, assumption, |ecx| {
|
||||
source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?;
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})?;
|
||||
Ok(Candidate { source, result })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -150,10 +152,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 +169,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>,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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() {}
|
||||
Reference in New Issue
Block a user