Skip to content

Commit 28b9354

Browse files
authored
Rollup merge of #109447 - lcnr:coherence, r=compiler-errors
new solver cleanup + implement coherence the cleanup: - change `Certainty::unify_and` to consider ambig + overflow to be ambig - rename `trait_candidate_should_be_dropped_in_favor_of` to `candidate_should_be_dropped_in_favor_of` - remove outdated fixme For coherence I mostly just add an ambiguous candidate if the current trait ref is unknowable. I am doing the same for reservation impl where I also just add an ambiguous candidate.
2 parents b22db3f + f86b035 commit 28b9354

File tree

20 files changed

+226
-62
lines changed

20 files changed

+226
-62
lines changed

compiler/rustc_infer/src/infer/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -585,8 +585,8 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
585585
self
586586
}
587587

588-
pub fn intercrate(mut self) -> Self {
589-
self.intercrate = true;
588+
pub fn intercrate(mut self, intercrate: bool) -> Self {
589+
self.intercrate = intercrate;
590590
self
591591
}
592592

compiler/rustc_middle/src/traits/solve.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,14 @@ impl Certainty {
6363
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
6464
(Certainty::Yes, Certainty::Maybe(_)) => other,
6565
(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)) => {
66+
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Ambiguity)) => {
7367
Certainty::Maybe(MaybeCause::Ambiguity)
7468
}
69+
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Overflow))
70+
| (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Ambiguity))
71+
| (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
72+
Certainty::Maybe(MaybeCause::Overflow)
73+
}
7574
}
7675
}
7776
}

compiler/rustc_trait_selection/src/solve/assembly.rs

+40-29
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
#[cfg(doc)]
44
use super::trait_goals::structural_traits::*;
5-
use super::EvalCtxt;
5+
use super::{EvalCtxt, SolverMode};
6+
use crate::traits::coherence;
67
use itertools::Itertools;
78
use rustc_hir::def_id::DefId;
89
use rustc_infer::traits::query::NoSolution;
@@ -87,6 +88,8 @@ pub(super) enum CandidateSource {
8788
pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
8889
fn self_ty(self) -> Ty<'tcx>;
8990

91+
fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>;
92+
9093
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
9194

9295
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
@@ -244,15 +247,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
244247

245248
self.assemble_object_bound_candidates(goal, &mut candidates);
246249

250+
self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
251+
247252
candidates
248253
}
249254

250255
/// If the self type of a goal is a projection, computing the relevant candidates is difficult.
251256
///
252257
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
253-
/// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
254-
/// this case as projections as self types add
255-
// FIXME complete the unfinished sentence above
258+
/// self type to the list of candidates in case that succeeds. We also have to consider candidates with the
259+
/// projection as a self type as well
256260
fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
257261
&mut self,
258262
goal: Goal<'tcx, G>,
@@ -468,25 +472,49 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
468472
}
469473
}
470474

475+
fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
476+
&mut self,
477+
goal: Goal<'tcx, G>,
478+
candidates: &mut Vec<Candidate<'tcx>>,
479+
) {
480+
match self.solver_mode() {
481+
SolverMode::Normal => return,
482+
SolverMode::Coherence => {
483+
let trait_ref = goal.predicate.trait_ref(self.tcx());
484+
match coherence::trait_ref_is_knowable(self.tcx(), trait_ref) {
485+
Ok(()) => {}
486+
Err(_) => match self
487+
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
488+
{
489+
Ok(result) => candidates
490+
.push(Candidate { source: CandidateSource::BuiltinImpl, result }),
491+
// FIXME: This will be reachable at some point if we're in
492+
// `assemble_candidates_after_normalizing_self_ty` and we get a
493+
// universe error. We'll deal with it at this point.
494+
Err(NoSolution) => bug!("coherence candidate resulted in NoSolution"),
495+
},
496+
}
497+
}
498+
}
499+
}
500+
471501
#[instrument(level = "debug", skip(self), ret)]
472-
pub(super) fn merge_candidates_and_discard_reservation_impls(
502+
pub(super) fn merge_candidates(
473503
&mut self,
474504
mut candidates: Vec<Candidate<'tcx>>,
475505
) -> QueryResult<'tcx> {
476506
match candidates.len() {
477507
0 => return Err(NoSolution),
478-
1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
508+
1 => return Ok(candidates.pop().unwrap().result),
479509
_ => {}
480510
}
481511

482512
if candidates.len() > 1 {
483513
let mut i = 0;
484514
'outer: while i < candidates.len() {
485515
for j in (0..candidates.len()).filter(|&j| i != j) {
486-
if self.trait_candidate_should_be_dropped_in_favor_of(
487-
&candidates[i],
488-
&candidates[j],
489-
) {
516+
if self.candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])
517+
{
490518
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
491519
candidates.swap_remove(i);
492520
continue 'outer;
@@ -511,11 +539,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
511539
}
512540
}
513541

514-
// FIXME: What if there are >1 candidates left with the same response, and one is a reservation impl?
515-
Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
542+
Ok(candidates.pop().unwrap().result)
516543
}
517544

