Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the new-style trait solver #56384

Merged
merged 19 commits into from
Dec 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "chalk-engine"
version = "0.8.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"chalk-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
Expand Down Expand Up @@ -2067,7 +2067,7 @@ dependencies = [
"backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"chalk-engine 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fmt_macros 0.0.0",
"graphviz 0.0.0",
Expand Down Expand Up @@ -2640,7 +2640,7 @@ name = "rustc_traits"
version = "0.0.0"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"chalk-engine 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"graphviz 0.0.0",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc 0.0.0",
Expand Down Expand Up @@ -3403,7 +3403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cargo_metadata 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d8dfe3adeb30f7938e6c1dd5327f29235d8ada3e898aeb08c343005ec2915a2"
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6749eb72e7d4355d944a99f15fbaea701b978c18c5e184a025fcde942b0c9779"
"checksum chalk-engine 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17ec698a6f053a23bfbe646d9f2fde4b02abc19125595270a99e6f44ae0bdd1a"
"checksum chalk-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "295635afd6853aa9f20baeb7f0204862440c0fe994c5a253d5f479dac41d047e"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ syntax_pos = { path = "../libsyntax_pos" }
backtrace = "0.3.3"
parking_lot = "0.6"
byteorder = { version = "1.1", features = ["i128"]}
chalk-engine = { version = "0.8.0", default-features=false }
chalk-engine = { version = "0.9.0", default-features=false }
rustc_fs_util = { path = "../librustc_fs_util" }
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }

