Skip to content

Commit c180d15

Browse files
committed
normalization: avoid incompletely constraining GAT args
1 parent 1a95cc6 commit c180d15

File tree

6 files changed

+87
-4
lines changed

6 files changed

+87
-4
lines changed

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

+30
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,36 @@ where
123123
return Err(NoSolution);
124124
}
125125
ecx.probe_trait_candidate(source).enter(|ecx| {
126+
// FIXME(generic_associated_types): Addresses aggressive inference in #92917.
127+
//
128+
// If this type is a GAT with currently unconstrained arguments, we do not
129+
// want to normalize it via a candidate which only applies for a specific
130+
// instantiation. We could otherwise keep the GAT as rigid and succeed this way.
131+
// See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs.
132+
//
133+
// This only avoids normalization if the GAT arguments are fully unconstrained.
134+
// This is quite arbitrary but fixing it causes some ambiguity, see #125196.
135+
match goal.predicate.alias.kind(cx) {
136+
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
137+
for arg in goal.predicate.alias.own_args(cx).iter() {
138+
let Some(term) = arg.as_term() else {
139+
continue;
140+
};
141+
let term = ecx.structurally_normalize_term(goal.param_env, term)?;
142+
if term.is_infer() {
143+
return ecx.evaluate_added_goals_and_make_canonical_response(
144+
Certainty::AMBIGUOUS,
145+
);
146+
}
147+
}
148+
}
149+
ty::AliasTermKind::OpaqueTy
150+
| ty::AliasTermKind::InherentTy
151+
| ty::AliasTermKind::InherentConst
152+
| ty::AliasTermKind::FreeTy
153+
| ty::AliasTermKind::FreeConst
154+
| ty::AliasTermKind::UnevaluatedConst => {}
155+
}
126156
let assumption_projection_pred =
127157
ecx.instantiate_binder_with_infer(projection_pred);
128158
ecx.eq(

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)