diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -68,6 +68,9 @@ - Fixed a crash-on-valid with consteval evaluation of a list-initialized constructor for a temporary object. This fixes `Issue 55871 `_. +- Fix `#57008 `_ - Builtin + C++ language extension type traits instantiated by a template with unexpected + number of arguments cause an assertion fault. Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/TypeTraits.h b/clang/include/clang/Basic/TypeTraits.h --- a/clang/include/clang/Basic/TypeTraits.h +++ b/clang/include/clang/Basic/TypeTraits.h @@ -67,6 +67,10 @@ const char *getTraitSpelling(TypeTrait T) LLVM_READONLY; const char *getTraitSpelling(ArrayTypeTrait T) LLVM_READONLY; const char *getTraitSpelling(UnaryExprOrTypeTrait T) LLVM_READONLY; + +/// Return the arity of the type trait \p T. +unsigned getTypeTraitArity(TypeTrait T) LLVM_READONLY; + } // namespace clang #endif diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5552,6 +5552,8 @@ bool isQualifiedMemberAccess(Expr *E); QualType CheckAddressOfOperand(ExprResult &Operand, SourceLocation OpLoc); + bool CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N); + ExprResult CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo, SourceLocation OpLoc, UnaryExprOrTypeTrait ExprKind, diff --git a/clang/lib/Basic/TypeTraits.cpp b/clang/lib/Basic/TypeTraits.cpp --- a/clang/lib/Basic/TypeTraits.cpp +++ b/clang/lib/Basic/TypeTraits.cpp @@ -55,6 +55,15 @@ #include "clang/Basic/TokenKinds.def" }; +static constexpr const unsigned TypeTraitArities[] = { +#define TYPE_TRAIT_1(Spelling, Name, Key) 1, +#include "clang/Basic/TokenKinds.def" +#define TYPE_TRAIT_2(Spelling, Name, Key) 2, +#include "clang/Basic/TokenKinds.def" +#define TYPE_TRAIT_N(Spelling, Name, Key) 0, +#include "clang/Basic/TokenKinds.def" +}; + const char *clang::getTraitName(TypeTrait T) { assert(T <= TT_Last && "invalid enum value!"); return TypeTraitNames[T]; @@ -84,3 +93,8 @@ assert(T <= UETT_Last && "invalid enum value!"); return UnaryExprOrTypeTraitSpellings[T]; } + +unsigned clang::getTypeTraitArity(TypeTrait T) { + assert(T <= TT_Last && "invalid enum value!"); + return TypeTraitArities[T]; +} diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -3724,14 +3724,6 @@ } } -static unsigned TypeTraitArity(tok::TokenKind kind) { - switch (kind) { - default: llvm_unreachable("Not a known type trait"); -#define TYPE_TRAIT(N,Spelling,K) case tok::kw_##Spelling: return N; -#include "clang/Basic/TokenKinds.def" - } -} - /// Parse the built-in type-trait pseudo-functions that allow /// implementation of the TR1/C++11 type traits templates. /// @@ -3745,7 +3737,6 @@ /// ExprResult Parser::ParseTypeTrait() { tok::TokenKind Kind = Tok.getKind(); - unsigned Arity = TypeTraitArity(Kind); SourceLocation Loc = ConsumeToken(); @@ -3780,18 +3771,6 @@ SourceLocation EndLoc = Parens.getCloseLocation(); - if (Arity && Args.size() != Arity) { - Diag(EndLoc, diag::err_type_trait_arity) - << Arity << 0 << (Arity > 1) << (int)Args.size() << SourceRange(Loc); - return ExprError(); - } - - if (!Arity && Args.empty()) { - Diag(EndLoc, diag::err_type_trait_arity) - << 1 << 1 << 1 << (int)Args.size() << SourceRange(Loc); - return ExprError(); - } - return Actions.ActOnTypeTrait(TypeTraitFromTokKind(Kind), Loc, Args, EndLoc); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5427,9 +5427,26 @@ } } +bool Sema::CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N) { + if (Arity && N != Arity) { + Diag(Loc, diag::err_type_trait_arity) + << Arity << 0 << (Arity > 1) << (int)N << SourceRange(Loc); + return false; + } + + if (!Arity && N == 0) { + Diag(Loc, diag::err_type_trait_arity) + << 1 << 1 << 1 << (int)N << SourceRange(Loc); + return false; + } + return true; +} + ExprResult Sema::BuildTypeTrait(TypeTrait Kind, SourceLocation KWLoc, ArrayRef Args, SourceLocation RParenLoc) { + if (!CheckTypeTraitArity(getTypeTraitArity(Kind), KWLoc, Args.size())) + return ExprError(); QualType ResultType = Context.getLogicalOperationType(); if (Kind <= UTT_Last && !CheckUnaryTypeTraitTypeCompleteness( diff --git a/clang/test/SemaCXX/type-trait-eval-crash-issue-57008.cpp b/clang/test/SemaCXX/type-trait-eval-crash-issue-57008.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/type-trait-eval-crash-issue-57008.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -fsyntax-only %s -verify + +// Shouldn't crash here +// Reported by https://github.com/llvm/llvm-project/issues/57008 +template bool b = __is_constructible(Ts...); // expected-error{{type trait requires 1 or more arguments}} +bool x = b<>; // expected-note{{in instantiation of variable template specialization}}