[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<class... Ts> 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<class... Ts> 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
This commit is contained in:
YingChi Long 2022-08-12 02:14:09 +08:00
parent 4b45999bdd
commit e5825190b8
No known key found for this signature in database
GPG Key ID: 7C4D5D7BA816DE62
7 changed files with 46 additions and 21 deletions

View File

@ -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 <https://github.com/llvm/llvm-project/issues/55871>`_.
- Fix `#57008 <https://github.com/llvm/llvm-project/issues/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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -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

View File

@ -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,

View File

@ -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];
}

View File

@ -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);
}

View File

@ -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<TypeSourceInfo *> Args,
SourceLocation RParenLoc) {
if (!CheckTypeTraitArity(getTypeTraitArity(Kind), KWLoc, Args.size()))
return ExprError();
QualType ResultType = Context.getLogicalOperationType();
if (Kind <= UTT_Last && !CheckUnaryTypeTraitTypeCompleteness(

View File

@ -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<class... Ts> 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}}