template simplifier: only add recursive instantiation if its arguments are a constant expression (#2138)

This commit is contained in:
IOBYTE 2019-09-02 00:51:19 -04:00 committed by Daniel Marjamäki
parent 005765a561
commit fd403bf7e6
2 changed files with 63 additions and 6 deletions

View File

@ -820,7 +820,7 @@ void TemplateSimplifier::getTemplateInstantiations()
Token *tok2 = Token::findsimplematch(tok->tokAt(2), ";");
if (tok2)
tok = tok2;
} else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|&|return|<|, %name% ::|<|(") ||
} else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|&|return|<|,|! %name% ::|<|(") ||
Token::Match(tok->previous(), "%type% %name% ::|<") ||
Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) {
std::string scopeName = tok->scopeInfo()->name;
@ -2081,11 +2081,26 @@ void TemplateSimplifier::expandTemplate(
}
std::string scope;
for (const Token *prev = tok3->tokAt(-2); Token::Match(prev, "%name% ::"); prev = prev->tokAt(-2)) {
const Token *prev = tok3;
for (; Token::Match(prev->tokAt(-2), "%name% ::"); prev = prev->tokAt(-2)) {
if (scope.empty())
scope = prev->str();
scope = prev->strAt(-2);
else
scope = prev->str() + " :: " + scope;
scope = prev->strAt(-2) + " :: " + scope;
}
// check for global scope
if (prev->strAt(-1) != "::") {
// adjust for current scope
std::string token_scope = tok3->scopeInfo()->name;
std::string::size_type end = token_scope.find_last_of(" :: ");
if (end != std::string::npos) {
token_scope.resize(end);
if (scope.empty())
scope = token_scope;
else
scope = token_scope + " :: " + scope;
}
}
// don't add instantiations in template definitions
@ -2136,8 +2151,14 @@ void TemplateSimplifier::expandTemplate(
}
// add new instantiations
for (const auto & inst : newInstantiations)
addInstantiation(inst.token, inst.scope);
for (const auto & inst : newInstantiations) {
std::string fullName = inst.scope + (inst.scope.empty() ? "" : " :: ") + inst.token->str();
simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket());
// only add recursive instantiation if its arguments are a constant expression
if (templateDeclaration.fullName() != fullName ||
(inst.token->tokAt(2)->isNumber() || inst.token->tokAt(2)->isStandardType()))
mTemplateInstantiations.emplace_back(inst.token, inst.scope);
}
}
static bool isLowerThanLogicalAnd(const Token *lower)

View File

@ -177,6 +177,7 @@ private:
TEST_CASE(template137); // #9288
TEST_CASE(template138);
TEST_CASE(template139);
TEST_CASE(template140);
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_enum); // #6299 Syntax error in complex enum declaration (including template)
@ -3392,6 +3393,41 @@ private:
}
}
void template140() {
{
const char code[] = "template <typename> struct a { };\n"
"template <typename b> struct d {\n"
" d();\n"
" d(d<a<b>> e);\n"
"};\n"
"void foo() { d<char> c; }";
const char exp[] = "struct a<char> ; "
"struct d<char> ; "
"void foo ( ) { d<char> c ; } "
"struct d<char> { "
"d<char> ( ) ; "
"d<char> ( d < a<char> > e ) ; "
"} ; "
"struct a<char> { } ;";
ASSERT_EQUALS(exp, tok(code));
}
{
const char code[] = "namespace a {\n"
"template <typename b> using c = typename b ::d;\n"
"template <typename> constexpr bool e() { return false; }\n"
"template <typename b> class f { f(f<c<b>>); };\n"
"static_assert(!e<f<char>>());\n"
"}";
const char exp[] = "namespace a { "
"const bool e<f<char>> ( ) ; "
"class f<char> ; "
"static_assert ( ! e<f<char>> ( ) ) ; } "
"class a :: f<char> { f<char> ( a :: f < b :: d > ) ; } ; "
"const bool a :: e<f<char>> ( ) { return false ; }";
ASSERT_EQUALS(exp, tok(code));
}
}
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
const char code[] = "template <typename T> struct C {};\n"
"template <typename T> struct S {a};\n"