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 <mizve...@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@-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@-1 {{cannot decompose this type; 
'std::tuple_size<MinusOne>::value' is not a valid size: -1}}
+// expected-error@-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@-1 {{cannot decompose this type; 
'std::tuple_size<UintMax>::value' is not a valid size: 4294967295}}
+// expected-error@-2 {{type 'UintMax' cannot be decomposed}}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to