Skip to content

Commit 244ec84

Browse files
authored
Rollup merge of #108071 - compiler-errors:new-solver-caching, r=lcnr
Implement goal caching with the new solver Maybe it's wrong, idk. Opening mostly for first impressions before I go to sleep. r? ``@lcnr,`` cc ``@cjgillot``
2 parents d47d4ad + d21e4d8 commit 244ec84

File tree

11 files changed

+183
-173
lines changed

11 files changed

+183
-173
lines changed

compiler/rustc_infer/src/traits/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ pub struct Obligation<'tcx, T> {
5353
pub recursion_depth: usize,
5454
}
5555

56+
impl<'tcx, P> From<Obligation<'tcx, P>> for solve::Goal<'tcx, P> {
57+
fn from(value: Obligation<'tcx, P>) -> Self {
58+
solve::Goal { param_env: value.param_env, predicate: value.predicate }
59+
}
60+
}
61+
5662
pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
5763
pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
5864

compiler/rustc_middle/src/traits/solve.rs

+94-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,104 @@
11
use std::ops::ControlFlow;
22

33
use rustc_data_structures::intern::Interned;
4+
use rustc_query_system::cache::Cache;
45

5-
use crate::infer::canonical::QueryRegionConstraints;
6+
use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints};
7+
use crate::traits::query::NoSolution;
8+
use crate::traits::Canonical;
69
use crate::ty::{
7-
FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
10+
self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable,
11+
TypeVisitor,
812
};
913

14+
pub type EvaluationCache<'tcx> = Cache<CanonicalGoal<'tcx>, QueryResult<'tcx>>;
15+
16+
/// A goal is a statement, i.e. `predicate`, we want to prove
17+
/// given some assumptions, i.e. `param_env`.
18+
///
19+
/// Most of the time the `param_env` contains the `where`-bounds of the function
20+
/// we're currently typechecking while the `predicate` is some trait bound.
21+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
22+
pub struct Goal<'tcx, P> {
23+
pub param_env: ty::ParamEnv<'tcx>,
24+
pub predicate: P,
25+
}
26+
27+
impl<'tcx, P> Goal<'tcx, P> {
28+
pub fn new(
29+
tcx: TyCtxt<'tcx>,
30+
param_env: ty::ParamEnv<'tcx>,
31+
predicate: impl ToPredicate<'tcx, P>,
32+
) -> Goal<'tcx, P> {
33+
Goal { param_env, predicate: predicate.to_predicate(tcx) }
34+
}
35+
36+
/// Updates the goal to one with a different `predicate` but the same `param_env`.
37+
pub fn with<Q>(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> {
38+
Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) }
39+
}
40+
}
41+
42+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
43+
pub struct Response<'tcx> {
44+
pub var_values: CanonicalVarValues<'tcx>,
45+
/// Additional constraints returned by this query.
46+
pub external_constraints: ExternalConstraints<'tcx>,
47+
pub certainty: Certainty,
48+
}
49+
50+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
51+
pub enum Certainty {
52+
Yes,
53+
Maybe(MaybeCause),
54+
}
55+
56+
impl Certainty {
57+
pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
58+
59+
/// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
60+
/// use this function to unify the certainty of these goals
61+
pub fn unify_and(self, other: Certainty) -> Certainty {
62+
match (self, other) {
63+
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
64+
(Certainty::Yes, Certainty::Maybe(_)) => other,
65+
(Certainty::Maybe(_), Certainty::Yes) => self,
66+
(Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
67+
Certainty::Maybe(MaybeCause::Overflow)
68+
}
69+
// If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal
70+
// may still result in failure.
71+
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_))
72+
| (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => {
73+
Certainty::Maybe(MaybeCause::Ambiguity)
74+
}
75+
}
76+
}
77+
}
78+
79+
/// Why we failed to evaluate a goal.
80+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
81+
pub enum MaybeCause {
82+
/// We failed due to ambiguity. This ambiguity can either
83+
/// be a true ambiguity, i.e. there are multiple different answers,
84+
/// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
85+
Ambiguity,
86+
/// We gave up due to an overflow, most often by hitting the recursion limit.
87+
Overflow,
88+
}
89+
90+
pub type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>;
91+
92+
pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
93+
94+
/// The result of evaluating a canonical query.
95+
///
96+
/// FIXME: We use a different type than the existing canonical queries. This is because
97+
/// we need to add a `Certainty` for `overflow` and may want to restructure this code without
98+
/// having to worry about changes to currently used code. Once we've made progress on this
99+
/// solver, merge the two responses again.
100+
pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
101+
10102
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
11103
pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>);
12104

