@@ -23,7 +23,7 @@ use middle::infer::{self, InferCtxt, TypeOrigin};
23
23
use middle:: region;
24
24
use middle:: subst:: { Subst , Substs } ;
25
25
use middle:: traits;
26
- use middle:: ty;
26
+ use middle:: ty:: { self , ImplOrTraitItem } ;
27
27
use syntax:: codemap:: DUMMY_SP ;
28
28
use util:: nodemap:: DefIdMap ;
29
29
@@ -51,6 +51,8 @@ pub struct SpecializationGraph {
51
51
/// Information pertinent to an overlapping impl error.
52
52
pub struct Overlap < ' tcx > {
53
53
pub with_impl : DefId ,
54
+
55
+ /// NB: this TraitRef can contain inference variables!
54
56
pub on_trait_ref : ty:: TraitRef < ' tcx > ,
55
57
}
56
58
@@ -72,9 +74,7 @@ impl SpecializationGraph {
72
74
-> Result < ( ) , Overlap < ' tcx > > {
73
75
assert ! ( impl_def_id. is_local( ) ) ;
74
76
75
- let infcx = infer:: new_infer_ctxt ( tcx, & tcx. tables , None , false ) ;
76
77
let mut parent = trait_ref. def_id ;
77
-
78
78
let mut my_children = vec ! [ ] ;
79
79
80
80
// descend the existing tree, looking for the right location to add this impl
@@ -84,13 +84,12 @@ impl SpecializationGraph {
84
84
for slot in possible_siblings. iter_mut ( ) {
85
85
let possible_sibling = * slot;
86
86
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) ;
90
89
91
90
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) ;
94
93
95
94
if le && !ge {
96
95
// the impl specializes possible_sibling
@@ -120,22 +119,184 @@ impl SpecializationGraph {
120
119
}
121
120
122
121
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.") ;
125
125
}
126
126
127
127
Ok ( ( ) )
128
128
}
129
129
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.
131
131
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 ( ) {
133
133
panic ! ( "When recording an impl from the crate store, information about its parent \
134
134
was already present.") ;
135
135
}
136
136
137
137
self . children . entry ( parent) . or_insert ( vec ! [ ] ) . push ( child) ;
138
138
}
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
139
300
}
140
301
141
302
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
155
316
/// Specialization is determined by the sets of types to which the impls apply;
156
317
/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies
157
318
/// 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 {
161
320
// We determine whether there's a subset relationship by:
162
321
//
163
322
// - skolemizing impl1,
323
+ // - instantiating impl2 with fresh inference variables,
164
324
// - assuming the where clauses for impl1,
165
325
// - unifying,
166
326
// - attempting to prove the where clauses for impl2
167
327
//
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
+ //
168
331
// See RFC 1210 for more details and justification.
169
332
333
+ let mut infcx = infer:: normalizing_infer_ctxt ( tcx, & tcx. tables ) ;
334
+
170
335
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
+
171
359
let ( impl1_trait_ref, impl1_obligations) = {
172
360
let selcx = & mut SelectionContext :: new ( & infcx) ;
173
361
util:: impl_trait_ref_and_oblig ( selcx, impl1_def_id, & impl1_substs)
174
362
} ;
175
363
176
364
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 ( ) ;
180
368
181
- let penv = ty:: ParameterEnvironment {
369
+ infcx . parameter_environment = ty:: ParameterEnvironment {
182
370
tcx : tcx,
183
371
free_substs : impl1_substs,
184
372
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)
188
376
free_id_outlive : region:: DUMMY_CODE_EXTENT , // FIXME: is this OK?
189
377
} ;
190
378
191
- // FIXME: unclear what `errors_will_be_reported` should be here...
192
- let infcx = infer:: new_infer_ctxt ( tcx, infcx. tables , Some ( penv) , true ) ;
193
379
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) ;
198
383
199
384
// do the impls unify? If not, no specialization.
200
385
if let Err ( _) = infer:: mk_eq_trait_refs ( & infcx,
201
386
true ,
202
387
TypeOrigin :: Misc ( DUMMY_SP ) ,
203
388
impl1_trait_ref,
204
389
impl2_trait_ref) {
205
- debug ! ( "specializes : {:?} does not unify with {:?}" ,
390
+ debug ! ( "fulfill_implication : {:?} does not unify with {:?}" ,
206
391
impl1_trait_ref,
207
392
impl2_trait_ref) ;
208
393
return false ;
@@ -212,21 +397,24 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId)
212
397
213
398
// attempt to prove all of the predicates for impl2 given those for impl1
214
399
// (which are packed up in penv)
400
+
215
401
for oblig in impl2_obligations. into_iter ( ) {
216
402
fulfill_cx. register_predicate_obligation ( & infcx, oblig) ;
217
403
}
218
404
219
405
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
+ {:?}",
221
409
impl1_trait_ref,
222
410
impl2_trait_ref,
223
411
errors,
224
412
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
226
419
}
227
-
228
- debug ! ( "specializes: an impl for {:?} specializes {:?} (`where` clauses elided)" ,
229
- impl1_trait_ref,
230
- impl2_trait_ref) ;
231
- true
232
420
}
0 commit comments