]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! "Object safety" refers to the ability for a trait to be converted | |
12 | //! to an object. In general, traits may only be converted to an | |
13 | //! object if all of their methods meet certain criteria. In particular, | |
14 | //! they must: | |
15 | //! | |
16 | //! - have a suitable receiver from which we can extract a vtable; | |
17 | //! - not reference the erased type `Self` except for in this receiver; | |
18 | //! - not have generic type parameters | |
19 | ||
1a4d82fc JJ |
20 | use super::elaborate_predicates; |
21 | ||
54a0048b | 22 | use hir::def_id::DefId; |
54a0048b | 23 | use traits; |
476ff2be SL |
24 | use ty::{self, Ty, TyCtxt, TypeFoldable}; |
25 | use ty::subst::Substs; | |
abe05a73 | 26 | use ty::util::ExplicitSelf; |
8bb4bdeb | 27 | use std::borrow::Cow; |
1a4d82fc | 28 | use syntax::ast; |
1a4d82fc | 29 | |
e9174d1e | 30 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] |
476ff2be | 31 | pub enum ObjectSafetyViolation { |
1a4d82fc JJ |
32 | /// Self : Sized declared on the trait |
33 | SizedSelf, | |
34 | ||
85aaf69f SL |
35 | /// Supertrait reference references `Self` an in illegal location |
36 | /// (e.g. `trait Foo : Bar<Self>`) | |
37 | SupertraitSelf, | |
38 | ||
1a4d82fc | 39 | /// Method has something illegal |
476ff2be | 40 | Method(ast::Name, MethodViolationCode), |
cc61c64b XL |
41 | |
42 | /// Associated const | |
43 | AssociatedConst(ast::Name), | |
1a4d82fc JJ |
44 | } |
45 | ||
8bb4bdeb XL |
46 | impl ObjectSafetyViolation { |
47 | pub fn error_msg(&self) -> Cow<'static, str> { | |
48 | match *self { | |
49 | ObjectSafetyViolation::SizedSelf => | |
50 | "the trait cannot require that `Self : Sized`".into(), | |
51 | ObjectSafetyViolation::SupertraitSelf => | |
52 | "the trait cannot use `Self` as a type parameter \ | |
cc61c64b | 53 | in the supertraits or where-clauses".into(), |
8bb4bdeb XL |
54 | ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod) => |
55 | format!("method `{}` has no receiver", name).into(), | |
56 | ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelf) => | |
57 | format!("method `{}` references the `Self` type \ | |
58 | in its arguments or return type", name).into(), | |
59 | ObjectSafetyViolation::Method(name, MethodViolationCode::Generic) => | |
60 | format!("method `{}` has generic type parameters", name).into(), | |
abe05a73 | 61 | ObjectSafetyViolation::Method(name, MethodViolationCode::NonStandardSelfType) => |
ff7c6d11 | 62 | format!("method `{}` has a non-standard `self` type", name).into(), |
cc61c64b XL |
63 | ObjectSafetyViolation::AssociatedConst(name) => |
64 | format!("the trait cannot contain associated consts like `{}`", name).into(), | |
8bb4bdeb XL |
65 | } |
66 | } | |
67 | } | |
68 | ||
1a4d82fc | 69 | /// Reasons a method might not be object-safe. |
e9174d1e | 70 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] |
1a4d82fc | 71 | pub enum MethodViolationCode { |
1a4d82fc JJ |
72 | /// e.g., `fn foo()` |
73 | StaticMethod, | |
74 | ||
75 | /// e.g., `fn foo(&self, x: Self)` or `fn foo(&self) -> Self` | |
76 | ReferencesSelf, | |
77 | ||
78 | /// e.g., `fn foo<A>()` | |
79 | Generic, | |
abe05a73 XL |
80 | |
81 | /// arbitrary `self` type, e.g. `self: Rc<Self>` | |
82 | NonStandardSelfType, | |
1a4d82fc JJ |
83 | } |
84 | ||
a7813a04 | 85 | impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { |
b039eaaf | 86 | |
a7813a04 XL |
87 | /// Returns the object safety violations that affect |
88 | /// astconv - currently, Self in supertraits. This is needed | |
89 | /// because `object_safety_violations` can't be used during | |
90 | /// type collection. | |
91 | pub fn astconv_object_safety_violations(self, trait_def_id: DefId) | |
476ff2be | 92 | -> Vec<ObjectSafetyViolation> |
a7813a04 XL |
93 | { |
94 | let mut violations = vec![]; | |
b039eaaf | 95 | |
32a655c1 SL |
96 | for def_id in traits::supertrait_def_ids(self, trait_def_id) { |
97 | if self.predicates_reference_self(def_id, true) { | |
98 | violations.push(ObjectSafetyViolation::SupertraitSelf); | |
99 | } | |
a7813a04 | 100 | } |
1a4d82fc | 101 | |
a7813a04 XL |
102 | debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", |
103 | trait_def_id, | |
104 | violations); | |
1a4d82fc | 105 | |
a7813a04 | 106 | violations |
1a4d82fc | 107 | } |
a7813a04 XL |
108 | |
109 | pub fn object_safety_violations(self, trait_def_id: DefId) | |
476ff2be | 110 | -> Vec<ObjectSafetyViolation> |
a7813a04 XL |
111 | { |
112 | traits::supertrait_def_ids(self, trait_def_id) | |
113 | .flat_map(|def_id| self.object_safety_violations_for_trait(def_id)) | |
114 | .collect() | |
85aaf69f | 115 | } |
1a4d82fc | 116 | |
a7813a04 | 117 | fn object_safety_violations_for_trait(self, trait_def_id: DefId) |
476ff2be | 118 | -> Vec<ObjectSafetyViolation> |
a7813a04 XL |
119 | { |
120 | // Check methods for violations. | |
476ff2be SL |
121 | let mut violations: Vec<_> = self.associated_items(trait_def_id) |
122 | .filter(|item| item.kind == ty::AssociatedKind::Method) | |
a7813a04 | 123 | .filter_map(|item| { |
476ff2be SL |
124 | self.object_safety_violation_for_method(trait_def_id, &item) |
125 | .map(|code| ObjectSafetyViolation::Method(item.name, code)) | |
126 | }).collect(); | |
1a4d82fc | 127 | |
a7813a04 XL |
128 | // Check the trait itself. |
129 | if self.trait_has_sized_self(trait_def_id) { | |
130 | violations.push(ObjectSafetyViolation::SizedSelf); | |
131 | } | |
32a655c1 | 132 | if self.predicates_reference_self(trait_def_id, false) { |
a7813a04 XL |
133 | violations.push(ObjectSafetyViolation::SupertraitSelf); |
134 | } | |
1a4d82fc | 135 | |
cc61c64b XL |
136 | violations.extend(self.associated_items(trait_def_id) |
137 | .filter(|item| item.kind == ty::AssociatedKind::Const) | |
138 | .map(|item| ObjectSafetyViolation::AssociatedConst(item.name))); | |
139 | ||
a7813a04 XL |
140 | debug!("object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", |
141 | trait_def_id, | |
142 | violations); | |
85aaf69f | 143 | |
a7813a04 XL |
144 | violations |
145 | } | |
c34b1796 | 146 | |
32a655c1 SL |
147 | fn predicates_reference_self( |
148 | self, | |
149 | trait_def_id: DefId, | |
150 | supertraits_only: bool) -> bool | |
151 | { | |
83c7162d | 152 | let trait_ref = ty::Binder::dummy(ty::TraitRef { |
476ff2be SL |
153 | def_id: trait_def_id, |
154 | substs: Substs::identity_for_item(self, trait_def_id) | |
155 | }); | |
32a655c1 | 156 | let predicates = if supertraits_only { |
7cac9316 | 157 | self.super_predicates_of(trait_def_id) |
32a655c1 | 158 | } else { |
7cac9316 | 159 | self.predicates_of(trait_def_id) |
32a655c1 | 160 | }; |
a7813a04 XL |
161 | predicates |
162 | .predicates | |
163 | .into_iter() | |
164 | .map(|predicate| predicate.subst_supertrait(self, &trait_ref)) | |
165 | .any(|predicate| { | |
166 | match predicate { | |
167 | ty::Predicate::Trait(ref data) => { | |
168 | // In the case of a trait predicate, we can skip the "self" type. | |
9e0c209e | 169 | data.skip_binder().input_types().skip(1).any(|t| t.has_self_ty()) |
a7813a04 XL |
170 | } |
171 | ty::Predicate::Projection(..) | | |
172 | ty::Predicate::WellFormed(..) | | |
173 | ty::Predicate::ObjectSafe(..) | | |
174 | ty::Predicate::TypeOutlives(..) | | |
175 | ty::Predicate::RegionOutlives(..) | | |
176 | ty::Predicate::ClosureKind(..) | | |
cc61c64b | 177 | ty::Predicate::Subtype(..) | |
ea8adc8c | 178 | ty::Predicate::ConstEvaluatable(..) => { |
a7813a04 XL |
179 | false |
180 | } | |
1a4d82fc | 181 | } |
a7813a04 | 182 | }) |
c34b1796 AL |
183 | } |
184 | ||
a7813a04 | 185 | fn trait_has_sized_self(self, trait_def_id: DefId) -> bool { |
9e0c209e | 186 | self.generics_require_sized_self(trait_def_id) |
a7813a04 | 187 | } |
1a4d82fc | 188 | |
9e0c209e | 189 | fn generics_require_sized_self(self, def_id: DefId) -> bool { |
ea8adc8c | 190 | let sized_def_id = match self.lang_items().sized_trait() { |
a7813a04 XL |
191 | Some(def_id) => def_id, |
192 | None => { return false; /* No Sized trait, can't require it! */ } | |
193 | }; | |
194 | ||
195 | // Search for a predicate like `Self : Sized` amongst the trait bounds. | |
7cac9316 XL |
196 | let predicates = self.predicates_of(def_id); |
197 | let predicates = predicates.instantiate_identity(self).predicates; | |
a7813a04 XL |
198 | elaborate_predicates(self, predicates) |
199 | .any(|predicate| { | |
200 | match predicate { | |
201 | ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => { | |
83c7162d | 202 | trait_pred.skip_binder().self_ty().is_self() |
a7813a04 XL |
203 | } |
204 | ty::Predicate::Projection(..) | | |
205 | ty::Predicate::Trait(..) | | |
cc61c64b | 206 | ty::Predicate::Subtype(..) | |
a7813a04 XL |
207 | ty::Predicate::RegionOutlives(..) | |
208 | ty::Predicate::WellFormed(..) | | |
209 | ty::Predicate::ObjectSafe(..) | | |
210 | ty::Predicate::ClosureKind(..) | | |
ea8adc8c XL |
211 | ty::Predicate::TypeOutlives(..) | |
212 | ty::Predicate::ConstEvaluatable(..) => { | |
a7813a04 XL |
213 | false |
214 | } | |
215 | } | |
216 | }) | |
217 | } | |
c34b1796 | 218 | |
a7813a04 XL |
219 | /// Returns `Some(_)` if this method makes the containing trait not object safe. |
220 | fn object_safety_violation_for_method(self, | |
221 | trait_def_id: DefId, | |
476ff2be | 222 | method: &ty::AssociatedItem) |
a7813a04 XL |
223 | -> Option<MethodViolationCode> |
224 | { | |
225 | // Any method that has a `Self : Sized` requisite is otherwise | |
226 | // exempt from the regulations. | |
9e0c209e | 227 | if self.generics_require_sized_self(method.def_id) { |
a7813a04 | 228 | return None; |
1a4d82fc JJ |
229 | } |
230 | ||
a7813a04 | 231 | self.virtual_call_violation_for_method(trait_def_id, method) |
1a4d82fc JJ |
232 | } |
233 | ||
a7813a04 XL |
234 | /// We say a method is *vtable safe* if it can be invoked on a trait |
235 | /// object. Note that object-safe traits can have some | |
236 | /// non-vtable-safe methods, so long as they require `Self:Sized` or | |
237 | /// otherwise ensure that they cannot be used when `Self=Trait`. | |
238 | pub fn is_vtable_safe_method(self, | |
239 | trait_def_id: DefId, | |
476ff2be | 240 | method: &ty::AssociatedItem) |
a7813a04 XL |
241 | -> bool |
242 | { | |
243 | // Any method that has a `Self : Sized` requisite can't be called. | |
9e0c209e | 244 | if self.generics_require_sized_self(method.def_id) { |
a7813a04 | 245 | return false; |
1a4d82fc | 246 | } |
1a4d82fc | 247 | |
a7813a04 | 248 | self.virtual_call_violation_for_method(trait_def_id, method).is_none() |
1a4d82fc JJ |
249 | } |
250 | ||
a7813a04 XL |
251 | /// Returns `Some(_)` if this method cannot be called on a trait |
252 | /// object; this does not necessarily imply that the enclosing trait | |
253 | /// is not object safe, because the method might have a where clause | |
254 | /// `Self:Sized`. | |
255 | fn virtual_call_violation_for_method(self, | |
256 | trait_def_id: DefId, | |
476ff2be | 257 | method: &ty::AssociatedItem) |
a7813a04 XL |
258 | -> Option<MethodViolationCode> |
259 | { | |
260 | // The method's first parameter must be something that derefs (or | |
261 | // autorefs) to `&self`. For now, we only accept `self`, `&self` | |
262 | // and `Box<Self>`. | |
476ff2be SL |
263 | if !method.method_has_self_argument { |
264 | return Some(MethodViolationCode::StaticMethod); | |
a7813a04 | 265 | } |
1a4d82fc | 266 | |
abe05a73 XL |
267 | let sig = self.fn_sig(method.def_id); |
268 | ||
269 | let self_ty = self.mk_self_type(); | |
270 | let self_arg_ty = sig.skip_binder().inputs()[0]; | |
271 | if let ExplicitSelf::Other = ExplicitSelf::determine(self_arg_ty, |ty| ty == self_ty) { | |
272 | return Some(MethodViolationCode::NonStandardSelfType); | |
273 | } | |
274 | ||
a7813a04 XL |
275 | // The `Self` type is erased, so it should not appear in list of |
276 | // arguments or return type apart from the receiver. | |
476ff2be | 277 | for input_ty in &sig.skip_binder().inputs()[1..] { |
a7813a04 XL |
278 | if self.contains_illegal_self_type_reference(trait_def_id, input_ty) { |
279 | return Some(MethodViolationCode::ReferencesSelf); | |
280 | } | |
281 | } | |
476ff2be | 282 | if self.contains_illegal_self_type_reference(trait_def_id, sig.output().skip_binder()) { |
5bcae85e | 283 | return Some(MethodViolationCode::ReferencesSelf); |
a7813a04 | 284 | } |
1a4d82fc | 285 | |
a7813a04 | 286 | // We can't monomorphize things like `fn foo<A>(...)`. |
7cac9316 | 287 | if !self.generics_of(method.def_id).types.is_empty() { |
a7813a04 XL |
288 | return Some(MethodViolationCode::Generic); |
289 | } | |
1a4d82fc | 290 | |
a7813a04 XL |
291 | None |
292 | } | |
293 | ||
294 | fn contains_illegal_self_type_reference(self, | |
295 | trait_def_id: DefId, | |
296 | ty: Ty<'tcx>) | |
297 | -> bool | |
298 | { | |
299 | // This is somewhat subtle. In general, we want to forbid | |
300 | // references to `Self` in the argument and return types, | |
301 | // since the value of `Self` is erased. However, there is one | |
302 | // exception: it is ok to reference `Self` in order to access | |
303 | // an associated type of the current trait, since we retain | |
304 | // the value of those associated types in the object type | |
305 | // itself. | |
306 | // | |
307 | // ```rust | |
308 | // trait SuperTrait { | |
309 | // type X; | |
310 | // } | |
311 | // | |
312 | // trait Trait : SuperTrait { | |
313 | // type Y; | |
314 | // fn foo(&self, x: Self) // bad | |
315 | // fn foo(&self) -> Self // bad | |
316 | // fn foo(&self) -> Option<Self> // bad | |
317 | // fn foo(&self) -> Self::Y // OK, desugars to next example | |
318 | // fn foo(&self) -> <Self as Trait>::Y // OK | |
319 | // fn foo(&self) -> Self::X // OK, desugars to next example | |
320 | // fn foo(&self) -> <Self as SuperTrait>::X // OK | |
321 | // } | |
322 | // ``` | |
323 | // | |
324 | // However, it is not as simple as allowing `Self` in a projected | |
325 | // type, because there are illegal ways to use `Self` as well: | |
326 | // | |
327 | // ```rust | |
328 | // trait Trait : SuperTrait { | |
329 | // ... | |
330 | // fn foo(&self) -> <Self as SomeOtherTrait>::X; | |
331 | // } | |
332 | // ``` | |
333 | // | |
334 | // Here we will not have the type of `X` recorded in the | |
335 | // object type, and we cannot resolve `Self as SomeOtherTrait` | |
336 | // without knowing what `Self` is. | |
337 | ||
338 | let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None; | |
339 | let mut error = false; | |
340 | ty.maybe_walk(|ty| { | |
341 | match ty.sty { | |
342 | ty::TyParam(ref param_ty) => { | |
9e0c209e | 343 | if param_ty.is_self() { |
a7813a04 XL |
344 | error = true; |
345 | } | |
346 | ||
347 | false // no contained types to walk | |
1a4d82fc JJ |
348 | } |
349 | ||
a7813a04 XL |
350 | ty::TyProjection(ref data) => { |
351 | // This is a projected type `<Foo as SomeTrait>::X`. | |
352 | ||
353 | // Compute supertraits of current trait lazily. | |
354 | if supertraits.is_none() { | |
83c7162d | 355 | let trait_ref = ty::Binder::bind(ty::TraitRef { |
476ff2be SL |
356 | def_id: trait_def_id, |
357 | substs: Substs::identity_for_item(self, trait_def_id) | |
358 | }); | |
a7813a04 XL |
359 | supertraits = Some(traits::supertraits(self, trait_ref).collect()); |
360 | } | |
361 | ||
362 | // Determine whether the trait reference `Foo as | |
363 | // SomeTrait` is in fact a supertrait of the | |
364 | // current trait. In that case, this type is | |
365 | // legal, because the type `X` will be specified | |
366 | // in the object type. Note that we can just use | |
367 | // direct equality here because all of these types | |
368 | // are part of the formal parameter listing, and | |
369 | // hence there should be no inference variables. | |
83c7162d | 370 | let projection_trait_ref = ty::Binder::bind(data.trait_ref(self)); |
a7813a04 XL |
371 | let is_supertrait_of_current_trait = |
372 | supertraits.as_ref().unwrap().contains(&projection_trait_ref); | |
373 | ||
374 | if is_supertrait_of_current_trait { | |
375 | false // do not walk contained types, do not report error, do collect $200 | |
376 | } else { | |
377 | true // DO walk contained types, POSSIBLY reporting an error | |
378 | } | |
1a4d82fc | 379 | } |
1a4d82fc | 380 | |
a7813a04 XL |
381 | _ => true, // walk contained types, if any |
382 | } | |
383 | }); | |
1a4d82fc | 384 | |
a7813a04 XL |
385 | error |
386 | } | |
1a4d82fc | 387 | } |
7cac9316 XL |
388 | |
389 | pub(super) fn is_object_safe_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
390 | trait_def_id: DefId) | |
391 | -> bool { | |
392 | tcx.object_safety_violations(trait_def_id).is_empty() | |
393 | } |