9
9
// except according to those terms.
10
10
11
11
use check:: { FnCtxt , structurally_resolved_type} ;
12
- use middle:: subst:: { FnSpace } ;
12
+ use middle:: subst:: { FnSpace , SelfSpace } ;
13
13
use middle:: traits;
14
14
use middle:: traits:: { Obligation , ObligationCause } ;
15
15
use middle:: traits:: report_fulfillment_errors;
@@ -141,15 +141,15 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
141
141
}
142
142
143
143
fn check_object_safety_inner < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
144
- object_trait : & ty:: PolyTraitRef < ' tcx > ,
145
- span : Span ) {
144
+ object_trait : & ty:: PolyTraitRef < ' tcx > ,
145
+ span : Span ) {
146
146
let trait_items = ty:: trait_items ( tcx, object_trait. def_id ( ) ) ;
147
147
148
148
let mut errors = Vec :: new ( ) ;
149
149
for item in trait_items. iter ( ) {
150
150
match * item {
151
151
ty:: MethodTraitItem ( ref m) => {
152
- errors. push ( check_object_safety_of_method ( tcx, & * * m) )
152
+ errors. push ( check_object_safety_of_method ( tcx, object_trait , & * * m) )
153
153
}
154
154
ty:: TypeTraitItem ( _) => { }
155
155
}
@@ -173,6 +173,7 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
173
173
/// type is not known (that's the whole point of a trait instance, after all, to obscure the
174
174
/// self type) and (b) the call must go through a vtable and hence cannot be monomorphized.
175
175
fn check_object_safety_of_method < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
176
+ object_trait : & ty:: PolyTraitRef < ' tcx > ,
176
177
method : & ty:: Method < ' tcx > )
177
178
-> Vec < String > {
178
179
let mut msgs = Vec :: new ( ) ;
@@ -196,7 +197,7 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
196
197
197
198
// reason (a) above
198
199
let check_for_self_ty = |ty| {
199
- if ty :: type_has_self ( ty) {
200
+ if contains_illegal_self_type_reference ( tcx , object_trait . def_id ( ) , ty) {
200
201
Some ( format ! (
201
202
"cannot call a method (`{}`) whose type contains \
202
203
a self-type (`{}`) through a trait object",
@@ -225,6 +226,98 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
225
226
226
227
msgs
227
228
}
229
+
230
+ fn contains_illegal_self_type_reference < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
231
+ trait_def_id : ast:: DefId ,
232
+ ty : Ty < ' tcx > )
233
+ -> bool
234
+ {
235
+ // This is somewhat subtle. In general, we want to forbid
236
+ // references to `Self` in the argument and return types,
237
+ // since the value of `Self` is erased. However, there is one
238
+ // exception: it is ok to reference `Self` in order to access
239
+ // an associated type of the current trait, since we retain
240
+ // the value of those associated types in the object type
241
+ // itself.
242
+ //
243
+ // ```rust
244
+ // trait SuperTrait {
245
+ // type X;
246
+ // }
247
+ //
248
+ // trait Trait : SuperTrait {
249
+ // type Y;
250
+ // fn foo(&self, x: Self) // bad
251
+ // fn foo(&self) -> Self // bad
252
+ // fn foo(&self) -> Option<Self> // bad
253
+ // fn foo(&self) -> Self::Y // OK, desugars to next example
254
+ // fn foo(&self) -> <Self as Trait>::Y // OK
255
+ // fn foo(&self) -> Self::X // OK, desugars to next example
256
+ // fn foo(&self) -> <Self as SuperTrait>::X // OK
257
+ // }
258
+ // ```
259
+ //
260
+ // However, it is not as simple as allowing `Self` in a projected
261
+ // type, because there are illegal ways to use `Self` as well:
262
+ //
263
+ // ```rust
264
+ // trait Trait : SuperTrait {
265
+ // ...
266
+ // fn foo(&self) -> <Self as SomeOtherTrait>::X;
267
+ // }
268
+ // ```
269
+ //
270
+ // Here we will not have the type of `X` recorded in the
271
+ // object type, and we cannot resolve `Self as SomeOtherTrait`
272
+ // without knowing what `Self` is.
273
+
274
+ let mut supertraits: Option < Vec < ty:: PolyTraitRef < ' tcx > > > = None ;
275
+ let mut error = false ;
276
+ ty:: maybe_walk_ty ( ty, |ty| {
277
+ match ty. sty {
278
+ ty:: ty_param( ref param_ty) => {
279
+ if param_ty. space == SelfSpace {
280
+ error = true ;
281
+ }
282
+
283
+ false // no contained types to walk
284
+ }
285
+
286
+ ty:: ty_projection( ref data) => {
287
+ // This is a projected type `<Foo as SomeTrait>::X`.
288
+
289
+ // Compute supertraits of current trait lazilly.
290
+ if supertraits. is_none ( ) {
291
+ let trait_def = ty:: lookup_trait_def ( tcx, trait_def_id) ;
292
+ let trait_ref = ty:: Binder ( trait_def. trait_ref . clone ( ) ) ;
293
+ supertraits = Some ( traits:: supertraits ( tcx, trait_ref) . collect ( ) ) ;
294
+ }
295
+
296
+ // Determine whether the trait reference `Foo as
297
+ // SomeTrait` is in fact a supertrait of the
298
+ // current trait. In that case, this type is
299
+ // legal, because the type `X` will be specified
300
+ // in the object type. Note that we can just use
301
+ // direct equality here because all of these types
302
+ // are part of the formal parameter listing, and
303
+ // hence there should be no inference variables.
304
+ let projection_trait_ref = ty:: Binder ( data. trait_ref . clone ( ) ) ;
305
+ let is_supertrait_of_current_trait =
306
+ supertraits. as_ref ( ) . unwrap ( ) . contains ( & projection_trait_ref) ;
307
+
308
+ if is_supertrait_of_current_trait {
309
+ false // do not walk contained types, do not report error, do collect $200
310
+ } else {
311
+ true // DO walk contained types, POSSIBLY reporting an error
312
+ }
313
+ }
314
+
315
+ _ => true , // walk contained types, if any
316
+ }
317
+ } ) ;
318
+
319
+ error
320
+ }
228
321
}
229
322
230
323
pub fn register_object_cast_obligations < ' a , ' tcx > ( fcx : & FnCtxt < ' a , ' tcx > ,
0 commit comments