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