object_safety.rs 14.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// 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::supertraits;
use super::elaborate_predicates;

N
Niko Matsakis 已提交
23
use middle::def_id::DefId;
24 25 26
use ty::subst::{self, SelfSpace, TypeSpace};
use traits;
use ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
27 28 29
use std::rc::Rc;
use syntax::ast;

30
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
31 32 33 34
pub enum ObjectSafetyViolation<'tcx> {
    /// Self : Sized declared on the trait
    SizedSelf,

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

J
Joseph Crail 已提交
39
    /// Method has something illegal
40 41 42 43
    Method(Rc<ty::Method<'tcx>>, MethodViolationCode),
}

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

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

N
Niko Matsakis 已提交
52
    /// e.g., `fn foo<A>()`
53 54 55
    Generic,
}

56
pub fn is_object_safe<'tcx>(tcx: &TyCtxt<'tcx>,
N
Niko Matsakis 已提交
57
                            trait_def_id: DefId)
58 59 60
                            -> bool
{
    // Because we query yes/no results frequently, we keep a cache:
61
    let def = tcx.lookup_trait_def(trait_def_id);
62

63 64
    let result = def.object_safety().unwrap_or_else(|| {
        let result = object_safety_violations(tcx, trait_def_id).is_empty();
65

66 67 68 69
        // 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);
70

71 72
        result
    });
73

74
    debug!("is_object_safe({:?}) = {}", trait_def_id, result);
75 76 77 78

    result
}

79 80 81 82
/// 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.
83
pub fn astconv_object_safety_violations<'tcx>(tcx: &TyCtxt<'tcx>,
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
                                              trait_def_id: DefId)
                                              -> Vec<ObjectSafetyViolation<'tcx>>
{
    let mut violations = vec![];

    if supertraits_reference_self(tcx, trait_def_id) {
        violations.push(ObjectSafetyViolation::SupertraitSelf);
    }

    debug!("object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
           trait_def_id,
           violations);

    violations
}

100
pub fn object_safety_violations<'tcx>(tcx: &TyCtxt<'tcx>,
N
Niko Matsakis 已提交
101
                                      trait_def_id: DefId)
102 103
                                      -> Vec<ObjectSafetyViolation<'tcx>>
{
104
    traits::supertrait_def_ids(tcx, trait_def_id)
105
        .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id))
106 107 108
        .collect()
}

109
fn object_safety_violations_for_trait<'tcx>(tcx: &TyCtxt<'tcx>,
N
Niko Matsakis 已提交
110
                                            trait_def_id: DefId)
111 112 113 114
                                            -> Vec<ObjectSafetyViolation<'tcx>>
{
    // Check methods for violations.
    let mut violations: Vec<_> =
115
        tcx.trait_items(trait_def_id).iter()
116
        .filter_map(|item| {
117 118
            match *item {
                ty::MethodTraitItem(ref m) => {
J
Jonas Schievink 已提交
119
                    object_safety_violation_for_method(tcx, trait_def_id, &m)
120 121
                        .map(|code| ObjectSafetyViolation::Method(m.clone(), code))
                }
122
                _ => None,
123 124 125 126 127 128 129 130
            }
        })
        .collect();

    // Check the trait itself.
    if trait_has_sized_self(tcx, trait_def_id) {
        violations.push(ObjectSafetyViolation::SizedSelf);
    }
131 132 133
    if supertraits_reference_self(tcx, trait_def_id) {
        violations.push(ObjectSafetyViolation::SupertraitSelf);
    }
134

135 136 137
    debug!("object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
           trait_def_id,
           violations);
138 139 140 141

    violations
}

142
pub fn supertraits_reference_self<'tcx>(tcx: &TyCtxt<'tcx>,
143 144
                                        trait_def_id: DefId)
                                        -> bool
145
{
146
    let trait_def = tcx.lookup_trait_def(trait_def_id);
147
    let trait_ref = trait_def.trait_ref.clone();
148
    let trait_ref = trait_ref.to_poly_trait_ref();
149
    let predicates = tcx.lookup_super_predicates(trait_def_id);
150
    predicates
151
        .predicates
152
        .into_iter()
153
        .map(|predicate| predicate.subst_supertrait(tcx, &trait_ref))
154 155 156 157
        .any(|predicate| {
            match predicate {
                ty::Predicate::Trait(ref data) => {
                    // In the case of a trait predicate, we can skip the "self" type.
158 159 160
                    data.0.trait_ref.substs.types.get_slice(TypeSpace)
                                                 .iter()
                                                 .cloned()
161
                                                 .any(|t| t.has_self_ty())
162 163
                }
                ty::Predicate::Projection(..) |
164 165
                ty::Predicate::WellFormed(..) |
                ty::Predicate::ObjectSafe(..) |
166 167 168 169 170 171 172 173 174
                ty::Predicate::TypeOutlives(..) |
                ty::Predicate::RegionOutlives(..) |
                ty::Predicate::Equate(..) => {
                    false
                }
            }
        })
}

175
fn trait_has_sized_self<'tcx>(tcx: &TyCtxt<'tcx>,
N
Niko Matsakis 已提交
176
                              trait_def_id: DefId)
177
                              -> bool
178
{
179 180
    let trait_def = tcx.lookup_trait_def(trait_def_id);
    let trait_predicates = tcx.lookup_predicates(trait_def_id);
181 182 183
    generics_require_sized_self(tcx, &trait_def.generics, &trait_predicates)
}

184
fn generics_require_sized_self<'tcx>(tcx: &TyCtxt<'tcx>,
185 186 187
                                     generics: &ty::Generics<'tcx>,
                                     predicates: &ty::GenericPredicates<'tcx>)
                                     -> bool
188 189 190 191 192 193 194
{
    let sized_def_id = match tcx.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.
195 196
    let free_substs = tcx.construct_free_substs(generics,
                                                tcx.region_maps.node_extent(ast::DUMMY_NODE_ID));
197
    let predicates = predicates.instantiate(tcx, &free_substs).predicates.into_vec();
198 199 200 201
    elaborate_predicates(tcx, predicates)
        .any(|predicate| {
            match predicate {
                ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => {
202
                    trait_pred.0.self_ty().is_self()
203 204 205 206 207
                }
                ty::Predicate::Projection(..) |
                ty::Predicate::Trait(..) |
                ty::Predicate::Equate(..) |
                ty::Predicate::RegionOutlives(..) |
208 209
                ty::Predicate::WellFormed(..) |
                ty::Predicate::ObjectSafe(..) |
210 211 212 213 214 215 216
                ty::Predicate::TypeOutlives(..) => {
                    false
                }
            }
        })
}

217
/// Returns `Some(_)` if this method makes the containing trait not object safe.
218
fn object_safety_violation_for_method<'tcx>(tcx: &TyCtxt<'tcx>,
N
Niko Matsakis 已提交
219
                                            trait_def_id: DefId,
220 221
                                            method: &ty::Method<'tcx>)
                                            -> Option<MethodViolationCode>
222
{
223 224 225 226 227 228
    // Any method that has a `Self : Sized` requisite is otherwise
    // exempt from the regulations.
    if generics_require_sized_self(tcx, &method.generics, &method.predicates) {
        return None;
    }

229 230 231 232 233 234 235
    virtual_call_violation_for_method(tcx, trait_def_id, method)
}

/// 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`.
236
pub fn is_vtable_safe_method<'tcx>(tcx: &TyCtxt<'tcx>,
N
Niko Matsakis 已提交
237
                                   trait_def_id: DefId,
238 239 240 241 242 243 244 245 246 247
                                   method: &ty::Method<'tcx>)
                                   -> bool
{
    virtual_call_violation_for_method(tcx, trait_def_id, method).is_none()
}

/// 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`.
248
fn virtual_call_violation_for_method<'tcx>(tcx: &TyCtxt<'tcx>,
N
Niko Matsakis 已提交
249
                                           trait_def_id: DefId,
250 251 252
                                           method: &ty::Method<'tcx>)
                                           -> Option<MethodViolationCode>
{
253 254 255
    // The method's first parameter must be something that derefs (or
    // autorefs) to `&self`. For now, we only accept `self`, `&self`
    // and `Box<Self>`.
256
    match method.explicit_self {
257
        ty::ExplicitSelfCategory::Static => {
258 259 260
            return Some(MethodViolationCode::StaticMethod);
        }

261 262 263
        ty::ExplicitSelfCategory::ByValue |
        ty::ExplicitSelfCategory::ByReference(..) |
        ty::ExplicitSelfCategory::ByBox => {
264 265 266 267 268 269
        }
    }

    // The `Self` type is erased, so it should not appear in list of
    // arguments or return type apart from the receiver.
    let ref sig = method.fty.sig;
270
    for &input_ty in &sig.0.inputs[1..] {
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
        if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) {
            return Some(MethodViolationCode::ReferencesSelf);
        }
    }
    if let ty::FnConverging(result_type) = sig.0.output {
        if contains_illegal_self_type_reference(tcx, trait_def_id, result_type) {
            return Some(MethodViolationCode::ReferencesSelf);
        }
    }

    // We can't monomorphize things like `fn foo<A>(...)`.
    if !method.generics.types.is_empty_in(subst::FnSpace) {
        return Some(MethodViolationCode::Generic);
    }

    None
}

289
fn contains_illegal_self_type_reference<'tcx>(tcx: &TyCtxt<'tcx>,
N
Niko Matsakis 已提交
290
                                              trait_def_id: DefId,
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
                                              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;
335
    ty.maybe_walk(|ty| {
336
        match ty.sty {
337
            ty::TyParam(ref param_ty) => {
338 339 340 341 342 343 344
                if param_ty.space == SelfSpace {
                    error = true;
                }

                false // no contained types to walk
            }

345
            ty::TyProjection(ref data) => {
346 347
                // This is a projected type `<Foo as SomeTrait>::X`.

J
Joseph Crail 已提交
348
                // Compute supertraits of current trait lazily.
349
                if supertraits.is_none() {
350
                    let trait_def = tcx.lookup_trait_def(trait_def_id);
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
                    let trait_ref = ty::Binder(trait_def.trait_ref.clone());
                    supertraits = Some(traits::supertraits(tcx, 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
                }
            }

            _ => true, // walk contained types, if any
        }
    });

    error
}