object_safety.rs 13.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! "Object safety" refers to the ability for a trait to be converted
//! to an object. In general, traits may only be converted to an
//! object if all of their methods meet certain criteria. In particular,
//! they must:
//!
//!   - have a suitable receiver from which we can extract a vtable;
//!   - not reference the erased type `Self` except for in this receiver;
//!   - not have generic type parameters

use super::elaborate_predicates;

22
use hir::def_id::DefId;
23 24
use traits;
use ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
25 26
use syntax::ast;

27
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
28
pub enum ObjectSafetyViolation {
29 30 31
    /// Self : Sized declared on the trait
    SizedSelf,

32 33 34 35
    /// Supertrait reference references `Self` an in illegal location
    /// (e.g. `trait Foo : Bar<Self>`)
    SupertraitSelf,

J
Joseph Crail 已提交
36
    /// Method has something illegal
37
    Method(ast::Name, MethodViolationCode),
38 39 40
}

/// Reasons a method might not be object-safe.
41
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
42
pub enum MethodViolationCode {
N
Niko Matsakis 已提交
43
    /// e.g., `fn foo()`
44 45
    StaticMethod,

N
Niko Matsakis 已提交
46
    /// e.g., `fn foo(&self, x: Self)` or `fn foo(&self) -> Self`
47 48
    ReferencesSelf,

N
Niko Matsakis 已提交
49
    /// e.g., `fn foo<A>()`
50 51 52
    Generic,
}

53
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
54 55 56
    pub fn is_object_safe(self, trait_def_id: DefId) -> bool {
        // Because we query yes/no results frequently, we keep a cache:
        let def = self.lookup_trait_def(trait_def_id);
57

58 59
        let result = def.object_safety().unwrap_or_else(|| {
            let result = self.object_safety_violations(trait_def_id).is_empty();
60

61 62 63 64
            // Record just a yes/no result in the cache; this is what is
            // queried most frequently. Note that this may overwrite a
            // previous result, but always with the same thing.
            def.set_object_safety(result);
65

66 67
            result
        });
68

69
        debug!("is_object_safe({:?}) = {}", trait_def_id, result);
70

71
        result
72 73
    }

74 75 76 77 78
    /// Returns the object safety violations that affect
    /// astconv - currently, Self in supertraits. This is needed
    /// because `object_safety_violations` can't be used during
    /// type collection.
    pub fn astconv_object_safety_violations(self, trait_def_id: DefId)
79
                                            -> Vec<ObjectSafetyViolation>
80 81 82 83 84 85
    {
        let mut violations = vec![];

        if self.supertraits_reference_self(trait_def_id) {
            violations.push(ObjectSafetyViolation::SupertraitSelf);
        }
86

87 88 89
        debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}",
               trait_def_id,
               violations);
90

91 92 93 94
        violations
    }

    pub fn object_safety_violations(self, trait_def_id: DefId)
95
                                    -> Vec<ObjectSafetyViolation>
96 97 98 99 100
    {
        traits::supertrait_def_ids(self, trait_def_id)
            .flat_map(|def_id| self.object_safety_violations_for_trait(def_id))
            .collect()
    }
101

102
    fn object_safety_violations_for_trait(self, trait_def_id: DefId)
103
                                          -> Vec<ObjectSafetyViolation>
104 105
    {
        // Check methods for violations.
106 107
        let mut violations: Vec<_> = self.associated_items(trait_def_id)
            .filter(|item| item.kind == ty::AssociatedKind::Method)
108
            .filter_map(|item| {
109 110 111
                self.object_safety_violation_for_method(trait_def_id, &item)
                    .map(|code| ObjectSafetyViolation::Method(item.name, code))
            }).collect();
112

113 114 115 116 117 118 119
        // Check the trait itself.
        if self.trait_has_sized_self(trait_def_id) {
            violations.push(ObjectSafetyViolation::SizedSelf);
        }
        if self.supertraits_reference_self(trait_def_id) {
            violations.push(ObjectSafetyViolation::SupertraitSelf);
        }
120

121 122 123
        debug!("object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
               trait_def_id,
               violations);
124

125 126
        violations
    }
127

128 129 130 131
    fn supertraits_reference_self(self, trait_def_id: DefId) -> bool {
        let trait_def = self.lookup_trait_def(trait_def_id);
        let trait_ref = trait_def.trait_ref.clone();
        let trait_ref = trait_ref.to_poly_trait_ref();
132
        let predicates = self.item_super_predicates(trait_def_id);
133 134 135 136 137 138 139 140
        predicates
            .predicates
            .into_iter()
            .map(|predicate| predicate.subst_supertrait(self, &trait_ref))
            .any(|predicate| {
                match predicate {
                    ty::Predicate::Trait(ref data) => {
                        // In the case of a trait predicate, we can skip the "self" type.
141
                        data.skip_binder().input_types().skip(1).any(|t| t.has_self_ty())
142 143 144 145 146 147 148 149 150 151
                    }
                    ty::Predicate::Projection(..) |
                    ty::Predicate::WellFormed(..) |
                    ty::Predicate::ObjectSafe(..) |
                    ty::Predicate::TypeOutlives(..) |
                    ty::Predicate::RegionOutlives(..) |
                    ty::Predicate::ClosureKind(..) |
                    ty::Predicate::Equate(..) => {
                        false
                    }
152
                }
153 154
            })
    }
155

156
    fn trait_has_sized_self(self, trait_def_id: DefId) -> bool {
157
        self.generics_require_sized_self(trait_def_id)
158
    }
159

160
    fn generics_require_sized_self(self, def_id: DefId) -> bool {
161 162 163 164 165 166
        let sized_def_id = match self.lang_items.sized_trait() {
            Some(def_id) => def_id,
            None => { return false; /* No Sized trait, can't require it! */ }
        };

        // Search for a predicate like `Self : Sized` amongst the trait bounds.
167
        let free_substs = self.construct_free_substs(def_id,
168
            self.region_maps.node_extent(ast::DUMMY_NODE_ID));
169
        let predicates = self.item_predicates(def_id);
170
        let predicates = predicates.instantiate(self, free_substs).predicates;
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
        elaborate_predicates(self, predicates)
            .any(|predicate| {
                match predicate {
                    ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => {
                        trait_pred.0.self_ty().is_self()
                    }
                    ty::Predicate::Projection(..) |
                    ty::Predicate::Trait(..) |
                    ty::Predicate::Equate(..) |
                    ty::Predicate::RegionOutlives(..) |
                    ty::Predicate::WellFormed(..) |
                    ty::Predicate::ObjectSafe(..) |
                    ty::Predicate::ClosureKind(..) |
                    ty::Predicate::TypeOutlives(..) => {
                        false
                    }
187
                }
188 189 190 191 192 193
            })
    }

    /// Returns `Some(_)` if this method makes the containing trait not object safe.
    fn object_safety_violation_for_method(self,
                                          trait_def_id: DefId,
194
                                          method: &ty::AssociatedItem)
195 196 197 198
                                          -> Option<MethodViolationCode>
    {
        // Any method that has a `Self : Sized` requisite is otherwise
        // exempt from the regulations.
199
        if self.generics_require_sized_self(method.def_id) {
200 201
            return None;
        }
202

203
        self.virtual_call_violation_for_method(trait_def_id, method)
204 205
    }

206 207 208 209 210 211
    /// We say a method is *vtable safe* if it can be invoked on a trait
    /// object.  Note that object-safe traits can have some
    /// non-vtable-safe methods, so long as they require `Self:Sized` or
    /// otherwise ensure that they cannot be used when `Self=Trait`.
    pub fn is_vtable_safe_method(self,
                                 trait_def_id: DefId,
212
                                 method: &ty::AssociatedItem)
213 214
                                 -> bool
    {
215
        // Any method that has a `Self : Sized` requisite can't be called.
216
        if self.generics_require_sized_self(method.def_id) {
217 218 219
            return false;
        }

220 221
        self.virtual_call_violation_for_method(trait_def_id, method).is_none()
    }
222

223 224 225 226 227 228
    /// Returns `Some(_)` if this method cannot be called on a trait
    /// object; this does not necessarily imply that the enclosing trait
    /// is not object safe, because the method might have a where clause
    /// `Self:Sized`.
    fn virtual_call_violation_for_method(self,
                                         trait_def_id: DefId,
229
                                         method: &ty::AssociatedItem)
230 231 232 233 234
                                         -> Option<MethodViolationCode>
    {
        // The method's first parameter must be something that derefs (or
        // autorefs) to `&self`. For now, we only accept `self`, `&self`
        // and `Box<Self>`.
235 236
        if !method.method_has_self_argument {
            return Some(MethodViolationCode::StaticMethod);
237 238
        }

239 240
        // The `Self` type is erased, so it should not appear in list of
        // arguments or return type apart from the receiver.
241
        let ref sig = self.item_type(method.def_id).fn_sig();
242 243 244 245
        for &input_ty in &sig.0.inputs[1..] {
            if self.contains_illegal_self_type_reference(trait_def_id, input_ty) {
                return Some(MethodViolationCode::ReferencesSelf);
            }
246
        }
247 248
        if self.contains_illegal_self_type_reference(trait_def_id, sig.0.output) {
            return Some(MethodViolationCode::ReferencesSelf);
249
        }
250 251

        // We can't monomorphize things like `fn foo<A>(...)`.
252
        if !self.item_generics(method.def_id).types.is_empty() {
253
            return Some(MethodViolationCode::Generic);
254 255
        }

256
        None
257 258
    }

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
    fn contains_illegal_self_type_reference(self,
                                            trait_def_id: DefId,
                                            ty: Ty<'tcx>)
                                            -> bool
    {
        // This is somewhat subtle. In general, we want to forbid
        // references to `Self` in the argument and return types,
        // since the value of `Self` is erased. However, there is one
        // exception: it is ok to reference `Self` in order to access
        // an associated type of the current trait, since we retain
        // the value of those associated types in the object type
        // itself.
        //
        // ```rust
        // trait SuperTrait {
        //     type X;
        // }
        //
        // trait Trait : SuperTrait {
        //     type Y;
        //     fn foo(&self, x: Self) // bad
        //     fn foo(&self) -> Self // bad
        //     fn foo(&self) -> Option<Self> // bad
        //     fn foo(&self) -> Self::Y // OK, desugars to next example
        //     fn foo(&self) -> <Self as Trait>::Y // OK
        //     fn foo(&self) -> Self::X // OK, desugars to next example
        //     fn foo(&self) -> <Self as SuperTrait>::X // OK
        // }
        // ```
        //
        // However, it is not as simple as allowing `Self` in a projected
        // type, because there are illegal ways to use `Self` as well:
        //
        // ```rust
        // trait Trait : SuperTrait {
        //     ...
        //     fn foo(&self) -> <Self as SomeOtherTrait>::X;
        // }
        // ```
        //
        // Here we will not have the type of `X` recorded in the
        // object type, and we cannot resolve `Self as SomeOtherTrait`
        // without knowing what `Self` is.

        let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
        let mut error = false;
        ty.maybe_walk(|ty| {
            match ty.sty {
                ty::TyParam(ref param_ty) => {
308
                    if param_ty.is_self() {
309 310 311 312
                        error = true;
                    }

                    false // no contained types to walk
313 314
                }

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
                ty::TyProjection(ref data) => {
                    // This is a projected type `<Foo as SomeTrait>::X`.

                    // Compute supertraits of current trait lazily.
                    if supertraits.is_none() {
                        let trait_def = self.lookup_trait_def(trait_def_id);
                        let trait_ref = ty::Binder(trait_def.trait_ref.clone());
                        supertraits = Some(traits::supertraits(self, trait_ref).collect());
                    }

                    // Determine whether the trait reference `Foo as
                    // SomeTrait` is in fact a supertrait of the
                    // current trait. In that case, this type is
                    // legal, because the type `X` will be specified
                    // in the object type.  Note that we can just use
                    // direct equality here because all of these types
                    // are part of the formal parameter listing, and
                    // hence there should be no inference variables.
                    let projection_trait_ref = ty::Binder(data.trait_ref.clone());
                    let is_supertrait_of_current_trait =
                        supertraits.as_ref().unwrap().contains(&projection_trait_ref);

                    if is_supertrait_of_current_trait {
                        false // do not walk contained types, do not report error, do collect $200
                    } else {
                        true // DO walk contained types, POSSIBLY reporting an error
                    }
342 343
                }

344
                _ => true, // walk contained types, if any
345
            }
346
        });
347

348 349
        error
    }
350
}