diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index bddd570a13d26..cd87b27d4f1aa 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -151,6 +151,7 @@ pub fn build_startup_objects(build: &Build, for_compiler: &Compiler, target: &st if !up_to_date(src_file, dst_file) { let mut cmd = Command::new(&compiler_path); build.run(cmd.env("RUSTC_BOOTSTRAP", "1") + .arg("--cfg").arg(format!("stage{}", compiler.stage)) .arg("--target").arg(target) .arg("--emit=obj") .arg("--out-dir").arg(dst_dir) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 393c01b0105c5..c0aa650a1e854 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 5989fa9007c44..32dfb63d6150a 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 deleted file mode 100644 index e14295982916f..0000000000000 --- a/src/librustc/ty/contents.rs +++ /dev/null @@ -1,255 +0,0 @@ -// 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 or the MIT license -// , 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::FxHashMap; - -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): - OwnsDtor = 0b0000_0000__0000_0010__0000, - // OwnsAll = 0b0000_0000__1111_1111__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 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::OwnsDtor) - } - - pub fn union(v: I, mut f: F) -> TypeContents where - I: IntoIterator, - F: FnMut(T) -> TypeContents, - { - v.into_iter().fold(TC::None, |tc, ty| tc | f(ty)) - } -} - -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 FxHashMap())); - - fn tc_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, - cache: &mut FxHashMap, 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>, ... } - // - // 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 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 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::TyNever | - ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar => { - TC::None - } - - ty::TyDynamic(..) => { - 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(def_id, ref substs) => { - TypeContents::union( - substs.upvar_tys(def_id, tcx), - |ty| tc_ty(tcx, &ty, cache)) - } - - ty::TyTuple(ref tys, _) => { - TypeContents::union(&tys[..], - |ty| tc_ty(tcx, *ty, cache)) - } - - ty::TyAdt(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.is_union() { - // unions don't have destructors regardless of the child types - res = res - TC::OwnsDtor; - } - - if def.has_dtor(tcx) { - res = res | TC::OwnsDtor; - } - - apply_lang_items(tcx, def.did, res) - } - - ty::TyProjection(..) | - ty::TyParam(_) | - ty::TyAnon(..) => { - 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 - } - } - } -} diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 8b7438c0bfad2..a41629258716d 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -436,9 +436,6 @@ pub struct GlobalCtxt<'tcx> { // Internal cache for metadata decoding. No need to track deps on this. pub rcache: RefCell>>, - // Cache for the type-contents routine. FIXME -- track deps? - pub tc_cache: RefCell, ty::contents::TypeContents>>, - // FIXME dep tracking -- should be harmless enough pub normalized_cache: RefCell, Ty<'tcx>>>, @@ -708,7 +705,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { freevars: RefCell::new(resolutions.freevars), maybe_unused_trait_imports: resolutions.maybe_unused_trait_imports, rcache: RefCell::new(FxHashMap()), - tc_cache: RefCell::new(FxHashMap()), normalized_cache: RefCell::new(FxHashMap()), inhabitedness_cache: RefCell::new(FxHashMap()), lang_items: lang_items, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index ab1a06aeacd18..5c0889976c21a 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -71,7 +71,6 @@ pub use self::sty::InferTy::*; pub use self::sty::Region::*; pub use self::sty::TypeVariants::*; -pub use self::contents::TypeContents; pub use self::context::{TyCtxt, GlobalArenas, tls}; pub use self::context::{Lift, TypeckTables}; @@ -99,7 +98,6 @@ pub mod walk; pub mod wf; pub mod util; -mod contents; mod context; mod flags; mod instance; @@ -425,6 +423,10 @@ 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, + const NEEDS_DROP_CACHED = 1 << 22, + const NEEDS_DROP = 1 << 23, } } @@ -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()), } } @@ -2375,40 +2381,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Some(self.item_mir(did)) } - /// If `type_needs_drop` returns true, then `ty` is definitely - /// non-copy and *might* have a destructor attached; if it returns - /// false, then `ty` definitely has no destructor (i.e. no drop glue). - /// - /// (Note that this implies that if `ty` has a destructor attached, - /// then `type_needs_drop` will definitely return `true` for `ty`.) - pub fn type_needs_drop_given_env(self, - ty: Ty<'gcx>, - param_env: &ty::ParameterEnvironment<'gcx>) -> bool { - // Issue #22536: We first query type_moves_by_default. It sees a - // normalized version of the type, and therefore will definitely - // know whether the type implements Copy (and thus needs no - // cleanup/drop/zeroing) ... - let tcx = self.global_tcx(); - let implements_copy = !ty.moves_by_default(tcx, param_env, DUMMY_SP); - - if implements_copy { return false; } - - // ... (issue #22536 continued) but as an optimization, still use - // prior logic of asking if the `needs_drop` bit is set; we need - // not zero non-Copy types if they have no destructor. - - // FIXME(#22815): Note that calling `ty::type_contents` is a - // conservative heuristic; it may report that `needs_drop` is set - // when actual type does not actually have a destructor associated - // with it. But since `ty` absolutely did not have the `Copy` - // bound attached (see above), it is sound to treat it as having a - // destructor (e.g. zero its memory on move). - - let contents = ty.type_contents(tcx); - debug!("type_needs_drop ty={:?} contents={:?}", ty, contents); - contents.needs_drop(tcx) - } - /// Get the attributes of a definition. pub fn get_attrs(self, did: DefId) -> Cow<'gcx, [ast::Attribute]> { if let Some(id) = self.hir.as_local_node_id(did) { @@ -2531,6 +2503,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 +2576,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 5334ee2835db2..49d79f6545e2d 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -21,7 +21,7 @@ use ty::fold::TypeVisitor; use ty::layout::{Layout, LayoutError}; use ty::TypeVariants::*; use util::common::ErrorReported; -use util::nodemap::FxHashMap; +use util::nodemap::{FxHashMap, FxHashSet}; use middle::lang_items; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; @@ -655,6 +655,165 @@ 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 + } + + /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely + /// non-copy and *might* have a destructor attached; if it returns + /// `false`, then `ty` definitely has no destructor (i.e. no drop glue). + /// + /// (Note that this implies that if `ty` has a destructor attached, + /// then `needs_drop` will definitely return `true` for `ty`.) + #[inline] + pub fn needs_drop(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>) -> bool { + if self.flags.get().intersects(TypeFlags::NEEDS_DROP_CACHED) { + return self.flags.get().intersects(TypeFlags::NEEDS_DROP); + } + + self.needs_drop_uncached(tcx, param_env, &mut FxHashSet()) + } + + fn needs_drop_inner(&'tcx self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + stack: &mut FxHashSet>) + -> bool { + if self.flags.get().intersects(TypeFlags::NEEDS_DROP_CACHED) { + return self.flags.get().intersects(TypeFlags::NEEDS_DROP); + } + + // This should be reported as an error by `check_representable`. + // + // Consider the type as not needing drop in the meanwhile to avoid + // further errors. + if let Some(_) = stack.replace(self) { + return false; + } + + let needs_drop = self.needs_drop_uncached(tcx, param_env, stack); + + // "Pop" the cycle detection "stack". + stack.remove(self); + + needs_drop + } + + fn needs_drop_uncached(&'tcx self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + stack: &mut FxHashSet>) + -> bool { + assert!(!self.needs_infer()); + + let result = match self.sty { + // Fast-path for primitive types + ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) | + ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyNever | + ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | + ty::TyRawPtr(_) | ty::TyRef(..) | ty::TyStr => false, + + // Issue #22536: We first query type_moves_by_default. It sees a + // normalized version of the type, and therefore will definitely + // know whether the type implements Copy (and thus needs no + // cleanup/drop/zeroing) ... + _ if !self.moves_by_default(tcx, param_env, DUMMY_SP) => false, + + // ... (issue #22536 continued) but as an optimization, still use + // prior logic of asking for the structural "may drop". + + // FIXME(#22815): Note that this is a conservative heuristic; + // it may report that the type "may drop" when actual type does + // not actually have a destructor associated with it. But since + // the type absolutely did not have the `Copy` bound attached + // (see above), it is sound to treat it as having a destructor. + + // User destructors are the only way to have concrete drop types. + ty::TyAdt(def, _) if def.has_dtor(tcx) => true, + + // Can refer to a type which may drop. + // FIXME(eddyb) check this against a ParameterEnvironment. + ty::TyDynamic(..) | ty::TyProjection(..) | ty::TyParam(_) | + ty::TyAnon(..) | ty::TyInfer(_) | ty::TyError => true, + + // Structural recursion. + ty::TyArray(ty, _) | ty::TySlice(ty) => { + ty.needs_drop_inner(tcx, param_env, stack) + } + + ty::TyClosure(def_id, ref substs) => { + substs.upvar_tys(def_id, tcx) + .any(|ty| ty.needs_drop_inner(tcx, param_env, stack)) + } + + ty::TyTuple(ref tys, _) => { + tys.iter().any(|ty| ty.needs_drop_inner(tcx, param_env, stack)) + } + + // unions don't have destructors regardless of the child types + ty::TyAdt(def, _) if def.is_union() => false, + + ty::TyAdt(def, substs) => { + def.variants.iter().any(|v| { + v.fields.iter().any(|f| { + f.ty(tcx, substs).needs_drop_inner(tcx, param_env, stack) + }) + }) + } + }; + + if !self.has_param_types() && !self.has_self_ty() { + self.flags.set(self.flags.get() | if result { + TypeFlags::NEEDS_DROP_CACHED | TypeFlags::NEEDS_DROP + } else { + TypeFlags::NEEDS_DROP_CACHED + }); + } + + result + } + #[inline] pub fn layout<'lcx>(&'tcx self, infcx: &InferCtxt<'a, 'tcx, 'lcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index dc01cbe5e7605..de5613dbfaa38 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -322,7 +322,7 @@ fn on_all_drop_children_bits<'a, 'tcx, F>( let ty = lvalue.ty(mir, tcx).to_ty(tcx); debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty); - if tcx.type_needs_drop_given_env(ty, &ctxt.param_env) { + if ty.needs_drop(tcx, &ctxt.param_env) { each_child(child); } else { debug!("on_all_drop_children_bits - skipping") diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 0ee9d4a42c7f8..1c69f3cff172a 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1152,7 +1152,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields { let param_env = &ty::ParameterEnvironment::for_item(ctx.tcx, item.id); for field in vdata.fields() { let field_ty = ctx.tcx.item_type(ctx.tcx.hir.local_def_id(field.id)); - if ctx.tcx.type_needs_drop_given_env(field_ty, param_env) { + if field_ty.needs_drop(ctx.tcx, param_env) { ctx.span_lint(UNIONS_WITH_DROP_FIELDS, field.span, "union contains a field with possibly non-trivial drop code, \ diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 5f9fb8e1b120f..db9da2a280b94 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -168,7 +168,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { type with inference types/regions", ty); }); - self.tcx.type_needs_drop_given_env(ty, &self.infcx.parameter_environment) + ty.needs_drop(self.tcx.global_tcx(), &self.infcx.parameter_environment) } pub fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index ac2bdaad24f76..892d67ac23725 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -357,7 +357,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // a regular goto. let ty = location.ty(&callee_mir, tcx).subst(tcx, callsite.substs); let ty = ty.to_ty(tcx); - if tcx.type_needs_drop_given_env(ty, ¶m_env) { + if ty.needs_drop(tcx, ¶m_env) { cost += CALL_PENALTY; if let Some(unwind) = unwind { work_list.push(unwind); diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 1313b24fa74f5..526c1488ab480 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -80,10 +80,10 @@ 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) { + if !ty.needs_drop(tcx, param_env) { *self = *self - Qualif::NEEDS_DROP; } } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 04a1fc891cf1e..07025fcfdb944 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -277,8 +277,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let mut fields = fields; fields.retain(|&(ref lvalue, _)| { - self.tcx().type_needs_drop_given_env( - self.lvalue_ty(lvalue), self.elaborator.param_env()) + self.lvalue_ty(lvalue).needs_drop(self.tcx(), self.elaborator.param_env()) }); debug!("drop_ladder - fields needing drop: {:?}", fields); diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 2c4439f80a239..fdb6752213378 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,11 +85,11 @@ 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; } - if self.tcx.type_needs_drop_given_env(ty, &self.param_env) { + if ty.needs_drop(self.tcx, &self.param_env) { self.promotable = false; } } diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index c4fdc46d030c9..e0a75f3caa7b3 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 7a53a03344fcb..eb3ac309be16d 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 c3770470bfd05..1d1921bf7b96d 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -392,13 +392,17 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { } pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - self.tcx.type_needs_drop_given_env(ty, &self.empty_param_env) + ty.needs_drop(self.tcx, &self.empty_param_env) } pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { 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 } diff --git a/src/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs index 65c85697ce720..e8b92aab1da1e 100644 --- a/src/rtstartup/rsbegin.rs +++ b/src/rtstartup/rsbegin.rs @@ -22,7 +22,7 @@ // object (usually called `crtX.o), which then invokes initialization callbacks // of other runtime components (registered via yet another special image section). -#![feature(no_core, lang_items)] +#![feature(no_core, lang_items, optin_builtin_traits)] #![crate_type="rlib"] #![no_core] #![allow(non_camel_case_types)] @@ -31,9 +31,12 @@ trait Sized {} #[lang = "sync"] trait Sync {} +impl Sync for .. {} #[lang = "copy"] trait Copy {} -impl Sync for T {} +#[cfg_attr(not(stage0), lang = "freeze")] +trait Freeze {} +impl Freeze for .. {} #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] pub mod eh_frames { diff --git a/src/test/run-make/simd-ffi/simd.rs b/src/test/run-make/simd-ffi/simd.rs index 49fec6f3619e4..8ab8f4715755d 100644 --- a/src/test/run-make/simd-ffi/simd.rs +++ b/src/test/run-make/simd-ffi/simd.rs @@ -12,7 +12,7 @@ #![crate_type = "lib"] // we can compile to a variety of platforms, because we don't need // cross-compiled standard libraries. -#![feature(no_core)] +#![feature(no_core, optin_builtin_traits)] #![no_core] #![feature(repr_simd, simd_ffi, link_llvm_intrinsics, lang_items)] @@ -78,3 +78,7 @@ pub trait Copy { } pub mod marker { pub use Copy; } + +#[lang = "freeze"] +trait Freeze {} +impl Freeze for .. {} diff --git a/src/test/run-make/target-specs/foo.rs b/src/test/run-make/target-specs/foo.rs index 15b5697723216..af24c3b460b2e 100644 --- a/src/test/run-make/target-specs/foo.rs +++ b/src/test/run-make/target-specs/foo.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(lang_items, no_core)] +#![feature(lang_items, no_core, optin_builtin_traits)] #![no_core] #[lang="copy"] @@ -17,6 +17,10 @@ trait Copy { } #[lang="sized"] trait Sized { } +#[lang = "freeze"] +trait Freeze {} +impl Freeze for .. {} + #[lang="start"] fn start(_main: *const u8, _argc: isize, _argv: *const *const u8) -> isize { 0 }