Skip to content

Commit 46ce6a0

Browse files
authored
[clang] diagnose invalid std::tuple_size sizes (llvm#159677)
This makes sure the tuple sizes remain within implementation limits, and this doesn't cause the compiler to crash later, as the tuple size is assumed to fit within an UnsignedOrNone. Fixes llvm#159563
1 parent dcce216 commit 46ce6a0

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)