Skip to content

Commit fb86d60

Browse files
committed
Auto merge of #56384 - scalexm:chalk, r=nikomatsakis
Implement the new-style trait solver Final PR of what I believe to be a minimally working implementation of the new-style trait solver. The new trait solver can be used by providing the `-Z chalk` command line flag. It is currently used everywhere in `rustc_typeck`, and for everything relying on `rustc::infer::canonical::query_response::enter_canonical_trait_query`. The trait solver is invoked in rustc by using the `evaluate_goal` canonical query. This is not optimal because each call to `evaluate_goal` creates a new `chalk_engine::Forest`, hence rustc cannot use answers to intermediate goals produced by the root goal. We'll need to change that but I guess that's ok for now. Some next steps, I think, are: * handle region constraints: region constraints are computed but are completely ignored for now, I think we may need additional support from `chalk_engine` (as a side effect, types or trait references with outlive requirements cannot be proved well-formed) * deactivate eager normalization in the presence of `-Z chalk` in order to leverage the lazy normalization strategy of the new-style trait solver * add the remaining built-in impls (only `Sized` is supported currently) * transition the compiler to using generic goals instead of predicates that still refer to named type parameters etc I added a few very simple tests to check that the new solver has the right behavior, they won't be needed anymore once it is mature enough. Additionally it shows off that we get [implied bounds](#44491) for free. r? @nikomatsakis
2 parents bc09637 + 993d213 commit fb86d60

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1311
-293
lines changed

Diff for: Cargo.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
273273

