Skip to content

Commit fd5af8c

Browse files
authored
Rollup merge of rust-lang#105661 - lcnr:evaluate-new, r=compiler-errors
implement the skeleton of the updated trait solver cc ```@rust-lang/initiative-trait-system-refactor``` This is mostly following the architecture discussed in the types team meetup. After discussing the desired changes for the trait solver, we encountered cyclic dependencies between them. Most notably between changing evaluate to be canonical and returning inference constraints. We cannot canonicalize evaluate without returning inference constraints due to coinductive cycles. However, caching inference constraints also relies on canonicalization. Implementing both of these changes at once in-place is not feasible. This somewhat closely mirrors the current `evaluate` implementation with the following notable differences: - it moves `project` into the core solver, allowing us to correctly deal with coinductive projections (will be required for implied bounds, perfect derive) - it changes trait solver overflow to be non-fatal (required to backcompat breakage from changes to the iteration order of nested goals, deferred projection equality, generally very useful) - it returns inference constraints and canonicalizes inputs and outputs (required for a lot things, most notably merging fulfill and evaluate, and deferred projection equality) - it is implemented to work with lazy normalization A lot of things aren't yet implemented, but the remaining FIXMEs should all be fairly self-contained and parallelizable. If the architecture looks correct and is what we want here, I would like to quickly merge this and then split the work. r? ```@compiler-errors``` / ```@rust-lang/types``` :3
2 parents 62cc869 + 750bf36 commit fd5af8c

File tree

22 files changed

+1538
-87
lines changed

22 files changed

+1538
-87
lines changed

compiler/rustc_infer/src/infer/canonical/query_response.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@ impl<'tcx> InferCtxt<'tcx> {
151151
})
152152
}
153153

154-
fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> {
154+
/// FIXME: This method should only be used for canonical queries and therefore be private.
155+
///
156+
/// As the new solver does canonicalization slightly differently, this is also used there
157+
/// for now. This should hopefully change fairly soon.
158+
pub fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> {
155159
self.inner
156160
.borrow_mut()
157161
.opaque_type_storage

compiler/rustc_middle/src/infer/canonical.rs

+10
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,16 @@ impl<'tcx, V> Canonical<'tcx, V> {
300300
let Canonical { max_universe, variables, value } = self;
301301
Canonical { max_universe, variables, value: map_op(value) }
302302
}
303+
304+
/// Allows you to map the `value` of a canonical while keeping the same set of
305+
/// bound variables.
306+
///
307+
/// **WARNING:** This function is very easy to mis-use, hence the name! See
308+
/// the comment of [Canonical::unchecked_map] for more details.
309+
pub fn unchecked_rebind<W>(self, value: W) -> Canonical<'tcx, W> {
310+
let Canonical { max_universe, variables, value: _ } = self;
311+
Canonical { max_universe, variables, value }
312+
}
303313
}
304314

305315
pub type QueryOutlivesConstraint<'tcx> = (

compiler/rustc_middle/src/traits/query.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> =
9696
pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
9797
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
9898

99-
#[derive(Copy, Clone, Debug, HashStable)]
99+
#[derive(Copy, Clone, Debug, HashStable, PartialEq, Eq)]
100100
pub struct NoSolution;
101101

102102
pub type Fallible<T> = Result<T, NoSolution>;

compiler/rustc_middle/src/traits/specialization_graph.rs

+1
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ impl Iterator for Ancestors<'_> {
180180
}
181181

182182
/// Information about the most specialized definition of an associated item.
183+
#[derive(Debug)]
183184
pub struct LeafDef {
184185
/// The associated item described by this `LeafDef`.
185186
pub item: ty::AssocItem,

compiler/rustc_middle/src/ty/mod.rs

+29-12
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,17 @@ impl<'tcx> Predicate<'tcx> {
535535
self
536536
}
537537

538+
#[instrument(level = "debug", skip(tcx), ret)]
539+
pub fn is_coinductive(self, tcx: TyCtxt<'tcx>) -> bool {
540+
match self.kind().skip_binder() {
541+
ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
542+
tcx.trait_is_coinductive(data.def_id())
543+
}
544+
ty::PredicateKind::WellFormed(_) => true,
545+
_ => false,
546+
}
547+
}
548+
538549
/// Whether this projection can be soundly normalized.
539550
///
540551
/// Wf predicates must not be normalized, as normalization
@@ -1018,6 +1029,24 @@ pub struct ProjectionPredicate<'tcx> {
10181029
pub term: Term<'tcx>,
10191030
}
10201031

