[clang] [CIR] Reject a record redefined with a different body (PR #230547)
Steffen Larsen via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 9 08:04:20 PDT 2026
https://github.com/steffenlarsen created https://github.com/llvm/llvm-project/pull/230547
A context holds a single record per kind and name, so parsing a named record returns any earlier definition of that name. When the earlier definition was complete, the parser kept it and silently dropped the new body, so a global declared with a different layout was given the first one. Report the mismatch instead.
Assisted-by: Claude Opus 5.5
>From 8bf824aad6f16aba390559f8d188e52f64dac7d5 Mon Sep 17 00:00:00 2001
From: Steffen Holst Larsen <sholstla at amd.com>
Date: Fri, 9 Oct 2026 10:00:00 -0500
Subject: [PATCH] [CIR] Reject a record redefined with a different body
A context holds a single record per kind and name, so parsing a named
record returns any earlier definition of that name. When the earlier
definition was complete, the parser kept it and silently dropped the new
body, so a global declared with a different layout was given the first
one. Report the mismatch instead.
Assisted-by: Claude Opus 5.5
Signed-off-by: Steffen Holst Larsen <sholstla at amd.com>
---
clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 25 ++++++--
.../CIR/IR/invalid-record-redefinition.cir | 63 +++++++++++++++++++
2 files changed, 84 insertions(+), 4 deletions(-)
create mode 100644 clang/test/CIR/IR/invalid-record-redefinition.cir
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index fe3f41a226e00e..596ae844bc6983 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -326,9 +326,17 @@ Type StructType::parse(mlir::AsmParser &parser) {
is_class, kindsRef);
if (!type)
return {};
- if (auto structTy = mlir::dyn_cast<StructType>(type))
- if (structTy.isIncomplete())
+ if (auto structTy = mlir::dyn_cast<StructType>(type)) {
+ if (structTy.isIncomplete()) {
structTy.complete(membersRef, packed, kindsRef);
+ } else if (structTy.getMembers() != membersRef ||
+ structTy.getPacked() != packed ||
+ structTy.getMemberKinds() != kindsRef) {
+ parser.emitError(loc, "record '")
+ << name.getValue() << "' redefined with a different body";
+ return {};
+ }
+ }
assert(!cir::MissingFeatures::astRecordDeclAttr());
} else {
parser.emitError(loc, "anonymous records must be complete");
@@ -491,9 +499,18 @@ Type UnionType::parse(mlir::AsmParser &parser) {
padding, kindsRef);
if (!type)
return {};
- if (auto unionTy = mlir::dyn_cast<UnionType>(type))
- if (unionTy.isIncomplete())
+ if (auto unionTy = mlir::dyn_cast<UnionType>(type)) {
+ if (unionTy.isIncomplete()) {
unionTy.complete(membersRef, packed, padding, kindsRef);
+ } else if (unionTy.getMembers() != membersRef ||
+ unionTy.getPacked() != packed ||
+ unionTy.getPadding() != padding ||
+ unionTy.getMemberKinds() != kindsRef) {
+ parser.emitError(loc, "record '")
+ << name.getValue() << "' redefined with a different body";
+ return {};
+ }
+ }
assert(!cir::MissingFeatures::astRecordDeclAttr());
} else {
parser.emitError(loc, "anonymous records must be complete");
diff --git a/clang/test/CIR/IR/invalid-record-redefinition.cir b/clang/test/CIR/IR/invalid-record-redefinition.cir
new file mode 100644
index 00000000000000..24e41fb3c35ffb
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-record-redefinition.cir
@@ -0,0 +1,63 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+// A context holds a single record per kind and name, so a second definition of
+// a name must have the same body as the first.
+
+!s32i = !cir.int<s, 32>
+module {
+ cir.global external @a = #cir.zero : !cir.struct<"S" {data !s32i}>
+ // expected-error @below {{record 'S' redefined with a different body}}
+ cir.global external @b = #cir.zero : !cir.struct<"S" {data !cir.double, data !s32i}>
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+module {
+ cir.global external @a = #cir.zero : !cir.struct<"S" {data !s32i}>
+ // expected-error @below {{record 'S' redefined with a different body}}
+ cir.global external @b = #cir.zero : !cir.struct<"S" packed {data !s32i}>
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+module {
+ cir.global external @a = #cir.zero : !cir.struct<"S" {data !u8i}>
+ // expected-error @below {{record 'S' redefined with a different body}}
+ cir.global external @b = #cir.zero : !cir.struct<"S" {pad !u8i}>
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+module {
+ cir.global external @a = #cir.zero : !cir.union<"U" {data !s32i}>
+ // expected-error @below {{record 'U' redefined with a different body}}
+ cir.global external @b = #cir.zero : !cir.union<"U" {data !s32i, data !cir.float}>
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!s32i = !cir.int<s, 32>
+module {
+ cir.global external @a = #cir.zero : !cir.union<"U" {data !s32i}>
+ // expected-error @below {{record 'U' redefined with a different body}}
+ cir.global external @b = #cir.zero : !cir.union<"U" {data !s32i}, padding = {!cir.array<!u8i x 4>}>
+}
+
+// -----
+
+// Still accepted: an identical redefinition, completing a record first seen
+// incomplete, and a struct, a class and a union that share a name, which are
+// distinct records.
+!s32i = !cir.int<s, 32>
+module {
+ cir.global external @a = #cir.zero : !cir.struct<"S" {data !s32i}>
+ cir.global external @b = #cir.zero : !cir.struct<"S" {data !s32i}>
+ cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<"F" incomplete>>
+ cir.global external @f = #cir.zero : !cir.struct<"F" {data !s32i}>
+ cir.global external @c = #cir.zero : !cir.struct<class "S" {data !cir.double}>
+ cir.global external @u = #cir.zero : !cir.union<"S" {data !s32i, data !cir.float}>
+}
More information about the cfe-commits
mailing list