document and test all LayoutError variants

This commit is contained in:
Lukas Markeffsky 2025-02-16 00:03:10 +01:00
parent 3b022d8cee
commit d0a5bbbb8e
5 changed files with 102 additions and 1 deletions

View file

@ -229,11 +229,32 @@ impl fmt::Display for ValidityRequirement {
#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
pub enum LayoutError<'tcx> {
/// A type doesn't have a sensible layout.
///
/// This variant is used for layout errors that don't necessarily cause
/// compile errors.
///
/// For example, this can happen if a struct contains an unsized type in a
/// non-tail field, but has an unsatisfiable bound like `str: Sized`.
Unknown(Ty<'tcx>),
/// The size of a type exceeds [`TargetDataLayout::obj_size_bound`].
SizeOverflow(Ty<'tcx>),
/// The layout can vary due to a generic parameter.
///
/// Unlike `Unknown`, this variant is a "soft" error and indicates that the layout
/// may become computable after further instantiating the generic parameter(s).
TooGeneric(Ty<'tcx>),
/// An alias failed to normalize.
///
/// This variant is necessary, because, due to trait solver incompleteness, it is
/// possible than an alias that was rigid during analysis fails to normalize after
/// revealing opaque types.
///
/// See `tests/ui/layout/normalization-failure.rs` for an example.
NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>),
/// A non-layout error is reported elsewhere.
ReferencesError(ErrorGuaranteed),
/// A type has cyclic layout, i.e. the type contains itself without indirection.
Cycle(ErrorGuaranteed),
}

View file

@ -82,3 +82,8 @@ type Impossible = (str, str); //~ ERROR: cannot be known at compilation time
#[rustc_layout(debug)]
union EmptyUnion {} //~ ERROR: has an unknown layout
//~^ ERROR: unions cannot have zero fields
// Test the error message of `LayoutError::TooGeneric`
// (this error is never emitted to users).
#[rustc_layout(debug)]
type TooGeneric<T> = T; //~ ERROR: does not have a fixed size

View file

@ -596,12 +596,18 @@ error: the type has an unknown layout
LL | union EmptyUnion {}
| ^^^^^^^^^^^^^^^^
error: `T` does not have a fixed size
--> $DIR/debug.rs:89:1
|
LL | type TooGeneric<T> = T;
| ^^^^^^^^^^^^^^^^^^
error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases
--> $DIR/debug.rs:75:5
|
LL | const C: () = ();
| ^^^^^^^^^^^
error: aborting due to 19 previous errors
error: aborting due to 20 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,57 @@
//! This test demonstrates how `LayoutError::NormalizationFailure` can happen and why
//! it is necessary.
//!
//! This code does not cause an immediate normalization error in typeck, because we
//! don't reveal the hidden type returned by `opaque<T>` in the analysis typing mode.
//! Instead, `<{opaque} as Project2>::Assoc2` is a *rigid projection*, because we know
//! that `{opaque}: Project2` holds, due to the opaque type's `impl Project2` bound,
//! but cannot normalize `<{opaque} as Project2>::Assoc2` any further.
//!
//! However, in the post-analysis typing mode, which is used for the layout computation,
//! the opaque's hidden type is revealed to be `PhantomData<T>`, and now we fail to
//! normalize `<PhantomData<T> as Project2>::Assoc2` if there is a `T: Project1` bound
//! in the param env! This happens, because `PhantomData<T>: Project2` only holds if
//! `<T as Project1>::Assoc1 == ()` holds. This would usually be satisfied by the
//! blanket `impl<T> Project1 for T`, but due to the `T: Project1` bound we do not
//! normalize `<T as Project1>::Assoc1` via the impl and treat it as rigid instead.
//! Therefore, `PhantomData<T>: Project2` does NOT hold and normalizing
//! `<PhantomData<T> as Project2>::Assoc2` fails.
//!
//! Note that this layout error can only happen when computing the layout in a generic
//! context, which is not required for codegen, but may happen for lints, MIR optimizations,
//! and the transmute check.
use std::marker::PhantomData;
trait Project1 {
type Assoc1;
}
impl<T> Project1 for T {
type Assoc1 = ();
}
trait Project2 {
type Assoc2;
fn get(self) -> Self::Assoc2;
}
impl<T: Project1<Assoc1 = ()>> Project2 for PhantomData<T> {
type Assoc2 = ();
fn get(self) -> Self::Assoc2 {}
}
fn opaque<T>() -> impl Project2 {
PhantomData::<T>
}
fn check<T: Project1>() {
unsafe {
std::mem::transmute::<_, ()>(opaque::<T>().get());
//~^ ERROR: cannot transmute
//~| NOTE: (unable to determine layout for `<impl Project2 as Project2>::Assoc2` because `<impl Project2 as Project2>::Assoc2` cannot be normalized)
//~| NOTE: (0 bits)
}
}
fn main() {}

View file

@ -0,0 +1,12 @@
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> $DIR/normalization-failure.rs:50:9
|
LL | std::mem::transmute::<_, ()>(opaque::<T>().get());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: source type: `<impl Project2 as Project2>::Assoc2` (unable to determine layout for `<impl Project2 as Project2>::Assoc2` because `<impl Project2 as Project2>::Assoc2` cannot be normalized)
= note: target type: `()` (0 bits)
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0512`.