Skip to content

Commit 6955473

Browse files
committed
skip proving trait goal if possible in NormalizesTo goal
also split the `assembly_and_merge_candidates` for `HostEffect` and `NormalizesTo`
1 parent fb505a7 commit 6955473

File tree

8 files changed

+264
-200
lines changed

8 files changed

+264
-200
lines changed

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

Lines changed: 18 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use rustc_type_ir::{
1717
};
1818
use tracing::{debug, instrument};
1919

20-
use super::trait_goals::TraitGoalProvenVia;
2120
use super::{has_only_region_constraints, inspect};
2221
use crate::delegate::SolverDelegate;
2322
use crate::solve::inspect::ProbeKind;
@@ -361,13 +360,15 @@ pub(super) enum AssembleCandidatesFrom {
361360
/// user-written and built-in impls. We only expect `ParamEnv` and `AliasBound`
362361
/// candidates to be assembled.
363362
EnvAndBounds,
363+
Impl,
364364
}
365365

366366
impl AssembleCandidatesFrom {
367367
fn should_assemble_impl_candidates(&self) -> bool {
368368
match self {
369369
AssembleCandidatesFrom::All => true,
370370
AssembleCandidatesFrom::EnvAndBounds => false,
371+
AssembleCandidatesFrom::Impl => true,
371372
}
372373
}
373374
}
@@ -424,11 +425,14 @@ where
424425
return (candidates, failed_candidate_info);
425426
}
426427

427-
self.assemble_alias_bound_candidates(goal, &mut candidates);
428-
self.assemble_param_env_candidates(goal, &mut candidates, &mut failed_candidate_info);
429-
430428
match assemble_from {
431429
AssembleCandidatesFrom::All => {
430+
self.assemble_alias_bound_candidates(goal, &mut candidates);
431+
self.assemble_param_env_candidates(
432+
goal,
433+
&mut candidates,
434+
&mut failed_candidate_info,
435+
);
432436
self.assemble_builtin_impl_candidates(goal, &mut candidates);
433437
// For performance we only assemble impls if there are no candidates
434438
// which would shadow them. This is necessary to avoid hangs in rayon,
@@ -455,6 +459,12 @@ where
455459
}
456460
}
457461
AssembleCandidatesFrom::EnvAndBounds => {
462+
self.assemble_alias_bound_candidates(goal, &mut candidates);
463+
self.assemble_param_env_candidates(
464+
goal,
465+
&mut candidates,
466+
&mut failed_candidate_info,
467+
);
458468
// This is somewhat inconsistent and may make #57893 slightly easier to exploit.
459469
// However, it matches the behavior of the old solver. See
460470
// `tests/ui/traits/next-solver/normalization-shadowing/use_object_if_empty_env.rs`.
@@ -464,6 +474,10 @@ where
464474
self.assemble_object_bound_candidates(goal, &mut candidates);
465475
}
466476
}
477+
AssembleCandidatesFrom::Impl => {
478+
self.assemble_builtin_impl_candidates(goal, &mut candidates);
479+
self.assemble_impl_candidates(goal, &mut candidates);
480+
}
467481
}
468482

469483
(candidates, failed_candidate_info)
@@ -1095,112 +1109,6 @@ where
10951109
}
10961110
}
10971111

