blob: a76dfc35dc1d3897ed10589c949d19ea20a57fa5 [file] [log] [blame]
// Copyright 2012-2015 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.
use dep_graph::DepNode;
use hir::def_id::DefId;
use traits::{self, specialization_graph};
use ty;
use ty::fast_reject;
use ty::{Ty, TyCtxt, TraitRef};
use std::cell::{Cell, RefCell};
use syntax::ast::Name;
use hir;
use util::nodemap::FnvHashMap;
/// As `TypeScheme` but for a trait ref.
pub struct TraitDef<'tcx> {
pub unsafety: hir::Unsafety,
/// If `true`, then this trait had the `#[rustc_paren_sugar]`
/// attribute, indicating that it should be used with `Foo()`
/// sugar. This is a temporary thing -- eventually any trait wil
/// be usable with the sugar (or without it).
pub paren_sugar: bool,
/// Generic type definitions. Note that `Self` is listed in here
/// as having a single bound, the trait itself (e.g., in the trait
/// `Eq`, there is a single bound `Self : Eq`). This is so that
/// default methods get to assume that the `Self` parameters
/// implements the trait.
pub generics: ty::Generics<'tcx>,
pub trait_ref: ty::TraitRef<'tcx>,
/// A list of the associated types defined in this trait. Useful
/// for resolving `X::Foo` type markers.
pub associated_type_names: Vec<Name>,
// Impls of a trait. To allow for quicker lookup, the impls are indexed by a
// simplified version of their `Self` type: impls with a simplifiable `Self`
// are stored in `nonblanket_impls` keyed by it, while all other impls are
// stored in `blanket_impls`.
//
// A similar division is used within `specialization_graph`, but the ones
// here are (1) stored as a flat list for the trait and (2) populated prior
// to -- and used while -- determining specialization order.
//
// FIXME: solve the reentrancy issues and remove these lists in favor of the
// ones in `specialization_graph`.
//
// These lists are tracked by `DepNode::TraitImpls`; we don't use
// a DepTrackingMap but instead have the `TraitDef` insert the
// required reads/writes.
/// Impls of the trait.
nonblanket_impls: RefCell<
FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>
>,
/// Blanket impls associated with the trait.
blanket_impls: RefCell<Vec<DefId>>,
/// The specialization order for impls of this trait.
pub specialization_graph: RefCell<traits::specialization_graph::Graph>,
/// Various flags
pub flags: Cell<TraitFlags>
}
impl<'a, 'gcx, 'tcx> TraitDef<'tcx> {
pub fn new(unsafety: hir::Unsafety,
paren_sugar: bool,
generics: ty::Generics<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
associated_type_names: Vec<Name>)
-> TraitDef<'tcx> {
TraitDef {
paren_sugar: paren_sugar,
unsafety: unsafety,
generics: generics,
trait_ref: trait_ref,
associated_type_names: associated_type_names,
nonblanket_impls: RefCell::new(FnvHashMap()),
blanket_impls: RefCell::new(vec![]),
flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS),
specialization_graph: RefCell::new(traits::specialization_graph::Graph::new()),
}
}
pub fn def_id(&self) -> DefId {
self.trait_ref.def_id
}
// returns None if not yet calculated
pub fn object_safety(&self) -> Option<bool> {
if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) {
Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE))
} else {
None
}
}
pub fn set_object_safety(&self, is_safe: bool) {
assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true));
self.flags.set(
self.flags.get() | if is_safe {
TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE
} else {
TraitFlags::OBJECT_SAFETY_VALID
}
);
}
fn write_trait_impls(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) {
tcx.dep_graph.write(DepNode::TraitImpls(self.trait_ref.def_id));
}
fn read_trait_impls(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) {
tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id));
}
/// Records a basic trait-to-implementation mapping.
///
/// Returns `true` iff the impl has not previously been recorded.
fn record_impl(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
impl_def_id: DefId,
impl_trait_ref: TraitRef<'tcx>)
-> bool {
debug!("TraitDef::record_impl for {:?}, from {:?}",
self, impl_trait_ref);
// Record the write into the impl set, but only for local
// impls: external impls are handled differently.
if impl_def_id.is_local() {
self.write_trait_impls(tcx);
}
// We don't want to borrow_mut after we already populated all impls,
// so check if an impl is present with an immutable borrow first.
if let Some(sty) = fast_reject::simplify_type(tcx,
impl_trait_ref.self_ty(), false) {
if let Some(is) = self.nonblanket_impls.borrow().get(&sty) {
if is.contains(&impl_def_id) {
return false; // duplicate - skip
}
}
self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id)
} else {
if self.blanket_impls.borrow().contains(&impl_def_id) {
return false; // duplicate - skip
}
self.blanket_impls.borrow_mut().push(impl_def_id)
}
true
}
/// Records a trait-to-implementation mapping for a crate-local impl.
pub fn record_local_impl(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
impl_def_id: DefId,
impl_trait_ref: TraitRef<'tcx>) {
assert!(impl_def_id.is_local());
let was_new = self.record_impl(tcx, impl_def_id, impl_trait_ref);
assert!(was_new);
}
/// Records a trait-to-implementation mapping for a non-local impl.
///
/// The `parent_impl` is the immediately-less-specialized impl, or the
/// trait's def ID if the impl is not a specialization -- information that
/// should be pulled from the metadata.
pub fn record_remote_impl(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
impl_def_id: DefId,
impl_trait_ref: TraitRef<'tcx>,
parent_impl: DefId) {
assert!(!impl_def_id.is_local());
// if the impl has not previously been recorded
if self.record_impl(tcx, impl_def_id, impl_trait_ref) {
// if the impl is non-local, it's placed directly into the
// specialization graph using parent information drawn from metadata.
self.specialization_graph.borrow_mut()
.record_impl_from_cstore(tcx, parent_impl, impl_def_id)
}
}
/// Adds a local impl into the specialization graph, returning an error with
/// overlap information if the impl overlaps but does not specialize an
/// existing impl.
pub fn add_impl_for_specialization(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
impl_def_id: DefId)
-> Result<(), traits::OverlapError> {
assert!(impl_def_id.is_local());
self.specialization_graph.borrow_mut()
.insert(tcx, impl_def_id)
}
pub fn ancestors(&'a self, of_impl: DefId) -> specialization_graph::Ancestors<'a, 'tcx> {
specialization_graph::ancestors(self, of_impl)
}
pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, mut f: F) {
self.read_trait_impls(tcx);
tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
for &impl_def_id in self.blanket_impls.borrow().iter() {
f(impl_def_id);
}
for v in self.nonblanket_impls.borrow().values() {
for &impl_def_id in v {
f(impl_def_id);
}
}
}
/// Iterate over every impl that could possibly match the
/// self-type `self_ty`.
pub fn for_each_relevant_impl<F: FnMut(DefId)>(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
self_ty: Ty<'tcx>,
mut f: F)
{
self.read_trait_impls(tcx);
tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
for &impl_def_id in self.blanket_impls.borrow().iter() {
f(impl_def_id);
}
// simplify_type(.., false) basically replaces type parameters and
// projections with infer-variables. This is, of course, done on
// the impl trait-ref when it is instantiated, but not on the
// predicate trait-ref which is passed here.
//
// for example, if we match `S: Copy` against an impl like
// `impl<T:Copy> Copy for Option<T>`, we replace the type variable
// in `Option<T>` with an infer variable, to `Option<_>` (this
// doesn't actually change fast_reject output), but we don't
// replace `S` with anything - this impl of course can't be
// selected, and as there are hundreds of similar impls,
// considering them would significantly harm performance.
if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) {
if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) {
for &impl_def_id in impls {
f(impl_def_id);
}
}
} else {
for v in self.nonblanket_impls.borrow().values() {
for &impl_def_id in v {
f(impl_def_id);
}
}
}
}
}
bitflags! {
flags TraitFlags: u32 {
const NO_TRAIT_FLAGS = 0,
const HAS_DEFAULT_IMPL = 1 << 0,
const IS_OBJECT_SAFE = 1 << 1,
const OBJECT_SAFETY_VALID = 1 << 2,
const IMPLS_VALID = 1 << 3,
}
}