diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-07-22 23:36:59 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-07-22 23:36:59 +0000 |
commit | 1a9b9adf6ff83d48a797f20cc607be9c5eb154c6 (patch) | |
tree | 9d5024a4b7fb4cb223347d9c35fe3d5065885a63 /lib | |
parent | 8747ea8f68604c5c2719f387628b4f76074f3a77 (diff) |
P0217R3: Parsing support and framework for AST representation of C++1z
decomposition declarations.
There are a couple of things in the wording that seem strange here:
decomposition declarations are permitted at namespace scope (which we partially
support here) and they are permitted as the declaration in a template (which we
reject).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@276492 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AST/ASTDumper.cpp | 7 | ||||
-rw-r--r-- | lib/AST/DeclBase.cpp | 2 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 38 | ||||
-rw-r--r-- | lib/AST/ItaniumMangle.cpp | 20 | ||||
-rw-r--r-- | lib/CodeGen/CGDecl.cpp | 5 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenModule.cpp | 1 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 76 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 1 | ||||
-rw-r--r-- | lib/Parse/ParseTentative.cpp | 9 | ||||
-rw-r--r-- | lib/Sema/DeclSpec.cpp | 36 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 253 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 13 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiateDecl.cpp | 10 | ||||
-rw-r--r-- | lib/Serialization/ASTCommon.cpp | 2 |
15 files changed, 445 insertions, 31 deletions
diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 872ba356a9..1a4207f512 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -428,6 +428,7 @@ namespace { void VisitFunctionDecl(const FunctionDecl *D); void VisitFieldDecl(const FieldDecl *D); void VisitVarDecl(const VarDecl *D); + void VisitDecompositionDecl(const DecompositionDecl *D); void VisitFileScopeAsmDecl(const FileScopeAsmDecl *D); void VisitImportDecl(const ImportDecl *D); void VisitPragmaCommentDecl(const PragmaCommentDecl *D); @@ -1217,6 +1218,12 @@ void ASTDumper::VisitVarDecl(const VarDecl *D) { } } +void ASTDumper::VisitDecompositionDecl(const DecompositionDecl *D) { + VisitVarDecl(D); + for (auto *B : D->bindings()) + dumpDecl(B); +} + void ASTDumper::VisitFileScopeAsmDecl(const FileScopeAsmDecl *D) { dumpStmt(D->getAsmString()); } diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index e07bf480ec..8918e18d43 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -598,6 +598,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case CXXConversion: case EnumConstant: case Var: + case Binding: case ImplicitParam: case ParmVar: case ObjCMethod: @@ -678,6 +679,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case Captured: case TranslationUnit: case ExternCContext: + case Decomposition: case UsingDirective: case BuiltinTemplate: diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index d069bfdc3d..7e6c725348 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -2306,6 +2306,44 @@ StaticAssertDecl *StaticAssertDecl::CreateDeserialized(ASTContext &C, nullptr, SourceLocation(), false); } +void BindingDecl::anchor() {} + +BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation IdLoc, IdentifierInfo *Id) { + return new (C, DC) BindingDecl(DC, IdLoc, Id); +} + +BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + return new (C, ID) BindingDecl(nullptr, SourceLocation(), nullptr); +} + +void DecompositionDecl::anchor() {} + +DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation StartLoc, + SourceLocation LSquareLoc, + QualType T, TypeSourceInfo *TInfo, + StorageClass SC, + ArrayRef<BindingDecl *> Bindings) { + size_t Extra = additionalSizeToAlloc<BindingDecl *>(Bindings.size()); + return new (C, DC, Extra) + DecompositionDecl(C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings); +} + +DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C, + unsigned ID, + unsigned NumBindings) { + size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings); + auto *Result = new (C, ID, Extra) DecompositionDecl( + C, nullptr, SourceLocation(), SourceLocation(), QualType(), nullptr, StorageClass(), None); + // Set up and clean out the bindings array. + Result->NumBindings = NumBindings; + auto *Trail = Result->getTrailingObjects<BindingDecl *>(); + for (unsigned I = 0; I != NumBindings; ++I) + new (Trail + I) BindingDecl*(nullptr); + return Result; +} + MSPropertyDecl *MSPropertyDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName N, QualType T, TypeSourceInfo *TInfo, diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 51de561d5f..5b278314ae 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -593,7 +593,7 @@ bool ItaniumMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) { return false; const VarDecl *VD = dyn_cast<VarDecl>(D); - if (VD) { + if (VD && !isa<DecompositionDecl>(D)) { // C variables are not mangled. if (VD->isExternC()) return false; @@ -1193,7 +1193,23 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, // ::= <source-name> switch (Name.getNameKind()) { case DeclarationName::Identifier: { - if (const IdentifierInfo *II = Name.getAsIdentifierInfo()) { + const IdentifierInfo *II = Name.getAsIdentifierInfo(); + + // We mangle decomposition declarations as the name of their first binding. + if (auto *DD = dyn_cast<DecompositionDecl>(ND)) { + auto B = DD->bindings(); + if (B.begin() == B.end()) { + // FIXME: This is ill-formed but we accept it as an extension. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle global empty decomposition decl"); + Diags.Report(DD->getLocation(), DiagID); + break; + } + II = (*B.begin())->getIdentifier(); + } + + if (II) { // We must avoid conflicts between internally- and externally- // linked variable and function declaration names in the same TU: // void test() { extern void foo(); } diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 89407cd70c..e0cb07b98d 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -114,12 +114,15 @@ void CodeGenFunction::EmitDecl(const Decl &D) { if (CGDebugInfo *DI = getDebugInfo()) DI->EmitUsingDirective(cast<UsingDirectiveDecl>(D)); return; - case Decl::Var: { + case Decl::Var: + case Decl::Decomposition: { const VarDecl &VD = cast<VarDecl>(D); assert(VD.isLocalVarDecl() && "Should not see file-scope variables inside a function!"); return EmitVarDecl(VD); } + case Decl::Binding: + return CGM.ErrorUnsupported(&D, "structured binding"); case Decl::OMPDeclareReduction: return CGM.EmitOMPDeclareReduction(cast<OMPDeclareReductionDecl>(&D), this); diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index f73e85dd42..96bb5bfe02 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -3761,6 +3761,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { break; case Decl::Var: + case Decl::Decomposition: // Skip variable templates if (cast<VarDecl>(D)->getDescribedVarTemplate()) return; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 45e1c3e465..91a3effe42 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4224,7 +4224,7 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) { if (Tok.is(tok::identifier)) { // We're missing a comma between enumerators. - SourceLocation Loc = PP.getLocForEndOfToken(PrevTokLocation); + SourceLocation Loc = getEndOfPreviousToken(); Diag(Loc, diag::err_enumerator_list_missing_comma) << FixItHint::CreateInsertion(Loc, ", "); continue; @@ -5200,12 +5200,22 @@ static SourceLocation getMissingDeclaratorIdLoc(Declarator &D, /// '~' class-name /// template-id /// +/// C++17 adds the following, which we also handle here: +/// +/// simple-declaration: +/// <decl-spec> '[' identifier-list ']' brace-or-equal-initializer ';' +/// /// Note, any additional constructs added here may need corresponding changes /// in isConstructorDeclarator. void Parser::ParseDirectDeclarator(Declarator &D) { DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec()); if (getLangOpts().CPlusPlus && D.mayHaveIdentifier()) { + // This might be a C++17 structured binding. + if (Tok.is(tok::l_square) && !D.mayOmitIdentifier() && + D.getCXXScopeSpec().isEmpty()) + return ParseDecompositionDeclarator(D); + // Don't parse FOO:BAR as if it were a typo for FOO::BAR inside a class, in // this context it is a bitfield. Also in range-based for statement colon // may delimit for-range-declaration. @@ -5435,6 +5445,70 @@ void Parser::ParseDirectDeclarator(Declarator &D) { } } +void Parser::ParseDecompositionDeclarator(Declarator &D) { + assert(Tok.is(tok::l_square)); + + // If this doesn't look like a structured binding, maybe it's a misplaced + // array declarator. + // FIXME: Consume the l_square first so we don't need extra lookahead for + // this. + if (!(NextToken().is(tok::identifier) && + GetLookAheadToken(2).isOneOf(tok::comma, tok::r_square)) && + !(NextToken().is(tok::r_square) && + GetLookAheadToken(2).isOneOf(tok::equal, tok::l_brace))) + return ParseMisplacedBracketDeclarator(D); + + BalancedDelimiterTracker T(*this, tok::l_square); + T.consumeOpen(); + + SmallVector<DecompositionDeclarator::Binding, 32> Bindings; + while (Tok.isNot(tok::r_square)) { + if (!Bindings.empty()) { + if (Tok.is(tok::comma)) + ConsumeToken(); + else { + if (Tok.is(tok::identifier)) { + SourceLocation EndLoc = getEndOfPreviousToken(); + Diag(EndLoc, diag::err_expected) + << tok::comma << FixItHint::CreateInsertion(EndLoc, ","); + } else { + Diag(Tok, diag::err_expected_comma_or_rsquare); + } + + SkipUntil(tok::r_square, tok::comma, tok::identifier, + StopAtSemi | StopBeforeMatch); + if (Tok.is(tok::comma)) + ConsumeToken(); + else if (Tok.isNot(tok::identifier)) + break; + } + } + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected) << tok::identifier; + break; + } + + Bindings.push_back({Tok.getIdentifierInfo(), Tok.getLocation()}); + ConsumeToken(); + } + + if (Tok.isNot(tok::r_square)) + // We've already diagnosed a problem here. + T.skipToEnd(); + else { + // C++17 does not allow the identifier-list in a structured binding + // to be empty. + if (Bindings.empty()) + Diag(Tok.getLocation(), diag::ext_decomp_decl_empty); + + T.consumeClose(); + } + + return D.setDecompositionBindings(T.getOpenLocation(), Bindings, + T.getCloseLocation()); +} + /// ParseParenDeclarator - We parsed the declarator D up to a paren. This is /// only called before the identifier, so these are most likely just grouping /// parens for precedence. If we find that these are actually function diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index b09d7dbc12..f0b81024fe 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -1003,6 +1003,7 @@ Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro, // return y; // } // }; + // } // If x was not const, the second use would require 'L' to capture, and // that would be an error. diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 7703c33b87..556fbf337b 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -74,11 +74,18 @@ bool Parser::isCXXDeclarationStatement() { /// /// simple-declaration: /// decl-specifier-seq init-declarator-list[opt] ';' +/// decl-specifier-seq ref-qualifier[opt] '[' identifier-list ']' +/// brace-or-equal-initializer ';' [C++17] /// /// (if AllowForRangeDecl specified) /// for ( for-range-declaration : for-range-initializer ) statement +/// /// for-range-declaration: -/// attribute-specifier-seqopt type-specifier-seq declarator +/// decl-specifier-seq declarator +/// decl-specifier-seq ref-qualifier[opt] '[' identifier-list ']' +/// +/// In any of the above cases there can be a preceding attribute-specifier-seq, +/// but the caller is expected to handle that. bool Parser::isCXXSimpleDeclaration(bool AllowForRangeDecl) { // C++ 6.8p1: // There is an ambiguity in the grammar involving expression-statements and diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index b9d2843b05..c294658c03 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -220,11 +220,11 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, // parameter list there (in an effort to avoid new/delete traffic). If it // is already used (consider a function returning a function pointer) or too // small (function with too many parameters), go to the heap. - if (!TheDeclarator.InlineParamsUsed && + if (!TheDeclarator.InlineStorageUsed && NumParams <= llvm::array_lengthof(TheDeclarator.InlineParams)) { I.Fun.Params = TheDeclarator.InlineParams; I.Fun.DeleteParams = false; - TheDeclarator.InlineParamsUsed = true; + TheDeclarator.InlineStorageUsed = true; } else { I.Fun.Params = new DeclaratorChunk::ParamInfo[NumParams]; I.Fun.DeleteParams = true; @@ -258,6 +258,38 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, return I; } +void Declarator::setDecompositionBindings( + SourceLocation LSquareLoc, + ArrayRef<DecompositionDeclarator::Binding> Bindings, + SourceLocation RSquareLoc) { + assert(!hasName() && "declarator given multiple names!"); + + BindingGroup.LSquareLoc = LSquareLoc; + BindingGroup.RSquareLoc = RSquareLoc; + BindingGroup.NumBindings = Bindings.size(); + Range.setEnd(RSquareLoc); + + // We're now past the identifier. + SetIdentifier(nullptr, LSquareLoc); + Name.EndLocation = RSquareLoc; + + // Allocate storage for bindings and stash them away. + if (Bindings.size()) { + if (!InlineStorageUsed && + Bindings.size() <= llvm::array_lengthof(InlineBindings)) { + BindingGroup.Bindings = InlineBindings; + BindingGroup.DeleteBindings = false; + InlineStorageUsed = true; + } else { + BindingGroup.Bindings = + new DecompositionDeclarator::Binding[Bindings.size()]; + BindingGroup.DeleteBindings = true; + } + std::uninitialized_copy(Bindings.begin(), Bindings.end(), + BindingGroup.Bindings); + } +} + bool Declarator::isDeclarationOfFunction() const { for (unsigned i = 0, i_end = DeclTypeInfo.size(); i < i_end; ++i) { switch (DeclTypeInfo[i].Kind) { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 683905b148..ea7a31dbc7 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -43,6 +43,7 @@ #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" #include <algorithm> #include <cstring> @@ -4921,7 +4922,9 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D, // All of these full declarators require an identifier. If it doesn't have // one, the ParsedFreeStandingDeclSpec action should be used. - if (!Name) { + if (D.isDecompositionDeclarator()) { + return ActOnDecompositionDeclarator(S, D, TemplateParamLists); + } else if (!Name) { if (!D.isInvalidType()) // Reject this if we think it is valid. Diag(D.getDeclSpec().getLocStart(), diag::err_declarator_need_ident) @@ -5845,14 +5848,30 @@ static bool isDeclExternC(const Decl *D) { llvm_unreachable("Unknown type of decl!"); } -NamedDecl * -Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, - TypeSourceInfo *TInfo, LookupResult &Previous, - MultiTemplateParamsArg TemplateParamLists, - bool &AddToScope) { +NamedDecl *Sema::ActOnVariableDeclarator( + Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, + LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, + bool &AddToScope, ArrayRef<BindingDecl *> Bindings) { QualType R = TInfo->getType(); DeclarationName Name = GetNameForDeclarator(D).getName(); + IdentifierInfo *II = Name.getAsIdentifierInfo(); + + if (D.isDecompositionDeclarator()) { + AddToScope = false; + // Take the name of the first declarator as our name for diagnostic + // purposes. + auto &Decomp = D.getDecompositionDeclarator(); + if (!Decomp.bindings().empty()) { + II = Decomp.bindings()[0].Name; + Name = II; + } + } else if (!II) { + Diag(D.getIdentifierLoc(), diag::err_bad_variable_name) + << Name; + return nullptr; + } + // OpenCL v2.0 s6.9.b - Image type can only be used as a function argument. // OpenCL v2.0 s6.13.16.1 - Pipe type can only be used as a function // argument. @@ -5920,13 +5939,6 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc()); } - IdentifierInfo *II = Name.getAsIdentifierInfo(); - if (!II) { - Diag(D.getIdentifierLoc(), diag::err_bad_variable_name) - << Name; - return nullptr; - } - DiagnoseFunctionSpecifiers(D.getDeclSpec()); if (!DC->isRecord() && S->getFnParent() == nullptr) { @@ -6095,6 +6107,10 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, return nullptr; NewVD = cast<VarDecl>(Res.get()); AddToScope = false; + } else if (D.isDecompositionDeclarator()) { + NewVD = DecompositionDecl::Create(Context, DC, D.getLocStart(), + D.getIdentifierLoc(), R, TInfo, SC, + Bindings); } else NewVD = VarDecl::Create(Context, DC, D.getLocStart(), D.getIdentifierLoc(), II, R, TInfo, SC); @@ -6200,8 +6216,13 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (NewTemplate) NewTemplate->setLexicalDeclContext(CurContext); - if (IsLocalExternDecl) - NewVD->setLocalExternDecl(); + if (IsLocalExternDecl) { + if (D.isDecompositionDeclarator()) + for (auto *B : Bindings) + B->setLocalExternDecl(); + else + NewVD->setLocalExternDecl(); + } bool EmitTLSUnsupportedError = false; if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) { @@ -6273,6 +6294,8 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewVD->setModulePrivate(); if (NewTemplate) NewTemplate->setModulePrivate(); + for (auto *B : Bindings) + B->setModulePrivate(); } } @@ -6480,7 +6503,7 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, } // Special handling of variable named 'main'. - if (Name.isIdentifier() && Name.getAsIdentifierInfo()->isStr("main") && + if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") && NewVD->getDeclContext()->getRedeclContext()->isTranslationUnit() && !getLangOpts().Freestanding && !NewVD->getDescribedVarTemplate()) { @@ -6511,6 +6534,157 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, return NewVD; } +NamedDecl * +Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, + MultiTemplateParamsArg TemplateParamLists) { + assert(D.isDecompositionDeclarator()); + const DecompositionDeclarator &Decomp = D.getDecompositionDeclarator(); + + // The syntax only allows a decomposition declarator as a simple-declaration + // or a for-range-declaration, but we parse it in more cases than that. + if (!D.mayHaveDecompositionDeclarator()) { + Diag(Decomp.getLSquareLoc(), diag::err_decomp_decl_context) + << Decomp.getSourceRange(); + return nullptr; + } + + if (!TemplateParamLists.empty()) { + // FIXME: There's no rule against this, but there are also no rules that + // would actually make it usable, so we reject it for now. + Diag(TemplateParamLists.front()->getTemplateLoc(), + diag::err_decomp_decl_template); + return nullptr; + } + + Diag(Decomp.getLSquareLoc(), getLangOpts().CPlusPlus1z + ? diag::warn_cxx14_compat_decomp_decl + : diag::ext_decomp_decl) + << Decomp.getSourceRange(); + + // The semantic context is always just the current context. + DeclContext *const DC = CurContext; + + // C++1z [dcl.dcl]/8: + // The decl-specifier-seq shall contain only the type-specifier auto + // and cv-qualifiers. + auto &DS = D.getDeclSpec(); + { + SmallVector<StringRef, 8> BadSpecifiers; + SmallVector<SourceLocation, 8> BadSpecifierLocs; + if (auto SCS = DS.getStorageClassSpec()) { + BadSpecifiers.push_back(DeclSpec::getSpecifierName(SCS)); + BadSpecifierLocs.push_back(DS.getStorageClassSpecLoc()); + } + if (auto TSCS = DS.getThreadStorageClassSpec()) { + BadSpecifiers.push_back(DeclSpec::getSpecifierName(TSCS)); + BadSpecifierLocs.push_back(DS.getThreadStorageClassSpecLoc()); + } + if (DS.isConstexprSpecified()) { + BadSpecifiers.push_back("constexpr"); + BadSpecifierLocs.push_back(DS.getConstexprSpecLoc()); + } + if (DS.isInlineSpecified()) { + BadSpecifiers.push_back("inline"); + BadSpecifierLocs.push_back(DS.getInlineSpecLoc()); + } + if (!BadSpecifiers.empty()) { + auto &&Err = Diag(BadSpecifierLocs.front(), diag::err_decomp_decl_spec); + Err << (int)BadSpecifiers.size() + << llvm::join(BadSpecifiers.begin(), BadSpecifiers.end(), " "); + // Don't add FixItHints to remove the specifiers; we do still respect + // them when building the underlying variable. + for (auto Loc : BadSpecifierLocs) + Err << SourceRange(Loc, Loc); + } + // We can't recover from it being declared as a typedef. + if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) + return nullptr; + } + + TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); + QualType R = TInfo->getType(); + + if (DiagnoseUnexpandedParameterPack(D.getIdentifierLoc(), TInfo, + UPPC_DeclarationType)) + D.setInvalidType(); + + // The syntax only allows a single ref-qualifier prior to the decomposition + // declarator. No other declarator chunks are permitted. Also check the type + // specifier here. + if (DS.getTypeSpecType() != DeclSpec::TST_auto || + D.hasGroupingParens() || D.getNumTypeObjects() > 1 || + (D.getNumTypeObjects() == 1 && + D.getTypeObject(0).Kind != DeclaratorChunk::Reference)) { + Diag(Decomp.getLSquareLoc(), + (D.hasGroupingParens() || + (D.getNumTypeObjects() && + D.getTypeObject(0).Kind == DeclaratorChunk::Paren)) + ? diag::err_decomp_decl_parens + : diag::err_decomp_decl_type) + << R; + + // In most cases, there's no actual problem with an explicitly-specified + // type, but a function type won't work here, and ActOnVariableDeclarator + // shouldn't be called for such a type. + if (R->isFunctionType()) + D.setInvalidType(); + } + + // Build the BindingDecls. + SmallVector<BindingDecl*, 8> Bindings; + + // Build the BindingDecls. + for (auto &B : D.getDecompositionDeclarator().bindings()) { + // Check for name conflicts. + DeclarationNameInfo NameInfo(B.Name, B.NameLoc); + LookupResult Previous(*this, NameInfo, LookupOrdinaryName, + ForRedeclaration); + LookupName(Previous, S, + /*CreateBuiltins*/DC->getRedeclContext()->isTranslationUnit()); + + // It's not permitted to shadow a template parameter name. + if (Previous.isSingleResult() && + Previous.getFoundDecl()->isTemplateParameter()) { + DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), + Previous.getFoundDecl()); + Previous.clear(); + } + + bool ConsiderLinkage = DC->isFunctionOrMethod() && + DS.getStorageClassSpec() == DeclSpec::SCS_extern; + FilterLookupForScope(Previous, DC, S, ConsiderLinkage, + /*AllowInlineNamespace*/false); + if (!Previous.empty()) { + auto *Old = Previous.getRepresentativeDecl(); + Diag(B.NameLoc, diag::err_redefinition) << B.Name; + Diag(Old->getLocation(), diag::note_previous_definition); + } + + auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name); + PushOnScopeChains(BD, S, true); + Bindings.push_back(BD); + ParsingInitForAutoVars.insert(BD); + } + + // There are no prior lookup results for the variable itself, because it + // is unnamed. + DeclarationNameInfo NameInfo((IdentifierInfo *)nullptr, + Decomp.getLSquareLoc()); + LookupResult Previous(*this, NameInfo, LookupOrdinaryName, ForRedeclaration); + + // Build the variable that holds the non-decomposed object. + bool AddToScope = true; + NamedDecl *New = + ActOnVariableDeclarator(S, D, DC, TInfo, Previous, + MultiTemplateParamsArg(), AddToScope, Bindings); + CurContext->addHiddenDecl(New); + + if (isInOpenMPDeclareTargetContext()) + checkDeclIsAllowedInOpenMPTarget(nullptr, New); + + return New; +} + /// Enum describing the %select options in diag::warn_decl_shadow. enum ShadowedDeclKind { SDK_Local, SDK_Global, SDK_StaticMember, SDK_Field }; @@ -9956,6 +10130,11 @@ void Sema::ActOnInitializerError(Decl *D) { VarDecl *VD = dyn_cast<VarDecl>(D); if (!VD) return; + // Bindings are not usable if we can't make sense of the initializer. + if (auto *DD = dyn_cast<DecompositionDecl>(D)) + for (auto *BD : DD->bindings()) + BD->setInvalidDecl(); + // Auto types are meaningless if we can't make sense of the initializer. if (ParsingInitForAutoVars.count(D)) { D->setInvalidDecl(); @@ -10501,6 +10680,10 @@ Sema::FinalizeDeclaration(Decl *ThisDecl) { if (!VD) return; + if (auto *DD = dyn_cast<DecompositionDecl>(ThisDecl)) + for (auto *BD : DD->bindings()) + FinalizeDeclaration(BD); + checkAttributesAfterMerging(*this, *VD); // Perform TLS alignment check here after attributes attached to the variable @@ -10679,13 +10862,36 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, Decls.push_back(DS.getRepAsDecl()); DeclaratorDecl *FirstDeclaratorInGroup = nullptr; - for (unsigned i = 0, e = Group.size(); i != e; ++i) + DecompositionDecl *FirstDecompDeclaratorInGroup = nullptr; + bool DiagnosedMultipleDecomps = false; + + for (unsigned i = 0, e = Group.size(); i != e; ++i) { if (Decl *D = Group[i]) { - if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) - if (!FirstDeclaratorInGroup) - FirstDeclaratorInGroup = DD; + auto *DD = dyn_cast<DeclaratorDecl>(D); + if (DD && !FirstDeclaratorInGroup) + FirstDeclaratorInGroup = DD; + + auto *Decomp = dyn_cast<DecompositionDecl>(D); + if (Decomp && !FirstDecompDeclaratorInGroup) + FirstDecompDeclaratorInGroup = Decomp; + + // A decomposition declaration cannot be combined with any other + // declaration in the same group. + auto *OtherDD = FirstDeclaratorInGroup; + if (OtherDD == FirstDecompDeclaratorInGroup) + OtherDD = DD; + if (OtherDD && FirstDecompDeclaratorInGroup && + OtherDD != FirstDecompDeclaratorInGroup && + !DiagnosedMultipleDecomps) { + Diag(FirstDecompDeclaratorInGroup->getLocation(), + diag::err_decomp_decl_not_alone) + << OtherDD->getSourceRange(); + DiagnosedMultipleDecomps = true; + } + Decls.push_back(D); } + } if (DeclSpec::isDeclRep(DS.getTypeSpecType())) { if (TagDecl *Tag = dyn_cast_or_null<TagDecl>(DS.getRepAsDecl())) { @@ -13351,6 +13557,13 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, Declarator &D, Expr *BitWidth, InClassInitStyle InitStyle, AccessSpecifier AS) { + if (D.isDecompositionDeclarator()) { + const DecompositionDeclarator &Decomp = D.getDecompositionDeclarator(); + Diag(Decomp.getLSquareLoc(), diag::err_decomp_decl_context) + << Decomp.getSourceRange(); + return nullptr; + } + IdentifierInfo *II = D.getIdentifier(); SourceLocation Loc = DeclStart; if (II) Loc = D.getIdentifierLoc(); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 8efce20a54..3bcce07a3a 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2192,7 +2192,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, } else { Member = HandleField(S, cast<CXXRecordDecl>(CurContext), Loc, D, BitWidth, InitStyle, AS); - assert(Member && "HandleField never returns null"); + if (!Member) + return nullptr; } } else { Member = HandleDeclarator(S, D, TemplateParameterLists); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 0abe75ab2d..6322e15826 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -340,10 +340,15 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, // See if this is an auto-typed variable whose initializer we are parsing. if (ParsingInitForAutoVars.count(D)) { - const AutoType *AT = cast<VarDecl>(D)->getType()->getContainedAutoType(); + if (isa<BindingDecl>(D)) { + Diag(Loc, diag::err_binding_cannot_appear_in_own_initializer) + << D->getDeclName(); + } else { + const AutoType *AT = cast<VarDecl>(D)->getType()->getContainedAutoType(); - Diag(Loc, diag::err_auto_variable_cannot_appear_in_own_initializer) - << D->getDeclName() << (unsigned)AT->getKeyword(); + Diag(Loc, diag::err_auto_variable_cannot_appear_in_own_initializer) + << D->getDeclName() << (unsigned)AT->getKeyword(); + } return true; } @@ -2939,6 +2944,8 @@ ExprResult Sema::BuildDeclarationNameExpr( case Decl::Var: case Decl::VarTemplateSpecialization: case Decl::VarTemplatePartialSpecialization: + case Decl::Decomposition: + case Decl::Binding: case Decl::OMPCapturedExpr: // In C, "extern void blah;" is valid and is an r-value. if (!getLangOpts().CPlusPlus && diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 6a213953ec..967c2beec4 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -598,6 +598,16 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { return Inst; } +Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) { + return BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(), + D->getIdentifier()); +} + +Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) { + // FIXME: Instantiate bindings and pass them in. + return VisitVarDecl(D, /*InstantiatingVarTemplate=*/false); +} + Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { return VisitVarDecl(D, /*InstantiatingVarTemplate=*/false); } diff --git a/lib/Serialization/ASTCommon.cpp b/lib/Serialization/ASTCommon.cpp index 22ead2b57c..07d0c1c9ae 100644 --- a/lib/Serialization/ASTCommon.cpp +++ b/lib/Serialization/ASTCommon.cpp @@ -307,6 +307,8 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::OMPCapturedExpr: case Decl::OMPDeclareReduction: case Decl::BuiltinTemplate: + case Decl::Decomposition: + case Decl::Binding: return false; // These indirectly derive from Redeclarable<T> but are not actually |