Fixed #9155 (Syntax error on valid C++ code) (#1880)

Refactored simplifyTemplateAliases to iterate over template type aliases
rather than instantiations. This fixed template type aliases that were
not templates.

Don't instantiate templates in template type aliases. They will get
instantiated once the type alias is instantiated. This required
increasing the template simplifier pass count to 3 so one of the
existing tests continued to work.
This commit is contained in:
IOBYTE 2019-06-12 01:44:48 -04:00 committed by Daniel Marjamäki
parent 4939e0c308
commit 2a4b28c267
3 changed files with 80 additions and 103 deletions

View File

@ -771,7 +771,7 @@ void TemplateSimplifier::getTemplateInstantiations()
const Token *tok2 = Token::findmatch(tok, "{|;"); const Token *tok2 = Token::findmatch(tok, "{|;");
if (tok2 && tok2->str() == "{") if (tok2 && tok2->str() == "{")
tok = tok2->link(); tok = tok2->link();
else if (!isUsing && tok2 && tok2->str() == ";") else if (tok2 && tok2->str() == ";")
tok = const_cast<Token *>(tok2); tok = const_cast<Token *>(tok2);
} }
} else if (Token::Match(tok, "template using %name% <")) { } else if (Token::Match(tok, "template using %name% <")) {
@ -1150,39 +1150,13 @@ void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration)
void TemplateSimplifier::simplifyTemplateAliases() void TemplateSimplifier::simplifyTemplateAliases()
{ {
std::list<TokenAndName>::iterator it1, it2, it3; for (std::list<TokenAndName>::iterator it1 = mTemplateDeclarations.begin(); it1 != mTemplateDeclarations.end();) {
for (it1 = mTemplateInstantiations.begin(); it1 != mTemplateInstantiations.end();) { TokenAndName &aliasDeclaration = *it1;
it3 = it1;
TokenAndName &templateAlias = *it1; if (!aliasDeclaration.isAlias()) {
++it1; ++it1;
Token *startToken = templateAlias.token;
if (!startToken)
continue; continue;
while (Token::Match(startToken->tokAt(-2), "%name% :: %name%")) }
startToken = startToken->tokAt(-2);
if (!(Token::Match(startToken->tokAt(-4), "> using %name% = %name% ::|<") ||
Token::Match(startToken->tokAt(-5), "> using %name% = typename %name% ::|<")))
continue;
const bool hasTypename(startToken->strAt(-1) == "typename");
// Get start token for alias
startToken = startToken->tokAt(hasTypename ? -6 : -5);
while (Token::Match(startToken, "%name%|<|>|>>|,"))
startToken = startToken->previous();
// handle case where 'template' is first token
if (!startToken) {
if (!Token::simpleMatch(mTokenList.front(), "template <"))
continue;
} else if (!Token::Match(startToken, "[;{}] template <"))
continue;
std::list<TokenAndName>::iterator it5 = std::find_if(mTemplateDeclarations.begin(),
mTemplateDeclarations.end(),
FindToken(!startToken ? mTokenList.front() : startToken->next()));
if (it5 == mTemplateDeclarations.end())
continue;
TokenAndName &aliasDeclaration = *it5;
// alias parameters.. // alias parameters..
std::vector<const Token *> aliasParameters; std::vector<const Token *> aliasParameters;
@ -1192,15 +1166,19 @@ void TemplateSimplifier::simplifyTemplateAliases()
aliasParameterNames[aliasParameters[argnr]->str()] = argnr; aliasParameterNames[aliasParameters[argnr]->str()] = argnr;
// Look for alias usages.. // Look for alias usages..
const Token *endToken = nullptr; bool found = false;
for (it2 = it1; it2 != mTemplateInstantiations.end(); ++it2) { for (std::list<TokenAndName>::iterator it2 = mTemplateInstantiations.begin(); it2 != mTemplateInstantiations.end();) {
TokenAndName &aliasUsage = *it2; TokenAndName &aliasUsage = *it2;
if (!aliasUsage.token || aliasUsage.fullName != aliasDeclaration.fullName) if (!aliasUsage.token || aliasUsage.fullName != aliasDeclaration.fullName) {
++it2;
continue; continue;
}
// don't recurse // don't recurse
if (aliasDeclaration.isAliasToken(aliasUsage.token)) if (aliasDeclaration.isAliasToken(aliasUsage.token)) {
++it2;
continue; continue;
}
std::vector<std::pair<Token *, Token *>> args; std::vector<std::pair<Token *, Token *>> args;
Token *tok2 = aliasUsage.token->tokAt(2); Token *tok2 = aliasUsage.token->tokAt(2);
@ -1221,37 +1199,32 @@ void TemplateSimplifier::simplifyTemplateAliases()
break; break;
} }
} }
if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size()) if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size()) {
++it2;
continue; continue;
// Replace template alias code..
aliasUsage.name = templateAlias.name;
aliasUsage.fullName = templateAlias.fullName;
aliasUsage.scope = templateAlias.scope;
const Token *temp = aliasDeclaration.aliasStartToken();
if (temp->str() == "typename")
temp = temp->next();
std::stack<Token *> links;
while (temp && temp != templateAlias.token) {
aliasUsage.token->insertToken(temp->str(), "", true);
Token * previous = aliasUsage.token->previous();
if (Token::Match(previous, "(|[|{"))
links.push(previous);
else if (!links.empty() && Token::Match(previous, ")|]|}")) {
Token::createMutualLinks(links.top(), previous);
links.pop();
} }
temp = temp->next(); // copy template-id from declaration to after instantiation
} Token * dst = aliasUsage.token->next()->findClosingBracket();
aliasUsage.token->str(templateAlias.token->str()); Token * end = TokenList::copyTokens(dst, aliasDeclaration.aliasStartToken(), aliasDeclaration.aliasEndToken()->previous(), false)->next();
tok2 = aliasUsage.token->next(); // the '<' // replace parameters
const Token * const endToken1 = templateAlias.token->next()->findClosingBracket(); for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) {
const Token * const endToken2 = TokenList::copyTokens(tok2, templateAlias.token->tokAt(2), endToken1->previous(), false)->next(); if (!tok1->isName())
for (const Token *tok1 = templateAlias.token->next(); tok2 != endToken2; tok1 = tok1->next(), tok2 = tok2->next()) { continue;
if (!tok2->isName()) if (aliasParameterNames.find(tok1->str()) != aliasParameterNames.end()) {
const unsigned int argnr = aliasParameterNames[tok1->str()];
const Token * const fromStart = args[argnr].first;
const Token * const fromEnd = args[argnr].second->previous();
TokenList::copyTokens(tok1, fromStart, fromEnd, true);
tok1->deleteThis();
} else if (tok1->str() == "typename")
tok1->deleteThis();
}
// add new instantiations
for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) {
if (!tok1->isName())
continue; continue;
if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) { if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) {
// Create template instance.. // Create template instance..
@ -1262,53 +1235,32 @@ void TemplateSimplifier::simplifyTemplateAliases()
if (it != mTemplateInstantiations.end()) if (it != mTemplateInstantiations.end())
addInstantiation(tok2, it->scope); addInstantiation(tok2, it->scope);
} }
continue;
} }
const unsigned int argnr = aliasParameterNames[tok2->str()];
const Token * const fromStart = args[argnr].first;
const Token * const fromEnd = args[argnr].second->previous();
Token * const destToken = tok2;
tok2 = TokenList::copyTokens(tok2, fromStart, fromEnd, true);
if (tok2 == destToken->next())
tok2 = destToken;
destToken->deleteThis();
} }
endToken = endToken1->next(); // erase the instantiation tokens
while (endToken->str() != ";") eraseTokens(aliasUsage.token->previous(), dst->next());
endToken = endToken->next(); found = true;
// Remove alias usage code (parameters) // erase this instantiation
Token::eraseTokens(tok2->previous(), args.back().second); it2 = mTemplateInstantiations.erase(it2);
}
if (endToken) {
// Remove all template instantiations in template alias
for (const Token *tok = aliasDeclaration.paramEnd->tokAt(4); tok != endToken; tok = tok->next()) {
if (!Token::Match(tok, "%name% <"))
continue;
std::list<TokenAndName>::iterator it = std::find_if(mTemplateInstantiations.begin(),
mTemplateInstantiations.end(),
FindToken(tok));
if (it == mTemplateInstantiations.end())
continue;
std::list<TokenAndName>::iterator next = it;
++next;
if (it == it1)
it1 = next;
mTemplateInstantiations.erase(it,next);
} }
if (startToken) if (found) {
eraseTokens(startToken, endToken->next() ? endToken->next() : endToken); Token *end = const_cast<Token *>(aliasDeclaration.aliasEndToken());
// remove declaration tokens
if (aliasDeclaration.token->previous())
eraseTokens(aliasDeclaration.token->previous(), end->next() ? end->next() : end);
else { else {
eraseTokens(mTokenList.front(), endToken->next() ? endToken->next() : endToken); eraseTokens(mTokenList.front(), end->next() ? end->next() : end);
deleteToken(mTokenList.front()); deleteToken(mTokenList.front());
} }
// remove declaration // remove declaration
mTemplateDeclarations.erase(it5); it1 = mTemplateDeclarations.erase(it1);
} else } else
mTemplateInstantiations.erase(it3); ++it1;
} }
} }
@ -3298,7 +3250,7 @@ void TemplateSimplifier::simplifyTemplates(
} }
} }
// TODO: 2 is not the ideal number of loops. // TODO: 3 is not the ideal number of loops.
// We should loop until the number of declarations is 0 but we can't // We should loop until the number of declarations is 0 but we can't
// do that until we instantiate unintstantiated templates with their symbolic types. // do that until we instantiate unintstantiated templates with their symbolic types.
// That will allow the uninstantiated template code to be removed from the symbol database. // That will allow the uninstantiated template code to be removed from the symbol database.
@ -3306,7 +3258,7 @@ void TemplateSimplifier::simplifyTemplates(
// the uninstantiated template code in the symbol database can't be removed until #8768 // the uninstantiated template code in the symbol database can't be removed until #8768
// is fixed. // is fixed.
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 3; ++i) {
if (i) { if (i) {
// it may take more than one pass to simplify type aliases // it may take more than one pass to simplify type aliases
while (mTokenizer->simplifyUsing()) while (mTokenizer->simplifyUsing())

View File

@ -151,6 +151,7 @@ private:
TEST_CASE(template111); // crash TEST_CASE(template111); // crash
TEST_CASE(template112); // #9146 syntax error TEST_CASE(template112); // #9146 syntax error
TEST_CASE(template113); TEST_CASE(template113);
TEST_CASE(template114); // #9155
TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..}; TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
@ -188,6 +189,7 @@ private:
TEST_CASE(templateAlias2); TEST_CASE(templateAlias2);
TEST_CASE(templateAlias3); // #8315 TEST_CASE(templateAlias3); // #8315
TEST_CASE(templateAlias4); // #9070 TEST_CASE(templateAlias4); // #9070
TEST_CASE(templateAlias5);
// Test TemplateSimplifier::instantiateMatch // Test TemplateSimplifier::instantiateMatch
TEST_CASE(instantiateMatch); TEST_CASE(instantiateMatch);
@ -2555,7 +2557,7 @@ private:
"template < typename > struct c ; " "template < typename > struct c ; "
"struct e<int> ; " "struct e<int> ; "
"} " "} "
"e<int> foo ; " "e<int> :: g foo ; "
"struct e<int> { " "struct e<int> { "
"bool h ; h = a < b<c<int>::g> > :: h ; " "bool h ; h = a < b<c<int>::g> > :: h ; "
"} ; " "} ; "
@ -2680,6 +2682,20 @@ private:
} }
} }
void template114() { // #9155
const char code[] = "template <typename a, a> struct b {};\n"
"template <typename> struct c;\n"
"template <typename> struct d : b<bool, std::is_polymorphic<int>{}> {};\n"
"template <bool> struct e;\n"
"template <typename a> using f = typename e<c<d<a>>::g>::h;";
const char exp[] = "template < typename a , a > struct b { } ; "
"template < typename > struct c ; "
"template < typename > struct d : b < bool , std :: is_polymorphic < int > { } > { } ; "
"template < bool > struct e ; "
"template < typename a > using f = typename e < c < d < a > > :: g > :: h ;";
ASSERT_EQUALS(exp, tok(code));
}
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..}; void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
const char code[] = "template <typename T> struct C {};\n" const char code[] = "template <typename T> struct C {};\n"
"template <typename T> struct S {a};\n" "template <typename T> struct S {a};\n"
@ -3678,6 +3694,16 @@ private:
ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS(expected, tok(code));
} }
void templateAlias5() {
const char code[] = "template<typename T> using A = int;\n"
"template<typename T> using B = T;\n"
"A<char> a;\n"
"B<char> b;";
const char expected[] = "int a ; "
"char b ;";
ASSERT_EQUALS(expected, tok(code));
}
unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) { unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) {
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);

View File

@ -4858,7 +4858,6 @@ private:
Tokenizer tokenizer(&settings0, this); Tokenizer tokenizer(&settings0, this);
std::istringstream istr(code); std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp"); tokenizer.tokenize(istr, "test.cpp");
ASSERT(nullptr != Token::findsimplematch(tokenizer.tokens(), "> > ;")->link());
ASSERT(nullptr != Token::findsimplematch(tokenizer.tokens(), "> ;")->link()); ASSERT(nullptr != Token::findsimplematch(tokenizer.tokens(), "> ;")->link());
} }