}
};
+ /// Introduces zero or more scopes for parsing. The scopes will all be exited
+ /// when the object is destroyed.
+ class MultiParseScope {
+ Parser &Self;
+ unsigned NumScopes = 0;
+
+ MultiParseScope(const MultiParseScope&) = delete;
+
+ public:
+ MultiParseScope(Parser &Self) : Self(Self) {}
+ void Enter(unsigned ScopeFlags) {
+ Self.EnterScope(ScopeFlags);
+ ++NumScopes;
+ }
+ void Exit() {
+ while (NumScopes) {
+ Self.ExitScope();
+ --NumScopes;
+ }
+ }
+ ~MultiParseScope() {
+ Exit();
+ }
+ };
+
/// EnterScope - Start a new scope.
void EnterScope(unsigned ScopeFlags);
/// ExitScope - Pop a scope off the scope stack.
void ExitScope();
+ /// Re-enter the template scopes for a declaration that might be a template.
+ unsigned ReenterTemplateScopes(MultiParseScope &S, Decl *D);
+
private:
/// RAII object used to modify the scope flags for the current scope.
class ParseScopeFlags {
Decl *D;
CachedTokens Toks;
- /// Whether this member function had an associated template
- /// scope. When true, D is a template declaration.
- /// otherwise, it is a member function declaration.
- bool TemplateScope;
-
- explicit LexedMethod(Parser* P, Decl *MD)
- : Self(P), D(MD), TemplateScope(false) {}
+ explicit LexedMethod(Parser *P, Decl *MD) : Self(P), D(MD) {}
void ParseLexedMethodDefs() override;
};
/// argument (C++ [class.mem]p2).
struct LateParsedMethodDeclaration : public LateParsedDeclaration {
explicit LateParsedMethodDeclaration(Parser *P, Decl *M)
- : Self(P), Method(M), TemplateScope(false),
- ExceptionSpecTokens(nullptr) {}
+ : Self(P), Method(M), ExceptionSpecTokens(nullptr) {}
void ParseLexedMethodDeclarations() override;
/// Method - The method declaration.
Decl *Method;
- /// Whether this member function had an associated template
- /// scope. When true, D is a template declaration.
- /// otherwise, it is a member function declaration.
- bool TemplateScope;
-
/// DefaultArgs - Contains the parameters of the function and
/// their default arguments. At least one of the parameters will
/// have a default argument, but all of the parameters of the
/// parsed after the corresponding top-level class is complete.
struct ParsingClass {
ParsingClass(Decl *TagOrTemplate, bool TopLevelClass, bool IsInterface)
- : TopLevelClass(TopLevelClass), TemplateScope(false),
- IsInterface(IsInterface), TagOrTemplate(TagOrTemplate) { }
+ : TopLevelClass(TopLevelClass), IsInterface(IsInterface),
+ TagOrTemplate(TagOrTemplate) {}
/// Whether this is a "top-level" class, meaning that it is
/// not nested within another class.
bool TopLevelClass : 1;
- /// Whether this class had an associated template
- /// scope. When true, TagOrTemplate is a template declaration;
- /// otherwise, it is a tag declaration.
- bool TemplateScope : 1;
-
/// Whether this class is an __interface.
bool IsInterface : 1;
SourceRange getSourceRange() const LLVM_READONLY;
};
+ // In ParseCXXInlineMethods.cpp.
+ struct ReenterTemplateScopeRAII;
+ struct ReenterClassScopeRAII;
+
void LexTemplateFunctionForLateParsing(CachedTokens &Toks);
void ParseLateTemplatedFuncDef(LateParsedTemplate &LPT);
DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
ParsingDeclRAIIObject &DiagsFromParams, SourceLocation &DeclEnd,
ParsedAttributes &AccessAttrs, AccessSpecifier AS = AS_none);
- bool ParseTemplateParameters(unsigned Depth,
+ bool ParseTemplateParameters(MultiParseScope &TemplateScopes, unsigned Depth,
SmallVectorImpl<NamedDecl *> &TemplateParams,
SourceLocation &LAngleLoc,
SourceLocation &RAngleLoc);
/// declared in.
bool isDeclScope(const Decl *D) const { return DeclsInScope.count(D) != 0; }
- DeclContext *getEntity() const { return Entity; }
- void setEntity(DeclContext *E) { Entity = E; }
+ /// Get the entity corresponding to this scope.
+ DeclContext *getEntity() const {
+ return isTemplateParamScope() ? nullptr : Entity;
+ }
+
+ /// Get the DeclContext in which to continue unqualified lookup after a
+ /// lookup in this scope.
+ DeclContext *getLookupEntity() const { return Entity; }
+
+ void setEntity(DeclContext *E) {
+ assert(!isTemplateParamScope() &&
+ "entity associated with template param scope");
+ Entity = E;
+ }
+ void setLookupEntity(DeclContext *E) { Entity = E; }
/// Determine whether any unrecoverable errors have occurred within this
/// scope. Note that this may return false even if the scope contains invalid
Decl *EnumDecl, ArrayRef<Decl *> Elements, Scope *S,
const ParsedAttributesView &Attr);
- DeclContext *getContainingDC(DeclContext *DC);
-
/// Set the current declaration context until it gets popped.
void PushDeclContext(Scope *S, DeclContext *DC);
void PopDeclContext();
void EnterDeclaratorContext(Scope *S, DeclContext *DC);
void ExitDeclaratorContext(Scope *S);
+ /// Enter a template parameter scope, after it's been associated with a particular
+ /// DeclContext. Causes lookup within the scope to chain through enclosing contexts
+ /// in the correct order.
+ void EnterTemplatedContext(Scope *S, DeclContext *DC);
+
/// Push the parameters of D, which must be a function, into scope.
void ActOnReenterFunctionContext(Scope* S, Decl* D);
void ActOnExitFunctionContext();
void ActOnFinishCXXNonNestedClass();
void ActOnReenterCXXMethodParameter(Scope *S, ParmVarDecl *Param);
- unsigned ActOnReenterTemplateScope(Scope *S, Decl *Template);
+ unsigned ActOnReenterTemplateScope(Decl *Template,
+ llvm::function_ref<Scope *()> EnterScope);
void ActOnStartDelayedMemberDeclarations(Scope *S, Decl *Record);
void ActOnStartDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
void ActOnDelayedCXXMethodParameter(Scope *S, Decl *Param);
}
bool Decl::isTemplated() const {
- // A declaration is dependent if it is a template or a template pattern, or
+ // A declaration is templated if it is a template or a template pattern, or
// is within (lexcially for a friend, semantically otherwise) a dependent
// context.
// FIXME: Should local extern declarations be treated like friends?
LexedMethod* LM = new LexedMethod(this, FnD);
getCurrentClass().LateParsedDeclarations.push_back(LM);
- LM->TemplateScope = getCurScope()->isTemplateParamScope() ||
- (FnD && isa<FunctionTemplateDecl>(FnD) &&
- cast<FunctionTemplateDecl>(FnD)->isAbbreviated());
CachedTokens &Toks = LM->Toks;
tok::TokenKind kind = Tok.getKind();
Self->ParseLexedPragma(*this);
}
+/// Utility to re-enter a possibly-templated scope while parsing its
+/// late-parsed components.
+struct Parser::ReenterTemplateScopeRAII {
+ Parser &P;
+ MultiParseScope Scopes;
+ TemplateParameterDepthRAII CurTemplateDepthTracker;
+
+ ReenterTemplateScopeRAII(Parser &P, Decl *MaybeTemplated, bool Enter = true)
+ : P(P), Scopes(P), CurTemplateDepthTracker(P.TemplateParameterDepth) {
+ if (Enter) {
+ CurTemplateDepthTracker.addDepth(
+ P.ReenterTemplateScopes(Scopes, MaybeTemplated));
+ }
+ }
+};
+
+/// Utility to re-enter a class scope while parsing its late-parsed components.
+struct Parser::ReenterClassScopeRAII : ReenterTemplateScopeRAII {
+ ParsingClass &Class;
+
+ ReenterClassScopeRAII(Parser &P, ParsingClass &Class)
+ : ReenterTemplateScopeRAII(P, Class.TagOrTemplate,
+ /*Enter=*/!Class.TopLevelClass),
+ Class(Class) {
+ // If this is the top-level class, we're still within its scope.
+ if (Class.TopLevelClass)
+ return;
+
+ // Re-enter the class scope itself.
+ Scopes.Enter(Scope::ClassScope|Scope::DeclScope);
+ P.Actions.ActOnStartDelayedMemberDeclarations(P.getCurScope(),
+ Class.TagOrTemplate);
+ }
+ ~ReenterClassScopeRAII() {
+ if (Class.TopLevelClass)
+ return;
+
+ P.Actions.ActOnFinishDelayedMemberDeclarations(P.getCurScope(),
+ Class.TagOrTemplate);
+ }
+};
+
/// ParseLexedMethodDeclarations - We finished parsing the member
/// specification of a top (non-nested) C++ class. Now go over the
/// stack of method declarations with some parts for which parsing was
/// delayed (such as default arguments) and parse them.
void Parser::ParseLexedMethodDeclarations(ParsingClass &Class) {
- bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
- ParseScope ClassTemplateScope(this, Scope::TemplateParamScope,
- HasTemplateScope);
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
- if (HasTemplateScope) {
- Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
- ++CurTemplateDepthTracker;
- }
+ ReenterClassScopeRAII InClassScope(*this, Class);
- // The current scope is still active if we're the top-level class.
- // Otherwise we'll need to push and enter a new scope.
- bool HasClassScope = !Class.TopLevelClass;
- ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope,
- HasClassScope);
- if (HasClassScope)
- Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
- Class.TagOrTemplate);
-
- for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) {
- Class.LateParsedDeclarations[i]->ParseLexedMethodDeclarations();
- }
-
- if (HasClassScope)
- Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(),
- Class.TagOrTemplate);
+ for (LateParsedDeclaration *LateD : Class.LateParsedDeclarations)
+ LateD->ParseLexedMethodDeclarations();
}
void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
// If this is a member template, introduce the template parameter scope.
- ParseScope TemplateScope(this, Scope::TemplateParamScope, LM.TemplateScope);
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
- if (LM.TemplateScope) {
- Actions.ActOnReenterTemplateScope(getCurScope(), LM.Method);
- ++CurTemplateDepthTracker;
- }
+ ReenterTemplateScopeRAII InFunctionTemplateScope(*this, LM.Method);
+
// Start the delayed C++ method declaration
Actions.ActOnStartDelayedCXXMethodDeclaration(getCurScope(), LM.Method);
// Introduce the parameters into scope and parse their default
// arguments.
- ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
- Scope::FunctionDeclarationScope | Scope::DeclScope);
+ InFunctionTemplateScope.Scopes.Enter(Scope::FunctionPrototypeScope |
+ Scope::FunctionDeclarationScope |
+ Scope::DeclScope);
for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) {
auto Param = cast<ParmVarDecl>(LM.DefaultArgs[I].Param);
// Introduce the parameter into scope.
LM.ExceptionSpecTokens = nullptr;
}
- PrototypeScope.Exit();
+ InFunctionTemplateScope.Scopes.Exit();
// Finish the delayed C++ method declaration.
Actions.ActOnFinishDelayedCXXMethodDeclaration(getCurScope(), LM.Method);
/// (non-nested) C++ class. Now go over the stack of lexed methods that were
/// collected during its parsing and parse them all.
void Parser::ParseLexedMethodDefs(ParsingClass &Class) {
- bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
- ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, HasTemplateScope);
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
- if (HasTemplateScope) {
- Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
- ++CurTemplateDepthTracker;
- }
- bool HasClassScope = !Class.TopLevelClass;
- ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope,
- HasClassScope);
+ ReenterClassScopeRAII InClassScope(*this, Class);
- for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) {
- Class.LateParsedDeclarations[i]->ParseLexedMethodDefs();
- }
+ for (LateParsedDeclaration *D : Class.LateParsedDeclarations)
+ D->ParseLexedMethodDefs();
}
void Parser::ParseLexedMethodDef(LexedMethod &LM) {
// If this is a member template, introduce the template parameter scope.
- ParseScope TemplateScope(this, Scope::TemplateParamScope, LM.TemplateScope);
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
- if (LM.TemplateScope) {
- Actions.ActOnReenterTemplateScope(getCurScope(), LM.D);
- ++CurTemplateDepthTracker;
- }
+ ReenterTemplateScopeRAII InFunctionTemplateScope(*this, LM.D);
ParenBraceBracketBalancer BalancerRAIIObj(*this);
/// of a top (non-nested) C++ class. Now go over the stack of lexed data member
/// initializers that were collected during its parsing and parse them all.
void Parser::ParseLexedMemberInitializers(ParsingClass &Class) {
- bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
- ParseScope ClassTemplateScope(this, Scope::TemplateParamScope,
- HasTemplateScope);
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
- if (HasTemplateScope) {
- Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
- ++CurTemplateDepthTracker;
- }
- // Set or update the scope flags.
- bool AlreadyHasClassScope = Class.TopLevelClass;
- unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope;
- ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope);
- ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope);
-
- if (!AlreadyHasClassScope)
- Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
- Class.TagOrTemplate);
+ ReenterClassScopeRAII InClassScope(*this, Class);
if (!Class.LateParsedDeclarations.empty()) {
// C++11 [expr.prim.general]p4:
// (9.2) of a class X, the expression this is a prvalue of type "pointer
// to X" within the optional brace-or-equal-initializer. It shall not
// appear elsewhere in the member-declarator.
+ // FIXME: This should be done in ParseLexedMemberInitializer, not here.
Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate,
Qualifiers());
- for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) {
- Class.LateParsedDeclarations[i]->ParseLexedMemberInitializers();
- }
+ for (LateParsedDeclaration *D : Class.LateParsedDeclarations)
+ D->ParseLexedMemberInitializers();
}
- if (!AlreadyHasClassScope)
- Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(),
- Class.TagOrTemplate);
-
Actions.ActOnFinishDelayedMemberInitializers(Class.TagOrTemplate);
}
/// Wrapper class which calls ParseLexedAttribute, after setting up the
/// scope appropriately.
void Parser::ParseLexedAttributes(ParsingClass &Class) {
- // Deal with templates
- // FIXME: Test cases to make sure this does the right thing for templates.
- bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
- ParseScope ClassTemplateScope(this, Scope::TemplateParamScope,
- HasTemplateScope);
- if (HasTemplateScope)
- Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
-
- // Set or update the scope flags.
- bool AlreadyHasClassScope = Class.TopLevelClass;
- unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope;
- ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope);
- ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope);
-
- // Enter the scope of nested classes
- if (!AlreadyHasClassScope)
- Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
- Class.TagOrTemplate);
- if (!Class.LateParsedDeclarations.empty()) {
- for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i){
- Class.LateParsedDeclarations[i]->ParseLexedAttributes();
- }
- }
+ ReenterClassScopeRAII InClassScope(*this, Class);
- if (!AlreadyHasClassScope)
- Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(),
- Class.TagOrTemplate);
+ for (LateParsedDeclaration *LateD : Class.LateParsedDeclarations)
+ LateD->ParseLexedAttributes();
}
/// Parse all attributes in LAs, and attach them to Decl D.
if (LA.Decls.size() == 1) {
// If the Decl is templatized, add template parameters to scope.
- bool HasTemplateScope = EnterScope && D->isTemplateDecl();
- ParseScope TempScope(this, Scope::TemplateParamScope, HasTemplateScope);
- if (HasTemplateScope)
- Actions.ActOnReenterTemplateScope(Actions.CurScope, D);
+ ReenterTemplateScopeRAII InDeclScope(*this, D, EnterScope);
// If the Decl is on a function, add function parameters to the scope.
bool HasFunScope = EnterScope && D->isFunctionOrFunctionTemplate();
- ParseScope FnScope(
- this, Scope::FnScope | Scope::DeclScope | Scope::CompoundStmtScope,
- HasFunScope);
- if (HasFunScope)
+ if (HasFunScope) {
+ InDeclScope.Scopes.Enter(Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
Actions.ActOnReenterFunctionContext(Actions.CurScope, D);
+ }
ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc,
nullptr, SourceLocation(), ParsedAttr::AS_GNU,
nullptr);
- if (HasFunScope) {
+ if (HasFunScope)
Actions.ActOnExitFunctionContext();
- FnScope.Exit(); // Pop scope, and remove Decls from IdResolver
- }
- if (HasTemplateScope) {
- TempScope.Exit();
- }
} else {
// If there are multiple decls, then the decl cannot be within the
// function scope.
}
void Parser::ParseLexedPragmas(ParsingClass &Class) {
- bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
- ParseScope ClassTemplateScope(this, Scope::TemplateParamScope,
- HasTemplateScope);
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
- if (HasTemplateScope) {
- Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
- ++CurTemplateDepthTracker;
- }
- bool HasClassScope = !Class.TopLevelClass;
- ParseScope ClassScope(this, Scope::ClassScope | Scope::DeclScope,
- HasClassScope);
+ ReenterClassScopeRAII InClassScope(*this, Class);
- for (LateParsedDeclaration *LPD : Class.LateParsedDeclarations)
- LPD->ParseLexedPragmas();
+ for (LateParsedDeclaration *D : Class.LateParsedDeclarations)
+ D->ParseLexedPragmas();
}
void Parser::ParseLexedPragma(LateParsedPragma &LP) {
// declarations.
auto LateMethod = new LateParsedMethodDeclaration(this, ThisDecl);
getCurrentClass().LateParsedDeclarations.push_back(LateMethod);
- LateMethod->TemplateScope = getCurScope()->isTemplateParamScope();
// Stash the exception-specification tokens in the late-pased method.
LateMethod->ExceptionSpecTokens = FTI.ExceptionSpecTokens;
// after the top-level class is completely defined. Therefore, add
// it to the list of nested classes within its parent.
assert(getCurScope()->isClassScope() && "Nested class outside of class scope?");
- ClassStack.top()->LateParsedDeclarations.push_back(new LateParsedClass(this, Victim));
- Victim->TemplateScope = getCurScope()->getParent()->isTemplateParamScope();
+ ClassStack.top()->LateParsedDeclarations.push_back(
+ new LateParsedClass(this, Victim));
}
/// Try to parse an 'identifier' which appears within an attribute-token.
};
// FIXME: Consider allowing this as an extension for GCC compatibiblity.
- const bool HasExplicitTemplateParams = Tok.is(tok::less);
- ParseScope TemplateParamScope(this, Scope::TemplateParamScope,
- /*EnteredScope=*/HasExplicitTemplateParams);
- if (HasExplicitTemplateParams) {
+ MultiParseScope TemplateParamScope(*this);
+ if (Tok.is(tok::less)) {
Diag(Tok, getLangOpts().CPlusPlus20
? diag::warn_cxx17_compat_lambda_template_parameter_list
: diag::ext_lambda_template_parameter_list);
SmallVector<NamedDecl*, 4> TemplateParams;
SourceLocation LAngleLoc, RAngleLoc;
- if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
+ if (ParseTemplateParameters(TemplateParamScope,
+ CurTemplateDepthTracker.getDepth(),
TemplateParams, LAngleLoc, RAngleLoc)) {
Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
return ExprError();
class FNContextRAII final {
Parser &P;
Sema::CXXThisScopeRAII *ThisScope;
- Parser::ParseScope *TempScope;
- Parser::ParseScope *FnScope;
- bool HasTemplateScope = false;
+ Parser::MultiParseScope Scopes;
bool HasFunScope = false;
FNContextRAII() = delete;
FNContextRAII(const FNContextRAII &) = delete;
FNContextRAII &operator=(const FNContextRAII &) = delete;
public:
- FNContextRAII(Parser &P, Parser::DeclGroupPtrTy Ptr) : P(P) {
+ FNContextRAII(Parser &P, Parser::DeclGroupPtrTy Ptr) : P(P), Scopes(P) {
Decl *D = *Ptr.get().begin();
NamedDecl *ND = dyn_cast<NamedDecl>(D);
RecordDecl *RD = dyn_cast_or_null<RecordDecl>(D->getDeclContext());
ND && ND->isCXXInstanceMember());
// If the Decl is templatized, add template parameters to scope.
- HasTemplateScope = D->isTemplateDecl();
- TempScope =
- new Parser::ParseScope(&P, Scope::TemplateParamScope, HasTemplateScope);
- if (HasTemplateScope)
- Actions.ActOnReenterTemplateScope(Actions.getCurScope(), D);
+ // FIXME: Track CurTemplateDepth?
+ P.ReenterTemplateScopes(Scopes, D);
// If the Decl is on a function, add function parameters to the scope.
- HasFunScope = D->isFunctionOrFunctionTemplate();
- FnScope = new Parser::ParseScope(
- &P, Scope::FnScope | Scope::DeclScope | Scope::CompoundStmtScope,
- HasFunScope);
- if (HasFunScope)
+ if (D->isFunctionOrFunctionTemplate()) {
+ HasFunScope = true;
+ Scopes.Enter(Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
Actions.ActOnReenterFunctionContext(Actions.getCurScope(), D);
+ }
}
~FNContextRAII() {
- if (HasFunScope) {
+ if (HasFunScope)
P.getActions().ActOnExitFunctionContext();
- FnScope->Exit(); // Pop scope, and remove Decls from IdResolver
- }
- if (HasTemplateScope)
- TempScope->Exit();
- delete FnScope;
- delete TempScope;
delete ThisScope;
}
};
#include "llvm/Support/TimeProfiler.h"
using namespace clang;
+/// Re-enter a possible template scope, creating as many template parameter
+/// scopes as necessary.
+/// \return The number of template parameter scopes entered.
+unsigned Parser::ReenterTemplateScopes(MultiParseScope &S, Decl *D) {
+ return Actions.ActOnReenterTemplateScope(D, [&] {
+ S.Enter(Scope::TemplateParamScope);
+ return Actions.getCurScope();
+ });
+}
+
/// Parse a template declaration, explicit instantiation, or
/// explicit specialization.
Decl *Parser::ParseDeclarationStartingWithTemplate(
assert(Tok.isOneOf(tok::kw_export, tok::kw_template) &&
"Token does not start a template declaration.");
- // Enter template-parameter scope.
- ParseScope TemplateParmScope(this, Scope::TemplateParamScope);
+ MultiParseScope TemplateParamScopes(*this);
// Tell the action that names should be checked in the context of
// the declaration to come.
// Parse the '<' template-parameter-list '>'
SourceLocation LAngleLoc, RAngleLoc;
SmallVector<NamedDecl*, 4> TemplateParams;
- if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
+ if (ParseTemplateParameters(TemplateParamScopes,
+ CurTemplateDepthTracker.getDepth(),
TemplateParams, LAngleLoc, RAngleLoc)) {
// Skip until the semi-colon or a '}'.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
TemplateParams, RAngleLoc, OptionalRequiresClauseConstraintER.get()));
} while (Tok.isOneOf(tok::kw_export, tok::kw_template));
- unsigned NewFlags = getCurScope()->getFlags() & ~Scope::TemplateParamScope;
- ParseScopeFlags TemplateScopeFlags(this, NewFlags, isSpecialization);
-
// Parse the actual template declaration.
if (Tok.is(tok::kw_concept))
return ParseConceptDefinition(
///
/// \returns true if an error occurred, false otherwise.
bool Parser::ParseTemplateParameters(
- unsigned Depth, SmallVectorImpl<NamedDecl *> &TemplateParams,
- SourceLocation &LAngleLoc, SourceLocation &RAngleLoc) {
+ MultiParseScope &TemplateScopes, unsigned Depth,
+ SmallVectorImpl<NamedDecl *> &TemplateParams, SourceLocation &LAngleLoc,
+ SourceLocation &RAngleLoc) {
// Get the template parameter list.
if (!TryConsumeToken(tok::less, LAngleLoc)) {
Diag(Tok.getLocation(), diag::err_expected_less_after) << "template";
// Try to parse the template parameter list.
bool Failed = false;
- if (!Tok.is(tok::greater) && !Tok.is(tok::greatergreater))
+ // FIXME: Missing greatergreatergreater support.
+ if (!Tok.is(tok::greater) && !Tok.is(tok::greatergreater)) {
+ TemplateScopes.Enter(Scope::TemplateParamScope);
Failed = ParseTemplateParameterList(Depth, TemplateParams);
+ }
if (Tok.is(tok::greatergreater)) {
// No diagnostic required here: a template-parameter-list can only be
SmallVector<NamedDecl*,8> TemplateParams;
SourceLocation LAngleLoc, RAngleLoc;
{
- ParseScope TemplateParmScope(this, Scope::TemplateParamScope);
- if (ParseTemplateParameters(Depth + 1, TemplateParams, LAngleLoc,
- RAngleLoc)) {
+ MultiParseScope TemplateParmScope(*this);
+ if (ParseTemplateParameters(TemplateParmScope, Depth + 1, TemplateParams,
+ LAngleLoc, RAngleLoc)) {
return nullptr;
}
}
Sema::ContextRAII GlobalSavedContext(
Actions, Actions.Context.getTranslationUnitDecl());
- SmallVector<ParseScope*, 4> TemplateParamScopeStack;
-
- // Get the list of DeclContexts to reenter. For inline methods, we only want
- // to push the DeclContext of the outermost class. This matches the way the
- // parser normally parses bodies of inline methods when the outermost class is
- // complete.
- struct ContainingDC {
- ContainingDC(DeclContext *DC, bool ShouldPush) : Pair(DC, ShouldPush) {}
- llvm::PointerIntPair<DeclContext *, 1, bool> Pair;
- DeclContext *getDC() { return Pair.getPointer(); }
- bool shouldPushDC() { return Pair.getInt(); }
- };
- SmallVector<ContainingDC, 4> DeclContextsToReenter;
- DeclContext *DD = FunD;
- DeclContext *NextContaining = Actions.getContainingDC(DD);
- while (DD && !DD->isTranslationUnit()) {
- bool ShouldPush = DD == NextContaining;
- DeclContextsToReenter.push_back({DD, ShouldPush});
- if (ShouldPush)
- NextContaining = Actions.getContainingDC(DD);
- DD = DD->getLexicalParent();
- }
-
- // Reenter template scopes from outermost to innermost.
- for (ContainingDC CDC : reverse(DeclContextsToReenter)) {
- TemplateParamScopeStack.push_back(
- new ParseScope(this, Scope::TemplateParamScope));
- unsigned NumParamLists = Actions.ActOnReenterTemplateScope(
- getCurScope(), cast<Decl>(CDC.getDC()));
- CurTemplateDepthTracker.addDepth(NumParamLists);
- if (CDC.shouldPushDC()) {
- TemplateParamScopeStack.push_back(new ParseScope(this, Scope::DeclScope));
- Actions.PushDeclContext(Actions.getCurScope(), CDC.getDC());
- }
+ MultiParseScope Scopes(*this);
+
+ // Get the list of DeclContexts to reenter.
+ SmallVector<DeclContext*, 4> DeclContextsToReenter;
+ for (DeclContext *DC = FunD; DC && !DC->isTranslationUnit();
+ DC = DC->getLexicalParent())
+ DeclContextsToReenter.push_back(DC);
+
+ // Reenter scopes from outermost to innermost.
+ for (DeclContext *DC : reverse(DeclContextsToReenter)) {
+ CurTemplateDepthTracker.addDepth(
+ ReenterTemplateScopes(Scopes, cast<Decl>(DC)));
+ Scopes.Enter(Scope::DeclScope);
+ // We'll reenter the function context itself below.
+ if (DC != FunD)
+ Actions.PushDeclContext(Actions.getCurScope(), DC);
}
assert(!LPT.Toks.empty() && "Empty body!");
Scope::CompoundStmtScope);
// Recreate the containing function DeclContext.
- Sema::ContextRAII FunctionSavedContext(Actions,
- Actions.getContainingDC(FunD));
+ Sema::ContextRAII FunctionSavedContext(Actions, FunD->getLexicalParent());
Actions.ActOnStartOfFunctionDef(getCurScope(), FunD);
} else
Actions.ActOnFinishFunctionBody(LPT.D, nullptr);
}
-
- // Exit scopes.
- FnScope.Exit();
- SmallVectorImpl<ParseScope *>::reverse_iterator I =
- TemplateParamScopeStack.rbegin();
- for (; I != TemplateParamScopeStack.rend(); ++I)
- delete *I;
}
/// Lex a delayed template function for late parsing.
return TemplateNameKindForDiagnostics::DependentTemplate;
}
-// Determines the context to return to after temporarily entering a
-// context. This depends in an unnecessarily complicated way on the
-// exact ordering of callbacks from the parser.
-DeclContext *Sema::getContainingDC(DeclContext *DC) {
-
- // Functions defined inline within classes aren't parsed until we've
- // finished parsing the top-level class, so the top-level class is
- // the context we'll need to return to.
- // A Lambda call operator whose parent is a class must not be treated
- // as an inline member function. A Lambda can be used legally
- // either as an in-class member initializer or a default argument. These
- // are parsed once the class has been marked complete and so the containing
- // context would be the nested class (when the lambda is defined in one);
- // If the class is not complete, then the lambda is being used in an
- // ill-formed fashion (such as to specify the width of a bit-field, or
- // in an array-bound) - in which case we still want to return the
- // lexically containing DC (which could be a nested class).
- if (isa<FunctionDecl>(DC) && !isLambdaCallOperator(DC)) {
- DC = DC->getLexicalParent();
-
- // A function not defined within a class will always return to its
- // lexical context.
- if (!isa<CXXRecordDecl>(DC))
- return DC;
-
- // A C++ inline method/friend is parsed *after* the topmost class
- // it was declared in is fully parsed ("complete"); the topmost
- // class is the context we need to return to.
- while (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC->getLexicalParent()))
- DC = RD;
-
- // Return the declaration context of the topmost class the inline method is
- // declared in.
- return DC;
- }
-
- return DC->getLexicalParent();
-}
-
void Sema::PushDeclContext(Scope *S, DeclContext *DC) {
- assert(getContainingDC(DC) == CurContext &&
+ assert(DC->getLexicalParent() == CurContext &&
"The next DeclContext should be lexically contained in the current one.");
CurContext = DC;
S->setEntity(DC);
void Sema::PopDeclContext() {
assert(CurContext && "DeclContext imbalance!");
- CurContext = getContainingDC(CurContext);
+ CurContext = CurContext->getLexicalParent();
assert(CurContext && "Popped translation unit!");
}
CurContext = DC;
S->setEntity(DC);
+
+ if (S->getParent()->isTemplateParamScope()) {
+ // Also set the corresponding entities for all immediately-enclosing
+ // template parameter scopes.
+ EnterTemplatedContext(S->getParent(), DC);
+ }
}
void Sema::ExitDeclaratorContext(Scope *S) {
// disappear.
}
+void Sema::EnterTemplatedContext(Scope *S, DeclContext *DC) {
+ assert(S->isTemplateParamScope() &&
+ "expected to be initializing a template parameter scope");
+
+ // C++20 [temp.local]p7:
+ // In the definition of a member of a class template that appears outside
+ // of the class template definition, the name of a member of the class
+ // template hides the name of a template-parameter of any enclosing class
+ // templates (but not a template-parameter of the member if the member is a
+ // class or function template).
+ // C++20 [temp.local]p9:
+ // In the definition of a class template or in the definition of a member
+ // of such a template that appears outside of the template definition, for
+ // each non-dependent base class (13.8.2.1), if the name of the base class
+ // or the name of a member of the base class is the same as the name of a
+ // template-parameter, the base class name or member name hides the
+ // template-parameter name (6.4.10).
+ //
+ // This means that a template parameter scope should be searched immediately
+ // after searching the DeclContext for which it is a template parameter
+ // scope. For example, for
+ // template<typename T> template<typename U> template<typename V>
+ // void N::A<T>::B<U>::f(...)
+ // we search V then B<U> (and base classes) then U then A<T> (and base
+ // classes) then T then N then ::.
+ unsigned ScopeDepth = getTemplateDepth(S);
+ for (; S && S->isTemplateParamScope(); S = S->getParent(), --ScopeDepth) {
+ DeclContext *SearchDCAfterScope = DC;
+ for (; DC; DC = DC->getLookupParent()) {
+ if (const TemplateParameterList *TPL =
+ cast<Decl>(DC)->getDescribedTemplateParams()) {
+ unsigned DCDepth = TPL->getDepth() + 1;
+ if (DCDepth > ScopeDepth)
+ continue;
+ if (ScopeDepth == DCDepth)
+ SearchDCAfterScope = DC = DC->getLookupParent();
+ break;
+ }
+ }
+ S->setLookupEntity(SearchDCAfterScope);
+ }
+}
+
void Sema::ActOnReenterFunctionContext(Scope* S, Decl *D) {
// We assume that the caller has already called
// ActOnReenterTemplateScope so getTemplatedDecl() works.
assert(isa<ObjCContainerDecl>(IDecl) &&
"ActOnObjCContainerStartDefinition - Not ObjCContainerDecl");
DeclContext *OCD = cast<DeclContext>(IDecl);
- assert(getContainingDC(OCD) == CurContext &&
+ assert(OCD->getLexicalParent() == CurContext &&
"The next DeclContext should be lexically contained in the current one.");
CurContext = OCD;
return IDecl;
}
}
-unsigned Sema::ActOnReenterTemplateScope(Scope *S, Decl *D) {
+unsigned
+Sema::ActOnReenterTemplateScope(Decl *D,
+ llvm::function_ref<Scope *()> EnterScope) {
if (!D)
return 0;
+ AdjustDeclIfTemplate(D);
- // The order of template parameters is not important here. All names
- // get added to the same scope.
+ // In order to get name lookup right, reenter template scopes in order from
+ // outermost to innermost.
SmallVector<TemplateParameterList *, 4> ParameterLists;
-
- if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D))
- D = TD->getTemplatedDecl();
-
- if (auto *PSD = dyn_cast<ClassTemplatePartialSpecializationDecl>(D))
- ParameterLists.push_back(PSD->getTemplateParameters());
+ DeclContext *LookupDC = dyn_cast<DeclContext>(D);
if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) {
for (unsigned i = 0; i < DD->getNumTemplateParameterLists(); ++i)
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate())
ParameterLists.push_back(FTD->getTemplateParameters());
- }
- }
+ } else if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ LookupDC = VD->getDeclContext();
- if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
+ if (VarTemplateDecl *VTD = VD->getDescribedVarTemplate())
+ ParameterLists.push_back(VTD->getTemplateParameters());
+ else if (auto *PSD = dyn_cast<VarTemplatePartialSpecializationDecl>(D))
+ ParameterLists.push_back(PSD->getTemplateParameters());
+ }
+ } else if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
for (unsigned i = 0; i < TD->getNumTemplateParameterLists(); ++i)
ParameterLists.push_back(TD->getTemplateParameterList(i));
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(TD)) {
if (ClassTemplateDecl *CTD = RD->getDescribedClassTemplate())
ParameterLists.push_back(CTD->getTemplateParameters());
+ else if (auto *PSD = dyn_cast<ClassTemplatePartialSpecializationDecl>(D))
+ ParameterLists.push_back(PSD->getTemplateParameters());
}
}
+ // FIXME: Alias declarations and concepts.
unsigned Count = 0;
+ Scope *InnermostTemplateScope = nullptr;
for (TemplateParameterList *Params : ParameterLists) {
- if (Params->size() > 0)
- // Ignore explicit specializations; they don't contribute to the template
- // depth.
- ++Count;
+ // Ignore explicit specializations; they don't contribute to the template
+ // depth.
+ if (Params->size() == 0)
+ continue;
+
+ InnermostTemplateScope = EnterScope();
for (NamedDecl *Param : *Params) {
if (Param->getDeclName()) {
- S->AddDecl(Param);
+ InnermostTemplateScope->AddDecl(Param);
IdResolver.AddDecl(Param);
}
}
+ ++Count;
+ }
+
+ // Associate the new template scopes with the corresponding entities.
+ if (InnermostTemplateScope) {
+ assert(LookupDC && "no enclosing DeclContext for template lookup");
+ EnterTemplatedContext(InnermostTemplateScope, LookupDC);
}
return Count;
return false;
}
-// Find the next outer declaration context from this scope. This
-// routine actually returns the semantic outer context, which may
-// differ from the lexical context (encoded directly in the Scope
-// stack) when we are parsing a member of a class template. In this
-// case, the second element of the pair will be true, to indicate that
-// name lookup should continue searching in this semantic context when
-// it leaves the current template parameter scope.
-static std::pair<DeclContext *, bool> findOuterContext(Scope *S) {
- DeclContext *DC = S->getEntity();
- DeclContext *Lexical = nullptr;
- for (Scope *OuterS = S->getParent(); OuterS;
- OuterS = OuterS->getParent()) {
- if (OuterS->getEntity()) {
- Lexical = OuterS->getEntity();
- break;
- }
- }
-
- // C++ [temp.local]p8:
- // In the definition of a member of a class template that appears
- // outside of the namespace containing the class template
- // definition, the name of a template-parameter hides the name of
- // a member of this namespace.
- //
- // Example:
- //
- // namespace N {
- // class C { };
- //
- // template<class T> class B {
- // void f(T);
- // };
- // }
- //
- // template<class C> void N::B<C>::f(C) {
- // C b; // C is the template parameter, not N::C
- // }
- //
- // In this example, the lexical context we return is the
- // TranslationUnit, while the semantic context is the namespace N.
- if (!Lexical || !DC || !S->getParent() ||
- !S->getParent()->isTemplateParamScope())
- return std::make_pair(Lexical, false);
-
- // Find the outermost template parameter scope.
- // For the example, this is the scope for the template parameters of
- // template<class C>.
- Scope *OutermostTemplateScope = S->getParent();
- while (OutermostTemplateScope->getParent() &&
- OutermostTemplateScope->getParent()->isTemplateParamScope())
- OutermostTemplateScope = OutermostTemplateScope->getParent();
-
- // Find the namespace context in which the original scope occurs. In
- // the example, this is namespace N.
- DeclContext *Semantic = DC;
- while (!Semantic->isFileContext())
- Semantic = Semantic->getParent();
-
- // Find the declaration context just outside of the template
- // parameter scope. This is the context in which the template is
- // being lexically declaration (a namespace context). In the
- // example, this is the global scope.
- if (Lexical->isFileContext() && !Lexical->Equals(Semantic) &&
- Lexical->Encloses(Semantic))
- return std::make_pair(Semantic, true);
-
- return std::make_pair(Lexical, false);
+/// Find the outer declaration context from this scope. This indicates the
+/// context that we should search up to (exclusive) before considering the
+/// parent of the specified scope.
+static DeclContext *findOuterContext(Scope *S) {
+ for (Scope *OuterS = S->getParent(); OuterS; OuterS = OuterS->getParent())
+ if (DeclContext *DC = OuterS->getLookupEntity())
+ return DC;
+ return nullptr;
}
namespace {
UnqualUsingDirectiveSet UDirs(*this);
bool VisitedUsingDirectives = false;
bool LeftStartingScope = false;
- DeclContext *OutsideOfTemplateParamDC = nullptr;
// When performing a scope lookup, we want to find local extern decls.
FindLocalExternScope FindLocals(R);
for (; S && !isNamespaceOrTranslationUnitScope(S); S = S->getParent()) {
- DeclContext *Ctx = S->getEntity();
bool SearchNamespaceScope = true;
// Check whether the IdResolver has anything in this scope.
for (; I != IEnd && S->isDeclScope(*I); ++I) {
if (!SearchNamespaceScope) {
R.resolveKind();
if (S->isClassScope())
- if (CXXRecordDecl *Record = dyn_cast_or_null<CXXRecordDecl>(Ctx))
+ if (CXXRecordDecl *Record =
+ dyn_cast_or_null<CXXRecordDecl>(S->getEntity()))
R.setNamingClass(Record);
return true;
}
return false;
}
- if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC &&
- S->getParent() && !S->getParent()->isTemplateParamScope()) {
- // We've just searched the last template parameter scope and
- // found nothing, so look into the contexts between the
- // lexical and semantic declaration contexts returned by
- // findOuterContext(). This implements the name lookup behavior
- // of C++ [temp.local]p8.
- Ctx = OutsideOfTemplateParamDC;
- OutsideOfTemplateParamDC = nullptr;
- }
-
- if (Ctx) {
- DeclContext *OuterCtx;
- bool SearchAfterTemplateScope;
- std::tie(OuterCtx, SearchAfterTemplateScope) = findOuterContext(S);
- if (SearchAfterTemplateScope)
- OutsideOfTemplateParamDC = OuterCtx;
-
+ if (DeclContext *Ctx = S->getLookupEntity()) {
+ DeclContext *OuterCtx = findOuterContext(S);
for (; Ctx && !Ctx->Equals(OuterCtx); Ctx = Ctx->getLookupParent()) {
// We do not directly look into transparent contexts, since
// those entities will be found in the nearest enclosing
return true;
}
- DeclContext *Ctx = S->getEntity();
- if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC &&
- S->getParent() && !S->getParent()->isTemplateParamScope()) {
- // We've just searched the last template parameter scope and
- // found nothing, so look into the contexts between the
- // lexical and semantic declaration contexts returned by
- // findOuterContext(). This implements the name lookup behavior
- // of C++ [temp.local]p8.
- Ctx = OutsideOfTemplateParamDC;
- OutsideOfTemplateParamDC = nullptr;
- }
-
+ DeclContext *Ctx = S->getLookupEntity();
if (Ctx) {
- DeclContext *OuterCtx;
- bool SearchAfterTemplateScope;
- std::tie(OuterCtx, SearchAfterTemplateScope) = findOuterContext(S);
- if (SearchAfterTemplateScope)
- OutsideOfTemplateParamDC = OuterCtx;
-
+ DeclContext *OuterCtx = findOuterContext(S);
for (; Ctx && !Ctx->Equals(OuterCtx); Ctx = Ctx->getLookupParent()) {
// We do not directly look into transparent contexts, since
// those entities will be found in the nearest enclosing
}
}
- // FIXME: C++ [temp.local]p8
- DeclContext *Entity = nullptr;
- if (S->getEntity()) {
+ DeclContext *Entity = S->getLookupEntity();
+ if (Entity) {
// Look into this scope's declaration context, along with any of its
// parent lookup contexts (e.g., enclosing classes), up to the point
// where we hit the context stored in the next outer scope.
- Entity = S->getEntity();
- DeclContext *OuterCtx = findOuterContext(S).first; // FIXME
+ DeclContext *OuterCtx = findOuterContext(S);
for (DeclContext *Ctx = Entity; Ctx && !Ctx->Equals(OuterCtx);
Ctx = Ctx->getLookupParent()) {
// Each template parameter scope represents one level of template parameter
// depth.
- for (Scope *TempParamScope = S->getTemplateParamParent();
- TempParamScope && !Depth;
+ for (Scope *TempParamScope = S->getTemplateParamParent(); TempParamScope;
TempParamScope = TempParamScope->getParent()->getTemplateParamParent()) {
++Depth;
}
};
}
-namespace dr458 { // dr458: no
+namespace dr458 { // dr458: 11
struct A {
int T;
int f();
int A::f() {
return T;
}
- template<typename T>
+ template<typename T> // expected-note {{declared here}}
int A::g() {
- return T; // FIXME: this is invalid, it finds the template parameter
+ return T; // expected-error {{'T' does not refer to a value}}
}
template<typename T>
int B<T>::g() {
return T;
}
- template<typename U> template<typename T>
+ template<typename U> template<typename T> // expected-note {{declared here}}
int B<U>::h() {
- return T; // FIXME: this is invalid, it finds the template parameter
+ return T; // expected-error {{'T' does not refer to a value}}
}
}
// RUN: %clang_cc1 -fsyntax-only -verify %s
-// expected-no-diagnostics
namespace N {
enum { C };
D d;
}
+// Ensure we properly interleave the searches within classes and template parameter lists.
+namespace SearchClassBetweenTemplateParameterLists {
+ int AA, BB; // none of the below lookups should ever consider these
+
+ struct Base {
+ using AA = void;
+ using BB = void;
+ };
+ struct BaseT : Base {
+ using T = void;
+ };
+ struct BaseU : Base {
+ using U = void;
+ };
+
+ template<typename T> struct A {
+ using AA = void;
+ template<typename U> struct B {
+ using BB = void;
+ void f(U);
+ void g(U);
+ void h(T);
+ void i(T);
+ template<typename V> void j(V);
+ template<typename V> void k(U);
+
+ // OK: these find the template parameter not the member.
+ template<typename AA> void l(AA x) { AA aa; }
+ template<typename BB> void m(BB x) { BB bb; }
+
+ struct C : Base {
+ // All OK; these find the template parameters.
+ template<typename> void f(T x) { T t; }
+ template<typename> void g(U x) { U u; }
+ template<typename AA> void h(AA x) { AA aa; }
+ template<typename BB> void i(BB x) { BB bb; }
+ };
+
+ struct CT : BaseT {
+ template<typename> void f(T x) { // expected-error {{void}}
+ T t; // expected-error {{incomplete}}
+ }
+ template<typename> void g(U x) { U u; }
+ template<typename AA> void h(AA x) { AA aa; }
+ template<typename BB> void i(BB x) { BB bb; }
+ };
+
+ struct CU : BaseU {
+ template<typename> void f(T x) { T t; }
+ template<typename> void g(U x) { // expected-error {{void}}
+ U u; // expected-error {{incomplete}}
+ }
+ template<typename AA> void h(AA x) { AA aa; }
+ template<typename BB> void i(BB x) { BB bb; }
+ };
+ };
+ };
+
+ // Search order for the below is:
+ // 1) template parameter scope of the function itself (if any)
+ // 2) class of which function is a member
+ // 3) template parameter scope of inner class
+ // 4) class of which class is a member
+ // 5) template parameter scope of outer class
+
+ // OK, 'AA' found in (3)
+ template<typename T> template<typename AA>
+ void A<T>::B<AA>::f(AA) {
+ AA aa;
+ }
+
+ // error, 'BB' found in (2)
+ template<typename T> template<typename BB>
+ void A<T>::B<BB>::g(BB) { // expected-error {{does not match}}
+ BB bb; // expected-error {{incomplete type}}
+ }
+
+ // error, 'AA' found in (4)
+ template<typename AA> template<typename U>
+ void A<AA>::B<U>::h(AA) { // expected-error {{does not match}}
+ AA aa; // expected-error {{incomplete type}}
+ }
+
+ // error, 'BB' found in (2)
+ template<typename BB> template<typename U>
+ void A<BB>::B<U>::i(BB) { // expected-error {{does not match}}
+ BB bb; // expected-error {{incomplete type}}
+ }
+
+ // OK, 'BB' found in (1)
+ template<typename T> template<typename U> template<typename BB>
+ void A<T>::B<U>::j(BB) {
+ BB bb;
+ }
+
+ // error, 'BB' found in (2)
+ template<typename T> template<typename BB> template<typename V>
+ void A<T>::B<BB>::k(V) { // expected-error {{does not match}}
+ BB bb; // expected-error {{incomplete type}}
+ }
+}
#if __cplusplus > 201402L
friend constexpr auto T::operator()(int) const;
friend constexpr T::operator ExpectedTypeT() const noexcept;
+
+ template<typename T>
+ friend constexpr void U::operator()(T&) const;
+ // FIXME: This should not match; the return type is specified as behaving
+ // "as if it were a decltype-specifier denoting the return type of
+ // [operator()]", which is not equivalent to this alias template.
+ template<typename T>
+ friend constexpr U::operator ExpectedTypeU<T>() const noexcept;
#else
friend auto T::operator()(int) const;
friend T::operator ExpectedTypeT() const;
-#endif
- // FIXME: The first of these should match. The second should not.
template<typename T>
- friend void U::operator()(T&) const; // expected-error {{does not match}}
+ friend void U::operator()(T&) const;
+ // FIXME: This should not match, as above.
template<typename T>
- friend U::operator ExpectedTypeU<T>() const; // expected-error {{does not match}}
+ friend U::operator ExpectedTypeU<T>() const;
+#endif
private:
int n;
<td><a href="https://wg21.link/cwg458">458</a></td>
<td>C++11</td>
<td>Hiding of member template parameters by other members</td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 11</td>
</tr>
<tr class="open" id="459">
<td><a href="https://wg21.link/cwg459">459</a></td>