#![stable(feature = "rust1", since = "1.0.0")]
+use cell::UnsafeCell;
use cmp;
use hash::Hash;
use hash::Hasher;
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<'a, T: Send + ?Sized> Send for &'a mut T {}
}
+
+/// Compiler-internal trait used to determine whether a type contains
+/// any `UnsafeCell` internally, but not through an indirection.
+/// This affects, for example, whether a `static` of that type is
+/// placed in read-only static memory or writable static memory.
+#[cfg_attr(not(stage0), lang = "freeze")]
+unsafe trait Freeze {}
+
+unsafe impl Freeze for .. {}
+
+impl<T: ?Sized> !Freeze for UnsafeCell<T> {}
+unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
+unsafe impl<T: ?Sized> Freeze for *const T {}
+unsafe impl<T: ?Sized> Freeze for *mut T {}
+unsafe impl<'a, T: ?Sized> Freeze for &'a T {}
+unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {}
UnsizeTraitLangItem, "unsize", unsize_trait;
CopyTraitLangItem, "copy", copy_trait;
SyncTraitLangItem, "sync", sync_trait;
+ FreezeTraitLangItem, "freeze", freeze_trait;
DropTraitLangItem, "drop", drop_trait;
/// easier for me (nmatsakis) to think about what is contained within
/// a type than to think about what is *not* contained within a type.
flags TypeContents: u8 {
- const INTERIOR_UNSAFE = 0b01,
- const OWNS_DTOR = 0b10,
+ const OWNS_DTOR = 0b1,
}
}
if cond {*self} else {TypeContents::empty()}
}
- pub fn interior_unsafe(&self) -> bool {
- self.intersects(TypeContents::INTERIOR_UNSAFE)
- }
-
pub fn needs_drop(&self, _: TyCtxt) -> bool {
self.intersects(TypeContents::OWNS_DTOR)
}
// unions don't have destructors regardless of the child types
- TypeContents::OWNS_DTOR.when(def.is_union())
| TypeContents::OWNS_DTOR.when(def.has_dtor(tcx))
- | TypeContents::INTERIOR_UNSAFE.when(
- Some(def.did) == tcx.lang_items.unsafe_cell_type())
}
-
ty::TyDynamic(..) |
ty::TyProjection(..) |
ty::TyParam(_) |
- ty::TyAnon(..) => {
- TypeContents::INTERIOR_UNSAFE | TypeContents::OWNS_DTOR
- }
+ ty::TyAnon(..) => TypeContents::OWNS_DTOR,
ty::TyInfer(_) |
ty::TyError => {
const IS_SIZED = 1 << 17,
const MOVENESS_CACHED = 1 << 18,
const MOVES_BY_DEFAULT = 1 << 19,
+ const FREEZENESS_CACHED = 1 << 20,
+ const IS_FREEZE = 1 << 21,
}
}
/// A cache for `type_is_sized`
pub is_sized_cache: RefCell<FxHashMap<Ty<'tcx>, bool>>,
+
+ /// A cache for `type_is_freeze`
+ pub is_freeze_cache: RefCell<FxHashMap<Ty<'tcx>, bool>>,
}
impl<'a, 'tcx> ParameterEnvironment<'tcx> {
free_id_outlive: self.free_id_outlive,
is_copy_cache: RefCell::new(FxHashMap()),
is_sized_cache: RefCell::new(FxHashMap()),
+ is_freeze_cache: RefCell::new(FxHashMap()),
}
}
free_id_outlive: free_id_outlive,
is_copy_cache: RefCell::new(FxHashMap()),
is_sized_cache: RefCell::new(FxHashMap()),
+ is_freeze_cache: RefCell::new(FxHashMap()),
}
}
free_id_outlive: free_id_outlive,
is_copy_cache: RefCell::new(FxHashMap()),
is_sized_cache: RefCell::new(FxHashMap()),
+ is_freeze_cache: RefCell::new(FxHashMap()),
};
let cause = traits::ObligationCause::misc(span, free_id_outlive.node_id(&self.region_maps));
result
}
+ /// Returns `true` if and only if there are no `UnsafeCell`s
+ /// nested within the type (ignoring `PhantomData` or pointers).
+ #[inline]
+ pub fn is_freeze(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ param_env: &ParameterEnvironment<'tcx>,
+ span: Span) -> bool
+ {
+ if self.flags.get().intersects(TypeFlags::FREEZENESS_CACHED) {
+ return self.flags.get().intersects(TypeFlags::IS_FREEZE);
+ }
+
+ self.is_freeze_uncached(tcx, param_env, span)
+ }
+
+ fn is_freeze_uncached(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ param_env: &ParameterEnvironment<'tcx>,
+ span: Span) -> bool {
+ assert!(!self.needs_infer());
+
+ // Fast-path for primitive types
+ let result = match self.sty {
+ TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
+ TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) |
+ TyStr | TyNever => Some(true),
+
+ TyArray(..) | TySlice(_) |
+ TyTuple(..) | TyClosure(..) | TyAdt(..) |
+ TyDynamic(..) | TyProjection(..) | TyParam(..) |
+ TyInfer(..) | TyAnon(..) | TyError => None
+ }.unwrap_or_else(|| {
+ self.impls_bound(tcx, param_env, tcx.require_lang_item(lang_items::FreezeTraitLangItem),
+ ¶m_env.is_freeze_cache, span) });
+
+ if !self.has_param_types() && !self.has_self_ty() {
+ self.flags.set(self.flags.get() | if result {
+ TypeFlags::FREEZENESS_CACHED | TypeFlags::IS_FREEZE
+ } else {
+ TypeFlags::FREEZENESS_CACHED
+ });
+ }
+
+ result
+ }
+
#[inline]
pub fn layout<'lcx>(&'tcx self, infcx: &InferCtxt<'a, 'tcx, 'lcx>)
-> Result<&'tcx Layout, LayoutError<'tcx>> {
fn restrict(&mut self, ty: Ty<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: &ty::ParameterEnvironment<'tcx>) {
- if !ty.type_contents(tcx).interior_unsafe() {
+ if ty.is_freeze(tcx, param_env, DUMMY_SP) {
*self = *self - Qualif::MUTABLE_INTERIOR;
}
if !tcx.type_needs_drop_given_env(ty, param_env) {
use rustc::hir::{self, PatKind, RangeEnd};
use syntax::ast;
-use syntax_pos::Span;
+use syntax_pos::{Span, DUMMY_SP};
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
use std::collections::hash_map::Entry;
// Adds the worst effect out of all the values of one type.
fn add_type(&mut self, ty: Ty<'gcx>) {
- if ty.type_contents(self.tcx).interior_unsafe() {
+ if !ty.is_freeze(self.tcx, &self.param_env, DUMMY_SP) {
self.promotable = false;
}
// `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as
// both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely
// on memory dependencies rather than pointer equality
- let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe();
+ let is_freeze = ccx.shared().type_is_freeze(mt.ty);
- if mt.mutbl != hir::MutMutable && !interior_unsafe {
+ if mt.mutbl != hir::MutMutable && is_freeze {
arg.attrs.set(ArgAttribute::NoAlias);
}
- if mt.mutbl == hir::MutImmutable && !interior_unsafe {
+ if mt.mutbl == hir::MutImmutable && is_freeze {
arg.attrs.set(ArgAttribute::ReadOnly);
}
// As an optimization, all shared statics which do not have interior
// mutability are placed into read-only memory.
if m != hir::MutMutable {
- let tcontents = ty.type_contents(ccx.tcx());
- if !tcontents.interior_unsafe() {
+ if ccx.shared().type_is_freeze(ty) {
llvm::LLVMSetGlobalConstant(g, llvm::True);
}
}
ty.is_sized(self.tcx, &self.empty_param_env, DUMMY_SP)
}
+ pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
+ ty.is_freeze(self.tcx, &self.empty_param_env, DUMMY_SP)
+ }
+
pub fn exported_symbols<'a>(&'a self) -> &'a NodeSet {
&self.exported_symbols
}