Expand Down
1 change: 1 addition & 0 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ define_dep_nodes!( <'tcx>
[] ImpliedOutlivesBounds(CanonicalTyGoal<'tcx>),
[] DropckOutlives(CanonicalTyGoal<'tcx>),
[] EvaluateObligation(CanonicalPredicateGoal<'tcx>),
[] EvaluateGoal(traits::ChalkCanonicalGoal<'tcx>),
[] TypeOpAscribeUserType(CanonicalTypeOpAscribeUserTypeGoal<'tcx>),
[] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>),
[] TypeOpSubtype(CanonicalTypeOpSubtypeGoal<'tcx>),
Expand Down
14 changes: 13 additions & 1 deletion src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,8 @@ for ty::steal::Steal<T>

impl_stable_hash_for!(struct ty::ParamEnv<'tcx> {
caller_bounds,
reveal
reveal,
def_id
});

impl_stable_hash_for!(enum traits::Reveal {
Expand Down Expand Up @@ -1194,6 +1195,10 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::Goal<'tcx> {
quantifier.hash_stable(hcx, hasher);
goal.hash_stable(hcx, hasher);
},
Subtype(a, b) => {
a.hash_stable(hcx, hasher);
b.hash_stable(hcx, hasher);
}
CannotProve => { },
}
}
Expand Down Expand Up @@ -1239,3 +1244,10 @@ impl_stable_hash_for!(
clauses,
}
);

impl_stable_hash_for!(
impl<'tcx, G> for struct traits::InEnvironment<'tcx, G> {
environment,
goal,
}
);
13 changes: 11 additions & 2 deletions src/librustc/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,13 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx>
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match t.sty {
ty::Infer(ty::TyVar(vid)) => {
debug!("canonical: type var found with vid {:?}", vid);
match self.infcx.unwrap().probe_ty_var(vid) {
// `t` could be a float / int variable: canonicalize that instead
Ok(t) => self.fold_ty(t),
Ok(t) => {
debug!("(resolved to {:?})", t);
self.fold_ty(t)
}

// `TyVar(vid)` is unresolved, track its universe index in the canonicalized
// result
Expand Down Expand Up @@ -448,7 +452,12 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {

// Fast path: nothing that needs to be canonicalized.
if !value.has_type_flags(needs_canonical_flags) {
let out_value = gcx.lift(value).unwrap();
let out_value = gcx.lift(value).unwrap_or_else(|| {
bug!(
"failed to lift `{:?}` (nothing to canonicalize)",
value
)
});
let canon_value = Canonical {
max_universe: ty::UniverseIndex::ROOT,
variables: List::empty(),
Expand Down
26 changes: 25 additions & 1 deletion src/librustc/infer/canonical/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,33 @@ BraceStructLiftImpl! {
}

impl<'tcx> CanonicalVarValues<'tcx> {
fn len(&self) -> usize {
pub fn len(&self) -> usize {
self.var_values.len()
}

/// Make an identity substitution from this one: each bound var
/// is matched to the same bound var, preserving the original kinds.
/// For example, if we have:
/// `self.var_values == [Type(u32), Lifetime('a), Type(u64)]`
/// we'll return a substitution `subst` with:
/// `subst.var_values == [Type(^0), Lifetime(^1), Type(^2)]`.
pub fn make_identity<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
use ty::subst::UnpackedKind;

CanonicalVarValues {
var_values: self.var_values.iter()
.zip(0..)
.map(|(kind, i)| match kind.unpack() {
UnpackedKind::Type(..) => tcx.mk_ty(
ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i).into())
).into(),
UnpackedKind::Lifetime(..) => tcx.mk_region(
ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(i))
).into(),
})
.collect()
}
}
}

impl<'a, 'tcx> IntoIterator for &'a CanonicalVarValues<'tcx> {
Expand Down
18 changes: 11 additions & 7 deletions src/librustc/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustc_data_structures::sync::Lrc;
use std::fmt::Debug;
use syntax_pos::DUMMY_SP;
use traits::query::{Fallible, NoSolution};
use traits::{FulfillmentContext, TraitEngine};
use traits::TraitEngine;
use traits::{Obligation, ObligationCause, PredicateObligation};
use ty::fold::TypeFoldable;
use ty::subst::{Kind, UnpackedKind};
Expand All @@ -48,7 +48,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> {
pub fn enter_canonical_trait_query<K, R>(
&'tcx mut self,
canonical_key: &Canonical<'tcx, K>,
operation: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, &mut FulfillmentContext<'tcx>, K)
operation: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, &mut dyn TraitEngine<'tcx>, K)
-> Fallible<R>,
) -> Fallible<CanonicalizedQueryResponse<'gcx, R>>
where
Expand All @@ -59,9 +59,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> {
DUMMY_SP,
canonical_key,
|ref infcx, key, canonical_inference_vars| {
let fulfill_cx = &mut FulfillmentContext::new();
let value = operation(infcx, fulfill_cx, key)?;
infcx.make_canonicalized_query_response(canonical_inference_vars, value, fulfill_cx)
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
let value = operation(infcx, &mut *fulfill_cx, key)?;
infcx.make_canonicalized_query_response(
canonical_inference_vars,
value,
&mut *fulfill_cx
)
},
)
}
Expand Down Expand Up @@ -91,7 +95,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
fulfill_cx: &mut FulfillmentContext<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
) -> Fallible<CanonicalizedQueryResponse<'gcx, T>>
where
T: Debug + Lift<'gcx> + TypeFoldable<'tcx>,
Expand Down Expand Up @@ -138,7 +142,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
fulfill_cx: &mut FulfillmentContext<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
) -> Result<QueryResponse<'tcx, T>, NoSolution>
where
T: Debug + TypeFoldable<'tcx> + Lift<'gcx>,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ impl<'tcx, T> InferOk<'tcx, T> {
pub fn into_value_registering_obligations(
self,
infcx: &InferCtxt<'_, '_, 'tcx>,
fulfill_cx: &mut impl TraitEngine<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
) -> T {
let InferOk { value, obligations } = self;
for obligation in obligations {
Expand Down
7 changes: 6 additions & 1 deletion src/librustc/traits/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,17 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
computed_preds.extend(user_computed_preds.iter().cloned());
let normalized_preds =
elaborate_predicates(tcx, computed_preds.clone().into_iter().collect());
new_env = ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal);
new_env = ty::ParamEnv::new(
tcx.mk_predicates(normalized_preds),
param_env.reveal,
None
);
}

let final_user_env = ty::ParamEnv::new(
tcx.mk_predicates(user_computed_preds.into_iter()),
user_env.reveal,
None
);
debug!(
"evaluate_nested_obligations(ty_did={:?}, trait_did={:?}): succeeded with '{:?}' \
Expand Down
165 changes: 165 additions & 0 deletions src/librustc/traits/chalk_fulfill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use traits::{
Environment,
InEnvironment,
TraitEngine,
ObligationCause,
PredicateObligation,
FulfillmentError,
FulfillmentErrorCode,
SelectionError,
};
use traits::query::NoSolution;
use infer::InferCtxt;
use infer::canonical::{Canonical, OriginalQueryValues};
use ty::{self, Ty};
use rustc_data_structures::fx::FxHashSet;

pub type CanonicalGoal<'tcx> = Canonical<'tcx, InEnvironment<'tcx, ty::Predicate<'tcx>>>;

pub struct FulfillmentContext<'tcx> {
obligations: FxHashSet<InEnvironment<'tcx, PredicateObligation<'tcx>>>,
}

impl FulfillmentContext<'tcx> {
crate fn new() -> Self {
FulfillmentContext {
obligations: FxHashSet::default(),
}
}
}

fn in_environment(
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
obligation: PredicateObligation<'tcx>
) -> InEnvironment<'tcx, PredicateObligation<'tcx>> {
assert!(!infcx.is_in_snapshot());
let obligation = infcx.resolve_type_vars_if_possible(&obligation);

let environment = match obligation.param_env.def_id {
Some(def_id) => infcx.tcx.environment(def_id),
None if obligation.param_env.caller_bounds.is_empty() => Environment {
clauses: ty::List::empty(),
},
_ => bug!("non-empty `ParamEnv` with no def-id"),
};

InEnvironment {
environment,
goal: obligation,
}
}

impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
fn normalize_projection_type(
&mut self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
_param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
_cause: ObligationCause<'tcx>,
) -> Ty<'tcx> {
infcx.tcx.mk_ty(ty::Projection(projection_ty))
}

fn register_predicate_obligation(
&mut self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
obligation: PredicateObligation<'tcx>,
) {
self.obligations.insert(in_environment(infcx, obligation));
}

fn select_all_or_error(
&mut self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
self.select_where_possible(infcx)?;

if self.obligations.is_empty() {
Ok(())
} else {
let errors = self.obligations.iter()
.map(|obligation| FulfillmentError {
obligation: obligation.goal.clone(),
code: FulfillmentErrorCode::CodeAmbiguity,
})
.collect();
Err(errors)
}
}

fn select_where_possible(
&mut self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
let mut errors = Vec::new();
let mut next_round = FxHashSet::default();
let mut making_progress;

loop {
making_progress = false;

// We iterate over all obligations, and record if we are able
// to unambiguously prove at least one obligation.
for obligation in self.obligations.drain() {
let mut orig_values = OriginalQueryValues::default();
let canonical_goal = infcx.canonicalize_query(&InEnvironment {
environment: obligation.environment,
goal: obligation.goal.predicate,
}, &mut orig_values);

match infcx.tcx.global_tcx().evaluate_goal(canonical_goal) {
Ok(response) => {
if response.is_proven() {
making_progress = true;

match infcx.instantiate_query_response_and_region_obligations(
&obligation.goal.cause,
obligation.goal.param_env,
&orig_values,
&response
) {
Ok(infer_ok) => next_round.extend(
infer_ok.obligations
.into_iter()
.map(|obligation| in_environment(infcx, obligation))
),

Err(_err) => errors.push(FulfillmentError {
obligation: obligation.goal,
code: FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented
),
}),
}
} else {
// Ambiguous: retry at next round.
next_round.insert(obligation);
}
}

Err(NoSolution) => errors.push(FulfillmentError {
obligation: obligation.goal,
code: FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented
),
})
}
}
next_round = std::mem::replace(&mut self.obligations, next_round);

if !making_progress {
break;
}
}

if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}

fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.obligations.iter().map(|obligation| obligation.goal.clone()).collect()
}
}
Loading