Refactor object-safety into its own (cached) module so that we can
check it more easily; also extend object safety to cover sized types as well as static methods. This makes it sufficient so that we can always ensure that `Foo : Foo` holds for any trait `Foo`.
This commit is contained in:
parent
2c1d7a7caa
commit
19dcecb225
@ -29,6 +29,10 @@ pub use self::fulfill::{FulfillmentContext, RegionObligation};
|
||||
pub use self::project::MismatchedProjectionTypes;
|
||||
pub use self::project::normalize;
|
||||
pub use self::project::Normalized;
|
||||
pub use self::object_safety::is_object_safe;
|
||||
pub use self::object_safety::object_safety_violations;
|
||||
pub use self::object_safety::ObjectSafetyViolation;
|
||||
pub use self::object_safety::MethodViolationCode;
|
||||
pub use self::select::SelectionContext;
|
||||
pub use self::select::SelectionCache;
|
||||
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
|
||||
@ -45,6 +49,7 @@ mod coherence;
|
||||
mod error_reporting;
|
||||
mod fulfill;
|
||||
mod project;
|
||||
mod object_safety;
|
||||
mod select;
|
||||
mod util;
|
||||
|
||||
|
302
src/librustc/middle/traits/object_safety.rs
Normal file
302
src/librustc/middle/traits/object_safety.rs
Normal file
@ -0,0 +1,302 @@
|
||||
// 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;
|
||||
|
||||
use middle::subst::{mod, SelfSpace};
|
||||
use middle::traits;
|
||||
use middle::ty::{mod, Ty};
|
||||
use std::rc::Rc;
|
||||
use syntax::ast;
|
||||
use util::ppaux::Repr;
|
||||
|
||||
pub enum ObjectSafetyViolation<'tcx> {
|
||||
/// Self : Sized declared on the trait
|
||||
SizedSelf,
|
||||
|
||||
/// Method has someting illegal
|
||||
Method(Rc<ty::Method<'tcx>>, MethodViolationCode),
|
||||
}
|
||||
|
||||
/// Reasons a method might not be object-safe.
|
||||
#[deriving(Copy,Clone,Show)]
|
||||
pub enum MethodViolationCode {
|
||||
/// fn(self),
|
||||
ByValueSelf,
|
||||
|
||||
// fn foo()
|
||||
StaticMethod,
|
||||
|
||||
// fn foo(&self, x: Self)
|
||||
// fn foo(&self) -> Self
|
||||
ReferencesSelf,
|
||||
|
||||
// fn foo<A>(),
|
||||
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 trait_def = ty::lookup_trait_def(tcx, trait_def_id);
|
||||
let param_env = ty::construct_parameter_environment(tcx,
|
||||
&trait_def.generics,
|
||||
ast::DUMMY_NODE_ID);
|
||||
let predicates = param_env.caller_bounds.predicates.as_slice().to_vec();
|
||||
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.
|
||||
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;
|
||||
for &input_ty in sig.0.inputs[1..].iter() {
|
||||
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`.
|
||||
|
||||
// Compute supertraits of current trait lazilly.
|
||||
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) =>
|
||||
format!("Method({},{})", m.repr(tcx), code),
|
||||
}
|
||||
}
|
||||
}
|
@ -827,6 +827,9 @@ pub struct ctxt<'tcx> {
|
||||
/// parameters are never placed into this cache, because their
|
||||
/// results are dependent on the parameter environment.
|
||||
pub type_impls_sized_cache: RefCell<HashMap<Ty<'tcx>,bool>>,
|
||||
|
||||
/// Caches whether traits are object safe
|
||||
pub object_safety_cache: RefCell<DefIdMap<bool>>,
|
||||
}
|
||||
|
||||
// Flags that we track on types. These flags are propagated upwards
|
||||
@ -2384,6 +2387,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
|
||||
repr_hint_cache: RefCell::new(DefIdMap::new()),
|
||||
type_impls_copy_cache: RefCell::new(HashMap::new()),
|
||||
type_impls_sized_cache: RefCell::new(HashMap::new()),
|
||||
object_safety_cache: RefCell::new(DefIdMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ impl PpSourceMode {
|
||||
}
|
||||
}
|
||||
|
||||
trait PrinterSupport<'ast>: pprust::PpAnn + Sized {
|
||||
trait PrinterSupport<'ast>: pprust::PpAnn {
|
||||
/// Provides a uniform interface for re-extracting a reference to a
|
||||
/// `Session` from a value that now owns it.
|
||||
fn sess<'a>(&'a self) -> &'a Session;
|
||||
@ -154,7 +154,7 @@ trait PrinterSupport<'ast>: pprust::PpAnn + Sized {
|
||||
///
|
||||
/// (Rust does not yet support upcasting from a trait object to
|
||||
/// an object for one of its super-traits.)
|
||||
fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self as &pprust::PpAnn }
|
||||
fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn;
|
||||
}
|
||||
|
||||
struct NoAnn<'ast> {
|
||||
@ -168,6 +168,8 @@ impl<'ast> PrinterSupport<'ast> for NoAnn<'ast> {
|
||||
fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> {
|
||||
self.ast_map.as_ref()
|
||||
}
|
||||
|
||||
fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self }
|
||||
}
|
||||
|
||||
impl<'ast> pprust::PpAnn for NoAnn<'ast> {}
|
||||
@ -183,6 +185,8 @@ impl<'ast> PrinterSupport<'ast> for IdentifiedAnnotation<'ast> {
|
||||
fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> {
|
||||
self.ast_map.as_ref()
|
||||
}
|
||||
|
||||
fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self }
|
||||
}
|
||||
|
||||
impl<'ast> pprust::PpAnn for IdentifiedAnnotation<'ast> {
|
||||
@ -232,6 +236,8 @@ impl<'ast> PrinterSupport<'ast> for HygieneAnnotation<'ast> {
|
||||
fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> {
|
||||
self.ast_map.as_ref()
|
||||
}
|
||||
|
||||
fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self }
|
||||
}
|
||||
|
||||
impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> {
|
||||
@ -265,6 +271,8 @@ impl<'tcx> PrinterSupport<'tcx> for TypedAnnotation<'tcx> {
|
||||
fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'tcx>> {
|
||||
Some(&self.analysis.ty_cx.map)
|
||||
}
|
||||
|
||||
fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self }
|
||||
}
|
||||
|
||||
impl<'tcx> pprust::PpAnn for TypedAnnotation<'tcx> {
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
use check::{FnCtxt, structurally_resolved_type};
|
||||
use middle::subst::{FnSpace, SelfSpace};
|
||||
use middle::traits;
|
||||
use middle::traits::{mod, ObjectSafetyViolation, MethodViolationCode};
|
||||
use middle::traits::{Obligation, ObligationCause};
|
||||
use middle::traits::report_fulfillment_errors;
|
||||
use middle::ty::{mod, Ty, AsPredicate};
|
||||
@ -133,217 +133,56 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
object_trait: &ty::TyTrait<'tcx>,
|
||||
span: Span)
|
||||
{
|
||||
// Also check that the type `object_trait` specifies all
|
||||
// associated types for all supertraits.
|
||||
let mut associated_types: FnvHashSet<(ast::DefId, ast::Name)> = FnvHashSet::new();
|
||||
|
||||
let object_trait_ref =
|
||||
object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
|
||||
for tr in traits::supertraits(tcx, object_trait_ref.clone()) {
|
||||
check_object_safety_inner(tcx, &tr, span);
|
||||
|
||||
let trait_def = ty::lookup_trait_def(tcx, object_trait_ref.def_id());
|
||||
for &associated_type_name in trait_def.associated_type_names.iter() {
|
||||
associated_types.insert((object_trait_ref.def_id(), associated_type_name));
|
||||
}
|
||||
if traits::is_object_safe(tcx, object_trait_ref.clone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for projection_bound in object_trait.bounds.projection_bounds.iter() {
|
||||
let pair = (projection_bound.0.projection_ty.trait_ref.def_id,
|
||||
projection_bound.0.projection_ty.item_name);
|
||||
associated_types.remove(&pair);
|
||||
}
|
||||
span_err!(tcx.sess, span, E0038,
|
||||
"cannot convert to a trait object because trait `{}` is not object-safe",
|
||||
ty::item_path_str(tcx, object_trait_ref.def_id()));
|
||||
|
||||
for (trait_def_id, name) in associated_types.into_iter() {
|
||||
tcx.sess.span_err(
|
||||
span,
|
||||
format!("the value of the associated type `{}` (from the trait `{}`) must be specified",
|
||||
name.user_string(tcx),
|
||||
ty::item_path_str(tcx, trait_def_id)).as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
object_trait: &ty::PolyTraitRef<'tcx>,
|
||||
span: Span) {
|
||||
let trait_items = ty::trait_items(tcx, object_trait.def_id());
|
||||
|
||||
let mut errors = Vec::new();
|
||||
for item in trait_items.iter() {
|
||||
match *item {
|
||||
ty::MethodTraitItem(ref m) => {
|
||||
errors.push(check_object_safety_of_method(tcx, object_trait, &**m))
|
||||
}
|
||||
ty::TypeTraitItem(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut errors = errors.iter().flat_map(|x| x.iter()).peekable();
|
||||
if errors.peek().is_some() {
|
||||
let trait_name = ty::item_path_str(tcx, object_trait.def_id());
|
||||
span_err!(tcx.sess, span, E0038,
|
||||
"cannot convert to a trait object because trait `{}` is not object-safe",
|
||||
trait_name);
|
||||
|
||||
for msg in errors {
|
||||
tcx.sess.note(msg[]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a vec of error messages. If the vec is empty - no errors!
|
||||
///
|
||||
/// There are some limitations to calling functions through an object, because (a) the self
|
||||
/// type is not known (that's the whole point of a trait instance, after all, to obscure the
|
||||
/// self type), (b) the call must go through a vtable and hence cannot be monomorphized and
|
||||
/// (c) the trait contains static methods which can't be called because we don't know the
|
||||
/// concrete type.
|
||||
fn check_object_safety_of_method<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
object_trait: &ty::PolyTraitRef<'tcx>,
|
||||
method: &ty::Method<'tcx>)
|
||||
-> Vec<String> {
|
||||
let mut msgs = Vec::new();
|
||||
|
||||
let method_name = method.name.repr(tcx);
|
||||
|
||||
match method.explicit_self {
|
||||
ty::ByValueExplicitSelfCategory => { // reason (a) above
|
||||
msgs.push(format!("cannot call a method (`{}`) with a by-value \
|
||||
receiver through a trait object", method_name))
|
||||
let violations = traits::object_safety_violations(tcx, object_trait_ref.clone());
|
||||
for violation in violations.into_iter() {
|
||||
match violation {
|
||||
ObjectSafetyViolation::SizedSelf => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
"the trait cannot require that `Self : Sized`");
|
||||
}
|
||||
|
||||
ty::StaticExplicitSelfCategory => {
|
||||
// Static methods are never object safe (reason (c)).
|
||||
msgs.push(format!("cannot call a static method (`{}`) \
|
||||
through a trait object",
|
||||
method_name));
|
||||
return msgs;
|
||||
ObjectSafetyViolation::Method(method, MethodViolationCode::ByValueSelf) => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
format!("method `{}` has a receiver type of `Self`, \
|
||||
which cannot be used with a trait object",
|
||||
method.name.user_string(tcx)).as_slice());
|
||||
}
|
||||
ty::ByReferenceExplicitSelfCategory(..) |
|
||||
ty::ByBoxExplicitSelfCategory => {}
|
||||
}
|
||||
|
||||
// reason (a) above
|
||||
let check_for_self_ty = |&: ty| {
|
||||
if contains_illegal_self_type_reference(tcx, object_trait.def_id(), ty) {
|
||||
Some(format!(
|
||||
"cannot call a method (`{}`) whose type contains \
|
||||
a self-type (`{}`) through a trait object",
|
||||
method_name, ty.user_string(tcx)))
|
||||
} else {
|
||||
None
|
||||
ObjectSafetyViolation::Method(method, MethodViolationCode::StaticMethod) => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
format!("method `{}` has no receiver",
|
||||
method.name.user_string(tcx)).as_slice());
|
||||
}
|
||||
};
|
||||
let ref sig = method.fty.sig;
|
||||
for &input_ty in sig.0.inputs[1..].iter() {
|
||||
if let Some(msg) = check_for_self_ty(input_ty) {
|
||||
msgs.push(msg);
|
||||
|
||||
ObjectSafetyViolation::Method(method, MethodViolationCode::ReferencesSelf) => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
format!("method `{}` references the `Self` type \
|
||||
in its arguments or return type",
|
||||
method.name.user_string(tcx)).as_slice());
|
||||
}
|
||||
|
||||
ObjectSafetyViolation::Method(method, MethodViolationCode::Generic) => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
format!("method `{}` has generic type parameters",
|
||||
method.name.user_string(tcx)).as_slice());
|
||||
}
|
||||
}
|
||||
if let ty::FnConverging(result_type) = sig.0.output {
|
||||
if let Some(msg) = check_for_self_ty(result_type) {
|
||||
msgs.push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if method.generics.has_type_params(FnSpace) {
|
||||
// reason (b) above
|
||||
msgs.push(format!("cannot call a generic method (`{}`) through a trait object",
|
||||
method_name));
|
||||
}
|
||||
|
||||
msgs
|
||||
}
|
||||
|
||||
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`.
|
||||
|
||||
// Compute supertraits of current trait lazilly.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,7 +231,7 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
cause.clone());
|
||||
}
|
||||
|
||||
// Finally, create obligations for the projection predicates.
|
||||
// Create obligations for the projection predicates.
|
||||
let projection_bounds =
|
||||
object_trait.projection_bounds_with_self_ty(fcx.tcx(), referent_ty);
|
||||
for projection_bound in projection_bounds.iter() {
|
||||
@ -401,9 +240,47 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
fcx.register_predicate(projection_obligation);
|
||||
}
|
||||
|
||||
// Finally, check that there IS a projection predicate for every associated type.
|
||||
check_object_type_binds_all_associated_types(fcx.tcx(),
|
||||
span,
|
||||
object_trait);
|
||||
|
||||
object_trait_ref
|
||||
}
|
||||
|
||||
fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
span: Span,
|
||||
object_trait: &ty::TyTrait<'tcx>)
|
||||
{
|
||||
let object_trait_ref =
|
||||
object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
|
||||
|
||||
let mut associated_types: FnvHashSet<(ast::DefId, ast::Name)> =
|
||||
traits::supertraits(tcx, object_trait_ref.clone())
|
||||
.flat_map(|tr| {
|
||||
let trait_def = ty::lookup_trait_def(tcx, tr.def_id());
|
||||
trait_def.associated_type_names
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(move |associated_type_name| (tr.def_id(), associated_type_name))
|
||||
})
|
||||
.collect();
|
||||
|
||||
for projection_bound in object_trait.bounds.projection_bounds.iter() {
|
||||
let pair = (projection_bound.0.projection_ty.trait_ref.def_id,
|
||||
projection_bound.0.projection_ty.item_name);
|
||||
associated_types.remove(&pair);
|
||||
}
|
||||
|
||||
for (trait_def_id, name) in associated_types.into_iter() {
|
||||
tcx.sess.span_err(
|
||||
span,
|
||||
format!("the value of the associated type `{}` (from the trait `{}`) must be specified",
|
||||
name.user_string(tcx),
|
||||
ty::item_path_str(tcx, trait_def_id)).as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
|
||||
debug!("select_all_fcx_obligations_or_error");
|
||||
|
||||
|
@ -21,6 +21,6 @@ impl Foo for Thing {
|
||||
|
||||
fn main() {
|
||||
let mut thing = Thing;
|
||||
let test: &Bar = &mut thing; //~ ERROR cannot convert to a trait object because trait `Foo`
|
||||
let test: &Bar = &mut thing; //~ ERROR cannot convert to a trait object
|
||||
foo(test);
|
||||
}
|
||||
|
47
src/test/compile-fail/object-safety-by-value-self.rs
Normal file
47
src/test/compile-fail/object-safety-by-value-self.rs
Normal file
@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
// Check that we correctly prevent users from making trait objects
|
||||
// from traits with a `fn(self)` method.
|
||||
|
||||
trait Bar {
|
||||
fn bar(self);
|
||||
}
|
||||
|
||||
trait Baz {
|
||||
fn baz(self: Self);
|
||||
}
|
||||
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE method `bar` has a receiver type of `Self`
|
||||
}
|
||||
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE method `bar` has a receiver type of `Self`
|
||||
}
|
||||
|
||||
fn make_baz<T:Baz>(t: &T) -> &Baz {
|
||||
t
|
||||
//~^ ERROR `Baz` is not object-safe
|
||||
//~| NOTE method `baz` has a receiver type of `Self`
|
||||
}
|
||||
|
||||
fn make_baz_explicit<T:Baz>(t: &T) -> &Baz {
|
||||
t as &Baz
|
||||
//~^ ERROR `Baz` is not object-safe
|
||||
//~| NOTE method `baz` has a receiver type of `Self`
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
@ -8,19 +8,24 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Check that object-safe methods are identified as such.
|
||||
// Check that we correctly prevent users from making trait objects
|
||||
// from traits with generic methods.
|
||||
|
||||
trait Tr {
|
||||
fn foo(&self);
|
||||
trait Bar {
|
||||
fn bar<T>(&self, t: T);
|
||||
}
|
||||
|
||||
struct St;
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE method `bar` has generic type parameters
|
||||
}
|
||||
|
||||
impl Tr for St {
|
||||
fn foo(&self) {}
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE method `bar` has generic type parameters
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s: &Tr = &St;
|
||||
s.foo();
|
||||
}
|
47
src/test/compile-fail/object-safety-mentions-Self.rs
Normal file
47
src/test/compile-fail/object-safety-mentions-Self.rs
Normal file
@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
// Check that we correctly prevent users from making trait objects
|
||||
// form traits that make use of `Self` in an argument or return position.
|
||||
|
||||
trait Bar {
|
||||
fn bar(&self, x: &Self);
|
||||
}
|
||||
|
||||
trait Baz {
|
||||
fn bar(&self) -> Self;
|
||||
}
|
||||
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE method `bar` references the `Self` type in its arguments or return type
|
||||
}
|
||||
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE method `bar` references the `Self` type in its arguments or return type
|
||||
}
|
||||
|
||||
fn make_baz<T:Baz>(t: &T) -> &Baz {
|
||||
t
|
||||
//~^ ERROR `Baz` is not object-safe
|
||||
//~| NOTE method `bar` references the `Self` type in its arguments or return type
|
||||
}
|
||||
|
||||
fn make_baz_explicit<T:Baz>(t: &T) -> &Baz {
|
||||
t as &Baz
|
||||
//~^ ERROR `Baz` is not object-safe
|
||||
//~| NOTE method `bar` references the `Self` type in its arguments or return type
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
31
src/test/compile-fail/object-safety-no-static.rs
Normal file
31
src/test/compile-fail/object-safety-no-static.rs
Normal file
@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
// Check that we correctly prevent users from making trait objects
|
||||
// from traits with static methods.
|
||||
|
||||
trait Foo {
|
||||
fn foo();
|
||||
}
|
||||
|
||||
fn foo_implicit<T:Foo+'static>(b: Box<T>) -> Box<Foo+'static> {
|
||||
b
|
||||
//~^ ERROR cannot convert to a trait object
|
||||
//~| NOTE method `foo` has no receiver
|
||||
}
|
||||
|
||||
fn foo_explicit<T:Foo+'static>(b: Box<T>) -> Box<Foo+'static> {
|
||||
b as Box<Foo>
|
||||
//~^ ERROR cannot convert to a trait object
|
||||
//~| NOTE method `foo` has no receiver
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
33
src/test/compile-fail/object-safety-sized-2.rs
Normal file
33
src/test/compile-fail/object-safety-sized-2.rs
Normal file
@ -0,0 +1,33 @@
|
||||
// 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.
|
||||
|
||||
// Check that we correctly prevent users from making trait objects
|
||||
// from traits where `Self : Sized`.
|
||||
|
||||
trait Bar
|
||||
where Self : Sized
|
||||
{
|
||||
fn bar<T>(&self, t: T);
|
||||
}
|
||||
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE the trait cannot require that `Self : Sized`
|
||||
}
|
||||
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE the trait cannot require that `Self : Sized`
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
31
src/test/compile-fail/object-safety-sized.rs
Normal file
31
src/test/compile-fail/object-safety-sized.rs
Normal file
@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
// Check that we correctly prevent users from making trait objects
|
||||
// from traits where `Self : Sized`.
|
||||
|
||||
trait Bar : Sized {
|
||||
fn bar<T>(&self, t: T);
|
||||
}
|
||||
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE the trait cannot require that `Self : Sized`
|
||||
}
|
||||
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~| NOTE the trait cannot require that `Self : Sized`
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
// 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.
|
||||
|
||||
trait Foo {
|
||||
fn foo(self);
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
fn bar(&self, x: &Self);
|
||||
}
|
||||
|
||||
trait Baz {
|
||||
fn baz<T>(&self, x: &T);
|
||||
}
|
||||
|
||||
impl Foo for int {
|
||||
fn foo(self) {}
|
||||
}
|
||||
|
||||
impl Bar for int {
|
||||
fn bar(&self, _x: &int) {}
|
||||
}
|
||||
|
||||
impl Baz for int {
|
||||
fn baz<T>(&self, _x: &T) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _: &Foo = &42i; //~ ERROR cannot convert to a trait object
|
||||
let _: &Bar = &42i; //~ ERROR cannot convert to a trait object
|
||||
let _: &Baz = &42i; //~ ERROR cannot convert to a trait object
|
||||
|
||||
let _ = &42i as &Foo; //~ ERROR cannot convert to a trait object
|
||||
let _ = &42i as &Bar; //~ ERROR cannot convert to a trait object
|
||||
let _ = &42i as &Baz; //~ ERROR cannot convert to a trait object
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
// 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.
|
||||
|
||||
|
||||
trait Foo : Sized {
|
||||
fn foo(self: Box<Self>) { bar(self as Box<Foo>); }
|
||||
}
|
||||
|
||||
fn bar(_b: Box<Foo>) { }
|
||||
|
||||
fn main() {}
|
Loading…
x
Reference in New Issue
Block a user