From: Richard Smith Date: Wed, 20 Sep 2017 22:17:55 +0000 (+0000) Subject: Give external linkage and mangling to lambdas inside inline variables and variable... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=56afb73f73ff1813807622911408aa59e86a0417;p=clang Give external linkage and mangling to lambdas inside inline variables and variable templates. This implements the proposed approach in https://github.com/itanium-cxx-abi/cxx-abi/issues/33 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@313827 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index c3e3de1e22..77f20fa00a 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -676,10 +676,10 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D, if (!LV.isVisibilityExplicit()) { // Use global type/value visibility as appropriate. Visibility globalVisibility; - if (computation == LVForValue) { + if ((computation & ~IgnoreTypeLinkageBit) == LVForValue) { globalVisibility = Context.getLangOpts().getValueVisibilityMode(); } else { - assert(computation == LVForType); + assert((computation & ~IgnoreTypeLinkageBit) == LVForType); globalVisibility = Context.getLangOpts().getTypeVisibilityMode(); } LV.mergeVisibility(globalVisibility, /*explicit*/ false); @@ -719,7 +719,8 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D, // // Note that we don't want to make the variable non-external // because of this, but unique-external linkage suits us. - if (Context.getLangOpts().CPlusPlus && !isFirstInExternCContext(Var)) { + if (Context.getLangOpts().CPlusPlus && !isFirstInExternCContext(Var) && + !(computation & IgnoreTypeLinkageBit)) { LinkageInfo TypeLV = getLVForType(*Var->getType(), computation); if (!isExternallyVisible(TypeLV.getLinkage())) return LinkageInfo::uniqueExternal(); @@ -759,8 +760,8 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D, // unique-external linkage, it's not legally usable from outside // this translation unit. However, we should use the C linkage // rules instead for extern "C" declarations. - if (Context.getLangOpts().CPlusPlus && - !Function->isInExternCContext()) { + if (Context.getLangOpts().CPlusPlus && !Function->isInExternCContext() && + !(computation & IgnoreTypeLinkageBit)) { // Only look at the type-as-written. If this function has an auto-deduced // return type, we can't compute the linkage of that type because it could // require looking at the linkage of this function, and we don't need this @@ -1122,8 +1123,18 @@ LinkageInfo LinkageComputer::getLVForClosure(const DeclContext *DC, // calculation determines the lambda has external linkage, it should be // downgraded to VisibleNoLinkage. if (ContextDecl) { + auto *VD = dyn_cast(ContextDecl); if (isa(ContextDecl)) DC = ContextDecl->getDeclContext()->getRedeclContext(); + else if (VD && VD->getType()->getContainedDeducedType()) + // If the declaration has a deduced type, we need to skip querying the + // linkage and visibility of that type, because it might involve this + // closure type. The only effect of this is that we might give a lambda + // VisibleNoLinkage rather than NoLinkage when we don't strictly need to, + // which is benign. + return computeLVForDecl( + cast(ContextDecl), + LVComputationKind(computation | IgnoreTypeLinkageBit)); else return getLVForDecl(cast(ContextDecl), computation); } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index dbda7a5dbd..3dc42f2a79 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -1691,10 +1691,16 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { // to emit that last part of the prefix here. if (Decl *Context = Lambda->getLambdaContextDecl()) { if ((isa(Context) || isa(Context)) && - Context->getDeclContext()->isRecord()) { + !isa(Context)) { + // FIXME: 'inline auto [a, b] = []{ return ... };' does not get a + // reasonable mangling here. if (const IdentifierInfo *Name = cast(Context)->getIdentifier()) { mangleSourceName(Name); + const TemplateArgumentList *TemplateArgs = nullptr; + if (const TemplateDecl *TD = + isTemplate(cast(Context), TemplateArgs)) + mangleTemplateArgs(*TemplateArgs); Out << 'M'; } } diff --git a/lib/AST/Linkage.h b/lib/AST/Linkage.h index fde9841cdf..d68970769a 100644 --- a/lib/AST/Linkage.h +++ b/lib/AST/Linkage.h @@ -24,7 +24,8 @@ namespace clang { enum : unsigned { IgnoreExplicitVisibilityBit = 2, - IgnoreAllVisibilityBit = 4 + IgnoreAllVisibilityBit = 4, + IgnoreTypeLinkageBit = 8, }; /// Kinds of LV computation. The linkage side of the computation is diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index ad16d2c5bb..8bca7badea 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2131,6 +2131,37 @@ Decl *Parser::ParseDeclarationAfterDeclarator( Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( Declarator &D, const ParsedTemplateInfo &TemplateInfo, ForRangeInit *FRI) { + // RAII type used to track whether we're inside an initializer. + struct InitializerScopeRAII { + Parser &P; + Declarator &D; + Decl *ThisDecl; + + InitializerScopeRAII(Parser &P, Declarator &D, Decl *ThisDecl) + : P(P), D(D), ThisDecl(ThisDecl) { + if (ThisDecl && P.getLangOpts().CPlusPlus) { + Scope *S = nullptr; + if (D.getCXXScopeSpec().isSet()) { + P.EnterScope(0); + S = P.getCurScope(); + } + P.Actions.ActOnCXXEnterDeclInitializer(S, ThisDecl); + } + } + ~InitializerScopeRAII() { pop(); } + void pop() { + if (ThisDecl && P.getLangOpts().CPlusPlus) { + Scope *S = nullptr; + if (D.getCXXScopeSpec().isSet()) + S = P.getCurScope(); + P.Actions.ActOnCXXExitDeclInitializer(S, ThisDecl); + if (S) + P.ExitScope(); + } + ThisDecl = nullptr; + } + }; + // Inform the current actions module that we just parsed this declarator. Decl *ThisDecl = nullptr; switch (TemplateInfo.Kind) { @@ -2208,10 +2239,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( else Diag(ConsumeToken(), diag::err_default_special_members); } else { - if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) { - EnterScope(0); - Actions.ActOnCXXEnterDeclInitializer(getCurScope(), ThisDecl); - } + InitializerScopeRAII InitScope(*this, D, ThisDecl); if (Tok.is(tok::code_completion)) { Actions.CodeCompleteInitializer(getCurScope(), ThisDecl); @@ -2234,10 +2262,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( FRI->RangeExpr = Init; } - if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) { - Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl); - ExitScope(); - } + InitScope.pop(); if (Init.isInvalid()) { SmallVector StopTokens; @@ -2259,10 +2284,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( ExprVector Exprs; CommaLocsTy CommaLocs; - if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) { - EnterScope(0); - Actions.ActOnCXXEnterDeclInitializer(getCurScope(), ThisDecl); - } + InitializerScopeRAII InitScope(*this, D, ThisDecl); llvm::function_ref ExprListCompleter; auto ThisVarDecl = dyn_cast_or_null(ThisDecl); @@ -2283,11 +2305,6 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( if (ParseExpressionList(Exprs, CommaLocs, ExprListCompleter)) { Actions.ActOnInitializerError(ThisDecl); SkipUntil(tok::r_paren, StopAtSemi); - - if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) { - Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl); - ExitScope(); - } } else { // Match the ')'. T.consumeClose(); @@ -2295,10 +2312,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( assert(!Exprs.empty() && Exprs.size()-1 == CommaLocs.size() && "Unexpected number of commas!"); - if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) { - Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl); - ExitScope(); - } + InitScope.pop(); ExprResult Initializer = Actions.ActOnParenListExpr(T.getOpenLocation(), T.getCloseLocation(), @@ -2311,17 +2325,11 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( // Parse C++0x braced-init-list. Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); - if (D.getCXXScopeSpec().isSet()) { - EnterScope(0); - Actions.ActOnCXXEnterDeclInitializer(getCurScope(), ThisDecl); - } + InitializerScopeRAII InitScope(*this, D, ThisDecl); ExprResult Init(ParseBraceInitializer()); - if (D.getCXXScopeSpec().isSet()) { - Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl); - ExitScope(); - } + InitScope.pop(); if (Init.isInvalid()) { Actions.ActOnInitializerError(ThisDecl); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 529084a517..b6d9b86535 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -14240,21 +14240,22 @@ void Sema::ActOnPureSpecifier(Decl *D, SourceLocation ZeroLoc) { Diag(D->getLocation(), diag::err_illegal_initializer); } -/// \brief Determine whether the given declaration is a static data member. -static bool isStaticDataMember(const Decl *D) { +/// \brief Determine whether the given declaration is a global variable or +/// static data member. +static bool isNonlocalVariable(const Decl *D) { if (const VarDecl *Var = dyn_cast_or_null(D)) - return Var->isStaticDataMember(); + return Var->hasGlobalStorage(); return false; } -/// ActOnCXXEnterDeclInitializer - Invoked when we are about to parse -/// an initializer for the out-of-line declaration 'Dcl'. The scope -/// is a fresh scope pushed for just this purpose. +/// Invoked when we are about to parse an initializer for the declaration +/// 'Dcl'. /// /// After this method is called, according to [C++ 3.4.1p13], if 'Dcl' is a /// static data member of class X, names should be looked up in the scope of -/// class X. +/// class X. If the declaration had a scope specifier, a scope will have +/// been created and passed in for this purpose. Otherwise, S will be null. void Sema::ActOnCXXEnterDeclInitializer(Scope *S, Decl *D) { // If there is no declaration, there was an error parsing it. if (!D || D->isInvalidDecl()) @@ -14264,28 +14265,27 @@ void Sema::ActOnCXXEnterDeclInitializer(Scope *S, Decl *D) { // might not be out of line if the specifier names the current namespace: // extern int n; // int ::n = 0; - if (D->isOutOfLine()) + if (S && D->isOutOfLine()) EnterDeclaratorContext(S, D->getDeclContext()); // If we are parsing the initializer for a static data member, push a // new expression evaluation context that is associated with this static // data member. - if (isStaticDataMember(D)) + if (isNonlocalVariable(D)) PushExpressionEvaluationContext( ExpressionEvaluationContext::PotentiallyEvaluated, D); } -/// ActOnCXXExitDeclInitializer - Invoked after we are finished parsing an -/// initializer for the out-of-line declaration 'D'. +/// Invoked after we are finished parsing an initializer for the declaration D. void Sema::ActOnCXXExitDeclInitializer(Scope *S, Decl *D) { // If there is no declaration, there was an error parsing it. if (!D || D->isInvalidDecl()) return; - if (isStaticDataMember(D)) + if (isNonlocalVariable(D)) PopExpressionEvaluationContext(); - if (D->isOutOfLine()) + if (S && D->isOutOfLine()) ExitDeclaratorContext(S); } diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index bbd7c4a093..996ed3ea7e 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -288,7 +288,9 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC, Normal, DefaultArgument, DataMember, - StaticDataMember + StaticDataMember, + InlineVariable, + VariableTemplate } Kind = Normal; // Default arguments of member function parameters that appear in a class @@ -303,6 +305,14 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC, } else if (VarDecl *Var = dyn_cast(ManglingContextDecl)) { if (Var->getDeclContext()->isRecord()) Kind = StaticDataMember; + else if (Var->getMostRecentDecl()->isInline()) + Kind = InlineVariable; + else if (Var->getDescribedVarTemplate()) + Kind = VariableTemplate; + else if (auto *VTS = dyn_cast(Var)) { + if (!VTS->isExplicitSpecialization()) + Kind = VariableTemplate; + } } else if (isa(ManglingContextDecl)) { Kind = DataMember; } @@ -343,6 +353,10 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC, // -- the in-class initializers of class members case DefaultArgument: // -- default arguments appearing in class definitions + case InlineVariable: + // -- the initializers of inline variables + case VariableTemplate: + // -- the initializers of templated variables return &ExprEvalContexts.back().getMangleNumberingContext(Context); } diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index e702d3c723..2c51c57bf3 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4140,12 +4140,8 @@ void Sema::InstantiateVariableInitializer( Var->setImplicitlyInline(); if (OldVar->getInit()) { - if (Var->isStaticDataMember() && !OldVar->isOutOfLine()) - PushExpressionEvaluationContext( - Sema::ExpressionEvaluationContext::ConstantEvaluated, OldVar); - else - PushExpressionEvaluationContext( - Sema::ExpressionEvaluationContext::PotentiallyEvaluated, OldVar); + EnterExpressionEvaluationContext Evaluated( + *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); // Instantiate the initializer. ExprResult Init; @@ -4173,8 +4169,6 @@ void Sema::InstantiateVariableInitializer( // because of a bogus initializer. Var->setInvalidDecl(); } - - PopExpressionEvaluationContext(); } else { if (Var->isStaticDataMember()) { if (!Var->isOutOfLine()) diff --git a/test/CodeGenCXX/mangle-lambdas.cpp b/test/CodeGenCXX/mangle-lambdas.cpp index 15987ebe46..d49ed4b2a5 100644 --- a/test/CodeGenCXX/mangle-lambdas.cpp +++ b/test/CodeGenCXX/mangle-lambdas.cpp @@ -26,6 +26,24 @@ void call_inline_func() { inline_func(17); } +// CHECK-LABEL: define linkonce_odr i32* @_ZNK10inline_varMUlvE_clEv( +// CHECK: @_ZZNK10inline_varMUlvE_clEvE1n +inline auto inline_var = [] { + static int n = 5; + return &n; +}; + +int *use_inline_var = inline_var(); + +// CHECK-LABEL: define linkonce_odr i32* @_ZNK12var_templateIiEMUlvE_clEv( +// CHECK: @_ZZNK12var_templateIiEMUlvE_clEvE1n +template auto var_template = [] { + static int n = 9; + return &n; +}; + +int *use_var_template = var_template(); + struct S { void f(int = []{return 1;}() + []{return 2;}(), @@ -118,7 +136,7 @@ T StaticMembers::z = accept_lambda([]{return 4;}); template int (*StaticMembers::f)() = []{return 5;}; -// CHECK-LABEL: define internal void @__cxx_global_var_init() +// CHECK-LABEL: define internal void @__cxx_global_var_init // CHECK: call i32 @_ZNK13StaticMembersIfE1xMUlvE_clEv // CHECK-NEXT: call i32 @_ZNK13StaticMembersIfE1xMUlvE0_clEv // CHECK-NEXT: add nsw @@ -128,23 +146,23 @@ int (*StaticMembers::f)() = []{return 5;}; // CHECK: ret i32 2 template float StaticMembers::x; -// CHECK-LABEL: define internal void @__cxx_global_var_init.1() +// CHECK-LABEL: define internal void @__cxx_global_var_init // CHECK: call i32 @_ZNK13StaticMembersIfE1yMUlvE_clEv // CHECK-LABEL: define linkonce_odr i32 @_ZNK13StaticMembersIfE1yMUlvE_clEv // CHECK: ret i32 3 template float StaticMembers::y; -// CHECK-LABEL: define internal void @__cxx_global_var_init.2() +// CHECK-LABEL: define internal void @__cxx_global_var_init // CHECK: call i32 @_Z13accept_lambdaIN13StaticMembersIfE1zMUlvE_EEiT_ // CHECK: declare i32 @_Z13accept_lambdaIN13StaticMembersIfE1zMUlvE_EEiT_() template float StaticMembers::z; -// CHECK-LABEL: define internal void @__cxx_global_var_init.3() +// CHECK-LABEL: define internal void @__cxx_global_var_init // CHECK: call {{.*}} @_ZNK13StaticMembersIfE1fMUlvE_cvPFivEEv // CHECK-LABEL: define linkonce_odr i32 ()* @_ZNK13StaticMembersIfE1fMUlvE_cvPFivEEv template int (*StaticMembers::f)(); -// CHECK-LABEL: define internal void @__cxx_global_var_init.4 +// CHECK-LABEL: define internal void @__cxx_global_var_init // CHECK: call i32 @"_ZNK13StaticMembersIdE3$_2clEv" // CHECK-LABEL: define internal i32 @"_ZNK13StaticMembersIdE3$_2clEv" // CHECK: ret i32 42 diff --git a/test/SemaCXX/vartemplate-lambda.cpp b/test/SemaCXX/vartemplate-lambda.cpp index 5b91e232e3..6744968bcc 100644 --- a/test/SemaCXX/vartemplate-lambda.cpp +++ b/test/SemaCXX/vartemplate-lambda.cpp @@ -8,7 +8,7 @@ template auto v1 = [](int a = T(1)) { return a; }(); struct S { template - static constexpr T t = [](int f = T(7)){return f;}(); // expected-error{{constexpr variable 't' must be initialized by a constant expression}} expected-error{{a lambda expression may not appear inside of a constant expression}} expected-note{{cannot be used in a constant expression}} + static constexpr T t = [](int f = T(7)){return f;}(); // expected-error{{constexpr variable 't' must be initialized by a constant expression}} expected-note{{cannot be used in a constant expression}} }; template diff --git a/test/SemaTemplate/instantiate-static-var.cpp b/test/SemaTemplate/instantiate-static-var.cpp index 648ee4153f..547470db5c 100644 --- a/test/SemaTemplate/instantiate-static-var.cpp +++ b/test/SemaTemplate/instantiate-static-var.cpp @@ -5,7 +5,7 @@ template class X { public: - static const T value = 10 / Divisor; // expected-error{{in-class initializer for static data member is not a constant expression}} + static const T value = 10 / Divisor; // expected-error{{in-class initializer for static data member is not a constant expression}} expected-warning {{division by zero}} }; int array1[X::value == 5? 1 : -1];