278 lines
10 KiB
Rust
278 lines
10 KiB
Rust
// 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,
|
|
}
|
|
}
|