diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 68de7f29193d8f61797996c481e82fbdfce92c7e..d52ff3b73b2ba9034d3629dd88596749d8ece703 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1100,10 +1100,12 @@ cache_on_disk_if { false } } - query layout_raw( - env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> - ) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> { - desc { "computing layout of `{}`", env.value } + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode, and will normalize the input type. + query layout_of( + key: ty::ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> Result, ty::layout::LayoutError<'tcx>> { + desc { "computing layout of `{}`", key.value } } query dylib_dependency_formats(_: CrateNum) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 8f90c7a8845ee24a2ff2d21e3cd8693312b719f7..1429769e8f2bc2e45ecc07746792b26c40c704b6 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -205,10 +205,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } -fn layout_raw<'tcx>( +fn layout_of<'tcx>( tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> Result<&'tcx Layout, LayoutError<'tcx>> { +) -> Result, LayoutError<'tcx>> { ty::tls::with_related_context(tcx, move |icx| { let (param_env, ty) = query.into_parts(); @@ -220,21 +220,33 @@ fn layout_raw<'tcx>( let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() }; ty::tls::enter_context(&icx, |_| { + let param_env = param_env.with_reveal_all_normalized(tcx); + let unnormalized_ty = ty; + let ty = tcx.normalize_erasing_regions(param_env, ty); + if ty != unnormalized_ty { + // Ensure this layout is also cached for the normalized type. + return tcx.layout_of(param_env.and(ty)); + } + let cx = LayoutCx { tcx, param_env }; - let layout = cx.layout_raw_uncached(ty); + + let layout = cx.layout_of_uncached(ty)?; + let layout = TyAndLayout { ty, layout }; + + cx.record_layout_for_printing(layout); + // Type-level uninhabitedness should always imply ABI uninhabitedness. - if let Ok(layout) = layout { - if tcx.conservative_is_privately_uninhabited(param_env.and(ty)) { - assert!(layout.abi.is_uninhabited()); - } + if tcx.conservative_is_privately_uninhabited(param_env.and(ty)) { + assert!(layout.abi.is_uninhabited()); } - layout + + Ok(layout) }) }) } pub fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { layout_raw, ..*providers }; + *providers = ty::query::Providers { layout_of, ..*providers }; } pub struct LayoutCx<'tcx, C> { @@ -492,7 +504,7 @@ fn univariant_uninterned( }) } - fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { + fn layout_of_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { let tcx = self.tcx; let param_env = self.param_env; let dl = self.data_layout(); @@ -889,7 +901,9 @@ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError< let present_first = match present_first { Some(present_first) => present_first, // Uninhabited because it has no variants, or only absent ones. - None if def.is_enum() => return tcx.layout_raw(param_env.and(tcx.types.never)), + None if def.is_enum() => { + return Ok(tcx.layout_of(param_env.and(tcx.types.never))?.layout); + } // If it's a struct, still compute a layout so that we can still compute the // field offsets. None => VariantIdx::new(0), @@ -1368,11 +1382,9 @@ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError< // Types with no meaningful known layout. ty::Projection(_) | ty::Opaque(..) => { - let normalized = tcx.normalize_erasing_regions(param_env, ty); - if ty == normalized { - return Err(LayoutError::Unknown(ty)); - } - tcx.layout_raw(param_env.and(normalized))? + // NOTE(eddyb) `layout_of` query should've normalized these away, + // if that was possible, so there's no reason to try again here. + return Err(LayoutError::Unknown(ty)); } ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { @@ -1712,7 +1724,7 @@ fn generator_layout( Ok(layout) } - /// This is invoked by the `layout_raw` query to record the final + /// This is invoked by the `layout_of` query to record the final /// layout of each type. #[inline(always)] fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) { @@ -2040,22 +2052,9 @@ impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { type TyAndLayout = Result, LayoutError<'tcx>>; /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. + /// executes in "reveal all" mode, and will normalize the input type. fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { - let param_env = self.param_env.with_reveal_all_normalized(self.tcx); - let ty = self.tcx.normalize_erasing_regions(param_env, ty); - let layout = self.tcx.layout_raw(param_env.and(ty))?; - let layout = TyAndLayout { ty, layout }; - - // N.B., this recording is normally disabled; when enabled, it - // can however trigger recursive invocations of `layout_of`. - // Therefore, we execute it *after* the main query has - // completed, to avoid problems around recursive structures - // and the like. (Admittedly, I wasn't able to reproduce a problem - // here, but it seems like the right thing to do. -nmatsakis) - self.record_layout_for_printing(layout); - - Ok(layout) + self.tcx.layout_of(self.param_env.and(ty)) } } @@ -2064,50 +2063,9 @@ impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> { type TyAndLayout = Result, LayoutError<'tcx>>; /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. + /// executes in "reveal all" mode, and will normalize the input type. fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { - let param_env = self.param_env.with_reveal_all_normalized(*self.tcx); - let ty = self.tcx.normalize_erasing_regions(param_env, ty); - let layout = self.tcx.layout_raw(param_env.and(ty))?; - let layout = TyAndLayout { ty, layout }; - - // N.B., this recording is normally disabled; when enabled, it - // can however trigger recursive invocations of `layout_of`. - // Therefore, we execute it *after* the main query has - // completed, to avoid problems around recursive structures - // and the like. (Admittedly, I wasn't able to reproduce a problem - // here, but it seems like the right thing to do. -nmatsakis) - let cx = LayoutCx { tcx: *self.tcx, param_env: self.param_env }; - cx.record_layout_for_printing(layout); - - Ok(layout) - } -} - -// Helper (inherent) `layout_of` methods to avoid pushing `LayoutCx` to users. -impl TyCtxt<'tcx> { - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - #[inline] - pub fn layout_of( - self, - param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result, LayoutError<'tcx>> { - let cx = LayoutCx { tcx: self, param_env: param_env_and_ty.param_env }; - cx.layout_of(param_env_and_ty.value) - } -} - -impl ty::query::TyCtxtAt<'tcx> { - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - #[inline] - pub fn layout_of( - self, - param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result, LayoutError<'tcx>> { - let cx = LayoutCx { tcx: self.at(self.span), param_env: param_env_and_ty.param_env }; - cx.layout_of(param_env_and_ty.value) + self.tcx.layout_of(self.param_env.and(ty)) } } diff --git a/compiler/rustc_mir/src/util/alignment.rs b/compiler/rustc_mir/src/util/alignment.rs index 5d4ca871faa2e46d3e0d189649afee1fdf465fb1..73adc60577bfcf41e43f243ba057f77256c8d112 100644 --- a/compiler/rustc_mir/src/util/alignment.rs +++ b/compiler/rustc_mir/src/util/alignment.rs @@ -24,7 +24,7 @@ pub fn is_disaligned<'tcx, L>( }; let ty = place.ty(local_decls, tcx).ty; - match tcx.layout_raw(param_env.and(ty)) { + match tcx.layout_of(param_env.and(ty)) { Ok(layout) if layout.align.abi <= pack => { // If the packed alignment is greater or equal to the field alignment, the type won't be // further disaligned. diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 88f1b1c320c1e3137722f2cc318ddd5a6b280df4..00b1b595022056f3378d6bca142f3544c88156df 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -1139,7 +1139,7 @@ pub fn scalar(cx: &C, scalar: Scalar) -> Self { /// to that obtained from `layout_of(ty)`, as we need to produce /// layouts for which Rust types do not exist, such as enum variants /// or synthetic fields of enums (i.e., discriminants) and fat pointers. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)] pub struct TyAndLayout<'a, Ty> { pub ty: Ty, pub layout: &'a Layout, diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 84dfdd4d4341b32fbe8e69d6d572f889752037d8..ae6bebcf727d18b0c46dbe3b06971c60b96fc2c3 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -1481,7 +1481,7 @@ fn restrict_repr_packed_field_ref_capture<'tcx>( match p.kind { ProjectionKind::Field(..) => match ty.kind() { ty::Adt(def, _) if def.repr.packed() => { - match tcx.layout_raw(param_env.and(p.ty)) { + match tcx.layout_of(param_env.and(p.ty)) { Ok(layout) if layout.align.abi.bytes() == 1 => { // if the alignment is 1, the type can't be further // disaligned. diff --git a/src/test/ui/consts/const-size_of-cycle.stderr b/src/test/ui/consts/const-size_of-cycle.stderr index 129457ebdf92913bd58756b2c687a2bfd344661c..1067eb003f7c765ad92b045730a3f2254fba575f 100644 --- a/src/test/ui/consts/const-size_of-cycle.stderr +++ b/src/test/ui/consts/const-size_of-cycle.stderr @@ -15,6 +15,7 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`.. LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Foo`... + = note: ...which requires computing layout of `[u8; _]`... = note: ...which requires normalizing `[u8; _]`... = note: ...which again requires simplifying constant for the type system `Foo::bytes::{constant#0}`, completing the cycle note: cycle used when checking that `Foo` is well-formed diff --git a/src/test/ui/consts/issue-44415.stderr b/src/test/ui/consts/issue-44415.stderr index 38841e99a72206f85fc2b18aa32d12e4554d87b8..9e3db5ce9a402ce3cdce028910d1d27821be98c5 100644 --- a/src/test/ui/consts/issue-44415.stderr +++ b/src/test/ui/consts/issue-44415.stderr @@ -15,6 +15,7 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`.. LL | bytes: [u8; unsafe { intrinsics::size_of::() }], | ^^^^^^ = note: ...which requires computing layout of `Foo`... + = note: ...which requires computing layout of `[u8; _]`... = note: ...which requires normalizing `[u8; _]`... = note: ...which again requires simplifying constant for the type system `Foo::bytes::{constant#0}`, completing the cycle note: cycle used when checking that `Foo` is well-formed diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs b/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs index 4d1cd059c27b5e64e5072f33f2e3b920ba1687b7..03cb3e24b7dbff0c49ca7c063aa2370779e8deda 100644 --- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs +++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs @@ -1,6 +1,8 @@ -//~ ERROR cycle detected when computing layout of -//~| NOTE ...which requires computing layout of -//~| NOTE ...which again requires computing layout of +//~ ERROR cycle detected when computing layout of `S` +//~| NOTE ...which requires computing layout of `std::option::Option<::It>`... +//~| NOTE ...which requires computing layout of `std::option::Option`... +//~| NOTE ...which again requires computing layout of `S`, completing the cycle +//~| NOTE cycle used when computing layout of `std::option::Option` // build-fail @@ -13,6 +15,5 @@ impl Mirror for T { struct S(Option<::It>); fn main() { - //~^ NOTE cycle used when optimizing MIR for `main` let _s = S(None); } diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr index be55890c08c88598619337f96ed63a1f557e24db..21c0e1e6de5f34b05f0704995dbda5d3e1d6ce88 100644 --- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr +++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr @@ -1,12 +1,9 @@ -error[E0391]: cycle detected when computing layout of `std::option::Option` +error[E0391]: cycle detected when computing layout of `S` | - = note: ...which requires computing layout of `S`... - = note: ...which again requires computing layout of `std::option::Option`, completing the cycle -note: cycle used when optimizing MIR for `main` - --> $DIR/issue-26548-recursion-via-normalize.rs:15:1 - | -LL | fn main() { - | ^^^^^^^^^ + = note: ...which requires computing layout of `std::option::Option<::It>`... + = note: ...which requires computing layout of `std::option::Option`... + = note: ...which again requires computing layout of `S`, completing the cycle + = note: cycle used when computing layout of `std::option::Option` error: aborting due to previous error