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