提交 4b9f4b22 编写于 作者: B bors

Auto merge of #88308 - eddyb:cooked-layouts, r=nagisa

Morph `layout_raw` query into `layout_of`.

Before this PR, `LayoutCx::layout_of` wrapped the `layout_raw` query, to:
* normalize the type, before attempting to compute the layout
* pass the layout to `record_layout_for_printing`, for `-Zprint-type-sizes`

Moving those two responsibilities into the query may reduce overhead (due to cached calls skipping those steps), but I want to do a perf run to know.

One of the changes I had to make was changing the return type of the query, to be able to both get out the type produced by normalizing inside the query *and* to match the signature of the old `TyCtxt::layout_of`. This change may be worse, perf-wise, so that's another reason I want to check.

r? `@nagisa` cc `@oli-obk`
...@@ -1100,10 +1100,12 @@ ...@@ -1100,10 +1100,12 @@
cache_on_disk_if { false } cache_on_disk_if { false }
} }
query layout_raw( /// Computes the layout of a type. Note that this implicitly
env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> /// executes in "reveal all" mode, and will normalize the input type.
) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> { query layout_of(
desc { "computing layout of `{}`", env.value } key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
) -> Result<ty::layout::TyAndLayout<'tcx>, ty::layout::LayoutError<'tcx>> {
desc { "computing layout of `{}`", key.value }
} }
query dylib_dependency_formats(_: CrateNum) query dylib_dependency_formats(_: CrateNum)
......
...@@ -205,10 +205,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ...@@ -205,10 +205,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
} }
} }
fn layout_raw<'tcx>( fn layout_of<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
) -> Result<&'tcx Layout, LayoutError<'tcx>> { ) -> Result<TyAndLayout<'tcx>, LayoutError<'tcx>> {
ty::tls::with_related_context(tcx, move |icx| { ty::tls::with_related_context(tcx, move |icx| {
let (param_env, ty) = query.into_parts(); let (param_env, ty) = query.into_parts();
...@@ -220,21 +220,33 @@ fn layout_raw<'tcx>( ...@@ -220,21 +220,33 @@ fn layout_raw<'tcx>(
let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() }; let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() };
ty::tls::enter_context(&icx, |_| { 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 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. // Type-level uninhabitedness should always imply ABI uninhabitedness.
if let Ok(layout) = layout { if tcx.conservative_is_privately_uninhabited(param_env.and(ty)) {
if tcx.conservative_is_privately_uninhabited(param_env.and(ty)) { assert!(layout.abi.is_uninhabited());
assert!(layout.abi.is_uninhabited());
}
} }
layout
Ok(layout)
}) })
}) })
} }
pub fn provide(providers: &mut ty::query::Providers) { 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> { pub struct LayoutCx<'tcx, C> {
...@@ -492,7 +504,7 @@ fn univariant_uninterned( ...@@ -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 tcx = self.tcx;
let param_env = self.param_env; let param_env = self.param_env;
let dl = self.data_layout(); let dl = self.data_layout();
...@@ -889,7 +901,9 @@ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError< ...@@ -889,7 +901,9 @@ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<
let present_first = match present_first { let present_first = match present_first {
Some(present_first) => present_first, Some(present_first) => present_first,
// Uninhabited because it has no variants, or only absent ones. // 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 // If it's a struct, still compute a layout so that we can still compute the
// field offsets. // field offsets.
None => VariantIdx::new(0), None => VariantIdx::new(0),
...@@ -1368,11 +1382,9 @@ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError< ...@@ -1368,11 +1382,9 @@ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<
// Types with no meaningful known layout. // Types with no meaningful known layout.
ty::Projection(_) | ty::Opaque(..) => { ty::Projection(_) | ty::Opaque(..) => {
let normalized = tcx.normalize_erasing_regions(param_env, ty); // NOTE(eddyb) `layout_of` query should've normalized these away,
if ty == normalized { // if that was possible, so there's no reason to try again here.
return Err(LayoutError::Unknown(ty)); return Err(LayoutError::Unknown(ty));
}
tcx.layout_raw(param_env.and(normalized))?
} }
ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => {
...@@ -1712,7 +1724,7 @@ fn generator_layout( ...@@ -1712,7 +1724,7 @@ fn generator_layout(
Ok(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. /// layout of each type.
#[inline(always)] #[inline(always)]
fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) { fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) {
...@@ -2040,22 +2052,9 @@ impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { ...@@ -2040,22 +2052,9 @@ impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> {
type TyAndLayout = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>; type TyAndLayout = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
/// Computes the layout of a type. Note that this implicitly /// 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 { fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout {
let param_env = self.param_env.with_reveal_all_normalized(self.tcx); self.tcx.layout_of(self.param_env.and(ty))
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)
} }
} }
...@@ -2064,50 +2063,9 @@ impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> { ...@@ -2064,50 +2063,9 @@ impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> {
type TyAndLayout = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>; type TyAndLayout = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
/// Computes the layout of a type. Note that this implicitly /// 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 { fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout {
let param_env = self.param_env.with_reveal_all_normalized(*self.tcx); self.tcx.layout_of(self.param_env.and(ty))
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<TyAndLayout<'tcx>, 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<TyAndLayout<'tcx>, 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)
} }
} }
......
...@@ -24,7 +24,7 @@ pub fn is_disaligned<'tcx, L>( ...@@ -24,7 +24,7 @@ pub fn is_disaligned<'tcx, L>(
}; };
let ty = place.ty(local_decls, tcx).ty; 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 => { Ok(layout) if layout.align.abi <= pack => {
// If the packed alignment is greater or equal to the field alignment, the type won't be // If the packed alignment is greater or equal to the field alignment, the type won't be
// further disaligned. // further disaligned.
......
...@@ -1139,7 +1139,7 @@ pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self { ...@@ -1139,7 +1139,7 @@ pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
/// to that obtained from `layout_of(ty)`, as we need to produce /// to that obtained from `layout_of(ty)`, as we need to produce
/// layouts for which Rust types do not exist, such as enum variants /// layouts for which Rust types do not exist, such as enum variants
/// or synthetic fields of enums (i.e., discriminants) and fat pointers. /// 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 struct TyAndLayout<'a, Ty> {
pub ty: Ty, pub ty: Ty,
pub layout: &'a Layout, pub layout: &'a Layout,
......
...@@ -1481,7 +1481,7 @@ fn restrict_repr_packed_field_ref_capture<'tcx>( ...@@ -1481,7 +1481,7 @@ fn restrict_repr_packed_field_ref_capture<'tcx>(
match p.kind { match p.kind {
ProjectionKind::Field(..) => match ty.kind() { ProjectionKind::Field(..) => match ty.kind() {
ty::Adt(def, _) if def.repr.packed() => { 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 => { Ok(layout) if layout.align.abi.bytes() == 1 => {
// if the alignment is 1, the type can't be further // if the alignment is 1, the type can't be further
// disaligned. // disaligned.
......
...@@ -15,6 +15,7 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`.. ...@@ -15,6 +15,7 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`..
LL | bytes: [u8; std::mem::size_of::<Foo>()] LL | bytes: [u8; std::mem::size_of::<Foo>()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `Foo`... = note: ...which requires computing layout of `Foo`...
= note: ...which requires computing layout of `[u8; _]`...
= note: ...which requires normalizing `[u8; _]`... = note: ...which requires normalizing `[u8; _]`...
= note: ...which again requires simplifying constant for the type system `Foo::bytes::{constant#0}`, completing the cycle = 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 note: cycle used when checking that `Foo` is well-formed
......
...@@ -15,6 +15,7 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`.. ...@@ -15,6 +15,7 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`..
LL | bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }], LL | bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
| ^^^^^^ | ^^^^^^
= note: ...which requires computing layout of `Foo`... = note: ...which requires computing layout of `Foo`...
= note: ...which requires computing layout of `[u8; _]`...
= note: ...which requires normalizing `[u8; _]`... = note: ...which requires normalizing `[u8; _]`...
= note: ...which again requires simplifying constant for the type system `Foo::bytes::{constant#0}`, completing the cycle = 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 note: cycle used when checking that `Foo` is well-formed
......
//~ ERROR cycle detected when computing layout of //~ ERROR cycle detected when computing layout of `S`
//~| NOTE ...which requires computing layout of //~| NOTE ...which requires computing layout of `std::option::Option<<S as Mirror>::It>`...
//~| NOTE ...which again requires computing layout of //~| NOTE ...which requires computing layout of `std::option::Option<S>`...
//~| NOTE ...which again requires computing layout of `S`, completing the cycle
//~| NOTE cycle used when computing layout of `std::option::Option<S>`
// build-fail // build-fail
...@@ -13,6 +15,5 @@ impl<T: ?Sized> Mirror for T { ...@@ -13,6 +15,5 @@ impl<T: ?Sized> Mirror for T {
struct S(Option<<S as Mirror>::It>); struct S(Option<<S as Mirror>::It>);
fn main() { fn main() {
//~^ NOTE cycle used when optimizing MIR for `main`
let _s = S(None); let _s = S(None);
} }
error[E0391]: cycle detected when computing layout of `std::option::Option<S>` error[E0391]: cycle detected when computing layout of `S`
| |
= note: ...which requires computing layout of `S`... = note: ...which requires computing layout of `std::option::Option<<S as Mirror>::It>`...
= note: ...which again requires computing layout of `std::option::Option<S>`, completing the cycle = note: ...which requires computing layout of `std::option::Option<S>`...
note: cycle used when optimizing MIR for `main` = note: ...which again requires computing layout of `S`, completing the cycle
--> $DIR/issue-26548-recursion-via-normalize.rs:15:1 = note: cycle used when computing layout of `std::option::Option<S>`
|
LL | fn main() {
| ^^^^^^^^^
error: aborting due to previous error error: aborting due to previous error
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册