rustc: replace interior_unsafe with a Freeze trait.

This commit is contained in:
Eduard-Mihai Burtescu 2017-04-17 21:18:56 +03:00
parent 3f5c311dc1
commit 6563374ed2
10 changed files with 83 additions and 20 deletions

View File

@ -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<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 {}

View File

@ -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;

View File

@ -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 => {

View File

@ -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<FxHashMap<Ty<'tcx>, bool>>,
/// A cache for `type_is_freeze`
pub is_freeze_cache: RefCell<FxHashMap<Ty<'tcx>, 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));

View File

@ -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),
&param_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>> {

View File

@ -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) {

View File

@ -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;
}

View File

@ -746,13 +746,13 @@ impl<'a, 'tcx> FnType<'tcx> {
// `&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);
}

View File

@ -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);
}
}

View File

@ -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
}