1098-
/// Assemble and merge candidates for goals which are related to an underlying trait
1099-
/// goal. Right now, this is normalizes-to and host effect goals.
1100-
///
1101-
/// We sadly can't simply take all possible candidates for normalization goals
1102-
/// and check whether they result in the same constraints. We want to make sure
1103-
/// that trying to normalize an alias doesn't result in constraints which aren't
1104-
/// otherwise required.
1105-
///
1106-
/// Most notably, when proving a trait goal by via a where-bound, we should not
1107-
/// normalize via impls which have stricter region constraints than the where-bound:
1108-
///
1109-
/// ```rust
1110-
/// trait Trait<'a> {
1111-
/// type Assoc;
1112-
/// }
1113-
///
1114-
/// impl<'a, T: 'a> Trait<'a> for T {
1115-
/// type Assoc = u32;
1116-
/// }
1117-
///
1118-
/// fn with_bound<'a, T: Trait<'a>>(_value: T::Assoc) {}
1119-
/// ```
1120-
///
1121-
/// The where-bound of `with_bound` doesn't specify the associated type, so we would
1122-
/// only be able to normalize `<T as Trait<'a>>::Assoc` by using the impl. This impl
1123-
/// adds a `T: 'a` bound however, which would result in a region error. Given that the
1124-
/// user explicitly wrote that `T: Trait<'a>` holds, this is undesirable and we instead
1125-
/// treat the alias as rigid.
1126-
///
1127-
/// See trait-system-refactor-initiative#124 for more details.
1128-
#[instrument(level = "debug", skip_all, fields(proven_via, goal), ret)]
1129-
pub(super) fn assemble_and_merge_candidates<G: GoalKind<D>>(
1130-
&mut self,
1131-
proven_via: Option<TraitGoalProvenVia>,
1132-
goal: Goal<I, G>,
1133-
inject_forced_ambiguity_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> Option<QueryResult<I>>,
1134-
inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
1135-
) -> QueryResult<I> {
1136-
let Some(proven_via) = proven_via else {
1137-
// We don't care about overflow. If proving the trait goal overflowed, then
1138-
// it's enough to report an overflow error for that, we don't also have to
1139-
// overflow during normalization.
1140-
//
1141-
// We use `forced_ambiguity` here over `make_ambiguous_response_no_constraints`
1142-
// because the former will also record a built-in candidate in the inspector.
1143-
return self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result);
1144-
};
1145-
1146-
match proven_via {
1147-
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
1148-
// Even when a trait bound has been proven using a where-bound, we
1149-
// still need to consider alias-bounds for normalization, see
1150-
// `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
1151-
let (mut candidates, _) = self
1152-
.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);
1153-
debug!(?candidates);
1154-
1155-
// If the trait goal has been proven by using the environment, we want to treat
1156-
// aliases as rigid if there are no applicable projection bounds in the environment.
1157-
if candidates.is_empty() {
1158-
return inject_normalize_to_rigid_candidate(self);
1159-
}
1160-
1161-
// If we're normalizing an GAT, we bail if using a where-bound would constrain
1162-
// its generic arguments.
1163-
if let Some(result) = inject_forced_ambiguity_candidate(self) {
1164-
return result;
1165-
}
1166-
1167-
// We still need to prefer where-bounds over alias-bounds however.
1168-
// See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`.
1169-
if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
1170-
candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));
1171-
}
1172-
1173-
if let Some((response, _)) = self.try_merge_candidates(&candidates) {
1174-
Ok(response)
1175-
} else {
1176-
self.flounder(&candidates)
1177-
}
1178-
}
1179-
TraitGoalProvenVia::Misc => {
1180-
let (mut candidates, _) =
1181-
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
1182-
1183-
// Prefer "orphaned" param-env normalization predicates, which are used
1184-
// (for example, and ideally only) when proving item bounds for an impl.
1185-
if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
1186-
candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));
1187-
}
1188-
1189-
// We drop specialized impls to allow normalization via a final impl here. In case
1190-
// the specializing impl has different inference constraints from the specialized
1191-
// impl, proving the trait goal is already ambiguous, so we never get here. This
1192-
// means we can just ignore inference constraints and don't have to special-case
1193-
// constraining the normalized-to `term`.
1194-
self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
1195-
if let Some((response, _)) = self.try_merge_candidates(&candidates) {
1196-
Ok(response)
1197-
} else {
1198-
self.flounder(&candidates)
1199-
}
1200-
}
1201-
}
1202-
}
1203-
12041112
/// Compute whether a param-env assumption is global or non-global after normalizing it.
12051113
///
12061114
/// This is necessary because, for example, given:

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ use rustc_type_ir::lang_items::SolverTraitLangItem;
77
use rustc_type_ir::solve::inspect::ProbeKind;
88
use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind};
99
use rustc_type_ir::{self as ty, Interner, TypingMode, elaborate};
10-
use tracing::instrument;
10+
use tracing::{debug, instrument};
1111

1212
use super::assembly::{Candidate, structural_traits};
1313
use crate::delegate::SolverDelegate;
14+
use crate::solve::assembly::{AllowInferenceConstraints, AssembleCandidatesFrom};
15+
use crate::solve::trait_goals::TraitGoalProvenVia;
1416
use crate::solve::{
15-
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution,
16-
QueryResult, assembly,
17+
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
18+
NoSolution, QueryResult, assembly,
1719
};
1820

