P0217R3: Parsing support and framework for AST representation of C++1z

decomposition declarations.

There are a couple of things in the wording that seem strange here:
decomposition declarations are permitted at namespace scope (which we partially
support here) and they are permitted as the declaration in a template (which we
reject).

llvm-svn: 276492
This commit is contained in:
Richard Smith 2016-07-22 23:36:59 +00:00
parent eea7c267b9
commit bdb84f374c
26 changed files with 879 additions and 62 deletions

View File

@ -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#

View File

@ -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, {

View File

@ -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>;

View File

@ -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 '+'">;

View File

@ -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<

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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());
}

View File

@ -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:

View File

@ -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,

View File

@ -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(); }

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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) {

View File

@ -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();

View File

@ -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);

View File

@ -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 &&

View File

@ -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);
}

View File

@ -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

View File

@ -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}}
}

View File

@ -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

View File

@ -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);