From 928cde55bd7297a5022323e783ba2a15d4afacae Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 30 Oct 2025 08:07:56 +0100 Subject: [PATCH] repr(transparent) check: do not compute check_unsuited more than once --- .../rustc_hir_analysis/src/check/check.rs | 124 ++++++++---------- 1 file changed, 56 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 25ab91ebc9cfa..442ba1bca53a4 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1508,22 +1508,10 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) let typing_env = ty::TypingEnv::non_body_analysis(tcx, adt.did()); // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST). - // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private - // fields or `repr(C)`. We call those fields "unsuited". struct FieldInfo<'tcx> { span: Span, trivial: bool, - unsuited: Option>, - } - struct UnsuitedInfo<'tcx> { - /// The source of the problem, a type that is found somewhere within the field type. ty: Ty<'tcx>, - reason: UnsuitedReason, - } - enum UnsuitedReason { - NonExhaustive, - PrivateField, - ReprC, } let field_infos = adt.all_fields().map(|field| { @@ -1532,60 +1520,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // We are currently checking the type this field came from, so it must be local let span = tcx.hir_span_if_local(field.did).unwrap(); let trivial = layout.is_ok_and(|layout| layout.is_1zst()); - if !trivial { - // No need to even compute `unsuited`. - return FieldInfo { span, trivial, unsuited: None }; - } - - fn check_unsuited<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ty: Ty<'tcx>, - ) -> ControlFlow> { - // We can encounter projections during traversal, so ensure the type is normalized. - let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); - match ty.kind() { - ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)), - ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty), - ty::Adt(def, args) => { - if !def.did().is_local() - && !find_attr!( - tcx.get_all_attrs(def.did()), - AttributeKind::PubTransparent(_) - ) - { - let non_exhaustive = def.is_variant_list_non_exhaustive() - || def - .variants() - .iter() - .any(ty::VariantDef::is_field_list_non_exhaustive); - let has_priv = def.all_fields().any(|f| !f.vis.is_public()); - if non_exhaustive || has_priv { - return ControlFlow::Break(UnsuitedInfo { - ty, - reason: if non_exhaustive { - UnsuitedReason::NonExhaustive - } else { - UnsuitedReason::PrivateField - }, - }); - } - } - if def.repr().c() { - return ControlFlow::Break(UnsuitedInfo { - ty, - reason: UnsuitedReason::ReprC, - }); - } - def.all_fields() - .map(|field| field.ty(tcx, args)) - .try_for_each(|t| check_unsuited(tcx, typing_env, t)) - } - _ => ControlFlow::Continue(()), - } - } - - FieldInfo { span, trivial, unsuited: check_unsuited(tcx, typing_env, ty).break_value() } + return FieldInfo { span, trivial, ty }; }); let non_trivial_fields = field_infos @@ -1603,10 +1538,63 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) return; } + // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private + // fields or `repr(C)`. We call those fields "unsuited". + struct UnsuitedInfo<'tcx> { + /// The source of the problem, a type that is found somewhere within the field type. + ty: Ty<'tcx>, + reason: UnsuitedReason, + } + enum UnsuitedReason { + NonExhaustive, + PrivateField, + ReprC, + } + + fn check_unsuited<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + ) -> ControlFlow> { + // We can encounter projections during traversal, so ensure the type is normalized. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match ty.kind() { + ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)), + ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty), + ty::Adt(def, args) => { + if !def.did().is_local() + && !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_)) + { + let non_exhaustive = def.is_variant_list_non_exhaustive() + || def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive); + let has_priv = def.all_fields().any(|f| !f.vis.is_public()); + if non_exhaustive || has_priv { + return ControlFlow::Break(UnsuitedInfo { + ty, + reason: if non_exhaustive { + UnsuitedReason::NonExhaustive + } else { + UnsuitedReason::PrivateField + }, + }); + } + } + if def.repr().c() { + return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::ReprC }); + } + def.all_fields() + .map(|field| field.ty(tcx, args)) + .try_for_each(|t| check_unsuited(tcx, typing_env, t)) + } + _ => ControlFlow::Continue(()), + } + } + let mut prev_unsuited_1zst = false; for field in field_infos { - if let Some(unsuited) = field.unsuited { - assert!(field.trivial); + if field.trivial + && let Some(unsuited) = check_unsuited(tcx, typing_env, field.ty).break_value() + { // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts. // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst. if non_trivial_count > 0 || prev_unsuited_1zst {