From ec7789f8002bffb6bb0c5d968895b3d955768dba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Sep 2026 23:01:46 +0200 Subject: [PATCH] turn aligned-in-packed error into lint --- .../src/error_codes/E0588.md | 4 +- .../rustc_hir_analysis/src/check/check.rs | 78 +++++++++++-------- compiler/rustc_lint_defs/src/builtin.rs | 30 +++++++ ...cked-struct-contains-aligned-type-73112.rs | 2 +- ...-struct-contains-aligned-type-73112.stderr | 4 +- tests/ui/repr/repr-packed-contains-align.rs | 33 +++++--- .../ui/repr/repr-packed-contains-align.stderr | 58 +++++++------- 7 files changed, 133 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0588.md b/compiler/rustc_error_codes/src/error_codes/E0588.md index 995d945f1589e..6bb4cafc331a6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0588.md +++ b/compiler/rustc_error_codes/src/error_codes/E0588.md @@ -1,9 +1,11 @@ +#### Note: this error code is no longer emitted by the compiler. + A type with `packed` representation hint has a field with `align` representation hint. Erroneous code example: -```compile_fail,E0588 +```ignore (no longer emitted) #[repr(align(16))] struct Aligned(i32); diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1895f586df2f0..9971bf37767e2 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -12,7 +12,9 @@ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::{Node, find_attr, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode, TraitErrors, WellFormedLoc}; -use rustc_lint_defs::builtin::{DEAD_CODE, UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; +use rustc_lint_defs::builtin::{ + ALIGNED_FIELDS_IN_PACKED, DEAD_CODE, UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS, +}; use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; @@ -1670,6 +1672,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { } } } + if repr.align.is_some() { struct_span_code_err!( tcx.dcx(), @@ -1678,51 +1681,60 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { "type has conflicting packed and align representation hints" ) .emit(); - } else if let Some(def_spans) = check_packed_inner(tcx, def.did(), &mut vec![]) { - let mut err = struct_span_code_err!( - tcx.dcx(), + } else if repr.c() + && let Some(def_spans) = check_packed_inner(tcx, def.did(), &mut vec![]) + { + tcx.emit_node_span_lint( + ALIGNED_FIELDS_IN_PACKED, + tcx.local_def_id_to_hir_id(def.did().as_local().unwrap()), sp, - E0588, - "packed type cannot transitively contain a `#[repr(align)]` type" - ); - - err.span_note( - tcx.def_span(def_spans[0].0), - format!("`{}` has a `#[repr(align)]` attribute", tcx.item_name(def_spans[0].0)), - ); + rustc_errors::DiagDecorator(|diag| { + diag.primary_message( + "packed type cannot transitively contain a `#[repr(align)]` type", + ); - if def_spans.len() > 2 { - let mut first = true; - for (adt_def, span) in def_spans.iter().skip(1).rev() { - let ident = tcx.item_name(*adt_def); - err.span_note( - *span, - if first { - format!( - "`{}` contains a field of type `{}`", - tcx.type_of(def.did()).instantiate_identity().skip_norm_wip(), - ident - ) - } else { - format!("...which contains a field of type `{ident}`") - }, + diag.span_note( + tcx.def_span(def_spans[0].0), + format!( + "`{}` has a `#[repr(align)]` attribute", + tcx.item_name(def_spans[0].0) + ), ); - first = false; - } - } - err.emit(); + if def_spans.len() > 2 { + let mut first = true; + for (adt_def, span) in def_spans.iter().skip(1).rev() { + let ident = tcx.item_name(*adt_def); + diag.span_note( + *span, + if first { + format!( + "`{}` contains a field of type `{}`", + tcx.type_of(def.did()) + .instantiate_identity() + .skip_norm_wip(), + ident + ) + } else { + format!("...which contains a field of type `{ident}`") + }, + ); + first = false; + } + } + }), + ); } } } -pub(super) fn check_packed_inner( +fn check_packed_inner( tcx: TyCtxt<'_>, def_id: DefId, stack: &mut Vec, ) -> Option> { if let ty::Adt(def, args) = tcx.type_of(def_id).instantiate_identity().skip_norm_wip().kind() { - if def.is_struct() || def.is_union() { + if def.repr().c() && (def.is_struct() || def.is_union()) { if def.repr().align.is_some() { return Some(vec![(def.did(), DUMMY_SP)]); } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a51c66f746ad7..de4cacbd76119 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -17,6 +17,7 @@ pub mod hardwired { // tidy-alphabetical-start AARCH64_SOFTFLOAT_NEON, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + ALIGNED_FIELDS_IN_PACKED, AMBIGUOUS_ASSOCIATED_ITEMS, AMBIGUOUS_DERIVE_HELPERS, AMBIGUOUS_GLOB_IMPORTED_TRAITS, @@ -5790,3 +5791,32 @@ declare_lint! { "duplicate tools found in crate-level `#[register_tools]` directives", @feature_gate = register_tool; } + +declare_lint! { + /// The `aligned_fields_in_packed` lint detects fields with `align` representation hints + /// inside `repr(C)` types with `packed` representation hint. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[repr(C, align(16))] + /// struct Aligned(i32); + /// + /// #[repr(C, packed)] // error! + /// struct Packed(Aligned); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The behavior of this combination of hints is inconsistent across C compilers. The layout + /// computed for these types by Rust may thus not match the layout actually used by C. + /// Specifically, Rust always follows the GCC convention, which makes it incompatible with MSVC + /// for these types. This may change in the future for targets where GCC is not the default C + /// compiler. + pub ALIGNED_FIELDS_IN_PACKED, + Deny, + "`repr(C, align)` types nested inside `repr(C, packed)` types \ + do not always have a C-compatible layout", +} diff --git a/tests/ui/repr/packed-struct-contains-aligned-type-73112.rs b/tests/ui/repr/packed-struct-contains-aligned-type-73112.rs index baeb75beb0aa1..f8272e6f652e5 100644 --- a/tests/ui/repr/packed-struct-contains-aligned-type-73112.rs +++ b/tests/ui/repr/packed-struct-contains-aligned-type-73112.rs @@ -8,7 +8,7 @@ fn main() { #[repr(C, packed)] struct SomeStruct { - //~^ ERROR packed type cannot transitively contain a `#[repr(align)]` type [E0588] + //~^ ERROR packed type cannot transitively contain a `#[repr(align)]` type page_table: PageTable, } } diff --git a/tests/ui/repr/packed-struct-contains-aligned-type-73112.stderr b/tests/ui/repr/packed-struct-contains-aligned-type-73112.stderr index 237c357db22ba..8cba8bf75c437 100644 --- a/tests/ui/repr/packed-struct-contains-aligned-type-73112.stderr +++ b/tests/ui/repr/packed-struct-contains-aligned-type-73112.stderr @@ -1,4 +1,4 @@ -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type +error: packed type cannot transitively contain a `#[repr(align)]` type --> $DIR/packed-struct-contains-aligned-type-73112.rs:10:5 | LL | struct SomeStruct { @@ -9,7 +9,7 @@ note: `PageTable` has a `#[repr(align)]` attribute | LL | pub struct PageTable { | ^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(aligned_fields_in_packed)]` on by default error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0588`. diff --git a/tests/ui/repr/repr-packed-contains-align.rs b/tests/ui/repr/repr-packed-contains-align.rs index bef5c7d8c62fc..06c8e92249866 100644 --- a/tests/ui/repr/repr-packed-contains-align.rs +++ b/tests/ui/repr/repr-packed-contains-align.rs @@ -1,53 +1,66 @@ #![allow(dead_code)] -#[repr(align(16))] +#[repr(C, align(16))] #[derive(Clone, Copy)] struct SA(i32); +#[repr(align(16))] +#[derive(Clone, Copy)] +struct SARust(i32); + +#[repr(C)] #[derive(Clone, Copy)] struct SB(SA); -#[repr(align(16))] +#[repr(C, align(16))] #[derive(Clone, Copy)] union UA { i: i32 } +#[repr(C)] #[derive(Clone, Copy)] union UB { a: UA } -#[repr(packed)] +#[repr(C, packed)] struct SC(SA); //~ ERROR: packed type cannot transitively contain a `#[repr(align)]` type -#[repr(packed)] +#[repr(C, packed)] struct SD(SB); //~ ERROR: packed type cannot transitively contain a `#[repr(align)]` type -#[repr(packed)] +#[repr(C, packed)] struct SE(UA); //~ ERROR: packed type cannot transitively contain a `#[repr(align)]` type -#[repr(packed)] +#[repr(C, packed)] struct SF(UB); //~ ERROR: packed type cannot transitively contain a `#[repr(align)]` type -#[repr(packed)] +#[repr(C, packed)] union UC { //~ ERROR: packed type cannot transitively contain a `#[repr(align)]` type a: UA } -#[repr(packed)] +#[repr(C, packed)] union UD { //~ ERROR: packed type cannot transitively contain a `#[repr(align)]` type n: UB } -#[repr(packed)] +#[repr(C, packed)] union UE { //~ ERROR: packed type cannot transitively contain a `#[repr(align)]` type a: SA } -#[repr(packed)] +#[repr(C, packed)] union UF { //~ ERROR: packed type cannot transitively contain a `#[repr(align)]` type n: SB } +#[repr(packed)] +struct SG(SA); // outer type not `repr(C)`, no lint +#[repr(C, packed)] +struct SH(SARust); // inner type not `repr(C)`, no lint + + + fn main() {} diff --git a/tests/ui/repr/repr-packed-contains-align.stderr b/tests/ui/repr/repr-packed-contains-align.stderr index 4c3a960cad2a6..4c94cda745d2e 100644 --- a/tests/ui/repr/repr-packed-contains-align.stderr +++ b/tests/ui/repr/repr-packed-contains-align.stderr @@ -1,5 +1,5 @@ -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type - --> $DIR/repr-packed-contains-align.rs:22:1 +error: packed type cannot transitively contain a `#[repr(align)]` type + --> $DIR/repr-packed-contains-align.rs:28:1 | LL | struct SC(SA); | ^^^^^^^^^ @@ -9,9 +9,10 @@ note: `SA` has a `#[repr(align)]` attribute | LL | struct SA(i32); | ^^^^^^^^^ + = note: `#[deny(aligned_fields_in_packed)]` on by default -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type - --> $DIR/repr-packed-contains-align.rs:25:1 +error: packed type cannot transitively contain a `#[repr(align)]` type + --> $DIR/repr-packed-contains-align.rs:31:1 | LL | struct SD(SB); | ^^^^^^^^^ @@ -22,86 +23,86 @@ note: `SA` has a `#[repr(align)]` attribute LL | struct SA(i32); | ^^^^^^^^^ note: `SD` contains a field of type `SB` - --> $DIR/repr-packed-contains-align.rs:25:11 + --> $DIR/repr-packed-contains-align.rs:31:11 | LL | struct SD(SB); | ^^ note: ...which contains a field of type `SA` - --> $DIR/repr-packed-contains-align.rs:8:11 + --> $DIR/repr-packed-contains-align.rs:13:11 | LL | struct SB(SA); | ^^ -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type - --> $DIR/repr-packed-contains-align.rs:28:1 +error: packed type cannot transitively contain a `#[repr(align)]` type + --> $DIR/repr-packed-contains-align.rs:34:1 | LL | struct SE(UA); | ^^^^^^^^^ | note: `UA` has a `#[repr(align)]` attribute - --> $DIR/repr-packed-contains-align.rs:12:1 + --> $DIR/repr-packed-contains-align.rs:17:1 | LL | union UA { | ^^^^^^^^ -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type - --> $DIR/repr-packed-contains-align.rs:31:1 +error: packed type cannot transitively contain a `#[repr(align)]` type + --> $DIR/repr-packed-contains-align.rs:37:1 | LL | struct SF(UB); | ^^^^^^^^^ | note: `UA` has a `#[repr(align)]` attribute - --> $DIR/repr-packed-contains-align.rs:12:1 + --> $DIR/repr-packed-contains-align.rs:17:1 | LL | union UA { | ^^^^^^^^ note: `SF` contains a field of type `UB` - --> $DIR/repr-packed-contains-align.rs:31:11 + --> $DIR/repr-packed-contains-align.rs:37:11 | LL | struct SF(UB); | ^^ note: ...which contains a field of type `UA` - --> $DIR/repr-packed-contains-align.rs:18:5 + --> $DIR/repr-packed-contains-align.rs:24:5 | LL | a: UA | ^ -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type - --> $DIR/repr-packed-contains-align.rs:34:1 +error: packed type cannot transitively contain a `#[repr(align)]` type + --> $DIR/repr-packed-contains-align.rs:40:1 | LL | union UC { | ^^^^^^^^ | note: `UA` has a `#[repr(align)]` attribute - --> $DIR/repr-packed-contains-align.rs:12:1 + --> $DIR/repr-packed-contains-align.rs:17:1 | LL | union UA { | ^^^^^^^^ -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type - --> $DIR/repr-packed-contains-align.rs:39:1 +error: packed type cannot transitively contain a `#[repr(align)]` type + --> $DIR/repr-packed-contains-align.rs:45:1 | LL | union UD { | ^^^^^^^^ | note: `UA` has a `#[repr(align)]` attribute - --> $DIR/repr-packed-contains-align.rs:12:1 + --> $DIR/repr-packed-contains-align.rs:17:1 | LL | union UA { | ^^^^^^^^ note: `UD` contains a field of type `UB` - --> $DIR/repr-packed-contains-align.rs:40:5 + --> $DIR/repr-packed-contains-align.rs:46:5 | LL | n: UB | ^ note: ...which contains a field of type `UA` - --> $DIR/repr-packed-contains-align.rs:18:5 + --> $DIR/repr-packed-contains-align.rs:24:5 | LL | a: UA | ^ -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type - --> $DIR/repr-packed-contains-align.rs:44:1 +error: packed type cannot transitively contain a `#[repr(align)]` type + --> $DIR/repr-packed-contains-align.rs:50:1 | LL | union UE { | ^^^^^^^^ @@ -112,8 +113,8 @@ note: `SA` has a `#[repr(align)]` attribute LL | struct SA(i32); | ^^^^^^^^^ -error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type - --> $DIR/repr-packed-contains-align.rs:49:1 +error: packed type cannot transitively contain a `#[repr(align)]` type + --> $DIR/repr-packed-contains-align.rs:55:1 | LL | union UF { | ^^^^^^^^ @@ -124,16 +125,15 @@ note: `SA` has a `#[repr(align)]` attribute LL | struct SA(i32); | ^^^^^^^^^ note: `UF` contains a field of type `SB` - --> $DIR/repr-packed-contains-align.rs:50:5 + --> $DIR/repr-packed-contains-align.rs:56:5 | LL | n: SB | ^ note: ...which contains a field of type `SA` - --> $DIR/repr-packed-contains-align.rs:8:11 + --> $DIR/repr-packed-contains-align.rs:13:11 | LL | struct SB(SA); | ^^ error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0588`.