Author: rsmith Date: Fri Jun 24 19:15:56 2016 New Revision: 273754 URL: http://llvm.org/viewvc/llvm-project?rev=273754&view=rev Log: Implement C++17 P0386R2, inline variables. (The 'inline' specifier gives a variable weak discardable linkage and partially-ordered initialization, and is implied for constexpr static data members.)
Added: cfe/trunk/test/CXX/basic/basic.def/p2.cpp cfe/trunk/test/CXX/basic/basic.def/p4.cpp cfe/trunk/test/CXX/class/class.static/class.static.data/p2.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p1.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p5.cpp cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp Modified: cfe/trunk/include/clang/AST/Decl.h cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Sema/SemaInternal.h cfe/trunk/lib/AST/ASTContext.cpp cfe/trunk/lib/AST/ASTDumper.cpp cfe/trunk/lib/AST/Decl.cpp cfe/trunk/lib/CodeGen/CodeGenModule.cpp cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp cfe/trunk/lib/Sema/Sema.cpp cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaDeclCXX.cpp cfe/trunk/lib/Sema/SemaDeclObjC.cpp cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp cfe/trunk/lib/Serialization/ASTReaderDecl.cpp cfe/trunk/lib/Serialization/ASTWriterDecl.cpp cfe/trunk/test/CXX/class/class.static/class.static.data/p3.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp cfe/trunk/test/CXX/temp/temp.spec/temp.inst/p1.cpp cfe/trunk/test/Sema/inline.c cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/AST/Decl.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Decl.h (original) +++ cfe/trunk/include/clang/AST/Decl.h Fri Jun 24 19:15:56 2016 @@ -881,6 +881,12 @@ protected: /// variable; see isARCPseudoStrong() for details. unsigned ARCPseudoStrong : 1; + /// \brief Whether this variable is (C++1z) inline. + unsigned IsInline : 1; + + /// \brief Whether this variable has (C++1z) inline explicitly specified. + unsigned IsInlineSpecified : 1; + /// \brief Whether this variable is (C++0x) constexpr. unsigned IsConstexpr : 1; @@ -1102,9 +1108,6 @@ public: /// definition of a static data member. bool isOutOfLine() const override; - /// \brief If this is a static data member, find its out-of-line definition. - VarDecl *getOutOfLineDefinition(); - /// isFileVarDecl - Returns true for file scoped variable declaration. bool isFileVarDecl() const { Kind K = getKind(); @@ -1250,6 +1253,24 @@ public: NonParmVarDeclBits.ARCPseudoStrong = ps; } + /// Whether this variable is (C++1z) inline. + bool isInline() const { + return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.IsInline; + } + bool isInlineSpecified() const { + return isa<ParmVarDecl>(this) ? false + : NonParmVarDeclBits.IsInlineSpecified; + } + void setInlineSpecified() { + assert(!isa<ParmVarDecl>(this)); + NonParmVarDeclBits.IsInline = true; + NonParmVarDeclBits.IsInlineSpecified = true; + } + void setImplicitlyInline() { + assert(!isa<ParmVarDecl>(this)); + NonParmVarDeclBits.IsInline = true; + } + /// Whether this variable is (C++11) constexpr. bool isConstexpr() const { return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.IsConstexpr; Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jun 24 19:15:56 2016 @@ -339,12 +339,16 @@ def err_language_linkage_spec_not_ascii def warn_use_out_of_scope_declaration : Warning< "use of out-of-scope declaration of %0">; def err_inline_non_function : Error< - "'inline' can only appear on functions">; + "'inline' can only appear on functions%select{| and non-local variables}0">; def err_noreturn_non_function : Error< "'_Noreturn' can only appear on functions">; def warn_qual_return_type : Warning< "'%0' type qualifier%s1 on return type %plural{1:has|:have}1 no effect">, InGroup<IgnoredQualifiers>, DefaultIgnore; +def warn_deprecated_redundant_constexpr_static_def : Warning< + "out-of-line definition of constexpr static data member is redundant " + "in C++17 and is deprecated">, + InGroup<Deprecated>, DefaultIgnore; def warn_decl_shadow : Warning<"declaration shadows a %select{" @@ -1086,6 +1090,12 @@ def warn_cxx14_compat_static_assert_no_m "static_assert with no message is incompatible with C++ standards before C++1z">, DefaultIgnore, InGroup<CXXPre1zCompat>; +def ext_inline_variable : ExtWarn< + "inline variables are a C++1z extension">, InGroup<CXX1z>; +def warn_cxx14_compat_inline_variable : Warning< + "inline variables are incompatible with C++ standards before C++1z">, + DefaultIgnore, InGroup<CXXPre1zCompat>; + def warn_inline_namespace_reopened_noninline : Warning< "inline namespace cannot be reopened as a non-inline namespace">; def err_inline_namespace_mismatch : Error< @@ -4305,6 +4315,7 @@ def warn_undefined_internal : Warning< InGroup<DiagGroup<"undefined-internal">>; def warn_undefined_inline : Warning<"inline function %q0 is not defined">, InGroup<DiagGroup<"undefined-inline">>; +def err_undefined_inline_var : Error<"inline variable %q0 is not defined">; def note_used_here : Note<"used here">; def err_internal_linkage_redeclaration : Error< Modified: cfe/trunk/include/clang/Sema/SemaInternal.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/SemaInternal.h?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/SemaInternal.h (original) +++ cfe/trunk/include/clang/Sema/SemaInternal.h Fri Jun 24 19:15:56 2016 @@ -73,10 +73,11 @@ inline void MarkVarDeclODRUsed(VarDecl * // Keep track of used but undefined variables. // FIXME: We shouldn't suppress this warning for static data members. if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly && - !Var->isExternallyVisible() && - !(Var->isStaticDataMember() && Var->hasInit())) { - SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()]; - if (old.isInvalid()) old = Loc; + (!Var->isExternallyVisible() || Var->isInline()) && + !(Var->isStaticDataMember() && Var->hasInit())) { + SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()]; + if (old.isInvalid()) + old = Loc; } QualType CaptureType, DeclRefType; SemaRef.tryCaptureVariable(Var, Loc, Sema::TryCapture_Implicit, Modified: cfe/trunk/lib/AST/ASTContext.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/AST/ASTContext.cpp (original) +++ cfe/trunk/lib/AST/ASTContext.cpp Fri Jun 24 19:15:56 2016 @@ -8486,15 +8486,19 @@ static GVALinkage basicGVALinkageForVari if (Context.isMSStaticDataMemberInlineDefinition(VD)) return GVA_DiscardableODR; + GVALinkage StrongLinkage = GVA_StrongExternal; + if (VD->isInline()) + StrongLinkage = GVA_DiscardableODR; + switch (VD->getTemplateSpecializationKind()) { case TSK_Undeclared: - return GVA_StrongExternal; + return StrongLinkage; case TSK_ExplicitSpecialization: return Context.getTargetInfo().getCXXABI().isMicrosoft() && VD->isStaticDataMember() ? GVA_StrongODR - : GVA_StrongExternal; + : StrongLinkage; case TSK_ExplicitInstantiationDefinition: return GVA_StrongODR; Modified: cfe/trunk/lib/AST/ASTDumper.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTDumper.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/AST/ASTDumper.cpp (original) +++ cfe/trunk/lib/AST/ASTDumper.cpp Fri Jun 24 19:15:56 2016 @@ -1196,6 +1196,10 @@ void ASTDumper::VisitVarDecl(const VarDe OS << " __module_private__"; if (D->isNRVOVariable()) OS << " nrvo"; + if (D->isInline()) + OS << " inline"; + if (D->isConstexpr()) + OS << " constexpr"; if (D->hasInit()) { switch (D->getInitStyle()) { case VarDecl::CInit: OS << " cinit"; break; Modified: cfe/trunk/lib/AST/Decl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/AST/Decl.cpp (original) +++ cfe/trunk/lib/AST/Decl.cpp Fri Jun 24 19:15:56 2016 @@ -592,12 +592,14 @@ static LinkageInfo getLVForNamespaceScop if (Var->getStorageClass() == SC_Static) return LinkageInfo::internal(); - // - a non-volatile object or reference that is explicitly declared const - // or constexpr and neither explicitly declared extern nor previously - // declared to have external linkage; or (there is no equivalent in C99) + // - a non-inline, non-volatile object or reference that is explicitly + // declared const or constexpr and neither explicitly declared extern + // nor previously declared to have external linkage; or (there is no + // equivalent in C99) if (Context.getLangOpts().CPlusPlus && Var->getType().isConstQualified() && - !Var->getType().isVolatileQualified()) { + !Var->getType().isVolatileQualified() && + !Var->isInline()) { const VarDecl *PrevVar = Var->getPreviousDecl(); if (PrevVar) return getLVForDecl(PrevVar, computation); @@ -1912,7 +1914,9 @@ VarDecl::isThisDeclarationADefinition(AS // C++ [basic.def]p2: // A declaration is a definition unless [...] it contains the 'extern' // specifier or a linkage-specification and neither an initializer [...], - // it declares a static data member in a class declaration [...]. + // it declares a non-inline static data member in a class declaration [...], + // it declares a static data member outside a class definition and the variable + // was defined within the class with the constexpr specifier [...], // C++1y [temp.expl.spec]p15: // An explicit specialization of a static data member or an explicit // specialization of a static data member template is a definition if the @@ -1922,6 +1926,8 @@ VarDecl::isThisDeclarationADefinition(AS // a static data member template outside the containing class? if (isStaticDataMember()) { if (isOutOfLine() && + !(getCanonicalDecl()->isInline() && + getCanonicalDecl()->isConstexpr()) && (hasInit() || // If the first declaration is out-of-line, this may be an // instantiation of an out-of-line partial specialization of a variable @@ -1932,6 +1938,8 @@ VarDecl::isThisDeclarationADefinition(AS TSK_ExplicitSpecialization) || isa<VarTemplatePartialSpecializationDecl>(this))) return Definition; + else if (!isOutOfLine() && isInline()) + return Definition; else return DeclarationOnly; } @@ -2072,18 +2080,6 @@ bool VarDecl::isOutOfLine() const { return false; } -VarDecl *VarDecl::getOutOfLineDefinition() { - if (!isStaticDataMember()) - return nullptr; - - for (auto RD : redecls()) { - if (RD->getLexicalDeclContext()->isFileContext()) - return RD; - } - - return nullptr; -} - void VarDecl::setInit(Expr *I) { if (auto *Eval = Init.dyn_cast<EvaluatedStmt *>()) { Eval->~EvaluatedStmt(); Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original) +++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Fri Jun 24 19:15:56 2016 @@ -3746,6 +3746,12 @@ void CodeGenModule::EmitTopLevelDecl(Dec case Decl::Namespace: EmitNamespace(cast<NamespaceDecl>(D)); break; + case Decl::CXXRecord: + // Emit any static data members, they may be definitions. + for (auto *I : cast<CXXRecordDecl>(D)->decls()) + if (isa<VarDecl>(I) || isa<CXXRecordDecl>(I)) + EmitTopLevelDecl(I); + break; // No code generation needed. case Decl::UsingShadow: case Decl::ClassTemplate: Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original) +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Fri Jun 24 19:15:56 2016 @@ -1905,10 +1905,18 @@ void ItaniumCXXABI::EmitGuardedInit(Code bool shouldPerformInit) { CGBuilderTy &Builder = CGF.Builder; - // We only need to use thread-safe statics for local non-TLS variables; - // global initialization is always single-threaded. + // Inline variables that weren't instantiated from variable templates have + // partially-ordered initialization within their translation unit. + bool NonTemplateInline = + D.isInline() && + !isTemplateInstantiation(D.getTemplateSpecializationKind()); + + // We only need to use thread-safe statics for local non-TLS variables and + // inline variables; other global initialization is always single-threaded + // or (through lazy dynamic loading in multiple threads) unsequenced. bool threadsafe = getContext().getLangOpts().ThreadsafeStatics && - D.isLocalVarDecl() && !D.getTLSKind(); + (D.isLocalVarDecl() || NonTemplateInline) && + !D.getTLSKind(); // If we have a global variable with internal linkage and thread-safe statics // are disabled, we can just let the guard variable be of type i8. @@ -1962,7 +1970,11 @@ void ItaniumCXXABI::EmitGuardedInit(Code if (!D.isLocalVarDecl() && C && CGM.getTarget().getTriple().isOSBinFormatELF()) { guard->setComdat(C); - CGF.CurFn->setComdat(C); + // An inline variable's guard function is run from the per-TU + // initialization function, not via a dedicated global ctor function, so + // we can't put it in a comdat. + if (!NonTemplateInline) + CGF.CurFn->setComdat(C); } else if (CGM.supportsCOMDAT() && guard->isWeakForLinker()) { guard->setComdat(CGM.getModule().getOrInsertComdat(guard->getName())); } Modified: cfe/trunk/lib/Sema/Sema.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/Sema/Sema.cpp (original) +++ cfe/trunk/lib/Sema/Sema.cpp Fri Jun 24 19:15:56 2016 @@ -469,7 +469,8 @@ static bool ShouldRemoveFromUnused(Sema return false; } -/// Obtains a sorted list of functions that are undefined but ODR-used. +/// Obtains a sorted list of functions and variables that are undefined but +/// ODR-used. void Sema::getUndefinedButUsed( SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined) { for (const auto &UndefinedUse : UndefinedButUsed) { @@ -488,9 +489,10 @@ void Sema::getUndefinedButUsed( !FD->getMostRecentDecl()->isInlined()) continue; } else { - if (cast<VarDecl>(ND)->hasDefinition() != VarDecl::DeclarationOnly) + auto *VD = cast<VarDecl>(ND); + if (VD->hasDefinition() != VarDecl::DeclarationOnly) continue; - if (ND->isExternallyVisible()) + if (VD->isExternallyVisible() && !VD->getMostRecentDecl()->isInline()) continue; } @@ -522,10 +524,15 @@ static void checkUndefinedButUsed(Sema & if (!ND->isExternallyVisible()) { S.Diag(ND->getLocation(), diag::warn_undefined_internal) << isa<VarDecl>(ND) << ND; - } else { - assert(cast<FunctionDecl>(ND)->getMostRecentDecl()->isInlined() && + } else if (auto *FD = dyn_cast<FunctionDecl>(ND)) { + assert(FD->getMostRecentDecl()->isInlined() && "used object requires definition but isn't inline or internal?"); + // FIXME: This is ill-formed; we should reject. S.Diag(ND->getLocation(), diag::warn_undefined_inline) << ND; + } else { + assert(cast<VarDecl>(ND)->getMostRecentDecl()->isInline() && + "used var requires definition but isn't inline or internal?"); + S.Diag(ND->getLocation(), diag::err_undefined_inline_var) << ND; } if (I->second.isValid()) S.Diag(I->second, diag::note_used_here); Modified: cfe/trunk/lib/Sema/SemaDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Jun 24 19:15:56 2016 @@ -1457,6 +1457,9 @@ bool Sema::ShouldWarnIfUnusedFileScopedD if (VD->isStaticDataMember() && VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) return false; + + if (VD->isInline() && !isMainFileLoc(*this, VD->getLocation())) + return false; } else { return false; } @@ -3621,6 +3624,23 @@ void Sema::MergeVarDecl(VarDecl *New, Lo return New->setInvalidDecl(); } + if (New->isInline() && !Old->getMostRecentDecl()->isInline()) { + if (VarDecl *Def = Old->getDefinition()) { + // C++1z [dcl.fcn.spec]p4: + // If the definition of a variable appears in a translation unit before + // its first declaration as inline, the program is ill-formed. + Diag(New->getLocation(), diag::err_inline_decl_follows_def) << New; + Diag(Def->getLocation(), diag::note_previous_definition); + } + } + + // If this redeclaration makes the function inline, we may need to add it to + // UndefinedButUsed. + if (!Old->isInline() && New->isInline() && Old->isUsed(false) && + !Old->getDefinition() && !New->isThisDeclarationADefinition()) + UndefinedButUsed.insert(std::make_pair(Old->getCanonicalDecl(), + SourceLocation())); + if (New->getTLSKind() != Old->getTLSKind()) { if (!Old->getTLSKind()) { Diag(New->getLocation(), diag::err_thread_non_thread) << New->getDeclName(); @@ -3652,6 +3672,12 @@ void Sema::MergeVarDecl(VarDecl *New, Lo New->getDeclContext()->isDependentContext())) { // The previous definition is hidden, and multiple definitions are // permitted (in separate TUs). Form another definition of it. + } else if (Old->isStaticDataMember() && + Old->getCanonicalDecl()->isInline() && + Old->getCanonicalDecl()->isConstexpr()) { + // This definition won't be a definition any more once it's been merged. + Diag(New->getLocation(), + diag::warn_deprecated_redundant_constexpr_static_def); } else { Diag(New->getLocation(), diag::err_redefinition) << New; Diag(Def->getLocation(), diag::note_previous_definition); @@ -3680,6 +3706,9 @@ void Sema::MergeVarDecl(VarDecl *New, Lo New->setAccess(Old->getAccess()); if (NewTemplate) NewTemplate->setAccess(New->getAccess()); + + if (Old->isInline()) + New->setImplicitlyInline(); } /// ParsedFreeStandingDeclSpec - This method is invoked when a declspec with @@ -3836,6 +3865,10 @@ Sema::ParsedFreeStandingDeclSpec(Scope * << DS.getSourceRange(); } + if (DS.isInlineSpecified()) + Diag(DS.getInlineSpecLoc(), diag::err_inline_non_function) + << getLangOpts().CPlusPlus1z; + if (DS.isConstexprSpecified()) { // C++0x [dcl.constexpr]p1: constexpr can only be applied to declarations // and definitions of functions and variables. @@ -5261,11 +5294,7 @@ NamedDecl *Sema::findLocallyScopedExtern /// does not identify a function. void Sema::DiagnoseFunctionSpecifiers(const DeclSpec &DS) { // FIXME: We should probably indicate the identifier in question to avoid - // confusion for constructs like "inline int a(), b;" - if (DS.isInlineSpecified()) - Diag(DS.getInlineSpecLoc(), - diag::err_inline_non_function); - + // confusion for constructs like "virtual int a(), b;" if (DS.isVirtualSpecified()) Diag(DS.getVirtualSpecLoc(), diag::err_virtual_non_function); @@ -5294,6 +5323,9 @@ Sema::ActOnTypedefDeclarator(Scope* S, D DiagnoseFunctionSpecifiers(D.getDeclSpec()); + if (D.getDeclSpec().isInlineSpecified()) + Diag(D.getDeclSpec().getInlineSpecLoc(), diag::err_inline_non_function) + << getLangOpts().CPlusPlus1z; if (D.getDeclSpec().isConstexprSpecified()) Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) << 1; @@ -6098,8 +6130,14 @@ Sema::ActOnVariableDeclarator(Scope *S, NewVD->setTemplateParameterListsInfo( Context, TemplateParamLists.drop_back(VDTemplateParamLists)); - if (D.getDeclSpec().isConstexprSpecified()) + if (D.getDeclSpec().isConstexprSpecified()) { NewVD->setConstexpr(true); + // C++1z [dcl.spec.constexpr]p1: + // A static data member declared with the constexpr specifier is + // implicitly an inline variable. + if (NewVD->isStaticDataMember() && getLangOpts().CPlusPlus1z) + NewVD->setImplicitlyInline(); + } if (D.getDeclSpec().isConceptSpecified()) { if (VarTemplateDecl *VTD = NewVD->getDescribedVarTemplate()) @@ -6142,6 +6180,20 @@ Sema::ActOnVariableDeclarator(Scope *S, } } + if (D.getDeclSpec().isInlineSpecified()) { + if (CurContext->isFunctionOrMethod()) { + // 'inline' is not allowed on block scope variable declaration. + Diag(D.getDeclSpec().getInlineSpecLoc(), + diag::err_inline_declaration_block_scope) << Name + << FixItHint::CreateRemoval(D.getDeclSpec().getInlineSpecLoc()); + } else { + Diag(D.getDeclSpec().getInlineSpecLoc(), + getLangOpts().CPlusPlus1z ? diag::warn_cxx14_compat_inline_variable + : diag::ext_inline_variable); + NewVD->setInlineSpecified(); + } + } + // Set the lexical context. If the declarator has a C++ scope specifier, the // lexical context will be different from the semantic context. NewVD->setLexicalDeclContext(CurContext); @@ -9759,7 +9811,7 @@ void Sema::AddInitializerToDecl(Decl *Re diag::ext_aggregate_init_not_constant) << Culprit->getSourceRange(); } - } else if (VDecl->isStaticDataMember() && + } else if (VDecl->isStaticDataMember() && !VDecl->isInline() && VDecl->getLexicalDeclContext()->isRecord()) { // This is an in-class initialization for a static data member, e.g., // @@ -9773,8 +9825,8 @@ void Sema::AddInitializerToDecl(Decl *Re // const enumeration type, see 9.4.2. // // C++11 [class.static.data]p3: - // If a non-volatile const static data member is of integral or - // enumeration type, its declaration in the class definition can + // If a non-volatile non-inline const static data member is of integral + // or enumeration type, its declaration in the class definition can // specify a brace-or-equal-initializer in which every initalizer-clause // that is an assignment-expression is a constant expression. A static // data member of literal type can be declared in the class definition @@ -9957,14 +10009,21 @@ void Sema::ActOnUninitializedDecl(Decl * // the definition of a variable [...] or the declaration of a static data // member. if (Var->isConstexpr() && !Var->isThisDeclarationADefinition()) { - if (Var->isStaticDataMember()) - Diag(Var->getLocation(), - diag::err_constexpr_static_mem_var_requires_init) - << Var->getDeclName(); - else + if (Var->isStaticDataMember()) { + // C++1z removes the relevant rule; the in-class declaration is always + // a definition there. + if (!getLangOpts().CPlusPlus1z) { + Diag(Var->getLocation(), + diag::err_constexpr_static_mem_var_requires_init) + << Var->getDeclName(); + Var->setInvalidDecl(); + return; + } + } else { Diag(Var->getLocation(), diag::err_invalid_constexpr_var_decl); - Var->setInvalidDecl(); - return; + Var->setInvalidDecl(); + return; + } } // C++ Concepts TS [dcl.spec.concept]p1: [...] A variable template @@ -10754,6 +10813,9 @@ Decl *Sema::ActOnParamDeclarator(Scope * if (DeclSpec::TSCS TSCS = DS.getThreadStorageClassSpec()) Diag(DS.getThreadStorageClassSpecLoc(), diag::err_invalid_thread) << DeclSpec::getSpecifierName(TSCS); + if (DS.isInlineSpecified()) + Diag(DS.getInlineSpecLoc(), diag::err_inline_non_function) + << getLangOpts().CPlusPlus1z; if (DS.isConstexprSpecified()) Diag(DS.getConstexprSpecLoc(), diag::err_invalid_constexpr) << 0; @@ -13327,6 +13389,9 @@ FieldDecl *Sema::HandleField(Scope *S, R DiagnoseFunctionSpecifiers(D.getDeclSpec()); + if (D.getDeclSpec().isInlineSpecified()) + Diag(D.getDeclSpec().getInlineSpecLoc(), diag::err_inline_non_function) + << getLangOpts().CPlusPlus1z; if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), diag::err_invalid_thread) Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Jun 24 19:15:56 2016 @@ -13995,6 +13995,9 @@ MSPropertyDecl *Sema::HandleMSProperty(S DiagnoseFunctionSpecifiers(D.getDeclSpec()); + if (D.getDeclSpec().isInlineSpecified()) + Diag(D.getDeclSpec().getInlineSpecLoc(), diag::err_inline_non_function) + << getLangOpts().CPlusPlus1z; if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), diag::err_invalid_thread) Modified: cfe/trunk/lib/Sema/SemaDeclObjC.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclObjC.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclObjC.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp Fri Jun 24 19:15:56 2016 @@ -4612,6 +4612,9 @@ Decl *Sema::ActOnObjCExceptionDecl(Scope Diag(DS.getStorageClassSpecLoc(), diag::err_storage_spec_on_catch_parm) << DeclSpec::getSpecifierName(SCS); } + if (DS.isInlineSpecified()) + Diag(DS.getInlineSpecLoc(), diag::err_inline_non_function) + << getLangOpts().CPlusPlus1z; if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), diag::err_invalid_thread) Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Fri Jun 24 19:15:56 2016 @@ -2520,8 +2520,7 @@ Sema::InstantiateClassMembers(SourceLoca // specialization and is only an explicit instantiation definition // of members whose definition is visible at the point of // instantiation. - if (!Var->getInstantiatedFromStaticDataMember() - ->getOutOfLineDefinition()) + if (!Var->getInstantiatedFromStaticDataMember()->getDefinition()) continue; Var->setTemplateSpecializationKind(TSK, PointOfInstantiation); Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Fri Jun 24 19:15:56 2016 @@ -3881,11 +3881,12 @@ void Sema::BuildVariableInstantiation( Context.setManglingNumber(NewVar, Context.getManglingNumber(OldVar)); Context.setStaticLocalNumber(NewVar, Context.getStaticLocalNumber(OldVar)); - // Delay instantiation of the initializer for variable templates until a - // definition of the variable is needed. We need it right away if the type - // contains 'auto'. + // Delay instantiation of the initializer for variable templates or inline + // static data members until a definition of the variable is needed. We need + // it right away if the type contains 'auto'. if ((!isa<VarTemplateSpecializationDecl>(NewVar) && - !InstantiatingVarTemplate) || + !InstantiatingVarTemplate && + !(OldVar->isInline() && OldVar->isThisDeclarationADefinition())) || NewVar->getType()->isUndeducedType()) InstantiateVariableInitializer(NewVar, OldVar, TemplateArgs); @@ -3901,6 +3902,13 @@ void Sema::BuildVariableInstantiation( void Sema::InstantiateVariableInitializer( VarDecl *Var, VarDecl *OldVar, const MultiLevelTemplateArgumentList &TemplateArgs) { + // We propagate the 'inline' flag with the initializer, because it + // would otherwise imply that the variable is a definition for a + // non-static data member. + if (OldVar->isInlineSpecified()) + Var->setInlineSpecified(); + else if (OldVar->isInline()) + Var->setImplicitlyInline(); if (Var->getAnyInitializer()) // We already have an initializer in the class. @@ -4083,7 +4091,7 @@ void Sema::InstantiateVariableDefinition assert(PatternDecl && "data member was not instantiated from a template?"); assert(PatternDecl->isStaticDataMember() && "not a static data member?"); - Def = PatternDecl->getOutOfLineDefinition(); + Def = PatternDecl->getDefinition(); } // FIXME: Check that the definition is visible before trying to instantiate @@ -4181,11 +4189,16 @@ void Sema::InstantiateVariableDefinition LocalInstantiationScope Local(*this); VarDecl *OldVar = Var; - if (!VarSpec) + if (Def->isStaticDataMember() && !Def->isOutOfLine()) { + // We're instantiating an inline static data member whose definition was + // provided inside the class. + // FIXME: Update record? + InstantiateVariableInitializer(Var, Def, TemplateArgs); + } else if (!VarSpec) { Var = cast_or_null<VarDecl>(SubstDecl(Def, Var->getDeclContext(), TemplateArgs)); - else if (Var->isStaticDataMember() && - Var->getLexicalDeclContext()->isRecord()) { + } else if (Var->isStaticDataMember() && + Var->getLexicalDeclContext()->isRecord()) { // We need to instantiate the definition of a static data member template, // and all we have is the in-class declaration of it. Instantiate a separate // declaration of the definition. Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original) +++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Fri Jun 24 19:15:56 2016 @@ -1217,6 +1217,8 @@ ASTDeclReader::RedeclarableResult ASTDec VD->NonParmVarDeclBits.NRVOVariable = Record[Idx++]; VD->NonParmVarDeclBits.CXXForRangeDecl = Record[Idx++]; VD->NonParmVarDeclBits.ARCPseudoStrong = Record[Idx++]; + VD->NonParmVarDeclBits.IsInline = Record[Idx++]; + VD->NonParmVarDeclBits.IsInlineSpecified = Record[Idx++]; VD->NonParmVarDeclBits.IsConstexpr = Record[Idx++]; VD->NonParmVarDeclBits.IsInitCapture = Record[Idx++]; VD->NonParmVarDeclBits.PreviousDeclInSameBlockScope = Record[Idx++]; Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original) +++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Fri Jun 24 19:15:56 2016 @@ -895,6 +895,8 @@ void ASTDeclWriter::VisitVarDecl(VarDecl Record.push_back(D->isNRVOVariable()); Record.push_back(D->isCXXForRangeDecl()); Record.push_back(D->isARCPseudoStrong()); + Record.push_back(D->isInline()); + Record.push_back(D->isInlineSpecified()); Record.push_back(D->isConstexpr()); Record.push_back(D->isInitCapture()); Record.push_back(D->isPreviousDeclInSameBlockScope()); @@ -941,6 +943,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl D->getInit() == nullptr && !isa<ParmVarDecl>(D) && !isa<VarTemplateSpecializationDecl>(D) && + !D->isInline() && !D->isConstexpr() && !D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() && @@ -1916,6 +1919,8 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong + Abv->Add(BitCodeAbbrevOp(0)); // isInline + Abv->Add(BitCodeAbbrevOp(0)); // isInlineSpecified Abv->Add(BitCodeAbbrevOp(0)); // isConstexpr Abv->Add(BitCodeAbbrevOp(0)); // isInitCapture Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope Added: cfe/trunk/test/CXX/basic/basic.def/p2.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.def/p2.cpp?rev=273754&view=auto ============================================================================== --- cfe/trunk/test/CXX/basic/basic.def/p2.cpp (added) +++ cfe/trunk/test/CXX/basic/basic.def/p2.cpp Fri Jun 24 19:15:56 2016 @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s -Wdeprecated + +namespace { + struct A { + static constexpr int n = 0; + }; + const int A::n; // expected-warning {{deprecated}} +} Added: cfe/trunk/test/CXX/basic/basic.def/p4.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.def/p4.cpp?rev=273754&view=auto ============================================================================== --- cfe/trunk/test/CXX/basic/basic.def/p4.cpp (added) +++ cfe/trunk/test/CXX/basic/basic.def/p4.cpp Fri Jun 24 19:15:56 2016 @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +inline int f(); // expected-warning {{inline function 'f' is not defined}} +extern inline int n; // expected-error {{inline variable 'n' is not defined}} + +int use = f() + n; // expected-note 2{{used here}} Added: cfe/trunk/test/CXX/class/class.static/class.static.data/p2.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.static/class.static.data/p2.cpp?rev=273754&view=auto ============================================================================== --- cfe/trunk/test/CXX/class/class.static/class.static.data/p2.cpp (added) +++ cfe/trunk/test/CXX/class/class.static/class.static.data/p2.cpp Fri Jun 24 19:15:56 2016 @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +struct X { + static struct A a; + static inline struct B b; // expected-error {{incomplete type}} expected-note {{forward decl}} + static inline struct C c = {}; // expected-error {{incomplete type}} expected-note {{forward decl}} +}; Modified: cfe/trunk/test/CXX/class/class.static/class.static.data/p3.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.static/class.static.data/p3.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/test/CXX/class/class.static/class.static.data/p3.cpp (original) +++ cfe/trunk/test/CXX/class/class.static/class.static.data/p3.cpp Fri Jun 24 19:15:56 2016 @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s struct NonLit { // expected-note 3{{no constexpr constructors}} NonLit(); @@ -6,7 +7,7 @@ struct NonLit { // expected-note 3{{no c struct S { static constexpr int a = 0; - static constexpr int b; // expected-error {{declaration of constexpr static data member 'b' requires an initializer}} + static constexpr int b; // expected-error {{initializ}} expected-note 0-1{{previous}} static constexpr int c = 0; static const int d; @@ -16,19 +17,27 @@ struct S { static const double f = 0.0; // expected-error {{requires 'constexpr' specifier}} expected-note {{add 'constexpr'}} static char *const g = 0; // expected-error {{requires 'constexpr' specifier}} static const NonLit h = NonLit(); // expected-error {{must be initialized out of line}} + + static inline int i; // expected-note {{previous}} expected-warning 0-1{{extension}} + static inline int j; // expected-note {{previous}} expected-warning 0-1{{extension}} + static constexpr int k = 0; }; constexpr int S::a; -constexpr int S::b = 0; +constexpr int S::b = 0; // expected-error 0-1{{redefinition}} const int S::c; constexpr int S::d = 0; constexpr int S::d2; +int S::i; // expected-error {{redefinition}} +int S::j; // expected-error {{redefinition}} +const int S::k; // ok (deprecated) + template<typename T> struct U { static constexpr int a = 0; - static constexpr int b; // expected-error {{declaration of constexpr static data member 'b' requires an initializer}} + static constexpr int b; // expected-error {{initializ}} static constexpr NonLit h = NonLit(); // expected-error {{cannot have non-literal type 'const NonLit'}} static constexpr T c = T(); // expected-error {{cannot have non-literal type}} static const T d; Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp (original) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp Fri Jun 24 19:15:56 2016 @@ -1,4 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s struct notlit { // expected-note {{not literal because}} notlit() {} @@ -26,7 +28,12 @@ void f2(constexpr int i) {} // expected- // non-static member struct s2 { constexpr int mi1; // expected-error {{non-static data member cannot be constexpr; did you intend to make it const?}} - static constexpr int mi2; // expected-error {{requires an initializer}} + static constexpr int mi2; +#if __cplusplus <= 201402L + // expected-error@-2 {{requires an initializer}} +#else + // expected-error@-4 {{default initialization of an object of const}} +#endif mutable constexpr int mi3 = 3; // expected-error-re {{non-static data member cannot be constexpr{{$}}}} expected-error {{'mutable' and 'const' cannot be mixed}} }; // typedef @@ -71,7 +78,7 @@ struct ConstexprDtor { template <typename T> constexpr T ft(T t) { return t; } template <typename T> T gt(T t) { return t; } struct S { - template<typename T> constexpr T f(); // expected-warning {{C++14}} + template<typename T> constexpr T f(); // expected-warning 0-1{{C++14}} expected-note 0-1{{candidate}} template <typename T> T g() const; // expected-note-re {{candidate template ignored: could not match 'T (){{( __attribute__\(\(thiscall\)\))?}} const' against 'char (){{( __attribute__\(\(thiscall\)\))?}}'}} }; @@ -82,7 +89,15 @@ template <> char ft(char c) { return c; template <> constexpr char ft(char nl); // expected-error {{constexpr declaration of 'ft<char>' follows non-constexpr declaration}} template <> constexpr int gt(int nl) { return nl; } template <> notlit S::f() const { return notlit(); } -template <> constexpr int S::g() { return 0; } // expected-note {{previous}} expected-warning {{C++14}} +#if __cplusplus >= 201402L +// expected-error@-2 {{no function template matches}} +#endif +template <> constexpr int S::g() { return 0; } // expected-note {{previous}} +#if __cplusplus < 201402L +// expected-warning@-2 {{C++14}} +#else +// expected-error@-4 {{does not match any declaration in 'S'}} +#endif template <> int S::g() const; // expected-error {{non-constexpr declaration of 'g<int>' follows constexpr declaration}} // specializations can drop the 'constexpr' but not the implied 'const'. template <> char S::g() { return 0; } // expected-error {{no function template matches}} @@ -123,3 +138,11 @@ int next(constexpr int x) { // expected- } extern constexpr int memsz; // expected-error {{constexpr variable declaration must be a definition}} + +namespace { + struct A { + static constexpr int n = 0; + }; + // FIXME: We should diagnose this prior to C++17. + const int &r = A::n; +} Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p1.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p1.cpp?rev=273754&view=auto ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p1.cpp (added) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p1.cpp Fri Jun 24 19:15:56 2016 @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +inline int f(); // ok +inline int n; // ok + +inline typedef int t; // expected-error {{'inline' can only appear on functions and non-local variables}} +inline struct S {}; // expected-error {{'inline' can only appear on functions and non-local variables}} +inline struct T {} s; // ok Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p5.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p5.cpp?rev=273754&view=auto ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p5.cpp (added) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.inline/p5.cpp Fri Jun 24 19:15:56 2016 @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +void x() { + inline int f(int); // expected-error {{inline declaration of 'f' not allowed in block scope}} + inline int n; // expected-error {{inline declaration of 'n' not allowed in block scope}} + static inline int m; // expected-error {{inline declaration of 'm' not allowed in block scope}} +} + +inline void g(); +struct X { + inline void f(); + // FIXME: This is ill-formed per [dcl.inline]p5. + inline void g(); + inline void h() {} +}; Modified: cfe/trunk/test/CXX/temp/temp.spec/temp.inst/p1.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.spec/temp.inst/p1.cpp?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/test/CXX/temp/temp.spec/temp.inst/p1.cpp (original) +++ cfe/trunk/test/CXX/temp/temp.spec/temp.inst/p1.cpp Fri Jun 24 19:15:56 2016 @@ -54,6 +54,16 @@ namespace ScopedEnum { int test2 = g<int>(); // expected-note {{here}} } +// - static data members +namespace StaticDataMembers { + template<typename T> + struct A { + static const int n = T::error; // expected-error {{has no members}} + static inline int m = T::error; // expected-warning {{extension}} + }; + A<int> ai; // expected-note {{here}} +} + // And it cases the implicit instantiations of the definitions of: // - unscoped member enumerations Added: cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp?rev=273754&view=auto ============================================================================== --- cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp (added) +++ cfe/trunk/test/CodeGenCXX/cxx1z-inline-variables.cpp Fri Jun 24 19:15:56 2016 @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple x86_64-linux-gnu | FileCheck %s + +struct Q { + // CHECK: @_ZN1Q1kE = linkonce_odr constant i32 5, comdat + static constexpr int k = 5; +}; +const int &r = Q::k; + +int f(); + +// const does not imply internal linkage. +// CHECK: @external_inline = linkonce_odr constant i32 5, comdat +inline const int external_inline = 5; +const int &use1 = external_inline; + +// static still does, though. +// CHECK: @_ZL15internal_inline = internal constant i32 5 +static inline const int internal_inline = 5; +const int &use2 = internal_inline; + +int a = f(); +// CHECK: @b = linkonce_odr global i32 0, comdat +// CHECK: @_ZGV1b = linkonce_odr global i64 0, comdat($b) +inline int b = f(); +int c = f(); + +template<typename T> struct X { + static int a; + static inline int b; + static int c; +}; +// CHECK: @_ZN1XIiE1aE = linkonce_odr global i32 10 +// CHECK: @_ZN1XIiE1bE = global i32 20 +// CHECK-NOT: @_ZN1XIiE1cE +template<> inline int X<int>::a = 10; +int &use3 = X<int>::a; +template<> int X<int>::b = 20; +template<> inline int X<int>::c = 30; + +// CHECK-LABEL: define {{.*}}global_var_init +// CHECK: call i32 @_Z1fv + +// CHECK-LABEL: define {{.*}}global_var_init +// CHECK-NOT: comdat +// CHECK-SAME: {{$}} +// CHECK: load atomic {{.*}} acquire +// CHECK: br +// CHECK: __cxa_guard_acquire(i64* @_ZGV1b) +// CHECK: br +// CHECK: call i32 @_Z1fv +// CHECK: __cxa_guard_release(i64* @_ZGV1b) + +// CHECK-LABEL: define {{.*}}global_var_init +// CHECK: call i32 @_Z1fv + +template<typename T> inline int d = f(); +int e = d<int>; + +// CHECK-LABEL: define {{.*}}global_var_init{{.*}}comdat +// CHECK: _ZGV1dIiE +// CHECK-NOT: __cxa_guard_acquire(i64* @_ZGV1b) +// CHECK: call i32 @_Z1fv +// CHECK-NOT: __cxa_guard_release(i64* @_ZGV1b) Modified: cfe/trunk/test/Sema/inline.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/inline.c?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/test/Sema/inline.c (original) +++ cfe/trunk/test/Sema/inline.c Fri Jun 24 19:15:56 2016 @@ -49,7 +49,7 @@ inline int useConst () { #include "inline.c" // Check that we don't allow illegal uses of inline -inline int a; // expected-error{{'inline' can only appear on functions}} +inline int a; // expected-warning{{inline variables are a C++1z extension}} typedef inline int b; // expected-error{{'inline' can only appear on functions}} int d(inline int a); // expected-error{{'inline' can only appear on functions}} Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=273754&r1=273753&r2=273754&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Fri Jun 24 19:15:56 2016 @@ -715,7 +715,7 @@ as the draft C++1z standard evolves.</p> <tr> <td>Inline variables</td> <td><a href="http://wg21.link/p0386r2">P0386R2</a></td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> <tr> <td>Structured bindings</td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits