17
17
// See traits/README.md for a bit more detail on how specialization
18
18
// fits together with the rest of the trait machinery.
19
19
20
- use super :: { util, build_selcx, SelectionContext } ;
20
+ use super :: { build_selcx, SelectionContext , FulfillmentContext } ;
21
+ use super :: util:: { fresh_type_vars_for_impl, impl_trait_ref_and_oblig} ;
21
22
22
23
use middle:: cstore:: CrateStore ;
23
24
use middle:: def_id:: DefId ;
@@ -40,117 +41,90 @@ pub struct Overlap<'a, 'tcx: 'a> {
40
41
/// Given a subst for the requested impl, translate it to a subst
41
42
/// appropriate for the actual item definition (whether it be in that impl,
42
43
/// a parent impl, or the trait).
43
- pub fn translate_substs < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
44
- from_impl : DefId ,
45
- from_impl_substs : Substs < ' tcx > ,
46
- to_node : specialization_graph:: Node )
47
- -> Substs < ' tcx > {
48
- match to_node {
49
- specialization_graph:: Node :: Impl ( to_impl) => {
44
+ // When we have selected one impl, but are actually using item definitions from
45
+ // a parent impl providing a default, we need a way to translate between the
46
+ // type parameters of the two impls. Here the `source_impl` is the one we've
47
+ // selected, and `source_substs` is a substitution of its generics (and
48
+ // possibly some relevant `FnSpace` variables as well). And `target_node` is
49
+ // the impl/trait we're actually going to get the definition from. The resulting
50
+ // substitution will map from `target_node`'s generics to `source_impl`'s
51
+ // generics as instantiated by `source_subst`.
52
+ //
53
+ // For example, consider the following scenario:
54
+ //
55
+ // ```rust
56
+ // trait Foo { ... }
57
+ // impl<T, U> Foo for (T, U) { ... } // target impl
58
+ // impl<V> Foo for (V, V) { ... } // source impl
59
+ // ```
60
+ //
61
+ // Suppose we have selected "source impl" with `V` instantiated with `u32`.
62
+ // This function will produce a substitution with `T` and `U` both mapping to `u32`.
63
+ //
64
+ // Where clauses add some trickiness here, because they can be used to "define"
65
+ // an argument indirectly:
66
+ //
67
+ // ```rust
68
+ // impl<'a, I, T: 'a> Iterator for Cloned<I>
69
+ // where I: Iterator<Item=&'a T>, T: Clone
70
+ // ```
71
+ //
72
+ // In a case like this, the substitution for `T` is determined indirectly,
73
+ // through associated type projection. We deal with such cases by using
74
+ // *fulfillment* to relate the two impls, requiring that all projections are
75
+ // resolved.
76
+ pub fn translate_substs < ' a , ' tcx > ( infcx : & InferCtxt < ' a , ' tcx > ,
77
+ source_impl : DefId ,
78
+ source_substs : Substs < ' tcx > ,
79
+ target_node : specialization_graph:: Node )
80
+ -> Substs < ' tcx >
81
+ {
82
+ let source_trait_ref = infcx. tcx
83
+ . impl_trait_ref ( source_impl)
84
+ . unwrap ( )
85
+ . subst ( infcx. tcx , & source_substs) ;
86
+
87
+ // translate the Self and TyParam parts of the substitution, since those
88
+ // vary across impls
89
+ let target_substs = match target_node {
90
+ specialization_graph:: Node :: Impl ( target_impl) => {
50
91
// no need to translate if we're targetting the impl we started with
51
- if from_impl == to_impl {
52
- return from_impl_substs ;
92
+ if source_impl == target_impl {
93
+ return source_substs ;
53
94
}
54
95
55
- translate_substs_between_impls ( tcx, from_impl, from_impl_substs, to_impl)
56
-
57
- }
58
- specialization_graph:: Node :: Trait ( ..) => {
59
- translate_substs_from_impl_to_trait ( tcx, from_impl, from_impl_substs)
96
+ fulfill_implication ( infcx, source_trait_ref, target_impl) . unwrap_or_else ( |_| {
97
+ infcx. tcx
98
+ . sess
99
+ . bug ( "When translating substitutions for specialization, the expected \
100
+ specializaiton failed to hold")
101
+ } )
60
102
}
61
- }
62
- }
63
-
64
- /// When we have selected one impl, but are actually using item definitions from
65
- /// a parent impl providing a default, we need a way to translate between the
66
- /// type parameters of the two impls. Here the `source_impl` is the one we've
67
- /// selected, and `source_substs` is a substitution of its generics (and
68
- /// possibly some relevant `FnSpace` variables as well). And `target_impl` is
69
- /// the impl we're actually going to get the definition from. The resulting
70
- /// substitution will map from `target_impl`'s generics to `source_impl`'s
71
- /// generics as instantiated by `source_subst`.
72
- ///
73
- /// For example, consider the following scenario:
74
- ///
75
- /// ```rust
76
- /// trait Foo { ... }
77
- /// impl<T, U> Foo for (T, U) { ... } // target impl
78
- /// impl<V> Foo for (V, V) { ... } // source impl
79
- /// ```
80
- ///
81
- /// Suppose we have selected "source impl" with `V` instantiated with `u32`.
82
- /// This function will produce a substitution with `T` and `U` both mapping to `u32`.
83
- ///
84
- /// Where clauses add some trickiness here, because they can be used to "define"
85
- /// an argument indirectly:
86
- ///
87
- /// ```rust
88
- /// impl<'a, I, T: 'a> Iterator for Cloned<I>
89
- /// where I: Iterator<Item=&'a T>, T: Clone
90
- /// ```
91
- ///
92
- /// In a case like this, the substitution for `T` is determined indirectly,
93
- /// through associated type projection. We deal with such cases by using
94
- /// *fulfillment* to relate the two impls, requiring that all projections are
95
- /// resolved.
96
- fn translate_substs_between_impls < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
97
- source_impl : DefId ,
98
- source_substs : Substs < ' tcx > ,
99
- target_impl : DefId )
100
- -> Substs < ' tcx > {
101
-
102
- // We need to build a subst that covers all the generics of
103
- // `target_impl`. Start by introducing fresh infer variables:
104
- let target_generics = tcx. lookup_item_type ( target_impl) . generics ;
105
- let mut infcx = infer:: normalizing_infer_ctxt ( tcx, & tcx. tables ) ;
106
- let mut target_substs = infcx. fresh_substs_for_generics ( DUMMY_SP , & target_generics) ;
107
- if source_substs. regions . is_erased ( ) {
108
- target_substs = target_substs. erase_regions ( )
109
- }
103
+ specialization_graph:: Node :: Trait ( ..) => source_trait_ref. substs . clone ( ) ,
104
+ } ;
110
105
111
- if !fulfill_implication ( & mut infcx,
112
- source_impl,
113
- source_substs. clone ( ) ,
114
- target_impl,
115
- target_substs. clone ( ) ) {
116
- tcx. sess
117
- . bug ( "When translating substitutions for specialization, the expected specializaiton \
118
- failed to hold")
119
- }
106
+ // retain erasure mode
107
+ // NB: this must happen before inheriting method generics below
108
+ let target_substs = if source_substs. regions . is_erased ( ) {
109
+ target_substs. erase_regions ( )
110
+ } else {
111
+ target_substs
112
+ } ;
120
113
121
- // Now resolve the *substitution* we built for the target earlier, replacing
122
- // the inference variables inside with whatever we got from fulfillment. We
123
- // also carry along any FnSpace substitutions, which don't need to be
124
- // adjusted when mapping from one impl to another.
125
- infcx. resolve_type_vars_if_possible ( & target_substs)
126
- . with_method_from_subst ( & source_substs)
114
+ // directly inherent the method generics, since those do not vary across impls
115
+ target_substs. with_method_from_subst ( & source_substs)
127
116
}
128
117
129
- /// When we've selected an impl but need to use an item definition provided by
130
- /// the trait itself, we need to translate the substitution applied to the impl
131
- /// to one that makes sense for the trait.
132
- fn translate_substs_from_impl_to_trait < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
133
- source_impl : DefId ,
134
- source_substs : Substs < ' tcx > )
135
- -> Substs < ' tcx > {
136
118
137
- let source_trait_ref = tcx. impl_trait_ref ( source_impl) . unwrap ( ) . subst ( tcx, & source_substs) ;
119
+ fn skolemizing_subst_for_impl < ' a , ' tcx > ( infcx : & InferCtxt < ' a , ' tcx > ,
120
+ impl_def_id : DefId )
121
+ -> Substs < ' tcx >
122
+ {
123
+ let impl_generics = infcx. tcx . lookup_item_type ( impl_def_id) . generics ;
138
124
139
- let mut new_substs = source_trait_ref. substs . clone ( ) ;
140
- if source_substs. regions . is_erased ( ) {
141
- new_substs = new_substs. erase_regions ( )
142
- }
143
-
144
- // Carry any FnSpace substitutions along; they don't need to be adjusted
145
- new_substs. with_method_from_subst ( & source_substs)
146
- }
125
+ let types = impl_generics. types . map ( |def| infcx. tcx . mk_param_from_def ( def) ) ;
147
126
148
- fn skolemizing_subst_for_impl < ' a > ( tcx : & ty:: ctxt < ' a > , impl_def_id : DefId ) -> Substs < ' a > {
149
- let impl_generics = tcx. lookup_item_type ( impl_def_id) . generics ;
150
-
151
- let types = impl_generics. types . map ( |def| tcx. mk_param_from_def ( def) ) ;
152
-
153
- // FIXME: figure out what we actually want here
127
+ // TODO: figure out what we actually want here
154
128
let regions = impl_generics. regions . map ( |_| ty:: Region :: ReStatic ) ;
155
129
// |d| infcx.next_region_var(infer::RegionVariableOrigin::EarlyBoundRegion(span, d.name)));
156
130
@@ -170,101 +144,101 @@ pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) ->
170
144
// We determine whether there's a subset relationship by:
171
145
//
172
146
// - skolemizing impl1,
173
- // - instantiating impl2 with fresh inference variables,
174
147
// - assuming the where clauses for impl1,
148
+ // - instantiating impl2 with fresh inference variables,
175
149
// - unifying,
176
150
// - attempting to prove the where clauses for impl2
177
151
//
178
- // The last three steps are essentially checking for an implication between two impls
179
- // after appropriate substitutions. This is what `fulfill_implication` checks for.
152
+ // The last three steps are encapsulated in `fulfill_implication`.
180
153
//
181
154
// See RFC 1210 for more details and justification.
182
155
183
156
let mut infcx = infer:: normalizing_infer_ctxt ( tcx, & tcx. tables ) ;
184
157
185
- let impl1_substs = skolemizing_subst_for_impl ( tcx, impl1_def_id) ;
186
- let impl2_substs = util:: fresh_type_vars_for_impl ( & infcx, DUMMY_SP , impl2_def_id) ;
187
-
188
- fulfill_implication ( & mut infcx,
189
- impl1_def_id,
190
- impl1_substs,
191
- impl2_def_id,
192
- impl2_substs)
193
- }
194
-
195
- /// Does impl1 (instantiated with the impl1_substs) imply impl2
196
- /// (instantiated with impl2_substs)?
197
- ///
198
- /// Mutates the `infcx` in two ways:
199
- /// - by adding the obligations of impl1 to the parameter environment
200
- /// - via fulfillment, so that if the implication holds the various unifications
201
- fn fulfill_implication < ' a , ' tcx > ( infcx : & mut InferCtxt < ' a , ' tcx > ,
202
- impl1_def_id : DefId ,
203
- impl1_substs : Substs < ' tcx > ,
204
- impl2_def_id : DefId ,
205
- impl2_substs : Substs < ' tcx > )
206
- -> bool {
207
- let tcx = & infcx. tcx ;
208
-
158
+ // Skiolemize impl1: we want to prove that "for all types matched by impl1,
159
+ // those types are also matched by impl2".
160
+ let impl1_substs = skolemizing_subst_for_impl ( & infcx, impl1_def_id) ;
209
161
let ( impl1_trait_ref, impl1_obligations) = {
210
162
let selcx = & mut SelectionContext :: new ( & infcx) ;
211
- util :: impl_trait_ref_and_oblig ( selcx, impl1_def_id, & impl1_substs)
163
+ impl_trait_ref_and_oblig ( selcx, impl1_def_id, & impl1_substs)
212
164
} ;
213
165
166
+ // Add impl1's obligations as assumptions to the environment.
214
167
let impl1_predicates: Vec < _ > = impl1_obligations. iter ( )
215
168
. cloned ( )
216
169
. map ( |oblig| oblig. predicate )
217
170
. collect ( ) ;
218
-
219
171
infcx. parameter_environment = ty:: ParameterEnvironment {
220
172
tcx : tcx,
221
173
free_substs : impl1_substs,
222
- implicit_region_bound : ty:: ReEmpty , // FIXME : is this OK?
174
+ implicit_region_bound : ty:: ReEmpty , // TODO : is this OK?
223
175
caller_bounds : impl1_predicates,
224
176
selection_cache : traits:: SelectionCache :: new ( ) ,
225
177
evaluation_cache : traits:: EvaluationCache :: new ( ) ,
226
- free_id_outlive : region:: DUMMY_CODE_EXTENT , // FIXME : is this OK?
178
+ free_id_outlive : region:: DUMMY_CODE_EXTENT , // TODO : is this OK?
227
179
} ;
228
180
229
- let selcx = & mut build_selcx ( & infcx) . project_topmost ( ) . build ( ) ;
230
- let ( impl2_trait_ref, impl2_obligations) = util:: impl_trait_ref_and_oblig ( selcx,
231
- impl2_def_id,
232
- & impl2_substs) ;
233
-
234
- // do the impls unify? If not, no specialization.
235
- if let Err ( _) = infer:: mk_eq_trait_refs ( & infcx,
236
- true ,
237
- TypeOrigin :: Misc ( DUMMY_SP ) ,
238
- impl1_trait_ref,
239
- impl2_trait_ref) {
240
- debug ! ( "fulfill_implication: {:?} does not unify with {:?}" ,
241
- impl1_trait_ref,
242
- impl2_trait_ref) ;
243
- return false ;
244
- }
181
+ // Attempt to prove that impl2 applies, given all of the above.
182
+ fulfill_implication ( & infcx, impl1_trait_ref, impl2_def_id) . is_ok ( )
183
+ }
245
184
246
- let mut fulfill_cx = infcx. fulfillment_cx . borrow_mut ( ) ;
185
+ /// Attempt to fulfill all obligations of `target_impl` after unification with
186
+ /// `source_trait_ref`. If successful, returns a substitution for *all* the
187
+ /// generics of `target_impl`, including both those needed to unify with
188
+ /// `source_trait_ref` and those whose identity is determined via a where
189
+ /// clause in the impl.
190
+ fn fulfill_implication < ' a , ' tcx > ( infcx : & InferCtxt < ' a , ' tcx > ,
191
+ source_trait_ref : ty:: TraitRef < ' tcx > ,
192
+ target_impl : DefId )
193
+ -> Result < Substs < ' tcx > , ( ) >
194
+ {
195
+ infcx. probe ( |_| {
196
+ let selcx = & mut build_selcx ( & infcx) . project_topmost ( ) . build ( ) ;
197
+ let target_substs = fresh_type_vars_for_impl ( & infcx, DUMMY_SP , target_impl) ;
198
+ let ( target_trait_ref, obligations) = impl_trait_ref_and_oblig ( selcx,
199
+ target_impl,
200
+ & target_substs) ;
201
+
202
+ // do the impls unify? If not, no specialization.
203
+ if let Err ( _) = infer:: mk_eq_trait_refs ( & infcx,
204
+ true ,
205
+ TypeOrigin :: Misc ( DUMMY_SP ) ,
206
+ source_trait_ref,
207
+ target_trait_ref) {
208
+ debug ! ( "fulfill_implication: {:?} does not unify with {:?}" ,
209
+ source_trait_ref,
210
+ target_trait_ref) ;
211
+ return Err ( ( ) ) ;
212
+ }
247
213
248
- // attempt to prove all of the predicates for impl2 given those for impl1
249
- // (which are packed up in penv)
214
+ // attempt to prove all of the predicates for impl2 given those for impl1
215
+ // (which are packed up in penv)
250
216
251
- for oblig in impl2_obligations. into_iter ( ) {
252
- fulfill_cx. register_predicate_obligation ( & infcx, oblig) ;
253
- }
217
+ let mut fulfill_cx = FulfillmentContext :: new ( ) ;
218
+ for oblig in obligations. into_iter ( ) {
219
+ fulfill_cx. register_predicate_obligation ( & infcx, oblig) ;
220
+ }
254
221
255
- if let Err ( errors) = infer:: drain_fulfillment_cx ( & infcx, & mut fulfill_cx, & ( ) ) {
256
- // no dice!
257
- debug ! ( "fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
258
- {:?}",
259
- impl1_trait_ref,
260
- impl2_trait_ref,
261
- errors,
262
- infcx. parameter_environment. caller_bounds) ;
263
- false
264
- } else {
265
- debug ! ( "fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)" ,
266
- impl1_trait_ref,
267
- impl2_trait_ref) ;
268
- true
269
- }
222
+ if let Err ( errors) = infer:: drain_fulfillment_cx ( & infcx, & mut fulfill_cx, & ( ) ) {
223
+ // no dice!
224
+ debug ! ( "fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} \
225
+ given {:?}",
226
+ source_trait_ref,
227
+ target_trait_ref,
228
+ errors,
229
+ infcx. parameter_environment. caller_bounds) ;
230
+ Err ( ( ) )
231
+ } else {
232
+ debug ! ( "fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses \
233
+ elided)",
234
+ source_trait_ref,
235
+ target_trait_ref) ;
236
+
237
+ // Now resolve the *substitution* we built for the target earlier, replacing
238
+ // the inference variables inside with whatever we got from fulfillment.
239
+
240
+ // TODO: should this use `fully_resolve` instead?
241
+ Ok ( infcx. resolve_type_vars_if_possible ( & target_substs) )
242
+ }
243
+ } )
270
244
}
0 commit comments