Skip to content

Commit a23c5fb

Browse files
committed
normalizes-to rework rigid alias handling
1 parent c182ce9 commit a23c5fb

File tree

7 files changed

+77
-99
lines changed

7 files changed

+77
-99
lines changed

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

+23-8
Original file line numberDiff line numberDiff line change
@@ -789,11 +789,12 @@ where
789789
/// treat the alias as rigid.
790790
///
791791
/// See trait-system-refactor-initiative#124 for more details.
792-
#[instrument(level = "debug", skip(self), ret)]
792+
#[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)]
793793
pub(super) fn merge_candidates(
794794
&mut self,
795795
proven_via: Option<TraitGoalProvenVia>,
796796
candidates: Vec<Candidate<I>>,
797+
inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
797798
) -> QueryResult<I> {
798799
let Some(proven_via) = proven_via else {
799800
// We don't care about overflow. If proving the trait goal overflowed, then
@@ -810,13 +811,27 @@ where
810811
// FIXME(const_trait_impl): should this behavior also be used by
811812
// constness checking. Doing so is *at least theoretically* breaking,
812813
// see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
813-
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => candidates
814-
.iter()
815-
.filter(|c| {
816-
matches!(c.source, CandidateSource::AliasBound | CandidateSource::ParamEnv(_))
817-
})
818-
.map(|c| c.result)
819-
.collect(),
814+
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
815+
let mut candidates_from_env: Vec<_> = candidates
816+
.iter()
817+
.filter(|c| {
818+
matches!(
819+
c.source,
820+
CandidateSource::AliasBound | CandidateSource::ParamEnv(_)
821+
)
822+
})
823+
.map(|c| c.result)
824+
.collect();
825+
826+
// If the trait goal has been proven by using the environment, we want to treat
827+
// aliases as rigid if there are no applicable projection bounds in the environment.
828+
if candidates_from_env.is_empty() {
829+
if let Ok(response) = inject_normalize_to_rigid_candidate(self) {
830+
candidates_from_env.push(response);
831+
}
832+
}
833+
candidates_from_env
834+
}
820835
TraitGoalProvenVia::Misc => candidates.iter().map(|c| c.result).collect(),
821836
};
822837

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,6 @@ where
398398
goal.with(ecx.cx(), goal.predicate.trait_ref);
399399
ecx.compute_trait_goal(trait_goal)
400400
})?;
401-
self.merge_candidates(proven_via, candidates)
401+
self.merge_candidates(proven_via, candidates, |_ecx| Err(NoSolution))
402402
}
403403
}

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