compiler/rustc_middle/src/ty/context.rs

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::mir::{
1717
};
1818
use crate::thir::Thir;
1919
use crate::traits;
20+
use crate::traits::solve;
2021
use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData};
2122
use crate::ty::query::{self, TyCtxtAt};
2223
use crate::ty::{
@@ -537,6 +538,9 @@ pub struct GlobalCtxt<'tcx> {
537538
/// Merge this with `selection_cache`?
538539
pub evaluation_cache: traits::EvaluationCache<'tcx>,
539540

541+
/// Caches the results of goal evaluation in the new solver.
542+
pub new_solver_evaluation_cache: solve::EvaluationCache<'tcx>,
543+
540544
/// Data layout specification for the current target.
541545
pub data_layout: TargetDataLayout,
542546

@@ -712,6 +716,7 @@ impl<'tcx> TyCtxt<'tcx> {
712716
pred_rcache: Default::default(),
713717
selection_cache: Default::default(),
714718
evaluation_cache: Default::default(),
719+
new_solver_evaluation_cache: Default::default(),
715720
data_layout,
716721
alloc_map: Lock::new(interpret::AllocMap::new()),
717722
}

compiler/rustc_trait_selection/src/solve/assembly.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
33
#[cfg(doc)]
44
use super::trait_goals::structural_traits::*;
5-
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
5+
use super::EvalCtxt;
66
use itertools::Itertools;
77
use rustc_hir::def_id::DefId;
88
use rustc_infer::traits::query::NoSolution;
99
use rustc_infer::traits::util::elaborate_predicates;
10+
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
1011
use rustc_middle::ty::TypeFoldable;
1112
use rustc_middle::ty::{self, Ty, TyCtxt};
1213
use std::fmt::Debug;

compiler/rustc_trait_selection/src/solve/mod.rs

+5-92
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ use rustc_hir::def_id::DefId;
2121
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
2222
use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
2323
use rustc_infer::traits::query::NoSolution;
24-
use rustc_infer::traits::Obligation;
25-
use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData};
24+
use rustc_middle::traits::solve::{
25+
CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraints, ExternalConstraintsData,
26+
Goal, MaybeCause, QueryResult, Response,
27+
};
2628
use rustc_middle::ty::{self, Ty, TyCtxt};
2729
use rustc_middle::ty::{
28-
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
30+
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
2931
};
3032
use rustc_span::DUMMY_SP;
3133

@@ -43,45 +45,6 @@ mod trait_goals;
4345
pub use eval_ctxt::EvalCtxt;
4446
pub use fulfill::FulfillmentCtxt;
4547

46-
/// A goal is a statement, i.e. `predicate`, we want to prove
47-
/// given some assumptions, i.e. `param_env`.
48-
///
49-
/// Most of the time the `param_env` contains the `where`-bounds of the function
50-
/// we're currently typechecking while the `predicate` is some trait bound.
51-
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
52-
pub struct Goal<'tcx, P> {
53-
param_env: ty::ParamEnv<'tcx>,
54-
predicate: P,
55-
}
56-
57-
impl<'tcx, P> Goal<'tcx, P> {
58-
pub fn new(
59-
tcx: TyCtxt<'tcx>,
60-
param_env: ty::ParamEnv<'tcx>,
61-
predicate: impl ToPredicate<'tcx, P>,
62-
) -> Goal<'tcx, P> {
63-
Goal { param_env, predicate: predicate.to_predicate(tcx) }
64-
}
65-
66-
/// Updates the goal to one with a different `predicate` but the same `param_env`.
67-
fn with<Q>(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> {
68-
Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) }
69-
}
70-
}
71-
72-
impl<'tcx, P> From<Obligation<'tcx, P>> for Goal<'tcx, P> {
73-
fn from(obligation: Obligation<'tcx, P>) -> Goal<'tcx, P> {
74-
Goal { param_env: obligation.param_env, predicate: obligation.predicate }
75-
}
76-
}
77-
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
78-
pub struct Response<'tcx> {
79-
pub var_values: CanonicalVarValues<'tcx>,
80-
/// Additional constraints returned by this query.
81-
pub external_constraints: ExternalConstraints<'tcx>,
82-
pub certainty: Certainty,
83-
}
84-
8548
trait CanonicalResponseExt {
8649
fn has_no_inference_or_external_constraints(&self) -> bool;
8750
}
@@ -94,56 +57,6 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
9457
}
9558
}
9659

