https://gcc.gnu.org/g:ac6b8763268139027c363dfe7c0eb51eedc82b6c
commit r17-4113-gac6b8763268139027c363dfe7c0eb51eedc82b6c Author: Lucas Ly Ba <[email protected]> Date: Sun Jun 28 18:42:04 2026 +0200 gccrs: add repr_c_enums_larger_than_int lint Warn on a `#[repr(C)]` enum whose discriminant fits into neither a C `int` nor a C `unsigned int`. Such an enum is non-portable: C only permits enums whose discriminants do not all fit into one of those two types since C23, and Rust interprets `repr(C)` discriminants as `isize`, so its size may not match the C one. The discriminant is const evaluated with query_compile_const_expr during type resolution, so any constant discriminant expression is handled. gcc/rust/ChangeLog: * typecheck/rust-hir-type-check-item.cc (check_repr_c_enum_discriminants): New. (TypeCheckItem::visit): Check repr(C) enum discriminants. gcc/testsuite/ChangeLog: * rust/compile/repr-c-enums-larger-than-int_0.rs: New test. Signed-off-by: Lucas Ly Ba <[email protected]> Diff: --- gcc/rust/typecheck/rust-hir-type-check-item.cc | 45 ++++++++++++++++++++++ .../rust/compile/repr-c-enums-larger-than-int_0.rs | 19 +++++++++ 2 files changed, 64 insertions(+) diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc b/gcc/rust/typecheck/rust-hir-type-check-item.cc index 112ba0b41653..1b8b929bdb81 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-item.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc @@ -36,10 +36,52 @@ #include "rust-type-util.h" #include "rust-tyty-variance-analysis.h" #include "rust-tyty.h" +#include "options.h" +#include "rust-compile-base.h" +#include "rust-compile-context.h" namespace Rust { namespace Resolver { +// Const-evaluate the discriminants of a repr(C) enum and warn when a value does +// not fit into a C int/unsigned int. Done here, during type resolution, using +// the compile context (a singleton shared with the backend). +static void +check_repr_c_enum_discriminants (Compile::Context *ctx, TyTy::BaseType *type) +{ + if (type->get_kind () != TyTy::TypeKind::ADT) + return; + + auto &adt = static_cast<TyTy::ADTType &> (*type); + if (!adt.is_enum () + || adt.get_repr_options ().repr_kind != TyTy::ADTType::ReprKind::C) + return; + + for (auto &variant : adt.get_variants ()) + { + if (!variant->has_discriminant ()) + continue; + + HIR::Expr &discriminant = variant->get_discriminant (); + TyTy::BaseType *discrim_ty = nullptr; + if (!ctx->get_tyctx ()->lookup_type ( + discriminant.get_mappings ().get_hirid (), &discrim_ty)) + continue; + + tree folded + = Compile::HIRCompileBase::query_compile_const_expr (ctx, discrim_ty, + discriminant); + if (folded == error_mark_node || TREE_CODE (folded) != INTEGER_CST) + continue; + + widest_int value = wi::to_widest (folded); + if (wi::lts_p (value, INT32_MIN) || wi::gts_p (value, UINT32_MAX)) + rust_warning_at (discriminant.get_locus (), OPT_Woverflow, + "%<repr(C)%> enum discriminant does not fit into C " + "%<int%> nor into C %<unsigned int%>"); + } +} + TypeCheckItem::TypeCheckItem () : TypeCheckBase (), infered (nullptr) {} TyTy::BaseType * @@ -493,6 +535,9 @@ TypeCheckItem::visit (HIR::Enum &enum_decl) infered = type; context->get_variance_analysis_ctx ().add_type_constraints (*type); + + if (flag_unused_check_2_0) + check_repr_c_enum_discriminants (Compile::Context::get (), type); } void diff --git a/gcc/testsuite/rust/compile/repr-c-enums-larger-than-int_0.rs b/gcc/testsuite/rust/compile/repr-c-enums-larger-than-int_0.rs new file mode 100644 index 000000000000..5dce847dbc99 --- /dev/null +++ b/gcc/testsuite/rust/compile/repr-c-enums-larger-than-int_0.rs @@ -0,0 +1,19 @@ +// { dg-additional-options "-frust-unused-check-2.0" } +// { dg-skip-if "discriminant needs a 64-bit isize" { ! lp64 } } +#![feature(no_core)] +#![no_core] + +#[repr(C)] +pub enum Big { + A = 9223372036854775807, +// { dg-warning "enum discriminant does not fit into C" "" { target *-*-* } .-1 } +} + +#[repr(C)] +pub enum Small { + B = 5, +} + +pub enum NoRepr { + C = 9223372036854775807, +}
