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