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:
parent
22d6160624
commit
c4b3d4cd77
|
@ -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 *> ¶ms)
|
||||
{
|
||||
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') {
|
||||
|
|
|
@ -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 >;"));
|
||||
|
|
Loading…
Reference in New Issue