normalization: avoid incompletely constraining GAT args
This commit is contained in:
@@ -117,6 +117,8 @@ where
|
|||||||
) -> Result<Candidate<I>, NoSolution> {
|
) -> Result<Candidate<I>, NoSolution> {
|
||||||
Self::fast_reject_assumption(ecx, goal, assumption)?;
|
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 {
|
ecx.probe(|candidate: &Result<Candidate<I>, NoSolution>| match candidate {
|
||||||
Ok(candidate) => inspect::ProbeKind::TraitCandidate {
|
Ok(candidate) => inspect::ProbeKind::TraitCandidate {
|
||||||
source: candidate.source,
|
source: candidate.source,
|
||||||
@@ -128,12 +130,12 @@ where
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.enter(|ecx| {
|
.enter(|ecx| {
|
||||||
Self::match_assumption(ecx, goal, assumption)?;
|
let mut source = CandidateSource::ParamEnv(ParamEnvSource::Global);
|
||||||
let source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?;
|
let result = Self::match_assumption(ecx, goal, assumption, |ecx| {
|
||||||
Ok(Candidate {
|
source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?;
|
||||||
source,
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?,
|
})?;
|
||||||
})
|
Ok(Candidate { source, result })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,10 +152,8 @@ where
|
|||||||
) -> Result<Candidate<I>, NoSolution> {
|
) -> Result<Candidate<I>, NoSolution> {
|
||||||
Self::fast_reject_assumption(ecx, goal, assumption)?;
|
Self::fast_reject_assumption(ecx, goal, assumption)?;
|
||||||
|
|
||||||
ecx.probe_trait_candidate(source).enter(|ecx| {
|
ecx.probe_trait_candidate(source)
|
||||||
Self::match_assumption(ecx, goal, assumption)?;
|
.enter(|ecx| Self::match_assumption(ecx, goal, assumption, then))
|
||||||
then(ecx)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`]
|
/// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`]
|
||||||
@@ -169,7 +169,8 @@ where
|
|||||||
ecx: &mut EvalCtxt<'_, D>,
|
ecx: &mut EvalCtxt<'_, D>,
|
||||||
goal: Goal<I, Self>,
|
goal: Goal<I, Self>,
|
||||||
assumption: I::Clause,
|
assumption: I::Clause,
|
||||||
) -> Result<(), NoSolution>;
|
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||||
|
) -> QueryResult<I>;
|
||||||
|
|
||||||
fn consider_impl_candidate(
|
fn consider_impl_candidate(
|
||||||
ecx: &mut EvalCtxt<'_, D>,
|
ecx: &mut EvalCtxt<'_, D>,
|
||||||
|
|||||||
@@ -61,13 +61,14 @@ where
|
|||||||
ecx: &mut EvalCtxt<'_, D>,
|
ecx: &mut EvalCtxt<'_, D>,
|
||||||
goal: Goal<I, Self>,
|
goal: Goal<I, Self>,
|
||||||
assumption: I::Clause,
|
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 host_clause = assumption.as_host_effect_clause().unwrap();
|
||||||
|
|
||||||
let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
|
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)?;
|
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.
|
/// Register additional assumptions for aliases corresponding to `~const` item bounds.
|
||||||
|
|||||||
@@ -129,7 +129,40 @@ where
|
|||||||
ecx: &mut EvalCtxt<'_, D>,
|
ecx: &mut EvalCtxt<'_, D>,
|
||||||
goal: Goal<I, Self>,
|
goal: Goal<I, Self>,
|
||||||
assumption: I::Clause,
|
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 projection_pred = assumption.as_projection_clause().unwrap();
|
||||||
|
|
||||||
let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred);
|
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
|
// Add GAT where clauses from the trait's definition
|
||||||
// FIXME: We don't need these, since these are the type's own WF obligations.
|
// FIXME: We don't need these, since these are the type's own WF obligations.
|
||||||
let cx = ecx.cx();
|
|
||||||
ecx.add_goals(
|
ecx.add_goals(
|
||||||
GoalSource::AliasWellFormed,
|
GoalSource::AliasWellFormed,
|
||||||
cx.own_predicates_of(goal.predicate.def_id())
|
cx.own_predicates_of(goal.predicate.def_id())
|
||||||
@@ -147,7 +179,7 @@ where
|
|||||||
.map(|pred| goal.with(cx, pred)),
|
.map(|pred| goal.with(cx, pred)),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
then(ecx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_additional_alias_assumptions(
|
fn consider_additional_alias_assumptions(
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates
|
|||||||
use crate::solve::inspect::ProbeKind;
|
use crate::solve::inspect::ProbeKind;
|
||||||
use crate::solve::{
|
use crate::solve::{
|
||||||
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
|
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
|
||||||
NoSolution, ParamEnvSource,
|
NoSolution, ParamEnvSource, QueryResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
|
impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
|
||||||
@@ -150,13 +150,14 @@ where
|
|||||||
ecx: &mut EvalCtxt<'_, D>,
|
ecx: &mut EvalCtxt<'_, D>,
|
||||||
goal: Goal<I, Self>,
|
goal: Goal<I, Self>,
|
||||||
assumption: I::Clause,
|
assumption: I::Clause,
|
||||||
) -> Result<(), NoSolution> {
|
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||||
|
) -> QueryResult<I> {
|
||||||
let trait_clause = assumption.as_trait_clause().unwrap();
|
let trait_clause = assumption.as_trait_clause().unwrap();
|
||||||
|
|
||||||
let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
|
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)?;
|
ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
|
||||||
|
|
||||||
Ok(())
|
then(ecx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_auto_trait_candidate(
|
fn consider_auto_trait_candidate(
|
||||||
|
|||||||
@@ -1760,12 +1760,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||||||
|
|
||||||
if is_match {
|
if is_match {
|
||||||
let generics = self.tcx().generics_of(obligation.predicate.def_id);
|
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,
|
// 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.
|
// that means that we must have newly inferred something about the GAT.
|
||||||
// We should give up in that case.
|
// 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>.
|
// <https://github.com/rust-lang/rust/issues/125196>.
|
||||||
if !generics.is_own_empty()
|
if !generics.is_own_empty()
|
||||||
&& obligation.predicate.args[generics.parent_count..].iter().any(|&p| {
|
&& 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::Region>
|
||||||
+ From<I::Const>
|
+ 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> {
|
fn as_type(&self) -> Option<I::Ty> {
|
||||||
if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None }
|
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> {
|
pub fn trait_ref(self, interner: I) -> TraitRef<I> {
|
||||||
self.trait_ref_and_own_args(interner).0
|
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.
|
/// 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
|
//@ 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 {
|
trait Tr {
|
||||||
type Gat<T>;
|
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