From bdb84f374cde7736ca68d5db2c2ecf5468346710 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 22 Jul 2016 23:36:59 +0000 Subject: [PATCH] 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). llvm-svn: 276492 --- clang/include/clang/AST/DeclCXX.h | 90 +++++++- clang/include/clang/AST/RecursiveASTVisitor.h | 12 + clang/include/clang/Basic/DeclNodes.td | 2 + clang/include/clang/Basic/DiagnosticParseKinds.td | 4 + clang/include/clang/Basic/DiagnosticSemaKinds.td | 23 ++ clang/include/clang/Parse/Parser.h | 5 + clang/include/clang/Sema/DeclSpec.h | 172 ++++++++++++--- clang/include/clang/Sema/Sema.h | 7 +- clang/lib/AST/ASTDumper.cpp | 7 + clang/lib/AST/DeclBase.cpp | 2 + clang/lib/AST/DeclCXX.cpp | 38 ++++ clang/lib/AST/ItaniumMangle.cpp | 20 +- clang/lib/CodeGen/CGDecl.cpp | 5 +- clang/lib/CodeGen/CodeGenModule.cpp | 1 + clang/lib/Parse/ParseDecl.cpp | 76 ++++++- clang/lib/Parse/ParseExprCXX.cpp | 1 + clang/lib/Parse/ParseTentative.cpp | 9 +- clang/lib/Sema/DeclSpec.cpp | 36 ++- clang/lib/Sema/SemaDecl.cpp | 253 ++++++++++++++++++++-- clang/lib/Sema/SemaDeclCXX.cpp | 3 +- clang/lib/Sema/SemaExpr.cpp | 13 +- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 10 + clang/lib/Serialization/ASTCommon.cpp | 2 + clang/test/Parser/cxx1z-decomposition.cpp | 135 ++++++++++++ clang/test/SemaCXX/cxx1z-decomposition.cpp | 11 + clang/tools/libclang/CIndex.cpp | 4 +- 26 files changed, 879 insertions(+), 62 deletions(-) create mode 100644 clang/test/Parser/cxx1z-decomposition.cpp create mode 100644 clang/test/SemaCXX/cxx1z-decomposition.cpp diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 7c53554..cd42e05 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -139,7 +139,6 @@ public: static bool classofKind(Kind K) { return K == AccessSpec; } }; - /// \brief Represents a base class of a C++ class. /// /// Each CXXBaseSpecifier represents a single, direct base class (or @@ -3366,6 +3365,95 @@ public: friend class ASTDeclReader; }; +/// A binding in a decomposition declaration. For instance, given: +/// +/// int n[3]; +/// auto &[a, b, c] = n; +/// +/// a, b, and c are BindingDecls, whose bindings are the expressions +/// x[0], x[1], and x[2] respectively, where x is the implicit +/// DecompositionDecl of type 'int (&)[3]'. +class BindingDecl : public ValueDecl { + void anchor() override; + + /// The binding represented by this declaration. References to this + /// declaration are effectively equivalent to this expression (except + /// that it is only evaluated once at the point of declaration of the + /// binding). + Expr *Binding; + + BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id) + : ValueDecl(Decl::Binding, DC, IdLoc, Id, QualType()), Binding(nullptr) {} + +public: + static BindingDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation IdLoc, IdentifierInfo *Id); + static BindingDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + /// Get the expression to which this declaration is bound. This may be null + /// in two different cases: while parsing the initializer for the + /// decomposition declaration, and when the initializer is type-dependent. + Expr *getBinding() const { return Binding; } + + /// Set the binding for this BindingDecl, along with its declared type (which + /// should be a possibly-cv-qualified form of the type of the binding, or a + /// reference to such a type). + void setBinding(QualType DeclaredType, Expr *Binding) { + setType(DeclaredType); + this->Binding = Binding; + } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == Decl::Binding; } +}; + +/// A decomposition declaration. For instance, given: +/// +/// int n[3]; +/// auto &[a, b, c] = n; +/// +/// the second line declares a DecompositionDecl of type 'int (&)[3]', and +/// three BindingDecls (named a, b, and c). An instance of this class is always +/// unnamed, but behaves in almost all other respects like a VarDecl. +class DecompositionDecl final + : public VarDecl, + private llvm::TrailingObjects { + void anchor() override; + + /// The number of BindingDecl*s following this object. + unsigned NumBindings; + + DecompositionDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, + SourceLocation LSquareLoc, QualType T, + TypeSourceInfo *TInfo, StorageClass SC, + ArrayRef Bindings) + : VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo, + SC), + NumBindings(Bindings.size()) { + std::uninitialized_copy(Bindings.begin(), Bindings.end(), + getTrailingObjects()); + } + +public: + static DecompositionDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation StartLoc, + SourceLocation LSquareLoc, + QualType T, TypeSourceInfo *TInfo, + StorageClass S, + ArrayRef Bindings); + static DecompositionDecl *CreateDeserialized(ASTContext &C, unsigned ID, + unsigned NumBindings); + + ArrayRef bindings() const { + return llvm::makeArrayRef(getTrailingObjects(), NumBindings); + } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == Decomposition; } + + friend TrailingObjects; +}; + /// An instance of this class represents the declaration of a property /// member. This is a Microsoft extension to C++, first introduced in /// Visual Studio .NET 2003 as a parallel to similar features in C# diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 7f08da8..71d7d19 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1803,6 +1803,18 @@ bool RecursiveASTVisitor::TraverseDeclaratorHelper(DeclaratorDecl *D) { return true; } +DEF_TRAVERSE_DECL(DecompositionDecl, { + TRY_TO(TraverseVarHelper(D)); + for (auto *Binding : D->bindings()) { + TRY_TO(TraverseDecl(Binding)); + } +}) + +DEF_TRAVERSE_DECL(BindingDecl, { + if (getDerived().shouldVisitImplicitCode()) + TRY_TO(TraverseStmt(D->getBinding())); +}) + DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); }) DEF_TRAVERSE_DECL(FieldDecl, { diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 4f7bbc0..f29c399 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -37,6 +37,7 @@ def Named : Decl<1>; def EnumConstant : DDecl; def UnresolvedUsingValue : DDecl; def IndirectField : DDecl; + def Binding : DDecl; def OMPDeclareReduction : DDecl, DeclContext; def Declarator : DDecl; def Field : DDecl; @@ -54,6 +55,7 @@ def Named : Decl<1>; : DDecl; def ImplicitParam : DDecl; def ParmVar : DDecl; + def Decomposition : DDecl; def OMPCapturedExpr : DDecl; def NonTypeTemplateParm : DDecl; def Template : DDecl; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index e5c6468..61a28e7 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -355,6 +355,10 @@ def err_expected_end_of_enumerator : Error< def err_expected_coloncolon_after_super : Error< "expected '::' after '__super'">; +def ext_decomp_decl_empty : ExtWarn< + "ISO C++1z does not allow a decomposition group to be empty">, + InGroup>; + /// Objective-C parser diagnostics def err_expected_minus_or_plus : Error< "method type specifier must start with '-' or '+'">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 129ebb2..dacc56f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -365,6 +365,26 @@ def warn_modifying_shadowing_decl : "field of %1">, InGroup, DefaultIgnore; +// C++ decomposition declarations +def err_decomp_decl_context : Error< + "decomposition declaration not permitted in this context">; +def warn_cxx14_compat_decomp_decl : Warning< + "decomposition declarations are incompatible with " + "C++ standards before C++1z">, DefaultIgnore, InGroup; +def ext_decomp_decl : ExtWarn< + "decomposition declarations are a C++1z extension">, InGroup; +def err_decomp_decl_spec : Error< + "decomposition declaration cannot be declared " + "%plural{1:'%1'|:with '%1' specifiers}0">; +def err_decomp_decl_type : Error< + "decomposition declaration cannot be declared with type %0; " + "declared type must be 'auto' or reference to 'auto'">; +def err_decomp_decl_parens : Error< + "decomposition declaration cannot be declared with parentheses">; +def err_decomp_decl_template : Error< + "decomposition declaration template not supported">; +def err_decomp_decl_not_alone : Error< + "decomposition declaration must be the only declaration in its group">; // C++ using declarations def err_using_requires_qualname : Error< @@ -1756,6 +1776,9 @@ def warn_cxx98_compat_auto_type_specifier : Warning< def err_auto_variable_cannot_appear_in_own_initializer : Error< "variable %0 declared with %select{'auto'|'decltype(auto)'|'__auto_type'}1 " "type cannot appear in its own initializer">; +def err_binding_cannot_appear_in_own_initializer : Error< + "binding %0 cannot appear in the initializer of its own " + "decomposition declaration">; def err_illegal_decl_array_of_auto : Error< "'%0' declared as array of %1">; def err_new_array_of_auto : Error< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 5838a44..2e5390a 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -314,6 +314,10 @@ public: return true; } + SourceLocation getEndOfPreviousToken() { + return PP.getLocForEndOfToken(PrevTokLocation); + } + /// Retrieve the underscored keyword (_Nonnull, _Nullable) that corresponds /// to the given nullability kind. IdentifierInfo *getNullabilityKeyword(NullabilityKind nullability) { @@ -2352,6 +2356,7 @@ private: bool AtomicAllowed = true, bool IdentifierRequired = false); void ParseDirectDeclarator(Declarator &D); + void ParseDecompositionDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); void ParseFunctionDeclarator(Declarator &D, ParsedAttributes &attrs, diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index afcd791..d48a1c2 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1188,7 +1188,7 @@ struct DeclaratorChunk { /// complete. Non-NULL indicates that there is a default argument. CachedTokens *DefaultArgTokens; - ParamInfo() {} + ParamInfo() = default; ParamInfo(IdentifierInfo *ident, SourceLocation iloc, Decl *param, CachedTokens *DefArgTokens = nullptr) @@ -1600,6 +1600,58 @@ struct DeclaratorChunk { } }; +/// A parsed C++17 decomposition declarator of the form +/// '[' identifier-list ']' +class DecompositionDeclarator { +public: + struct Binding { + IdentifierInfo *Name; + SourceLocation NameLoc; + }; + +private: + /// The locations of the '[' and ']' tokens. + SourceLocation LSquareLoc, RSquareLoc; + + /// The bindings. + Binding *Bindings; + unsigned NumBindings : 31; + unsigned DeleteBindings : 1; + + friend class Declarator; + +public: + DecompositionDeclarator() + : Bindings(nullptr), NumBindings(0), DeleteBindings(false) {} + DecompositionDeclarator(const DecompositionDeclarator &G) = delete; + DecompositionDeclarator &operator=(const DecompositionDeclarator &G) = delete; + ~DecompositionDeclarator() { + if (DeleteBindings) + delete[] Bindings; + } + + void clear() { + LSquareLoc = RSquareLoc = SourceLocation(); + if (DeleteBindings) + delete[] Bindings; + Bindings = nullptr; + NumBindings = 0; + DeleteBindings = false; + } + + ArrayRef bindings() const { + return llvm::makeArrayRef(Bindings, NumBindings); + } + + bool isSet() const { return LSquareLoc.isValid(); } + + SourceLocation getLSquareLoc() const { return LSquareLoc; } + SourceLocation getRSquareLoc() const { return RSquareLoc; } + SourceRange getSourceRange() const { + return SourceRange(LSquareLoc, RSquareLoc); + } +}; + /// \brief Described the kind of function definition (if any) provided for /// a function. enum FunctionDefinitionKind { @@ -1658,6 +1710,9 @@ private: /// \brief Where we are parsing this declarator. TheContext Context; + /// The C++17 structured binding, if any. This is an alternative to a Name. + DecompositionDeclarator BindingGroup; + /// DeclTypeInfo - This holds each type that the declarator includes as it is /// parsed. This is pushed from the identifier out, which means that element /// #0 will be the most closely bound to the identifier, and @@ -1679,18 +1734,6 @@ private: /// \brief Is this Declarator a redeclaration? unsigned Redeclaration : 1; - /// Attrs - Attributes. - ParsedAttributes Attrs; - - /// \brief The asm label, if specified. - Expr *AsmLabel; - - /// InlineParams - This is a local array used for the first function decl - /// chunk to avoid going to the heap for the common case when we have one - /// function chunk in the declarator. - DeclaratorChunk::ParamInfo InlineParams[16]; - bool InlineParamsUsed; - /// \brief true if the declaration is preceded by \c __extension__. unsigned Extension : 1; @@ -1700,6 +1743,23 @@ private: /// Indicates whether this is an Objective-C 'weak' property. unsigned ObjCWeakProperty : 1; + /// Indicates whether the InlineParams / InlineBindings storage has been used. + unsigned InlineStorageUsed : 1; + + /// Attrs - Attributes. + ParsedAttributes Attrs; + + /// \brief The asm label, if specified. + Expr *AsmLabel; + + union { + /// InlineParams - This is a local array used for the first function decl + /// chunk to avoid going to the heap for the common case when we have one + /// function chunk in the declarator. + DeclaratorChunk::ParamInfo InlineParams[16]; + DecompositionDeclarator::Binding InlineBindings[16]; + }; + /// \brief If this is the second or subsequent declarator in this declaration, /// the location of the comma before this declarator. SourceLocation CommaLoc; @@ -1712,14 +1772,12 @@ private: public: Declarator(const DeclSpec &ds, TheContext C) - : DS(ds), Range(ds.getSourceRange()), Context(C), - InvalidType(DS.getTypeSpecType() == DeclSpec::TST_error), - GroupingParens(false), FunctionDefinition(FDK_Declaration), - Redeclaration(false), - Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr), - InlineParamsUsed(false), Extension(false), ObjCIvar(false), - ObjCWeakProperty(false) { - } + : DS(ds), Range(ds.getSourceRange()), Context(C), + InvalidType(DS.getTypeSpecType() == DeclSpec::TST_error), + GroupingParens(false), FunctionDefinition(FDK_Declaration), + Redeclaration(false), Extension(false), ObjCIvar(false), + ObjCWeakProperty(false), InlineStorageUsed(false), + Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr) {} ~Declarator() { clear(); @@ -1746,6 +1804,10 @@ public: /// \brief Retrieve the name specified by this declarator. UnqualifiedId &getName() { return Name; } + + const DecompositionDeclarator &getDecompositionDeclarator() const { + return BindingGroup; + } TheContext getContext() const { return Context; } @@ -1789,13 +1851,14 @@ public: SS.clear(); Name.clear(); Range = DS.getSourceRange(); - + BindingGroup.clear(); + for (unsigned i = 0, e = DeclTypeInfo.size(); i != e; ++i) DeclTypeInfo[i].destroy(); DeclTypeInfo.clear(); Attrs.clear(); AsmLabel = nullptr; - InlineParamsUsed = false; + InlineStorageUsed = false; ObjCIvar = false; ObjCWeakProperty = false; CommaLoc = SourceLocation(); @@ -1906,6 +1969,45 @@ public: llvm_unreachable("unknown context kind!"); } + /// Return true if the context permits a C++17 decomposition declarator. + bool mayHaveDecompositionDeclarator() const { + switch (Context) { + case FileContext: + // FIXME: It's not clear that the proposal meant to allow file-scope + // structured bindings, but it does. + case BlockContext: + case ForContext: + case InitStmtContext: + return true; + + case ConditionContext: + case MemberContext: + case PrototypeContext: + case TemplateParamContext: + // Maybe one day... + return false; + + // These contexts don't allow any kind of non-abstract declarator. + case KNRTypeListContext: + case TypeNameContext: + case AliasDeclContext: + case AliasTemplateContext: + case LambdaExprParameterContext: + case ObjCParameterContext: + case ObjCResultContext: + case CXXNewContext: + case CXXCatchContext: + case ObjCCatchContext: + case BlockLiteralContext: + case LambdaExprContext: + case ConversionIdContext: + case TemplateTypeArgContext: + case TrailingReturnContext: + return false; + } + llvm_unreachable("unknown context kind!"); + } + /// mayBeFollowedByCXXDirectInit - Return true if the declarator can be /// followed by a C++ direct initializer, e.g. "int x(1);". bool mayBeFollowedByCXXDirectInit() const { @@ -1959,14 +2061,22 @@ public: } /// isPastIdentifier - Return true if we have parsed beyond the point where - /// the + /// the name would appear. (This may happen even if we haven't actually parsed + /// a name, perhaps because this context doesn't require one.) bool isPastIdentifier() const { return Name.isValid(); } /// hasName - Whether this declarator has a name, which might be an /// identifier (accessible via getIdentifier()) or some kind of - /// special C++ name (constructor, destructor, etc.). - bool hasName() const { - return Name.getKind() != UnqualifiedId::IK_Identifier || Name.Identifier; + /// special C++ name (constructor, destructor, etc.), or a structured + /// binding (which is not exactly a name, but occupies the same position). + bool hasName() const { + return Name.getKind() != UnqualifiedId::IK_Identifier || Name.Identifier || + isDecompositionDeclarator(); + } + + /// Return whether this declarator is a decomposition declarator. + bool isDecompositionDeclarator() const { + return BindingGroup.isSet(); } IdentifierInfo *getIdentifier() const { @@ -1981,7 +2091,13 @@ public: void SetIdentifier(IdentifierInfo *Id, SourceLocation IdLoc) { Name.setIdentifier(Id, IdLoc); } - + + /// Set the decomposition bindings for this declarator. + void + setDecompositionBindings(SourceLocation LSquareLoc, + ArrayRef Bindings, + SourceLocation RSquareLoc); + /// AddTypeInfo - Add a chunk to this declarator. Also extend the range to /// EndLoc, which should be the last token of the chunk. void AddTypeInfo(const DeclaratorChunk &TI, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index cfc2a1f..a04ab94 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -73,6 +73,7 @@ namespace clang { class ASTWriter; class ArrayType; class AttributeList; + class BindingDecl; class BlockDecl; class CapturedDecl; class CXXBasePath; @@ -1719,7 +1720,11 @@ public: TypeSourceInfo *TInfo, LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, - bool &AddToScope); + bool &AddToScope, + ArrayRef Bindings = None); + NamedDecl * + ActOnDecompositionDeclarator(Scope *S, Declarator &D, + MultiTemplateParamsArg TemplateParamLists); // Returns true if the variable declaration is a redeclaration bool CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous); void CheckVariableDeclarationType(VarDecl *NewVD); diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index 872ba35..1a4207f 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/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/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index e07bf48..8918e18 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/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/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index d069bfd..7e6c725 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/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 Bindings) { + size_t Extra = additionalSizeToAlloc(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(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(); + 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/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 51de561..5b27831 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -593,7 +593,7 @@ bool ItaniumMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) { return false; const VarDecl *VD = dyn_cast(D); - if (VD) { + if (VD && !isa(D)) { // C variables are not mangled. if (VD->isExternC()) return false; @@ -1193,7 +1193,23 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, // ::= 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(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/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 89407cd..e0cb07b 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -114,12 +114,15 @@ void CodeGenFunction::EmitDecl(const Decl &D) { if (CGDebugInfo *DI = getDebugInfo()) DI->EmitUsingDirective(cast(D)); return; - case Decl::Var: { + case Decl::Var: + case Decl::Decomposition: { const VarDecl &VD = cast(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(&D), this); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index f73e85d..96bb5bf 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/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(D)->getDescribedVarTemplate()) return; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 45e1c3e..91a3eff 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/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: +/// '[' 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 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/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index b09d7db..f0b8102 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1003,6 +1003,7 @@ Optional 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/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 7703c33..556fbf3 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/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/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index b9d2843..c294658 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/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 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/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 683905b..ea7a31d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/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 #include @@ -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 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(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 BadSpecifiers; + SmallVector 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 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(D); if (!VD) return; + // Bindings are not usable if we can't make sense of the initializer. + if (auto *DD = dyn_cast(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(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(D)) - if (!FirstDeclaratorInGroup) - FirstDeclaratorInGroup = DD; + auto *DD = dyn_cast(D); + if (DD && !FirstDeclaratorInGroup) + FirstDeclaratorInGroup = DD; + + auto *Decomp = dyn_cast(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(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/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 8efce20..3bcce07 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2192,7 +2192,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, } else { Member = HandleField(S, cast(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/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 0abe75a..6322e15 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/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(D)->getType()->getContainedAutoType(); + if (isa(D)) { + Diag(Loc, diag::err_binding_cannot_appear_in_own_initializer) + << D->getDeclName(); + } else { + const AutoType *AT = cast(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/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 6a21395..967c2be 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/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/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 22ead2b..07d0c1c 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/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 but are not actually diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp new file mode 100644 index 0000000..23e5775 --- /dev/null +++ b/clang/test/Parser/cxx1z-decomposition.cpp @@ -0,0 +1,135 @@ +// RUN: %clang_cc1 -std=c++1z %s -verify -fcxx-exceptions + +struct S { int a, b, c; }; + +// A simple-declaration can be a decompsition declaration. +namespace SimpleDecl { + auto [a_x, b_x, c_x] = S(); + + void f(S s) { + auto [a, b, c] = S(); + { + for (auto [a, b, c] = S();;) {} + if (auto [a, b, c] = S(); true) {} + switch (auto [a, b, c] = S(); 0) { case 0:; } + } + } +} + +// A for-range-declaration can be a decomposition declaration. +namespace ForRangeDecl { + extern S arr[10]; + void h() { + for (auto [a, b, c] : arr) { + } + } +} + +// Other kinds of declaration cannot. +namespace OtherDecl { + // A parameter-declaration is not a simple-declaration. + // This parses as an array declaration. + void f(auto [a, b, c]); // expected-error {{'auto' not allowed in function prototype}} expected-error {{'a'}} + + void g() { + // A condition is not a simple-declaration. + for (; auto [a, b, c] = S(); ) {} // expected-error {{not permitted in this context}} + if (auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} + if (int n; auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} + switch (auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} + switch (int n; auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} + while (auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} + + // An exception-declaration is not a simple-declaration. + try {} + catch (auto [a, b, c]) {} // expected-error {{'auto' not allowed in exception declaration}} expected-error {{'a'}} + } + + // A member-declaration is not a simple-declaration. + class A { + auto [a, b, c] = S(); // expected-error {{not permitted in this context}} + static auto [a, b, c] = S(); // expected-error {{not permitted in this context}} + }; +} + +namespace GoodSpecifiers { + void f() { + int n[1]; + const volatile auto &[a] = n; + } +} + +namespace BadSpecifiers { + typedef int I1[1]; + I1 n; + struct S { int n; } s; + void f() { + // storage-class-specifiers + static auto &[a] = n; // expected-error {{cannot be declared 'static'}} + thread_local auto &[b] = n; // expected-error {{cannot be declared 'thread_local'}} + extern auto &[c] = n; // expected-error {{cannot be declared 'extern'}} + struct S { + mutable auto &[d] = n; // expected-error {{not permitted in this context}} + + // function-specifiers + virtual auto &[e] = n; // expected-error {{not permitted in this context}} + explicit auto &[f] = n; // expected-error {{not permitted in this context}} + + // misc decl-specifiers + friend auto &[g] = n; // expected-error {{'auto' not allowed}} expected-error {{friends can only be classes or functions}} + }; + typedef auto &[h] = n; // expected-error {{cannot be declared 'typedef'}} + constexpr auto &[i] = n; // expected-error {{cannot be declared 'constexpr'}} + + static constexpr thread_local auto &[j] = n; // expected-error {{cannot be declared with 'static thread_local constexpr' specifiers}} + } + inline auto &[k] = n; // expected-error {{cannot be declared 'inline'}} + + const int K = 5; + void g() { + // defining-type-specifiers other than cv-qualifiers and 'auto' + S [a] = s; // expected-error {{cannot be declared with type 'BadSpecifiers::S'}} + decltype(auto) [b] = s; // expected-error {{cannot be declared with type 'decltype(auto)'}} + auto ([c]) = s; // expected-error {{cannot be declared with parentheses}} + + // FIXME: This error is not very good. + auto [d]() = s; // expected-error {{expected ';'}} expected-error {{expected expression}} + auto [e][1] = s; // expected-error {{expected ';'}} expected-error {{requires an initializer}} + + // FIXME: This should fire the 'misplaced array declarator' diagnostic. + int [K] arr = {0}; // expected-error {{expected ';'}} expected-error {{cannot be declared with type 'int'}} + int [5] arr = {0}; // expected-error {{place the brackets after the name}} + + auto *[f] = s; // expected-error {{cannot be declared with type 'auto *'}} expected-error {{incompatible initializer}} + auto S::*[g] = s; // expected-error {{cannot be declared with type 'auto BadSpecifiers::S::*'}} expected-error {{incompatible initializer}} + + // ref-qualifiers are OK. + auto &&[ok_1] = S(); + auto &[ok_2] = s; + + // attributes are OK. + [[]] auto [ok_3] = s; + alignas(S) auto [ok_4] = s; + + // ... but not after the identifier or declarator. + // FIXME: These errors are not very good. + auto [bad_attr_1 [[]]] = s; // expected-error {{attribute list cannot appear here}} expected-error 2{{}} + auto [bad_attr_2] [[]] = s; // expected-error {{expected ';'}} expected-error {{}} + } +} + +namespace MultiDeclarator { + struct S { int n; }; + void f(S s) { + auto [a] = s, [b] = s; // expected-error {{must be the only declaration}} + auto [c] = s, d = s; // expected-error {{must be the only declaration}} + auto e = s, [f] = s; // expected-error {{must be the only declaration}} + auto g = s, h = s, i = s, [j] = s; // expected-error {{must be the only declaration}} + } +} + +namespace Template { + int n[3]; + // FIXME: There's no actual rule against this... + template auto [a, b, c] = n; // expected-error {{decomposition declaration template not supported}} +} diff --git a/clang/test/SemaCXX/cxx1z-decomposition.cpp b/clang/test/SemaCXX/cxx1z-decomposition.cpp new file mode 100644 index 0000000..5343477 --- /dev/null +++ b/clang/test/SemaCXX/cxx1z-decomposition.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +void use_from_own_init() { + auto [a] = a; // expected-error {{binding 'a' cannot appear in the initializer of its own decomposition declaration}} +} + +// FIXME: create correct bindings +// FIXME: template instantiation +// FIXME: ast file support +// FIXME: code generation +// FIXME: constant expression evaluation diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 55804f0..261df78 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -5614,6 +5614,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::TemplateTypeParm: case Decl::EnumConstant: case Decl::Field: + case Decl::Binding: case Decl::MSProperty: case Decl::IndirectField: case Decl::ObjCIvar: @@ -5684,7 +5685,8 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::Var: case Decl::VarTemplateSpecialization: - case Decl::VarTemplatePartialSpecialization: { + case Decl::VarTemplatePartialSpecialization: + case Decl::Decomposition: { // Ask the variable if it has a definition. if (const VarDecl *Def = cast(D)->getDefinition()) return MakeCXCursor(Def, TU); -- 2.7.4