Fix orphan checking (cc #19470). (This is not a complete fix of #19470 because of the backwards compatibility feature gate.)

This is a [breaking-change]. The new rules require that, for an impl of a trait defined
in some other crate, two conditions must hold:

1. Some type must be local.
2. Every type parameter must appear "under" some local type.

Here are some examples that are legal:

```rust
struct MyStruct<T> { ... }

// Here `T` appears "under' `MyStruct`.
impl<T> Clone for MyStruct<T> { }

// Here `T` appears "under' `MyStruct` as well. Note that it also appears
// elsewhere.
impl<T> Iterator<T> for MyStruct<T> { }
```

Here is an illegal example:

```rust
// Here `U` does not appear "under" `MyStruct` or any other local type.
// We call `U` "uncovered".
impl<T,U> Iterator<U> for MyStruct<T> { }
```

There are a couple of ways to rewrite this last example so that it is
legal:

1. In some cases, the uncovered type parameter (here, `U`) should be converted
   into an associated type. This is however a non-local change that requires access
   to the original trait. Also, associated types are not fully baked.
2. Add `U` as a type parameter of `MyStruct`:
   ```rust
   struct MyStruct<T,U> { ... }
   impl<T,U> Iterator<U> for MyStruct<T,U> { }
   ```
3. Create a newtype wrapper for `U`
   ```rust
   impl<T,U> Iterator<Wrapper<U>> for MyStruct<T,U> { }
   ```

Because associated types are not fully baked, which in the case of the
`Hash` trait makes adhering to this rule impossible, you can
temporarily disable this rule in your crate by using
`#![feature(old_orphan_check)]`. Note that the `old_orphan_check`
feature will be removed before 1.0 is released.
This commit is contained in:
Niko Matsakis 2014-12-26 03:30:51 -05:00
parent 77723d24a8
commit c61a0092bc
26 changed files with 172 additions and 96 deletions

View File

@ -64,7 +64,8 @@
html_root_url = "http://doc.rust-lang.org/nightly/")]
#![no_std]
#![feature(lang_items, phase, unsafe_destructor, default_type_params)]
#![allow(unknown_features)]
#![feature(lang_items, phase, unsafe_destructor, default_type_params, old_orphan_check)]
#[phase(plugin, link)]
extern crate core;

View File

@ -25,6 +25,7 @@
#![feature(macro_rules, default_type_params, phase, globs)]
#![feature(unsafe_destructor, slicing_syntax)]
#![feature(unboxed_closures)]
#![feature(old_orphan_check)]
#![no_std]
#[phase(plugin, link)] extern crate core;

View File

