}
};
+ /// 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);
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
}
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?
};
// 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();
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;
+ MultiParseScope Scopes(*this);
// 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
// Reenter template scopes from outermost to innermost.
for (ContainingDC CDC : reverse(DeclContextsToReenter)) {
- TemplateParamScopeStack.push_back(
- new ParseScope(this, Scope::TemplateParamScope));
+ Scopes.Enter(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));
+ Scopes.Enter(Scope::DeclScope);
Actions.PushDeclContext(Actions.getCurScope(), CDC.getDC());
}
}
} 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.
CurContext = DC;
S->setEntity(DC);
+
+ if (!S->getParent()->isTemplateParamScope())
+ return;
+
+ // Also set the corresponding entities for all immediately-enclosing template
+ // parameter scopes.
+ //
+ // 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 (Scope *OuterS = S->getParent(); OuterS && OuterS->isTemplateParamScope();
+ OuterS = OuterS->getParent(), --ScopeDepth) {
+ auto *SearchDCAfterScope = DC;
+ for (; DC; DC = DC->getLookupParent()) {
+ if (auto *TD = cast<Decl>(DC)->getDescribedTemplate()) {
+ unsigned DCDepth = TD->getTemplateParameters()->getDepth() + 1;
+ if (DCDepth > ScopeDepth)
+ continue;
+ if (ScopeDepth == DCDepth)
+ SearchDCAfterScope = DC = DC->getLookupParent();
+ break;
+ }
+ }
+ OuterS->setLookupEntity(SearchDCAfterScope);
+ }
}
void Sema::ExitDeclaratorContext(Scope *S) {
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
+
+ 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);
+ };
+ };
+
+ // 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>
<td><a href="https://wg21.link/cwg2082">2082</a></td>
<td>CD4</td>
<td>Referring to parameters in unevaluated operands of default arguments</td>
- <td class="none" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 11</td>
</tr>
<tr id="2083">
<td><a href="https://wg21.link/cwg2083">2083</a></td>
<td><a href="https://wg21.link/cwg2346">2346</a></td>
<td>DRWP</td>
<td>Local variables in default arguments</td>
- <td class="none" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 11</td>
</tr>
<tr id="2347">
<td><a href="https://wg21.link/cwg2347">2347</a></td>