1032+
impl<'tcx> ProjectionPredicate<'tcx> {
1033+
pub fn self_ty(self) -> Ty<'tcx> {
1034+
self.projection_ty.self_ty()
1035+
}
1036+
1037+
pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ProjectionPredicate<'tcx> {
1038+
Self { projection_ty: self.projection_ty.with_self_ty(tcx, self_ty), ..self }
1039+
}
1040+
1041+
pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
1042+
self.projection_ty.trait_def_id(tcx)
1043+
}
1044+
1045+
pub fn def_id(self) -> DefId {
1046+
self.projection_ty.def_id
1047+
}
1048+
}
1049+
10211050
pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>;
10221051

10231052
impl<'tcx> PolyProjectionPredicate<'tcx> {
@@ -1054,18 +1083,6 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
10541083
}
10551084
}
10561085

1057-
impl<'tcx> ProjectionPredicate<'tcx> {
1058-
pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
1059-
Self {
1060-
projection_ty: tcx.mk_alias_ty(
1061-
self.projection_ty.def_id,
1062-
[self_ty.into()].into_iter().chain(self.projection_ty.substs.iter().skip(1)),
1063-
),
1064-
..self
1065-
}
1066-
}
1067-
}
1068-
10691086
pub trait ToPolyTraitRef<'tcx> {
10701087
fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
10711088
}

compiler/rustc_middle/src/ty/sty.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,7 @@ pub struct AliasTy<'tcx> {
11691169
}
11701170

11711171
impl<'tcx> AliasTy<'tcx> {
1172-
pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId {
1172+
pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
11731173
match tcx.def_kind(self.def_id) {
11741174
DefKind::AssocTy | DefKind::AssocConst => tcx.parent(self.def_id),
11751175
DefKind::ImplTraitPlaceholder => {
@@ -1183,7 +1183,7 @@ impl<'tcx> AliasTy<'tcx> {
11831183
/// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
11841184
/// then this function would return a `T: Iterator` trait reference and `['a]` as the own substs
11851185
pub fn trait_ref_and_own_substs(
1186-
&self,
1186+
self,
11871187
tcx: TyCtxt<'tcx>,
11881188
) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
11891189
debug_assert!(matches!(tcx.def_kind(self.def_id), DefKind::AssocTy | DefKind::AssocConst));
@@ -1202,14 +1202,18 @@ impl<'tcx> AliasTy<'tcx> {
12021202
/// WARNING: This will drop the substs for generic associated types
12031203
/// consider calling [Self::trait_ref_and_own_substs] to get those
12041204
/// as well.
1205-
pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
1205+
pub fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
12061206
let def_id = self.trait_def_id(tcx);
12071207
tcx.mk_trait_ref(def_id, self.substs.truncate_to(tcx, tcx.generics_of(def_id)))
12081208
}
12091209

1210-
pub fn self_ty(&self) -> Ty<'tcx> {
1210+
pub fn self_ty(self) -> Ty<'tcx> {
12111211
self.substs.type_at(0)
12121212
}
1213+
1214+
pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
1215+
tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1)))
1216+
}
12131217
}
12141218

12151219
#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift)]

compiler/rustc_middle/src/ty/subst.rs

+4
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,10 @@ impl<T> EarlyBinder<T> {
577577
pub fn rebind<U>(&self, value: U) -> EarlyBinder<U> {
578578
EarlyBinder(value)
579579
}
580+
581+
pub fn skip_binder(self) -> T {
582+
self.0
583+
}
580584
}
581585

