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<T> {
-    fn from_ok(x: T) -> Self;
-    fn map_same<F: FnOnce(T) -> T>(self, f: F) -> Self;
+    type Error;
+
+    fn from(x: Result<T, Self::Error>) -> Self;
+    fn to_result(self) -> Result<T, Self::Error>;
 }
 
 impl<T> MaybeResult<T> for T {
-    fn from_ok(x: T) -> Self {
+    type Error = !;
+
+    fn from(x: Result<T, Self::Error>) -> Self {
+        let Ok(x) = x;
         x
     }
-    fn map_same<F: FnOnce(T) -> T>(self, f: F) -> Self {
-        f(self)
+    fn to_result(self) -> Result<T, Self::Error> {
+        Ok(self)
     }
 }
 
 impl<T, E> MaybeResult<T> for Result<T, E> {
-    fn from_ok(x: T) -> Self {
-        Ok(x)
+    type Error = E;
+
+    fn from(x: Result<T, Self::Error>) -> Self {
+        x
     }
-    fn map_same<F: FnOnce(T) -> T>(self, f: F) -> Self {
-        self.map(f)
+    fn to_result(self) -> Result<T, Self::Error> {
+        self
     }
 }
 
@@ -1656,7 +1674,8 @@ impl ty::query::TyCtxtAt<'a, 'tcx, '_> {
 
 impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
     where C: LayoutOf<Ty = Ty<'tcx>> + HasTyCtxt<'tcx>,
-          C::TyLayout: MaybeResult<TyLayout<'tcx>>
+          C::TyLayout: MaybeResult<TyLayout<'tcx>>,
+          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<PointeeInfo> {
+        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<T>`, not `Box<T>`.
+                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<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns.
-    UniqueOwned
-}
-
-#[derive(Copy, Clone)]
-pub struct PointeeInfo {
-    pub size: Size,
-    pub align: Align,
-    pub safe: Option<PointerKind>,
-}
-
 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<T>`, not `Box<T>`.
-                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<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns.
+    UniqueOwned
+}
+
+#[derive(Copy, Clone)]
+pub struct PointeeInfo {
+    pub size: Size,
+    pub align: Align,
+    pub safe: Option<PointerKind>,
+}
+
 pub trait TyLayoutMethods<'a, C: LayoutOf<Ty = Self>>: Sized {
     fn for_variant(
         this: TyLayout<'a, Self>,
@@ -917,6 +939,11 @@ pub trait TyLayoutMethods<'a, C: LayoutOf<Ty = Self>>: 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<PointeeInfo>;
 }
 
 impl<'a, Ty> TyLayout<'a, Ty> {
@@ -928,6 +955,10 @@ impl<'a, Ty> TyLayout<'a, Ty> {
     where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> {
         Ty::field(self, cx, i)
     }
+    pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo>
+    where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> {
+        Ty::pointee_info_at(self, cx, offset)
+    }
 }
 
 impl<'a, Ty> TyLayout<'a, Ty> {