97-
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
98-
pub enum Certainty {
99-
Yes,
100-
Maybe(MaybeCause),
101-
}
102-
103-
impl Certainty {
104-
pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
105-
106-
/// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
107-
/// use this function to unify the certainty of these goals
108-
pub fn unify_and(self, other: Certainty) -> Certainty {
109-
match (self, other) {
110-
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
111-
(Certainty::Yes, Certainty::Maybe(_)) => other,
112-
(Certainty::Maybe(_), Certainty::Yes) => self,
113-
(Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
114-
Certainty::Maybe(MaybeCause::Overflow)
115-
}
116-
// If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal
117-
// may still result in failure.
118-
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_))
119-
| (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => {
120-
Certainty::Maybe(MaybeCause::Ambiguity)
121-
}
122-
}
123-
}
124-
}
125-
126-
/// Why we failed to evaluate a goal.
127-
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
128-
pub enum MaybeCause {
129-
/// We failed due to ambiguity. This ambiguity can either
130-
/// be a true ambiguity, i.e. there are multiple different answers,
131-
/// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
132-
Ambiguity,
133-
/// We gave up due to an overflow, most often by hitting the recursion limit.
134-
Overflow,
135-
}
136-
137-
type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>;
138-
type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
139-
/// The result of evaluating a canonical query.
140-
///
141-
/// FIXME: We use a different type than the existing canonical queries. This is because
142-
/// we need to add a `Certainty` for `overflow` and may want to restructure this code without
143-
/// having to worry about changes to currently used code. Once we've made progress on this
144-
/// solver, merge the two responses again.
145-
pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
146-
14760
pub trait InferCtxtEvalExt<'tcx> {
14861
/// Evaluates a goal from **outside** of the trait solver.
14962
///

compiler/rustc_trait_selection/src/solve/project_goals.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::traits::{specialization_graph, translate_substs};
22

33
use super::assembly;
44
use super::trait_goals::structural_traits;
5-
use super::{Certainty, EvalCtxt, Goal, QueryResult};
5+
use super::EvalCtxt;
66
use rustc_errors::ErrorGuaranteed;
77
use rustc_hir::def::DefKind;
88
use rustc_hir::def_id::DefId;
@@ -11,6 +11,7 @@ use rustc_infer::infer::InferCtxt;
1111
use rustc_infer::traits::query::NoSolution;
1212
use rustc_infer::traits::specialization_graph::LeafDef;
1313
use rustc_infer::traits::Reveal;
14+
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
1415
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
1516
use rustc_middle::ty::ProjectionPredicate;
1617
use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -512,7 +513,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
512513
fn consider_builtin_dyn_upcast_candidates(
513514
_ecx: &mut EvalCtxt<'_, 'tcx>,
514515
goal: Goal<'tcx, Self>,
515-
) -> Vec<super::CanonicalResponse<'tcx>> {
516+
) -> Vec<CanonicalResponse<'tcx>> {
516517
bug!("`Unsize` does not have an associated type: {:?}", goal);
517518
}
518519

compiler/rustc_trait_selection/src/solve/search_graph/cache.rs

+1-26
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@
88
//!
99
//! FIXME(@lcnr): Write that section, feel free to ping me if you need help here
1010
//! before then or if I still haven't done that before January 2023.
11-
use super::overflow::OverflowData;
1211
use super::StackDepth;
13-
use crate::solve::{CanonicalGoal, QueryResult};
1412
use rustc_data_structures::fx::FxHashMap;
1513
use rustc_index::vec::IndexVec;
16-
use rustc_middle::ty::TyCtxt;
14+
use rustc_middle::traits::solve::{CanonicalGoal, QueryResult};
1715

1816
rustc_index::newtype_index! {
1917
pub struct EntryIndex {}
@@ -98,26 +96,3 @@ impl<'tcx> ProvisionalCache<'tcx> {
9896
self.entries[entry_index].response
9997
}
10098
}
101-
102-
pub(super) fn try_move_finished_goal_to_global_cache<'tcx>(
103-
tcx: TyCtxt<'tcx>,
104-
overflow_data: &mut OverflowData,
105-
stack: &IndexVec<super::StackDepth, super::StackElem<'tcx>>,
106-
goal: CanonicalGoal<'tcx>,
107-
response: QueryResult<'tcx>,
108-
) {
109-
// We move goals to the global cache if we either did not hit an overflow or if it's
110-
// the root goal as that will now always hit the same overflow limit.
111-
//
112-
// NOTE: We cannot move any non-root goals to the global cache even if their final result
113-
// isn't impacted by the overflow as that goal still has unstable query dependencies
114-
// because it didn't go its full depth.
115-
//
116-
// FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though.
117-
// Tracking that info correctly isn't trivial, so I haven't implemented it for now.
118-
let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty();
119-
if should_cache_globally {
120-
// FIXME: move the provisional entry to the global cache.
121-
let _ = (tcx, goal, response);
122-
}
123-
}

0 commit comments

Comments
 (0)