Skip to content

Commit b60e55e

Browse files
author
Your Name
committed
Improve member functions
1 parent 695de6e commit b60e55e

2 files changed

Lines changed: 331 additions & 37 deletions

File tree

lib/templatesimplifier.cpp

Lines changed: 236 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,10 @@ namespace {
12521252
// Scope aware symbol table used for function template argument
12531253
// deduction. It is filled during a single forward pass over the token
12541254
// list; at any point during that pass it contains exactly the
1255-
// declarations that are visible at the current position.
1255+
// declarations that are visible at the current position. The member
1256+
// declarations of a class are kept when its scope ends so that they stay
1257+
// visible in out of class member function bodies and in classes that
1258+
// derive from it.
12561259
class DeductionSymbolTable {
12571260
public:
12581261
DeductionSymbolTable() : mScopes(1) {} // global scope
@@ -1263,6 +1266,19 @@ namespace {
12631266
mScopes.back().endTok = endTok;
12641267
}
12651268

1269+
// enter the body of the class |className| (a full name)
1270+
void enterClassScope(const Token *endTok, std::string className) {
1271+
enterScope(endTok);
1272+
mScopes.back().memberClass = std::move(className);
1273+
mScopes.back().isClassBody = true;
1274+
}
1275+
1276+
// record that the class |className| (a full name) has the given
1277+
// base classes (names as written in the code)
1278+
void addBaseClasses(const std::string &className, std::vector<std::string> baseClasses) {
1279+
mBaseClasses[className] = std::move(baseClasses);
1280+
}
1281+
12661282
// Called when the forward pass reaches |tok| (a ')' or '}'): leave
12671283
// the scopes that end here. A parenthesized group that is directly
12681284
// attached to a body - a function parameter list or a for/if/while
@@ -1273,28 +1289,48 @@ namespace {
12731289
const Token *bodyStart = findAttachedBody(tok);
12741290
if (bodyStart && bodyStart->link()) {
12751291
mScopes.back().endTok = bodyStart->link();
1292+
// in an out of class member function body the class
1293+
// members are visible
1294+
if (mScopes.back().memberClass.empty())
1295+
mScopes.back().memberClass = memberFunctionClass(tok);
12761296
continue; // re-check: the end token changed
12771297
}
12781298
}
1299+
// keep the members of a class when its scope ends
1300+
if (mScopes.back().isClassBody)
1301+
mClassMembers[mScopes.back().memberClass] = std::move(mScopes.back().members);
12791302
mScopes.pop_back();
12801303
}
12811304
}
12821305

12831306
void addVariable(const std::string &name, const ParsedType &type) {
1284-
mScopes.back().variables[name] = type;
1307+
mScopes.back().members.variables[name] = type;
12851308
}
12861309

12871310
void addFunction(const std::string &name, const ParsedType &returnType) {
1288-
mScopes.back().functions[name].push_back(returnType);
1311+
mScopes.back().members.functions[name].push_back(returnType);
12891312
}
12901313

12911314
bool lookupVariable(const std::string &name, ParsedType &result) const {
12921315
for (auto scope = mScopes.crbegin(); scope != mScopes.crend(); ++scope) {
1293-
const auto it = scope->variables.find(name);
1294-
if (it != scope->variables.cend()) {
1316+
const auto it = scope->members.variables.find(name);
1317+
if (it != scope->members.variables.cend()) {
12951318
result = it->second;
12961319
return true;
12971320
}
1321+
if (scope->memberClass.empty())
1322+
continue;
1323+
// class members (and inherited members) are visible here
1324+
for (const std::string &className : withBaseClasses(scope->memberClass)) {
1325+
const auto classIt = mClassMembers.find(className);
1326+
if (classIt == mClassMembers.cend())
1327+
continue;
1328+
const auto varIt = classIt->second.variables.find(name);
1329+
if (varIt != classIt->second.variables.cend()) {
1330+
result = varIt->second;
1331+
return true;
1332+
}
1333+
}
12981334
}
12991335
return false;
13001336
}
@@ -1303,20 +1339,79 @@ namespace {
13031339
bool lookupFunctionReturnType(const std::string &name, ParsedType &result) const {
13041340
bool found = false;
13051341
for (auto scope = mScopes.crbegin(); scope != mScopes.crend(); ++scope) {
1306-
const auto it = scope->functions.find(name);
1307-
if (it == scope->functions.cend())
1342+
if (!collectFunctionReturnType(scope->members, name, result, found))
1343+
return false;
1344+
if (scope->memberClass.empty())
13081345
continue;
1309-
for (const ParsedType &declaration : it->second) {
1310-
if (found && !sameDeducedType(result.type, declaration.type))
1311-
return false; // overloads with different return types
1312-
result = declaration;
1313-
found = true;
1346+
for (const std::string &className : withBaseClasses(scope->memberClass)) {
1347+
const auto classIt = mClassMembers.find(className);
1348+
if (classIt != mClassMembers.cend() &&
1349+
!collectFunctionReturnType(classIt->second, name, result, found))
1350+
return false;
13141351
}
13151352
}
13161353
return found;
13171354
}
13181355

1356+
// The class |scope| followed by its transitive base classes; used to
1357+
// search a class scope the way unqualified lookup does. For a name
1358+
// that is not a known class only the scope itself is returned.
1359+
std::vector<std::string> withBaseClasses(const std::string &scope) const {
1360+
std::vector<std::string> result;
1361+
result.push_back(scope);
1362+
if (scope.empty())
1363+
return result;
1364+
for (std::size_t i = 0; i < result.size() && result.size() < 20; ++i) {
1365+
const auto it = mBaseClasses.find(result[i]);
1366+
if (it == mBaseClasses.cend())
1367+
continue;
1368+
const std::string::size_type separator = result[i].rfind(" :: ");
1369+
const std::string enclosing = (separator == std::string::npos) ? "" : result[i].substr(0, separator);
1370+
for (const std::string &base : it->second) {
1371+
const std::string resolved = resolveClassName(enclosing, base);
1372+
if (std::find(result.cbegin(), result.cend(), resolved) == result.cend())
1373+
result.push_back(resolved);
1374+
}
1375+
}
1376+
return result;
1377+
}
1378+
13191379
private:
1380+
struct Members {
1381+
std::unordered_map<std::string, ParsedType> variables;
1382+
std::unordered_map<std::string, std::vector<ParsedType>> functions;
1383+
};
1384+
1385+
// add the return types of the declarations of |name| in |members|;
1386+
// returns false when the declarations do not agree on the type
1387+
static bool collectFunctionReturnType(const Members &members, const std::string &name, ParsedType &result, bool &found) {
1388+
const auto it = members.functions.find(name);
1389+
if (it == members.functions.cend())
1390+
return true;
1391+
for (const ParsedType &declaration : it->second) {
1392+
if (found && !sameDeducedType(result.type, declaration.type))
1393+
return false; // overloads with different return types
1394+
result = declaration;
1395+
found = true;
1396+
}
1397+
return true;
1398+
}
1399+
1400+
// Resolve the class name |name| as it would be looked up from
1401+
// |enclosingScope|: the innermost enclosing scope wins.
1402+
std::string resolveClassName(std::string enclosingScope, const std::string &name) const {
1403+
for (;;) {
1404+
const std::string candidate = enclosingScope.empty() ? name : enclosingScope + " :: " + name;
1405+
if (mClassMembers.find(candidate) != mClassMembers.cend() ||
1406+
mBaseClasses.find(candidate) != mBaseClasses.cend())
1407+
return candidate;
1408+
if (enclosingScope.empty())
1409+
return name;
1410+
const std::string::size_type separator = enclosingScope.rfind(" :: ");
1411+
enclosingScope = (separator == std::string::npos) ? "" : enclosingScope.substr(0, separator);
1412+
}
1413+
}
1414+
13201415
// If the parenthesized group ending at |rpar| is directly attached to
13211416
// a body ("...) {", "...) const {", "...) : init(x) {") return the
13221417
// "{" of that body, otherwise nullptr.
@@ -1341,12 +1436,37 @@ namespace {
13411436
return Token::simpleMatch(tok, "{") ? tok : nullptr;
13421437
}
13431438

1439+
// For the parameter list of an out of class member function
1440+
// definition ("void A::g(..)") return the full name of the class,
1441+
// otherwise an empty string.
1442+
std::string memberFunctionClass(const Token *rpar) const {
1443+
const Token *lpar = rpar->link();
1444+
if (!lpar)
1445+
return "";
1446+
const Token *nameTok = lpar->previous();
1447+
if (!nameTok || !nameTok->isName())
1448+
return "";
1449+
std::string qualifier;
1450+
const Token *tok = nameTok;
1451+
while (Token::Match(tok->tokAt(-2), "%name% ::")) {
1452+
qualifier = qualifier.empty() ? tok->strAt(-2) : tok->strAt(-2) + " :: " + qualifier;
1453+
tok = tok->tokAt(-2);
1454+
}
1455+
if (qualifier.empty())
1456+
return "";
1457+
const std::string enclosingScope = nameTok->scopeInfo() ? nameTok->scopeInfo()->name : std::string();
1458+
return resolveClassName(enclosingScope, qualifier);
1459+
}
1460+
13441461
struct Scope {
13451462
const Token *endTok = nullptr;
1346-
std::unordered_map<std::string, ParsedType> variables;
1347-
std::unordered_map<std::string, std::vector<ParsedType>> functions;
1463+
std::string memberClass; // class whose members are visible in this scope
1464+
bool isClassBody = false;
1465+
Members members;
13481466
};
13491467
std::vector<Scope> mScopes;
1468+
std::unordered_map<std::string, Members> mClassMembers;
1469+
std::unordered_map<std::string, std::vector<std::string>> mBaseClasses;
13501470
};
13511471
}
13521472

@@ -1867,22 +1987,29 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
18671987
DeductionSymbolTable symbols;
18681988
const DeductionContext ctx = { mSettings, mTokenList, symbols };
18691989

1990+
// the body of the class definition that is about to start
1991+
const Token *classBodyStart = nullptr;
1992+
std::string className;
1993+
18701994
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
1871-
// Skip template declarations: the declarations inside them are not
1872-
// visible outside and calls inside them are only deduced when the
1873-
// template is instantiated. User specializations (template <>)
1874-
// contain concrete code and are walked.
18751995
if (!tok->isName()) {
18761996
// scope begin/end (only "(){}[]" have links; "[]" is not a scope)
18771997
if (tok->link()) {
18781998
if (Token::Match(tok, ")|}"))
18791999
symbols.leaveScopesEndingAt(tok);
1880-
else if (Token::Match(tok, "(|{"))
2000+
else if (tok == classBodyStart) {
2001+
symbols.enterClassScope(tok->link(), std::move(className));
2002+
classBodyStart = nullptr;
2003+
} else if (Token::Match(tok, "(|{"))
18812004
symbols.enterScope(tok->link());
18822005
}
18832006
continue;
18842007
}
18852008

2009+
// Skip template declarations: the declarations inside them are not
2010+
// visible outside and calls inside them are only deduced when the
2011+
// template is instantiated. User specializations (template <>)
2012+
// contain concrete code and are walked.
18862013
if (Token::simpleMatch(tok, "template <")) {
18872014
Token *closing = tok->next()->findClosingBracket();
18882015
if (!closing)
@@ -1896,6 +2023,60 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
18962023
continue;
18972024
}
18982025

2026+
// class definition: record the base classes and remember the body
2027+
// so that the member declarations can be associated with the class
2028+
if (Token::Match(tok, "class|struct|union %name%") && !Token::simpleMatch(tok->previous(), "enum")) {
2029+
Token *nameTok = tok->next();
2030+
while (Token::Match(nameTok, "%name% %name%")) // skip attribute macros
2031+
nameTok = nameTok->next();
2032+
Token *after = nameTok->next();
2033+
if (Token::simpleMatch(after, "final"))
2034+
after = after->next();
2035+
std::vector<std::string> baseClasses;
2036+
if (Token::simpleMatch(after, ":")) {
2037+
Token *base = after->next();
2038+
after = nullptr;
2039+
while (base) {
2040+
while (Token::Match(base, "public|protected|private|virtual"))
2041+
base = base->next();
2042+
if (!base || !base->isName())
2043+
break;
2044+
std::string baseName = base->str();
2045+
while (Token::Match(base->next(), ":: %name%")) {
2046+
baseName += " :: " + base->strAt(2);
2047+
base = base->tokAt(2);
2048+
}
2049+
base = base->next();
2050+
if (base && base->str() == "<") {
2051+
// template base classes are not supported
2052+
base = base->findClosingBracket();
2053+
if (base)
2054+
base = base->next();
2055+
baseName.clear();
2056+
}
2057+
if (!baseName.empty())
2058+
baseClasses.push_back(std::move(baseName));
2059+
if (Token::simpleMatch(base, ","))
2060+
base = base->next();
2061+
else
2062+
break;
2063+
}
2064+
if (Token::simpleMatch(base, "{"))
2065+
after = base;
2066+
else
2067+
baseClasses.clear();
2068+
}
2069+
if (Token::simpleMatch(after, "{") && after->link()) {
2070+
const std::string &enclosingScope = tok->scopeInfo() ? tok->scopeInfo()->name : std::string();
2071+
className = enclosingScope.empty() ? nameTok->str() : (enclosingScope + " :: " + nameTok->str());
2072+
if (!baseClasses.empty())
2073+
symbols.addBaseClasses(className, std::move(baseClasses));
2074+
classBodyStart = after;
2075+
tok = after->previous(); // continue at the "{"
2076+
}
2077+
continue;
2078+
}
2079+
18992080
// function template call with type deduction? (a deducible call is
19002081
// "name (" or "name :: name (" - anything else can be skipped cheaply)
19012082
if (Token::Match(tok->next(), "(|::") && isTemplateInstantion(tok) && tok->scopeInfo()) {
@@ -1914,23 +2095,30 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
19142095
continue;
19152096

19162097
// The name is looked up like the compiler does: in the
1917-
// enclosing scopes from the innermost outwards, where a match
1918-
// in an inner scope shadows declarations in outer scopes.
2098+
// enclosing scopes from the innermost outwards (searching the
2099+
// base classes of a class scope), where a match in an inner
2100+
// scope shadows declarations in outer scopes.
19192101
std::vector<const DeductionCandidate *> candidates;
2102+
std::string matchedScope;
2103+
std::string levelScope;
19202104
std::string scopePath = scopeName;
19212105
for (;;) {
1922-
std::string fullName;
1923-
if (!scopePath.empty())
1924-
fullName = scopePath + " :: ";
2106+
levelScope = scopePath;
19252107
if (!qualification.empty())
1926-
fullName += qualification + " :: ";
1927-
fullName += tok->str();
1928-
1929-
for (auto pos = range.first; pos != range.second; ++pos) {
1930-
// look for declaration with same qualification or constructor with same qualification
1931-
if (pos->second.declaration->fullName() == fullName ||
1932-
(pos->second.declaration->scope() == fullName && tok->str() == pos->second.declaration->name()))
1933-
candidates.push_back(&pos->second);
2108+
levelScope += (levelScope.empty() ? "" : " :: ") + qualification;
2109+
2110+
for (const std::string &scope : symbols.withBaseClasses(levelScope)) {
2111+
const std::string fullName = scope.empty() ? tok->str() : (scope + " :: " + tok->str());
2112+
for (auto pos = range.first; pos != range.second; ++pos) {
2113+
// look for declaration with same qualification or constructor with same qualification
2114+
if (pos->second.declaration->fullName() == fullName ||
2115+
(pos->second.declaration->scope() == fullName && tok->str() == pos->second.declaration->name()))
2116+
candidates.push_back(&pos->second);
2117+
}
2118+
if (!candidates.empty()) {
2119+
matchedScope = scope;
2120+
break; // a match shadows the base classes
2121+
}
19342122
}
19352123
if (!candidates.empty() || scopePath.empty())
19362124
break;
@@ -1939,8 +2127,23 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
19392127
scopePath = (separator == std::string::npos) ? "" : scopePath.substr(0, separator);
19402128
}
19412129

1942-
if (!candidates.empty())
2130+
if (!candidates.empty()) {
19432131
deduceTemplateArgumentsAtFunctionCall(ctx, tok, candidates);
2132+
if (matchedScope != levelScope && tok->strAt(1) == "<") {
2133+
// The declaration was found in a base class: qualify
2134+
// the call so that the instantiation is matched to
2135+
// the declaration.
2136+
std::string::size_type start = 0;
2137+
for (;;) {
2138+
const std::string::size_type separator = matchedScope.find(" :: ", start);
2139+
tok->insertTokenBefore(matchedScope.substr(start, separator == std::string::npos ? separator : separator - start));
2140+
tok->insertTokenBefore("::");
2141+
if (separator == std::string::npos)
2142+
break;
2143+
start = separator + 4;
2144+
}
2145+
}
2146+
}
19442147
continue;
19452148
}
19462149
}

0 commit comments

Comments
 (0)