[clang] [clang] diagnose invalid std::tuple_size sizes (PR #159677)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 19 10:54:55 PDT 2025
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/159677
>From 2b8e0e637ee7f1ab4d55b3926f2d694d05d168bb Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Thu, 18 Sep 2025 20:22:40 -0300
Subject: [PATCH] [clang] diagnose invalid std::tuple_size sizes
Fixes #159563
---
clang/docs/ReleaseNotes.rst | 6 +++--
.../clang/Basic/DiagnosticSemaKinds.td | 3 +++
clang/lib/Sema/SemaDeclCXX.cpp | 25 ++++++++++++++-----
.../builtin-structured-binding-size.cpp | 20 +++++++++++++--
4 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c898784b3f93e..ec14594761f3c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -262,6 +262,8 @@ Improvements to Clang's diagnostics
Moved the warning for a missing (though implied) attribute on a redeclaration into this group.
Added a new warning in this group for the case where the attribute is missing/implicit on
an override of a virtual method.
+- Implemented diagnostics when retrieving the tuple size for types where its specialization of `std::tuple_size`
+ produces an invalid size (either negative or greater than the implementation limit). (#GH159563)
- Fixed fix-it hint for fold expressions. Clang now correctly places the suggested right
parenthesis when diagnosing malformed fold expressions. (#GH151787)
- Added fix-it hint for when scoped enumerations require explicit conversions for binary operations. (#GH24265)
@@ -347,8 +349,8 @@ Bug Fixes in This Version
and vector of 4 ``float`` values. (#GH155405)
- Fixed inconsistent shadow warnings for lambda capture of structured bindings.
Previously, ``[val = val]`` (regular parameter) produced no warnings with ``-Wshadow``
- while ``[a = a]`` (where ``a`` is from ``auto [a, b] = std::make_pair(1, 2)``)
- incorrectly produced warnings. Both cases now consistently show no warnings with
+ while ``[a = a]`` (where ``a`` is from ``auto [a, b] = std::make_pair(1, 2)``)
+ incorrectly produced warnings. Both cases now consistently show no warnings with
``-Wshadow`` and show uncaptured-local warnings with ``-Wshadow-all``. (#GH68605)
- Fixed a failed assertion with a negative limit parameter value inside of
``__has_embed``. (#GH157842)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8b862ae47af89..42608debe8609 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -638,6 +638,9 @@ def err_decomp_decl_std_tuple_element_not_specialized : Error<
def err_decomp_decl_std_tuple_size_not_constant : Error<
"cannot decompose this type; 'std::tuple_size<%0>::value' "
"is not a valid integral constant expression">;
+def err_decomp_decl_std_tuple_size_invalid
+ : Error<"cannot decompose this type; 'std::tuple_size<%0>::value' "
+ "is not a valid size: %1">;
def note_in_binding_decl_init : Note<
"in implicit initialization of binding declaration %0">;
def err_arg_is_not_destructurable : Error<
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index fb57b43882911..ea08f41437e70 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1177,7 +1177,7 @@ getTrivialTypeTemplateArgument(Sema &S, SourceLocation Loc, QualType T) {
namespace { enum class IsTupleLike { TupleLike, NotTupleLike, Error }; }
static IsTupleLike isTupleLike(Sema &S, SourceLocation Loc, QualType T,
- llvm::APSInt &Size) {
+ unsigned &OutSize) {
EnterExpressionEvaluationContext ContextRAII(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
@@ -1218,10 +1218,24 @@ static IsTupleLike isTupleLike(Sema &S, SourceLocation Loc, QualType T,
if (E.isInvalid())
return IsTupleLike::Error;
+ llvm::APSInt Size;
E = S.VerifyIntegerConstantExpression(E.get(), &Size, Diagnoser);
if (E.isInvalid())
return IsTupleLike::Error;
+ // The implementation limit is UINT_MAX-1, to allow this to be passed down on
+ // an UnsignedOrNone.
+ if (Size < 0 || Size >= UINT_MAX) {
+ llvm::SmallVector<char, 16> Str;
+ Size.toString(Str);
+ S.Diag(Loc, diag::err_decomp_decl_std_tuple_size_invalid)
+ << printTemplateArgs(S.Context.getPrintingPolicy(), Args,
+ /*Params=*/nullptr)
+ << StringRef(Str.data(), Str.size());
+ return IsTupleLike::Error;
+ }
+
+ OutSize = Size.getExtValue();
return IsTupleLike::TupleLike;
}
@@ -1279,9 +1293,8 @@ struct InitializingBinding {
static bool checkTupleLikeDecomposition(Sema &S,
ArrayRef<BindingDecl *> Bindings,
VarDecl *Src, QualType DecompType,
- const llvm::APSInt &TupleSize) {
+ unsigned NumElems) {
auto *DD = cast<DecompositionDecl>(Src);
- unsigned NumElems = (unsigned)TupleSize.getLimitedValue(UINT_MAX);
if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems))
return true;
@@ -1641,7 +1654,7 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
// C++1z [dcl.decomp]/3:
// if the expression std::tuple_size<E>::value is a well-formed integral
// constant expression, [...]
- llvm::APSInt TupleSize(32);
+ unsigned TupleSize;
switch (isTupleLike(*this, DD->getLocation(), DecompType, TupleSize)) {
case IsTupleLike::Error:
DD->setInvalidDecl();
@@ -1690,12 +1703,12 @@ UnsignedOrNone Sema::GetDecompositionElementCount(QualType T,
if (T->getAs<ComplexType>())
return 2u;
- llvm::APSInt TupleSize(Ctx.getTypeSize(Ctx.getSizeType()));
+ unsigned TupleSize;
switch (isTupleLike(*this, Loc, T, TupleSize)) {
case IsTupleLike::Error:
return std::nullopt;
case IsTupleLike::TupleLike:
- return static_cast<unsigned>(TupleSize.getExtValue());
+ return TupleSize;
case IsTupleLike::NotTupleLike:
break;
}
diff --git a/clang/test/SemaCXX/builtin-structured-binding-size.cpp b/clang/test/SemaCXX/builtin-structured-binding-size.cpp
index 53576048754ab..bcd13a6fb720d 100644
--- a/clang/test/SemaCXX/builtin-structured-binding-size.cpp
+++ b/clang/test/SemaCXX/builtin-structured-binding-size.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
-// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 %s -triple=x86_64 -std=c++2c -fsyntax-only -verify
+// RUN: %clang_cc1 %s -triple=x86_64 -std=c++2c -fsyntax-only -verify -fexperimental-new-constant-interpreter
struct S0 {};
@@ -229,3 +229,19 @@ static_assert(__is_same_as(tag_of_t<S1>, int));
static_assert(__is_same_as(tag_of_t<int>, int)); // error
// expected-error at -1 {{constraints not satisfied for alias template 'tag_of_t' [with T = int]}}
// expected-note@#tag-of-constr {{because substituted constraint expression is ill-formed: type 'int' cannot be decomposed}}
+
+struct MinusOne;
+template <> struct ::std::tuple_size<MinusOne> {
+ static constexpr int value = -1;
+};
+int minus_one = __builtin_structured_binding_size(MinusOne);
+// expected-error at -1 {{cannot decompose this type; 'std::tuple_size<MinusOne>::value' is not a valid size: -1}}
+// expected-error at -2 {{type 'MinusOne' cannot be decomposed}}
+
+struct UintMax;
+template <> struct ::std::tuple_size<UintMax> {
+ static constexpr unsigned value = -1;
+};
+int uint_max = __builtin_structured_binding_size(UintMax);
+// expected-error at -1 {{cannot decompose this type; 'std::tuple_size<UintMax>::value' is not a valid size: 4294967295}}
+// expected-error at -2 {{type 'UintMax' cannot be decomposed}}
More information about the cfe-commits
mailing list