Skip to content

Commit bdb84f3

Browse files
committed
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
1 parent eea7c26 commit bdb84f3

26 files changed

+879
-62
lines changed

clang/include/clang/AST/DeclCXX.h

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ class AccessSpecDecl : public Decl {
139139
static bool classofKind(Kind K) { return K == AccessSpec; }
140140
};
141141

142-
143142
/// \brief Represents a base class of a C++ class.
144143
///
145144
/// Each CXXBaseSpecifier represents a single, direct base class (or
@@ -3366,6 +3365,95 @@ class StaticAssertDecl : public Decl {
33663365
friend class ASTDeclReader;
33673366
};
33683367

3368+
/// A binding in a decomposition declaration. For instance, given:
3369+
///
3370+
/// int n[3];
3371+
/// auto &[a, b, c] = n;
3372+
///
3373+
/// a, b, and c are BindingDecls, whose bindings are the expressions
3374+
/// x[0], x[1], and x[2] respectively, where x is the implicit
3375+
/// DecompositionDecl of type 'int (&)[3]'.
3376+
class BindingDecl : public ValueDecl {
3377+
void anchor() override;
3378+
3379+
/// The binding represented by this declaration. References to this
3380+
/// declaration are effectively equivalent to this expression (except
3381+
/// that it is only evaluated once at the point of declaration of the
3382+
/// binding).
3383+
Expr *Binding;
3384+
3385+
BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id)
3386+
: ValueDecl(Decl::Binding, DC, IdLoc, Id, QualType()), Binding(nullptr) {}
3387+
3388+
public:
3389+
static BindingDecl *Create(ASTContext &C, DeclContext *DC,
3390+
SourceLocation IdLoc, IdentifierInfo *Id);
3391+
static BindingDecl *CreateDeserialized(ASTContext &C, unsigned ID);
3392+
3393+
/// Get the expression to which this declaration is bound. This may be null
3394+
/// in two different cases: while parsing the initializer for the
3395+
/// decomposition declaration, and when the initializer is type-dependent.
3396+
Expr *getBinding() const { return Binding; }
3397+
3398+
/// Set the binding for this BindingDecl, along with its declared type (which
3399+
/// should be a possibly-cv-qualified form of the type of the binding, or a
3400+
/// reference to such a type).
3401+
void setBinding(QualType DeclaredType, Expr *Binding) {
3402+
setType(DeclaredType);
3403+
this->Binding = Binding;
3404+
}
3405+
3406+
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
3407+
static bool classofKind(Kind K) { return K == Decl::Binding; }
3408+
};
3409+
3410+
/// A decomposition declaration. For instance, given:
3411+
///
3412+
/// int n[3];
3413+
/// auto &[a, b, c] = n;
3414+
///
3415+
/// the second line declares a DecompositionDecl of type 'int (&)[3]', and
3416+
/// three BindingDecls (named a, b, and c). An instance of this class is always
3417+
/// unnamed, but behaves in almost all other respects like a VarDecl.
3418+
class DecompositionDecl final
3419+
: public VarDecl,
3420+
private llvm::TrailingObjects<DecompositionDecl, BindingDecl *> {
3421+
void anchor() override;
3422+
3423+
/// The number of BindingDecl*s following this object.
3424+
unsigned NumBindings;
3425+
3426+
DecompositionDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
3427+
SourceLocation LSquareLoc, QualType T,
3428+
TypeSourceInfo *TInfo, StorageClass SC,
3429+
ArrayRef<BindingDecl *> Bindings)
3430+
: VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo,
3431+
SC),
3432+
NumBindings(Bindings.size()) {
3433+
std::uninitialized_copy(Bindings.begin(), Bindings.end(),
3434+
getTrailingObjects<BindingDecl *>());
3435+
}
3436+
3437+
public:
3438+
static DecompositionDecl *Create(ASTContext &C, DeclContext *DC,
3439+
SourceLocation StartLoc,
3440+
SourceLocation LSquareLoc,
3441+
QualType T, TypeSourceInfo *TInfo,
3442+
StorageClass S,
3443+
ArrayRef<BindingDecl *> Bindings);
3444+
static DecompositionDecl *CreateDeserialized(ASTContext &C, unsigned ID,
3445+
unsigned NumBindings);
3446+
3447+
ArrayRef<BindingDecl *> bindings() const {
3448+
return llvm::makeArrayRef(getTrailingObjects<BindingDecl *>(), NumBindings);
3449+
}
3450+
3451+
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
3452+
static bool classofKind(Kind K) { return K == Decomposition; }
3453+
3454+
friend TrailingObjects;
3455+
};
3456+
33693457
/// An instance of this class represents the declaration of a property
33703458
/// member. This is a Microsoft extension to C++, first introduced in
33713459
/// Visual Studio .NET 2003 as a parallel to similar features in C#

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,6 +1803,18 @@ bool RecursiveASTVisitor<Derived>::TraverseDeclaratorHelper(DeclaratorDecl *D) {
18031803
return true;
18041804
}
18051805

1806+
DEF_TRAVERSE_DECL(DecompositionDecl, {
1807+
TRY_TO(TraverseVarHelper(D));
1808+
for (auto *Binding : D->bindings()) {
1809+
TRY_TO(TraverseDecl(Binding));
1810+
}
1811+
})
1812+
1813+
DEF_TRAVERSE_DECL(BindingDecl, {
1814+
if (getDerived().shouldVisitImplicitCode())
1815+
TRY_TO(TraverseStmt(D->getBinding()));
1816+
})
1817+
18061818
DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); })
18071819

18081820
DEF_TRAVERSE_DECL(FieldDecl, {

clang/include/clang/Basic/DeclNodes.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def Named : Decl<1>;
3737
def EnumConstant : DDecl<Value>;
3838
def UnresolvedUsingValue : DDecl<Value>;
3939
def IndirectField : DDecl<Value>;
40+
def Binding : DDecl<Value>;
4041
def OMPDeclareReduction : DDecl<Value>, DeclContext;
4142
def Declarator : DDecl<Value, 1>;
4243
def Field : DDecl<Declarator>;
@@ -54,6 +55,7 @@ def Named : Decl<1>;
5455
: DDecl<VarTemplateSpecialization>;
5556
def ImplicitParam : DDecl<Var>;
5657
def ParmVar : DDecl<Var>;
58+
def Decomposition : DDecl<Var>;
5759
def OMPCapturedExpr : DDecl<Var>;
5860
def NonTypeTemplateParm : DDecl<Declarator>;
5961
def Template : DDecl<Named, 1>;

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ def err_expected_end_of_enumerator : Error<
355355
def err_expected_coloncolon_after_super : Error<
356356
"expected '::' after '__super'">;
357357

358+
def ext_decomp_decl_empty : ExtWarn<
359+
"ISO C++1z does not allow a decomposition group to be empty">,
360+
InGroup<DiagGroup<"empty-decomposition">>;
361+
358362
/// Objective-C parser diagnostics
359363
def err_expected_minus_or_plus : Error<
360364
"method type specifier must start with '-' or '+'">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,26 @@ def warn_modifying_shadowing_decl :
365365
"field of %1">,
366366
InGroup<ShadowFieldInConstructorModified>, DefaultIgnore;
367367

368+
// C++ decomposition declarations
369+
def err_decomp_decl_context : Error<
370+
"decomposition declaration not permitted in this context">;
371+
def warn_cxx14_compat_decomp_decl : Warning<
372+
"decomposition declarations are incompatible with "
373+
"C++ standards before C++1z">, DefaultIgnore, InGroup<CXXPre1zCompat>;
374+
def ext_decomp_decl : ExtWarn<
375+
"decomposition declarations are a C++1z extension">, InGroup<CXX1z>;
376+
def err_decomp_decl_spec : Error<
377+
"decomposition declaration cannot be declared "
378+
"%plural{1:'%1'|:with '%1' specifiers}0">;
379+
def err_decomp_decl_type : Error<
380+
"decomposition declaration cannot be declared with type %0; "
381+
"declared type must be 'auto' or reference to 'auto'">;
382+
def err_decomp_decl_parens : Error<
383+
"decomposition declaration cannot be declared with parentheses">;
384+
def err_decomp_decl_template : Error<
385+
"decomposition declaration template not supported">;
386+
def err_decomp_decl_not_alone : Error<
387+
"decomposition declaration must be the only declaration in its group">;
368388

369389
// C++ using declarations
370390
def err_using_requires_qualname : Error<
@@ -1756,6 +1776,9 @@ def warn_cxx98_compat_auto_type_specifier : Warning<
17561776
def err_auto_variable_cannot_appear_in_own_initializer : Error<
17571777
"variable %0 declared with %select{'auto'|'decltype(auto)'|'__auto_type'}1 "
17581778
"type cannot appear in its own initializer">;
1779+
def err_binding_cannot_appear_in_own_initializer : Error<
1780+
"binding %0 cannot appear in the initializer of its own "
1781+
"decomposition declaration">;
17591782
def err_illegal_decl_array_of_auto : Error<
17601783
"'%0' declared as array of %1">;
17611784
def err_new_array_of_auto : Error<

clang/include/clang/Parse/Parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ class Parser : public CodeCompletionHandler {
314314
return true;
315315
}
316316

317+
SourceLocation getEndOfPreviousToken() {
318+
return PP.getLocForEndOfToken(PrevTokLocation);
319+
}
320+
317321
/// Retrieve the underscored keyword (_Nonnull, _Nullable) that corresponds
318322
/// to the given nullability kind.
319323
IdentifierInfo *getNullabilityKeyword(NullabilityKind nullability) {
@@ -2352,6 +2356,7 @@ class Parser : public CodeCompletionHandler {
23522356
bool AtomicAllowed = true,
23532357
bool IdentifierRequired = false);
23542358
void ParseDirectDeclarator(Declarator &D);
2359+
void ParseDecompositionDeclarator(Declarator &D);
23552360
void ParseParenDeclarator(Declarator &D);
23562361
void ParseFunctionDeclarator(Declarator &D,
23572362
ParsedAttributes &attrs,

0 commit comments

Comments
 (0)