blob: 14b0a8070983c84a3628b665e1d07648689768ea [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 hir::def_id::{DefId};
use ty::{self, Ty, TyCtxt};
use util::common::MemoizationMap;
use util::nodemap::FnvHashMap;
use std::fmt;
use std::ops;
use syntax::ast;
/// Type contents is how the type checker reasons about kinds.
/// They track what kinds of things are found within a type. You can
/// think of them as kind of an "anti-kind". They track the kinds of values
/// and thinks that are contained in types. Having a larger contents for
/// a type tends to rule that type *out* from various kinds. For example,
/// a type that contains a reference is not sendable.
///
/// The reason we compute type contents and not kinds is that it is
/// easier for me (nmatsakis) to think about what is contained within
/// a type than to think about what is *not* contained within a type.
#[derive(Clone, Copy)]
pub struct TypeContents {
pub bits: u64
}
macro_rules! def_type_content_sets {
(mod $mname:ident { $($name:ident = $bits:expr),+ }) => {
#[allow(non_snake_case)]
mod $mname {
use super::TypeContents;
$(
#[allow(non_upper_case_globals)]
pub const $name: TypeContents = TypeContents { bits: $bits };
)+
}
}
}
def_type_content_sets! {
mod TC {
None = 0b0000_0000__0000_0000__0000,
// Things that are interior to the value (first nibble):
InteriorUnsafe = 0b0000_0000__0000_0000__0010,
InteriorParam = 0b0000_0000__0000_0000__0100,
// InteriorAll = 0b00000000__00000000__1111,
// Things that are owned by the value (second and third nibbles):
OwnsOwned = 0b0000_0000__0000_0001__0000,
OwnsDtor = 0b0000_0000__0000_0010__0000,
OwnsAll = 0b0000_0000__1111_1111__0000,
// Things that mean drop glue is necessary
NeedsDrop = 0b0000_0000__0000_0111__0000,
// All bits
All = 0b1111_1111__1111_1111__1111
}
}
impl TypeContents {
pub fn when(&self, cond: bool) -> TypeContents {
if cond {*self} else {TC::None}
}
pub fn intersects(&self, tc: TypeContents) -> bool {
(self.bits & tc.bits) != 0
}
pub fn owns_owned(&self) -> bool {
self.intersects(TC::OwnsOwned)
}
pub fn interior_param(&self) -> bool {
self.intersects(TC::InteriorParam)
}
pub fn interior_unsafe(&self) -> bool {
self.intersects(TC::InteriorUnsafe)
}
pub fn needs_drop(&self, _: TyCtxt) -> bool {
self.intersects(TC::NeedsDrop)
}
/// Includes only those bits that still apply when indirected through a `Box` pointer
pub fn owned_pointer(&self) -> TypeContents {
TC::OwnsOwned | (*self & TC::OwnsAll)
}
pub fn union<T, F>(v: &[T], mut f: F) -> TypeContents where
F: FnMut(&T) -> TypeContents,
{
v.iter().fold(TC::None, |tc, ty| tc | f(ty))
}
pub fn has_dtor(&self) -> bool {
self.intersects(TC::OwnsDtor)
}
}
impl ops::BitOr for TypeContents {
type Output = TypeContents;
fn bitor(self, other: TypeContents) -> TypeContents {
TypeContents {bits: self.bits | other.bits}
}
}
impl ops::BitAnd for TypeContents {
type Output = TypeContents;
fn bitand(self, other: TypeContents) -> TypeContents {
TypeContents {bits: self.bits & other.bits}
}
}
impl ops::Sub for TypeContents {
type Output = TypeContents;
fn sub(self, other: TypeContents) -> TypeContents {
TypeContents {bits: self.bits & !other.bits}
}
}
impl fmt::Debug for TypeContents {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TypeContents({:b})", self.bits)
}
}
impl<'a, 'tcx> ty::TyS<'tcx> {
pub fn type_contents(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> TypeContents {
return tcx.tc_cache.memoize(self, || tc_ty(tcx, self, &mut FnvHashMap()));
fn tc_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty: Ty<'tcx>,
cache: &mut FnvHashMap<Ty<'tcx>, TypeContents>) -> TypeContents
{
// Subtle: Note that we are *not* using tcx.tc_cache here but rather a
// private cache for this walk. This is needed in the case of cyclic
// types like:
//
// struct List { next: Box<Option<List>>, ... }
//
// When computing the type contents of such a type, we wind up deeply
// recursing as we go. So when we encounter the recursive reference
// to List, we temporarily use TC::None as its contents. Later we'll
// patch up the cache with the correct value, once we've computed it
// (this is basically a co-inductive process, if that helps). So in
// the end we'll compute TC::OwnsOwned, in this case.
//
// The problem is, as we are doing the computation, we will also
// compute an *intermediate* contents for, e.g., Option<List> of
// TC::None. This is ok during the computation of List itself, but if
// we stored this intermediate value into tcx.tc_cache, then later
// requests for the contents of Option<List> would also yield TC::None
// which is incorrect. This value was computed based on the crutch
// value for the type contents of list. The correct value is
// TC::OwnsOwned. This manifested as issue #4821.
if let Some(tc) = cache.get(&ty) {
return *tc;
}
// Must check both caches!
if let Some(tc) = tcx.tc_cache.borrow().get(&ty) {
return *tc;
}
cache.insert(ty, TC::None);
let result = match ty.sty {
// usize and isize are ffi-unsafe
ty::TyUint(ast::UintTy::Us) | ty::TyInt(ast::IntTy::Is) => {
TC::None
}
// Scalar and unique types are sendable, and durable
ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) |
ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar => {
TC::None
}
ty::TyBox(typ) => {
tc_ty(tcx, typ, cache).owned_pointer()
}
ty::TyTrait(_) => {
TC::All - TC::InteriorParam
}
ty::TyRawPtr(_) => {
TC::None
}
ty::TyRef(_, _) => {
TC::None
}
ty::TyArray(ty, _) => {
tc_ty(tcx, ty, cache)
}
ty::TySlice(ty) => {
tc_ty(tcx, ty, cache)
}
ty::TyStr => TC::None,
ty::TyClosure(_, ref substs) => {
TypeContents::union(&substs.upvar_tys, |ty| tc_ty(tcx, &ty, cache))
}
ty::TyTuple(ref tys) => {
TypeContents::union(&tys[..],
|ty| tc_ty(tcx, *ty, cache))
}
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
let mut res =
TypeContents::union(&def.variants, |v| {
TypeContents::union(&v.fields, |f| {
tc_ty(tcx, f.ty(tcx, substs), cache)
})
});
if def.has_dtor() {
res = res | TC::OwnsDtor;
}
apply_lang_items(tcx, def.did, res)
}
ty::TyProjection(..) |
ty::TyParam(_) => {
TC::All
}
ty::TyInfer(_) |
ty::TyError => {
bug!("asked to compute contents of error type");
}
};
cache.insert(ty, result);
result
}
fn apply_lang_items<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
did: DefId, tc: TypeContents)
-> TypeContents {
if Some(did) == tcx.lang_items.unsafe_cell_type() {
tc | TC::InteriorUnsafe
} else {
tc
}
}
}
}