[clang] [clang][Sema] Reject oversized arrays deduced from initializer lists (PR #226983)
Kiroo via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 7 19:15:45 PDT 2026
https://github.com/zlfn updated https://github.com/llvm/llvm-project/pull/226983
>From c6ad057f6b28a3d78338e312ee6bd4d538574292 Mon Sep 17 00:00:00 2001
From: zlfn <ung at zlfn.space>
Date: Mon, 28 Sep 2026 22:19:59 +0900
Subject: [PATCH 1/5] [clang][Sema] Reject oversized arrays deduced from
initializer lists
An array sized by its initializer list skipped the size check that
arrays with an explicit bound go through. On targets with a 16-bit
size_t, the element counter also wrapped and truncated the size.
Count the elements in 64 bits and apply the same size limit.
---
clang/docs/ReleaseNotes.md | 2 ++
clang/lib/Sema/SemaInit.cpp | 16 +++++++++
clang/test/Sema/array-init-list-too-large.c | 37 +++++++++++++++++++++
3 files changed, 55 insertions(+)
create mode 100644 clang/test/Sema/array-init-list-too-large.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6acf353f93f..d713fae8f3f1d 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -561,6 +561,8 @@ features cannot lower the translation-unit ABI level;
- Fixed a bug where a bit-field accessed as the result of a statement expression
(e.g. `({ s.b; })`) was not subject to integer promotion, unlike an ordinary
bit-field access. (#GH221542)
+- Clang now diagnoses arrays whose size is deduced from an initializer list
+ when they exceed the maximum object size.
#### Bug Fixes to Compiler Builtins
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 3f30e94aa8976..99e5517720f90 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -2208,6 +2208,10 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
return;
}
+ // Count in 64 bits so that the index cannot wrap with a narrow size_t.
+ if (elementIndex.getBitWidth() < 64)
+ elementIndex = elementIndex.extend(64);
+
// We might know the maximum number of elements in advance.
llvm::APSInt maxElements(elementIndex.getBitWidth(),
elementIndex.isUnsigned());
@@ -2293,6 +2297,18 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
SemaRef.Diag(IList->getBeginLoc(), diag::ext_typecheck_zero_array_size);
}
+ if (!elementType->isDependentType() && !elementType->isIncompleteType() &&
+ ConstantArrayType::getNumAddressingBits(SemaRef.Context, elementType,
+ maxElements) >
+ ConstantArrayType::getMaxSizeBits(SemaRef.Context)) {
+ SemaRef.Diag(IList->getBeginLoc(), diag::err_array_too_large)
+ << toString(maxElements, 10, maxElements.isSigned(),
+ /*formatAsCLiteral=*/false, /*UpperCase=*/false,
+ /*InsertSeparators=*/true);
+ hadError = true;
+ return;
+ }
+
DeclType = SemaRef.Context.getConstantArrayType(
elementType, maxElements, nullptr, ArraySizeModifier::Normal, 0);
}
diff --git a/clang/test/Sema/array-init-list-too-large.c b/clang/test/Sema/array-init-list-too-large.c
new file mode 100644
index 0000000000000..705eed7ee009e
--- /dev/null
+++ b/clang/test/Sema/array-init-list-too-large.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple i386 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple msp430 -fsyntax-only -verify=expected,size16 %s
+// RUN: %clang_cc1 -triple avr -fsyntax-only -verify=expected,size16 %s
+// RUN: %clang_cc1 -triple msp430 -fsyntax-only -verify=expected,size16 -x c++ %s
+
+typedef char Quarter[__SIZE_MAX__ / 4 + 1];
+Quarter fits[] = {{0}, {0}, {0}};
+Quarter explicit_bound[4]; // expected-error {{array is too large (4 elements)}}
+Quarter deduced_bound[] = {{0}, {0}, {0}, {0}}; // expected-error {{array is too large (4 elements)}}
+
+#define X2(x) x, x
+#define X4(x) X2(x), X2(x)
+#define X8(x) X4(x), X4(x)
+#define X16(x) X8(x), X8(x)
+#define X32(x) X16(x), X16(x)
+#define X64(x) X32(x), X32(x)
+#define X128(x) X64(x), X64(x)
+#define X256(x) X128(x), X128(x)
+#define X512(x) X256(x), X256(x)
+#define X1024(x) X512(x), X512(x)
+#define X2048(x) X1024(x), X1024(x)
+#define X4096(x) X2048(x), X2048(x)
+#define X8192(x) X4096(x), X4096(x)
+#define X16384(x) X8192(x), X8192(x)
+#define X32768(x) X16384(x), X16384(x)
+
+char past_int16_max[] = {X32768(0), 0};
+_Static_assert(sizeof(past_int16_max) == 32769, "");
+
+char past_size16_max[] = {X32768(0), X32768(0)}; // size16-error {{array is too large (65'536 elements)}}
+
+#ifdef __cplusplus
+template <typename T> void deduced_in_template() {
+ static T a[] = {{0}, {0}, {0}, {0}}; // expected-error {{array is too large (4 elements)}}
+}
+template void deduced_in_template<Quarter>(); // expected-note {{in instantiation of}}
+#endif
>From 2cd8ea9d8f6901361355c428ca594714d485b3a1 Mon Sep 17 00:00:00 2001
From: zlfn <ung at zlfn.space>
Date: Mon, 28 Sep 2026 22:33:58 +0900
Subject: [PATCH 2/5] Check the element count for zero-sized elements
---
clang/lib/Sema/SemaInit.cpp | 10 ++++++----
clang/test/Sema/array-init-list-too-large.c | 3 +++
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 99e5517720f90..3667ff280cc6e 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -2297,10 +2297,12 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
SemaRef.Diag(IList->getBeginLoc(), diag::ext_typecheck_zero_array_size);
}
- if (!elementType->isDependentType() && !elementType->isIncompleteType() &&
- ConstantArrayType::getNumAddressingBits(SemaRef.Context, elementType,
- maxElements) >
- ConstantArrayType::getMaxSizeBits(SemaRef.Context)) {
+ unsigned ActiveSizeBits = maxElements.getActiveBits();
+ if (!elementType->isDependentType() && !elementType->isIncompleteType())
+ ActiveSizeBits = std::max(ActiveSizeBits,
+ ConstantArrayType::getNumAddressingBits(
+ SemaRef.Context, elementType, maxElements));
+ if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(SemaRef.Context)) {
SemaRef.Diag(IList->getBeginLoc(), diag::err_array_too_large)
<< toString(maxElements, 10, maxElements.isSigned(),
/*formatAsCLiteral=*/false, /*UpperCase=*/false,
diff --git a/clang/test/Sema/array-init-list-too-large.c b/clang/test/Sema/array-init-list-too-large.c
index 705eed7ee009e..74a235a8fbd7f 100644
--- a/clang/test/Sema/array-init-list-too-large.c
+++ b/clang/test/Sema/array-init-list-too-large.c
@@ -29,6 +29,9 @@ _Static_assert(sizeof(past_int16_max) == 32769, "");
char past_size16_max[] = {X32768(0), X32768(0)}; // size16-error {{array is too large (65'536 elements)}}
+struct E {};
+struct E zero_sized[] = {X32768({}), X32768({})}; // size16-error {{array is too large (65'536 elements)}}
+
#ifdef __cplusplus
template <typename T> void deduced_in_template() {
static T a[] = {{0}, {0}, {0}, {0}}; // expected-error {{array is too large (4 elements)}}
>From a9fe1b0ccd09dccb006d4a4c101f04f2148d1d91 Mon Sep 17 00:00:00 2001
From: zlfn <ung at zlfn.space>
Date: Mon, 28 Sep 2026 23:15:56 +0900
Subject: [PATCH 3/5] Test x86_64 as well
---
clang/test/Sema/array-init-list-too-large.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/clang/test/Sema/array-init-list-too-large.c b/clang/test/Sema/array-init-list-too-large.c
index 74a235a8fbd7f..08a0aba154c04 100644
--- a/clang/test/Sema/array-init-list-too-large.c
+++ b/clang/test/Sema/array-init-list-too-large.c
@@ -1,9 +1,15 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple i386 -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple msp430 -fsyntax-only -verify=expected,size16 %s
// RUN: %clang_cc1 -triple avr -fsyntax-only -verify=expected,size16 %s
// RUN: %clang_cc1 -triple msp430 -fsyntax-only -verify=expected,size16 -x c++ %s
+#if __SIZEOF_SIZE_T__ == 8
+// Array sizes are limited to 61 bits.
+typedef char Quarter[1ULL << 59];
+#else
typedef char Quarter[__SIZE_MAX__ / 4 + 1];
+#endif
Quarter fits[] = {{0}, {0}, {0}};
Quarter explicit_bound[4]; // expected-error {{array is too large (4 elements)}}
Quarter deduced_bound[] = {{0}, {0}, {0}, {0}}; // expected-error {{array is too large (4 elements)}}
>From 395a4619533025777b46af35eff779abb6ba8dbf Mon Sep 17 00:00:00 2001
From: zlfn <ung at zlfn.space>
Date: Tue, 29 Sep 2026 02:33:58 +0900
Subject: [PATCH 4/5] Factor out the array size check
---
clang/include/clang/Sema/Sema.h | 6 ++++++
clang/lib/Sema/SemaExprCXX.cpp | 14 +++----------
clang/lib/Sema/SemaInit.cpp | 12 ++---------
clang/lib/Sema/SemaType.cpp | 36 +++++++++++++++++++++------------
4 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index eb3b908351448..f4047558d747c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15313,6 +15313,12 @@ class Sema final : public SemaBase {
QualType BuildArrayType(QualType T, ArraySizeModifier ASM, Expr *ArraySize,
unsigned Quals, SourceRange Brackets,
DeclarationName Entity);
+
+ /// Diagnose an array of \p NumElements elements of type \p ElementType
+ /// that is too large. Returns true if a diagnostic was emitted.
+ bool checkArrayTooLarge(QualType ElementType, const llvm::APSInt &NumElements,
+ SourceLocation Loc,
+ SourceRange Range = SourceRange());
QualType BuildVectorType(QualType T, Expr *VecSize, SourceLocation AttrLoc);
/// Build an ext-vector type.
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index deb7bdd09fdc7..d92ee2f7506de 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2406,17 +2406,9 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
<< (*ArraySize)->getSourceRange());
}
- if (!AllocType->isDependentType()) {
- unsigned ActiveSizeBits =
- ConstantArrayType::getNumAddressingBits(Context, AllocType, *Value);
- if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(Context))
- return ExprError(
- Diag((*ArraySize)->getBeginLoc(), diag::err_array_too_large)
- << toString(*Value, 10, Value->isSigned(),
- /*formatAsCLiteral=*/false, /*UpperCase=*/false,
- /*InsertSeparators=*/true)
- << (*ArraySize)->getSourceRange());
- }
+ if (checkArrayTooLarge(AllocType, *Value, (*ArraySize)->getBeginLoc(),
+ (*ArraySize)->getSourceRange()))
+ return ExprError();
KnownArraySize = Value->getZExtValue();
} else if (TypeIdParens.isValid()) {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 3667ff280cc6e..d2aa8691102aa 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -2297,16 +2297,8 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
SemaRef.Diag(IList->getBeginLoc(), diag::ext_typecheck_zero_array_size);
}
- unsigned ActiveSizeBits = maxElements.getActiveBits();
- if (!elementType->isDependentType() && !elementType->isIncompleteType())
- ActiveSizeBits = std::max(ActiveSizeBits,
- ConstantArrayType::getNumAddressingBits(
- SemaRef.Context, elementType, maxElements));
- if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(SemaRef.Context)) {
- SemaRef.Diag(IList->getBeginLoc(), diag::err_array_too_large)
- << toString(maxElements, 10, maxElements.isSigned(),
- /*formatAsCLiteral=*/false, /*UpperCase=*/false,
- /*InsertSeparators=*/true);
+ if (SemaRef.checkArrayTooLarge(elementType, maxElements,
+ IList->getBeginLoc())) {
hadError = true;
return;
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index b5c71d72a23ff..543d2838c74f2 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -2084,6 +2084,27 @@ bool Sema::checkArrayElementAlignment(QualType EltTy, SourceLocation Loc) {
return false;
}
+bool Sema::checkArrayTooLarge(QualType ElementType,
+ const llvm::APSInt &NumElements,
+ SourceLocation Loc, SourceRange Range) {
+ unsigned ActiveSizeBits = NumElements.getActiveBits();
+ if (!ElementType->isDependentType() &&
+ !ElementType->isVariablyModifiedType() &&
+ !ElementType->isIncompleteType() && !ElementType->isUndeducedType())
+ ActiveSizeBits =
+ std::max(ActiveSizeBits, ConstantArrayType::getNumAddressingBits(
+ Context, ElementType, NumElements));
+ if (ActiveSizeBits <= ConstantArrayType::getMaxSizeBits(Context))
+ return false;
+
+ Diag(Loc, diag::err_array_too_large)
+ << toString(NumElements, 10, NumElements.isSigned(),
+ /*formatAsCLiteral=*/false, /*UpperCase=*/false,
+ /*InsertSeparators=*/true)
+ << Range;
+ return true;
+}
+
QualType Sema::BuildArrayType(QualType T, ArraySizeModifier ASM,
Expr *ArraySize, unsigned Quals,
SourceRange Brackets, DeclarationName Entity) {
@@ -2307,20 +2328,9 @@ QualType Sema::BuildArrayType(QualType T, ArraySizeModifier ASM,
return QualType();
}
- // Is the array too large?
- unsigned ActiveSizeBits =
- (!T->isDependentType() && !T->isVariablyModifiedType() &&
- !T->isIncompleteType() && !T->isUndeducedType())
- ? ConstantArrayType::getNumAddressingBits(Context, T, ConstVal)
- : ConstVal.getActiveBits();
- if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(Context)) {
- Diag(ArraySize->getBeginLoc(), diag::err_array_too_large)
- << toString(ConstVal, 10, ConstVal.isSigned(),
- /*formatAsCLiteral=*/false, /*UpperCase=*/false,
- /*InsertSeparators=*/true)
- << ArraySize->getSourceRange();
+ if (checkArrayTooLarge(T, ConstVal, ArraySize->getBeginLoc(),
+ ArraySize->getSourceRange()))
return QualType();
- }
T = Context.getConstantArrayType(T, ConstVal, ArraySize, ASM, Quals);
}
>From 09fdb7f9c4e9f3ad8a95929fad28c3b621f43fbc Mon Sep 17 00:00:00 2001
From: zlfn <ung at zlfn.space>
Date: Tue, 29 Sep 2026 02:39:42 +0900
Subject: [PATCH 5/5] Check the size of arrays of pointers to VLAs
---
clang/lib/Sema/SemaType.cpp | 5 ++---
clang/test/Sema/array-init-list-too-large.c | 7 +++++++
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 543d2838c74f2..be7d377624f51 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -2088,9 +2088,8 @@ bool Sema::checkArrayTooLarge(QualType ElementType,
const llvm::APSInt &NumElements,
SourceLocation Loc, SourceRange Range) {
unsigned ActiveSizeBits = NumElements.getActiveBits();
- if (!ElementType->isDependentType() &&
- !ElementType->isVariablyModifiedType() &&
- !ElementType->isIncompleteType() && !ElementType->isUndeducedType())
+ if (!ElementType->isDependentType() && !ElementType->isIncompleteType() &&
+ !ElementType->isUndeducedType() && ElementType->isConstantSizeType())
ActiveSizeBits =
std::max(ActiveSizeBits, ConstantArrayType::getNumAddressingBits(
Context, ElementType, NumElements));
diff --git a/clang/test/Sema/array-init-list-too-large.c b/clang/test/Sema/array-init-list-too-large.c
index 08a0aba154c04..4b5a5d6f01edf 100644
--- a/clang/test/Sema/array-init-list-too-large.c
+++ b/clang/test/Sema/array-init-list-too-large.c
@@ -38,6 +38,13 @@ char past_size16_max[] = {X32768(0), X32768(0)}; // size16-error {{array is too
struct E {};
struct E zero_sized[] = {X32768({}), X32768({})}; // size16-error {{array is too large (65'536 elements)}}
+#ifndef __cplusplus
+void pointers_to_vla(int n) {
+ int (*explicit_ptrs[32768])[n]; // size16-error {{array is too large (32'768 elements)}}
+ int (*deduced_ptrs[])[n] = {X32768(0)}; // size16-error {{array is too large (32'768 elements)}}
+}
+#endif
+
#ifdef __cplusplus
template <typename T> void deduced_in_template() {
static T a[] = {{0}, {0}, {0}, {0}}; // expected-error {{array is too large (4 elements)}}
More information about the cfe-commits
mailing list