582586
impl<T> EarlyBinder<Option<T>> {

compiler/rustc_trait_selection/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#![feature(let_chains)]
2020
#![feature(if_let_guard)]
2121
#![feature(never_type)]
22+
#![feature(result_option_inspect)]
2223
#![feature(type_alias_impl_trait)]
2324
#![recursion_limit = "512"] // For rustdoc
2425

@@ -37,4 +38,5 @@ extern crate smallvec;
3738
pub mod autoderef;
3839
pub mod errors;
3940
pub mod infer;
41+
pub mod solve;
4042
pub mod traits;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//! Code shared by trait and projection goals for candidate assembly.
2+
3+
use super::infcx_ext::InferCtxtExt;
4+
use super::{
5+
fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty,
6+
EvalCtxt, Goal,
7+
};
8+
use rustc_hir::def_id::DefId;
9+
use rustc_infer::infer::TyCtxtInferExt;
10+
use rustc_infer::infer::{
11+
canonical::{CanonicalVarValues, OriginalQueryValues},
12+
InferCtxt,
13+
};
14+
use rustc_infer::traits::query::NoSolution;
15+
use rustc_middle::ty::TypeFoldable;
16+
use rustc_middle::ty::{self, Ty, TyCtxt};
17+
use rustc_span::DUMMY_SP;
18+
use std::fmt::Debug;
19+
20+
/// A candidate is a possible way to prove a goal.
21+
///
22+
/// It consists of both the `source`, which describes how that goal would be proven,
23+
/// and the `result` when using the given `source`.
24+
///
25+
/// For the list of possible candidates, please look at the documentation of
26+
/// [super::trait_goals::CandidateSource] and [super::project_goals::CandidateSource].
27+
#[derive(Debug, Clone)]
28+
pub(super) struct Candidate<'tcx, G: GoalKind<'tcx>> {
29+
pub(super) source: G::CandidateSource,
30+
pub(super) result: CanonicalResponse<'tcx>,
31+
}
32+
33+
pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
34+
type CandidateSource: Debug + Copy;
35+
36+
fn self_ty(self) -> Ty<'tcx>;
37+
38+
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
39+
40+
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
41+
42+
fn consider_impl_candidate(
43+
acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
44+
goal: Goal<'tcx, Self>,
45+
impl_def_id: DefId,
46+
);
47+
}
48+
49+
/// An abstraction which correctly deals with the canonical results for candidates.
50+
///
51+
/// It also deduplicates the behavior between trait and projection predicates.
52+
pub(super) struct AssemblyCtxt<'a, 'tcx, G: GoalKind<'tcx>> {
53+
pub(super) cx: &'a mut EvalCtxt<'tcx>,
54+
pub(super) infcx: &'a InferCtxt<'tcx>,
55+
var_values: CanonicalVarValues<'tcx>,
56+
candidates: Vec<Candidate<'tcx, G>>,
57+
}
58+
59+
impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> {
60+
pub(super) fn assemble_and_evaluate_candidates(
61+
cx: &'a mut EvalCtxt<'tcx>,
62+
goal: CanonicalGoal<'tcx, G>,
63+
) -> Vec<Candidate<'tcx, G>> {
64+
let (ref infcx, goal, var_values) =
65+
cx.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
66+
let mut acx = AssemblyCtxt { cx, infcx, var_values, candidates: Vec::new() };
67+
68+
acx.assemble_candidates_after_normalizing_self_ty(goal);
69+
70+
acx.assemble_impl_candidates(goal);
71+
72+
acx.candidates
73+
}
74+
75+
pub(super) fn try_insert_candidate(
76+
&mut self,
77+
source: G::CandidateSource,
78+
certainty: Certainty,
79+
) {
80+
match self.infcx.make_canonical_response(self.var_values.clone(), certainty) {
81+
Ok(result) => self.candidates.push(Candidate { source, result }),
82+
Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"),
83+
}
84+
}
85+
86+
/// If the self type of a goal is a projection, computing the relevant candidates is difficult.
87+
///
88+
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
89+
/// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
90+
/// this case as projections as self types add `
91+
fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) {
92+
let tcx = self.cx.tcx;
93+
// FIXME: We also have to normalize opaque types, not sure where to best fit that in.
94+
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
95+
return
96+
};
97+
self.infcx.probe(|_| {
98+
let normalized_ty = self.infcx.next_ty_infer();
99+
let normalizes_to_goal = goal.with(
100+
tcx,
101+
ty::Binder::dummy(ty::ProjectionPredicate {
102+
projection_ty,
103+
term: normalized_ty.into(),
104+
}),
105+
);
106+
let normalization_certainty =
107+
match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) {
108+
Ok((_, certainty)) => certainty,
109+
Err(NoSolution) => return,
110+
};
111+
112+
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
113+
// This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
114+
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
115+
let mut orig_values = OriginalQueryValues::default();
116+
let goal = self.infcx.canonicalize_query(goal, &mut orig_values);
117+
let normalized_candidates =
118+
AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal);
119+
120+
// Map each candidate from being canonical wrt the current inference context to being
121+
// canonical wrt the caller.
122+
for Candidate { source, result } in normalized_candidates {
123+
self.infcx.probe(|_| {
124+
let candidate_certainty = fixme_instantiate_canonical_query_response(
125+
&self.infcx,
126+
&orig_values,
127+
result,
128+
);
129+
130+
// FIXME: This is a bit scary if the `normalizes_to_goal` overflows.
131+
//
132+
// If we have an ambiguous candidate it hides that normalization
133+
// caused an overflow which may cause issues.
134+
self.try_insert_candidate(
135+
source,
136+
normalization_certainty.unify_and(candidate_certainty),
137+
)
138+
})
139+
}
140+
})
141+
}
142+
143+
fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) {
144+
self.cx.tcx.for_each_relevant_impl(
145+
goal.predicate.trait_def_id(self.cx.tcx),
146+
goal.predicate.self_ty(),
147+
|impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id),
148+
);
149+
}
150+
}

0 commit comments

Comments
 (0)