Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1f84e11

Browse filesBrowse files
authored
Rollup merge of #140712 - lcnr:normalization-gat-args, r=compiler-errors
normalization: avoid incompletely constraining GAT args We need to copy the behavior of #125214 in the new solver. This fixes rust-lang/trait-system-refactor-initiative#202 which seems to be the cause of the regression in `deptypes`. r? ```@compiler-errors```
2 parents 7a8fd99 + 0185484 commit 1f84e11
Copy full SHA for 1f84e11

File tree

Expand file treeCollapse file tree

9 files changed

+118
-31
lines changed
Filter options
Expand file treeCollapse file tree

9 files changed

+118
-31
lines changed

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

Copy file name to clipboardExpand all lines: compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+19-19Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
pub(super) mod structural_traits;
44

5+
use std::cell::Cell;
56
use std::ops::ControlFlow;
67

78
use derive_where::derive_where;
@@ -117,24 +118,24 @@ where
117118
) -> Result<Candidate<I>, NoSolution> {
118119
Self::fast_reject_assumption(ecx, goal, assumption)?;
119120

120-
ecx.probe(|candidate: &Result<Candidate<I>, NoSolution>| match candidate {
121-
Ok(candidate) => inspect::ProbeKind::TraitCandidate {
122-
source: candidate.source,
123-
result: Ok(candidate.result),
124-
},
125-
Err(NoSolution) => inspect::ProbeKind::TraitCandidate {
126-
source: CandidateSource::ParamEnv(ParamEnvSource::Global),
127-
result: Err(NoSolution),
128-
},
121+
// Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily
122+
// check whether the candidate is global while considering normalization.
123+
//
124+
// We need to write into `source` inside of `match_assumption`, but need to access it
125+
// in `probe` even if the candidate does not apply before we get there. We handle this
126+
// by using a `Cell` here. We only ever write into it inside of `match_assumption`.
127+
let source = Cell::new(CandidateSource::ParamEnv(ParamEnvSource::Global));
128+
ecx.probe(|result: &QueryResult<I>| inspect::ProbeKind::TraitCandidate {
129+
source: source.get(),
130+
result: *result,
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)?,
133+
Self::match_assumption(ecx, goal, assumption, |ecx| {
134+
source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?);
135+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
136136
})
137137
})
138+
.map(|result| Candidate { source: source.get(), result })
138139
}
139140

140141
/// Try equating an assumption predicate against a goal's predicate. If it
@@ -150,10 +151,8 @@ where
150151
) -> Result<Candidate<I>, NoSolution> {
151152
Self::fast_reject_assumption(ecx, goal, assumption)?;
152153

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

159158
/// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`]
@@ -169,7 +168,8 @@ where
169168
ecx: &mut EvalCtxt<'_, D>,
170169
goal: Goal<I, Self>,
171170
assumption: I::Clause,
172-
) -> Result<(), NoSolution>;
171+
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
172+
) -> QueryResult<I>;
173173

174174
fn consider_impl_candidate(
175175
ecx: &mut EvalCtxt<'_, D>,

‎compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

Copy file name to clipboardExpand all lines: compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+3-2Lines changed: 3 additions & 2 deletions
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

Copy file name to clipboardExpand all lines: compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+35-3Lines changed: 35 additions & 3 deletions
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

Copy file name to clipboardExpand all lines: compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+4-3Lines changed: 4 additions & 3 deletions
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

Copy file name to clipboardExpand all lines: compiler/rustc_trait_selection/src/traits/select/mod.rs
+4-3Lines changed: 4 additions & 3 deletions
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

Copy file name to clipboardExpand all lines: compiler/rustc_type_ir/src/inherent.rs
+8Lines changed: 8 additions & 0 deletions
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

Copy file name to clipboardExpand all lines: compiler/rustc_type_ir/src/predicate.rs
+7Lines changed: 7 additions & 0 deletions
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

Copy file name to clipboardExpand all lines: tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs
+5-1Lines changed: 5 additions & 1 deletion
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>;
+33Lines changed: 33 additions & 0 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.