Skip to content

Commit

Permalink
Auto merge of #48995 - aravind-pg:canonical-query, r=<try>
Browse files Browse the repository at this point in the history
[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
  • Loading branch information
bors committed Mar 20, 2018
2 parents 75af15e + f37a1d2 commit 0fdc38f
Show file tree
Hide file tree
Showing 22 changed files with 167 additions and 82 deletions.
4 changes: 3 additions & 1 deletion src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
use std::fmt;
use std::hash::Hash;
use syntax_pos::symbol::InternedString;
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
use traits::query::{CanonicalProjectionGoal,
CanonicalTyGoal, CanonicalPredicateGoal};
use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty};
use ty::subst::Substs;

Expand Down Expand Up @@ -638,6 +639,7 @@ define_dep_nodes!( <'tcx>
[] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>),
[] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>),
[] DropckOutlives(CanonicalTyGoal<'tcx>),
[] EvaluateObligation(CanonicalPredicateGoal<'tcx>),

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

Expand Down
5 changes: 4 additions & 1 deletion src/librustc/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
recursion_depth: 0,
predicate: p })
.chain(obligations)
.find(|o| !selcx.evaluate_obligation(o));
.find(|o| !selcx.evaluate_obligation(o).may_apply());
// FIXME: the call to `selcx.evaluate_obligation().may_apply` above should be
// ported to the canonical trait query form, `infcx.predicate_may_hold`, once
// the new system supports intercrate mode (which coherence needs).

if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
Expand Down
16 changes: 3 additions & 13 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,12 +644,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
trait_pred
});
let unit_obligation = Obligation {
predicate: ty::Predicate::Trait(predicate),
.. obligation.clone()
};
let mut selcx = SelectionContext::new(self);
if selcx.evaluate_obligation(&unit_obligation) {
if self.predicate_may_hold(obligation.param_env,
ty::Predicate::Trait(predicate)) {
err.note("the trait is implemented for `()`. \
Possibly this error has been caused by changes to \
Rust's type-inference algorithm \
Expand Down Expand Up @@ -1312,13 +1308,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
&cleaned_pred
).value;

let obligation = Obligation::new(
ObligationCause::dummy(),
param_env,
cleaned_pred.to_predicate()
);

selcx.evaluate_obligation(&obligation)
self.predicate_may_hold(param_env, cleaned_pred.to_predicate())
})
}

Expand Down
3 changes: 2 additions & 1 deletion src/librustc/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ fn process_predicate<'a, 'gcx, 'tcx>(
if data.is_global() {
// no type variables present, can use evaluation for better caching.
// FIXME: consider caching errors too.
if selcx.evaluate_obligation_conservatively(&obligation) {
if selcx.infcx().predicate_must_hold(obligation.param_env,
obligation.predicate) {
debug!("selecting trait `{:?}` at depth {} evaluated to holds",
data, obligation.recursion_depth);
return Ok(Some(vec![]))
Expand Down
11 changes: 2 additions & 9 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub use self::object_safety::ObjectSafetyViolation;
pub use self::object_safety::MethodViolationCode;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
pub use self::select::IntercrateAmbiguityCause;
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause};
pub use self::specialize::{OverlapError, specialization_graph, translate_substs};
pub use self::specialize::{SpecializesCache, find_associated_item};
pub use self::util::elaborate_predicates;
Expand Down Expand Up @@ -512,15 +512,8 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
def_id,
substs: infcx.tcx.mk_substs_trait(ty, &[]),
};
let obligation = Obligation {
param_env,
cause: ObligationCause::misc(span, ast::DUMMY_NODE_ID),
recursion_depth: 0,
predicate: trait_ref.to_predicate(),
};

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

Expand Down
59 changes: 59 additions & 0 deletions src/librustc/traits/query/evaluate_obligation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use infer::InferCtxt;
use infer::canonical::{Canonical, Canonicalize};
use traits::EvaluationResult;
use traits::query::CanonicalPredicateGoal;
use ty::{ParamEnv, ParamEnvAnd, Predicate, TyCtxt};

impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// Evaluates whether the predicate can be satisfied (by any means)
/// in the given `ParamEnv`.
pub fn predicate_may_hold(
&self,
param_env: ParamEnv<'tcx>,
predicate: Predicate<'tcx>,
) -> bool {
self.evaluate_obligation(param_env, predicate).may_apply()
}

/// Evaluates whether the predicate can be satisfied in the given
/// `ParamEnv`, and returns `false` if not certain. However, this is
/// not entirely accurate if inference variables are involved.
pub fn predicate_must_hold(
&self,
param_env: ParamEnv<'tcx>,
predicate: Predicate<'tcx>,
) -> bool {
self.evaluate_obligation(param_env, predicate) == EvaluationResult::EvaluatedToOk
}

fn evaluate_obligation(
&self,
param_env: ParamEnv<'tcx>,
predicate: Predicate<'tcx>,
) -> EvaluationResult {
let (c_pred, _) = self.canonicalize_query(&param_env.and(predicate));

self.tcx.global_tcx().evaluate_obligation(c_pred)
}
}

impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ParamEnvAnd<'tcx, Predicate<'tcx>> {
type Canonicalized = CanonicalPredicateGoal<'gcx>;

fn intern(
_gcx: TyCtxt<'_, 'gcx, 'gcx>,
value: Canonical<'gcx, Self::Lifted>,
) -> Self::Canonicalized {
value
}
}
4 changes: 4 additions & 0 deletions src/librustc/traits/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use infer::canonical::Canonical;
use ty::{self, Ty};

pub mod dropck_outlives;
pub mod evaluate_obligation;
pub mod normalize;
pub mod normalize_erasing_regions;

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

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

pub type CanonicalPredicateGoal<'tcx> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct NoSolution;

Expand Down
37 changes: 14 additions & 23 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ enum BuiltinImplConditions<'tcx> {
/// all the "potential success" candidates can potentially succeed,
/// so they are no-ops when unioned with a definite error, and within
/// the categories it's easy to see that the unions are correct.
enum EvaluationResult {
pub enum EvaluationResult {
/// Evaluation successful
EvaluatedToOk,
/// Evaluation is known to be ambiguous - it *might* hold for some
Expand Down Expand Up @@ -383,7 +383,7 @@ enum EvaluationResult {
}

impl EvaluationResult {
fn may_apply(self) -> bool {
pub fn may_apply(self) -> bool {
match self {
EvaluatedToOk |
EvaluatedToAmbig |
Expand All @@ -406,6 +406,14 @@ impl EvaluationResult {
}
}

impl_stable_hash_for!(enum self::EvaluationResult {
EvaluatedToOk,
EvaluatedToAmbig,
EvaluatedToUnknown,
EvaluatedToRecur,
EvaluatedToErr
});

#[derive(Clone)]
pub struct EvaluationCache<'tcx> {
hashmap: RefCell<FxHashMap<ty::PolyTraitRef<'tcx>, WithDepNode<EvaluationResult>>>
Expand Down Expand Up @@ -544,33 +552,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// The result is "true" if the obligation *may* hold and "false" if
// we can be sure it does not.

/// Evaluates whether the obligation `obligation` can be satisfied (by any means).
/// Evaluates whether the obligation `obligation` can be satisfied and returns
/// an `EvaluationResult`.
pub fn evaluate_obligation(&mut self,
obligation: &PredicateObligation<'tcx>)
-> bool
{
debug!("evaluate_obligation({:?})",
obligation);

self.probe(|this, _| {
this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
.may_apply()
})
}

/// Evaluates whether the obligation `obligation` can be satisfied,
/// and returns `false` if not certain. However, this is not entirely
/// accurate if inference variables are involved.
pub fn evaluate_obligation_conservatively(&mut self,
obligation: &PredicateObligation<'tcx>)
-> bool
-> EvaluationResult
{
debug!("evaluate_obligation_conservatively({:?})",
obligation);
debug!("evaluate_obligation({:?})", obligation);

self.probe(|this, _| {
this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
== EvaluatedToOk
})
}

Expand Down
8 changes: 7 additions & 1 deletion src/librustc/ty/maps/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use dep_graph::SerializedDepNodeIndex;
use hir::def_id::{CrateNum, DefId, DefIndex};
use mir::interpret::{GlobalId};
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal};
use ty::{self, ParamEnvAnd, Ty, TyCtxt};
use ty::subst::Substs;
use ty::maps::queries;
Expand Down Expand Up @@ -73,6 +73,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::normalize_ty_after_erasing_region
}
}

impl<'tcx> QueryDescription<'tcx> for queries::evaluate_obligation<'tcx> {
fn describe(_tcx: TyCtxt, goal: CanonicalPredicateGoal<'tcx>) -> String {
format!("evaluating trait selection obligation `{}`", goal.value.value)
}
}

impl<'tcx> QueryDescription<'tcx> for queries::is_copy_raw<'tcx> {
fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
format!("computing whether `{}` is `Copy`", env.value)
Expand Down
12 changes: 11 additions & 1 deletion src/librustc/ty/maps/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! Defines the set of legal keys that can be used in queries.

use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex};
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal};
use ty::{self, Ty, TyCtxt};
use ty::subst::Substs;
use ty::fast_reject::SimplifiedType;
Expand Down Expand Up @@ -191,3 +191,13 @@ impl<'tcx> Key for CanonicalTyGoal<'tcx> {
DUMMY_SP
}
}

