about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs21
-rw-r--r--tests/ui/layout/debug.rs5
-rw-r--r--tests/ui/layout/debug.stderr8
-rw-r--r--tests/ui/layout/normalization-failure.rs57
-rw-r--r--tests/ui/layout/normalization-failure.stderr12
5 files changed, 102 insertions, 1 deletions
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index bbb8a9fa671..2a058addf15 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -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),
 }
 
diff --git a/tests/ui/layout/debug.rs b/tests/ui/layout/debug.rs
index 81dc7285254..c0adf9ec677 100644
--- a/tests/ui/layout/debug.rs
+++ b/tests/ui/layout/debug.rs
@@ -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
diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr
index 319c0de26a9..d64dee4e27f 100644
--- a/tests/ui/layout/debug.stderr
+++ b/tests/ui/layout/debug.stderr
@@ -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`.
diff --git a/tests/ui/layout/normalization-failure.rs b/tests/ui/layout/normalization-failure.rs
new file mode 100644
index 00000000000..c0f8710c03c
--- /dev/null
+++ b/tests/ui/layout/normalization-failure.rs
@@ -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() {}
diff --git a/tests/ui/layout/normalization-failure.stderr b/tests/ui/layout/normalization-failure.stderr
new file mode 100644
index 00000000000..5fe38d4403a
--- /dev/null
+++ b/tests/ui/layout/normalization-failure.stderr
@@ -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`.