Skip to content

Commit 90c62dc

Browse files
author
Your Name
committed
Handle overloading better
1 parent de77bc6 commit 90c62dc

2 files changed

Lines changed: 92 additions & 16 deletions

File tree

lib/templatesimplifier.cpp

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -848,12 +848,21 @@ namespace {
848848

849849
class DeductionSymbolTable;
850850

851+
// a function template declaration that a call could instantiate,
852+
// together with its template type parameters
853+
struct DeductionCandidate {
854+
const TemplateSimplifier::TokenAndName *declaration;
855+
std::vector<const Token *> templateParams;
856+
};
857+
851858
// Token keyword flags are not set yet when templates are simplified so
852859
// keywords are recognized by their string.
853860
struct DeductionContext {
854861
const Settings &settings;
855862
const TokenList &tokenList;
856863
const DeductionSymbolTable &symbols;
864+
// the function templates, by name
865+
const std::multimap<std::string, DeductionCandidate> &templateFunctions;
857866

858867
// is |tok| a name that can be a user defined identifier?
859868
bool isIdentifier(const Token *tok) const {
@@ -1312,7 +1321,8 @@ namespace {
13121321
// the walk can record a declaration twice (class members are
13131322
// recorded up front and again when the body is walked)
13141323
if (std::none_of(declarations.cbegin(), declarations.cend(), [&](const ParsedType &declaration) {
1315-
return sameDeducedType(declaration.type, returnType.type);
1324+
return (!declaration.type.valid && !returnType.type.valid) ||
1325+
sameDeducedType(declaration.type, returnType.type);
13161326
}))
13171327
declarations.push_back(returnType);
13181328
}
@@ -1341,6 +1351,23 @@ namespace {
13411351
return false;
13421352
}
13431353

1354+
// is a (non template) function with this name visible?
1355+
bool hasFunction(const std::string &name) const {
1356+
for (auto scope = mScopes.crbegin(); scope != mScopes.crend(); ++scope) {
1357+
if (scope->members.functions.find(name) != scope->members.functions.cend())
1358+
return true;
1359+
if (scope->memberClass.empty())
1360+
continue;
1361+
for (const std::string &className : withBaseClasses(scope->memberClass)) {
1362+
const auto classIt = mClassMembers.find(className);
1363+
if (classIt != mClassMembers.cend() &&
1364+
classIt->second.functions.find(name) != classIt->second.functions.cend())
1365+
return true;
1366+
}
1367+
}
1368+
return false;
1369+
}
1370+
13441371
// all visible declarations of the function must agree on the return type
13451372
bool lookupFunctionReturnType(const std::string &name, ParsedType &result) const {
13461373
bool found = false;
@@ -1574,8 +1601,12 @@ static ExprType parseOperandType(const DeductionContext &ctx, const Token *&tok,
15741601
return ExprType();
15751602
} else if (ctx.isIdentifier(tok)) {
15761603
if (tok->next() != end && Token::simpleMatch(tok->next(), "(")) {
1577-
// function call
1604+
// function call: the return type of the visible declarations.
1605+
// When the name is also a function template the call could
1606+
// resolve to an instantiation with another return type.
15781607
ParsedType returnType;
1608+
if (ctx.templateFunctions.count(tok->str()) != 0)
1609+
return ExprType();
15791610
if (!ctx.symbols.lookupFunctionReturnType(tok->str(), returnType))
15801611
return ExprType();
15811612
if (!tok->next()->link())
@@ -1784,15 +1815,6 @@ static bool deduceTemplateArgumentFromParam(const ParsedType &param, const ExprT
17841815
return renderDeducedType(type, keepTopConst, tokens);
17851816
}
17861817

1787-
namespace {
1788-
// a function template declaration that a call could instantiate,
1789-
// together with its template type parameters
1790-
struct DeductionCandidate {
1791-
const TemplateSimplifier::TokenAndName *declaration;
1792-
std::vector<const Token *> templateParams;
1793-
};
1794-
}
1795-
17961818
// Deduce the template arguments of the function template call at |tok| from
17971819
// the argument types and insert the explicit "< ... >" tokens after the
17981820
// function name when the deduction succeeds.
@@ -1978,8 +2000,14 @@ static void recordDeclaration(const DeductionContext &ctx, DeductionSymbolTable
19782000
const Token *rpar = tok->next()->link();
19792001
if (Token::Match(rpar->next(), ";|{|const|noexcept")) {
19802002
const Token *start = findDeclarationTypeStart(ctx, tok);
1981-
if (start && parseType(ctx, start, tok, parsed) == tok && parsed.type.valid)
1982-
symbols.addFunction(tok->str(), parsed);
2003+
if (start) {
2004+
if (parseType(ctx, start, tok, parsed) == tok && parsed.type.valid)
2005+
symbols.addFunction(tok->str(), parsed);
2006+
else
2007+
// unsupported return type ("void" etc): record that the
2008+
// function exists but that its type is not known
2009+
symbols.addFunction(tok->str(), ParsedType());
2010+
}
19832011
}
19842012
}
19852013
}
@@ -2031,7 +2059,7 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
20312059
// during the walk so that at every call site the declarations that are
20322060
// visible there - and only those - can be looked up by name.
20332061
DeductionSymbolTable symbols;
2034-
const DeductionContext ctx = { mSettings, mTokenList, symbols };
2062+
const DeductionContext ctx = { mSettings, mTokenList, symbols, functionNameMap };
20352063

20362064
// the body of the class definition that is about to start
20372065
const Token *classBodyStart = nullptr;
@@ -2138,9 +2166,13 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
21382166
if (tok->strAt(1) == "(") {
21392167
const auto range = functionNameMap.equal_range(tok->str());
21402168

2141-
// a visible variable with the same name hides the function templates
2169+
// A visible variable with the same name hides the function
2170+
// templates. A visible non template function with the same
2171+
// name might be a better match than the templates (overload
2172+
// resolution is not supported). Bail out in both cases.
21422173
ParsedType shadowingVariable;
2143-
if (range.first == range.second || ctx.symbols.lookupVariable(tok->str(), shadowingVariable))
2174+
if (range.first == range.second || ctx.symbols.lookupVariable(tok->str(), shadowingVariable) ||
2175+
ctx.symbols.hasFunction(tok->str()))
21442176
continue;
21452177

21462178
// The name is looked up like the compiler does: in the

test/testsimplifytemplate.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ class TestSimplifyTemplate : public TestFixture {
292292
TEST_CASE(templateTypeDeduction11); // unqualified lookup: enclosing scopes, shadowing
293293
TEST_CASE(templateTypeDeduction12); // base class member templates, out of class method bodies
294294
TEST_CASE(templateTypeDeduction13); // members declared after the member function are visible
295+
TEST_CASE(templateTypeDeduction14); // a non template overload might be a better match
295296

296297
TEST_CASE(simplifyTemplateArgs1);
297298
TEST_CASE(simplifyTemplateArgs2);
@@ -6756,6 +6757,49 @@ class TestSimplifyTemplate : public TestFixture {
67566757
}
67576758
}
67586759

6760+
void templateTypeDeduction14() { // a non template overload might be a better match: don't deduce
6761+
{
6762+
// the non template f(int) wins the overload resolution (and the
6763+
// template would fail the substitution of int::type)
6764+
const char code[] = "template<class T>\n"
6765+
"typename T::type f(T);\n"
6766+
"int f(int);\n"
6767+
"void use() { f(1); }";
6768+
const char exp[] = "template < class T > "
6769+
"T :: type f ( T ) ; "
6770+
"int f ( int ) ; "
6771+
"void use ( ) { f ( 1 ) ; }";
6772+
ASSERT_EQUALS(exp, tok(code));
6773+
}
6774+
{
6775+
// member function overload set
6776+
const char code[] = "struct M {\n"
6777+
" template<class T> void mf(T t) { (void)t; }\n"
6778+
" void mf(int);\n"
6779+
" void go() { mf(1); }\n"
6780+
"};";
6781+
const char exp[] = "struct M { "
6782+
"template < class T > void mf ( T t ) { ( void ) t ; } "
6783+
"void mf ( int ) ; "
6784+
"void go ( ) { mf ( 1 ) ; } "
6785+
"} ;";
6786+
ASSERT_EQUALS(exp, tok(code));
6787+
}
6788+
{
6789+
// mixed overload set as a nested call: the return type is not
6790+
// known so the outer call is not deduced either
6791+
const char code[] = "template<class T> void g(T t) { (void)t; }\n"
6792+
"template<class T> T h(T t) { return t; }\n"
6793+
"long h(char);\n"
6794+
"void use() { g(h('a')); }";
6795+
const char exp[] = "template < class T > void g ( T t ) { ( void ) t ; } "
6796+
"template < class T > T h ( T t ) { return t ; } "
6797+
"long h ( char ) ; "
6798+
"void use ( ) { g ( h ( 'a' ) ) ; }";
6799+
ASSERT_EQUALS(exp, tok(code));
6800+
}
6801+
}
6802+
67596803
void simplifyTemplateArgs1() {
67606804
ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template<int N> foo = N; foo < ( 2 ) >;"));
67616805
ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template<int N> foo = N; foo < 1 + 1 >;"));

0 commit comments

Comments
 (0)