Skip to content

Commit f7098be

Browse files
Don't assemble non-env/bound candidates if projection is rigid
1 parent 883f9f7 commit f7098be

File tree

6 files changed

+97
-56
lines changed

6 files changed

+97
-56
lines changed

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

+44-33
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,14 @@ where
284284
) -> Vec<Candidate<I>>;
285285
}
286286

287+
pub(super) enum AssembleCandidatesFrom {
288+
All,
289+
/// Only assemble candidates from the environment and alias bounds, ignoring
290+
/// /user-written and built-in impls. We only expect `ParamEnv` and `AliasBound`
291+
/// candidates to be assembled.
292+
EnvAndBounds,
293+
}
294+
287295
impl<D, I> EvalCtxt<'_, D>
288296
where
289297
D: SolverDelegate<Interner = I>,
@@ -292,6 +300,7 @@ where
292300
pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<D>>(
293301
&mut self,
294302
goal: Goal<I, G>,
303+
assemble_from: AssembleCandidatesFrom,
295304
) -> Vec<Candidate<I>> {
296305
let Ok(normalized_self_ty) =
297306
self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
@@ -318,16 +327,18 @@ where
318327
}
319328
}
320329

321-
self.assemble_impl_candidates(goal, &mut candidates);
322-
323-
self.assemble_builtin_impl_candidates(goal, &mut candidates);
324-
325330
self.assemble_alias_bound_candidates(goal, &mut candidates);
326-
327-
self.assemble_object_bound_candidates(goal, &mut candidates);
328-
329331
self.assemble_param_env_candidates(goal, &mut candidates);
330332

333+
match assemble_from {
334+
AssembleCandidatesFrom::All => {
335+
self.assemble_impl_candidates(goal, &mut candidates);
336+
self.assemble_builtin_impl_candidates(goal, &mut candidates);
337+
self.assemble_object_bound_candidates(goal, &mut candidates);
338+
}
339+
AssembleCandidatesFrom::EnvAndBounds => {}
340+
}
341+
331342
candidates
332343
}
333344

@@ -750,6 +761,9 @@ where
750761
})
751762
}
752763

764+
/// Assemble and merge candidates for goals which are related to an underlying trait
765+
/// goal. Right now, this is normalizes-to and host effect goals.
766+
///
753767
/// We sadly can't simply take all possible candidates for normalization goals
754768
/// and check whether they result in the same constraints. We want to make sure
755769
/// that trying to normalize an alias doesn't result in constraints which aren't
@@ -778,47 +792,41 @@ where
778792
///
779793
/// See trait-system-refactor-initiative#124 for more details.
780794
#[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)]
781-
pub(super) fn merge_candidates(
795+
pub(super) fn assemble_and_merge_candidates<G: GoalKind<D>>(
782796
&mut self,
783797
proven_via: Option<TraitGoalProvenVia>,
784-
candidates: Vec<Candidate<I>>,
798+
goal: Goal<I, G>,
785799
inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
786800
) -> QueryResult<I> {
787801
let Some(proven_via) = proven_via else {
788802
// We don't care about overflow. If proving the trait goal overflowed, then
789803
// it's enough to report an overflow error for that, we don't also have to
790804
// overflow during normalization.
791-
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Ambiguity));
805+
return Ok(self.forced_ambiguity(MaybeCause::Ambiguity)?.result);
792806
};
793807

794808
match proven_via {
795809
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-
804810
// Even when a trait bound has been proven using a where-bound, we
805811
// still need to consider alias-bounds for normalization, see
806-
// tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
807-
//
812+
// `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
813+
let candidates_from_env_and_bounds: Vec<_> = self
814+
.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);
815+
808816
// 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-
}
817+
// See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`.
818+
let mut considered_candidates: Vec<_> = if candidates_from_env_and_bounds
819+
.iter()
820+
.any(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
821+
{
822+
candidates_from_env_and_bounds
823+
.into_iter()
824+
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
825+
.map(|c| c.result)
826+
.collect()
827+
} else {
828+
candidates_from_env_and_bounds.into_iter().map(|c| c.result).collect()
829+
};
822830

823831
// If the trait goal has been proven by using the environment, we want to treat
824832
// aliases as rigid if there are no applicable projection bounds in the environment.
@@ -835,6 +843,9 @@ where
835843
}
836844
}
837845
TraitGoalProvenVia::Misc => {
846+
let candidates =
847+
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
848+
838849
// Prefer "orphaned" param-env normalization predicates, which are used
839850
// (for example, and ideally only) when proving item bounds for an impl.
840851
let candidates_from_env: Vec<_> = candidates

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
}

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,

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tracing::{instrument, trace};
1313

1414
use crate::delegate::SolverDelegate;
1515
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
16-
use crate::solve::assembly::{self, Candidate};
16+
use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate};
1717
use crate::solve::inspect::ProbeKind;
1818
use crate::solve::{
1919
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
@@ -1365,7 +1365,7 @@ where
13651365
&mut self,
13661366
goal: Goal<I, TraitPredicate<I>>,
13671367
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
1368-
let candidates = self.assemble_and_evaluate_candidates(goal);
1368+
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
13691369
self.merge_trait_candidates(goal, candidates)
13701370
}
13711371
}

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.
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)