diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 393c01b0105..c0aa650a1e8 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -16,6 +16,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use cell::UnsafeCell; use cmp; use hash::Hash; use hash::Hasher; @@ -553,3 +554,19 @@ mod impls { #[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 !Freeze for UnsafeCell {} +unsafe impl Freeze for PhantomData {} +unsafe impl Freeze for *const T {} +unsafe impl Freeze for *mut T {} +unsafe impl<'a, T: ?Sized> Freeze for &'a T {} +unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 5989fa9007c..32dfb63d615 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -274,6 +274,7 @@ language_item_table! { UnsizeTraitLangItem, "unsize", unsize_trait; CopyTraitLangItem, "copy", copy_trait; SyncTraitLangItem, "sync", sync_trait; + FreezeTraitLangItem, "freeze", freeze_trait; DropTraitLangItem, "drop", drop_trait; diff --git a/src/librustc/ty/contents.rs b/src/librustc/ty/contents.rs index c690795b342..a1cb848213a 100644 --- a/src/librustc/ty/contents.rs +++ b/src/librustc/ty/contents.rs @@ -24,8 +24,7 @@ bitflags! { /// 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, } } @@ -34,10 +33,6 @@ impl TypeContents { 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) } @@ -124,17 +119,12 @@ impl<'a, 'tcx> ty::TyS<'tcx> { // 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 => { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 78d3885bb98..7325235ca7b 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -425,6 +425,8 @@ bitflags! { 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, } } @@ -1181,6 +1183,9 @@ pub struct ParameterEnvironment<'tcx> { /// A cache for `type_is_sized` pub is_sized_cache: RefCell, bool>>, + + /// A cache for `type_is_freeze` + pub is_freeze_cache: RefCell, bool>>, } impl<'a, 'tcx> ParameterEnvironment<'tcx> { @@ -1195,6 +1200,7 @@ 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()), } } @@ -2531,6 +2537,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { free_id_outlive: free_id_outlive, is_copy_cache: RefCell::new(FxHashMap()), is_sized_cache: RefCell::new(FxHashMap()), + is_freeze_cache: RefCell::new(FxHashMap()), } } @@ -2603,6 +2610,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { 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)); diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 5334ee2835d..d43d570397b 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -655,6 +655,50 @@ impl<'a, 'tcx> ty::TyS<'tcx> { 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>> { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 1313b24fa74..cc7d2562843 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -80,7 +80,7 @@ impl<'a, 'tcx> Qualif { 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) { diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 2c4439f80a2..535c6a7ab9e 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -46,7 +46,7 @@ use rustc::lint::builtin::CONST_ERR; 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; @@ -85,7 +85,7 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { // 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; } diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index c4fdc46d030..e0a75f3caa7 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -746,13 +746,13 @@ impl<'a, 'tcx> FnType<'tcx> { // `&T` where `T` contains no `UnsafeCell` 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); } diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index 7a53a03344f..eb3ac309be1 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -261,8 +261,7 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // 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); } } diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index c3770470bfd..fd9ff17cc64 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -399,6 +399,10 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { 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 }