Skip to content

Commit e41380a

Browse files
committed
support fulfillment with non-fatal overflow
1 parent 5072826 commit e41380a

File tree

2 files changed

+68
-28
lines changed

2 files changed

+68
-28
lines changed

compiler/rustc_trait_selection/src/traits/engine.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ use rustc_span::Span;
2727
pub trait TraitEngineExt<'tcx> {
2828
fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
2929
fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self>;
30+
/// Creates a new fulfillment context which does not abort compilation
31+
/// on overflow.
32+
///
33+
/// WARNING: Overflow will be returned as an error in `select_where_possible`
34+
/// even though the overflow may not be the final result for the given obligation,
35+
/// so you have to be incredibly careful when using `select_where_possible.
36+
fn with_query_mode_canonical(tcx: TyCtxt<'tcx>) -> Box<Self>;
3037
}
3138

3239
impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
@@ -45,6 +52,14 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
4552
TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()),
4653
}
4754
}
55+
56+
fn with_query_mode_canonical(tcx: TyCtxt<'tcx>) -> Box<Self> {
57+
match tcx.sess.opts.unstable_opts.trait_solver {
58+
TraitSolver::Classic => Box::new(FulfillmentContext::with_query_mode_canonical()),
59+
TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()),
60+
TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()),
61+
}
62+
}
4863
}
4964

5065
/// Used if you want to have pleasant experience when dealing
@@ -63,6 +78,18 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
6378
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx.tcx)) }
6479
}
6580

81+
/// You have to be incredibly careful when using this method.
82+
///
83+
/// Used in places where we have to deal with overflow in a non-fatal way.
84+
/// Note that by using this the trait solver ends up being incomplete in a
85+
/// may way because overflow is returned as a hard error instead of ambiguity.
86+
pub fn with_query_mode_canonical(infcx: &'a InferCtxt<'tcx>) -> Self {
87+
Self {
88+
infcx,
89+
engine: RefCell::new(<dyn TraitEngine<'_>>::with_query_mode_canonical(infcx.tcx)),
90+
}
91+
}
92+
6693
pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
6794
self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
6895
}

compiler/rustc_trait_selection/src/traits/fulfill.rs

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use rustc_middle::ty::subst::SubstsRef;
1212
use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt};
1313
use std::marker::PhantomData;
1414

15-
use super::const_evaluatable;
1615
use super::project::{self, ProjectAndUnifyResult};
1716
use super::select::SelectionContext;
1817
use super::wf;
@@ -22,6 +21,7 @@ use super::CodeSelectionError;
2221
use super::EvaluationResult;
2322
use super::PredicateObligation;
2423
use super::Unimplemented;
24+
use super::{const_evaluatable, TraitQueryMode};
2525
use super::{FulfillmentError, FulfillmentErrorCode};
2626

2727
use crate::traits::project::PolyProjectionObligation;
@@ -64,6 +64,8 @@ pub struct FulfillmentContext<'tcx> {
6464
// a snapshot (they don't *straddle* a snapshot, so there
6565
// is no trouble there).
6666
usable_in_snapshot: bool,
67+
68+
query_mode: TraitQueryMode,
6769
}
6870

6971
#[derive(Clone, Debug)]
@@ -80,38 +82,30 @@ pub struct PendingPredicateObligation<'tcx> {
8082
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
8183
static_assert_size!(PendingPredicateObligation<'_>, 72);
8284

83-
impl<'a, 'tcx> FulfillmentContext<'tcx> {
85+
impl<'tcx> FulfillmentContext<'tcx> {
8486
/// Creates a new fulfillment context.
8587
pub(super) fn new() -> FulfillmentContext<'tcx> {
86-
FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: false }
88+
FulfillmentContext {
89+
predicates: ObligationForest::new(),
90+
usable_in_snapshot: false,
91+
query_mode: TraitQueryMode::Standard,
92+
}
8793
}
8894

8995
pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> {
90-
FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: true }
96+
FulfillmentContext {
97+
predicates: ObligationForest::new(),
98+
usable_in_snapshot: true,
99+
query_mode: TraitQueryMode::Standard,
100+
}
91101
}
92102

93-
/// Attempts to select obligations using `selcx`.
94-
fn select(&mut self, selcx: SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
95-
let span = debug_span!("select", obligation_forest_size = ?self.predicates.len());
96-
let _enter = span.enter();
97-
98-
// Process pending obligations.
99-
let outcome: Outcome<_, _> =
100-
self.predicates.process_obligations(&mut FulfillProcessor { selcx });
101-
102-
// FIXME: if we kept the original cache key, we could mark projection
103-
// obligations as complete for the projection cache here.
104-
105-
let errors: Vec<FulfillmentError<'tcx>> =
106-
outcome.errors.into_iter().map(to_fulfillment_error).collect();
107-
108-
debug!(
109-
"select({} predicates remaining, {} errors) done",
110-
self.predicates.len(),
111-
errors.len()
112-
);
113-
114-
errors
103+
pub(super) fn with_query_mode_canonical() -> FulfillmentContext<'tcx> {
104+
FulfillmentContext {
105+
predicates: ObligationForest::new(),
106+
usable_in_snapshot: false,
107+
query_mode: TraitQueryMode::Canonical,
108+
}
115109
}
116110
}
117111

@@ -138,8 +132,27 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
138132
}
139133

140134
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
141-
let selcx = SelectionContext::new(infcx);
142-
self.select(selcx)
135+
let span = debug_span!("select", obligation_forest_size = ?self.predicates.len());
136+
let selcx = SelectionContext::with_query_mode(infcx, self.query_mode);
137+
let _enter = span.enter();
138+
139+
// Process pending obligations.
140+
let outcome: Outcome<_, _> =
141+
self.predicates.process_obligations(&mut FulfillProcessor { selcx });
142+
143+
// FIXME: if we kept the original cache key, we could mark projection
144+
// obligations as complete for the projection cache here.
145+
146+
let errors: Vec<FulfillmentError<'tcx>> =
147+
outcome.errors.into_iter().map(to_fulfillment_error).collect();
148+
149+
debug!(
150+
"select({} predicates remaining, {} errors) done",
151+
self.predicates.len(),
152+
errors.len()
153+
);
154+
155+
errors
143156
}
144157

145158
fn drain_unstalled_obligations(

0 commit comments

Comments
 (0)