fix #9983 (TemplateSimplifier: template not simplified : f(0,0);) (#2905)

Currently template type deduction for functions only works for single
template argument single function argument functions with a literal
argument.

This patch starts to add support for single template argument multiple
function argument functions.  It correctly handles functions with
multiple arguments of the same type. It also handles a mix of template
and non-template arguments.

It does not add support for overloading non-template arguments.
It does not add support for multiple parameter template functions.
It does not add support for type deduction from variable arguments.

Co-authored-by: Robert Reif <reif@FX6840>
This commit is contained in:
IOBYTE 2020-11-17 00:51:32 -05:00 committed by GitHub
parent 22d6160624
commit c4b3d4cd77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 161 additions and 10 deletions

View File

@ -780,6 +780,32 @@ void TemplateSimplifier::addInstantiation(Token *token, const std::string &scope
mTemplateInstantiations.emplace_back(instantiation);
}
static void getFunctionArguments(const Token *nameToken, std::vector<const Token *> &args)
{
const Token *argToken = nameToken->tokAt(2);
if (argToken->str() == ")")
return;
args.push_back(argToken);
while ((argToken = argToken->nextArgumentBeforeCreateLinks2()))
args.push_back(argToken);
}
static bool areAllParamsTypes(const std::vector<const Token *> &params)
{
if (params.empty())
return false;
for (auto param : params) {
if (!Token::Match(param->previous(), "typename|class %name% ,|>"))
return false;
}
return true;
}
void TemplateSimplifier::getTemplateInstantiations()
{
std::multimap<std::string, const TokenAndName *> functionNameMap;
@ -847,9 +873,17 @@ void TemplateSimplifier::getTemplateInstantiations()
tok = tok->tokAt(2);
}
// skip specialization
if (tok == skip) {
skip = nullptr;
continue;
}
// look for function instantiation with type deduction
// fixme: only single argument functions supported
if (tok->strAt(1) == "(") {
std::vector<const Token *> instantiationArgs;
getFunctionArguments(tok, instantiationArgs);
std::string fullName;
if (!qualification.empty())
fullName = qualification + " :: " + tok->str();
@ -857,39 +891,69 @@ void TemplateSimplifier::getTemplateInstantiations()
fullName = scopeName + " :: " + tok->str();
else
fullName = tok->str();
// get all declarations with this name
for (auto pos = functionNameMap.lower_bound(tok->str());
pos != functionNameMap.upper_bound(tok->str()); ++pos) {
// look for declaration with same qualification
if (pos->second->fullName() == fullName) {
// make sure it is a single argument function
if (Token::Match(pos->second->token()->tokAt(2), "typename|class %name% >") &&
Token::Match(pos->second->nameToken()->tokAt(2), "const| %type% &| %name%| )") &&
Token::Match(tok->tokAt(2), "%num%|%str%|%char%|%bool% )")) {
std::vector<const Token *> templateParams;
getTemplateParametersInDeclaration(pos->second->token()->tokAt(2), templateParams);
// todo: handle more than one template parameter
if (templateParams.size() != 1 || !areAllParamsTypes(templateParams))
continue;
std::vector<const Token *> declarationParams;
getFunctionArguments(pos->second->nameToken(), declarationParams);
// function argument counts must match
if (instantiationArgs.empty() || instantiationArgs.size() != declarationParams.size())
continue;
size_t match = 0;
size_t argMatch = 0;
for (size_t i = 0; i < declarationParams.size(); ++i) {
// fixme: only type deducton from literals is supported
bool isArgLiteral = Token::Match(instantiationArgs[i], "%num%|%str%|%char%|%bool% ,|)");
if (isArgLiteral && Token::Match(declarationParams[i], "const| %type% &| %name%| ,|)")) {
match++;
// check if parameter types match
if (templateParams[0]->str() == declarationParams[i]->str())
argMatch = i;
else {
// todo: check if non-template args match for function overloads
}
}
}
if (match == declarationParams.size()) {
const Token *arg = instantiationArgs[argMatch];
tok->insertToken(">");
switch (tok->tokAt(3)->tokType()) {
switch (arg->tokType()) {
case Token::eBoolean:
tok->insertToken("bool");
break;
case Token::eChar:
if (tok->tokAt(3)->isLong())
if (arg->isLong())
tok->insertToken("wchar_t");
else
tok->insertToken("char");
break;
case Token::eString:
tok->insertToken("*");
if (tok->tokAt(4)->isLong())
if (arg->isLong())
tok->insertToken("wchar_t");
else
tok->insertToken("char");
tok->insertToken("const");
break;
case Token::eNumber: {
MathLib::value num(tok->strAt(3));
MathLib::value num(arg->str());
if (num.isFloat()) {
// MathLib::getSuffix doesn't work for floating point numbers
char suffix = tok->strAt(3).back();
char suffix = arg->str().back();
if (suffix == 'f' || suffix == 'F')
tok->insertToken("float");
else if (suffix == 'l' || suffix == 'L') {

View File

@ -248,6 +248,7 @@ private:
TEST_CASE(templateTypeDeduction1); // #8962
TEST_CASE(templateTypeDeduction2);
TEST_CASE(templateTypeDeduction3);
TEST_CASE(templateTypeDeduction4); // #9983
TEST_CASE(simplifyTemplateArgs1);
TEST_CASE(simplifyTemplateArgs2);
@ -5281,6 +5282,92 @@ private:
ASSERT_EQUALS(exp, tok(code));
}
void templateTypeDeduction4() { // #9983
{
const char code[] = "int a = 1;\n"
"template <typename T> void f(T x, T y) { a = x + y; }\n"
"void test() { f(0, 0); }";
const char exp[] = "int a ; a = 1 ; "
"void f<int> ( int x , int y ) ; "
"void test ( ) { f<int> ( 0 , 0 ) ; } "
"void f<int> ( int x , int y ) { a = x + y ; }";
ASSERT_EQUALS(exp, tok(code));
}
{
const char code[] = "int a = 1;\n"
"template <typename T> void f(T x, double y) { a = x + y; }\n"
"void test() { f(0, 0.0); }";
const char exp[] = "int a ; a = 1 ; "
"void f<int> ( int x , double y ) ; "
"void test ( ) { f<int> ( 0 , 0.0 ) ; } "
"void f<int> ( int x , double y ) { a = x + y ; }";
ASSERT_EQUALS(exp, tok(code));
}
{
const char code[] = "int a = 1;\n"
"template <typename T> void f(double x, T y) { a = x + y; }\n"
"void test() { f(0.0, 0); }";
const char exp[] = "int a ; a = 1 ; "
"void f<int> ( double x , int y ) ; "
"void test ( ) { f<int> ( 0.0 , 0 ) ; } "
"void f<int> ( double x , int y ) { a = x + y ; }";
ASSERT_EQUALS(exp, tok(code));
}
{
const char code[] = "int a = 1;\n"
"template <typename T> void f(double x, T y) { a = x + y; }\n"
"template <typename T> void f(int x, T y) { a = x + y; }\n"
"void test() {\n"
" f(0, 0);\n"
" f(0.0, 0);\n"
" f(0, 0.0);\n"
" f(0.0, 0.0);\n"
"}";
const char exp[] = "int a ; a = 1 ; "
"void f<int> ( int x , int y ) ; "
"void f<int> ( double x , int y ) ; "
"void f<double> ( int x , double y ) ; "
"void f<double> ( double x , double y ) ; "
"void test ( ) { "
"f<int> ( 0 , 0 ) ; "
"f<int> ( 0.0 , 0 ) ; "
"f<double> ( 0 , 0.0 ) ; "
"f<double> ( 0.0 , 0.0 ) ; "
"} "
"void f<int> ( int x , int y ) { a = x + y ; } "
"void f<int> ( double x , int y ) { a = x + y ; } "
"void f<double> ( int x , double y ) { a = x + y ; } "
"void f<double> ( double x , double y ) { a = x + y ; }";
const char act[] = "int a ; a = 1 ; "
"template < typename T > void f ( double x , T y ) { a = x + y ; } "
"void f<int> ( int x , int y ) ; void f<double> ( int x , double y ) ; "
"void test ( ) { "
"f<int> ( 0 , 0 ) ; "
"f<int> ( 0.0 , 0 ) ; "
"f<double> ( 0 , 0.0 ) ; "
"f<double> ( 0.0 , 0.0 ) ; "
"} "
"void f<int> ( int x , int y ) { a = x + y ; } "
"void f<double> ( int x , double y ) { a = x + y ; }";
TODO_ASSERT_EQUALS(exp, act, tok(code));
}
{
const char code[] = "int a = 1;\n"
"template <typename T, typename U> void f(T x, U y) { a = x + y; }\n"
"void test() { f(0, 0.0); }";
const char exp[] = "int a ; a = 1 ; "
"void f<int,double> ( int x , double y ) ; "
"void test ( ) { f<int,double> ( 0 , 0.0 ) ; } "
"void f<int,double> ( int x , double y ) { a = x + y ; }";
const char act[] = "int a ; a = 1 ; "
"template < typename T , typename U > void f ( T x , U y ) { a = x + y ; } "
"void test ( ) { f ( 0 , 0.0 ) ; }";
TODO_ASSERT_EQUALS(exp, act, tok(code));
}
}
void simplifyTemplateArgs1() {
ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template<int N> foo = N; foo < ( 2 ) >;"));
ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template<int N> foo = N; foo < 1 + 1 >;"));