Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,24 @@ impl<'tcx> SizeSkeleton<'tcx> {
ty: Ty<'tcx>,
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
) -> Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>> {
Self::compute_inner(ty, tcx, typing_env, 0)
}

fn compute_inner(
ty: Ty<'tcx>,
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
depth: usize,
) -> Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>> {
debug_assert!(!ty.has_non_region_infer());

// Bail out if we've recursed too deeply (issue #156137); a cyclic type
// alias can otherwise blow the stack here.
if !tcx.recursion_limit().value_within_limit(depth) {
return Err(tcx.arena.alloc(LayoutError::Unknown(ty)));
Copy link
Copy Markdown
Contributor

@oli-obk oli-obk May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just raise a recursion limit error here. There's a function for doing that, several other recursion limit checks should be doing that, too.

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean the emit_err(RecursionLimitReached{...}) pattern used in struct_tail_raw?

but SizeSkeleton::compute has no span. should I add it and create RecursionLimitReachedSizeSkeleton diagnostic in error.rs?

}

// First try computing a static layout.
let err = match tcx.layout_of(typing_env.as_query_input(ty)) {
Ok(layout) => {
Expand Down Expand Up @@ -407,7 +422,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
return Ok(SizeSkeleton::Known(Size::from_bytes(0), None));
}

match SizeSkeleton::compute(inner, tcx, typing_env)? {
match SizeSkeleton::compute_inner(inner, tcx, typing_env, depth + 1)? {
// This may succeed because the multiplication of two types may overflow
// but a single size of a nested array will not.
SizeSkeleton::Known(s, a) => {
Expand All @@ -434,10 +449,9 @@ impl<'tcx> SizeSkeleton<'tcx> {
// Get a zero-sized variant or a pointer newtype.
let zero_or_ptr_variant = |i| {
let i = VariantIdx::from_usize(i);
let fields =
def.variant(i).fields.iter().map(|field| {
SizeSkeleton::compute(field.ty(tcx, args), tcx, typing_env)
});
let fields = def.variant(i).fields.iter().map(|field| {
SizeSkeleton::compute_inner(field.ty(tcx, args), tcx, typing_env, depth + 1)
});
let mut ptr = None;
for field in fields {
let field = field?;
Expand Down Expand Up @@ -487,13 +501,13 @@ impl<'tcx> SizeSkeleton<'tcx> {
if ty == normalized {
Err(err)
} else {
SizeSkeleton::compute(normalized, tcx, typing_env)
SizeSkeleton::compute_inner(normalized, tcx, typing_env, depth + 1)
}
}

ty::Pat(base, pat) => {
// Pattern types are always the same size as their base.
let base = SizeSkeleton::compute(base, tcx, typing_env);
let base = SizeSkeleton::compute_inner(base, tcx, typing_env, depth + 1);
match *pat {
ty::PatternKind::Range { .. } | ty::PatternKind::Or(_) => base,
// But in the case of `!null` patterns we need to note that in the
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/transmute/recursive-size-skeleton.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Regression test for issue #156137.
// Computing the `SizeSkeleton` of a type whose layout depends on itself
// through a normalizing type alias used to recurse without bound and
// blow the stack. We now bail out via the recursion limit and emit a
// regular error instead of ICE-ing.

use std::mem::transmute;

trait Trait {
type Assoc;
}
impl<T> Trait for T {
type Assoc = T;
}
type Alias<T> = <T as Trait>::Assoc;

pub struct Thing<T: ?Sized>(*const T, Alias<Thing<T>>);

pub fn weird<T: ?Sized>(value: Thing<T>) {
let _: i32 = unsafe { transmute(value) };
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
}

fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/transmute/recursive-size-skeleton.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> $DIR/recursive-size-skeleton.rs:20:27
|
LL | let _: i32 = unsafe { transmute(value) };
| ^^^^^^^^^
|
= note: source type: `Thing<T>` (the type `*const T` has an unknown layout)
= note: target type: `i32` (32 bits)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0512`.
Loading