Skip to content

Commit 405e127

Browse files
Auto merge of #149533 - adwinwhite:skip-trait-goal-if-possible, r=<try>
skip proving the trait goal if possible in `NormalizesTo` goal
2 parents 47cd712 + 6955473 commit 405e127

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;
@@ -363,13 +362,15 @@ pub(super) enum AssembleCandidatesFrom {
363362
/// user-written and built-in impls. We only expect `ParamEnv` and `AliasBound`
364363
/// candidates to be assembled.
365364
EnvAndBounds,
365+
Impl,
366366
}
367367

368368
impl AssembleCandidatesFrom {
369369
fn should_assemble_impl_candidates(&self) -> bool {
370370
match self {
371371
AssembleCandidatesFrom::All => true,
372372
AssembleCandidatesFrom::EnvAndBounds => false,
373+
AssembleCandidatesFrom::Impl => true,
373374
}
374375
}
375376
}
@@ -426,11 +427,14 @@ where
426427
return (candidates, failed_candidate_info);
427428
}
428429

429-
self.assemble_alias_bound_candidates(goal, &mut candidates);
430-
self.assemble_param_env_candidates(goal, &mut candidates, &mut failed_candidate_info);
431-
432430
match assemble_from {
433431
AssembleCandidatesFrom::All => {
432+
self.assemble_alias_bound_candidates(goal, &mut candidates);
433+
self.assemble_param_env_candidates(
434+
goal,
435+
&mut candidates,
436+
&mut failed_candidate_info,
437+
);
434438
self.assemble_builtin_impl_candidates(goal, &mut candidates);
435439
// For performance we only assemble impls if there are no candidates
436440
// which would shadow them. This is necessary to avoid hangs in rayon,
@@ -457,6 +461,12 @@ where
457461
}
458462
}
459463
AssembleCandidatesFrom::EnvAndBounds => {
464+
self.assemble_alias_bound_candidates(goal, &mut candidates);
465+
self.assemble_param_env_candidates(
466+
goal,
467+
&mut candidates,
468+
&mut failed_candidate_info,
469+
);
460470
// This is somewhat inconsistent and may make #57893 slightly easier to exploit.
461471
// However, it matches the behavior of the old solver. See
462472
// `tests/ui/traits/next-solver/normalization-shadowing/use_object_if_empty_env.rs`.
@@ -466,6 +476,10 @@ where
466476
self.assemble_object_bound_candidates(goal, &mut candidates);
467477
}
468478
}
479+
AssembleCandidatesFrom::Impl => {
480+
self.assemble_builtin_impl_candidates(goal, &mut candidates);
481+
self.assemble_impl_candidates(goal, &mut candidates);
482+
}
469483
}
470484

471485
(candidates, failed_candidate_info)
@@ -1097,112 +1111,6 @@ where
10971111
}
10981112
}
10991113

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

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)