518-
fn trait_candidate_should_be_dropped_in_favor_of(
545+
fn candidate_should_be_dropped_in_favor_of(
519546
&self,
520547
candidate: &Candidate<'tcx>,
521548
other: &Candidate<'tcx>,
@@ -528,20 +555,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
528555
| (CandidateSource::BuiltinImpl, _) => false,
529556
}
530557
}
531-
532-
fn discard_reservation_impl(&mut self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> {
533-
if let CandidateSource::Impl(def_id) = candidate.source {
534-
if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) {
535-
debug!("Selected reservation impl");
536-
// We assemble all candidates inside of a probe so by
537-
// making a new canonical response here our result will
538-
// have no constraints.
539-
candidate.result = self
540-
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
541-
.unwrap();
542-
}
543-
}
544-
545-
candidate
546-
}
547558
}

compiler/rustc_trait_selection/src/solve/eval_ctxt.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_span::DUMMY_SP;
1717
use std::ops::ControlFlow;
1818

1919
use super::search_graph::{self, OverflowHandler};
20+
use super::SolverMode;
2021
use super::{search_graph::SearchGraph, Goal};
2122

2223
pub struct EvalCtxt<'a, 'tcx> {
@@ -78,7 +79,9 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
7879
&self,
7980
goal: Goal<'tcx, ty::Predicate<'tcx>>,
8081
) -> Result<(bool, Certainty), NoSolution> {
81-
let mut search_graph = search_graph::SearchGraph::new(self.tcx);
82+
let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
83+
84+
let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode);
8285

8386
let mut ecx = EvalCtxt {
8487
search_graph: &mut search_graph,
@@ -101,6 +104,10 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
101104
}
102105

103106
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
107+
pub(super) fn solver_mode(&self) -> SolverMode {
108+
self.search_graph.solver_mode()
109+
}
110+
104111
/// The entry point of the solver.
105112
///
106113
/// This function deals with (coinductive) cycles, overflow, and caching
@@ -120,8 +127,14 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
120127
//
121128
// The actual solver logic happens in `ecx.compute_goal`.
122129
search_graph.with_new_goal(tcx, canonical_goal, |search_graph| {
123-
let (ref infcx, goal, var_values) =
124-
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
130+
let intercrate = match search_graph.solver_mode() {
131+
SolverMode::Normal => false,
132+
SolverMode::Coherence => true,
133+
};
134+
let (ref infcx, goal, var_values) = tcx
135+
.infer_ctxt()
136+
.intercrate(intercrate)
137+
.build_with_canonical(DUMMY_SP, &canonical_goal);
125138
let mut ecx = EvalCtxt {
126139
infcx,
127140
var_values,

compiler/rustc_trait_selection/src/solve/mod.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
//! FIXME(@lcnr): Write that section. If you read this before then ask me
1010
//! about it on zulip.
1111
12-
// FIXME: Instead of using `infcx.canonicalize_query` we have to add a new routine which
13-
// preserves universes and creates a unique var (in the highest universe) for each
14-
// appearance of a region.
15-
1612
// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
1713

1814
use rustc_hir::def_id::DefId;
@@ -41,6 +37,19 @@ mod trait_goals;
4137
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
4238
pub use fulfill::FulfillmentCtxt;
4339

40+
#[derive(Debug, Clone, Copy)]
41+
enum SolverMode {
42+
/// Ordinary trait solving, using everywhere except for coherence.
43+
Normal,
44+
/// Trait solving during coherence. There are a few notable differences
45+
/// between coherence and ordinary trait solving.
46+
///
47+
/// Most importantly, trait solving during coherence must not be incomplete,
48+
/// i.e. return `Err(NoSolution)` for goals for which a solution exists.
49+
/// This means that we must not make any guesses or arbitrary choices.
50+
Coherence,
51+
}
52+
4453
trait CanonicalResponseExt {
4554
fn has_no_inference_or_external_constraints(&self) -> bool;
4655
}
@@ -255,7 +264,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
255264
return Err(NoSolution);
256265
}
257266

258-
// FIXME(-Ztreat-solver=next): We should instead try to find a `Certainty::Yes` response with
267+
// FIXME(-Ztrait-solver=next): We should instead try to find a `Certainty::Yes` response with
259268
// a subset of the constraints that all the other responses have.
260269
let one = candidates[0];
261270
if candidates[1..].iter().all(|resp| resp == &one) {

compiler/rustc_trait_selection/src/solve/project_goals.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
3434
// projection cache in the solver.
3535
if self.term_is_fully_unconstrained(goal) {
3636
let candidates = self.assemble_and_evaluate_candidates(goal);
37-
self.merge_candidates_and_discard_reservation_impls(candidates)
37+
self.merge_candidates(candidates)
3838
} else {
3939
let predicate = goal.predicate;
4040
let unconstrained_rhs = self.next_term_infer_of_kind(predicate.term);
@@ -56,6 +56,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
5656
self.self_ty()
5757
}
5858

59+
fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
60+
self.projection_ty.trait_ref(tcx)
61+
}
62+
5963
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
6064
self.with_self_ty(tcx, self_ty)
6165
}

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

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
mod cache;
22
mod overflow;
33

4+
pub(super) use overflow::OverflowHandler;
5+
46
use self::cache::ProvisionalEntry;
5-
pub(super) use crate::solve::search_graph::overflow::OverflowHandler;
67
use cache::ProvisionalCache;
78
use overflow::OverflowData;
89
use rustc_index::vec::IndexVec;
@@ -11,6 +12,8 @@ use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryRes
1112
use rustc_middle::ty::TyCtxt;
1213
use std::{collections::hash_map::Entry, mem};
1314

15+
use super::SolverMode;
16+
1417
rustc_index::newtype_index! {
1518
pub struct StackDepth {}
1619
}
@@ -21,6 +24,7 @@ struct StackElem<'tcx> {
2124
}
2225

