Author: rsmith Date: Wed Sep 4 13:30:37 2019 New Revision: 370972 URL: http://llvm.org/viewvc/llvm-project?rev=370972&view=rev Log: [c++20] P1143R2: Add support for the C++20 'constinit' keyword.
This is mostly the same as the [[clang::require_constant_initialization]] attribute, but has a couple of additional syntactic and semantic restrictions. In passing, I added a warning for the attribute form being added after we have already seen the initialization of the variable (but before we see the definition); that case previously slipped between the cracks and the attribute was silently ignored. Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp Modified: cfe/trunk/include/clang/AST/Decl.h cfe/trunk/include/clang/Basic/Attr.td cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Basic/Specifiers.h cfe/trunk/include/clang/Basic/TokenKinds.def cfe/trunk/lib/AST/Decl.cpp cfe/trunk/lib/Frontend/InitPreprocessor.cpp cfe/trunk/lib/Parse/ParseDecl.cpp cfe/trunk/lib/Parse/ParseDeclCXX.cpp cfe/trunk/lib/Parse/ParseTentative.cpp cfe/trunk/lib/Sema/DeclSpec.cpp cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaDeclAttr.cpp cfe/trunk/lib/Sema/SemaType.cpp cfe/trunk/test/FixIt/fixit-c++2a.cpp cfe/trunk/test/Lexer/cxx-features.cpp cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp cfe/trunk/test/Misc/pragma-attribute-cxx.cpp cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test cfe/trunk/test/Parser/cxx0x-decl.cpp cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp 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=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Decl.h (original) +++ cfe/trunk/include/clang/AST/Decl.h Wed Sep 4 13:30:37 2019 @@ -1226,6 +1226,14 @@ public: void setInit(Expr *I); + /// Get the initializing declaration of this variable, if any. This is + /// usually the definition, except that for a static data member it can be + /// the in-class declaration. + VarDecl *getInitializingDeclaration(); + const VarDecl *getInitializingDeclaration() const { + return const_cast<VarDecl *>(this)->getInitializingDeclaration(); + } + /// Determine whether this variable's value might be usable in a /// constant expression, according to the relevant language standard. /// This only checks properties of the declaration, and does not check Modified: cfe/trunk/include/clang/Basic/Attr.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/Attr.td (original) +++ cfe/trunk/include/clang/Basic/Attr.td Wed Sep 4 13:30:37 2019 @@ -911,6 +911,17 @@ def Const : InheritableAttr { let Documentation = [Undocumented]; } +def ConstInit : InheritableAttr { + // This attribute does not have a C [[]] spelling because it requires the + // CPlusPlus language option. + let Spellings = [Keyword<"constinit">, + Clang<"require_constant_initialization", 0>]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag>; + let Accessors = [Accessor<"isConstinit", [Keyword<"constinit">]>]; + let Documentation = [RequireConstantInitDocs]; + let LangOpts = [CPlusPlus]; +} + def Constructor : InheritableAttr { let Spellings = [GCC<"constructor">]; let Args = [DefaultIntArgument<"Priority", 65535>]; @@ -1935,15 +1946,6 @@ def ReqdWorkGroupSize : InheritableAttr let Documentation = [Undocumented]; } -def RequireConstantInit : InheritableAttr { - // This attribute does not have a C [[]] spelling because it requires the - // CPlusPlus language option. - let Spellings = [Clang<"require_constant_initialization", 0>]; - let Subjects = SubjectList<[GlobalVar], ErrorDiag>; - let Documentation = [RequireConstantInitDocs]; - let LangOpts = [CPlusPlus]; -} - def WorkGroupSizeHint : InheritableAttr { // Does not have a [[]] spelling because it is an OpenCL-related attribute. let Spellings = [GNU<"work_group_size_hint">]; Modified: cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td Wed Sep 4 13:30:37 2019 @@ -12,6 +12,11 @@ let Component = "Common" in { +// Substitutions. + +def select_constexpr_spec_kind : TextSubstitution< + "%select{<ERROR>|constexpr|consteval|constinit}0">; + // Basic. def fatal_too_many_errors @@ -112,6 +117,10 @@ def err_attribute_not_type_attr : Error< "%0 attribute cannot be applied to types">; def err_enum_template : Error<"enumeration cannot be a template">; +def warn_cxx20_compat_consteval : Warning< + "'consteval' specifier is incompatible with C++ standards before C++20">, + InGroup<CXX2aCompat>, DefaultIgnore; + } let CategoryName = "Nullability Issue" in { @@ -177,9 +186,6 @@ def ext_cxx11_longlong : Extension< def warn_cxx98_compat_longlong : Warning< "'long long' is incompatible with C++98">, InGroup<CXX98CompatPedantic>, DefaultIgnore; -def warn_cxx20_compat_consteval : Warning< - "consteval is incompatible with C++ standards before C++20">, - InGroup<CXX2aCompat>, DefaultIgnore; def err_integer_literal_too_large : Error< "integer literal is too large to be represented in any %select{signed |}0" "integer type">; Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Wed Sep 4 13:30:37 2019 @@ -358,7 +358,8 @@ def err_typename_invalid_storageclass : def err_typename_invalid_functionspec : Error< "type name does not allow function specifier to be specified">; def err_typename_invalid_constexpr : Error< - "type name does not allow %select{constexpr|consteval}0 specifier to be specified">; + "type name does not allow %sub{select_constexpr_spec_kind}0 specifier " + "to be specified">; def err_typename_identifiers_only : Error< "typename is allowed for identifiers only">; Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Sep 4 13:30:37 2019 @@ -2343,18 +2343,18 @@ def warn_cxx14_compat_constexpr_not_cons "in C++14; add 'const' to avoid a change in behavior">, InGroup<DiagGroup<"constexpr-not-const">>; def err_invalid_constexpr : Error< - "%select{function parameter|typedef|non-static data member}0 " - "cannot be %select{constexpr|consteval}1">; + "%select{function parameter|typedef}0 " + "cannot be %sub{select_constexpr_spec_kind}1">; def err_invalid_constexpr_member : Error<"non-static data member cannot be " "constexpr%select{; did you intend to make it %select{const|static}0?|}1">; def err_constexpr_tag : Error< "%select{class|struct|interface|union|enum}0 " - "cannot be marked %select{constexpr|consteval}1">; + "cannot be marked %sub{select_constexpr_spec_kind}1">; def err_constexpr_dtor : Error< - "destructor cannot be marked %select{constexpr|consteval}0">; + "destructor cannot be marked %sub{select_constexpr_spec_kind}0">; def err_constexpr_wrong_decl_kind : Error< - "%select{constexpr|consteval}0 can only be used " - "in %select{variable and |}0function declarations">; + "%sub{select_constexpr_spec_kind}0 can only be used " + "in %select{|variable and function|function|variable}0 declarations">; def err_invalid_constexpr_var_decl : Error< "constexpr variable declaration must be a definition">; def err_constexpr_static_mem_var_requires_init : Error< @@ -7514,10 +7514,30 @@ def note_inequality_comparison_to_or_ass def err_incomplete_type_used_in_type_trait_expr : Error< "incomplete type %0 used in type trait expression">; +// C++20 constinit and require_constant_initialization attribute +def warn_cxx20_compat_constinit : Warning< + "'constinit' specifier is incompatible with C++ standards before C++20">, + InGroup<CXX2aCompat>, DefaultIgnore; +def err_constinit_local_variable : Error< + "local variable cannot be declared 'constinit'">; def err_require_constant_init_failed : Error< "variable does not have a constant initializer">; def note_declared_required_constant_init_here : Note< - "required by 'require_constant_initialization' attribute here">; + "required by %select{'require_constant_initialization' attribute|" + "'constinit' specifier}0 here">; +def ext_constinit_missing : ExtWarn< + "'constinit' specifier missing on initializing declaration of %0">, + InGroup<DiagGroup<"missing-constinit">>; +def note_constinit_specified_here : Note<"variable declared constinit here">; +def err_constinit_added_too_late : Error< + "'constinit' specifier added after initialization of variable">; +def warn_require_const_init_added_too_late : Warning< + "'require_constant_initialization' attribute added after initialization " + "of variable">, InGroup<IgnoredAttributes>; +def note_constinit_missing_here : Note< + "add the " + "%select{'require_constant_initialization' attribute|'constinit' specifier}0 " + "to the initializing declaration here">; def err_dimension_expr_not_constant_integer : Error< "dimension expression does not evaluate to a constant unsigned int">; Modified: cfe/trunk/include/clang/Basic/Specifiers.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Specifiers.h?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/Specifiers.h (original) +++ cfe/trunk/include/clang/Basic/Specifiers.h Wed Sep 4 13:30:37 2019 @@ -32,7 +32,8 @@ namespace clang { enum ConstexprSpecKind { CSK_unspecified, CSK_constexpr, - CSK_consteval + CSK_consteval, + CSK_constinit }; /// Specifies the width of a type, e.g., short, long, or long long. Modified: cfe/trunk/include/clang/Basic/TokenKinds.def URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/TokenKinds.def?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/TokenKinds.def (original) +++ cfe/trunk/include/clang/Basic/TokenKinds.def Wed Sep 4 13:30:37 2019 @@ -389,6 +389,7 @@ MODULES_KEYWORD(import) // C++20 keywords. CXX2A_KEYWORD(char8_t , CHAR8SUPPORT) CXX2A_KEYWORD(consteval , 0) +CXX2A_KEYWORD(constinit , 0) // C11 Extension KEYWORD(_Float16 , KEYALL) Modified: cfe/trunk/lib/AST/Decl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/AST/Decl.cpp (original) +++ cfe/trunk/lib/AST/Decl.cpp Wed Sep 4 13:30:37 2019 @@ -2220,6 +2220,22 @@ Stmt **VarDecl::getInitAddress() { return Init.getAddrOfPtr1(); } +VarDecl *VarDecl::getInitializingDeclaration() { + VarDecl *Def = nullptr; + for (auto I : redecls()) { + if (I->hasInit()) + return I; + + if (I->isThisDeclarationADefinition()) { + if (isStaticDataMember()) + return I; + else + Def = I; + } + } + return Def; +} + bool VarDecl::isOutOfLine() const { if (Decl::isOutOfLine()) return true; Modified: cfe/trunk/lib/Frontend/InitPreprocessor.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/InitPreprocessor.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/Frontend/InitPreprocessor.cpp (original) +++ cfe/trunk/lib/Frontend/InitPreprocessor.cpp Wed Sep 4 13:30:37 2019 @@ -541,8 +541,10 @@ static void InitializeCPlusPlusFeatureTe Builder.defineMacro("__cpp_template_template_args", "201611L"); // C++20 features. - if (LangOpts.CPlusPlus2a) + if (LangOpts.CPlusPlus2a) { Builder.defineMacro("__cpp_conditional_explicit", "201806L"); + Builder.defineMacro("__cpp_constinit", "201907L"); + } if (LangOpts.Char8) Builder.defineMacro("__cpp_char8_t", "201811L"); Builder.defineMacro("__cpp_impl_destroying_delete", "201806L"); Modified: cfe/trunk/lib/Parse/ParseDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseDecl.cpp (original) +++ cfe/trunk/lib/Parse/ParseDecl.cpp Wed Sep 4 13:30:37 2019 @@ -2514,7 +2514,7 @@ void Parser::ParseSpecifierQualifierList // Issue diagnostic and remove constexpr specifier if present. if (DS.hasConstexprSpecifier() && DSC != DeclSpecContext::DSC_condition) { Diag(DS.getConstexprSpecLoc(), diag::err_typename_invalid_constexpr) - << (DS.getConstexprSpecifier() == CSK_consteval); + << DS.getConstexprSpecifier(); DS.ClearConstexprSpec(); } } @@ -3653,15 +3653,16 @@ void Parser::ParseDeclarationSpecifiers( isInvalid = DS.setModulePrivateSpec(Loc, PrevSpec, DiagID); break; - // constexpr + // constexpr, consteval, constinit specifiers case tok::kw_constexpr: isInvalid = DS.SetConstexprSpec(CSK_constexpr, Loc, PrevSpec, DiagID); break; - - // consteval case tok::kw_consteval: isInvalid = DS.SetConstexprSpec(CSK_consteval, Loc, PrevSpec, DiagID); break; + case tok::kw_constinit: + isInvalid = DS.SetConstexprSpec(CSK_constinit, Loc, PrevSpec, DiagID); + break; // type-specifier case tok::kw_short: @@ -5080,8 +5081,9 @@ bool Parser::isDeclarationSpecifier(bool case tok::annot_decltype: case tok::kw_constexpr: - // C++20 consteval. + // C++20 consteval and constinit. case tok::kw_consteval: + case tok::kw_constinit: // C11 _Atomic case tok::kw__Atomic: Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original) +++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed Sep 4 13:30:37 2019 @@ -1313,6 +1313,8 @@ bool Parser::isValidAfterTypeSpecifier(b case tok::kw_mutable: // struct foo {...} mutable x; case tok::kw_thread_local: // struct foo {...} thread_local x; case tok::kw_constexpr: // struct foo {...} constexpr x; + case tok::kw_consteval: // struct foo {...} consteval x; + case tok::kw_constinit: // struct foo {...} constinit x; // As shown above, type qualifiers and storage class specifiers absolutely // can occur after class specifiers according to the grammar. However, // almost no one actually writes code like this. If we see one of these, Modified: cfe/trunk/lib/Parse/ParseTentative.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTentative.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseTentative.cpp (original) +++ cfe/trunk/lib/Parse/ParseTentative.cpp Wed Sep 4 13:30:37 2019 @@ -1408,6 +1408,7 @@ Parser::isCXXDeclarationSpecifier(Parser case tok::kw_typedef: case tok::kw_constexpr: case tok::kw_consteval: + case tok::kw_constinit: // storage-class-specifier case tok::kw_register: case tok::kw_static: Modified: cfe/trunk/lib/Sema/DeclSpec.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/DeclSpec.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/Sema/DeclSpec.cpp (original) +++ cfe/trunk/lib/Sema/DeclSpec.cpp Wed Sep 4 13:30:37 2019 @@ -569,6 +569,7 @@ const char *DeclSpec::getSpecifierName(C case CSK_unspecified: return "unspecified"; case CSK_constexpr: return "constexpr"; case CSK_consteval: return "consteval"; + case CSK_constinit: return "constinit"; } llvm_unreachable("Unknown ConstexprSpecKind"); } @@ -1036,13 +1037,9 @@ bool DeclSpec::setModulePrivateSpec(Sour bool DeclSpec::SetConstexprSpec(ConstexprSpecKind ConstexprKind, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { - if (getConstexprSpecifier() != CSK_unspecified) { - if (getConstexprSpecifier() == CSK_consteval || ConstexprKind == CSK_consteval) - return BadSpecifier(ConstexprKind, getConstexprSpecifier(), PrevSpec, DiagID); - DiagID = diag::warn_duplicate_declspec; - PrevSpec = "constexpr"; - return true; - } + if (getConstexprSpecifier() != CSK_unspecified) + return BadSpecifier(ConstexprKind, getConstexprSpecifier(), PrevSpec, + DiagID); ConstexprSpecifier = ConstexprKind; ConstexprLoc = Loc; return false; @@ -1291,8 +1288,10 @@ void DeclSpec::Finish(Sema &S, const Pri << (TypeSpecType == TST_char16 ? "char16_t" : "char32_t"); if (getConstexprSpecifier() == CSK_constexpr) S.Diag(ConstexprLoc, diag::warn_cxx98_compat_constexpr); - if (getConstexprSpecifier() == CSK_consteval) + else if (getConstexprSpecifier() == CSK_consteval) S.Diag(ConstexprLoc, diag::warn_cxx20_compat_consteval); + else if (getConstexprSpecifier() == CSK_constinit) + S.Diag(ConstexprLoc, diag::warn_cxx20_compat_constinit); // C++ [class.friend]p6: // No storage-class-specifier shall appear in the decl-specifier-seq // of a friend declaration. Modified: cfe/trunk/lib/Sema/SemaDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Sep 4 13:30:37 2019 @@ -2661,6 +2661,60 @@ static void checkNewAttributesAfterDef(S } } +static void diagnoseMissingConstinit(Sema &S, const VarDecl *InitDecl, + const ConstInitAttr *CIAttr, + bool AttrBeforeInit) { + SourceLocation InsertLoc = InitDecl->getInnerLocStart(); + + // Figure out a good way to write this specifier on the old declaration. + // FIXME: We should just use the spelling of CIAttr, but we don't preserve + // enough of the attribute list spelling information to extract that without + // heroics. + std::string SuitableSpelling; + if (S.getLangOpts().CPlusPlus2a) + SuitableSpelling = + S.PP.getLastMacroWithSpelling(InsertLoc, {tok::kw_constinit}); + if (SuitableSpelling.empty() && S.getLangOpts().CPlusPlus11) + SuitableSpelling = S.PP.getLastMacroWithSpelling( + InsertLoc, + {tok::l_square, tok::l_square, S.PP.getIdentifierInfo("clang"), + tok::coloncolon, + S.PP.getIdentifierInfo("require_constant_initialization"), + tok::r_square, tok::r_square}); + if (SuitableSpelling.empty()) + SuitableSpelling = S.PP.getLastMacroWithSpelling( + InsertLoc, + {tok::kw___attribute, tok::l_paren, tok::r_paren, + S.PP.getIdentifierInfo("require_constant_initialization"), + tok::r_paren, tok::r_paren}); + if (SuitableSpelling.empty() && S.getLangOpts().CPlusPlus2a) + SuitableSpelling = "constinit"; + if (SuitableSpelling.empty() && S.getLangOpts().CPlusPlus11) + SuitableSpelling = "[[clang::require_constant_initialization]]"; + if (SuitableSpelling.empty()) + SuitableSpelling = "__attribute__((require_constant_initialization))"; + SuitableSpelling += " "; + + if (AttrBeforeInit) { + // extern constinit int a; + // int a = 0; // error (missing 'constinit'), accepted as extension + assert(CIAttr->isConstinit() && "should not diagnose this for attribute"); + S.Diag(InitDecl->getLocation(), diag::ext_constinit_missing) + << InitDecl << FixItHint::CreateInsertion(InsertLoc, SuitableSpelling); + S.Diag(CIAttr->getLocation(), diag::note_constinit_specified_here); + } else { + // int a = 0; + // constinit extern int a; // error (missing 'constinit') + S.Diag(CIAttr->getLocation(), + CIAttr->isConstinit() ? diag::err_constinit_added_too_late + : diag::warn_require_const_init_added_too_late) + << FixItHint::CreateRemoval(SourceRange(CIAttr->getLocation())); + S.Diag(InitDecl->getLocation(), diag::note_constinit_missing_here) + << CIAttr->isConstinit() + << FixItHint::CreateInsertion(InsertLoc, SuitableSpelling); + } +} + /// mergeDeclAttributes - Copy attributes from the Old decl to the New one. void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK) { @@ -2673,6 +2727,41 @@ void Sema::mergeDeclAttributes(NamedDecl if (!Old->hasAttrs() && !New->hasAttrs()) return; + // [dcl.constinit]p1: + // If the [constinit] specifier is applied to any declaration of a + // variable, it shall be applied to the initializing declaration. + const auto *OldConstInit = Old->getAttr<ConstInitAttr>(); + const auto *NewConstInit = New->getAttr<ConstInitAttr>(); + if (bool(OldConstInit) != bool(NewConstInit)) { + const auto *OldVD = cast<VarDecl>(Old); + auto *NewVD = cast<VarDecl>(New); + + // Find the initializing declaration. Note that we might not have linked + // the new declaration into the redeclaration chain yet. + const VarDecl *InitDecl = OldVD->getInitializingDeclaration(); + if (!InitDecl && + (NewVD->hasInit() || NewVD->isThisDeclarationADefinition())) + InitDecl = NewVD; + + if (InitDecl == NewVD) { + // This is the initializing declaration. If it would inherit 'constinit', + // that's ill-formed. (Note that we do not apply this to the attribute + // form). + if (OldConstInit && OldConstInit->isConstinit()) + diagnoseMissingConstinit(*this, NewVD, OldConstInit, + /*AttrBeforeInit=*/true); + } else if (NewConstInit) { + // This is the first time we've been told that this declaration should + // have a constant initializer. If we already saw the initializing + // declaration, this is too late. + if (InitDecl && InitDecl != NewVD) { + diagnoseMissingConstinit(*this, InitDecl, NewConstInit, + /*AttrBeforeInit=*/false); + NewVD->dropAttr<ConstInitAttr>(); + } + } + } + // Attributes declared post-definition are currently ignored. checkNewAttributesAfterDef(*this, New, Old); @@ -4315,13 +4404,13 @@ Sema::ParsedFreeStandingDeclSpec(Scope * // and definitions of functions and variables. // C++2a [dcl.constexpr]p1: The consteval specifier shall be applied only to // the declaration of a function or function template - bool IsConsteval = DS.getConstexprSpecifier() == CSK_consteval; if (Tag) Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_tag) - << GetDiagnosticTypeSpecifierID(DS.getTypeSpecType()) << IsConsteval; + << GetDiagnosticTypeSpecifierID(DS.getTypeSpecType()) + << DS.getConstexprSpecifier(); else Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_wrong_decl_kind) - << IsConsteval; + << DS.getConstexprSpecifier(); // Don't emit warnings after this error. return TagD; } @@ -5776,7 +5865,7 @@ Sema::ActOnTypedefDeclarator(Scope* S, D << getLangOpts().CPlusPlus17; if (D.getDeclSpec().hasConstexprSpecifier()) Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) - << 1 << (D.getDeclSpec().getConstexprSpecifier() == CSK_consteval); + << 1 << D.getDeclSpec().getConstexprSpecifier(); if (D.getName().Kind != UnqualifiedIdKind::IK_Identifier) { if (D.getName().Kind == UnqualifiedIdKind::IK_DeductionGuideName) @@ -6671,19 +6760,6 @@ NamedDecl *Sema::ActOnVariableDeclarator if (TemplateParamLists.size() > VDTemplateParamLists) NewVD->setTemplateParameterListsInfo( Context, TemplateParamLists.drop_back(VDTemplateParamLists)); - - if (D.getDeclSpec().hasConstexprSpecifier()) { - 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().CPlusPlus17) - NewVD->setImplicitlyInline(); - if (D.getDeclSpec().getConstexprSpecifier() == CSK_consteval) - Diag(D.getDeclSpec().getConstexprSpecLoc(), - diag::err_constexpr_wrong_decl_kind) - << /*consteval*/ 1; - } } if (D.getDeclSpec().isInlineSpecified()) { @@ -6749,6 +6825,36 @@ NamedDecl *Sema::ActOnVariableDeclarator NewVD->setTSCSpec(TSCS); } + switch (D.getDeclSpec().getConstexprSpecifier()) { + case CSK_unspecified: + break; + + case CSK_consteval: + Diag(D.getDeclSpec().getConstexprSpecLoc(), + diag::err_constexpr_wrong_decl_kind) + << D.getDeclSpec().getConstexprSpecifier(); + LLVM_FALLTHROUGH; + + case CSK_constexpr: + 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().CPlusPlus17) + NewVD->setImplicitlyInline(); + break; + + case CSK_constinit: + if (!NewVD->hasGlobalStorage()) + Diag(D.getDeclSpec().getConstexprSpecLoc(), + diag::err_constinit_local_variable); + else + NewVD->addAttr(::new (Context) ConstInitAttr( + SourceRange(D.getDeclSpec().getConstexprSpecLoc()), Context, + ConstInitAttr::Keyword_constinit)); + break; + } + // C99 6.7.4p3 // An inline definition of a function with external linkage shall // not contain a definition of a modifiable object with static or @@ -7989,7 +8095,7 @@ static StorageClass getFunctionStorageCl return SC_None; } -static FunctionDecl* CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, +static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, DeclContext *DC, QualType &R, TypeSourceInfo *TInfo, StorageClass SC, @@ -8021,7 +8127,16 @@ static FunctionDecl* CreateNewFunctionDe } ExplicitSpecifier ExplicitSpecifier = D.getDeclSpec().getExplicitSpecifier(); + ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier(); + if (ConstexprKind == CSK_constinit) { + SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(), + diag::err_constexpr_wrong_decl_kind) + << ConstexprKind; + ConstexprKind = CSK_unspecified; + D.getMutableDeclSpec().ClearConstexprSpec(); + } + // Check that the return type is not an abstract class type. // For record types, this is done by the AbstractClassUsageDiagnoser once // the class has been completely parsed. @@ -8452,7 +8567,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, bool isInline = D.getDeclSpec().isInlineSpecified(); bool isVirtual = D.getDeclSpec().isVirtualSpecified(); bool hasExplicit = D.getDeclSpec().hasExplicitSpecifier(); - ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier(); isFriend = D.getDeclSpec().isFriendSpecified(); if (isFriend && !isInline && D.isFunctionDefinition()) { // C++ [class.friend]p5 @@ -8651,7 +8765,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, } } - if (ConstexprKind != CSK_unspecified) { + if (ConstexprSpecKind ConstexprKind = + D.getDeclSpec().getConstexprSpecifier()) { // C++11 [dcl.constexpr]p2: constexpr functions and constexpr constructors // are implicitly inline. NewFD->setImplicitlyInline(); @@ -8659,9 +8774,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, // C++11 [dcl.constexpr]p3: functions declared constexpr are required to // be either constructors or to return a literal type. Therefore, // destructors cannot be declared constexpr. - if (isa<CXXDestructorDecl>(NewFD)) + if (isa<CXXDestructorDecl>(NewFD)) { Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor) - << (ConstexprKind == CSK_consteval); + << ConstexprKind; + } } // If __module_private__ was specified, mark the function accordingly. @@ -12043,17 +12159,17 @@ void Sema::CheckCompleteVariableDeclarat // Don't emit further diagnostics about constexpr globals since they // were just diagnosed. - if (!var->isConstexpr() && GlobalStorage && - var->hasAttr<RequireConstantInitAttr>()) { + if (!var->isConstexpr() && GlobalStorage && var->hasAttr<ConstInitAttr>()) { // FIXME: Need strict checking in C++03 here. bool DiagErr = getLangOpts().CPlusPlus11 ? !var->checkInitIsICE() : !checkConstInit(); if (DiagErr) { - auto attr = var->getAttr<RequireConstantInitAttr>(); + auto *Attr = var->getAttr<ConstInitAttr>(); Diag(var->getLocation(), diag::err_require_constant_init_failed) << Init->getSourceRange(); - Diag(attr->getLocation(), diag::note_declared_required_constant_init_here) - << attr->getRange(); + Diag(Attr->getLocation(), + diag::note_declared_required_constant_init_here) + << Attr->getRange() << Attr->isConstinit(); if (getLangOpts().CPlusPlus11) { APValue Value; SmallVector<PartialDiagnosticAt, 8> Notes; @@ -12546,7 +12662,7 @@ Decl *Sema::ActOnParamDeclarator(Scope * << getLangOpts().CPlusPlus17; if (DS.hasConstexprSpecifier()) Diag(DS.getConstexprSpecLoc(), diag::err_invalid_constexpr) - << 0 << (D.getDeclSpec().getConstexprSpecifier() == CSK_consteval); + << 0 << D.getDeclSpec().getConstexprSpecifier(); DiagnoseFunctionSpecifiers(DS); Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Wed Sep 4 13:30:37 2019 @@ -7057,8 +7057,8 @@ static void ProcessDeclAttribute(Sema &S case ParsedAttr::AT_VecTypeHint: handleVecTypeHint(S, D, AL); break; - case ParsedAttr::AT_RequireConstantInit: - handleSimpleAttribute<RequireConstantInitAttr>(S, D, AL); + case ParsedAttr::AT_ConstInit: + handleSimpleAttribute<ConstInitAttr>(S, D, AL); break; case ParsedAttr::AT_InitPriority: handleInitPriorityAttr(S, D, AL); Modified: cfe/trunk/lib/Sema/SemaType.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaType.cpp (original) +++ cfe/trunk/lib/Sema/SemaType.cpp Wed Sep 4 13:30:37 2019 @@ -5146,9 +5146,9 @@ static TypeSourceInfo *GetFullTypeForDec // C++0x [dcl.constexpr]p9: // A constexpr specifier used in an object declaration declares the object // as const. - if (D.getDeclSpec().hasConstexprSpecifier() && T->isObjectType()) { + if (D.getDeclSpec().getConstexprSpecifier() == CSK_constexpr && + T->isObjectType()) T.addConst(); - } // If there was an ellipsis in the declarator, the declaration declares a // parameter pack whose type may be a pack expansion type. Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp?rev=370972&view=auto ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp (added) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp Wed Sep 4 13:30:37 2019 @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +constinit int a; +constinit thread_local int b; +constinit static int c; + +void f() { + constinit static int a; + constinit thread_local int b; + constinit int c; // expected-error {{local variable cannot be declared 'constinit'}} +} + +namespace missing { + int a; // expected-note {{add the 'constinit' specifier}} + extern constinit int a; // expected-error {{added after initialization}} + + // We allow inheriting 'constinit' from a forward declaration as an extension. + extern constinit int b; // expected-note {{here}} + int b; // expected-warning {{'constinit' specifier missing}} +} + +struct S { + static constinit int a; // expected-note {{here}} + static constinit constexpr int b; // expected-error {{cannot combine with previous}} expected-note {{here}} + static constinit const int c = 1; + static constinit const int d = 1; +}; +int S::a; // expected-warning {{'constinit' specifier missing}} +int S::b; // expected-warning {{'constinit' specifier missing}} +const int S::c; +inline const int S::d; + +struct T { + static int a; + static constexpr int b = 1; // expected-note {{add the 'constinit' specifier}} + static const int c = 1; // expected-note {{add the 'constinit' specifier}} + static const int d = 1; // expected-note {{add the 'constinit' specifier}} +}; +constinit int T::a; +constinit const int T::b; // expected-error {{'constinit' specifier added after initialization}} +constinit const int T::c; // expected-error {{'constinit' specifier added after initialization}} +constinit inline const int T::d; // expected-error {{'constinit' specifier added after initialization}} + +constinit void g() {} // expected-error {{constinit can only be used in variable declarations}} + +// (These used to trigger crashes.) +void h(); +constinit void h(); // expected-error {{constinit can only be used in variable declarations}} +constexpr void i(); // expected-note {{here}} +constinit void i(); // expected-error {{non-constexpr declaration of 'i' follows constexpr declaration}} +// expected-error@-1 {{constinit can only be used in variable declarations}} + +typedef constinit int type; // expected-error {{typedef cannot be constinit}} +using type = constinit int; // expected-error {{type name does not allow constinit specifier}} +auto q() -> int constinit; // expected-error {{type name does not allow constinit specifier}} Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp?rev=370972&view=auto ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp (added) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp Wed Sep 4 13:30:37 2019 @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +int f(); // expected-note 2{{declared here}} + +constinit int a; +constinit int b = f(); // expected-error {{does not have a constant initializer}} expected-note {{required by}} expected-note {{non-constexpr function 'f'}} +extern constinit int c; // expected-note {{here}} expected-note {{required by}} +int c = f(); // expected-warning {{missing}} expected-error {{does not have a constant initializer}} expected-note {{non-constexpr function 'f'}} Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp?rev=370972&view=auto ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp (added) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp Wed Sep 4 13:30:37 2019 @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +const char *g() { return "dynamic initialization"; } // expected-note {{declared here}} +constexpr const char *f(bool b) { return b ? "constant initialization" : g(); } // expected-note {{non-constexpr function 'g'}} +constinit const char *c = f(true); +constinit const char *d = f(false); // expected-error {{does not have a constant initializer}} expected-note 2{{}} Modified: cfe/trunk/test/FixIt/fixit-c++2a.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/fixit-c%2B%2B2a.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/test/FixIt/fixit-c++2a.cpp (original) +++ cfe/trunk/test/FixIt/fixit-c++2a.cpp Wed Sep 4 13:30:37 2019 @@ -1,7 +1,8 @@ -// RUN: %clang_cc1 -verify -std=c++2a %s +// RUN: %clang_cc1 -verify -std=c++2a -pedantic-errors %s // RUN: cp %s %t // RUN: not %clang_cc1 -x c++ -std=c++2a -fixit %t -// RUN: %clang_cc1 -Wall -pedantic -x c++ -std=c++2a %t +// RUN: %clang_cc1 -Wall -pedantic-errors -x c++ -std=c++2a %t +// RUN: cat %t | FileCheck %s /* This is a test of the various code modification hints that only apply in C++2a. */ @@ -13,3 +14,36 @@ template<typename ...T> void init_captur [&...a]{}; // expected-error {{must appear after the name}} [...&a]{}; // expected-error {{must appear after the name}} } + +namespace constinit_mismatch { + extern thread_local constinit int a; // expected-note {{declared constinit here}} + thread_local int a = 123; // expected-error {{'constinit' specifier missing on initializing declaration of 'a'}} + // CHECK: {{^}} constinit thread_local int a = 123; + + int b = 123; // expected-note {{add the 'constinit' specifier}} + extern constinit int b; // expected-error {{'constinit' specifier added after initialization of variable}} + // CHECK: {{^}} extern int b; + + template<typename> struct X { + template<int> static constinit int n; // expected-note {{constinit}} + }; + template<typename T> template<int N> + int X<T>::n = 123; // expected-error {{missing}} + // CHECK: {{^}} constinit int X<T>::n = 123; + +#define ABSL_CONST_INIT [[clang::require_constant_initialization]] + extern constinit int c; // expected-note {{constinit}} + int c; // expected-error {{missing}} + // CHECK: {{^}} ABSL_CONST_INIT int c; + +#define MY_CONST_INIT constinit + extern constinit int d; // expected-note {{constinit}} + int d; // expected-error {{missing}} + // CHECK: {{^}} MY_CONST_INIT int d; +#undef MY_CONST_INIT + + extern constinit int e; // expected-note {{constinit}} + int e; // expected-error {{missing}} + // CHECK: {{^}} ABSL_CONST_INIT int e; +#undef ABSL_CONST_INIT +} Modified: cfe/trunk/test/Lexer/cxx-features.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Lexer/cxx-features.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/test/Lexer/cxx-features.cpp (original) +++ cfe/trunk/test/Lexer/cxx-features.cpp Wed Sep 4 13:30:37 2019 @@ -34,6 +34,10 @@ #error "wrong value for __cpp_char8_t" #endif +#if check(constinit, 0, 0, 0, 0, 201907) +#error "wrong value for __cpp_constinit" +#endif + #if check(impl_destroying_delete, 201806, 201806, 201806, 201806, 201806) #error "wrong value for __cpp_impl_destroying_delete" #endif Modified: cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp (original) +++ cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp Wed Sep 4 13:30:37 2019 @@ -11,3 +11,5 @@ int co_yield = 0; // expected-warning {{ int char8_t = 0; // expected-warning {{'char8_t' is a keyword in C++2a}} int concept = 0; // expected-warning {{'concept' is a keyword in C++2a}} int requires = 0; // expected-warning {{'requires' is a keyword in C++2a}} +int consteval = 0; // expected-warning {{'consteval' is a keyword in C++2a}} +int constinit = 0; // expected-warning {{'constinit' is a keyword in C++2a}} Modified: cfe/trunk/test/Misc/pragma-attribute-cxx.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-cxx.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/test/Misc/pragma-attribute-cxx.cpp (original) +++ cfe/trunk/test/Misc/pragma-attribute-cxx.cpp Wed Sep 4 13:30:37 2019 @@ -87,14 +87,14 @@ void testLambdaMethod() { int testCI1 = 1; // CHECK-LABEL: VarDecl{{.*}} testCI1 // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: RequireConstantInitAttr +// CHECK-NEXT: ConstInitAttr #pragma clang attribute pop int testNoCI = 0; // CHECK-LABEL: VarDecl{{.*}} testNoCI // CHECK-NEXT: IntegerLiteral -// CHECK-NOT: RequireConstantInitAttr +// CHECK-NOT: ConstInitAttr // Check support for CXX11 style attributes #pragma clang attribute push ([[noreturn]], apply_to = function) Modified: cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test (original) +++ cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test Wed Sep 4 13:30:37 2019 @@ -38,6 +38,7 @@ // CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: Cold (SubjectMatchRule_function) // CHECK-NEXT: Common (SubjectMatchRule_variable) +// CHECK-NEXT: ConstInit (SubjectMatchRule_variable_is_global) // CHECK-NEXT: Constructor (SubjectMatchRule_function) // CHECK-NEXT: Consumable (SubjectMatchRule_record) // CHECK-NEXT: ConsumableAutoCast (SubjectMatchRule_record) @@ -124,7 +125,6 @@ // CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function) // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function) -// CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global) // CHECK-NEXT: Restrict (SubjectMatchRule_function) // CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function) Modified: cfe/trunk/test/Parser/cxx0x-decl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx0x-decl.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/test/Parser/cxx0x-decl.cpp (original) +++ cfe/trunk/test/Parser/cxx0x-decl.cpp Wed Sep 4 13:30:37 2019 @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -std=c++11 -pedantic-errors -triple x86_64-linux-gnu %s +// RUN: %clang_cc1 -verify -fsyntax-only -std=c++2a -pedantic-errors -triple x86_64-linux-gnu %s // Make sure we know these are legitimate commas and not typos for ';'. namespace Commas { @@ -108,14 +108,25 @@ namespace UsingDeclAttrs { } namespace DuplicateSpecifier { - constexpr constexpr int f(); // expected-warning {{duplicate 'constexpr' declaration specifier}} - constexpr int constexpr a = 0; // expected-warning {{duplicate 'constexpr' declaration specifier}} + constexpr constexpr int f(); // expected-error {{duplicate 'constexpr' declaration specifier}} + constexpr int constexpr a = 0; // expected-error {{duplicate 'constexpr' declaration specifier}} struct A { friend constexpr int constexpr friend f(); // expected-warning {{duplicate 'friend' declaration specifier}} \ - // expected-warning {{duplicate 'constexpr' declaration specifier}} + // expected-error {{duplicate 'constexpr' declaration specifier}} friend struct A friend; // expected-warning {{duplicate 'friend'}} expected-error {{'friend' must appear first}} }; + + constinit constexpr int n1 = 0; // expected-error {{cannot combine with previous 'constinit'}} + constexpr constinit int n2 = 0; // expected-error {{cannot combine with previous 'constexpr'}} + constinit constinit int n3 = 0; // expected-error {{duplicate 'constinit' declaration specifier}} + + consteval constexpr int f1(); // expected-error {{cannot combine with previous 'consteval'}} + constexpr consteval int f2(); // expected-error {{cannot combine with previous 'constexpr'}} + consteval consteval int f3(); // expected-error {{duplicate 'consteval' declaration specifier}} + + constinit consteval int wat = 0; // expected-error {{cannot combine with previous 'constinit'}} + consteval constinit int huh(); // expected-error {{cannot combine with previous 'consteval'}} } namespace ColonColonDecltype { Modified: cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp (original) +++ cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp Wed Sep 4 13:30:37 2019 @@ -300,6 +300,17 @@ ATTR TestCtor<NotC> t(42); // expected-e ATTR const char *foo[] = {"abc", "def"}; ATTR PODType bar[] = {{}, {123, 456}}; + +namespace AttrAddedTooLate { + struct A { + static const int n = 0; // expected-note {{here}} + }; + ATTR const int A::n; // expected-warning {{added after initialization}} + + int m = 0; // expected-note {{here}} + extern ATTR int m; // expected-warning {{added after initialization}} +} + #elif defined(TEST_TWO) // Test for duplicate warnings struct NotC { constexpr NotC(void *) {} Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=370972&r1=370971&r2=370972&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Wed Sep 4 13:30:37 2019 @@ -1135,7 +1135,7 @@ as the draft C++2a standard evolves. <tr> <td><tt>constinit</tt></td> <td><a href="http://wg21.link/p1143r2">P1143R2</a></td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> </table> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits