object_safety.rs 10.6 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;

23
use middle::subst::{self, SelfSpace};
24
use middle::traits;
25
use middle::ty::{self, Ty};
26 27 28 29 30 31 32 33
use std::rc::Rc;
use syntax::ast;
use util::ppaux::Repr;

pub enum ObjectSafetyViolation<'tcx> {
    /// Self : Sized declared on the trait
    SizedSelf,

J
Joseph Crail 已提交
34
    /// Method has something illegal
35 36 37 38
    Method(Rc<ty::Method<'tcx>>, MethodViolationCode),
}

/// Reasons a method might not be object-safe.
39
#[derive(Copy,Clone,Show)]
40
pub enum MethodViolationCode {
N
Niko Matsakis 已提交
41
    /// e.g., `fn(self)`
42 43
    ByValueSelf,

N
Niko Matsakis 已提交
44
    /// e.g., `fn foo()`
45 46
    StaticMethod,

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

N
Niko Matsakis 已提交
50
    /// e.g., `fn foo<A>()`
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
    Generic,
}

pub fn is_object_safe<'tcx>(tcx: &ty::ctxt<'tcx>,
                            trait_ref: ty::PolyTraitRef<'tcx>)
                            -> bool
{
    // Because we query yes/no results frequently, we keep a cache:
    let cached_result =
        tcx.object_safety_cache.borrow().get(&trait_ref.def_id()).map(|&r| r);

    let result =
        cached_result.unwrap_or_else(|| {
            let result = object_safety_violations(tcx, trait_ref.clone()).is_empty();

            // 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.
            tcx.object_safety_cache.borrow_mut().insert(trait_ref.def_id(), result);

            result
        });

    debug!("is_object_safe({}) = {}", trait_ref.repr(tcx), result);

    result
}

pub fn object_safety_violations<'tcx>(tcx: &ty::ctxt<'tcx>,
                                      sub_trait_ref: ty::PolyTraitRef<'tcx>)
                                      -> Vec<ObjectSafetyViolation<'tcx>>
{
    supertraits(tcx, sub_trait_ref)
        .flat_map(|tr| object_safety_violations_for_trait(tcx, tr.def_id()).into_iter())
        .collect()
}

fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
                                            trait_def_id: ast::DefId)
                                            -> Vec<ObjectSafetyViolation<'tcx>>
{
    // Check methods for violations.
    let mut violations: Vec<_> =
        ty::trait_items(tcx, trait_def_id).iter()
        .flat_map(|item| {
            match *item {
                ty::MethodTraitItem(ref m) => {
                    object_safety_violations_for_method(tcx, trait_def_id, &**m)
                        .map(|code| ObjectSafetyViolation::Method(m.clone(), code))
                        .into_iter()
                }
                ty::TypeTraitItem(_) => {
                    None.into_iter()
                }
            }
        })
        .collect();

    // Check the trait itself.
    if trait_has_sized_self(tcx, trait_def_id) {
        violations.push(ObjectSafetyViolation::SizedSelf);
    }

    debug!("object_safety_violations_for_trait(trait_def_id={}) = {}",
           trait_def_id.repr(tcx),
           violations.repr(tcx));

    violations
}

fn trait_has_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
                              trait_def_id: ast::DefId)
                              -> bool
{
    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.
131 132 133
    let trait_def = ty::lookup_trait_def(tcx, trait_def_id);
    let free_substs = ty::construct_free_substs(tcx, &trait_def.generics, ast::DUMMY_NODE_ID);
    let predicates = trait_def.generics.to_bounds(tcx, &free_substs).predicates.into_vec();
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    elaborate_predicates(tcx, predicates)
        .any(|predicate| {
            match predicate {
                ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => {
                    let self_ty = trait_pred.0.self_ty();
                    match self_ty.sty {
                        ty::ty_param(ref data) => data.space == subst::SelfSpace,
                        _ => false,
                    }
                }
                ty::Predicate::Projection(..) |
                ty::Predicate::Trait(..) |
                ty::Predicate::Equate(..) |
                ty::Predicate::RegionOutlives(..) |
                ty::Predicate::TypeOutlives(..) => {
                    false
                }
            }
        })
}

fn object_safety_violations_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                                             trait_def_id: ast::DefId,
                                             method: &ty::Method<'tcx>)
                                             -> Option<MethodViolationCode>
{
    // The method's first parameter must be something that derefs to
    // `&self`. For now, we only accept `&self` and `Box<Self>`.
    match method.explicit_self {
        ty::ByValueExplicitSelfCategory => {
            return Some(MethodViolationCode::ByValueSelf);
        }

        ty::StaticExplicitSelfCategory => {
            return Some(MethodViolationCode::StaticMethod);
        }

        ty::ByReferenceExplicitSelfCategory(..) |
        ty::ByBoxExplicitSelfCategory => {
        }
    }

    // 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;
J
Jorge Aparicio 已提交
179
    for &input_ty in sig.0.inputs[1..].iter() {
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
        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
}

fn contains_illegal_self_type_reference<'tcx>(tcx: &ty::ctxt<'tcx>,
                                              trait_def_id: ast::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(ty, |ty| {
        match ty.sty {
            ty::ty_param(ref param_ty) => {
                if param_ty.space == SelfSpace {
                    error = true;
                }

                false // no contained types to walk
            }

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

J
Joseph Crail 已提交
257
                // Compute supertraits of current trait lazily.
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
                if supertraits.is_none() {
                    let trait_def = ty::lookup_trait_def(tcx, trait_def_id);
                    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
}

impl<'tcx> Repr<'tcx> for ObjectSafetyViolation<'tcx> {
    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
        match *self {
            ObjectSafetyViolation::SizedSelf =>
                format!("SizedSelf"),
            ObjectSafetyViolation::Method(ref m, code) =>
296
                format!("Method({},{:?})", m.repr(tcx), code),
297 298 299
        }
    }
}