[clang] [CIR] Reject void record members, array elements and union padding (PR #233898)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 20 23:12:12 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clangir
Author: Henrich Lauko (xlauko)
<details>
<summary>Changes</summary>
!cir.void has no data layout, so a record or array that holds one has
none either. The verifiers accepted such types, and the first size query
crashed. CallConvLowering hit a DataLayout fatal error on a union
argument, and LowerToLLVM asserted in LLVMStructType::setBody.
The record and array verifiers now reject void, so no consumer has to
guard against it. CallConvLowering still accepts void as a parameter
type, which the x86_64 classifier ignores.
Assisted-by: Claude Code (Claude Opus 5.5)
---
Full diff: https://github.com/llvm/llvm-project/pull/233898.diff
4 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td (+4)
- (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.td (+1-1)
- (modified) clang/lib/CIR/Dialect/IR/CIRTypes.cpp (+17)
- (added) clang/test/CIR/IR/invalid-void-member.cir (+31)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index 97d7b30236b82..d294f6a9a0371 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -281,6 +281,10 @@ class CIR_PtrToAnyOf<list<Type> types, string summary = "">
def CIR_AnyVoidType : CIR_TypeBase<"::cir::VoidType", "void type">,
BuildableType<"$_builder.getType<" # cppType # ">()">;
+// Void has no layout, so neither does an array of it.
+def CIR_ArrayElementType
+ : Type<Neg<CIR_AnyVoidType.predicate>, "any type except void">;
+
// Void pointer type constraints
def CIR_VoidPtrType : CIR_PtrTo<CIR_AnyVoidType, "void type">;
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 366a4669c3a56..9cdb493a569be 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -456,7 +456,7 @@ def CIR_ArrayType : CIR_Type<"Array", "array", [
`!cir.array` represents C/C++ constant arrays.
}];
- let parameters = (ins "mlir::Type":$elementType, "uint64_t":$size);
+ let parameters = (ins CIR_ArrayElementType:$elementType, "uint64_t":$size);
let builders = [
TypeBuilderWithInferredContext<(ins
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index ecdd5fd8584d0..4ccad854c7b46 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -171,6 +171,17 @@ verifyRecordMemberKinds(function_ref<mlir::InFlightDiagnostic()> emitError,
return mlir::success();
}
+/// A void member has no layout, so a record holding one has none either.
+static mlir::LogicalResult
+verifyRecordMemberTypes(function_ref<mlir::InFlightDiagnostic()> emitError,
+ llvm::ArrayRef<mlir::Type> members) {
+ for (auto [idx, memberTy] : llvm::enumerate(members))
+ if (mlir::isa<cir::VoidType>(memberTy))
+ return emitError() << "record member at index " << idx
+ << " cannot be void";
+ return mlir::success();
+}
+
/// The keywords that spell a member kind. A union's tail-padding slot probes
/// for one of these to reject it, since that slot is not a member.
static const llvm::StringRef memberKindMarks[] = {"data", "pad", "empty",
@@ -349,6 +360,8 @@ mlir::LogicalResult StructType::verify(
bool packed, llvm::ArrayRef<RecordMemberKind> member_kinds, bool is_class) {
if (name && name.getValue().empty())
return emitError() << "identified records cannot have an empty name";
+ if (failed(verifyRecordMemberTypes(emitError, members)))
+ return mlir::failure();
return verifyRecordMemberKinds(emitError, members.size(), member_kinds);
}
@@ -524,6 +537,10 @@ UnionType::verify(function_ref<mlir::InFlightDiagnostic()> emitError,
// padding slot.
if (llvm::is_contained(member_kinds, RecordMemberKind::Pad))
return emitError() << "a union member cannot be marked pad";
+ if (failed(verifyRecordMemberTypes(emitError, members)))
+ return mlir::failure();
+ if (mlir::isa_and_nonnull<cir::VoidType>(padding))
+ return emitError() << "union padding cannot be void";
// Every variant starts at offset zero, so a bit-field variant is an access
// unit of its own. There is no run for a zero-width bit-field to end, so a
// variant always has storage.
diff --git a/clang/test/CIR/IR/invalid-void-member.cir b/clang/test/CIR/IR/invalid-void-member.cir
new file mode 100644
index 0000000000000..e5eb295a3efa1
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-void-member.cir
@@ -0,0 +1,31 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+// Void has no layout, so it cannot be a member, element or padding.
+
+!s32i = !cir.int<s, 32>
+// expected-error @below {{record member at index 1 cannot be void}}
+!rec_S = !cir.struct<"S" {data !s32i, data !cir.void}>
+
+module {}
+
+// -----
+
+// expected-error @below {{record member at index 0 cannot be void}}
+!rec_U = !cir.union<"U" {data !cir.void}>
+
+module {}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+// expected-error @below {{union padding cannot be void}}
+!rec_U = !cir.union<"U" {data !s32i}, padding = {!cir.void}>
+
+module {}
+
+// -----
+
+// expected-error @below {{failed to verify 'elementType': any type except void}}
+!arr = !cir.array<!cir.void x 4>
+
+module {}
``````````
</details>
https://github.com/llvm/llvm-project/pull/233898
More information about the cfe-commits
mailing list