1921
impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I>
@@ -446,6 +448,61 @@ where
446448
goal.with(ecx.cx(), goal.predicate.trait_ref);
447449
ecx.compute_trait_goal(trait_goal)
448450
})?;
449-
self.assemble_and_merge_candidates(proven_via, goal, |_ecx| None, |_ecx| Err(NoSolution))
451+
self.assemble_and_merge_candidates(proven_via, goal)
452+
}
453+
454+
#[instrument(level = "debug", skip(self), ret)]
455+
fn assemble_and_merge_candidates(
456+
&mut self,
457+
proven_via: Option<TraitGoalProvenVia>,
458+
goal: Goal<I, ty::HostEffectPredicate<I>>,
459+
) -> QueryResult<I> {
460+
let Some(proven_via) = proven_via else {
461+
// We don't care about overflow. If proving the trait goal overflowed, then
462+
// it's enough to report an overflow error for that, we don't also have to
463+
// overflow during normalization.
464+
//
465+
// We use `forced_ambiguity` here over `make_ambiguous_response_no_constraints`
466+
// because the former will also record a built-in candidate in the inspector.
467+
return self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result);
468+
};
469+
470+
match proven_via {
471+
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
472+
let (mut candidates, _) = self
473+
.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);
474+
debug!(?candidates);
475+
476+
if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
477+
candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));
478+
}
479+
480+
if let Some((response, _)) = self.try_merge_candidates(&candidates) {
481+
Ok(response)
482+
} else {
483+
self.flounder(&candidates)
484+
}
485+
}
486+
TraitGoalProvenVia::Misc => {
487+
let (mut candidates, _) =
488+
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
489+
490+
if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
491+
candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));
492+
}
493+
494+
// We drop specialized impls to allow normalization via a final impl here. In case
495+
// the specializing impl has different inference constraints from the specialized
496+
// impl, proving the trait goal is already ambiguous, so we never get here. This
497+
// means we can just ignore inference constraints and don't have to special-case
498+
// constraining the normalized-to `term`.
499+
self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
500+
if let Some((response, _)) = self.try_merge_candidates(&candidates) {
501+
Ok(response)
502+
} else {
503+
self.flounder(&candidates)
504+
}
505+
}
506+
}
450507
}
451508
}

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

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod anon_const;
22
mod free_alias;
33
mod inherent;
44
mod opaque_types;
5+
mod projection;
56

67
use rustc_type_ir::fast_reject::DeepRejectCtxt;
78
use rustc_type_ir::inherent::*;
@@ -13,7 +14,6 @@ use tracing::instrument;
1314
use crate::delegate::SolverDelegate;
1415
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
1516
use crate::solve::assembly::{self, Candidate};
16-
use crate::solve::inspect::ProbeKind;
1717
use crate::solve::{
1818
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
1919
NoSolution, QueryResult,
@@ -33,55 +33,7 @@ where
3333
let cx = self.cx();
3434
match goal.predicate.alias.kind(cx) {
3535
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
36-
let trait_ref = goal.predicate.alias.trait_ref(cx);
37-
let (_, proven_via) =
38-
self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
39-
let trait_goal: Goal<I, ty::TraitPredicate<I>> = goal.with(cx, trait_ref);
40-
ecx.compute_trait_goal(trait_goal)
41-
})?;
42-
self.assemble_and_merge_candidates(
43-
proven_via,
44-
goal,
45-
|ecx| {
46-
// FIXME(generic_associated_types): Addresses aggressive inference in #92917.
47-
//
48-
// If this type is a GAT with currently unconstrained arguments, we do not
49-
// want to normalize it via a candidate which only applies for a specific
50-
// instantiation. We could otherwise keep the GAT as rigid and succeed this way.
51-
// See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs.
52-
//
53-
// This only avoids normalization if a GAT argument is fully unconstrained.
54-
// This is quite arbitrary but fixing it causes some ambiguity, see #125196.
55-
for arg in goal.predicate.alias.own_args(cx).iter() {
56-
let Some(term) = arg.as_term() else {
57-
continue;
58-
};
59-
match ecx.structurally_normalize_term(goal.param_env, term) {
60-
Ok(term) => {
61-
if term.is_infer() {
62-
return Some(
63-
ecx.evaluate_added_goals_and_make_canonical_response(
64-
Certainty::AMBIGUOUS,
65-
),
66-
);
67-
}
68-
}
69-
Err(NoSolution) => return Some(Err(NoSolution)),
70-
}
71-
}
72-
73-
None
74-
},
75-
|ecx| {
76-
ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
77-
this.structurally_instantiate_normalizes_to_term(
78-
goal,
79-
goal.predicate.alias,
80-
);
81-
this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
82-
})
83-
},
84-
)
36+
self.normalize_trait_associated_term(goal)
8537
}
8638
ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => {
8739
self.normalize_inherent_associated_term(goal)

0 commit comments

Comments
 (0)