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
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0588.md
Original file line number Diff line number Diff line change
@@ -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);

Expand Down
78 changes: 45 additions & 33 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
Expand All @@ -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<DefId>,
) -> Option<Vec<(DefId, Span)>> {
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)]);
}
Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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",
}
2 changes: 1 addition & 1 deletion tests/ui/repr/packed-struct-contains-aligned-type-73112.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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`.
33 changes: 23 additions & 10 deletions tests/ui/repr/repr-packed-contains-align.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Loading
Loading