Skip to content

Commit e847bbf

Browse files
Don't assemble non-env/bound candidates if projection is rigid
1 parent afa859f commit e847bbf

File tree

6 files changed

+106
-59
lines changed

6 files changed

+106
-59
lines changed

Diff for: compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

+54-37
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,10 @@ where
289289
D: SolverDelegate<Interner = I>,
290290
I: Interner,
291291
{
292-
pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<D>>(
292+
fn assemble_and_evaluate_candidates_with_normalized_goal<G: GoalKind<D>>(
293293
&mut self,
294294
goal: Goal<I, G>,
295+
assemble: impl FnOnce(&mut Self, Goal<I, G>) -> Vec<Candidate<I>>,
295296
) -> Vec<Candidate<I>> {
296297
let Ok(normalized_self_ty) =
297298
self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
@@ -310,25 +311,40 @@ where
310311
// normalizing the self type as well, since type variables are not uniquified.
311312
let goal = self.resolve_vars_if_possible(goal);
312313

313-
let mut candidates = vec![];
314-
315314
if let TypingMode::Coherence = self.typing_mode() {
316315
if let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) {
317316
return vec![candidate];
318317
}
319318
}
320319

321-
self.assemble_impl_candidates(goal, &mut candidates);
322-
323-
self.assemble_builtin_impl_candidates(goal, &mut candidates);
324-
325-
self.assemble_alias_bound_candidates(goal, &mut candidates);
326-
327-
self.assemble_object_bound_candidates(goal, &mut candidates);
320+
assemble(self, goal)
321+
}
328322

329-
self.assemble_param_env_candidates(goal, &mut candidates);
323+
pub(super) fn assemble_and_evaluate_candidates_from_env_and_bounds<G: GoalKind<D>>(
324+
&mut self,
325+
goal: Goal<I, G>,
326+
) -> Vec<Candidate<I>> {
327+
self.assemble_and_evaluate_candidates_with_normalized_goal(goal, |ecx, goal| {
328+
let mut candidates = vec![];
329+
ecx.assemble_alias_bound_candidates(goal, &mut candidates);
330+
ecx.assemble_param_env_candidates(goal, &mut candidates);
331+
candidates
332+
})
333+
}
330334

331-
candidates
335+
pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<D>>(
336+
&mut self,
337+
goal: Goal<I, G>,
338+
) -> Vec<Candidate<I>> {
339+
self.assemble_and_evaluate_candidates_with_normalized_goal(goal, |ecx, goal| {
340+
let mut candidates = vec![];
341+
ecx.assemble_impl_candidates(goal, &mut candidates);
342+
ecx.assemble_builtin_impl_candidates(goal, &mut candidates);
343+
ecx.assemble_object_bound_candidates(goal, &mut candidates);
344+
ecx.assemble_alias_bound_candidates(goal, &mut candidates);
345+
ecx.assemble_param_env_candidates(goal, &mut candidates);
346+
candidates
347+
})
332348
}
333349

334350
pub(super) fn forced_ambiguity(
@@ -778,10 +794,10 @@ where
778794
///
779795
/// See trait-system-refactor-initiative#124 for more details.
780796
#[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)]
781-
pub(super) fn merge_candidates(
797+
pub(super) fn assemble_and_merge_candidates<G: GoalKind<D>>(
782798
&mut self,
783799
proven_via: Option<TraitGoalProvenVia>,
784-
candidates: Vec<Candidate<I>>,
800+
goal: Goal<I, G>,
785801
inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
786802
) -> QueryResult<I> {
787803
let Some(proven_via) = proven_via else {
@@ -793,32 +809,31 @@ where
793809

794810
match proven_via {
795811
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
796-
let mut considered_candidates = Vec::new();
797-
considered_candidates.extend(
798-
candidates
799-
.iter()
800-
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
801-
.map(|c| c.result),
802-
);
803-
804812
// Even when a trait bound has been proven using a where-bound, we
805813
// still need to consider alias-bounds for normalization, see
806-
// tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
807-
//
814+
// `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
815+
let candidates_from_env_and_bounds: Vec<_> =
816+
self.assemble_and_evaluate_candidates_from_env_and_bounds(goal);
817+
818+
debug_assert!(candidates_from_env_and_bounds.iter().all(|c| matches!(
819+
c.source,
820+
CandidateSource::AliasBound | CandidateSource::ParamEnv(_)
821+
)));
822+
808823
// We still need to prefer where-bounds over alias-bounds however.
809-
// See tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs.
810-
//
811-
// FIXME(const_trait_impl): should this behavior also be used by
812-
// constness checking. Doing so is *at least theoretically* breaking,
813-
// see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
814-
if considered_candidates.is_empty() {
815-
considered_candidates.extend(
816-
candidates
817-
.iter()
818-
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
819-
.map(|c| c.result),
820-
);
821-
}
824+
// See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`.
825+
let mut considered_candidates: Vec<_> = if candidates_from_env_and_bounds
826+
.iter()
827+
.any(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
828+
{
829+
candidates_from_env_and_bounds
830+
.into_iter()
831+
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
832+
.map(|c| c.result)
833+
.collect()
834+
} else {
835+
candidates_from_env_and_bounds.into_iter().map(|c| c.result).collect()
836+
};
822837

823838
// If the trait goal has been proven by using the environment, we want to treat
824839
// aliases as rigid if there are no applicable projection bounds in the environment.
@@ -835,6 +850,8 @@ where
835850
}
836851
}
837852
TraitGoalProvenVia::Misc => {
853+
let candidates = self.assemble_and_evaluate_candidates(goal);
854+
838855
// Prefer "orphaned" param-env normalization predicates, which are used
839856
// (for example, and ideally only) when proving item bounds for an impl.
840857
let candidates_from_env: Vec<_> = candidates

Diff for: compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -399,12 +399,11 @@ where
399399
&mut self,
400400
goal: Goal<I, ty::HostEffectPredicate<I>>,
401401
) -> QueryResult<I> {
402-
let candidates = self.assemble_and_evaluate_candidates(goal);
403402
let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
404403
let trait_goal: Goal<I, ty::TraitPredicate<I>> =
405404
goal.with(ecx.cx(), goal.predicate.trait_ref);
406405
ecx.compute_trait_goal(trait_goal)
407406
})?;
408-
self.merge_candidates(proven_via, candidates, |_ecx| Err(NoSolution))
407+
self.assemble_and_merge_candidates(proven_via, goal, |_ecx| Err(NoSolution))
409408
}
410409
}

Diff for: compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,13 @@ where
3232
let cx = self.cx();
3333
match goal.predicate.alias.kind(cx) {
3434
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
35-
let candidates = self.assemble_and_evaluate_candidates(goal);
3635
let trait_ref = goal.predicate.alias.trait_ref(cx);
3736
let (_, proven_via) =
3837
self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
3938
let trait_goal: Goal<I, ty::TraitPredicate<I>> = goal.with(cx, trait_ref);
4039
ecx.compute_trait_goal(trait_goal)
4140
})?;
42-
self.merge_candidates(proven_via, candidates, |ecx| {
41+
self.assemble_and_merge_candidates(proven_via, goal, |ecx| {
4342
ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
4443
this.structurally_instantiate_normalizes_to_term(
4544
goal,

Diff for: compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ impl<'tcx> BestObligation<'tcx> {
269269
goal: &inspect::InspectGoal<'_, 'tcx>,
270270
self_ty: Ty<'tcx>,
271271
) -> ControlFlow<PredicateObligation<'tcx>> {
272-
assert!(!self.consider_ambiguities);
272+
//assert!(!self.consider_ambiguities);
273273
let tcx = goal.infcx().tcx;
274274
if let ty::Alias(..) = self_ty.kind() {
275275
let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span);

Diff for: tests/ui/impl-unused-tps.stderr

+17-17
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,6 @@ LL | impl<T> Foo<T> for [isize; 0] {
77
LL | impl<T, U> Foo<T> for U {
88
| ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[isize; 0]`
99

10-
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
11-
--> $DIR/impl-unused-tps.rs:32:9
12-
|
13-
LL | impl<T, U> Bar for T {
14-
| ^ unconstrained type parameter
15-
16-
error[E0119]: conflicting implementations of trait `Bar`
17-
--> $DIR/impl-unused-tps.rs:40:1
18-
|
19-
LL | impl<T, U> Bar for T {
20-
| -------------------- first implementation here
21-
...
22-
LL | / impl<T, U> Bar for T
23-
LL | | where
24-
LL | | T: Bar<Out = U>,
25-
| |____________________^ conflicting implementation
26-
2710
error[E0119]: conflicting implementations of trait `Foo<[isize; 0]>` for type `[isize; 0]`
2811
--> $DIR/impl-unused-tps.rs:49:1
2912
|
@@ -52,6 +35,12 @@ error[E0207]: the type parameter `U` is not constrained by the impl trait, self
5235
LL | impl<T, U> Foo<T> for [isize; 1] {
5336
| ^ unconstrained type parameter
5437

38+
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
39+
--> $DIR/impl-unused-tps.rs:32:9
40+
|
41+
LL | impl<T, U> Bar for T {
42+
| ^ unconstrained type parameter
43+
5544
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
5645
--> $DIR/impl-unused-tps.rs:40:9
5746
|
@@ -70,6 +59,17 @@ error[E0207]: the type parameter `V` is not constrained by the impl trait, self
7059
LL | impl<T, U, V> Foo<T> for T
7160
| ^ unconstrained type parameter
7261

62+
error[E0119]: conflicting implementations of trait `Bar`
63+
--> $DIR/impl-unused-tps.rs:40:1
64+
|
65+
LL | impl<T, U> Bar for T {
66+
| -------------------- first implementation here
67+
...
68+
LL | / impl<T, U> Bar for T
69+
LL | | where
70+
LL | | T: Bar<Out = U>,
71+
| |____________________^ conflicting implementation
72+
7373
error: aborting due to 9 previous errors
7474

7575
Some errors have detailed explanations: E0119, E0207.
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@ compile-flags: -Znext-solver
2+
//@ check-pass
3+
//@ edition: 2024
4+
5+
// Ensure we don't end up in a query cycle due to trying to assemble an impl candidate
6+
// for an RPITIT normalizes-to goal, even though that impl candidate would *necessarily*
7+
// be made rigid by a where clause. This query cycle is thus avoidable by not assembling
8+
// that impl candidate which we *know* we are going to throw away anyways.
9+
10+
use std::future::Future;
11+
12+
pub trait ReactiveFunction: Send {
13+
type Output;
14+
15+
fn invoke(self) -> Self::Output;
16+
}
17+
18+
trait AttributeValue {
19+
fn resolve(self) -> impl Future<Output = ()> + Send;
20+
}
21+
22+
impl<F, V> AttributeValue for F
23+
where
24+
F: ReactiveFunction<Output = V>,
25+
V: AttributeValue,
26+
{
27+
async fn resolve(self) {
28+
self.invoke().resolve().await
29+
}
30+
}
31+
32+
fn main() {}

0 commit comments

Comments
 (0)