Skip to content

Commit 31ebe11

Browse files
committed
normalization: avoid incompletely constraining GAT args
1 parent 1973872 commit 31ebe11

File tree

9 files changed

+111
-23
lines changed

9 files changed

+111
-23
lines changed

compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ where
117117
) -> Result<Candidate<I>, NoSolution> {
118118
Self::fast_reject_assumption(ecx, goal, assumption)?;
119119

120+
// Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily
121+
// check whether the candidate is global.
120122
ecx.probe(|candidate: &Result<Candidate<I>, NoSolution>| match candidate {
121123
Ok(candidate) => inspect::ProbeKind::TraitCandidate {
122124
source: candidate.source,
@@ -128,12 +130,12 @@ where
128130
},
129131
})
130132
.enter(|ecx| {
131-
Self::match_assumption(ecx, goal, assumption)?;
132-
let source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?;
133-
Ok(Candidate {
134-
source,
135-
result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?,
136-
})
133+
let mut source = CandidateSource::ParamEnv(ParamEnvSource::Global);
134+
let result = Self::match_assumption(ecx, goal, assumption, |ecx| {
135+
source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?;
136+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
137+
})?;
138+
Ok(Candidate { source, result })
137139
})
138140
}
139141

@@ -150,10 +152,8 @@ where
150152
) -> Result<Candidate<I>, NoSolution> {
151153
Self::fast_reject_assumption(ecx, goal, assumption)?;
152154

153-
ecx.probe_trait_candidate(source).enter(|ecx| {
154-
Self::match_assumption(ecx, goal, assumption)?;
155-
then(ecx)
156-
})
155+
ecx.probe_trait_candidate(source)
156+
.enter(|ecx| Self::match_assumption(ecx, goal, assumption, then))
157157
}
158158

159159
/// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`]
@@ -169,7 +169,8 @@ where
169169
ecx: &mut EvalCtxt<'_, D>,
170170
goal: Goal<I, Self>,
171171
assumption: I::Clause,
172-
) -> Result<(), NoSolution>;
172+
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
173+
) -> QueryResult<I>;
173174

174175
fn consider_impl_candidate(
175176
ecx: &mut EvalCtxt<'_, D>,

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@ where
6161
ecx: &mut EvalCtxt<'_, D>,
6262
goal: Goal<I, Self>,
6363
assumption: I::Clause,
64-
) -> Result<(), NoSolution> {
64+
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
65+
) -> QueryResult<I> {
6566
let host_clause = assumption.as_host_effect_clause().unwrap();
6667

6768
let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
6869
ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
6970

70-
Ok(())
71+
then(ecx)
7172
}
7273

7374
/// Register additional assumptions for aliases corresponding to `~const` item bounds.

compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

+35-3
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,40 @@ where
129129
ecx: &mut EvalCtxt<'_, D>,
130130
goal: Goal<I, Self>,
131131
assumption: I::Clause,
132-
) -> Result<(), NoSolution> {
132+
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
133+
) -> QueryResult<I> {
134+
let cx = ecx.cx();
135+
// FIXME(generic_associated_types): Addresses aggressive inference in #92917.
136+
//
137+
// If this type is a GAT with currently unconstrained arguments, we do not
138+
// want to normalize it via a candidate which only applies for a specific
139+
// instantiation. We could otherwise keep the GAT as rigid and succeed this way.
140+
// See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs.
141+
//
142+
// This only avoids normalization if the GAT arguments are fully unconstrained.
143+
// This is quite arbitrary but fixing it causes some ambiguity, see #125196.
144+
match goal.predicate.alias.kind(cx) {
145+
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
146+
for arg in goal.predicate.alias.own_args(cx).iter() {
147+
let Some(term) = arg.as_term() else {
148+
continue;
149+
};
150+
let term = ecx.structurally_normalize_term(goal.param_env, term)?;
151+
if term.is_infer() {
152+
return ecx.evaluate_added_goals_and_make_canonical_response(
153+
Certainty::AMBIGUOUS,
154+
);
155+
}
156+
}
157+
}
158+
ty::AliasTermKind::OpaqueTy
159+
| ty::AliasTermKind::InherentTy
160+
| ty::AliasTermKind::InherentConst
161+
| ty::AliasTermKind::FreeTy
162+
| ty::AliasTermKind::FreeConst
163+
| ty::AliasTermKind::UnevaluatedConst => {}
164+
}
165+
133166
let projection_pred = assumption.as_projection_clause().unwrap();
134167

135168
let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred);
@@ -139,15 +172,14 @@ where
139172

