Skip to content

Commit 97daa1e

Browse files
committed
[clang] diagnose invalid std::tuple_size sizes
Fixes #159563
1 parent fcf2c46 commit 97daa1e

File tree

4 files changed

+44
-10
lines changed

4 files changed

+44
-10
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ Improvements to Clang's diagnostics
271271
Moved the warning for a missing (though implied) attribute on a redeclaration into this group.
272272
Added a new warning in this group for the case where the attribute is missing/implicit on
273273
an override of a virtual method.
274+
- Implemented diagnostics when retrieving the tuple size for types where its specialization of `std::tuple_size`
275+
produces an invalid size (either negative or greater than the implementation limit). (#GH159563)
274276
- Fixed fix-it hint for fold expressions. Clang now correctly places the suggested right
275277
parenthesis when diagnosing malformed fold expressions. (#GH151787)
276278
- Added fix-it hint for when scoped enumerations require explicit conversions for binary operations. (#GH24265)
@@ -356,8 +358,8 @@ Bug Fixes in This Version
356358
and vector of 4 ``float`` values. (#GH155405)
357359
- Fixed inconsistent shadow warnings for lambda capture of structured bindings.
358360
Previously, ``[val = val]`` (regular parameter) produced no warnings with ``-Wshadow``
359-
while ``[a = a]`` (where ``a`` is from ``auto [a, b] = std::make_pair(1, 2)``)
360-
incorrectly produced warnings. Both cases now consistently show no warnings with
361+
while ``[a = a]`` (where ``a`` is from ``auto [a, b] = std::make_pair(1, 2)``)
362+
incorrectly produced warnings. Both cases now consistently show no warnings with
361363
``-Wshadow`` and show uncaptured-local warnings with ``-Wshadow-all``. (#GH68605)
362364
- Fixed a failed assertion with a negative limit parameter value inside of
363365
``__has_embed``. (#GH157842)

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,9 @@ def err_decomp_decl_std_tuple_element_not_specialized : Error<
638638
def err_decomp_decl_std_tuple_size_not_constant : Error<
639639
"cannot decompose this type; 'std::tuple_size<%0>::value' "
640640
"is not a valid integral constant expression">;
641+
def err_decomp_decl_std_tuple_size_invalid
642+
: Error<"cannot decompose this type; 'std::tuple_size<%0>::value' "
643+
"is not a valid size: %1">;
641644
def note_in_binding_decl_init : Note<
642645
"in implicit initialization of binding declaration %0">;
643646
def err_arg_is_not_destructurable : Error<

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,7 +1177,7 @@ getTrivialTypeTemplateArgument(Sema &S, SourceLocation Loc, QualType T) {
11771177
namespace { enum class IsTupleLike { TupleLike, NotTupleLike, Error }; }
11781178

11791179
static IsTupleLike isTupleLike(Sema &S, SourceLocation Loc, QualType T,
1180-
llvm::APSInt &Size) {
1180+
unsigned &OutSize) {
11811181
EnterExpressionEvaluationContext ContextRAII(
11821182
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
11831183

@@ -1218,10 +1218,24 @@ static IsTupleLike isTupleLike(Sema &S, SourceLocation Loc, QualType T,
12181218
if (E.isInvalid())
12191219
return IsTupleLike::Error;
12201220

1221+
llvm::APSInt Size;
12211222
E = S.VerifyIntegerConstantExpression(E.get(), &Size, Diagnoser);
12221223
if (E.isInvalid())
12231224
return IsTupleLike::Error;
12241225

1226+
// The implementation limit is UINT_MAX-1, to allow this to be passed down on
1227+
// an UnsignedOrNone.
1228+
if (Size < 0 || Size >= UINT_MAX) {
1229+
llvm::SmallVector<char, 16> Str;
1230+
Size.toString(Str);
1231+
S.Diag(Loc, diag::err_decomp_decl_std_tuple_size_invalid)
1232+
<< printTemplateArgs(S.Context.getPrintingPolicy(), Args,
1233+
/*Params=*/nullptr)
1234+
<< StringRef(Str.data(), Str.size());
1235+
return IsTupleLike::Error;
1236+
}
1237+
1238+
OutSize = Size.getExtValue();
12251239
return IsTupleLike::TupleLike;
12261240
}
12271241

@@ -1279,9 +1293,8 @@ struct InitializingBinding {
12791293
static bool checkTupleLikeDecomposition(Sema &S,
12801294
ArrayRef<BindingDecl *> Bindings,
12811295
VarDecl *Src, QualType DecompType,
1282-
const llvm::APSInt &TupleSize) {
1296+
unsigned NumElems) {
12831297
auto *DD = cast<DecompositionDecl>(Src);
1284-
unsigned NumElems = (unsigned)TupleSize.getLimitedValue(UINT_MAX);
12851298
if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems))
12861299
return true;
12871300

@@ -1641,7 +1654,7 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
16411654
// C++1z [dcl.decomp]/3:
16421655
// if the expression std::tuple_size<E>::value is a well-formed integral
16431656
// constant expression, [...]
1644-
llvm::APSInt TupleSize(32);
1657+
unsigned TupleSize;
16451658
switch (isTupleLike(*this, DD->getLocation(), DecompType, TupleSize)) {
16461659
case IsTupleLike::Error:
16471660
DD->setInvalidDecl();
@@ -1690,12 +1703,12 @@ UnsignedOrNone Sema::GetDecompositionElementCount(QualType T,
16901703
if (T->getAs<ComplexType>())
16911704
return 2u;
16921705

1693-
llvm::APSInt TupleSize(Ctx.getTypeSize(Ctx.getSizeType()));
1706+
unsigned TupleSize;
16941707
switch (isTupleLike(*this, Loc, T, TupleSize)) {
16951708
case IsTupleLike::Error:
16961709
return std::nullopt;
16971710
case IsTupleLike::TupleLike:
1698-
return static_cast<unsigned>(TupleSize.getExtValue());
1711+
return TupleSize;
16991712
case IsTupleLike::NotTupleLike:
17001713
break;
17011714
}

clang/test/SemaCXX/builtin-structured-binding-size.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
2-
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify -fexperimental-new-constant-interpreter
1+
// RUN: %clang_cc1 %s -triple=x86_64 -std=c++2c -fsyntax-only -verify
2+
// RUN: %clang_cc1 %s -triple=x86_64 -std=c++2c -fsyntax-only -verify -fexperimental-new-constant-interpreter
33

44

55
struct S0 {};
@@ -229,3 +229,19 @@ static_assert(__is_same_as(tag_of_t<S1>, int));
229229
static_assert(__is_same_as(tag_of_t<int>, int)); // error
230230
// expected-error@-1 {{constraints not satisfied for alias template 'tag_of_t' [with T = int]}}
231231
// expected-note@#tag-of-constr {{because substituted constraint expression is ill-formed: type 'int' cannot be decomposed}}
232+
233+
struct MinusOne;
234+
template <> struct ::std::tuple_size<MinusOne> {
235+
static constexpr int value = -1;
236+
};
237+
int minus_one = __builtin_structured_binding_size(MinusOne);
238+
// expected-error@-1 {{cannot decompose this type; 'std::tuple_size<MinusOne>::value' is not a valid size: -1}}
239+
// expected-error@-2 {{type 'MinusOne' cannot be decomposed}}
240+
241+
struct UintMax;
242+
template <> struct ::std::tuple_size<UintMax> {
243+
static constexpr unsigned value = -1;
244+
};
245+
int uint_max = __builtin_structured_binding_size(UintMax);
246+
// expected-error@-1 {{cannot decompose this type; 'std::tuple_size<UintMax>::value' is not a valid size: 4294967295}}
247+
// expected-error@-2 {{type 'UintMax' cannot be decomposed}}

0 commit comments

Comments
 (0)