Fix #11418 Crash in TemplateSimplifier::expandTemplate() (#5019)

This commit is contained in:
chrchr-github 2023-04-28 08:25:52 +02:00 committed by GitHub
parent 96cf2b34fd
commit 77717f73fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 8 deletions

View File

@ -1583,6 +1583,12 @@ bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclara
return Token::simpleMatch(tok->tokAt(offset), scope.c_str(), scope.size()); return Token::simpleMatch(tok->tokAt(offset), scope.c_str(), scope.size());
} }
struct newInstantiation {
newInstantiation(Token* t, std::string s) : token(t), scope(std::move(s)) {}
Token* token;
std::string scope;
};
void TemplateSimplifier::expandTemplate( void TemplateSimplifier::expandTemplate(
const TokenAndName &templateDeclaration, const TokenAndName &templateDeclaration,
const TokenAndName &templateInstantiation, const TokenAndName &templateInstantiation,
@ -1599,11 +1605,7 @@ void TemplateSimplifier::expandTemplate(
const bool isFunction = templateDeclaration.isFunction(); const bool isFunction = templateDeclaration.isFunction();
const bool isSpecialization = templateDeclaration.isSpecialization(); const bool isSpecialization = templateDeclaration.isSpecialization();
const bool isVariable = templateDeclaration.isVariable(); const bool isVariable = templateDeclaration.isVariable();
struct newInstantiation {
newInstantiation(Token *t, std::string s) : token(t), scope(std::move(s)) {}
Token *token;
std::string scope;
};
std::vector<newInstantiation> newInstantiations; std::vector<newInstantiation> newInstantiations;
// add forward declarations // add forward declarations
@ -2251,7 +2253,9 @@ void TemplateSimplifier::expandTemplate(
// add new instantiations // add new instantiations
for (const auto & inst : newInstantiations) { for (const auto & inst : newInstantiations) {
simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket()); if (!inst.token)
continue;
simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket(), &newInstantiations);
// only add recursive instantiation if its arguments are a constant expression // only add recursive instantiation if its arguments are a constant expression
if (templateDeclaration.name() != inst.token->str() || if (templateDeclaration.name() != inst.token->str() ||
(inst.token->tokAt(2)->isNumber() || inst.token->tokAt(2)->isStandardType())) (inst.token->tokAt(2)->isNumber() || inst.token->tokAt(2)->isStandardType()))
@ -2409,7 +2413,17 @@ static Token *skipTernaryOp(Token *tok, const Token *backToken)
return tok; return tok;
} }
void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end) static void invalidateInst(const Token* beg, const Token* end, std::vector<newInstantiation>* newInst) {
if (!newInst)
return;
for (auto& inst : *newInst) {
for (const Token* tok = beg; tok != end; tok = tok->next())
if (inst.token == tok)
inst.token = nullptr;
}
}
void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end, std::vector<newInstantiation>* newInst)
{ {
// start could be erased so use the token before start if available // start could be erased so use the token before start if available
Token * first = (start && start->previous()) ? start->previous() : mTokenList.front(); Token * first = (start && start->previous()) ? start->previous() : mTokenList.front();
@ -2517,6 +2531,7 @@ void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end)
} }
if (Token::Match(tok->next(), "false|0")) { if (Token::Match(tok->next(), "false|0")) {
invalidateInst(tok->next(), colon, newInst);
// Use code after colon, remove code before it. // Use code after colon, remove code before it.
Token::eraseTokens(tok, colon); Token::eraseTokens(tok, colon);
@ -2543,6 +2558,7 @@ void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end)
else if (endTok->str() == ">" && !end) else if (endTok->str() == ">" && !end)
; ;
else { else {
invalidateInst(colon->tokAt(-1), endTok, newInst);
Token::eraseTokens(colon->tokAt(-2), endTok); Token::eraseTokens(colon->tokAt(-2), endTok);
again = true; again = true;
break; break;

View File

@ -37,6 +37,7 @@ class Settings;
class Token; class Token;
class Tokenizer; class Tokenizer;
class TokenList; class TokenList;
struct newInstantiation;
/// @addtogroup Core /// @addtogroup Core
/// @{ /// @{
@ -333,7 +334,7 @@ public:
* @param start first token of arguments * @param start first token of arguments
* @param end token following last argument token * @param end token following last argument token
*/ */
void simplifyTemplateArgs(Token *start, const Token *end); void simplifyTemplateArgs(Token *start, const Token *end, std::vector<newInstantiation>* newInst = nullptr);
private: private:
/** /**

View File

@ -278,6 +278,7 @@ private:
TEST_CASE(simplifyTemplateArgs1); TEST_CASE(simplifyTemplateArgs1);
TEST_CASE(simplifyTemplateArgs2); TEST_CASE(simplifyTemplateArgs2);
TEST_CASE(simplifyTemplateArgs3);
TEST_CASE(template_variadic_1); // #9144 TEST_CASE(template_variadic_1); // #9144
TEST_CASE(template_variadic_2); // #4349 TEST_CASE(template_variadic_2); // #4349
@ -6051,6 +6052,35 @@ private:
ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS(expected, tok(code));
} }
void simplifyTemplateArgs3() { // #11418
const char code[] = "template <class T> struct S {};\n"
"template<typename T>\n"
"T f() {}\n"
"template<typename T, typename U>\n"
"void g() {\n"
" S<decltype(true ? f<T>() : f<U>())> s1;\n"
" S<decltype(false ? f<T>() : f<U>())> s2;\n"
"}\n"
"void h() {\n"
" g<int, char>();\n"
"}\n";
const char expected[] = "struct S<decltype((f<int>()))> ; "
"struct S<decltype(f<char>())> ; "
"int f<int> ( ) ; "
"char f<char> ( ) ; "
"void g<int,char> ( ) ; "
"void h ( ) { g<int,char> ( ) ; } "
"void g<int,char> ( ) { "
"S<decltype((f<int>()))> s1 ; "
"S<decltype(f<char>())> s2 ; "
"} "
"int f<int> ( ) { } "
"char f<char> ( ) { } "
"struct S<decltype((f<int>()))> { } ; "
"struct S<decltype(f<char>())> { } ;";
ASSERT_EQUALS(expected, tok(code));
}
void template_variadic_1() { // #9144 void template_variadic_1() { // #9144
const char code[] = "template <typename...> struct e {};\n" const char code[] = "template <typename...> struct e {};\n"
"static_assert(sizeof(e<>) == sizeof(e<int,int>), \"\");"; "static_assert(sizeof(e<>) == sizeof(e<int,int>), \"\");";