diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index dcfa81dab2526..0b3c0be1a4e1a 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -37,9 +37,6 @@ middle_autodiff_unsafe_inner_const_ref = reading from a `Duplicated` const {$ty} middle_bounds_check = index out of bounds: the length is {$len} but the index is {$index} -middle_cannot_be_normalized = - unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized - middle_conflict_types = this expression supplies two conflicting concrete types for the same opaque type @@ -52,9 +49,6 @@ middle_const_eval_non_int = middle_const_not_used_in_type_alias = const parameter `{$ct}` is part of concrete type but not used in parameter list for the `impl Trait` type alias -middle_cycle = - a cycle occurred during layout computation - middle_deprecated = use of deprecated {$kind} `{$path}`{$has_note -> [true] : {$note} *[other] {""} @@ -78,9 +72,23 @@ middle_erroneous_constant = erroneous constant encountered middle_failed_writing_file = failed to write file {$path}: {$error}" +middle_layout_cycle = + a cycle occurred during layout computation + +middle_layout_normalization_failure = + unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized + middle_layout_references_error = the type has an unknown layout +middle_layout_size_overflow = + values of the type `{$ty}` are too big for the target architecture + +middle_layout_too_generic = the type `{$ty}` does not have a fixed layout + +middle_layout_unknown = + the type `{$ty}` has an unknown layout + middle_opaque_hidden_type_mismatch = concrete type differs from previous defining opaque type use .label = expected `{$self_ty}`, got `{$other_ty}` @@ -98,16 +106,8 @@ middle_strict_coherence_needs_negative_coherence = to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled .label = due to this attribute -middle_too_generic = `{$ty}` does not have a fixed size - middle_type_length_limit = reached the type-length limit while instantiating `{$shrunk}` -middle_unknown_layout = - the type `{$ty}` has an unknown layout - middle_unsupported_union = we don't support unions yet: '{$ty_name}' -middle_values_too_big = - values of the type `{$ty}` are too big for the target architecture - middle_written_to_path = the full type name has been written to '{$path}' diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index c53e3d54cc491..be8a3403ba956 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -132,19 +132,19 @@ impl fmt::Debug for CustomSubdiagnostic<'_> { #[derive(Diagnostic)] pub enum LayoutError<'tcx> { - #[diag(middle_unknown_layout)] + #[diag(middle_layout_unknown)] Unknown { ty: Ty<'tcx> }, - #[diag(middle_too_generic)] + #[diag(middle_layout_too_generic)] TooGeneric { ty: Ty<'tcx> }, - #[diag(middle_values_too_big)] + #[diag(middle_layout_size_overflow)] Overflow { ty: Ty<'tcx> }, - #[diag(middle_cannot_be_normalized)] + #[diag(middle_layout_normalization_failure)] NormalizationFailure { ty: Ty<'tcx>, failure_ty: String }, - #[diag(middle_cycle)] + #[diag(middle_layout_cycle)] Cycle, #[diag(middle_layout_references_error)] diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index bbb8a9fa67149..19fa832357499 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), } @@ -243,11 +264,11 @@ impl<'tcx> LayoutError<'tcx> { use crate::fluent_generated::*; match self { - Unknown(_) => middle_unknown_layout, - SizeOverflow(_) => middle_values_too_big, - TooGeneric(_) => middle_too_generic, - NormalizationFailure(_, _) => middle_cannot_be_normalized, - Cycle(_) => middle_cycle, + Unknown(_) => middle_layout_unknown, + SizeOverflow(_) => middle_layout_size_overflow, + TooGeneric(_) => middle_layout_too_generic, + NormalizationFailure(_, _) => middle_layout_normalization_failure, + Cycle(_) => middle_layout_cycle, ReferencesError(_) => middle_layout_references_error, } } @@ -276,7 +297,7 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { match *self { LayoutError::Unknown(ty) => write!(f, "the type `{ty}` has an unknown layout"), LayoutError::TooGeneric(ty) => { - write!(f, "`{ty}` does not have a fixed size") + write!(f, "the type `{ty}` does not have a fixed layout") } LayoutError::SizeOverflow(ty) => { write!(f, "values of the type `{ty}` are too big for the target architecture") diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 8429e68b600da..556512e0236e5 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -134,42 +134,32 @@ fn univariant_uninterned<'tcx>( cx: &LayoutCx<'tcx>, ty: Ty<'tcx>, fields: &IndexSlice>, - repr: &ReprOptions, kind: StructKind, ) -> Result, &'tcx LayoutError<'tcx>> { - let pack = repr.pack; - if pack.is_some() && repr.align.is_some() { - cx.tcx().dcx().bug("struct cannot be packed and aligned"); - } - - cx.calc.univariant(fields, repr, kind).map_err(|err| map_error(cx, ty, err)) + let repr = ReprOptions::default(); + cx.calc.univariant(fields, &repr, kind).map_err(|err| map_error(cx, ty, err)) } fn extract_const_value<'tcx>( - const_: ty::Const<'tcx>, - ty: Ty<'tcx>, cx: &LayoutCx<'tcx>, + ty: Ty<'tcx>, + ct: ty::Const<'tcx>, ) -> Result, &'tcx LayoutError<'tcx>> { - match const_.kind() { + match ct.kind() { ty::ConstKind::Value(cv) => Ok(cv), - ty::ConstKind::Error(guar) => { - return Err(error(cx, LayoutError::ReferencesError(guar))); - } - ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) => { - if !const_.has_param() { - bug!("no generic type found in the type: {ty:?}"); - } - return Err(error(cx, LayoutError::TooGeneric(ty))); - } - ty::ConstKind::Unevaluated(_) => { - if !const_.has_param() { - return Err(error(cx, LayoutError::Unknown(ty))); - } else { - return Err(error(cx, LayoutError::TooGeneric(ty))); + ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) => { + if !ct.has_param() { + bug!("failed to normalize const, but it is not generic: {ct:?}"); } + Err(error(cx, LayoutError::TooGeneric(ty))) } - ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { - bug!("unexpected type: {ty:?}"); + ty::ConstKind::Infer(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Error(_) => { + // `ty::ConstKind::Error` is handled at the top of `layout_of_uncached` + // (via `ty.error_reported()`). + bug!("layout_of: unexpected const: {ct:?}"); } } } @@ -194,10 +184,9 @@ fn layout_of_uncached<'tcx>( }; let scalar = |value: Primitive| tcx.mk_layout(LayoutData::scalar(cx, scalar_unit(value))); - let univariant = - |fields: &IndexSlice>, repr: &ReprOptions, kind| { - Ok(tcx.mk_layout(univariant_uninterned(cx, ty, fields, repr, kind)?)) - }; + let univariant = |fields: &IndexSlice>, kind| { + Ok(tcx.mk_layout(univariant_uninterned(cx, ty, fields, kind)?)) + }; debug_assert!(!ty.has_non_region_infer()); Ok(match *ty.kind() { @@ -210,12 +199,12 @@ fn layout_of_uncached<'tcx>( &mut layout.backend_repr { if let Some(start) = start { - scalar.valid_range_mut().start = extract_const_value(start, ty, cx)? + scalar.valid_range_mut().start = extract_const_value(cx, ty, start)? .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; } if let Some(end) = end { - let mut end = extract_const_value(end, ty, cx)? + let mut end = extract_const_value(cx, ty, end)? .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; if !include_end { @@ -274,16 +263,11 @@ fn layout_of_uncached<'tcx>( data_ptr.valid_range_mut().start = 1; } - let pointee = tcx.normalize_erasing_regions(cx.typing_env, pointee); if pointee.is_sized(tcx, cx.typing_env) { return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr))); } - let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() - // Projection eagerly bails out when the pointee references errors, - // fall back to structurally deducing metadata. - && !pointee.references_error() - { + let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() { let pointee_metadata = Ty::new_projection(tcx, metadata_def_id, [pointee]); let metadata_ty = match tcx.try_normalize_erasing_regions(cx.typing_env, pointee_metadata) { @@ -354,7 +338,7 @@ fn layout_of_uncached<'tcx>( // Arrays and slices. ty::Array(element, count) => { - let count = extract_const_value(count, ty, cx)? + let count = extract_const_value(cx, ty, count)? .try_to_target_usize(tcx) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; @@ -415,17 +399,10 @@ fn layout_of_uncached<'tcx>( }), // Odd unit types. - ty::FnDef(..) => { - univariant(IndexSlice::empty(), &ReprOptions::default(), StructKind::AlwaysSized)? - } + ty::FnDef(..) => univariant(IndexSlice::empty(), StructKind::AlwaysSized)?, ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { - let mut unit = univariant_uninterned( - cx, - ty, - IndexSlice::empty(), - &ReprOptions::default(), - StructKind::AlwaysSized, - )?; + let mut unit = + univariant_uninterned(cx, ty, IndexSlice::empty(), StructKind::AlwaysSized)?; match unit.backend_repr { BackendRepr::Memory { ref mut sized } => *sized = false, _ => bug!(), @@ -439,7 +416,6 @@ fn layout_of_uncached<'tcx>( let tys = args.as_closure().upvar_tys(); univariant( &tys.iter().map(|ty| cx.layout_of(ty)).try_collect::>()?, - &ReprOptions::default(), StructKind::AlwaysSized, )? } @@ -448,7 +424,6 @@ fn layout_of_uncached<'tcx>( let tys = args.as_coroutine_closure().upvar_tys(); univariant( &tys.iter().map(|ty| cx.layout_of(ty)).try_collect::>()?, - &ReprOptions::default(), StructKind::AlwaysSized, )? } @@ -457,11 +432,7 @@ fn layout_of_uncached<'tcx>( let kind = if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; - univariant( - &tys.iter().map(|k| cx.layout_of(k)).try_collect::>()?, - &ReprOptions::default(), - kind, - )? + univariant(&tys.iter().map(|k| cx.layout_of(k)).try_collect::>()?, kind)? } // SIMD vector types. @@ -719,25 +690,30 @@ fn layout_of_uncached<'tcx>( } // Types with no meaningful known layout. + ty::Param(_) => { + return Err(error(cx, LayoutError::TooGeneric(ty))); + } + ty::Alias(..) => { - if ty.has_param() { - return Err(error(cx, LayoutError::TooGeneric(ty))); - } // 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(error(cx, LayoutError::Unknown(ty))); - } - - ty::Bound(..) | ty::CoroutineWitness(..) | ty::Infer(_) | ty::Error(_) => { - bug!("Layout::compute: unexpected type `{}`", ty) - } - - ty::Param(_) => { - return Err(error(cx, LayoutError::TooGeneric(ty))); + let err = if ty.has_param() { + LayoutError::TooGeneric(ty) + } else { + // This is only reachable with unsatisfiable predicates. For example, if we have + // `u8: Iterator`, then we can't compute the layout of `::Item`. + LayoutError::Unknown(ty) + }; + return Err(error(cx, err)); } - ty::Placeholder(..) => { - return Err(error(cx, LayoutError::Unknown(ty))); + ty::Placeholder(..) + | ty::Bound(..) + | ty::CoroutineWitness(..) + | ty::Infer(_) + | ty::Error(_) => { + // `ty::Error` is handled at the top of this function. + bug!("layout_of: unexpected type `{ty}`") } }) } @@ -911,13 +887,7 @@ fn coroutine_layout<'tcx>( .chain(iter::once(Ok(tag_layout))) .chain(promoted_layouts) .try_collect::>()?; - let prefix = univariant_uninterned( - cx, - ty, - &prefix_layouts, - &ReprOptions::default(), - StructKind::AlwaysSized, - )?; + let prefix = univariant_uninterned(cx, ty, &prefix_layouts, StructKind::AlwaysSized)?; let (prefix_size, prefix_align) = (prefix.size, prefix.align); @@ -982,7 +952,6 @@ fn coroutine_layout<'tcx>( cx, ty, &variant_only_tys.map(|ty| cx.layout_of(ty)).try_collect::>()?, - &ReprOptions::default(), StructKind::Prefixed(prefix_size, prefix_align.abi), )?; variant.variants = Variants::Single { index }; diff --git a/tests/ui/layout/debug.rs b/tests/ui/layout/debug.rs index 81dc72852543d..b87a1d2031df4 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; //~ ERROR: does not have a fixed layout diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index 319c0de26a90e..0daf2d3b9e7f2 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: the type `T` does not have a fixed layout + --> $DIR/debug.rs:89:1 + | +LL | type TooGeneric = 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 0000000000000..c0f8710c03cb3 --- /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` 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`, and now we fail to +//! normalize ` as Project2>::Assoc2` if there is a `T: Project1` bound +//! in the param env! This happens, because `PhantomData: Project2` only holds if +//! `::Assoc1 == ()` holds. This would usually be satisfied by the +//! blanket `impl Project1 for T`, but due to the `T: Project1` bound we do not +//! normalize `::Assoc1` via the impl and treat it as rigid instead. +//! Therefore, `PhantomData: Project2` does NOT hold and normalizing +//! ` 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 Project1 for T { + type Assoc1 = (); +} + +trait Project2 { + type Assoc2; + fn get(self) -> Self::Assoc2; +} + +impl> Project2 for PhantomData { + type Assoc2 = (); + fn get(self) -> Self::Assoc2 {} +} + +fn opaque() -> impl Project2 { + PhantomData:: +} + +fn check() { + unsafe { + std::mem::transmute::<_, ()>(opaque::().get()); + //~^ ERROR: cannot transmute + //~| NOTE: (unable to determine layout for `::Assoc2` because `::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 0000000000000..5fe38d4403a20 --- /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::().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `::Assoc2` (unable to determine layout for `::Assoc2` because `::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`.