template simplifier: specialized member class not recognized causing wrong instantiation (#1876)

Specialized member classes declared outsize the class were not
recognized. This caused the the member class to be instantiated rather
than the specialized class. We already had a test for this but it was
wrong so it went unnoticed.
This commit is contained in:
IOBYTE 2019-06-09 02:11:59 -04:00 committed by Daniel Marjamäki
parent 1f24aa778b
commit 5af8beecf6
3 changed files with 81 additions and 16 deletions

View File

@ -89,7 +89,7 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s,
throw InternalError(tok, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX); throw InternalError(tok, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX);
} }
isClass(Token::Match(paramEnd->next(), "class|struct|union %name% <|{|:|;")); isClass(Token::Match(paramEnd->next(), "class|struct|union %name% <|{|:|;|::"));
if (token->strAt(1) == "<" && !isSpecialization()) { if (token->strAt(1) == "<" && !isSpecialization()) {
const Token *end = token->next()->findClosingBracket(); const Token *end = token->next()->findClosingBracket();
isVariadic(end && Token::findmatch(token->tokAt(2), "typename|class . . .", end)); isVariadic(end && Token::findmatch(token->tokAt(2), "typename|class . . .", end));
@ -113,8 +113,8 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s,
if (tok1) if (tok1)
isForwardDeclaration(tok1->str() == ";"); isForwardDeclaration(tok1->str() == ";");
} }
// check for member function and adjust scope // check for member class or function and adjust scope
if (isFunction() && nameToken->strAt(-1) == "::") { if ((isFunction() || isClass()) && nameToken->strAt(-1) == "::") {
const Token * start = nameToken; const Token * start = nameToken;
while (Token::Match(start->tokAt(-2), "%name% ::") || while (Token::Match(start->tokAt(-2), "%name% ::") ||
@ -1397,16 +1397,48 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *to
return false; return false;
} }
bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok, int &namepos)
{
if (Token::Match(tok, "> class|struct|union %type% :|<|;|{|::")) {
namepos = 2;
tok = tok->tokAt(2);
while (Token::Match(tok, "%type% :: %type%") ||
(Token::Match(tok, "%type% <") && Token::Match(tok->next()->findClosingBracket(), "> :: %type%"))) {
if (tok->strAt(1) == "::") {
tok = tok->tokAt(2);
namepos += 2;
} else {
const Token *end = tok->next()->findClosingBracket();
if (!end || !end->tokAt(2)) {
// syntax error
namepos = -1;
return true;
}
end = end->tokAt(2);
do {
tok = tok->next();
namepos += 1;
} while (tok && tok != end);
}
}
return true;
}
return false;
}
int TemplateSimplifier::getTemplateNamePosition(const Token *tok) int TemplateSimplifier::getTemplateNamePosition(const Token *tok)
{ {
// FIXME: tok == ">>" is a tokenizer bug that needs to be fixed
assert(tok && (tok->str() == ">" || tok->str() == ">>"));
auto it = mTemplateNamePos.find(tok); auto it = mTemplateNamePos.find(tok);
if (!mSettings->debugtemplate && it != mTemplateNamePos.end()) { if (!mSettings->debugtemplate && it != mTemplateNamePos.end()) {
return it->second; return it->second;
} }
// get the position of the template name // get the position of the template name
int namepos = 0; int namepos = 0;
if (Token::Match(tok, "> class|struct|union %type% :|<|;|{")) if (getTemplateNamePositionTemplateClass(tok, namepos))
namepos = 2; ;
else if (Token::Match(tok, "> using %name% =")) else if (Token::Match(tok, "> using %name% ="))
namepos = 2; namepos = 2;
else if (getTemplateNamePositionTemplateVariable(tok, namepos)) else if (getTemplateNamePositionTemplateVariable(tok, namepos))
@ -1500,7 +1532,9 @@ void TemplateSimplifier::expandTemplate(
templateDeclaration.token->insertToken(templateDeclarationToken->strAt(1), "", true); templateDeclaration.token->insertToken(templateDeclarationToken->strAt(1), "", true);
templateDeclaration.token->insertToken(newName, "", true); templateDeclaration.token->insertToken(newName, "", true);
templateDeclaration.token->insertToken(";", "", true); templateDeclaration.token->insertToken(";", "", true);
} else if ((isFunction && (copy || isSpecialization)) || (isVariable && !isSpecialization)) { } else if ((isFunction && (copy || isSpecialization)) ||
(isVariable && !isSpecialization) ||
(isClass && isSpecialization && mTemplateSpecializationMap.find(templateDeclaration.token) != mTemplateSpecializationMap.end())) {
Token * dst = templateDeclaration.token; Token * dst = templateDeclaration.token;
Token * dstStart = dst->previous(); Token * dstStart = dst->previous();
bool isStatic = false; bool isStatic = false;

View File

@ -240,11 +240,19 @@ public:
int getTemplateNamePosition(const Token *tok); int getTemplateNamePosition(const Token *tok);
/** /**
* Get function template name position * Get class template name position
* @param tok The ">" token e.g. before "class" * @param tok The ">" token e.g. before "class"
* @param namepos return offset to name * @param namepos return offset to name
* @return true if name found, false if not * @return true if name found, false if not
* */ * */
static bool getTemplateNamePositionTemplateClass(const Token *tok, int &namepos);
/**
* Get function template name position
* @param tok The ">" token
* @param namepos return offset to name
* @return true if name found, false if not
* */
static bool getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos); static bool getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos);
/** /**

View File

@ -976,7 +976,12 @@ private:
" template<typename T> struct X { T t; };" " template<typename T> struct X { T t; };"
"};" "};"
"template<> struct A::X<int> { int *t; };"; "template<> struct A::X<int> { int *t; };";
ASSERT_EQUALS("struct A { struct X<int> ; } ; struct A :: X<int> { int t ; } ;", tok(code)); const char expected[] = "struct A { "
"struct X<int> ; "
"template < typename T > struct X { T t ; } ; "
"} ; "
"struct A :: X<int> { int * t ; } ;";
ASSERT_EQUALS(expected, tok(code));
} }
void template41() { // #4710 - const in template instantiation not handled perfectly void template41() { // #4710 - const in template instantiation not handled perfectly
@ -1021,6 +1026,7 @@ private:
" return 0;\n" " return 0;\n"
"}"; "}";
const char expected[] = "struct E<void*> ; " const char expected[] = "struct E<void*> ; "
"struct C<B<A>> ; "
"struct C<A> ; " "struct C<A> ; "
"struct D<B<A>> ; " "struct D<B<A>> ; "
"int f2<B<A>> ( ) ; " "int f2<B<A>> ( ) ; "
@ -1215,7 +1221,8 @@ private:
" enum { value = 1 }; " " enum { value = 1 }; "
"};" "};"
"const int x = Factorial<4>::value;"; "const int x = Factorial<4>::value;";
const char expected[] = "struct Factorial<4> ; " const char expected[] = "struct Factorial<0> ; "
"struct Factorial<4> ; "
"struct Factorial<3> ; " "struct Factorial<3> ; "
"struct Factorial<2> ; " "struct Factorial<2> ; "
"struct Factorial<1> ; " "struct Factorial<1> ; "
@ -1342,7 +1349,8 @@ private:
"int main () {\n" "int main () {\n"
" return diagonalGroupTest<4>();\n" " return diagonalGroupTest<4>();\n"
"}"; "}";
const char exp[] = "struct Factorial<4> ; " const char exp[] = "struct Factorial<0> ; "
"struct Factorial<4> ; "
"struct Factorial<3> ; " "struct Factorial<3> ; "
"struct Factorial<2> ; " "struct Factorial<2> ; "
"struct Factorial<1> ; " "struct Factorial<1> ; "
@ -1576,13 +1584,22 @@ private:
" void foo() { }\n" " void foo() { }\n"
"};"; "};";
const char exp [] = "template < typename T , typename V , int KeySize = 0 > class Bar ; " const char exp [] = "template < typename T , typename V , int KeySize = 0 > class Bar ; "
"class Bar<void,void> ; "
"class Bar<void,void> { " "class Bar<void,void> { "
"} ; " "} ; "
"template < typename K , typename V , int KeySize = 0 > " "template < typename K , typename V , int KeySize = 0 > "
"class Bar : private Bar<void,void> { " "class Bar : private Bar<void,void> { "
"void foo ( ) { } " "void foo ( ) { } "
"} ;"; "} ;";
ASSERT_EQUALS(exp, tok(code)); const char act [] = "template < typename T , typename V , int KeySize = 0 > class Bar ; "
"class Bar<void,void> { "
"} ; "
"class Bar<void,void> ; "
"template < typename K , typename V , int KeySize = 0 > "
"class Bar : private Bar<void,void> { "
"void foo ( ) { } "
"} ;";
TODO_ASSERT_EQUALS(exp, act, tok(code));
} }
void template71() { // #8821 void template71() { // #8821
@ -1679,7 +1696,8 @@ private:
" std::cout << is_void<char>::value << std::endl;\n" " std::cout << is_void<char>::value << std::endl;\n"
" std::cout << is_void<void>::value << std::endl;\n" " std::cout << is_void<void>::value << std::endl;\n"
"}"; "}";
const char exp[] = "struct is_void<char> ; " const char exp[] = "struct is_void<void> ; "
"struct is_void<char> ; "
"struct is_void<void> : std :: true_type { } ; " "struct is_void<void> : std :: true_type { } ; "
"int main ( ) { " "int main ( ) { "
"std :: cout << is_void<char> :: value << std :: endl ; " "std :: cout << is_void<char> :: value << std :: endl ; "
@ -2846,7 +2864,8 @@ private:
" b.A<>::Print();\n" " b.A<>::Print();\n"
" return 0;\n" " return 0;\n"
"}"; "}";
ASSERT_EQUALS("template < typename T0 > class A ; " ASSERT_EQUALS("class A<void> ; "
"template < typename T0 > class A ; "
"class A<void> { " "class A<void> { "
"public: " "public: "
"A<void> ( ) { } " "A<void> ( ) { } "
@ -3261,6 +3280,7 @@ private:
"template class Fred<1>;\n" "template class Fred<1>;\n"
"}\n"; "}\n";
ASSERT_EQUALS("namespace NS { " ASSERT_EQUALS("namespace NS { "
"struct Barney<1> ; "
"template < int type > struct Barney ; " "template < int type > struct Barney ; "
"struct Barney<1> { } ; " "struct Barney<1> { } ; "
"class Fred<1> ; " "class Fred<1> ; "
@ -3577,7 +3597,8 @@ private:
const char code[] = "template<typename T> class C { };\n" const char code[] = "template<typename T> class C { };\n"
"template<> class C<char> { };\n" "template<> class C<char> { };\n"
"map<int> m;\n"; "map<int> m;\n";
const char expected[] = "template < typename T > class C { } ; " const char expected[] = "class C<char> ; "
"template < typename T > class C { } ; "
"class C<char> { } ; " "class C<char> { } ; "
"map < int > m ;"; "map < int > m ;";
ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS(expected, tok(code));
@ -3587,7 +3608,8 @@ private:
"template<> class C<char> { };\n" "template<> class C<char> { };\n"
"map<int> m;\n" "map<int> m;\n"
"C<int> i;"; "C<int> i;";
const char expected[] = "class C<int> ; " const char expected[] = "class C<char> ; "
"class C<int> ; "
"class C<char> { } ; " "class C<char> { } ; "
"map < int > m ; " "map < int > m ; "
"C<int> i ; " "C<int> i ; "
@ -3600,7 +3622,8 @@ private:
"map<int> m;\n" "map<int> m;\n"
"C<int> i;\n" "C<int> i;\n"
"C<char> c;"; "C<char> c;";
const char expected[] = "class C<int> ; " const char expected[] = "class C<char> ; "
"class C<int> ; "
"class C<char> { } ; " "class C<char> { } ; "
"map < int > m ; " "map < int > m ; "
"C<int> i ; " "C<int> i ; "