140173
// Add GAT where clauses from the trait's definition
141174
// FIXME: We don't need these, since these are the type's own WF obligations.
142-
let cx = ecx.cx();
143175
ecx.add_goals(
144176
GoalSource::AliasWellFormed,
145177
cx.own_predicates_of(goal.predicate.def_id())
146178
.iter_instantiated(cx, goal.predicate.alias.args)
147179
.map(|pred| goal.with(cx, pred)),
148180
);
149181

150-
Ok(())
182+
then(ecx)
151183
}
152184

153185
fn consider_additional_alias_assumptions(

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates
1717
use crate::solve::inspect::ProbeKind;
1818
use crate::solve::{
1919
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
20-
NoSolution, ParamEnvSource,
20+
NoSolution, ParamEnvSource, QueryResult,
2121
};
2222

2323
impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
@@ -150,13 +150,14 @@ where
150150
ecx: &mut EvalCtxt<'_, D>,
151151
goal: Goal<I, Self>,
152152
assumption: I::Clause,
153-
) -> Result<(), NoSolution> {
153+
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
154+
) -> QueryResult<I> {
154155
let trait_clause = assumption.as_trait_clause().unwrap();
155156

156157
let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
157158
ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
158159

159-
Ok(())
160+
then(ecx)
160161
}
161162

162163
fn consider_auto_trait_candidate(

compiler/rustc_trait_selection/src/traits/select/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1760,12 +1760,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
17601760

17611761
if is_match {
17621762
let generics = self.tcx().generics_of(obligation.predicate.def_id);
1763-
// FIXME(generic-associated-types): Addresses aggressive inference in #92917.
1763+
// FIXME(generic_associated_types): Addresses aggressive inference in #92917.
17641764
// If this type is a GAT, and of the GAT args resolve to something new,
17651765
// that means that we must have newly inferred something about the GAT.
17661766
// We should give up in that case.
1767-
// FIXME(generic-associated-types): This only detects one layer of inference,
1768-
// which is probably not what we actually want, but fixing it causes some ambiguity:
1767+
//
1768+
// This only detects one layer of inference, which is probably not what we actually
1769+
// want, but fixing it causes some ambiguity:
17691770
// <https://github.com/rust-lang/rust/issues/125196>.
17701771
if !generics.is_own_empty()
17711772
&& obligation.predicate.args[generics.parent_count..].iter().any(|&p| {

compiler/rustc_type_ir/src/inherent.rs

+8
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,14 @@ pub trait GenericArg<I: Interner<GenericArg = Self>>:
298298
+ From<I::Region>
299299
+ From<I::Const>
300300
{
301+
fn as_term(&self) -> Option<I::Term> {
302+
match self.kind() {
303+
ty::GenericArgKind::Lifetime(_) => None,
304+
ty::GenericArgKind::Type(ty) => Some(ty.into()),
305+
ty::GenericArgKind::Const(ct) => Some(ct.into()),
306+
}
307+
}
308+
301309
fn as_type(&self) -> Option<I::Ty> {
302310
if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None }
303311
}

compiler/rustc_type_ir/src/predicate.rs

+7
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,13 @@ impl<I: Interner> AliasTerm<I> {
682682
pub fn trait_ref(self, interner: I) -> TraitRef<I> {
683683
self.trait_ref_and_own_args(interner).0
684684
}
685+
686+
/// Extract the own args from this projection.
687+
/// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
688+
/// then this function would return the slice `['a]` as the own args.
689+
pub fn own_args(self, interner: I) -> I::GenericArgsSlice {
690+
self.trait_ref_and_own_args(interner).1
691+
}
685692
}
686693

687694
/// The following methods work only with inherent associated term projections.

tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
// Fix for <https://github.com/rust-lang/rust/issues/125196>.
21
//@ check-pass
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
5+
6+
// Fix for <https://github.com/rust-lang/rust/issues/125196>.
37

48
trait Tr {
59
type Gat<T>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ check-pass
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
5+
6+
// Regression test for trait-system-refactor-initiative#202. We have
7+
// to make sure we don't constrain ambiguous GAT args when normalizing
8+
// via where bounds or item bounds.
9+
10+
trait Trait {
11+
type Assoc<U>;
12+
}
13+
14+
fn ret<T: Trait, U>(x: U) -> <T as Trait>::Assoc<U> {
15+
loop {}
16+
}
17+
18+
fn where_bound<T: Trait<Assoc<u32> = u32>>() {
19+
let inf = Default::default();
20+
let x = ret::<T, _>(inf);
21+
let _: i32 = inf;
22+
}
23+
24+
trait ItemBound {
25+
type Bound: Trait<Assoc<u32> = u32>;
26+
}
27+
fn item_bound<T: ItemBound>() {
28+
let inf = Default::default();
29+
let x = ret::<T::Bound, _>(inf);
30+
let _: i32 = inf;
31+
}
32+
33+
fn main() {}

0 commit comments

Comments
 (0)