diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index f4506c8e81976..2c09d0a1b74f1 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -22,6 +22,8 @@ use std::iter; use std::mem; use std::ops::Bound; +use hir; + use ich::StableHashingContext; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, @@ -1497,6 +1499,7 @@ impl<'gcx, 'tcx, T: HasTyCtxt<'gcx>> HasTyCtxt<'gcx> for LayoutCx<'tcx, T> { pub trait MaybeResult { fn from_ok(x: T) -> Self; fn map_same T>(self, f: F) -> Self; + fn ok(self) -> Option; } impl MaybeResult for T { @@ -1506,6 +1509,9 @@ impl MaybeResult for T { fn map_same T>(self, f: F) -> Self { f(self) } + fn ok(self) -> Option { + Some(self) + } } impl MaybeResult for Result { @@ -1515,6 +1521,9 @@ impl MaybeResult for Result { fn map_same T>(self, f: F) -> Self { self.map(f) } + fn ok(self) -> Option { + self.ok() + } } pub type TyLayout<'tcx> = ::rustc_target::abi::TyLayout<'tcx, Ty<'tcx>>; @@ -1610,6 +1619,8 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> where C: LayoutOf> + HasTyCtxt<'tcx>, C::TyLayout: MaybeResult> { + type ParamEnv = ty::ParamEnv<'tcx>; + fn for_variant(this: TyLayout<'tcx>, cx: &C, variant_index: VariantIdx) -> TyLayout<'tcx> { let details = match this.variants { Variants::Single { index } if index == variant_index => this.details, @@ -1762,6 +1773,131 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> } }) } + + fn pointee_info_at( + this: TyLayout<'tcx>, + cx: &C, + offset: Size, + param_env: Self::ParamEnv, + ) -> Option { + match this.ty.sty { + ty::RawPtr(mt) if offset.bytes() == 0 => { + cx.layout_of(mt.ty).ok() + .map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: None, + }) + } + + ty::Ref(_, ty, mt) if offset.bytes() == 0 => { + let tcx = cx.tcx(); + let is_freeze = ty.is_freeze(tcx, param_env, DUMMY_SP); + let kind = match mt { + hir::MutImmutable => if is_freeze { + PointerKind::Frozen + } else { + PointerKind::Shared + }, + hir::MutMutable => { + // Previously we would only emit noalias annotations for LLVM >= 6 or in + // panic=abort mode. That was deemed right, as prior versions had many bugs + // in conjunction with unwinding, but later versions didn’t seem to have + // said issues. See issue #31681. + // + // Alas, later on we encountered a case where noalias would generate wrong + // code altogether even with recent versions of LLVM in *safe* code with no + // unwinding involved. See #54462. + // + // For now, do not enable mutable_noalias by default at all, while the + // issue is being figured out. + let mutable_noalias = tcx.sess.opts.debugging_opts.mutable_noalias + .unwrap_or(false); + if mutable_noalias { + PointerKind::UniqueBorrowed + } else { + PointerKind::Shared + } + } + }; + + cx.layout_of(ty).ok() + .map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: Some(kind), + }) + } + + _ => { + let mut data_variant = match this.variants { + Variants::NicheFilling { dataful_variant, .. } => { + // Only the niche in this is always initialized, + // so only check for a pointer at its offset. + // + // If the niche is a pointer, it's either valid + // (according to its type), or null (which the + // niche field's scalar validity range encodes). + // This allows using `dereferenceable_or_null` + // for e.g., `Option<&T>`, and this will continue + // to work as long as we don't start using more + // niches than just null (e.g., the first page + // of the address space, or unaligned pointers). + if this.fields.offset(0) == offset { + Some(this.for_variant(cx, dataful_variant)) + } else { + None + } + } + _ => Some(this) + }; + + if let Some(variant) = data_variant { + // We're not interested in any unions. + if let FieldPlacement::Union(_) = variant.fields { + data_variant = None; + } + } + + let mut result = None; + + if let Some(variant) = data_variant { + let ptr_end = offset + Pointer.size(cx); + for i in 0..variant.fields.count() { + let field_start = variant.fields.offset(i); + if field_start <= offset { + let field = variant.field(cx, i); + result = field.ok() + .and_then(|field| { + if ptr_end <= field_start + field.size { + let off = offset - field_start; + // We found the right field, look inside it. + Self::pointee_info_at(field, cx, off, param_env) + } else { + None + } + }); + if result.is_some() { + break; + } + } + } + } + + // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. + if let Some(ref mut pointee) = result { + if let ty::Adt(def, _) = this.ty.sty { + if def.is_box() && offset.bytes() == 0 { + pointee.safe = Some(PointerKind::UniqueOwned); + } + } + } + + result + } + } + } + } struct Niche { diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 8c4f7a58dab40..5e3b42dfc3ce4 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -15,15 +15,15 @@ use context::CodegenCx; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::mir::operand::OperandValue; use type_::Type; -use type_of::{LayoutLlvmExt, PointerKind}; +use type_of::{LayoutLlvmExt}; use value::Value; use rustc_target::abi::call::ArgType; use rustc_codegen_ssa::traits::*; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TyLayout, Abi as LayoutAbi}; -use rustc::ty::{self, Ty, Instance}; -use rustc::ty::layout; +use rustc::ty::{self, Ty, Instance, ParamEnv}; +use rustc::ty::layout::{self, PointerKind}; use libc::c_uint; @@ -487,7 +487,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { } } - if let Some(pointee) = layout.pointee_info_at(cx, offset) { + if let Some(pointee) = layout.pointee_info_at(cx, offset, ParamEnv::reveal_all()) { if let Some(kind) = pointee.safe { attrs.pointee_size = pointee.size; attrs.pointee_align = Some(pointee.align); diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index b75cd8f68b368..aebce5b5c8579 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -18,7 +18,6 @@ use value::Value; use monomorphize::partitioning::CodegenUnit; use type_::Type; -use type_of::PointeeInfo; use rustc_codegen_ssa::traits::*; use libc::c_uint; @@ -27,7 +26,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc::mir::mono::Stats; use rustc::session::config::{self, DebugInfo}; use rustc::session::Session; -use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout, VariantIdx}; +use rustc::ty::layout::{LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::FxHashMap; use rustc_target::spec::{HasTargetSpec, Target}; diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index 52b560c662540..759be6e84a75c 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -10,10 +10,9 @@ use abi::{FnType, FnTypeExt}; use common::*; -use rustc::hir; use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; -use rustc_target::abi::FloatTy; +use rustc::ty::layout::{self, Align, LayoutOf, PointeeInfo, Size, TyLayout}; +use rustc_target::abi::{FloatTy, TyLayoutMethods}; use rustc_mir::monomorphize::item::DefPathBasedNames; use rustc_codegen_ssa::traits::*; use type_::Type; @@ -179,28 +178,6 @@ impl<'a, 'tcx> CodegenCx<'a, 'tcx> { } } -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum PointerKind { - /// Most general case, we know no restrictions to tell LLVM. - Shared, - - /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. - Frozen, - - /// `&mut T`, when we know `noalias` is safe for LLVM. - UniqueBorrowed, - - /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. - UniqueOwned -} - -#[derive(Copy, Clone)] -pub struct PointeeInfo { - pub size: Size, - pub align: Align, - pub safe: Option, -} - pub trait LayoutLlvmExt<'tcx> { fn is_llvm_immediate(&self) -> bool; fn is_llvm_scalar_pair<'a>(&self) -> bool; @@ -411,110 +388,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { return pointee; } - let mut result = None; - match self.ty.sty { - ty::RawPtr(mt) if offset.bytes() == 0 => { - let (size, align) = cx.size_and_align_of(mt.ty); - result = Some(PointeeInfo { - size, - align, - safe: None - }); - } - - ty::Ref(_, ty, mt) if offset.bytes() == 0 => { - let (size, align) = cx.size_and_align_of(ty); - - let kind = match mt { - hir::MutImmutable => if cx.type_is_freeze(ty) { - PointerKind::Frozen - } else { - PointerKind::Shared - }, - hir::MutMutable => { - // Previously we would only emit noalias annotations for LLVM >= 6 or in - // panic=abort mode. That was deemed right, as prior versions had many bugs - // in conjunction with unwinding, but later versions didn’t seem to have - // said issues. See issue #31681. - // - // Alas, later on we encountered a case where noalias would generate wrong - // code altogether even with recent versions of LLVM in *safe* code with no - // unwinding involved. See #54462. - // - // For now, do not enable mutable_noalias by default at all, while the - // issue is being figured out. - let mutable_noalias = cx.tcx.sess.opts.debugging_opts.mutable_noalias - .unwrap_or(false); - if mutable_noalias { - PointerKind::UniqueBorrowed - } else { - PointerKind::Shared - } - } - }; - - result = Some(PointeeInfo { - size, - align, - safe: Some(kind) - }); - } - - _ => { - let mut data_variant = match self.variants { - layout::Variants::NicheFilling { dataful_variant, .. } => { - // Only the niche itself is always initialized, - // so only check for a pointer at its offset. - // - // If the niche is a pointer, it's either valid - // (according to its type), or null (which the - // niche field's scalar validity range encodes). - // This allows using `dereferenceable_or_null` - // for e.g., `Option<&T>`, and this will continue - // to work as long as we don't start using more - // niches than just null (e.g., the first page - // of the address space, or unaligned pointers). - if self.fields.offset(0) == offset { - Some(self.for_variant(cx, dataful_variant)) - } else { - None - } - } - _ => Some(*self) - }; - - if let Some(variant) = data_variant { - // We're not interested in any unions. - if let layout::FieldPlacement::Union(_) = variant.fields { - data_variant = None; - } - } - - if let Some(variant) = data_variant { - let ptr_end = offset + layout::Pointer.size(cx); - for i in 0..variant.fields.count() { - let field_start = variant.fields.offset(i); - if field_start <= offset { - let field = variant.field(cx, i); - if ptr_end <= field_start + field.size { - // We found the right field, look inside it. - result = field.pointee_info_at(cx, offset - field_start); - break; - } - } - } - } - - // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. - if let Some(ref mut pointee) = result { - if let ty::Adt(def, _) = self.ty.sty { - if def.is_box() && offset.bytes() == 0 { - pointee.safe = Some(PointerKind::UniqueOwned); - } - } - } - } - } + let result = Ty::pointee_info_at(*self, cx, offset, ty::ParamEnv::reveal_all()); cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); result diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 5912da9d3aa2f..7f255c0f1267e 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -913,13 +913,43 @@ pub trait LayoutOf { fn layout_of(&self, ty: Self::Ty) -> Self::TyLayout; } +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum PointerKind { + /// Most general case, we know no restrictions to tell LLVM. + Shared, + + /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. + Frozen, + + /// `&mut T`, when we know `noalias` is safe for LLVM. + UniqueBorrowed, + + /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. + UniqueOwned +} + +#[derive(Copy, Clone)] +pub struct PointeeInfo { + pub size: Size, + pub align: Align, + pub safe: Option, +} + pub trait TyLayoutMethods<'a, C: LayoutOf>: Sized { + type ParamEnv; + fn for_variant( this: TyLayout<'a, Self>, cx: &C, variant_index: VariantIdx, ) -> TyLayout<'a, Self>; fn field(this: TyLayout<'a, Self>, cx: &C, i: usize) -> C::TyLayout; + fn pointee_info_at( + this: TyLayout<'a, Self>, + cx: &C, + offset: Size, + param_env: Self::ParamEnv, + ) -> Option; } impl<'a, Ty> TyLayout<'a, Ty> { @@ -931,6 +961,10 @@ impl<'a, Ty> TyLayout<'a, Ty> { where Ty: TyLayoutMethods<'a, C>, C: LayoutOf { Ty::field(self, cx, i) } + pub fn pointee_info_at(self, cx: &C, offset: Size, param_env: Ty::ParamEnv) -> Option + where Ty: TyLayoutMethods<'a, C>, C: LayoutOf { + Ty::pointee_info_at(self, cx, offset, param_env) + } } impl<'a, Ty> TyLayout<'a, Ty> {