@ -96,9 +96,9 @@ fn cmp(&self, other: &MaybeOwnedVector<T>) -> Ordering {
}
#[allow(deprecated)]
impl<'a, T: PartialEq, Sized? V: AsSlice<T>> Equiv<V> for MaybeOwnedVector<'a, T> {
fn equiv(&self, other: &V) -> bool {
self.as_slice() == other.as_slice()
impl<'a, T: PartialEq> Equiv<[T]> for MaybeOwnedVector<'a, T> {
fn equiv(&self, other: &[T]) -> bool {
self.as_slice() == other
}
}

View File

@ -22,10 +22,12 @@
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://doc.rust-lang.org/nightly/")]
#![allow(unknown_features)]
#![feature(default_type_params, globs, macro_rules, phase, quote)]
#![feature(slicing_syntax, unsafe_destructor)]
#![feature(rustc_diagnostic_macros)]
#![feature(unboxed_closures)]
#![feature(old_orphan_check)]
extern crate arena;
extern crate flate;

View File

@ -14,10 +14,10 @@
use super::{Obligation, ObligationCause};
use super::util;
use middle::subst;
use middle::subst::Subst;
use middle::ty::{mod, Ty};
use middle::infer::InferCtxt;
use std::collections::HashSet;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::DUMMY_SP;
@ -52,9 +52,21 @@ pub fn impl_can_satisfy(infcx: &InferCtxt,
selcx.evaluate_impl(impl2_def_id, &obligation)
}
pub fn impl_is_local(tcx: &ty::ctxt,
impl_def_id: ast::DefId)
-> bool
#[allow(missing_copy_implementations)]
pub enum OrphanCheckErr {
NoLocalInputType,
UncoveredTypeParameter(ty::ParamTy),
}
/// Checks the coherence orphan rules. `impl_def_id` should be the
/// def-id of a trait impl. To pass, either the trait must be local, or else
/// two conditions must be satisfied:
///
/// 1. At least one of the input types must involve a local type.
/// 2. All type parameters must be covered by a local type.
pub fn orphan_check(tcx: &ty::ctxt,
impl_def_id: ast::DefId)
-> Result<(), OrphanCheckErr>
{
debug!("impl_is_local({})", impl_def_id.repr(tcx));
@ -63,20 +75,40 @@ pub fn impl_is_local(tcx: &ty::ctxt,
let trait_ref = ty::impl_trait_ref(tcx, impl_def_id).unwrap();
debug!("trait_ref={}", trait_ref.repr(tcx));
// If the trait is local to the crate, ok.
// If the *trait* is local to the crate, ok.
if trait_ref.def_id.krate == ast::LOCAL_CRATE {
debug!("trait {} is local to current crate",
trait_ref.def_id.repr(tcx));
return true;
return Ok(());
}
// Otherwise, at least one of the input types must be local to the
// crate.
trait_ref.input_types().iter().any(|&t| ty_is_local(tcx, t))
// Check condition 1: at least one type must be local.
if !trait_ref.input_types().iter().any(|&t| ty_reaches_local(tcx, t)) {
return Err(OrphanCheckErr::NoLocalInputType);
}
// Check condition 2: type parameters must be "covered" by a local type.
let covered_params: HashSet<_> =
trait_ref.input_types().iter()
.flat_map(|&t| type_parameters_covered_by_ty(tcx, t).into_iter())
.collect();
let all_params: HashSet<_> =
trait_ref.input_types().iter()
.flat_map(|&t| type_parameters_reachable_from_ty(t).into_iter())
.collect();
for &param in all_params.difference(&covered_params) {
return Err(OrphanCheckErr::UncoveredTypeParameter(param));
}
return Ok(());
}
pub fn ty_is_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
debug!("ty_is_local({})", ty.repr(tcx));
fn ty_reaches_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
ty.walk().any(|t| ty_is_local_constructor(tcx, t))
}
fn ty_is_local_constructor<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
debug!("ty_is_local_constructor({})", ty.repr(tcx));
match ty.sty {
ty::ty_bool |
@ -84,78 +116,33 @@ pub fn ty_is_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
ty::ty_int(..) |
ty::ty_uint(..) |
ty::ty_float(..) |
ty::ty_str(..) => {
false
}
ty::ty_unboxed_closure(..) => {
// This routine is invoked on types specified by users as
// part of an impl and hence an unboxed closure type
// cannot appear.
tcx.sess.bug("ty_is_local applied to unboxed closure type")
}
ty::ty_str(..) |
ty::ty_bare_fn(..) |
ty::ty_closure(..) => {
ty::ty_closure(..) |
ty::ty_vec(..) |
ty::ty_ptr(..) |
ty::ty_rptr(..) |
ty::ty_tup(..) |
ty::ty_param(..) |
ty::ty_projection(..) => {
false
}
ty::ty_uniq(t) => {
ty::ty_enum(def_id, _) |
ty::ty_struct(def_id, _) => {
def_id.krate == ast::LOCAL_CRATE
}
ty::ty_uniq(_) => { // treat ~T like Box<T>
let krate = tcx.lang_items.owned_box().map(|d| d.krate);
krate == Some(ast::LOCAL_CRATE) || ty_is_local(tcx, t)
}
ty::ty_vec(t, _) |
ty::ty_ptr(ty::mt { ty: t, .. }) |
ty::ty_rptr(_, ty::mt { ty: t, .. }) => {
ty_is_local(tcx, t)
}
ty::ty_tup(ref ts) => {
ts.iter().any(|&t| ty_is_local(tcx, t))
}
ty::ty_enum(def_id, ref substs) |
ty::ty_struct(def_id, ref substs) => {
def_id.krate == ast::LOCAL_CRATE || {
let variances = ty::item_variances(tcx, def_id);
subst::ParamSpace::all().iter().any(|&space| {
substs.types.get_slice(space).iter().enumerate().any(
|(i, &t)| {
match *variances.types.get(space, i) {
ty::Bivariant => {
// If Foo<T> is bivariant with respect to
// T, then it doesn't matter whether T is
// local or not, because `Foo<U>` for any
// U will be a subtype of T.
false
}
ty::Contravariant |
ty::Covariant |
ty::Invariant => {
ty_is_local(tcx, t)
}
}
})
})
}
krate == Some(ast::LOCAL_CRATE)
}
ty::ty_trait(ref tt) => {
tt.principal_def_id().krate == ast::LOCAL_CRATE
}
// Type parameters may be bound to types that are not local to
// the crate.
ty::ty_param(..) => {
false
}
// Associated types could be anything, I guess.
ty::ty_projection(..) => {
false
}
ty::ty_unboxed_closure(..) |
ty::ty_infer(..) |
ty::ty_open(..) |
ty::ty_err => {
@ -165,3 +152,27 @@ pub fn ty_is_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
}
}
}
fn type_parameters_covered_by_ty<'tcx>(tcx: &ty::ctxt<'tcx>,
ty: Ty<'tcx>)
-> HashSet<ty::ParamTy>
{
if ty_is_local_constructor(tcx, ty) {
type_parameters_reachable_from_ty(ty)
} else {
ty.walk_children().flat_map(|t| type_parameters_covered_by_ty(tcx, t).into_iter()).collect()
}
}
/// All type parameters reachable from `ty`
fn type_parameters_reachable_from_ty<'tcx>(ty: Ty<'tcx>) -> HashSet<ty::ParamTy> {
ty.walk()
.filter_map(|t| {
match t.sty {
ty::ty_param(ref param_ty) => Some(param_ty.clone()),
_ => None,
}
})
.collect()
}

View File

@ -25,6 +25,8 @@
use util::ppaux::Repr;
pub use self::error_reporting::report_fulfillment_errors;
pub use self::coherence::orphan_check;
pub use self::coherence::OrphanCheckErr;
pub use self::fulfill::{FulfillmentContext, RegionObligation};
pub use self::project::MismatchedProjectionTypes;
pub use self::project::normalize;
@ -245,15 +247,6 @@ pub struct VtableBuiltinData<N> {
pub nested: subst::VecPerParamSpace<N>
}
/// True if neither the trait nor self type is local. Note that `impl_def_id` must refer to an impl
/// of a trait, not an inherent impl.
pub fn is_orphan_impl(tcx: &ty::ctxt,
impl_def_id: ast::DefId)
-> bool
{
!coherence::impl_is_local(tcx, impl_def_id)
}
/// True if there exist types that satisfy both of the two given impls.
pub fn overlapping_impls(infcx: &InferCtxt,
impl1_def_id: ast::DefId,

View File

@ -32,6 +32,7 @@
#![allow(unknown_features)]
#![feature(globs, phase, macro_rules, slicing_syntax)]
#![feature(unboxed_closures)]
#![feature(old_orphan_check)]
#[phase(plugin, link)]
extern crate log;

View File

@ -16,10 +16,12 @@
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://doc.rust-lang.org/nightly/")]
#![allow(unknown_features)]
#![feature(default_type_params, globs, macro_rules, phase, quote)]
#![feature(slicing_syntax, unsafe_destructor)]
#![feature(rustc_diagnostic_macros)]
#![feature(unboxed_closures)]
#![feature(old_orphan_check)]
#![allow(non_camel_case_types)]
#[phase(plugin, link)] extern crate log;

View File

@ -22,10 +22,12 @@
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://doc.rust-lang.org/nightly/")]
#![allow(unknown_features)]
#![feature(default_type_params, globs, macro_rules, phase, quote)]
#![feature(slicing_syntax, unsafe_destructor)]
#![feature(rustc_diagnostic_macros)]
#![feature(unboxed_closures)]
#![feature(old_orphan_check)]
extern crate arena;
extern crate flate;

View File

@ -18,7 +18,7 @@
use syntax::ast_util;
use syntax::codemap::Span;
use syntax::visit;
use util::ppaux::Repr;
use util::ppaux::{Repr, UserString};
pub fn check(tcx: &ty::ctxt) {
let mut orphan = OrphanChecker { tcx: tcx };
@ -67,10 +67,27 @@ fn visit_item(&mut self, item: &'v ast::Item) {
ast::ItemImpl(_, _, Some(_), _, _) => {
// "Trait" impl
debug!("coherence2::orphan check: trait impl {}", item.repr(self.tcx));
if traits::is_orphan_impl(self.tcx, def_id) {
span_err!(self.tcx.sess, item.span, E0117,
"cannot provide an extension implementation \
where both trait and type are not defined in this crate");
match traits::orphan_check(self.tcx, def_id) {
Ok(()) => { }
Err(traits::OrphanCheckErr::NoLocalInputType) => {
span_err!(self.tcx.sess, item.span, E0117,
"cannot provide an extension implementation \
where both trait and type are not defined in this crate");
}
Err(traits::OrphanCheckErr::UncoveredTypeParameter(param_ty)) => {
if !self.tcx.sess.features.borrow().old_orphan_check {
self.tcx.sess.span_err(
item.span,
format!("type parameter `{}` must also appear as a type parameter \
of some type defined within this crate",
param_ty.user_string(self.tcx)).as_slice());
self.tcx.sess.span_note(
item.span,
format!("for a limited time, you can add \
`#![feature(old_orphan_check)]` to your crate \
to disable this rule").as_slice());
}
}
}
}
_ => {

View File

@ -20,6 +20,7 @@
#![allow(unknown_features)]
#![feature(globs, macro_rules, phase, slicing_syntax)]
#![feature(unboxed_closures)]
#![feature(old_orphan_check)]
extern crate arena;
extern crate getopts;

View File

@ -81,7 +81,7 @@
//! use serialize::json;
//!
//! // Automatically generate `Decodable` and `Encodable` trait implementations
//! #[deriving(Decodable, Encodable)]
//! #[deriving(RustcDecodable, RustcEncodable)]
//! pub struct TestStruct {
//! data_int: u8,
//! data_str: String,

View File

@ -107,6 +107,7 @@
#![feature(macro_rules, globs, linkage, thread_local, asm)]
#![feature(default_type_params, phase, lang_items, unsafe_destructor)]
#![feature(slicing_syntax, unboxed_closures)]
#![feature(old_orphan_check)]
// Don't link to std. We are std.
#![no_std]

View File

@ -77,8 +77,11 @@
// to bootstrap fix for #5723.
("issue_5723_bootstrap", Accepted),
// A way to temporary opt out of opt in copy. This will *never* be accepted.
("opt_out_copy", Active),
// A way to temporarily opt out of opt in copy. This will *never* be accepted.
("opt_out_copy", Deprecated),
// A way to temporarily opt out of the new orphan rules. This will *never* be accepted.
("old_orphan_check", Deprecated),
// These are used to test this portion of the compiler, they don't actually
// mean anything
@ -91,6 +94,10 @@ enum Status {
/// currently being considered for addition/removal.
Active,
/// Represents a feature gate that is temporarily enabling deprecated behavior.
/// This gate will never be accepted.
Deprecated,
/// Represents a feature which has since been removed (it was once Active)
Removed,
@ -108,6 +115,7 @@ pub struct Features {
pub visible_private_types: bool,
pub quote: bool,
pub opt_out_copy: bool,
pub old_orphan_check: bool,
}
impl Features {
@ -120,6 +128,7 @@ pub fn new() -> Features {
visible_private_types: false,
quote: false,
opt_out_copy: false,
old_orphan_check: false,
}
}
}
@ -442,7 +451,16 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
};
match KNOWN_FEATURES.iter()
.find(|& &(n, _)| name == n) {
Some(&(name, Active)) => { cx.features.push(name); }
Some(&(name, Active)) => {
cx.features.push(name);
}
Some(&(name, Deprecated)) => {
cx.features.push(name);
span_handler.span_warn(
mi.span,
"feature is deprecated and will only be available \
for a limited time, please rewrite code that relies on it");
}
Some(&(_, Removed)) => {
span_handler.span_err(mi.span, "feature has been removed");
}
@ -469,6 +487,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
visible_private_types: cx.has_feature("visible_private_types"),
quote: cx.has_feature("quote"),
opt_out_copy: cx.has_feature("opt_out_copy"),
old_orphan_check: cx.has_feature("old_orphan_check"),
},
unknown_features)
}

View File

@ -26,6 +26,7 @@
#![feature(macro_rules, globs, default_type_params, phase, slicing_syntax)]
#![feature(quote, unsafe_destructor)]
#![feature(unboxed_closures)]
#![feature(old_orphan_check)]
extern crate arena;
extern crate fmt_macros;

View File

@ -31,8 +31,10 @@
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://doc.rust-lang.org/nightly/")]
#![allow(unknown_features)]
#![feature(asm, macro_rules, phase, globs, slicing_syntax)]
#![feature(unboxed_closures, default_type_params)]
#![feature(old_orphan_check)]
extern crate getopts;
extern crate regex;

View File

@ -20,7 +20,10 @@
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://doc.rust-lang.org/nightly/",
html_playground_url = "http://play.rust-lang.org/")]
#![allow(unknown_features)]
#![feature(phase, globs)]
#![feature(old_orphan_check)]
#[cfg(test)] #[phase(plugin, link)] extern crate log;

View File

@ -9,6 +9,8 @@
// except according to those terms.
#![feature(opt_out_copy)]
//~^ WARNING feature is deprecated
//~| WARNING feature is deprecated
// Test that when using the `opt-out-copy` feature we still consider
// destructors to be non-movable

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(old_orphan_check)]
extern crate serialize;
use serialize::{Encodable, Decodable};

View File

@ -11,6 +11,8 @@
// This briefly tests the capability of `Cell` and `RefCell` to implement the
// `Encodable` and `Decodable` traits via `#[deriving(Encodable, Decodable)]`
#![feature(old_orphan_check)]
extern crate serialize;
use std::cell::{Cell, RefCell};

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(old_orphan_check)]
extern crate serialize;
extern crate rand;

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(old_orphan_check)]
extern crate rbml;
extern crate serialize;

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(old_orphan_check)]
extern crate serialize;

View File

@ -8,6 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// If `Index` used an associated type for its output, this test would
// work more smoothly.
#![feature(old_orphan_check)]
struct Mat<T> { data: Vec<T>, cols: uint, }
impl<T> Mat<T> {

View File

@ -8,6 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// If `Mul` used an associated type for its output, this test would
// work more smoothly.
#![feature(old_orphan_check)]
struct Vec2 {
x: f64,
y: f64

View File

@ -14,9 +14,9 @@
use std::ops::Fn;
struct G;
struct G<A>;
impl<'a, A: Add<int, int>> Fn<(A,), int> for G {
impl<'a, A: Add<int, int>> Fn<(A,), int> for G<A> {
extern "rust-call" fn call(&self, (arg,): (A,)) -> int {
arg.add(1)
}