274274
[[package]]
275275
name = "chalk-engine"
276-
version = "0.8.0"
276+
version = "0.9.0"
277277
source = "registry+https://github.com/rust-lang/crates.io-index"
278278
dependencies = [
279279
"chalk-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2067,7 +2067,7 @@ dependencies = [
20672067
"backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
20682068
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
20692069
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
2070-
"chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
2070+
"chalk-engine 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
20712071
"flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
20722072
"fmt_macros 0.0.0",
20732073
"graphviz 0.0.0",
@@ -2640,7 +2640,7 @@ name = "rustc_traits"
26402640
version = "0.0.0"
26412641
dependencies = [
26422642
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
2643-
"chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
2643+
"chalk-engine 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
26442644
"graphviz 0.0.0",
26452645
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
26462646
"rustc 0.0.0",
@@ -3403,7 +3403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
34033403
"checksum cargo_metadata 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d8dfe3adeb30f7938e6c1dd5327f29235d8ada3e898aeb08c343005ec2915a2"
34043404
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
34053405
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
3406-
"checksum chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6749eb72e7d4355d944a99f15fbaea701b978c18c5e184a025fcde942b0c9779"
3406+
"checksum chalk-engine 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17ec698a6f053a23bfbe646d9f2fde4b02abc19125595270a99e6f44ae0bdd1a"
34073407
"checksum chalk-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "295635afd6853aa9f20baeb7f0204862440c0fe994c5a253d5f479dac41d047e"
34083408
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
34093409
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"

Diff for: src/librustc/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ syntax_pos = { path = "../libsyntax_pos" }
3030
backtrace = "0.3.3"
3131
parking_lot = "0.6"
3232
byteorder = { version = "1.1", features = ["i128"]}
33-
chalk-engine = { version = "0.8.0", default-features=false }
33+
chalk-engine = { version = "0.9.0", default-features=false }
3434
rustc_fs_util = { path = "../librustc_fs_util" }
3535
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
3636

Diff for: src/librustc/dep_graph/dep_node.rs

+1
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ define_dep_nodes!( <'tcx>
648648
[] ImpliedOutlivesBounds(CanonicalTyGoal<'tcx>),
649649
[] DropckOutlives(CanonicalTyGoal<'tcx>),
650650
[] EvaluateObligation(CanonicalPredicateGoal<'tcx>),
651+
[] EvaluateGoal(traits::ChalkCanonicalGoal<'tcx>),
651652
[] TypeOpAscribeUserType(CanonicalTypeOpAscribeUserTypeGoal<'tcx>),
652653
[] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>),
653654
[] TypeOpSubtype(CanonicalTypeOpSubtypeGoal<'tcx>),

Diff for: src/librustc/ich/impls_ty.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,8 @@ for ty::steal::Steal<T>
885885

886886
impl_stable_hash_for!(struct ty::ParamEnv<'tcx> {
887887
caller_bounds,
888-
reveal
888+
reveal,
889+
def_id
889890
});
890891

891892
impl_stable_hash_for!(enum traits::Reveal {
@@ -1194,6 +1195,10 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::Goal<'tcx> {
11941195
quantifier.hash_stable(hcx, hasher);
11951196
goal.hash_stable(hcx, hasher);
11961197
},
1198+
Subtype(a, b) => {
1199+
a.hash_stable(hcx, hasher);
1200+
b.hash_stable(hcx, hasher);
1201+
}
11971202
CannotProve => { },
11981203
}
11991204
}
@@ -1239,3 +1244,10 @@ impl_stable_hash_for!(
12391244
clauses,
12401245
}
12411246
);
1247+
1248+
impl_stable_hash_for!(
1249+
impl<'tcx, G> for struct traits::InEnvironment<'tcx, G> {
1250+
environment,
1251+
goal,
1252+
}
1253+
);

Diff for: src/librustc/infer/canonical/canonicalizer.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,13 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx>
330330
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
331331
match t.sty {
332332
ty::Infer(ty::TyVar(vid)) => {
333+
debug!("canonical: type var found with vid {:?}", vid);
333334
match self.infcx.unwrap().probe_ty_var(vid) {
334335
// `t` could be a float / int variable: canonicalize that instead
335-
Ok(t) => self.fold_ty(t),
336+
Ok(t) => {
337+
debug!("(resolved to {:?})", t);
338+
self.fold_ty(t)
339+
}
336340

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

449453
// Fast path: nothing that needs to be canonicalized.
450454
if !value.has_type_flags(needs_canonical_flags) {
451-
let out_value = gcx.lift(value).unwrap();
455+
let out_value = gcx.lift(value).unwrap_or_else(|| {
456+
bug!(
457+
"failed to lift `{:?}` (nothing to canonicalize)",
458+
value
459+
)
460+
});
452461
let canon_value = Canonical {
453462
max_universe: ty::UniverseIndex::ROOT,
454463
variables: List::empty(),

Diff for: src/librustc/infer/canonical/mod.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,33 @@ BraceStructLiftImpl! {
420420
}
421421

422422
impl<'tcx> CanonicalVarValues<'tcx> {
423-
fn len(&self) -> usize {
423+
pub fn len(&self) -> usize {
424424
self.var_values.len()
425425
}
426+
427+
/// Make an identity substitution from this one: each bound var
428+
/// is matched to the same bound var, preserving the original kinds.
429+
/// For example, if we have:
430+
/// `self.var_values == [Type(u32), Lifetime('a), Type(u64)]`
431+
/// we'll return a substitution `subst` with:
432+
/// `subst.var_values == [Type(^0), Lifetime(^1), Type(^2)]`.
433+
pub fn make_identity<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
434+
use ty::subst::UnpackedKind;
435+
436+
CanonicalVarValues {
437+
var_values: self.var_values.iter()
438+
.zip(0..)
439+
.map(|(kind, i)| match kind.unpack() {
440+
UnpackedKind::Type(..) => tcx.mk_ty(
441+
ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i).into())
442+
).into(),
443+
UnpackedKind::Lifetime(..) => tcx.mk_region(
444+
ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(i))
445+
).into(),
446+
})
447+
.collect()
448+
}
449+
}
426450
}
427451

428452
impl<'a, 'tcx> IntoIterator for &'a CanonicalVarValues<'tcx> {

Diff for: src/librustc/infer/canonical/query_response.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_data_structures::sync::Lrc;
2121
use std::fmt::Debug;
2222
use syntax_pos::DUMMY_SP;
2323
use traits::query::{Fallible, NoSolution};
24-
use traits::{FulfillmentContext, TraitEngine};
24+
use traits::TraitEngine;
2525
use traits::{Obligation, ObligationCause, PredicateObligation};
2626
use ty::fold::TypeFoldable;
2727
use ty::subst::{Kind, UnpackedKind};
@@ -48,7 +48,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> {
4848
pub fn enter_canonical_trait_query<K, R>(
4949
&'tcx mut self,
5050
canonical_key: &Canonical<'tcx, K>,
51-
operation: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, &mut FulfillmentContext<'tcx>, K)
51+
operation: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, &mut dyn TraitEngine<'tcx>, K)
5252
-> Fallible<R>,
5353
) -> Fallible<CanonicalizedQueryResponse<'gcx, R>>
5454
where
@@ -59,9 +59,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> {
5959
DUMMY_SP,
6060
canonical_key,
6161
|ref infcx, key, canonical_inference_vars| {
62-
let fulfill_cx = &mut FulfillmentContext::new();
63-
let value = operation(infcx, fulfill_cx, key)?;
64-
infcx.make_canonicalized_query_response(canonical_inference_vars, value, fulfill_cx)
62+
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
63+
let value = operation(infcx, &mut *fulfill_cx, key)?;
64+
infcx.make_canonicalized_query_response(
65+
canonical_inference_vars,
66+
value,
67+
&mut *fulfill_cx
68+
)
6569
},
6670
)
6771
}
@@ -91,7 +95,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
9195
&self,
9296
inference_vars: CanonicalVarValues<'tcx>,
9397
answer: T,
94-
fulfill_cx: &mut FulfillmentContext<'tcx>,
98+
fulfill_cx: &mut dyn TraitEngine<'tcx>,
9599
) -> Fallible<CanonicalizedQueryResponse<'gcx, T>>
96100
where
97101
T: Debug + Lift<'gcx> + TypeFoldable<'tcx>,
@@ -138,7 +142,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
138142
&self,
139143
inference_vars: CanonicalVarValues<'tcx>,
140144
answer: T,
141-
fulfill_cx: &mut FulfillmentContext<'tcx>,
145+
fulfill_cx: &mut dyn TraitEngine<'tcx>,
142146
) -> Result<QueryResponse<'tcx, T>, NoSolution>
143147
where
144148
T: Debug + TypeFoldable<'tcx> + Lift<'gcx>,

Diff for: src/librustc/infer/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ impl<'tcx, T> InferOk<'tcx, T> {
581581
pub fn into_value_registering_obligations(
582582
self,
583583
infcx: &InferCtxt<'_, '_, 'tcx>,
584-
fulfill_cx: &mut impl TraitEngine<'tcx>,
584+
fulfill_cx: &mut dyn TraitEngine<'tcx>,
585585
) -> T {
586586
let InferOk { value, obligations } = self;
587587
for obligation in obligations {

Diff for: src/librustc/traits/auto_trait.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,17 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
388388
computed_preds.extend(user_computed_preds.iter().cloned());
389389
let normalized_preds =
390390
elaborate_predicates(tcx, computed_preds.clone().into_iter().collect());
391-
new_env = ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal);
391+
new_env = ty::ParamEnv::new(
392+
tcx.mk_predicates(normalized_preds),
393+
param_env.reveal,
394+
None
395+
);
392396
}
393397

394398
let final_user_env = ty::ParamEnv::new(
395399
tcx.mk_predicates(user_computed_preds.into_iter()),
396400
user_env.reveal,
401+
None
397402
);
398403
debug!(
399404
"evaluate_nested_obligations(ty_did={:?}, trait_did={:?}): succeeded with '{:?}' \

Diff for: src/librustc/traits/chalk_fulfill.rs

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
use traits::{
2+
Environment,
3+
InEnvironment,
4+
TraitEngine,
5+
ObligationCause,
6+
PredicateObligation,
7+
FulfillmentError,
8+
FulfillmentErrorCode,
9+
SelectionError,
10+
};
11+
use traits::query::NoSolution;
12+
use infer::InferCtxt;
13+
use infer::canonical::{Canonical, OriginalQueryValues};
14+
use ty::{self, Ty};
15+
use rustc_data_structures::fx::FxHashSet;
16+
17+
pub type CanonicalGoal<'tcx> = Canonical<'tcx, InEnvironment<'tcx, ty::Predicate<'tcx>>>;
18+
19+
pub struct FulfillmentContext<'tcx> {
20+
obligations: FxHashSet<InEnvironment<'tcx, PredicateObligation<'tcx>>>,
21+
}
22+
23+
impl FulfillmentContext<'tcx> {
24+
crate fn new() -> Self {
25+
FulfillmentContext {
26+
obligations: FxHashSet::default(),
27+
}
28+
}
29+
}
30+
31+
fn in_environment(
32+
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
33+
obligation: PredicateObligation<'tcx>
34+
) -> InEnvironment<'tcx, PredicateObligation<'tcx>> {
35+
assert!(!infcx.is_in_snapshot());
36+
let obligation = infcx.resolve_type_vars_if_possible(&obligation);
37+
38+
let environment = match obligation.param_env.def_id {
39+
Some(def_id) => infcx.tcx.environment(def_id),
40+
None if obligation.param_env.caller_bounds.is_empty() => Environment {
41+
clauses: ty::List::empty(),
42+
},
43+
_ => bug!("non-empty `ParamEnv` with no def-id"),
44+
};
45+
46+
InEnvironment {
47+
environment,
48+
goal: obligation,
49+
}
50+
}
51+
52+
impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
53+
fn normalize_projection_type(
54+
&mut self,
55+
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
56+
_param_env: ty::ParamEnv<'tcx>,
57+
projection_ty: ty::ProjectionTy<'tcx>,
58+
_cause: ObligationCause<'tcx>,
59+
) -> Ty<'tcx> {
60+
infcx.tcx.mk_ty(ty::Projection(projection_ty))
61+
}
62+
63+
fn register_predicate_obligation(
64+
&mut self,
65+
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
66+
obligation: PredicateObligation<'tcx>,
67+
) {
68+
self.obligations.insert(in_environment(infcx, obligation));
69+
}
70+
71+
fn select_all_or_error(
72+
&mut self,
73+
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
74+
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
75+
self.select_where_possible(infcx)?;
76+
77+
if self.obligations.is_empty() {
78+
Ok(())
79+
} else {
80+
let errors = self.obligations.iter()
81+
.map(|obligation| FulfillmentError {
82+
obligation: obligation.goal.clone(),
83+
code: FulfillmentErrorCode::CodeAmbiguity,
84+
})
85+
.collect();
86+
Err(errors)
87+
}
88+
}
89+
90+
fn select_where_possible(
91+
&mut self,
92+
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
93+
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
94+
let mut errors = Vec::new();
95+
let mut next_round = FxHashSet::default();
96+
let mut making_progress;
97+
98+
loop {
99+
making_progress = false;
100+
101+
// We iterate over all obligations, and record if we are able
102+
// to unambiguously prove at least one obligation.
103+
for obligation in self.obligations.drain() {
104+
let mut orig_values = OriginalQueryValues::default();
105+
let canonical_goal = infcx.canonicalize_query(&InEnvironment {
106+
environment: obligation.environment,
107+
goal: obligation.goal.predicate,
108+
}, &mut orig_values);
109+
110+
match infcx.tcx.global_tcx().evaluate_goal(canonical_goal) {
111+
Ok(response) => {
112+
if response.is_proven() {
113+
making_progress = true;
114+
115+
match infcx.instantiate_query_response_and_region_obligations(
116+
&obligation.goal.cause,
117+
obligation.goal.param_env,
118+
&orig_values,
119+
&response
120+
) {
121+
Ok(infer_ok) => next_round.extend(
122+
infer_ok.obligations
123+
.into_iter()
124+
.map(|obligation| in_environment(infcx, obligation))
125+
),
126+
127+
Err(_err) => errors.push(FulfillmentError {
128+
obligation: obligation.goal,
129+
code: FulfillmentErrorCode::CodeSelectionError(
130+
SelectionError::Unimplemented
131+
),
132+
}),
133+
}
134+
} else {
135+
// Ambiguous: retry at next round.
136+
next_round.insert(obligation);
137+
}
138+
}
139+
140+
Err(NoSolution) => errors.push(FulfillmentError {
141+
obligation: obligation.goal,
142+
code: FulfillmentErrorCode::CodeSelectionError(
143+
SelectionError::Unimplemented
144+
),
145+
})
146+
}
147+
}
148+
next_round = std::mem::replace(&mut self.obligations, next_round);
149+
150+
if !making_progress {
151+
break;
152+
}
153+
}
154+
155+
if errors.is_empty() {
156+
Ok(())
157+
} else {
158+
Err(errors)
159+
}
160+
}
161+
162+
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
163+
self.obligations.iter().map(|obligation| obligation.goal.clone()).collect()
164+
}
165+
}

0 commit comments

Comments
 (0)