Skip to content

Commit 0fdc38f

Browse files
committed
Auto merge of #48995 - aravind-pg:canonical-query, r=<try>
[WIP] Create a canonical trait query for `evaluate_obligation` This builds on the canonical query machinery introduced in #48411 to introduce a new canonical trait query for `evaluate_obligation` in the trait selector. Also ports most callers of the original `evaluate_obligation` to the new system (except in coherence, which requires support for intercrate mode). Closes #48537. r? @nikomatsakis
2 parents 75af15e + f37a1d2 commit 0fdc38f

22 files changed

+167
-82
lines changed

src/librustc/dep_graph/dep_node.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
7070
use std::fmt;
7171
use std::hash::Hash;
7272
use syntax_pos::symbol::InternedString;
73-
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
73+
use traits::query::{CanonicalProjectionGoal,
74+
CanonicalTyGoal, CanonicalPredicateGoal};
7475
use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty};
7576
use ty::subst::Substs;
7677

@@ -638,6 +639,7 @@ define_dep_nodes!( <'tcx>
638639
[] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>),
639640
[] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>),
640641
[] DropckOutlives(CanonicalTyGoal<'tcx>),
642+
[] EvaluateObligation(CanonicalPredicateGoal<'tcx>),
641643

642644
[] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) },
643645

src/librustc/traits/coherence.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
156156
recursion_depth: 0,
157157
predicate: p })
158158
.chain(obligations)
159-
.find(|o| !selcx.evaluate_obligation(o));
159+
.find(|o| !selcx.evaluate_obligation(o).may_apply());
160+
// FIXME: the call to `selcx.evaluate_obligation().may_apply` above should be
161+
// ported to the canonical trait query form, `infcx.predicate_may_hold`, once
162+
// the new system supports intercrate mode (which coherence needs).
160163

161164
if let Some(failing_obligation) = opt_failing_obligation {
162165
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);

src/librustc/traits/error_reporting.rs

+3-13
Original file line numberDiff line numberDiff line change
@@ -644,12 +644,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
644644
}
645645
trait_pred
646646
});
647-
let unit_obligation = Obligation {
648-
predicate: ty::Predicate::Trait(predicate),
649-
.. obligation.clone()
650-
};
651-
let mut selcx = SelectionContext::new(self);
652-
if selcx.evaluate_obligation(&unit_obligation) {
647+
if self.predicate_may_hold(obligation.param_env,
648+
ty::Predicate::Trait(predicate)) {
653649
err.note("the trait is implemented for `()`. \
654650
Possibly this error has been caused by changes to \
655651
Rust's type-inference algorithm \
@@ -1312,13 +1308,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
13121308
&cleaned_pred
13131309
).value;
13141310

1315-
let obligation = Obligation::new(
1316-
ObligationCause::dummy(),
1317-
param_env,
1318-
cleaned_pred.to_predicate()
1319-
);
1320-
1321-
selcx.evaluate_obligation(&obligation)
1311+
self.predicate_may_hold(param_env, cleaned_pred.to_predicate())
13221312
})
13231313
}
13241314