+37-71
Original file line numberDiff line numberDiff line change
@@ -30,75 +30,26 @@ where
3030
) -> QueryResult<I> {
3131
self.set_is_normalizes_to_goal();
3232
debug_assert!(self.term_is_fully_unconstrained(goal));
33-
let normalize_result = self
34-
.probe(|&result| ProbeKind::TryNormalizeNonRigid { result })
35-
.enter(|this| this.normalize_at_least_one_step(goal));
36-
37-
match normalize_result {
38-
Ok(res) => Ok(res),
39-
Err(NoSolution) => {
40-
self.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
41-
let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
42-
this.add_rigid_constraints(param_env, alias)?;
43-
this.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?;
44-
this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
45-
})
46-
}
47-
}
48-
}
49-
50-
/// Register any obligations that are used to validate that an alias should be
51-
/// treated as rigid.
52-
///
53-
/// An alias may be considered rigid if it fails normalization, but we also don't
54-
/// want to consider aliases that are not well-formed to be rigid simply because
55-
/// they fail normalization.
56-
///
57-
/// For example, some `<T as Trait>::Assoc` where `T: Trait` does not hold, or an
58-
/// opaque type whose hidden type doesn't actually satisfy the opaque item bounds.
59-
fn add_rigid_constraints(
60-
&mut self,
61-
param_env: I::ParamEnv,
62-
rigid_alias: ty::AliasTerm<I>,
63-
) -> Result<(), NoSolution> {
64-
let cx = self.cx();
65-
match rigid_alias.kind(cx) {
66-
// Projections are rigid only if their trait ref holds,
67-
// and the GAT where-clauses hold.
68-
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
69-
let trait_ref = rigid_alias.trait_ref(cx);
70-
self.add_goal(GoalSource::AliasWellFormed, Goal::new(cx, param_env, trait_ref));
71-
Ok(())
72-
}
73-
ty::AliasTermKind::OpaqueTy => {
74-
if self.opaque_type_is_rigid(rigid_alias.def_id) {
75-
Ok(())
76-
} else {
77-
Err(NoSolution)
78-
}
79-
}
80-
// FIXME(generic_const_exprs): we would need to support generic consts here
81-
ty::AliasTermKind::UnevaluatedConst => Err(NoSolution),
82-
// Inherent and weak types are never rigid. This type must not be well-formed.
83-
ty::AliasTermKind::WeakTy | ty::AliasTermKind::InherentTy => Err(NoSolution),
84-
}
85-
}
86-
87-
/// Normalize the given alias by at least one step. If the alias is rigid, this
88-
/// returns `NoSolution`.
89-
#[instrument(level = "trace", skip(self), ret)]
90-
fn normalize_at_least_one_step(&mut self, goal: Goal<I, NormalizesTo<I>>) -> QueryResult<I> {
9133
let cx = self.cx();
9234
match goal.predicate.alias.kind(cx) {
9335
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
9436
let candidates = self.assemble_and_evaluate_candidates(goal);
37+
let trait_ref = goal.predicate.alias.trait_ref(cx);
9538
let (_, proven_via) =
9639
self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
97-
let trait_goal: Goal<I, ty::TraitPredicate<I>> =
98-
goal.with(cx, goal.predicate.alias.trait_ref(cx));
40+
let trait_goal: Goal<I, ty::TraitPredicate<I>> = goal.with(cx, trait_ref);
9941
ecx.compute_trait_goal(trait_goal)
10042
})?;
101-
self.merge_candidates(proven_via, candidates)
43+
self.merge_candidates(proven_via, candidates, |ecx| {
44+
ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
45+
this.structurally_instantiate_normalizes_to_term(
46+
goal,
47+
goal.predicate.alias,
48+
);
49+
this.add_goal(GoalSource::AliasWellFormed, goal.with(cx, trait_ref));
50+
this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
51+
})
52+
})
10253
}
10354
ty::AliasTermKind::InherentTy => self.normalize_inherent_associated_type(goal),
10455
ty::AliasTermKind::OpaqueTy => self.normalize_opaque_type(goal),
@@ -120,6 +71,17 @@ where
12071
self.eq(goal.param_env, goal.predicate.term, term)
12172
.expect("expected goal term to be fully unconstrained");
12273
}
74+
75+
/// Unlike `instantiate_normalizes_to_term` this instantiates the expected term
76+
/// with a rigid alias. Using this is pretty much always wrong.
77+
pub fn structurally_instantiate_normalizes_to_term(
78+
&mut self,
79+
goal: Goal<I, NormalizesTo<I>>,
80+
term: ty::AliasTerm<I>,
81+
) {
82+
self.relate_rigid_alias_non_alias(goal.param_env, term, ty::Invariant, goal.predicate.term)
83+
.expect("expected goal term to be fully unconstrained");
84+
}
12385
}
12486

12587
impl<D, I> assembly::GoalKind<D> for NormalizesTo<I>
@@ -850,12 +812,14 @@ where
850812
todo!("discr subgoal...")
851813
}
852814

853-
// We do not call `Ty::discriminant_ty` on alias, param, or placeholder
854-
// types, which return `<self_ty as DiscriminantKind>::Discriminant`
855-
// (or ICE in the case of placeholders). Projecting a type to itself
856-
// is never really productive.
815+
// Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid
816+
// alias. In case there's a where-bound further constraining this alias it is preferred over
817+
// this impl candidate anyways. It's still a bit scuffed.
857818
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
858-
return Err(NoSolution);
819+
return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
820+
ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
821+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
822+
});
859823
}
860824

861825
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
@@ -902,12 +866,14 @@ where
902866
todo!()
903867
}
904868

