Skip to content

Commit 01374c3

Browse files
committed
---
yaml --- r: 273141 b: refs/heads/beta c: 7e42a78 h: refs/heads/master i: 273139: c602c27
1 parent 071591e commit 01374c3

File tree

10 files changed

+297
-139
lines changed

10 files changed

+297
-139
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ refs/tags/0.9: 36870b185fc5f5486636d4515f0e22677493f225
2323
refs/tags/0.10: ac33f2b15782272ae348dbd7b14b8257b2148b5a
2424
refs/tags/0.11.0: e1247cb1d0d681be034adb4b558b5a0c0d5720f9
2525
refs/tags/0.12.0: f0c419429ef30723ceaf6b42f9b5a2aeb5d2e2d1
26-
refs/heads/beta: 957ee5ce347999ef2c1acfdb63a76643c61f6516
26+
refs/heads/beta: 7e42a780161e757ddd7d20925691a861f9d86725
2727
refs/tags/1.0.0-alpha: e42bd6d93a1d3433c486200587f8f9e12590a4d7
2828
refs/heads/tmp: e06d2ad9fcd5027bcaac5b08fc9aa39a49d0ecd3
2929
refs/tags/1.0.0-alpha.2: 4c705f6bc559886632d3871b04f58aab093bfa2f

branches/beta/src/librustc/middle/traits/coherence.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,7 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
8585
return None
8686
}
8787

88-
let substituted = selcx.infcx().resolve_type_vars_if_possible(&a_impl_header);
89-
let freshened = selcx.infcx().freshen(substituted);
90-
91-
Some(freshened)
88+
Some(selcx.infcx().resolve_type_vars_if_possible(&a_impl_header))
9289
}
9390

9491
pub fn trait_ref_is_knowable<'tcx>(tcx: &TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> bool

branches/beta/src/librustc/middle/traits/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub use self::select::SelectionContext;
5050
pub use self::select::SelectionCache;
5151
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
5252
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
53-
pub use self::specialize::{Overlap, SpecializationGraph, specializes};
53+
pub use self::specialize::{Overlap, SpecializationGraph, get_impl_item_or_default, ItemSource, specializes};
5454
pub use self::util::elaborate_predicates;
5555
pub use self::util::get_vtable_index_of_object_method;
5656
pub use self::util::trait_ref_for_builtin_bound;

branches/beta/src/librustc/middle/traits/select.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16291629
// i.e. EvaluatedToOk:
16301630
if other.evaluation == EvaluatedToOk {
16311631
if let ImplCandidate(victim_def) = victim.candidate {
1632-
return traits::specializes(self.infcx(), other_def, victim_def);
1632+
return traits::specializes(self.tcx(), other_def, victim_def);
16331633
}
16341634
}
16351635

branches/beta/src/librustc/middle/traits/specialize.rs

Lines changed: 221 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use middle::infer::{self, InferCtxt, TypeOrigin};
2323
use middle::region;
2424
use middle::subst::{Subst, Substs};
2525
use middle::traits;
26-
use middle::ty;
26+
use middle::ty::{self, ImplOrTraitItem};
2727
use syntax::codemap::DUMMY_SP;
2828
use util::nodemap::DefIdMap;
2929

@@ -51,6 +51,8 @@ pub struct SpecializationGraph {
5151
/// Information pertinent to an overlapping impl error.
5252
pub struct Overlap<'tcx> {
5353
pub with_impl: DefId,
54+
55+
/// NB: this TraitRef can contain inference variables!
5456
pub on_trait_ref: ty::TraitRef<'tcx>,
5557
}
5658

@@ -72,9 +74,7 @@ impl SpecializationGraph {
7274
-> Result<(), Overlap<'tcx>> {
7375
assert!(impl_def_id.is_local());
7476

75-
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false);
7677
let mut parent = trait_ref.def_id;
77-
7878
let mut my_children = vec![];
7979

8080
// descend the existing tree, looking for the right location to add this impl
@@ -84,13 +84,12 @@ impl SpecializationGraph {
8484
for slot in possible_siblings.iter_mut() {
8585
let possible_sibling = *slot;
8686

87-
let overlap = infcx.probe(|_| {
88-
traits::overlapping_impls(&infcx, possible_sibling, impl_def_id)
89-
});
87+
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false);
88+
let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id);
9089

9190
if let Some(trait_ref) = overlap {
92-
let le = specializes(&infcx, impl_def_id, possible_sibling);
93-
let ge = specializes(&infcx, possible_sibling, impl_def_id);
91+
let le = specializes(tcx, impl_def_id, possible_sibling);
92+
let ge = specializes(tcx, possible_sibling, impl_def_id);
9493

9594
if le && !ge {
9695
// the impl specializes possible_sibling
@@ -120,22 +119,184 @@ impl SpecializationGraph {
120119
}
121120

122121
if self.children.insert(impl_def_id, my_children).is_some() {
123-
panic!("When inserting an impl into the specialization graph, existing children for \
124-
the impl were already present.");
122+
tcx.sess
123+
.bug("When inserting an impl into the specialization graph, existing children for \
124+
the impl were already present.");
125125
}
126126

127127
Ok(())
128128
}
129129

130-
/// Insert cached metadata mapping from a child impl back to its parent
130+
/// Insert cached metadata mapping from a child impl back to its parent.
131131
pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) {
132-
if self.parent.insert(child, Some(parent)).is_some() {
132+
if self.parent.insert(child, parent).is_some() {
133133
panic!("When recording an impl from the crate store, information about its parent \
134134
was already present.");
135135
}
136136

137137
self.children.entry(parent).or_insert(vec![]).push(child);
138138
}
139+
140+
/// The parent of a given impl, which is the def id of the trait when the
141+
/// impl is a "specialization root".
142+
pub fn parent(&self, child: DefId) -> DefId {
143+
*self.parent.get(&child).unwrap()
144+
}
145+
}
146+
147+
/// When we have selected one impl, but are actually using item definitions from
148+
/// a parent impl providing a default, we need a way to translate between the
149+
/// type parameters of the two impls. Here the `source_impl` is the one we've
150+
/// selected, and `source_substs` is a substitution of its generics (and possibly
151+
/// some relevant `FnSpace` variables as well). And `target_impl` is the impl
152+
/// we're actually going to get the definition from.
153+
fn translate_substs_between_impls<'tcx>(tcx: &ty::ctxt<'tcx>,
154+
source_impl: DefId,
155+
source_substs: Substs<'tcx>,
156+
target_impl: DefId)
157+
-> Substs<'tcx> {
158+
159+
// We need to build a subst that covers all the generics of
160+
// `target_impl`. Start by introducing fresh infer variables:
161+
let target_generics = tcx.lookup_item_type(target_impl).generics;
162+
let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables);
163+
let mut target_substs = infcx.fresh_substs_for_generics(DUMMY_SP, &target_generics);
164+
if source_substs.regions.is_erased() {
165+
target_substs = target_substs.erase_regions()
166+
}
167+
168+
if !fulfill_implication(&mut infcx,
169+
source_impl,
170+
source_substs.clone(),
171+
target_impl,
172+
target_substs.clone()) {
173+
tcx.sess
174+
.bug("When translating substitutions for specialization, the expected specializaiton \
175+
failed to hold")
176+
}
177+
178+
// Now resolve the *substitution* we built for the target earlier, replacing
179+
// the inference variables inside with whatever we got from fulfillment. We
180+
// also carry along any FnSpace substitutions, which don't need to be
181+
// adjusted when mapping from one impl to another.
182+
infcx.resolve_type_vars_if_possible(&target_substs)
183+
.with_method_from_subst(&source_substs)
184+
}
185+
186+
/// When we've selected an impl but need to use an item definition provided by
187+
/// the trait itself, we need to translate the substitution applied to the impl
188+
/// to one that makes sense for the trait.
189+
fn translate_substs_from_impl_to_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
190+
source_impl: DefId,
191+
source_substs: Substs<'tcx>)
192+
-> Substs<'tcx> {
193+
194+
let source_trait_ref = tcx.impl_trait_ref(source_impl).unwrap().subst(tcx, &source_substs);
195+
196+
let mut new_substs = source_trait_ref.substs.clone();
197+
if source_substs.regions.is_erased() {
198+
new_substs = new_substs.erase_regions()
199+
}
200+
201+
// Carry any FnSpace substitutions along; they don't need to be adjusted
202+
new_substs.with_method_from_subst(&source_substs)
203+
}
204+
205+
#[derive(Debug, Copy, Clone)]
206+
/// When looking up an item in an impl, it may turn out that the item
207+
/// is actually provided as a default by a more generic impl, or by
208+
/// the trait itself. This enum says where the item came from.
209+
pub enum ItemSource {
210+
Impl {
211+
requested_impl: DefId,
212+
actual_impl: DefId,
213+
},
214+
Trait {
215+
requested_impl: DefId,
216+
},
217+
}
218+
219+
impl ItemSource {
220+
pub fn is_from_trait(&self) -> bool {
221+
match *self {
222+
ItemSource::Trait { .. } => true,
223+
_ => false,
224+
}
225+
}
226+
227+
/// Given a subst for the requested impl, translate it to a subst
228+
/// appropriate for the actual item definition (whether it be in that impl,
229+
/// a parent impl, or the trait).
230+
pub fn translate_substs<'tcx>(&self,
231+
tcx: &ty::ctxt<'tcx>,
232+
requested_impl_substs: Substs<'tcx>)
233+
-> Substs<'tcx> {
234+
match *self {
235+
ItemSource::Impl { requested_impl, actual_impl } => {
236+
// no need to translate if we're targetting the impl we started with
237+
if requested_impl == actual_impl {
238+
return requested_impl_substs;
239+
}
240+
241+
translate_substs_between_impls(tcx,
242+
requested_impl,
243+
requested_impl_substs,
244+
actual_impl)
245+
246+
}
247+
ItemSource::Trait { requested_impl } => {
248+
translate_substs_from_impl_to_trait(tcx, requested_impl, requested_impl_substs)
249+
}
250+
}
251+
}
252+
}
253+
254+
/// Lookup the definition of an item within `requested_impl` or its specialization
255+
/// parents, including provided items from the trait itself.
256+
///
257+
/// The closure `f` works in the style of `filter_map`.
258+
pub fn get_impl_item_or_default<'tcx, I, F>(tcx: &ty::ctxt<'tcx>,
259+
requested_impl: DefId,
260+
mut f: F)
261+
-> Option<(I, ItemSource)>
262+
where F: for<'a> FnMut(&ImplOrTraitItem<'tcx>) -> Option<I>
263+
{
264+
let impl_or_trait_items_map = tcx.impl_or_trait_items.borrow();
265+
let trait_def_id = tcx.trait_id_of_impl(requested_impl).unwrap();
266+
let trait_def = tcx.lookup_trait_def(trait_def_id);
267+
268+
// Walk up the specialization tree, looking for a matching item definition
269+
270+
let mut current_impl = requested_impl;
271+
loop {
272+
for impl_item_id in &tcx.impl_items.borrow()[&current_impl] {
273+
let impl_item = &impl_or_trait_items_map[&impl_item_id.def_id()];
274+
if let Some(t) = f(impl_item) {
275+
let source = ItemSource::Impl {
276+
requested_impl: requested_impl,
277+
actual_impl: current_impl,
278+
};
279+
return Some((t, source));
280+
}
281+
}
282+
283+
if let Some(parent) = trait_def.parent_of_impl(current_impl) {
284+
current_impl = parent;
285+
} else {
286+
break;
287+
}
288+
}
289+
290+
// The item isn't defined anywhere in the hierarchy. Get the
291+
// default from the trait.
292+
293+
for trait_item in tcx.trait_items(trait_def_id).iter() {
294+
if let Some(t) = f(trait_item) {
295+
return Some((t, ItemSource::Trait { requested_impl: requested_impl }));
296+
}
297+
}
298+
299+
None
139300
}
140301

141302
fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Substs<'a> {
@@ -155,30 +316,57 @@ fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Sub
155316
/// Specialization is determined by the sets of types to which the impls apply;
156317
/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies
157318
/// to.
158-
pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool {
159-
let tcx = &infcx.tcx;
160-
319+
pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool {
161320
// We determine whether there's a subset relationship by:
162321
//
163322
// - skolemizing impl1,
323+
// - instantiating impl2 with fresh inference variables,
164324
// - assuming the where clauses for impl1,
165325
// - unifying,
166326
// - attempting to prove the where clauses for impl2
167327
//
328+
// The last three steps are essentially checking for an implication between two impls
329+
// after appropriate substitutions. This is what `fulfill_implication` checks for.
330+
//
168331
// See RFC 1210 for more details and justification.
169332

333+
let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables);
334+
170335
let impl1_substs = skolemizing_subst_for_impl(tcx, impl1_def_id);
336+
let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id);
337+
338+
fulfill_implication(&mut infcx,
339+
impl1_def_id,
340+
impl1_substs,
341+
impl2_def_id,
342+
impl2_substs)
343+
}
344+
345+
/// Does impl1 (instantiated with the impl1_substs) imply impl2
346+
/// (instantiated with impl2_substs)?
347+
///
348+
/// Mutates the `infcx` in two ways:
349+
/// - by adding the obligations of impl1 to the parameter environment
350+
/// - via fulfillment, so that if the implication holds the various unifications
351+
fn fulfill_implication<'a, 'tcx>(infcx: &mut InferCtxt<'a, 'tcx>,
352+
impl1_def_id: DefId,
353+
impl1_substs: Substs<'tcx>,
354+
impl2_def_id: DefId,
355+
impl2_substs: Substs<'tcx>)
356+
-> bool {
357+
let tcx = &infcx.tcx;
358+
171359
let (impl1_trait_ref, impl1_obligations) = {
172360
let selcx = &mut SelectionContext::new(&infcx);
173361
util::impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs)
174362
};
175363

176364
let impl1_predicates: Vec<_> = impl1_obligations.iter()
177-
.cloned()
178-
.map(|oblig| oblig.predicate)
179-
.collect();
365+
.cloned()
366+
.map(|oblig| oblig.predicate)
367+
.collect();
180368

181-
let penv = ty::ParameterEnvironment {
369+
infcx.parameter_environment = ty::ParameterEnvironment {
182370
tcx: tcx,
183371
free_substs: impl1_substs,
184372
implicit_region_bound: ty::ReEmpty, // FIXME: is this OK?
@@ -188,21 +376,18 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId)
188376
free_id_outlive: region::DUMMY_CODE_EXTENT, // FIXME: is this OK?
189377
};
190378

191-
// FIXME: unclear what `errors_will_be_reported` should be here...
192-
let infcx = infer::new_infer_ctxt(tcx, infcx.tables, Some(penv), true);
193379
let selcx = &mut SelectionContext::new(&infcx);
194-
195-
let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id);
196-
let (impl2_trait_ref, impl2_obligations) =
197-
util::impl_trait_ref_and_oblig(selcx, impl2_def_id, &impl2_substs);
380+
let (impl2_trait_ref, impl2_obligations) = util::impl_trait_ref_and_oblig(selcx,
381+
impl2_def_id,
382+
&impl2_substs);
198383

199384
// do the impls unify? If not, no specialization.
200385
if let Err(_) = infer::mk_eq_trait_refs(&infcx,
201386
true,
202387
TypeOrigin::Misc(DUMMY_SP),
203388
impl1_trait_ref,
204389
impl2_trait_ref) {
205-
debug!("specializes: {:?} does not unify with {:?}",
390+
debug!("fulfill_implication: {:?} does not unify with {:?}",
206391
impl1_trait_ref,
207392
impl2_trait_ref);
208393
return false;
@@ -212,21 +397,24 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId)
212397

213398
// attempt to prove all of the predicates for impl2 given those for impl1
214399
// (which are packed up in penv)
400+
215401
for oblig in impl2_obligations.into_iter() {
216402
fulfill_cx.register_predicate_obligation(&infcx, oblig);
217403
}
218404

219405
if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) {
220-
debug!("specializes: for impls on {:?} and {:?}, could not fulfill: {:?} given {:?}",
406+
// no dice!
407+
debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
408+
{:?}",
221409
impl1_trait_ref,
222410
impl2_trait_ref,
223411
errors,
224412
infcx.parameter_environment.caller_bounds);
225-
return false;
413+
false
414+
} else {
415+
debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)",
416+
impl1_trait_ref,
417+
impl2_trait_ref);
418+
true
226419
}
227-
228-
debug!("specializes: an impl for {:?} specializes {:?} (`where` clauses elided)",
229-
impl1_trait_ref,
230-
impl2_trait_ref);
231-
true
232420
}

0 commit comments

Comments
 (0)