impl<'tcx> Key for CanonicalPredicateGoal<'tcx> {
fn map_crate(&self) -> CrateNum {
LOCAL_CRATE
}

fn default_span(&self, _tcx: TyCtxt) -> Span {
DUMMY_SP
}
}
10 changes: 8 additions & 2 deletions src/librustc/ty/maps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ use mir;
use mir::interpret::{GlobalId};
use session::{CompileResult, CrateDisambiguator};
use session::config::OutputFilenames;
use traits::Vtable;
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal, NoSolution};
use traits::{self, Vtable};
use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal,
CanonicalTyGoal, NoSolution};
use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult};
use traits::query::normalize::NormalizationResult;
use traits::specialization_graph;
Expand Down Expand Up @@ -407,6 +408,11 @@ define_maps! { <'tcx>
NoSolution,
>,

/// Do not call this query directly: invoke `infcx.predicate_may_hold()` or
/// `infcx.predicate_must_hold()` instead.
[] fn evaluate_obligation:
EvaluateObligation(CanonicalPredicateGoal<'tcx>) -> traits::EvaluationResult,

[] fn substitute_normalize_and_test_predicates:
substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,

Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/maps/plumbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
DepKind::NormalizeProjectionTy |
DepKind::NormalizeTyAfterErasingRegions |
DepKind::DropckOutlives |
DepKind::EvaluateObligation |
DepKind::SubstituteNormalizeAndTestPredicates |
DepKind::InstanceDefSizeEstimate |

Expand Down
34 changes: 34 additions & 0 deletions src/librustc_traits/evaluate_obligation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc::traits::{EvaluationResult, Obligation, ObligationCause, SelectionContext};
use rustc::traits::query::CanonicalPredicateGoal;
use rustc::ty::{ParamEnvAnd, TyCtxt};
use syntax::codemap::DUMMY_SP;

crate fn evaluate_obligation<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
goal: CanonicalPredicateGoal<'tcx>,
) -> EvaluationResult {
tcx.infer_ctxt().enter(|ref infcx| {
let (
ParamEnvAnd {
param_env,
value: predicate,
},
_canonical_inference_vars,
) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal);

let mut selcx = SelectionContext::new(&infcx);
let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate);

selcx.evaluate_obligation(&obligation)
})
}
2 changes: 2 additions & 0 deletions src/librustc_traits/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ extern crate syntax;
extern crate syntax_pos;

mod dropck_outlives;
mod evaluate_obligation;
mod normalize_projection_ty;
mod normalize_erasing_regions;
mod util;
Expand All @@ -41,6 +42,7 @@ pub fn provide(p: &mut Providers) {
normalize_ty_after_erasing_regions:
normalize_erasing_regions::normalize_ty_after_erasing_regions,
program_clauses_for: lowering::program_clauses_for,
evaluate_obligation: evaluate_obligation::evaluate_obligation,
..*p
};
}
8 changes: 3 additions & 5 deletions src/librustc_typeck/check/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,13 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {

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

let mut selcx = traits::SelectionContext::new(self.fcx);
let obligation = traits::Obligation::new(cause.clone(),
self.fcx.param_env,
trait_ref.to_predicate());
if !selcx.evaluate_obligation(&obligation) {
if !self.fcx.predicate_may_hold(self.fcx.param_env,
trait_ref.to_predicate()) {
debug!("overloaded_deref_ty: cannot match obligation");
return None;
}

let mut selcx = traits::SelectionContext::new(self.fcx);
let normalized = traits::normalize_projection_type(&mut selcx,
self.fcx.param_env,
ty::ProjectionTy::from_ref_and_name(
Expand Down
Loading

0 comments on commit 0fdc38f

Please sign in to comment.