Skip to content

Commit d144601

Browse files
committed
DR458: Search template parameter scopes in the right order.
C++ unqualified name lookup searches template parameter scopes immediately after finishing searching the entity the parameters belong to. (Eg, for a class template, you search the template parameter scope after looking in that class template and its base classes and before looking in the scope containing the class template.) This is complicated by the fact that scope lookup within a template parameter scope looks in a different sequence of places prior to reaching the end of the declarator-id in the template declaration. We used to approximate the proper lookup rule with a hack in the scope / decl context walk inside name lookup. Now we instead compute the lookup parent for each template parameter scope. In order to get this right, we now make sure to enter a distinct Scope for each template parameter scope, and make sure to re-enter the enclosing class scopes properly when handling delay-parsed regions within a class.
1 parent c2bb88c commit d144601

File tree

17 files changed

+409
-427
lines changed

17 files changed

+409
-427
lines changed

clang/include/clang/Parse/Parser.h

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,12 +1088,40 @@ class Parser : public CodeCompletionHandler {
10881088
}
10891089
};
10901090

1091+
/// Introduces zero or more scopes for parsing. The scopes will all be exited
1092+
/// when the object is destroyed.
1093+
class MultiParseScope {
1094+
Parser &Self;
1095+
unsigned NumScopes = 0;
1096+
1097+
MultiParseScope(const MultiParseScope&) = delete;
1098+
1099+
public:
1100+
MultiParseScope(Parser &Self) : Self(Self) {}
1101+
void Enter(unsigned ScopeFlags) {
1102+
Self.EnterScope(ScopeFlags);
1103+
++NumScopes;
1104+
}
1105+
void Exit() {
1106+
while (NumScopes) {
1107+
Self.ExitScope();
1108+
--NumScopes;
1109+
}
1110+
}
1111+
~MultiParseScope() {
1112+
Exit();
1113+
}
1114+
};
1115+
10911116
/// EnterScope - Start a new scope.
10921117
void EnterScope(unsigned ScopeFlags);
10931118

10941119
/// ExitScope - Pop a scope off the scope stack.
10951120
void ExitScope();
10961121

1122+
/// Re-enter the template scopes for a declaration that might be a template.
1123+
unsigned ReenterTemplateScopes(MultiParseScope &S, Decl *D);
1124+
10971125
private:
10981126
/// RAII object used to modify the scope flags for the current scope.
10991127
class ParseScopeFlags {
@@ -1278,13 +1306,7 @@ class Parser : public CodeCompletionHandler {
12781306
Decl *D;
12791307
CachedTokens Toks;
12801308

1281-
/// Whether this member function had an associated template
1282-
/// scope. When true, D is a template declaration.
1283-
/// otherwise, it is a member function declaration.
1284-
bool TemplateScope;
1285-
1286-
explicit LexedMethod(Parser* P, Decl *MD)
1287-
: Self(P), D(MD), TemplateScope(false) {}
1309+
explicit LexedMethod(Parser *P, Decl *MD) : Self(P), D(MD) {}
12881310

12891311
void ParseLexedMethodDefs() override;
12901312
};
@@ -1314,8 +1336,7 @@ class Parser : public CodeCompletionHandler {
13141336
/// argument (C++ [class.mem]p2).
13151337
struct LateParsedMethodDeclaration : public LateParsedDeclaration {
13161338
explicit LateParsedMethodDeclaration(Parser *P, Decl *M)
1317-
: Self(P), Method(M), TemplateScope(false),
1318-
ExceptionSpecTokens(nullptr) {}
1339+
: Self(P), Method(M), ExceptionSpecTokens(nullptr) {}
13191340

13201341
void ParseLexedMethodDeclarations() override;
13211342

@@ -1324,11 +1345,6 @@ class Parser : public CodeCompletionHandler {
13241345
/// Method - The method declaration.
13251346
Decl *Method;
13261347

1327-
/// Whether this member function had an associated template
1328-
/// scope. When true, D is a template declaration.
1329-
/// otherwise, it is a member function declaration.
1330-
bool TemplateScope;
1331-
13321348
/// DefaultArgs - Contains the parameters of the function and
13331349
/// their default arguments. At least one of the parameters will
13341350
/// have a default argument, but all of the parameters of the
@@ -1373,18 +1389,13 @@ class Parser : public CodeCompletionHandler {
13731389
/// parsed after the corresponding top-level class is complete.
13741390
struct ParsingClass {
13751391
ParsingClass(Decl *TagOrTemplate, bool TopLevelClass, bool IsInterface)
1376-
: TopLevelClass(TopLevelClass), TemplateScope(false),
1377-
IsInterface(IsInterface), TagOrTemplate(TagOrTemplate) { }
1392+
: TopLevelClass(TopLevelClass), IsInterface(IsInterface),
1393+
TagOrTemplate(TagOrTemplate) {}
13781394

13791395
/// Whether this is a "top-level" class, meaning that it is
13801396
/// not nested within another class.
13811397
bool TopLevelClass : 1;
13821398

1383-
/// Whether this class had an associated template
1384-
/// scope. When true, TagOrTemplate is a template declaration;
1385-
/// otherwise, it is a tag declaration.
1386-
bool TemplateScope : 1;
1387-
13881399
/// Whether this class is an __interface.
13891400
bool IsInterface : 1;
13901401

@@ -1483,6 +1494,10 @@ class Parser : public CodeCompletionHandler {
14831494
SourceRange getSourceRange() const LLVM_READONLY;
14841495
};
14851496

1497+
// In ParseCXXInlineMethods.cpp.
1498+
struct ReenterTemplateScopeRAII;
1499+
struct ReenterClassScopeRAII;
1500+
14861501
void LexTemplateFunctionForLateParsing(CachedTokens &Toks);
14871502
void ParseLateTemplatedFuncDef(LateParsedTemplate &LPT);
14881503

@@ -3240,7 +3255,7 @@ class Parser : public CodeCompletionHandler {
32403255
DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
32413256
ParsingDeclRAIIObject &DiagsFromParams, SourceLocation &DeclEnd,
32423257
ParsedAttributes &AccessAttrs, AccessSpecifier AS = AS_none);
3243-
bool ParseTemplateParameters(unsigned Depth,
3258+
bool ParseTemplateParameters(MultiParseScope &TemplateScopes, unsigned Depth,
32443259
SmallVectorImpl<NamedDecl *> &TemplateParams,
32453260
SourceLocation &LAngleLoc,
32463261
SourceLocation &RAngleLoc);

clang/include/clang/Sema/Scope.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,21 @@ class Scope {
322322
/// declared in.
323323
bool isDeclScope(const Decl *D) const { return DeclsInScope.count(D) != 0; }
324324

325-
DeclContext *getEntity() const { return Entity; }
326-
void setEntity(DeclContext *E) { Entity = E; }
325+
/// Get the entity corresponding to this scope.
326+
DeclContext *getEntity() const {
327+
return isTemplateParamScope() ? nullptr : Entity;
328+
}
329+
330+
/// Get the DeclContext in which to continue unqualified lookup after a
331+
/// lookup in this scope.
332+
DeclContext *getLookupEntity() const { return Entity; }
333+
334+
void setEntity(DeclContext *E) {
335+
assert(!isTemplateParamScope() &&
336+
"entity associated with template param scope");
337+
Entity = E;
338+
}
339+
void setLookupEntity(DeclContext *E) { Entity = E; }
327340

328341
/// Determine whether any unrecoverable errors have occurred within this
329342
/// scope. Note that this may return false even if the scope contains invalid

clang/include/clang/Sema/Sema.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,8 +2866,6 @@ class Sema final {
28662866
Decl *EnumDecl, ArrayRef<Decl *> Elements, Scope *S,
28672867
const ParsedAttributesView &Attr);
28682868

2869-
DeclContext *getContainingDC(DeclContext *DC);
2870-
28712869
/// Set the current declaration context until it gets popped.
28722870
void PushDeclContext(Scope *S, DeclContext *DC);
28732871
void PopDeclContext();
@@ -2877,6 +2875,11 @@ class Sema final {
28772875
void EnterDeclaratorContext(Scope *S, DeclContext *DC);
28782876
void ExitDeclaratorContext(Scope *S);
28792877

2878+
/// Enter a template parameter scope, after it's been associated with a particular
2879+
/// DeclContext. Causes lookup within the scope to chain through enclosing contexts
2880+
/// in the correct order.
2881+
void EnterTemplatedContext(Scope *S, DeclContext *DC);
2882+
28802883
/// Push the parameters of D, which must be a function, into scope.
28812884
void ActOnReenterFunctionContext(Scope* S, Decl* D);
28822885
void ActOnExitFunctionContext();
@@ -6789,7 +6792,8 @@ class Sema final {
67896792
void ActOnFinishCXXNonNestedClass();
67906793

67916794
void ActOnReenterCXXMethodParameter(Scope *S, ParmVarDecl *Param);
6792-
unsigned ActOnReenterTemplateScope(Scope *S, Decl *Template);
6795+
unsigned ActOnReenterTemplateScope(Decl *Template,
6796+
llvm::function_ref<Scope *()> EnterScope);
67936797
void ActOnStartDelayedMemberDeclarations(Scope *S, Decl *Record);
67946798
void ActOnStartDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
67956799
void ActOnDelayedCXXMethodParameter(Scope *S, Decl *Param);

clang/lib/AST/DeclBase.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ const TemplateParameterList *Decl::getDescribedTemplateParams() const {
251251
}
252252

253253
bool Decl::isTemplated() const {
254-
// A declaration is dependent if it is a template or a template pattern, or
254+
// A declaration is templated if it is a template or a template pattern, or
255255
// is within (lexcially for a friend, semantically otherwise) a dependent
256256
// context.
257257
// FIXME: Should local extern declarations be treated like friends?

0 commit comments

Comments
 (0)