2326
pub(super) struct SearchGraph<'tcx> {
27+
mode: SolverMode,
2428
/// The stack of goals currently being computed.
2529
///
2630
/// An element is *deeper* in the stack if its index is *lower*.
@@ -30,14 +34,19 @@ pub(super) struct SearchGraph<'tcx> {
3034
}
3135

3236
impl<'tcx> SearchGraph<'tcx> {
33-
pub(super) fn new(tcx: TyCtxt<'tcx>) -> SearchGraph<'tcx> {
37+
pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> {
3438
Self {
39+
mode,
3540
stack: Default::default(),
3641
overflow_data: OverflowData::new(tcx),
3742
provisional_cache: ProvisionalCache::empty(),
3843
}
3944
}
4045

46+
pub(super) fn solver_mode(&self) -> SolverMode {
47+
self.mode
48+
}
49+
4150
pub(super) fn is_empty(&self) -> bool {
4251
self.stack.is_empty() && self.provisional_cache.is_empty()
4352
}
@@ -245,7 +254,8 @@ impl<'tcx> SearchGraph<'tcx> {
245254
// dependencies, our non-root goal may no longer appear as child of the root goal.
246255
//
247256
// See https://github.com/rust-lang/rust/pull/108071 for some additional context.
248-
let should_cache_globally = !self.overflow_data.did_overflow() || self.stack.is_empty();
257+
let should_cache_globally = matches!(self.solver_mode(), SolverMode::Normal)
258+
&& (!self.overflow_data.did_overflow() || self.stack.is_empty());
249259
if should_cache_globally {
250260
tcx.new_solver_evaluation_cache.insert(
251261
current_goal.goal,

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::iter;
44

5-
use super::{assembly, EvalCtxt};
5+
use super::{assembly, EvalCtxt, SolverMode};
66
use rustc_hir::def_id::DefId;
77
use rustc_hir::LangItem;
88
use rustc_infer::traits::query::NoSolution;
@@ -20,6 +20,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
2020
self.self_ty()
2121
}
2222

23+
fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
24+
self.trait_ref
25+
}
26+
2327
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
2428
self.with_self_ty(tcx, self_ty)
2529
}
@@ -43,6 +47,22 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
4347
return Err(NoSolution);
4448
}
4549

50+
let impl_polarity = tcx.impl_polarity(impl_def_id);
51+
// An upper bound of the certainty of this goal, used to lower the certainty
52+
// of reservation impl to ambiguous during coherence.
53+
let maximal_certainty = match impl_polarity {
54+
ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => {
55+
match impl_polarity == goal.predicate.polarity {
56+
true => Certainty::Yes,
57+
false => return Err(NoSolution),
58+
}
59+
}
60+
ty::ImplPolarity::Reservation => match ecx.solver_mode() {
61+
SolverMode::Normal => return Err(NoSolution),
62+
SolverMode::Coherence => Certainty::AMBIGUOUS,
63+
},
64+
};
65+
4666
ecx.probe(|ecx| {
4767
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
4868
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
@@ -55,7 +75,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
5575
.into_iter()
5676
.map(|pred| goal.with(tcx, pred));
5777
ecx.add_goals(where_clause_bounds);
58-
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
78+
79+
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
5980
})
6081
}
6182

@@ -547,6 +568,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
547568
goal: Goal<'tcx, TraitPredicate<'tcx>>,
548569
) -> QueryResult<'tcx> {
549570
let candidates = self.assemble_and_evaluate_candidates(goal);
550-
self.merge_candidates_and_discard_reservation_impls(candidates)
571+
self.merge_candidates(candidates)
551572
}
552573
}

0 commit comments

Comments
 (0)