diff options
26 files changed, 879 insertions, 62 deletions
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 7c535546f44..cd42e05ace6 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<DecompositionDecl, BindingDecl *> { +  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<BindingDecl *> Bindings) +      : VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo, +                SC), +        NumBindings(Bindings.size()) { +    std::uninitialized_copy(Bindings.begin(), Bindings.end(), +                            getTrailingObjects<BindingDecl *>()); +  } + +public: +  static DecompositionDecl *Create(ASTContext &C, DeclContext *DC, +                                   SourceLocation StartLoc, +                                   SourceLocation LSquareLoc, +                                   QualType T, TypeSourceInfo *TInfo, +                                   StorageClass S, +                                   ArrayRef<BindingDecl *> Bindings); +  static DecompositionDecl *CreateDeserialized(ASTContext &C, unsigned ID, +                                               unsigned NumBindings); + +  ArrayRef<BindingDecl *> bindings() const { +    return llvm::makeArrayRef(getTrailingObjects<BindingDecl *>(), 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 7f08da804ed..71d7d19fed6 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1803,6 +1803,18 @@ bool RecursiveASTVisitor<Derived>::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 4f7bbc078d9..f29c399c665 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<Value>;      def UnresolvedUsingValue : DDecl<Value>;      def IndirectField : DDecl<Value>; +    def Binding : DDecl<Value>;      def OMPDeclareReduction : DDecl<Value>, DeclContext;      def Declarator : DDecl<Value, 1>;        def Field : DDecl<Declarator>; @@ -54,6 +55,7 @@ def Named : Decl<1>;              : DDecl<VarTemplateSpecialization>;          def ImplicitParam : DDecl<Var>;          def ParmVar : DDecl<Var>; +        def Decomposition : DDecl<Var>;          def OMPCapturedExpr : DDecl<Var>;        def NonTypeTemplateParm : DDecl<Declarator>;    def Template : DDecl<Named, 1>; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index e5c64681e48..61a28e7896a 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<DiagGroup<"empty-decomposition">>; +  /// 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 129ebb27ed2..dacc56f86f2 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<ShadowFieldInConstructorModified>, 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<CXXPre1zCompat>; +def ext_decomp_decl : ExtWarn< +  "decomposition declarations are a C++1z extension">, InGroup<CXX1z>; +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 5838a447c3b..2e5390a5c0f 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 afcd791bca2..d48a1c2420c 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<Binding> 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<DecompositionDeclarator::Binding> 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 cfc2a1f5956..a04ab9496fc 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<BindingDecl *> 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 872ba356a9b..1a4207f5120 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 e07bf480ec6..8918e18d430 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 d069bfdc3dc..7e6c7253483 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<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/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 51de561d5f1..5b278314aee 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<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/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 89407cd70c3..e0cb07b98dd 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<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/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index f73e85dd42b..96bb5bfe027 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<VarDecl>(D)->getDescribedVarTemplate())        return; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 45e1c3e465c..91a3effe426 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: +///         <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/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index b09d7dbc12f..f0b81024fe6 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/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/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 7703c33b878..556fbf337b9 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 b9d2843b055..c294658c031 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<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/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 683905b1480..ea7a31dbc77 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 <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/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 8efce20a547..3bcce07a3a5 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<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/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 0abe75ab2d8..6322e15826a 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<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/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 6a213953ec9..967c2beec4c 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 22ead2b57c7..07d0c1c9ae5 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<T> 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 00000000000..23e5775176d --- /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<typename T> 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 00000000000..53434771ac7 --- /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 55804f0f842..261df787d2b 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<VarDecl>(D)->getDefinition())        return MakeCXCursor(Def, TU);  | 

