Revert "DR458: Search template parameter scopes in the right order."

We weren't re-entering template scopes in the right order, causing this
to break self-host with -fdelayed-template-parsing.

This reverts commit 237c2a23b6.
This commit is contained in:
Richard Smith 2020-06-16 23:53:44 -07:00
parent 2e613d2ded
commit 02fccc26cf
12 changed files with 160 additions and 206 deletions

View File

@ -1088,31 +1088,6 @@ public:
}
};
/// 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);
@ -3265,7 +3240,7 @@ private:
DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
ParsingDeclRAIIObject &DiagsFromParams, SourceLocation &DeclEnd,
ParsedAttributes &AccessAttrs, AccessSpecifier AS = AS_none);
bool ParseTemplateParameters(MultiParseScope &TemplateScopes, unsigned Depth,
bool ParseTemplateParameters(unsigned Depth,
SmallVectorImpl<NamedDecl *> &TemplateParams,
SourceLocation &LAngleLoc,
SourceLocation &RAngleLoc);

View File

@ -322,21 +322,8 @@ public:
/// declared in.
bool isDeclScope(const Decl *D) const { return DeclsInScope.count(D) != 0; }
/// 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; }
DeclContext *getEntity() const { return Entity; }
void setEntity(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

View File

@ -241,7 +241,7 @@ TemplateDecl *Decl::getDescribedTemplate() const {
}
bool Decl::isTemplated() const {
// A declaration is templated if it is a template or a template pattern, or
// A declaration is dependent 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?

View File

@ -1258,16 +1258,17 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
};
// FIXME: Consider allowing this as an extension for GCC compatibiblity.
MultiParseScope TemplateParamScope(*this);
if (Tok.is(tok::less)) {
const bool HasExplicitTemplateParams = Tok.is(tok::less);
ParseScope TemplateParamScope(this, Scope::TemplateParamScope,
/*EnteredScope=*/HasExplicitTemplateParams);
if (HasExplicitTemplateParams) {
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(TemplateParamScope,
CurTemplateDepthTracker.getDepth(),
if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
TemplateParams, LAngleLoc, RAngleLoc)) {
Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
return ExprError();

View File

@ -67,7 +67,8 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization(
assert(Tok.isOneOf(tok::kw_export, tok::kw_template) &&
"Token does not start a template declaration.");
MultiParseScope TemplateParamScopes(*this);
// Enter template-parameter scope.
ParseScope TemplateParmScope(this, Scope::TemplateParamScope);
// Tell the action that names should be checked in the context of
// the declaration to come.
@ -115,8 +116,7 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization(
// Parse the '<' template-parameter-list '>'
SourceLocation LAngleLoc, RAngleLoc;
SmallVector<NamedDecl*, 4> TemplateParams;
if (ParseTemplateParameters(TemplateParamScopes,
CurTemplateDepthTracker.getDepth(),
if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
TemplateParams, LAngleLoc, RAngleLoc)) {
// Skip until the semi-colon or a '}'.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
@ -150,6 +150,9 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization(
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(
@ -427,9 +430,8 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
///
/// \returns true if an error occurred, false otherwise.
bool Parser::ParseTemplateParameters(
MultiParseScope &TemplateScopes, unsigned Depth,
SmallVectorImpl<NamedDecl *> &TemplateParams, SourceLocation &LAngleLoc,
SourceLocation &RAngleLoc) {
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";
@ -438,11 +440,8 @@ bool Parser::ParseTemplateParameters(
// Try to parse the template parameter list.
bool Failed = false;
// FIXME: Missing greatergreatergreater support.
if (!Tok.is(tok::greater) && !Tok.is(tok::greatergreater)) {
TemplateScopes.Enter(Scope::TemplateParamScope);
if (!Tok.is(tok::greater) && !Tok.is(tok::greatergreater))
Failed = ParseTemplateParameterList(Depth, TemplateParams);
}
if (Tok.is(tok::greatergreater)) {
// No diagnostic required here: a template-parameter-list can only be
@ -851,9 +850,9 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
SmallVector<NamedDecl*,8> TemplateParams;
SourceLocation LAngleLoc, RAngleLoc;
{
MultiParseScope TemplateParmScope(*this);
if (ParseTemplateParameters(TemplateParmScope, Depth + 1, TemplateParams,
LAngleLoc, RAngleLoc)) {
ParseScope TemplateParmScope(this, Scope::TemplateParamScope);
if (ParseTemplateParameters(Depth + 1, TemplateParams, LAngleLoc,
RAngleLoc)) {
return nullptr;
}
}
@ -1631,7 +1630,7 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) {
Sema::ContextRAII GlobalSavedContext(
Actions, Actions.Context.getTranslationUnitDecl());
MultiParseScope Scopes(*this);
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
@ -1656,12 +1655,13 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) {
// Reenter template scopes from outermost to innermost.
for (ContainingDC CDC : reverse(DeclContextsToReenter)) {
Scopes.Enter(Scope::TemplateParamScope);
TemplateParamScopeStack.push_back(
new ParseScope(this, Scope::TemplateParamScope));
unsigned NumParamLists = Actions.ActOnReenterTemplateScope(
getCurScope(), cast<Decl>(CDC.getDC()));
CurTemplateDepthTracker.addDepth(NumParamLists);
if (CDC.shouldPushDC()) {
Scopes.Enter(Scope::DeclScope);
TemplateParamScopeStack.push_back(new ParseScope(this, Scope::DeclScope));
Actions.PushDeclContext(Actions.getCurScope(), CDC.getDC());
}
}
@ -1709,6 +1709,13 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) {
} 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.

View File

@ -1358,50 +1358,6 @@ void Sema::EnterDeclaratorContext(Scope *S, DeclContext *DC) {
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) {

View File

@ -1153,14 +1153,73 @@ static bool isNamespaceOrTranslationUnitScope(Scope *S) {
return 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;
// 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);
}
namespace {
@ -1227,11 +1286,13 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
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) {
@ -1263,8 +1324,7 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
if (!SearchNamespaceScope) {
R.resolveKind();
if (S->isClassScope())
if (CXXRecordDecl *Record =
dyn_cast_or_null<CXXRecordDecl>(S->getEntity()))
if (CXXRecordDecl *Record = dyn_cast_or_null<CXXRecordDecl>(Ctx))
R.setNamingClass(Record);
return true;
}
@ -1278,8 +1338,24 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
return false;
}
if (DeclContext *Ctx = S->getLookupEntity()) {
DeclContext *OuterCtx = findOuterContext(S);
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;
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
@ -1404,9 +1480,25 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
return true;
}
DeclContext *Ctx = S->getLookupEntity();
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;
}
if (Ctx) {
DeclContext *OuterCtx = findOuterContext(S);
DeclContext *OuterCtx;
bool SearchAfterTemplateScope;
std::tie(OuterCtx, SearchAfterTemplateScope) = findOuterContext(S);
if (SearchAfterTemplateScope)
OutsideOfTemplateParamDC = OuterCtx;
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
@ -3903,12 +3995,14 @@ private:
}
}
DeclContext *Entity = S->getLookupEntity();
if (Entity) {
// FIXME: C++ [temp.local]p8
DeclContext *Entity = nullptr;
if (S->getEntity()) {
// 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.
DeclContext *OuterCtx = findOuterContext(S);
Entity = S->getEntity();
DeclContext *OuterCtx = findOuterContext(S).first; // FIXME
for (DeclContext *Ctx = Entity; Ctx && !Ctx->Equals(OuterCtx);
Ctx = Ctx->getLookupParent()) {

View File

@ -51,7 +51,8 @@ unsigned Sema::getTemplateDepth(Scope *S) const {
// Each template parameter scope represents one level of template parameter
// depth.
for (Scope *TempParamScope = S->getTemplateParamParent(); TempParamScope;
for (Scope *TempParamScope = S->getTemplateParamParent();
TempParamScope && !Depth;
TempParamScope = TempParamScope->getParent()->getTemplateParamParent()) {
++Depth;
}

View File

@ -690,7 +690,7 @@ namespace dr457 { // dr457: yes
};
}
namespace dr458 { // dr458: 11
namespace dr458 { // dr458: no
struct A {
int T;
int f();
@ -706,9 +706,9 @@ namespace dr458 { // dr458: 11
int A::f() {
return T;
}
template<typename T> // expected-note {{declared here}}
template<typename T>
int A::g() {
return T; // expected-error {{'T' does not refer to a value}}
return T; // FIXME: this is invalid, it finds the template parameter
}
template<typename T>
@ -719,9 +719,9 @@ namespace dr458 { // dr458: 11
int B<T>::g() {
return T;
}
template<typename U> template<typename T> // expected-note {{declared here}}
template<typename U> template<typename T>
int B<U>::h() {
return T; // expected-error {{'T' does not refer to a value}}
return T; // FIXME: this is invalid, it finds the template parameter
}
}

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// expected-no-diagnostics
namespace N {
enum { C };
@ -51,63 +52,3 @@ void N::Y::f(D) {
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}}
}
}

View File

@ -601,25 +601,17 @@ namespace ConversionOperatorDoesNotHaveDeducedReturnType {
#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;
template<typename T>
friend void U::operator()(T&) const;
// FIXME: This should not match, as above.
template<typename T>
friend U::operator ExpectedTypeU<T>() 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}}
template<typename T>
friend U::operator ExpectedTypeU<T>() const; // expected-error {{does not match}}
private:
int n;
};

View File

@ -2789,7 +2789,7 @@ of class templates</td>
<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="unreleased" align="center">Clang 11</td>
<td class="none" align="center">No</td>
</tr>
<tr class="open" id="459">
<td><a href="https://wg21.link/cwg459">459</a></td>
@ -12307,7 +12307,7 @@ and <I>POD class</I></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="unreleased" align="center">Clang 11</td>
<td class="none" align="center">Unknown</td>
</tr>
<tr id="2083">
<td><a href="https://wg21.link/cwg2083">2083</a></td>
@ -13891,7 +13891,7 @@ and <I>POD class</I></td>
<td><a href="https://wg21.link/cwg2346">2346</a></td>
<td>DRWP</td>
<td>Local variables in default arguments</td>
<td class="unreleased" align="center">Clang 11</td>
<td class="none" align="center">Unknown</td>
</tr>
<tr id="2347">
<td><a href="https://wg21.link/cwg2347">2347</a></td>