905-
// We do not call `Ty::async_destructor_ty` on alias, param, or placeholder
906-
// types, which return `<self_ty as AsyncDestruct>::AsyncDestructor`
907-
// (or ICE in the case of placeholders). Projecting a type to itself
908-
// is never really productive.
869+
// Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid
870+
// alias. In case there's a where-bound further constraining this alias it is preferred over
871+
// this impl candidate anyways. It's still a bit scuffed.
909872
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
910-
return Err(NoSolution);
873+
return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
874+
ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
875+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
876+
});
911877
}
912878

913879
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))

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

+15-13
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@ where
3535
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
3636
}
3737
TypingMode::Analysis { defining_opaque_types } => {
38-
let Some(def_id) = opaque_ty.def_id.as_local() else {
39-
return Err(NoSolution);
38+
let Some(def_id) = opaque_ty
39+
.def_id
40+
.as_local()
41+
.filter(|&def_id| defining_opaque_types.contains(&def_id))
42+
else {
43+
self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
44+
return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
4045
};
4146

42-
if !defining_opaque_types.contains(&def_id) {
43-
return Err(NoSolution);
44-
}
45-
4647
// FIXME: This may have issues when the args contain aliases...
4748
match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) {
4849
Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
@@ -97,15 +98,16 @@ where
9798
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
9899
}
99100
TypingMode::PostBorrowckAnalysis { defined_opaque_types } => {
100-
let Some(def_id) = opaque_ty.def_id.as_local() else {
101-
return Err(NoSolution);
101+
let Some(def_id) = opaque_ty
102+
.def_id
103+
.as_local()
104+
.filter(|&def_id| defined_opaque_types.contains(&def_id))
105+
else {
106+
self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
107+
return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
102108
};
103109

104-
if !defined_opaque_types.contains(&def_id) {
105-
return Err(NoSolution);
106-
}
107-
108-
let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args);
110+
let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
109111
// FIXME: Actually use a proper binder here instead of relying on `ReErased`.
110112
//
111113
// This is also probably unsound or sth :shrug:

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

-2
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
300300
inspect::ProbeKind::NormalizedSelfTyAssembly
301301
| inspect::ProbeKind::UnsizeAssembly
302302
| inspect::ProbeKind::Root { .. }
303-
| inspect::ProbeKind::TryNormalizeNonRigid { .. }
304303
| inspect::ProbeKind::TraitCandidate { .. }
305304
| inspect::ProbeKind::OpaqueTypeStorageLookup { .. }
306305
| inspect::ProbeKind::RigidAlias { .. } => {
@@ -325,7 +324,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
325324
// We add a candidate even for the root evaluation if there
326325
// is only one way to prove a given goal, e.g. for `WellFormed`.
327326
inspect::ProbeKind::Root { result }
328-
| inspect::ProbeKind::TryNormalizeNonRigid { result }
329327
| inspect::ProbeKind::TraitCandidate { source: _, result }
330328
| inspect::ProbeKind::OpaqueTypeStorageLookup { result }
331329
| inspect::ProbeKind::RigidAlias { result } => {

compiler/rustc_trait_selection/src/solve/select.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,7 @@ fn to_selection<'tcx>(
175175
span_bug!(span, "didn't expect to select an unknowable candidate")
176176
}
177177
},
178-
ProbeKind::TryNormalizeNonRigid { result: _ }
179-
| ProbeKind::NormalizedSelfTyAssembly
178+
ProbeKind::NormalizedSelfTyAssembly
180179
| ProbeKind::UnsizeAssembly
181180
| ProbeKind::UpcastProjectionCompatibility
182181
| ProbeKind::OpaqueTypeStorageLookup { result: _ }

compiler/rustc_type_ir/src/solve/inspect.rs

-2
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@ pub enum ProbeStep<I: Interner> {
111111
pub enum ProbeKind<I: Interner> {
112112
/// The root inference context while proving a goal.
113113
Root { result: QueryResult<I> },
114-
/// Trying to normalize an alias by at least one step in `NormalizesTo`.
115-
TryNormalizeNonRigid { result: QueryResult<I> },
116114
/// Probe entered when normalizing the self ty during candidate assembly
117115
NormalizedSelfTyAssembly,
118116
/// A candidate for proving a trait or alias-relate goal.

0 commit comments

Comments
 (0)