Skip to content

Implement new coherence orphan rules #20416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/libcollections/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/libgraphviz/maybe_owned_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ impl<'a, T: Ord> Ord for MaybeOwnedVector<'a, T> {
}

#[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
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -98,6 +100,7 @@ pub mod middle {
pub mod traits;
pub mod ty;
pub mod ty_fold;
pub mod ty_walk;
pub mod weak_lang_items;
}

Expand Down
12 changes: 9 additions & 3 deletions src/librustc/metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,9 +441,15 @@ pub fn get_impl_trait<'tcx>(cdata: Cmd,
-> Option<Rc<ty::TraitRef<'tcx>>>
{
let item_doc = lookup_item(id, cdata.data());
reader::maybe_get_doc(item_doc, tag_item_trait_ref).map(|tp| {
doc_trait_ref(tp, tcx, cdata)
})
let fam = item_family(item_doc);
match fam {
Family::Impl => {
reader::maybe_get_doc(item_doc, tag_item_trait_ref).map(|tp| {
doc_trait_ref(tp, tcx, cdata)
})
}
_ => None
}
}

pub fn get_impl_vtables<'tcx>(cdata: Cmd,
Expand Down
15 changes: 10 additions & 5 deletions src/librustc/middle/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
//! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes.
use util::nodemap::{NodeMap, DefIdMap};
use middle::ty;
use metadata::csearch;
use syntax::codemap::Span;
use syntax::{attr, visit};
use syntax::ast;
Expand All @@ -21,8 +22,8 @@ use syntax::ast::{TypeMethod, Method, Generics, StructField, TypeTraitItem};
use syntax::ast_util::is_local;
use syntax::attr::Stability;
use syntax::visit::{FnKind, FkMethod, Visitor};
use middle::ty;
use metadata::csearch;
use util::nodemap::{NodeMap, DefIdMap};
use util::ppaux::Repr;

use std::mem::replace;

Expand Down Expand Up @@ -154,10 +155,13 @@ impl Index {
/// Lookup the stability for a node, loading external crate
/// metadata as necessary.
pub fn lookup(tcx: &ty::ctxt, id: DefId) -> Option<Stability> {
debug!("lookup(id={})",
id.repr(tcx));

// is this definition the implementation of a trait method?
match ty::trait_item_of_item(tcx, id) {
Some(ty::MethodTraitItemId(trait_method_id))
if trait_method_id != id => {
Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => {
debug!("lookup: trait_method_id={}", trait_method_id);
return lookup(tcx, trait_method_id)
}
_ => {}
Expand All @@ -178,6 +182,7 @@ pub fn lookup(tcx: &ty::ctxt, id: DefId) -> Option<Stability> {
// stability of the trait to determine the stability of any
// unmarked impls for it. See FIXME above for more details.

debug!("lookup: trait_id={}", trait_id);
lookup(tcx, trait_id)
} else {
None
Expand Down
153 changes: 82 additions & 71 deletions src/librustc/middle/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ use super::SelectionContext;
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;
Expand Down Expand Up @@ -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));

Expand All @@ -63,99 +75,74 @@ 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(());
}

fn ty_reaches_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
ty.walk().any(|t| ty_is_local_constructor(tcx, t))
}

pub fn ty_is_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
debug!("ty_is_local({})", ty.repr(tcx));
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 |
ty::ty_char |
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) => {
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, _) |
ty::ty_struct(def_id, _) => {
def_id.krate == ast::LOCAL_CRATE
}

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)
}
}
})
})
}
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::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 => {
Expand All @@ -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()
}

11 changes: 2 additions & 9 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use syntax::codemap::{Span, DUMMY_SP};
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;
Expand Down Expand Up @@ -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,
Expand Down
Loading