diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-09-04 20:30:37 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-09-04 20:30:37 +0000 |
commit | a6e8b685e13492abfb2e58dc49007deda165a00d (patch) | |
tree | 279bbf503976a5c882db2f0699a858d10e66a5e9 /clang/lib/Sema/SemaDecl.cpp | |
parent | eca01b031d44da54239d9956ba0acc7cf2f7798b (diff) | |
download | bcm5719-llvm-a6e8b685e13492abfb2e58dc49007deda165a00d.tar.gz bcm5719-llvm-a6e8b685e13492abfb2e58dc49007deda165a00d.zip |
[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.
llvm-svn: 370972
Diffstat (limited to 'clang/lib/Sema/SemaDecl.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 172 |
1 files changed, 144 insertions, 28 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a96f0d28e86..a2708223c19 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2661,6 +2661,60 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) { } } +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 *New, Decl *Old, 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 *S, AccessSpecifier AS, DeclSpec &DS, // 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, Declarator& D, DeclContext* DC, << 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 getFunctionStorageClass(Sema &SemaRef, Declarator &D) { 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* CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, } 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, Declarator &D, DeclContext *DC, 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, Declarator &D, DeclContext *DC, } } - 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, Declarator &D, DeclContext *DC, // 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::CheckCompleteVariableDeclaration(VarDecl *var) { // 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 *S, Declarator &D) { << getLangOpts().CPlusPlus17; if (DS.hasConstexprSpecifier()) Diag(DS.getConstexprSpecLoc(), diag::err_invalid_constexpr) - << 0 << (D.getDeclSpec().getConstexprSpecifier() == CSK_consteval); + << 0 << D.getDeclSpec().getConstexprSpecifier(); DiagnoseFunctionSpecifiers(DS); |