src/librustc/traits/fulfill.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,8 @@ fn process_predicate<'a, 'gcx, 'tcx>(
330330
if data.is_global() {
331331
// no type variables present, can use evaluation for better caching.
332332
// FIXME: consider caching errors too.
333-
if selcx.evaluate_obligation_conservatively(&obligation) {
333+
if selcx.infcx().predicate_must_hold(obligation.param_env,
334+
obligation.predicate) {
334335
debug!("selecting trait `{:?}` at depth {} evaluated to holds",
335336
data, obligation.recursion_depth);
336337
return Ok(Some(vec![]))

src/librustc/traits/mod.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub use self::object_safety::ObjectSafetyViolation;
4242
pub use self::object_safety::MethodViolationCode;
4343
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
4444
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
45-
pub use self::select::IntercrateAmbiguityCause;
45+
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause};
4646
pub use self::specialize::{OverlapError, specialization_graph, translate_substs};
4747
pub use self::specialize::{SpecializesCache, find_associated_item};
4848
pub use self::util::elaborate_predicates;
@@ -512,15 +512,8 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
512512
def_id,
513513
substs: infcx.tcx.mk_substs_trait(ty, &[]),
514514
};
515-
let obligation = Obligation {
516-
param_env,
517-
cause: ObligationCause::misc(span, ast::DUMMY_NODE_ID),
518-
recursion_depth: 0,
519-
predicate: trait_ref.to_predicate(),
520-
};
521515

522-
let result = SelectionContext::new(infcx)
523-
.evaluate_obligation_conservatively(&obligation);
516+
let result = infcx.predicate_must_hold(param_env, trait_ref.to_predicate());
524517
debug!("type_known_to_meet_ty={:?} bound={} => {:?}",
525518
ty, infcx.tcx.item_path_str(def_id), result);
526519

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use infer::InferCtxt;
12+
use infer::canonical::{Canonical, Canonicalize};
13+
use traits::EvaluationResult;
14+
use traits::query::CanonicalPredicateGoal;
15+
use ty::{ParamEnv, ParamEnvAnd, Predicate, TyCtxt};
16+
17+
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
18+
/// Evaluates whether the predicate can be satisfied (by any means)
19+
/// in the given `ParamEnv`.
20+
pub fn predicate_may_hold(
21+
&self,
22+
param_env: ParamEnv<'tcx>,
23+
predicate: Predicate<'tcx>,
24+
) -> bool {
25+
self.evaluate_obligation(param_env, predicate).may_apply()
26+
}
27+
28+
/// Evaluates whether the predicate can be satisfied in the given
29+
/// `ParamEnv`, and returns `false` if not certain. However, this is
30+
/// not entirely accurate if inference variables are involved.
31+
pub fn predicate_must_hold(
32+
&self,
33+
param_env: ParamEnv<'tcx>,
34+
predicate: Predicate<'tcx>,
35+
) -> bool {
36+
self.evaluate_obligation(param_env, predicate) == EvaluationResult::EvaluatedToOk
37+
}
38+
39+
fn evaluate_obligation(
40+
&self,
41+
param_env: ParamEnv<'tcx>,
42+
predicate: Predicate<'tcx>,
43+
) -> EvaluationResult {
44+
let (c_pred, _) = self.canonicalize_query(&param_env.and(predicate));
45+
46+
self.tcx.global_tcx().evaluate_obligation(c_pred)
47+
}
48+
}
49+
50+
impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ParamEnvAnd<'tcx, Predicate<'tcx>> {
51+
type Canonicalized = CanonicalPredicateGoal<'gcx>;
52+
53+
fn intern(
54+
_gcx: TyCtxt<'_, 'gcx, 'gcx>,
55+
value: Canonical<'gcx, Self::Lifted>,
56+
) -> Self::Canonicalized {
57+
value
58+
}
59+
}

src/librustc/traits/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use infer::canonical::Canonical;
1919
use ty::{self, Ty};
2020

2121
pub mod dropck_outlives;
22+
pub mod evaluate_obligation;
2223
pub mod normalize;
2324
pub mod normalize_erasing_regions;
2425

@@ -27,6 +28,9 @@ pub type CanonicalProjectionGoal<'tcx> =
2728

2829
pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>;
2930

31+
pub type CanonicalPredicateGoal<'tcx> =
32+
Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>;
33+
3034
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
3135
pub struct NoSolution;
3236

src/librustc/traits/select.rs

+14-23
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ enum BuiltinImplConditions<'tcx> {
319319
/// all the "potential success" candidates can potentially succeed,
320320
/// so they are no-ops when unioned with a definite error, and within
321321
/// the categories it's easy to see that the unions are correct.
322-
enum EvaluationResult {
322+
pub enum EvaluationResult {
323323
/// Evaluation successful
324324
EvaluatedToOk,
325325
/// Evaluation is known to be ambiguous - it *might* hold for some
@@ -383,7 +383,7 @@ enum EvaluationResult {
383383
}
384384

385385
impl EvaluationResult {
386-
fn may_apply(self) -> bool {
386+
pub fn may_apply(self) -> bool {
387387
match self {
388388
EvaluatedToOk |
389389
EvaluatedToAmbig |
@@ -406,6 +406,14 @@ impl EvaluationResult {
406406
}
407407
}
408408

409+
impl_stable_hash_for!(enum self::EvaluationResult {
410+
EvaluatedToOk,
411+
EvaluatedToAmbig,
412+
EvaluatedToUnknown,
413+
EvaluatedToRecur,
414+
EvaluatedToErr
415+
});
416+
409417
#[derive(Clone)]
410418
pub struct EvaluationCache<'tcx> {
411419
hashmap: RefCell<FxHashMap<ty::PolyTraitRef<'tcx>, WithDepNode<EvaluationResult>>>
@@ -544,33 +552,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
544552
// The result is "true" if the obligation *may* hold and "false" if
545553
// we can be sure it does not.
546554

547-
/// Evaluates whether the obligation `obligation` can be satisfied (by any means).
555+
/// Evaluates whether the obligation `obligation` can be satisfied and returns
556+
/// an `EvaluationResult`.
548557
pub fn evaluate_obligation(&mut self,
549558
obligation: &PredicateObligation<'tcx>)
550-
-> bool
551-
{
552-
debug!("evaluate_obligation({:?})",
553-
obligation);
554-
555-
self.probe(|this, _| {
556-
this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
557-
.may_apply()
558-
})
559-
}
560-
561-
/// Evaluates whether the obligation `obligation` can be satisfied,
562-
/// and returns `false` if not certain. However, this is not entirely
563-
/// accurate if inference variables are involved.
564-
pub fn evaluate_obligation_conservatively(&mut self,
565-
obligation: &PredicateObligation<'tcx>)
566-
-> bool
559+
-> EvaluationResult
567560
{
568-
debug!("evaluate_obligation_conservatively({:?})",
569-
obligation);
561+
debug!("evaluate_obligation({:?})", obligation);
570562

571563
self.probe(|this, _| {
572564
this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
573-
== EvaluatedToOk
574565
})
575566
}
576567

src/librustc/ty/maps/config.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use dep_graph::SerializedDepNodeIndex;
1212
use hir::def_id::{CrateNum, DefId, DefIndex};
1313
use mir::interpret::{GlobalId};
14-
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
14+
use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal};
1515
use ty::{self, ParamEnvAnd, Ty, TyCtxt};
1616
use ty::subst::Substs;
1717
use ty::maps::queries;
@@ -73,6 +73,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::normalize_ty_after_erasing_region
7373
}
7474
}
7575

76+
impl<'tcx> QueryDescription<'tcx> for queries::evaluate_obligation<'tcx> {
77+
fn describe(_tcx: TyCtxt, goal: CanonicalPredicateGoal<'tcx>) -> String {
78+
format!("evaluating trait selection obligation `{}`", goal.value.value)
79+
}
80+
}
81+
7682
impl<'tcx> QueryDescription<'tcx> for queries::is_copy_raw<'tcx> {
7783
fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
7884
format!("computing whether `{}` is `Copy`", env.value)

src/librustc/ty/maps/keys.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! Defines the set of legal keys that can be used in queries.
1212
1313
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex};
14-
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
14+
use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal};
1515
use ty::{self, Ty, TyCtxt};
1616
use ty::subst::Substs;
1717
use ty::fast_reject::SimplifiedType;
@@ -191,3 +191,13 @@ impl<'tcx> Key for CanonicalTyGoal<'tcx> {
191191
DUMMY_SP
192192
}
193193
}
194+
195+
impl<'tcx> Key for CanonicalPredicateGoal<'tcx> {
196+
fn map_crate(&self) -> CrateNum {
197+
LOCAL_CRATE
198+
}
199+
200+
fn default_span(&self, _tcx: TyCtxt) -> Span {
201+
DUMMY_SP
202+
}
203+
}

