From e5825190b8ad7ac8fe762fe4101cd4af04f4c057 Mon Sep 17 00:00:00 2001 From: YingChi Long Date: Fri, 12 Aug 2022 02:14:09 +0800 Subject: [PATCH] [clang] fix frontend crash when evaluating type trait Before this patch type traits are checked in Parser, so use type traits directly did not cause assertion faults. However if type traits are initialized from a template, we didn't perform arity checks before evaluating. This patch moves arity checks from Parser to Sema, and performing arity checks in Sema actions, so type traits get checked corretly. Crash input: ``` template bool b = __is_constructible(Ts...); bool x = b<>; ``` After this patch: ``` clang/test/SemaCXX/type-trait-eval-crash-issue-57008.cpp:5:32: error: type trait requires 1 or more arguments; have 0 arguments template bool b = __is_constructible(Ts...); ^~~~~~~~~~~~~~~~~~ clang/test/SemaCXX/type-trait-eval-crash-issue-57008.cpp:6:10: note: in instantiation of variable template specialization 'b<>' requested here bool x = b<>; ^ 1 error generated. ``` See https://godbolt.org/z/q39W78hsK. Fixes https://github.com/llvm/llvm-project/issues/57008 Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D131423 --- clang/docs/ReleaseNotes.rst | 3 +++ clang/include/clang/Basic/TypeTraits.h | 4 ++++ clang/include/clang/Sema/Sema.h | 2 ++ clang/lib/Basic/TypeTraits.cpp | 14 ++++++++++++++ clang/lib/Parse/ParseExprCXX.cpp | 21 --------------------- clang/lib/Sema/SemaExprCXX.cpp | 17 +++++++++++++++++ .../SemaCXX/type-trait-eval-crash-issue-57008.cpp | 6 ++++++ 7 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 clang/test/SemaCXX/type-trait-eval-crash-issue-57008.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5729619..6970149 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -68,6 +68,9 @@ Bug Fixes - 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 index a0f06be..eb8b192 100644 --- a/clang/include/clang/Basic/TypeTraits.h +++ b/clang/include/clang/Basic/TypeTraits.h @@ -67,6 +67,10 @@ const char *getTraitName(UnaryExprOrTypeTrait T) LLVM_READONLY; 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 index bb2fa13..6908eaf 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5552,6 +5552,8 @@ public: 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 index 3b723af..4dbf678 100644 --- a/clang/lib/Basic/TypeTraits.cpp +++ b/clang/lib/Basic/TypeTraits.cpp @@ -55,6 +55,15 @@ static constexpr const char *UnaryExprOrTypeTraitSpellings[] = { #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 @@ const char *clang::getTraitSpelling(UnaryExprOrTypeTrait T) { 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 index 9bd89ed..2f30075 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -3724,14 +3724,6 @@ static ExpressionTrait ExpressionTraitFromTokKind(tok::TokenKind kind) { } } -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 @@ static unsigned TypeTraitArity(tok::TokenKind kind) { /// ExprResult Parser::ParseTypeTrait() { tok::TokenKind Kind = Tok.getKind(); - unsigned Arity = TypeTraitArity(Kind); SourceLocation Loc = ConsumeToken(); @@ -3780,18 +3771,6 @@ ExprResult Parser::ParseTypeTrait() { 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 index cdc1e75..e3e2df0 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5427,9 +5427,26 @@ void DiagnoseBuiltinDeprecation(Sema& S, TypeTrait Kind, } } +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 index 0000000..3ccaf27 --- /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}} -- 2.7.4