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:
parent
eea7c267b9
commit
bdb84f374c
|
|
@ -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#
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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 '+'">;
|
||||
|
|
|
|||
|
|
@ -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<
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue