diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 1ae219781c69..31840694da27 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1088,6 +1088,31 @@ 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); @@ -3240,7 +3265,7 @@ private: 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 &TemplateParams, SourceLocation &LAngleLoc, SourceLocation &RAngleLoc); diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h index 9b8f1415a1e3..b7260f15fe1b 100644 --- a/clang/include/clang/Sema/Scope.h +++ b/clang/include/clang/Sema/Scope.h @@ -322,8 +322,21 @@ public: /// 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 diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 2aab53f4fa90..50ad0255f838 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -241,7 +241,7 @@ TemplateDecl *Decl::getDescribedTemplate() const { } 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? diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 48277dc85466..aa35200c33b6 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1258,17 +1258,16 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( }; // 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 TemplateParams; SourceLocation LAngleLoc, RAngleLoc; - if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(), + if (ParseTemplateParameters(TemplateParamScope, + CurTemplateDepthTracker.getDepth(), TemplateParams, LAngleLoc, RAngleLoc)) { Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope()); return ExprError(); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 16aecffa494a..847c513b5baa 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -67,8 +67,7 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization( 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. @@ -116,7 +115,8 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization( // Parse the '<' template-parameter-list '>' SourceLocation LAngleLoc, RAngleLoc; SmallVector 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); @@ -150,9 +150,6 @@ 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( @@ -430,8 +427,9 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, /// /// \returns true if an error occurred, false otherwise. bool Parser::ParseTemplateParameters( - unsigned Depth, SmallVectorImpl &TemplateParams, - SourceLocation &LAngleLoc, SourceLocation &RAngleLoc) { + MultiParseScope &TemplateScopes, unsigned Depth, + SmallVectorImpl &TemplateParams, SourceLocation &LAngleLoc, + SourceLocation &RAngleLoc) { // Get the template parameter list. if (!TryConsumeToken(tok::less, LAngleLoc)) { Diag(Tok.getLocation(), diag::err_expected_less_after) << "template"; @@ -440,8 +438,11 @@ bool Parser::ParseTemplateParameters( // 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 @@ -850,9 +851,9 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) { SmallVector 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; } } @@ -1630,7 +1631,7 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { Sema::ContextRAII GlobalSavedContext( Actions, Actions.Context.getTranslationUnitDecl()); - SmallVector 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 @@ -1655,13 +1656,12 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { // 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(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()); } } @@ -1709,13 +1709,6 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { } else Actions.ActOnFinishFunctionBody(LPT.D, nullptr); } - - // Exit scopes. - FnScope.Exit(); - SmallVectorImpl::reverse_iterator I = - TemplateParamScopeStack.rbegin(); - for (; I != TemplateParamScopeStack.rend(); ++I) - delete *I; } /// Lex a delayed template function for late parsing. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 2bf16d138d5a..1d046c776709 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1358,6 +1358,50 @@ 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 template template + // void N::A::B::f(...) + // we search V then B (and base classes) then U then A (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(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) { diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 06a4dfa0a636..5757eaf3fac0 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1153,73 +1153,14 @@ static bool isNamespaceOrTranslationUnitScope(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 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 B { - // void f(T); - // }; - // } - // - // template void N::B::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. - 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 { @@ -1286,13 +1227,11 @@ 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) { @@ -1324,7 +1263,8 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { if (!SearchNamespaceScope) { R.resolveKind(); if (S->isClassScope()) - if (CXXRecordDecl *Record = dyn_cast_or_null(Ctx)) + if (CXXRecordDecl *Record = + dyn_cast_or_null(S->getEntity())) R.setNamingClass(Record); return true; } @@ -1338,24 +1278,8 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { 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 @@ -1480,25 +1404,9 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { 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 @@ -3995,14 +3903,12 @@ private: } } - // 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()) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 073b4e818a24..6bf46f273a02 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -51,8 +51,7 @@ unsigned Sema::getTemplateDepth(Scope *S) const { // 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; } diff --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp index 2c762237037d..ff90ee82d4d4 100644 --- a/clang/test/CXX/drs/dr4xx.cpp +++ b/clang/test/CXX/drs/dr4xx.cpp @@ -690,7 +690,7 @@ namespace dr457 { // dr457: yes }; } -namespace dr458 { // dr458: no +namespace dr458 { // dr458: 11 struct A { int T; int f(); @@ -706,9 +706,9 @@ namespace dr458 { // dr458: no int A::f() { return T; } - template + template // 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 @@ -719,9 +719,9 @@ namespace dr458 { // dr458: no int B::g() { return T; } - template template + template template // expected-note {{declared here}} int B::h() { - return T; // FIXME: this is invalid, it finds the template parameter + return T; // expected-error {{'T' does not refer to a value}} } } diff --git a/clang/test/CXX/temp/temp.res/temp.local/p8.cpp b/clang/test/CXX/temp/temp.res/temp.local/p8.cpp index fecfed06f109..c2b4c1950ea5 100644 --- a/clang/test/CXX/temp/temp.res/temp.local/p8.cpp +++ b/clang/test/CXX/temp/temp.res/temp.local/p8.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -// expected-no-diagnostics namespace N { enum { C }; @@ -52,3 +51,63 @@ 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 struct A { + using AA = void; + template struct B { + using BB = void; + void f(U); + void g(U); + void h(T); + void i(T); + template void j(V); + template 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 template + void A::B::f(AA) { + AA aa; + } + + // error, 'BB' found in (2) + template template + void A::B::g(BB) { // expected-error {{does not match}} + BB bb; // expected-error {{incomplete type}} + } + + // error, 'AA' found in (4) + template template + void A::B::h(AA) { // expected-error {{does not match}} + AA aa; // expected-error {{incomplete type}} + } + + // error, 'BB' found in (2) + template template + void A::B::i(BB) { // expected-error {{does not match}} + BB bb; // expected-error {{incomplete type}} + } + + // OK, 'BB' found in (1) + template template template + void A::B::j(BB) { + BB bb; + } + + // error, 'BB' found in (2) + template template template + void A::B::k(V) { // expected-error {{does not match}} + BB bb; // expected-error {{incomplete type}} + } +} diff --git a/clang/test/SemaCXX/lambda-expressions.cpp b/clang/test/SemaCXX/lambda-expressions.cpp index 0f4edc4d1f34..3240d5351fc5 100644 --- a/clang/test/SemaCXX/lambda-expressions.cpp +++ b/clang/test/SemaCXX/lambda-expressions.cpp @@ -601,16 +601,24 @@ namespace ConversionOperatorDoesNotHaveDeducedReturnType { #if __cplusplus > 201402L friend constexpr auto T::operator()(int) const; friend constexpr T::operator ExpectedTypeT() const noexcept; + + template + 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 + friend constexpr U::operator ExpectedTypeU() 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 - 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 - friend U::operator ExpectedTypeU() const; // expected-error {{does not match}} + friend U::operator ExpectedTypeU() const; +#endif private: int n; diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 4abdc0fa2639..22755e4b20ea 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -2789,7 +2789,7 @@ of class templates 458 C++11 Hiding of member template parameters by other members - No + Clang 11 459 @@ -12307,7 +12307,7 @@ and POD class 2082 CD4 Referring to parameters in unevaluated operands of default arguments - Unknown + Clang 11 2083 @@ -13891,7 +13891,7 @@ and POD class 2346 DRWP Local variables in default arguments - Unknown + Clang 11 2347