src/librustc/ty/maps/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ use mir;
3333
use mir::interpret::{GlobalId};
3434
use session::{CompileResult, CrateDisambiguator};
3535
use session::config::OutputFilenames;
36-
use traits::Vtable;
37-
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal, NoSolution};
36+
use traits::{self, Vtable};
37+
use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal,
38+
CanonicalTyGoal, NoSolution};
3839
use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult};
3940
use traits::query::normalize::NormalizationResult;
4041
use traits::specialization_graph;
@@ -407,6 +408,11 @@ define_maps! { <'tcx>
407408
NoSolution,
408409
>,
409410

411+
/// Do not call this query directly: invoke `infcx.predicate_may_hold()` or
412+
/// `infcx.predicate_must_hold()` instead.
413+
[] fn evaluate_obligation:
414+
EvaluateObligation(CanonicalPredicateGoal<'tcx>) -> traits::EvaluationResult,
415+
410416
[] fn substitute_normalize_and_test_predicates:
411417
substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,
412418

src/librustc/ty/maps/plumbing.rs

+1
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
775775
DepKind::NormalizeProjectionTy |
776776
DepKind::NormalizeTyAfterErasingRegions |
777777
DepKind::DropckOutlives |
778+
DepKind::EvaluateObligation |
778779
DepKind::SubstituteNormalizeAndTestPredicates |
779780
DepKind::InstanceDefSizeEstimate |
780781

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::traits::{EvaluationResult, Obligation, ObligationCause, SelectionContext};
12+
use rustc::traits::query::CanonicalPredicateGoal;
13+
use rustc::ty::{ParamEnvAnd, TyCtxt};
14+
use syntax::codemap::DUMMY_SP;
15+
16+
crate fn evaluate_obligation<'tcx>(
17+
tcx: TyCtxt<'_, 'tcx, 'tcx>,
18+
goal: CanonicalPredicateGoal<'tcx>,
19+
) -> EvaluationResult {
20+
tcx.infer_ctxt().enter(|ref infcx| {
21+
let (
22+
ParamEnvAnd {
23+
param_env,
24+
value: predicate,
25+
},
26+
_canonical_inference_vars,
27+
) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal);
28+
29+
let mut selcx = SelectionContext::new(&infcx);
30+
let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate);
31+
32+
selcx.evaluate_obligation(&obligation)
33+
})
34+
}

src/librustc_traits/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extern crate syntax;
2626
extern crate syntax_pos;
2727

2828
mod dropck_outlives;
29+
mod evaluate_obligation;
2930
mod normalize_projection_ty;
3031
mod normalize_erasing_regions;
3132
mod util;
@@ -41,6 +42,7 @@ pub fn provide(p: &mut Providers) {
4142
normalize_ty_after_erasing_regions:
4243
normalize_erasing_regions::normalize_ty_after_erasing_regions,
4344
program_clauses_for: lowering::program_clauses_for,
45+
evaluate_obligation: evaluate_obligation::evaluate_obligation,
4446
..*p
4547
};
4648
}

src/librustc_typeck/check/autoderef.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,13 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
120120

121121
let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
122122

123-
let mut selcx = traits::SelectionContext::new(self.fcx);
124-
let obligation = traits::Obligation::new(cause.clone(),
125-
self.fcx.param_env,
126-
trait_ref.to_predicate());
127-
if !selcx.evaluate_obligation(&obligation) {
123+
if !self.fcx.predicate_may_hold(self.fcx.param_env,
124+
trait_ref.to_predicate()) {
128125
debug!("overloaded_deref_ty: cannot match obligation");
129126
return None;
130127
}
131128

129+
let mut selcx = traits::SelectionContext::new(self.fcx);
132130
let normalized = traits::normalize_projection_type(&mut selcx,
133131
self.fcx.param_env,
134132
ty::ProjectionTy::from_ref_and_name(

0 commit comments

Comments
 (0)