diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 21e4d8b07a19f..d1a8a9a34e155 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -12,6 +12,7 @@ use std::iter; use std::mem; use std::ops::Bound; +use crate::hir; use crate::ich::StableHashingContext; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, @@ -1518,6 +1519,10 @@ pub trait HasTyCtxt<'tcx>: HasDataLayout { fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx>; } +pub trait HasParamEnv<'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx>; +} + impl<'a, 'gcx, 'tcx> HasDataLayout for TyCtxt<'a, 'gcx, 'tcx> { fn data_layout(&self) -> &TargetDataLayout { &self.data_layout @@ -1530,6 +1535,12 @@ impl<'a, 'gcx, 'tcx> HasTyCtxt<'gcx> for TyCtxt<'a, 'gcx, 'tcx> { } } +impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> { + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } +} + impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> { fn data_layout(&self) -> &TargetDataLayout { self.tcx.data_layout() @@ -1543,25 +1554,32 @@ 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; + type Error; + + fn from(x: Result) -> Self; + fn to_result(self) -> Result; } impl MaybeResult for T { - fn from_ok(x: T) -> Self { + type Error = !; + + fn from(x: Result) -> Self { + let Ok(x) = x; x } - fn map_same T>(self, f: F) -> Self { - f(self) + fn to_result(self) -> Result { + Ok(self) } } impl MaybeResult for Result { - fn from_ok(x: T) -> Self { - Ok(x) + type Error = E; + + fn from(x: Result) -> Self { + x } - fn map_same T>(self, f: F) -> Self { - self.map(f) + fn to_result(self) -> Result { + self } } @@ -1656,7 +1674,8 @@ impl ty::query::TyCtxtAt<'a, 'tcx, '_> { impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> where C: LayoutOf> + HasTyCtxt<'tcx>, - C::TyLayout: MaybeResult> + C::TyLayout: MaybeResult>, + C: HasParamEnv<'tcx> { fn for_variant(this: TyLayout<'tcx>, cx: &C, variant_index: VariantIdx) -> TyLayout<'tcx> { let details = match this.variants { @@ -1664,10 +1683,9 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> Variants::Single { index } => { // Deny calling for_variant more than once for non-Single enums. - cx.layout_of(this.ty).map_same(|layout| { + if let Ok(layout) = cx.layout_of(this.ty).to_result() { assert_eq!(layout.variants, Variants::Single { index }); - layout - }); + } let fields = match this.ty.sty { ty::Adt(def, _) => def.variants[variant_index].fields.len(), @@ -1700,10 +1718,10 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> let tcx = cx.tcx(); let discr_layout = |discr: &Scalar| -> C::TyLayout { let layout = LayoutDetails::scalar(cx, discr.clone()); - MaybeResult::from_ok(TyLayout { + MaybeResult::from(Ok(TyLayout { details: tcx.intern_layout(layout), - ty: discr.value.to_ty(tcx) - }) + ty: discr.value.to_ty(tcx), + })) }; cx.layout_of(match this.ty.sty { @@ -1737,10 +1755,10 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> } else { tcx.mk_mut_ref(tcx.lifetimes.re_static, nil) }; - return cx.layout_of(ptr_ty).map_same(|mut ptr_layout| { + return MaybeResult::from(cx.layout_of(ptr_ty).to_result().map(|mut ptr_layout| { ptr_layout.ty = this.ty; ptr_layout - }); + })); } match tcx.struct_tail(pointee).sty { @@ -1824,6 +1842,130 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> } }) } + + fn pointee_info_at( + this: TyLayout<'tcx>, + cx: &C, + offset: Size, + ) -> Option { + match this.ty.sty { + ty::RawPtr(mt) if offset.bytes() == 0 => { + cx.layout_of(mt.ty).to_result().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, cx.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).to_result().ok() + .map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: Some(kind), + }) + } + + _ => { + let mut data_variant = match this.variants { + // Within the discriminant field, only the niche itself is + // always initialized, so we 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). + Variants::Multiple { + discr_kind: DiscriminantKind::Niche { + dataful_variant, + .. + }, + discr_index, + .. + } if this.fields.offset(discr_index) == offset => + Some(this.for_variant(cx, dataful_variant)), + _ => 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.to_result().ok() + .and_then(|field| { + if ptr_end <= field_start + field.size { + // We found the right field, look inside it. + field.pointee_info_at(cx, offset - field_start) + } 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 2c4a1ded97f39..70d184240fccd 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -2,8 +2,8 @@ use crate::llvm::{self, AttributePlace}; use crate::builder::Builder; use crate::context::CodegenCx; use crate::type_::Type; -use crate::type_of::{LayoutLlvmExt, PointerKind}; use crate::value::Value; +use crate::type_of::{LayoutLlvmExt}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::mir::operand::OperandValue; @@ -13,7 +13,7 @@ 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::layout::{self, PointerKind}; use libc::c_uint; diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 123fda1e215ff..bc2bb97a19e54 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -66,6 +66,12 @@ impl ty::layout::HasTyCtxt<'tcx> for Builder<'_, '_, 'tcx> { } } +impl ty::layout::HasParamEnv<'tcx> for Builder<'_, '_, 'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.cx.param_env() + } +} + impl ty::layout::LayoutOf for Builder<'_, '_, 'tcx> { type Ty = Ty<'tcx>; type TyLayout = TyLayout<'tcx>; diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index f6956bd5736eb..7bf8f705ea8ad 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -8,7 +8,6 @@ use rustc::hir; use crate::monomorphize::partitioning::CodegenUnit; use crate::type_::Type; -use crate::type_of::PointeeInfo; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n; @@ -16,7 +15,9 @@ 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, HasParamEnv +}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::FxHashMap; use rustc_target::spec::{HasTargetSpec, Target}; @@ -862,3 +863,9 @@ impl LayoutOf for CodegenCx<'ll, 'tcx> { }) } } + +impl<'tcx, 'll> HasParamEnv<'tcx> for CodegenCx<'ll, 'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx> { + ty::ParamEnv::reveal_all() + } +} diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index cbcc457fda9a2..ff25ed9256613 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -1,10 +1,9 @@ use crate::abi::{FnType, FnTypeExt}; use crate::common::*; use crate::type_::Type; -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::*; @@ -174,28 +173,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; @@ -406,112 +383,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 { - // Within the discriminant field, only the niche itself is - // always initialized, so we 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). - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Niche { - dataful_variant, - .. - }, - discr_index, - .. - } if self.fields.offset(discr_index) == offset => - Some(self.for_variant(cx, dataful_variant)), - _ => 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); cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); result diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index 48142fc9fa9f4..a3f99cd869e28 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs @@ -10,7 +10,7 @@ use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; use crate::MemFlags; use rustc::ty::Ty; -use rustc::ty::layout::{Align, Size}; +use rustc::ty::layout::{Align, Size, HasParamEnv}; use std::ops::Range; use std::iter::TrustedLen; @@ -29,6 +29,8 @@ pub trait BuilderMethods<'a, 'tcx: 'a>: + IntrinsicCallMethods<'tcx> + AsmBuilderMethods<'tcx> + StaticBuilderMethods<'tcx> + + HasParamEnv<'tcx> + { fn new_block<'b>(cx: &'a Self::CodegenCx, llfn: Self::Value, name: &'b str) -> Self; fn with_cx(cx: &'a Self::CodegenCx) -> Self; diff --git a/src/librustc_codegen_ssa/traits/mod.rs b/src/librustc_codegen_ssa/traits/mod.rs index 8fe8b7ecd4709..c237cd8bd2645 100644 --- a/src/librustc_codegen_ssa/traits/mod.rs +++ b/src/librustc_codegen_ssa/traits/mod.rs @@ -41,6 +41,8 @@ pub use self::type_::{ ArgTypeMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMethods, }; pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; +use rustc::ty::layout::{HasParamEnv}; + use std::fmt; @@ -58,6 +60,7 @@ pub trait CodegenMethods<'tcx>: + DeclareMethods<'tcx> + AsmMethods<'tcx> + PreDefineMethods<'tcx> + + HasParamEnv<'tcx> { } @@ -72,6 +75,7 @@ impl<'tcx, T> CodegenMethods<'tcx> for T where + DeclareMethods<'tcx> + AsmMethods<'tcx> + PreDefineMethods<'tcx> + + HasParamEnv<'tcx> { } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index ad4bc6a91f5a1..db827afdb94f4 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -175,6 +175,14 @@ impl<'a, 'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for InterpretCx<'a, 'mir, 'tcx, } } +impl<'a, 'mir, 'tcx, M> layout::HasParamEnv<'tcx> for InterpretCx<'a, 'mir, 'tcx, M> + where M: Machine<'a, 'mir, 'tcx> +{ + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } +} + impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> LayoutOf for InterpretCx<'a, 'mir, 'tcx, M> { diff --git a/src/librustc_passes/layout_test.rs b/src/librustc_passes/layout_test.rs index 6940f8f442ee9..7041a5593abbf 100644 --- a/src/librustc_passes/layout_test.rs +++ b/src/librustc_passes/layout_test.rs @@ -7,6 +7,7 @@ use rustc::ty::layout::HasTyCtxt; use rustc::ty::layout::LayoutOf; use rustc::ty::layout::TargetDataLayout; use rustc::ty::layout::TyLayout; +use rustc::ty::layout::HasParamEnv; use rustc::ty::ParamEnv; use rustc::ty::Ty; use rustc::ty::TyCtxt; @@ -122,6 +123,12 @@ impl<'me, 'tcx> HasTyCtxt<'tcx> for UnwrapLayoutCx<'me, 'tcx> { } } +impl<'me, 'tcx> HasParamEnv<'tcx> for UnwrapLayoutCx<'me, 'tcx> { + fn param_env(&self) -> ParamEnv<'tcx> { + self.param_env + } +} + impl<'me, 'tcx> HasDataLayout for UnwrapLayoutCx<'me, 'tcx> { fn data_layout(&self) -> &TargetDataLayout { self.tcx.data_layout() diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 59eda97a2f9f5..4b61057e5cf6c 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -910,6 +910,28 @@ 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 { fn for_variant( this: TyLayout<'a, Self>, @@ -917,6 +939,11 @@ pub trait TyLayoutMethods<'a, C: LayoutOf>: Sized { 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, + ) -> Option; } impl<'a, Ty> TyLayout<'a, Ty> { @@ -928,6 +955,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) -> Option + where Ty: TyLayoutMethods<'a, C>, C: LayoutOf { + Ty::pointee_info_at(self, cx, offset) + } } impl<'a, Ty> TyLayout<'a, Ty> {