Skip to content

Commit de77bc6

Browse files
author
Your Name
committed
Handle late class members
1 parent b60e55e commit de77bc6

2 files changed

Lines changed: 109 additions & 14 deletions

File tree

lib/templatesimplifier.cpp

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,7 +1308,13 @@ namespace {
13081308
}
13091309

13101310
void addFunction(const std::string &name, const ParsedType &returnType) {
1311-
mScopes.back().members.functions[name].push_back(returnType);
1311+
std::vector<ParsedType> &declarations = mScopes.back().members.functions[name];
1312+
// the walk can record a declaration twice (class members are
1313+
// recorded up front and again when the body is walked)
1314+
if (std::none_of(declarations.cbegin(), declarations.cend(), [&](const ParsedType &declaration) {
1315+
return sameDeducedType(declaration.type, returnType.type);
1316+
}))
1317+
declarations.push_back(returnType);
13121318
}
13131319

13141320
bool lookupVariable(const std::string &name, ParsedType &result) const {
@@ -1959,6 +1965,46 @@ static void deduceTemplateArgumentsAtFunctionCall(const DeductionContext &ctx, T
19591965
insertPos->insertToken(">");
19601966
}
19611967

1968+
// Record |tok| in the symbol table when it is the name of a variable or
1969+
// function declaration.
1970+
static void recordDeclaration(const DeductionContext &ctx, DeductionSymbolTable &symbols, const Token *tok)
1971+
{
1972+
ParsedType parsed;
1973+
if (parseVariableDeclaration(ctx, tok, parsed)) {
1974+
symbols.addVariable(tok->str(), parsed);
1975+
} else if (Token::simpleMatch(tok->next(), "(") && tok->next()->link() &&
1976+
!Token::Match(tok->previous(), ".|::|->|new")) {
1977+
// function declaration: record the return type
1978+
const Token *rpar = tok->next()->link();
1979+
if (Token::Match(rpar->next(), ";|{|const|noexcept")) {
1980+
const Token *start = findDeclarationTypeStart(ctx, tok);
1981+
if (start && parseType(ctx, start, tok, parsed) == tok && parsed.type.valid)
1982+
symbols.addFunction(tok->str(), parsed);
1983+
}
1984+
}
1985+
}
1986+
1987+
// Record the member declarations of the class body starting at |bodyStart|
1988+
// before it is walked: in a complete-class context (member function bodies,
1989+
// default arguments, constructor initializer lists) the members that are
1990+
// declared later in the class are visible too. The scan is shallow - nested
1991+
// bodies contain locals and nested class members, not members of this class.
1992+
static void recordClassMembers(const DeductionContext &ctx, DeductionSymbolTable &symbols, const Token *bodyStart)
1993+
{
1994+
const Token *const bodyEnd = bodyStart->link();
1995+
for (const Token *tok = bodyStart->next(); tok && tok != bodyEnd; tok = tok->next()) {
1996+
if (tok->link() && Token::Match(tok, "(|{|["))
1997+
tok = tok->link();
1998+
else if (Token::simpleMatch(tok, "template <")) {
1999+
const Token *closing = tok->next()->findClosingBracket();
2000+
if (!closing)
2001+
return;
2002+
tok = closing;
2003+
} else if (tok->isName())
2004+
recordDeclaration(ctx, symbols, tok);
2005+
}
2006+
}
2007+
19622008
void TemplateSimplifier::deduceFunctionTemplateArguments()
19632009
{
19642010
// map from name to the function template declarations a call could instantiate
@@ -1999,6 +2045,9 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
19992045
symbols.leaveScopesEndingAt(tok);
20002046
else if (tok == classBodyStart) {
20012047
symbols.enterClassScope(tok->link(), std::move(className));
2048+
// members declared later in the class are visible in the
2049+
// member function bodies: record them all up front
2050+
recordClassMembers(ctx, symbols, tok);
20022051
classBodyStart = nullptr;
20032052
} else if (Token::Match(tok, "(|{"))
20042053
symbols.enterScope(tok->link());
@@ -2149,19 +2198,7 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
21492198
}
21502199

21512200
// record variable and function declarations
2152-
ParsedType parsed;
2153-
if (parseVariableDeclaration(ctx, tok, parsed)) {
2154-
symbols.addVariable(tok->str(), parsed);
2155-
} else if (Token::simpleMatch(tok->next(), "(") && tok->next()->link() &&
2156-
!Token::Match(tok->previous(), ".|::|->|new")) {
2157-
// function declaration: record the return type
2158-
const Token *rpar = tok->next()->link();
2159-
if (Token::Match(rpar->next(), ";|{|const|noexcept")) {
2160-
const Token *start = findDeclarationTypeStart(ctx, tok);
2161-
if (start && parseType(ctx, start, tok, parsed) == tok && parsed.type.valid)
2162-
symbols.addFunction(tok->str(), parsed);
2163-
}
2164-
}
2201+
recordDeclaration(ctx, symbols, tok);
21652202
}
21662203
}
21672204

test/testsimplifytemplate.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ class TestSimplifyTemplate : public TestFixture {
291291
TEST_CASE(templateTypeDeduction10); // parameter visibility: init list, const method
292292
TEST_CASE(templateTypeDeduction11); // unqualified lookup: enclosing scopes, shadowing
293293
TEST_CASE(templateTypeDeduction12); // base class member templates, out of class method bodies
294+
TEST_CASE(templateTypeDeduction13); // members declared after the member function are visible
294295

295296
TEST_CASE(simplifyTemplateArgs1);
296297
TEST_CASE(simplifyTemplateArgs2);
@@ -6698,6 +6699,63 @@ class TestSimplifyTemplate : public TestFixture {
66986699
}
66996700
}
67006701

6702+
void templateTypeDeduction13() { // members declared after the member function are visible
6703+
{
6704+
const char code[] = "struct A {\n"
6705+
" template<class T> void tf(T t) { (void)t; }\n"
6706+
" void m() { tf(later); tf(getl()); }\n"
6707+
" int later;\n"
6708+
" long getl();\n"
6709+
"};";
6710+
const char exp[] = "struct A { "
6711+
"void tf<int> ( int t ) ; "
6712+
"void tf<long> ( long t ) ; "
6713+
"void m ( ) { tf<int> ( later ) ; tf<long> ( getl ( ) ) ; } "
6714+
"int later ; "
6715+
"long getl ( ) ; "
6716+
"} ; "
6717+
"void A :: tf<int> ( int t ) { ( void ) t ; } "
6718+
"void A :: tf<long> ( long t ) { ( void ) t ; }";
6719+
ASSERT_EQUALS(exp, tok(code));
6720+
}
6721+
{
6722+
// constructor body; the member template is also declared later
6723+
const char code[] = "struct B {\n"
6724+
" B() : v(1.0f) { tf(later); }\n"
6725+
" template<class T> void tf(T t) { (void)t; }\n"
6726+
" float v;\n"
6727+
" double later;\n"
6728+
"};";
6729+
const char exp[] = "struct B { "
6730+
"B ( ) : v ( 1.0f ) { tf<double> ( later ) ; } "
6731+
"void tf<double> ( double t ) ; "
6732+
"float v ; "
6733+
"double later ; "
6734+
"} ; "
6735+
"void B :: tf<double> ( double t ) { ( void ) t ; }";
6736+
ASSERT_EQUALS(exp, tok(code));
6737+
}
6738+
{
6739+
// a parameter shadows a member that is declared later
6740+
const char code[] = "struct C {\n"
6741+
" template<class T> void tf(T t) { (void)t; }\n"
6742+
" void n(short x) { tf(x); }\n"
6743+
" void o() { tf(x); }\n"
6744+
" long x;\n"
6745+
"};";
6746+
const char exp[] = "struct C { "
6747+
"void tf<short> ( short t ) ; "
6748+
"void tf<long> ( long t ) ; "
6749+
"void n ( short x ) { tf<short> ( x ) ; } "
6750+
"void o ( ) { tf<long> ( x ) ; } "
6751+
"long x ; "
6752+
"} ; "
6753+
"void C :: tf<short> ( short t ) { ( void ) t ; } "
6754+
"void C :: tf<long> ( long t ) { ( void ) t ; }";
6755+
ASSERT_EQUALS(exp, tok(code));
6756+
}
6757+
}
6758+
67016759
void simplifyTemplateArgs1() {
67026760
ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template<int N> foo = N; foo < ( 2 ) >;"));
67036761
ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template<int N> foo = N; foo < 1 + 1 >;"));

0 commit comments

Comments
 (0)