Auto merge of #41349 - eddyb:ty-contents, r=nikomatsakis
rustc: replace TypeContents with two independent properties (is_freeze / needs_drop). `InteriorUnsafe` / `interior_unsafe` was replaced with a private lang-item `Freeze` auto trait in libcore. `OwnsDtor` / `needs_drop` was replaced with a specialized traversal that *doesn't* avoid caching results in case of a cycle, as the only cycles left can only occur in erroneous "types with infinite sizes", references and raw pointers not having destructors. Also, `Copy` is now checked at every step of the recursion. r? @nikomatsakis
This commit is contained in:
commit
5695c3e943
@ -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)
|
||||
|
@ -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 {}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 <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::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<I, T, F>(v: I, mut f: F) -> TypeContents where
|
||||
I: IntoIterator<Item=T>,
|
||||
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<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::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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -436,9 +436,6 @@ pub struct GlobalCtxt<'tcx> {
|
||||
// Internal cache for metadata decoding. No need to track deps on this.
|
||||
pub rcache: RefCell<FxHashMap<ty::CReaderCacheKey, Ty<'tcx>>>,
|
||||
|
||||
// Cache for the type-contents routine. FIXME -- track deps?
|
||||
pub tc_cache: RefCell<FxHashMap<Ty<'tcx>, ty::contents::TypeContents>>,
|
||||
|
||||
// FIXME dep tracking -- should be harmless enough
|
||||
pub normalized_cache: RefCell<FxHashMap<Ty<'tcx>, 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,
|
||||
|
@ -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<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()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
@ -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<Ty<'tcx>>)
|
||||
-> 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<Ty<'tcx>>)
|
||||
-> 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>> {
|
||||
|
@ -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")
|
||||
|
@ -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, \
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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<T> 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 {
|
||||
|
@ -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 .. {}
|
||||
|
@ -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 }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user