From 44c247f0f009eec70a193335c8a353d6f8602bfd Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 22 Feb 2013 08:32:16 +0000 Subject: [PATCH] Handle alignas(foo...) pack expansions. llvm-svn: 175875 --- clang/include/clang/AST/Attr.h | 9 ++- clang/include/clang/Basic/DiagnosticParseKinds.td | 2 - clang/include/clang/Sema/AttributeList.h | 30 +++++--- clang/include/clang/Sema/Sema.h | 4 +- clang/lib/Parse/ParseDecl.cpp | 14 ++-- clang/lib/Sema/SemaDeclAttr.cpp | 44 +++++++----- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 86 +++++++++++++++++------ clang/test/SemaCXX/attr-cxx0x.cpp | 17 +++-- clang/test/SemaTemplate/alignas.cpp | 23 ++++++ 9 files changed, 154 insertions(+), 75 deletions(-) create mode 100644 clang/test/SemaTemplate/alignas.cpp diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index fbb0970..61c57f9 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -50,6 +50,8 @@ protected: bool Inherited : 1; + bool IsPackExpansion : 1; + virtual ~Attr(); void* operator new(size_t bytes) throw() { @@ -73,7 +75,7 @@ public: protected: Attr(attr::Kind AK, SourceRange R, unsigned SpellingListIndex = 0) : Range(R), AttrKind(AK), SpellingListIndex(SpellingListIndex), - Inherited(false) {} + Inherited(false), IsPackExpansion(false) {} public: @@ -89,8 +91,11 @@ public: bool isInherited() const { return Inherited; } + void setPackExpansion(bool PE) { IsPackExpansion = PE; } + bool isPackExpansion() const { return IsPackExpansion; } + // Clone this attribute. - virtual Attr* clone(ASTContext &C) const = 0; + virtual Attr *clone(ASTContext &C) const = 0; virtual bool isLateParsed() const { return false; } diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 6035937..2fbd99a 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -516,8 +516,6 @@ def err_attributes_not_allowed : Error<"an attribute list cannot appear here">; def err_l_square_l_square_not_attribute : Error< "C++11 only allows consecutive left square brackets when " "introducing an attribute">; -def err_alignas_pack_exp_unsupported : Error< - "pack expansions in alignment specifiers are not supported yet">; def err_ms_declspec_type : Error< "__declspec attributes must be an identifier or string literal">; def warn_ms_declspec_unknown : Warning< diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index 17444cb..a8d3eb4 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -44,8 +44,9 @@ struct AvailabilityChange { bool isValid() const { return !Version.empty(); } }; -/// AttributeList - Represents GCC's __attribute__ declaration. There are -/// 4 forms of this construct...they are: +/// AttributeList - Represents a syntactic attribute. +/// +/// For a GNU attribute, there are four forms of this construct: /// /// 1: __attribute__(( const )). ParmName/Args/NumArgs will all be unused. /// 2: __attribute__(( mode(byte) )). ParmName used, Args/NumArgs unused. @@ -72,6 +73,7 @@ private: SourceRange AttrRange; SourceLocation ScopeLoc; SourceLocation ParmLoc; + SourceLocation EllipsisLoc; /// The number of expression arguments this attribute has. /// The expressions themselves are stored after the object. @@ -154,11 +156,11 @@ private: IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *parmName, SourceLocation parmLoc, Expr **args, unsigned numArgs, - Syntax syntaxUsed) + Syntax syntaxUsed, SourceLocation ellipsisLoc) : AttrName(attrName), ScopeName(scopeName), ParmName(parmName), AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), - NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false), - UsedAsTypeAttr(false), IsAvailability(false), + EllipsisLoc(ellipsisLoc), NumArgs(numArgs), SyntaxUsed(syntaxUsed), + Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), NextInPosition(0), NextInPool(0) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); @@ -175,7 +177,7 @@ private: const Expr *messageExpr, Syntax syntaxUsed) : AttrName(attrName), ScopeName(scopeName), ParmName(parmName), - AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), + AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), @@ -196,7 +198,7 @@ private: bool mustBeNull, Syntax syntaxUsed) : AttrName(attrName), ScopeName(scopeName), ParmName(argumentKindName), AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(argumentKindLoc), - NumArgs(0), SyntaxUsed(syntaxUsed), + EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), NextInPosition(NULL), NextInPool(NULL) { TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -246,6 +248,9 @@ public: bool isUsedAsTypeAttr() const { return UsedAsTypeAttr; } void setUsedAsTypeAttr() { UsedAsTypeAttr = true; } + bool isPackExpansion() const { return EllipsisLoc.isValid(); } + SourceLocation getEllipsisLoc() const { return EllipsisLoc; } + Kind getKind() const { return Kind(AttrKind); } static Kind getKind(const IdentifierInfo *Name, const IdentifierInfo *Scope, Syntax SyntaxUsed); @@ -459,13 +464,15 @@ public: IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *parmName, SourceLocation parmLoc, Expr **args, unsigned numArgs, - AttributeList::Syntax syntax) { + AttributeList::Syntax syntax, + SourceLocation ellipsisLoc = SourceLocation()) { void *memory = allocate(sizeof(AttributeList) + numArgs * sizeof(Expr*)); return add(new (memory) AttributeList(attrName, attrRange, scopeName, scopeLoc, parmName, parmLoc, - args, numArgs, syntax)); + args, numArgs, syntax, + ellipsisLoc)); } AttributeList *create(IdentifierInfo *attrName, SourceRange attrRange, @@ -599,10 +606,11 @@ public: IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *parmName, SourceLocation parmLoc, Expr **args, unsigned numArgs, - AttributeList::Syntax syntax) { + AttributeList::Syntax syntax, + SourceLocation ellipsisLoc = SourceLocation()) { AttributeList *attr = pool.create(attrName, attrRange, scopeName, scopeLoc, parmName, parmLoc, - args, numArgs, syntax); + args, numArgs, syntax, ellipsisLoc); add(attr); return attr; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index da82325..7101286 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6545,9 +6545,9 @@ public: /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. void AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, - unsigned SpellingListIndex); + unsigned SpellingListIndex, bool IsPackExpansion); void AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *T, - unsigned SpellingListIndex); + unsigned SpellingListIndex, bool IsPackExpansion); /// \brief The kind of conversion being performed. enum CheckedConversionKind { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index a2045327..4628f3d 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2101,7 +2101,7 @@ ExprResult Parser::ParseAlignArgument(SourceLocation Start, /// [C++11] 'alignas' '(' type-id ...[opt] ')' /// [C++11] 'alignas' '(' assignment-expression ...[opt] ')' void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, - SourceLocation *endLoc) { + SourceLocation *EndLoc) { assert((Tok.is(tok::kw_alignas) || Tok.is(tok::kw__Alignas)) && "Not an alignment-specifier!"); @@ -2120,19 +2120,13 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, } T.consumeClose(); - if (endLoc) - *endLoc = T.getCloseLocation(); - - // FIXME: Handle pack-expansions here. - if (EllipsisLoc.isValid()) { - Diag(EllipsisLoc, diag::err_alignas_pack_exp_unsupported); - return; - } + if (EndLoc) + *EndLoc = T.getCloseLocation(); ExprVector ArgExprs; ArgExprs.push_back(ArgExpr.release()); Attrs.addNew(KWName, KWLoc, 0, KWLoc, 0, T.getOpenLocation(), - ArgExprs.data(), 1, AttributeList::AS_Keyword); + ArgExprs.data(), 1, AttributeList::AS_Keyword, EllipsisLoc); } /// ParseDeclarationSpecifiers diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 1142e0d..8e2bac6 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3315,27 +3315,28 @@ static void handleAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { return; } - // FIXME: The C++11 version of this attribute should error out when it is - // used to specify a weaker alignment, rather than being silently - // ignored. This constraint cannot be applied until we have seen - // all the attributes which apply to the variable. - if (Attr.getNumArgs() == 0) { D->addAttr(::new (S.Context) AlignedAttr(Attr.getRange(), S.Context, true, 0, Attr.getAttributeSpellingListIndex())); return; } - S.AddAlignedAttr(Attr.getRange(), D, Attr.getArg(0), - Attr.getAttributeSpellingListIndex()); -} + Expr *E = Attr.getArg(0); + if (Attr.isPackExpansion() && !E->containsUnexpandedParameterPack()) { + S.Diag(Attr.getEllipsisLoc(), + diag::err_pack_expansion_without_parameter_packs); + return; + } -void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, - unsigned SpellingListIndex) { - // FIXME: Handle pack-expansions here. - if (DiagnoseUnexpandedParameterPack(E)) + if (!Attr.isPackExpansion() && S.DiagnoseUnexpandedParameterPack(E)) return; + S.AddAlignedAttr(Attr.getRange(), D, E, Attr.getAttributeSpellingListIndex(), + Attr.isPackExpansion()); +} + +void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, + unsigned SpellingListIndex, bool IsPackExpansion) { AlignedAttr TmpAttr(AttrRange, Context, true, E, SpellingListIndex); SourceLocation AttrLoc = AttrRange.getBegin(); @@ -3379,7 +3380,9 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, if (E->isTypeDependent() || E->isValueDependent()) { // Save dependent expressions in the AST to be instantiated. - D->addAttr(::new (Context) AlignedAttr(TmpAttr)); + AlignedAttr *AA = ::new (Context) AlignedAttr(TmpAttr); + AA->setPackExpansion(IsPackExpansion); + D->addAttr(AA); return; } @@ -3414,17 +3417,20 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, } } - D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, - ICE.take(), SpellingListIndex)); + AlignedAttr *AA = ::new (Context) AlignedAttr(AttrRange, Context, true, + ICE.take(), SpellingListIndex); + AA->setPackExpansion(IsPackExpansion); + D->addAttr(AA); } void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *TS, - unsigned SpellingListIndex) { + unsigned SpellingListIndex, bool IsPackExpansion) { // FIXME: Cache the number on the Attr object if non-dependent? // FIXME: Perform checking of type validity - D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, false, TS, - SpellingListIndex)); - return; + AlignedAttr *AA = ::new (Context) AlignedAttr(AttrRange, Context, false, TS, + SpellingListIndex); + AA->setPackExpansion(IsPackExpansion); + D->addAttr(AA); } void Sema::CheckAlignasUnderalignment(Decl *D) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index defa968..a829070 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -60,6 +60,64 @@ bool TemplateDeclInstantiator::SubstQualifier(const TagDecl *OldDecl, // Include attribute instantiation code. #include "clang/Sema/AttrTemplateInstantiate.inc" +static void instantiateDependentAlignedAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const AlignedAttr *Aligned, Decl *New, bool IsPackExpansion) { + if (Aligned->isAlignmentExpr()) { + // The alignment expression is a constant expression. + EnterExpressionEvaluationContext Unevaluated(S, Sema::ConstantEvaluated); + ExprResult Result = S.SubstExpr(Aligned->getAlignmentExpr(), TemplateArgs); + if (!Result.isInvalid()) + S.AddAlignedAttr(Aligned->getLocation(), New, Result.takeAs(), + Aligned->getSpellingListIndex(), IsPackExpansion); + } else { + TypeSourceInfo *Result = S.SubstType(Aligned->getAlignmentType(), + TemplateArgs, Aligned->getLocation(), + DeclarationName()); + if (Result) + S.AddAlignedAttr(Aligned->getLocation(), New, Result, + Aligned->getSpellingListIndex(), IsPackExpansion); + } +} + +static void instantiateDependentAlignedAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const AlignedAttr *Aligned, Decl *New) { + if (!Aligned->isPackExpansion()) { + instantiateDependentAlignedAttr(S, TemplateArgs, Aligned, New, false); + return; + } + + SmallVector Unexpanded; + if (Aligned->isAlignmentExpr()) + S.collectUnexpandedParameterPacks(Aligned->getAlignmentExpr(), + Unexpanded); + else + S.collectUnexpandedParameterPacks(Aligned->getAlignmentType()->getTypeLoc(), + Unexpanded); + assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); + + // Determine whether we can expand this attribute pack yet. + bool Expand = true, RetainExpansion = false; + Optional NumExpansions; + // FIXME: Use the actual location of the ellipsis. + SourceLocation EllipsisLoc = Aligned->getLocation(); + if (S.CheckParameterPacksForExpansion(EllipsisLoc, Aligned->getRange(), + Unexpanded, TemplateArgs, Expand, + RetainExpansion, NumExpansions)) + return; + + if (!Expand) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(S, -1); + instantiateDependentAlignedAttr(S, TemplateArgs, Aligned, New, true); + } else { + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(S, I); + instantiateDependentAlignedAttr(S, TemplateArgs, Aligned, New, false); + } + } +} + void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl, Decl *New, LateInstantiatedAttrVec *LateAttrs, @@ -69,31 +127,13 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, const Attr *TmplAttr = *i; // FIXME: This should be generalized to more than just the AlignedAttr. - if (const AlignedAttr *Aligned = dyn_cast(TmplAttr)) { - if (Aligned->isAlignmentDependent()) { - if (Aligned->isAlignmentExpr()) { - // The alignment expression is a constant expression. - EnterExpressionEvaluationContext Unevaluated(*this, - Sema::ConstantEvaluated); - - ExprResult Result = SubstExpr(Aligned->getAlignmentExpr(), - TemplateArgs); - if (!Result.isInvalid()) - AddAlignedAttr(Aligned->getLocation(), New, Result.takeAs(), - Aligned->getSpellingListIndex()); - } else { - TypeSourceInfo *Result = SubstType(Aligned->getAlignmentType(), - TemplateArgs, - Aligned->getLocation(), - DeclarationName()); - if (Result) - AddAlignedAttr(Aligned->getLocation(), New, Result, - Aligned->getSpellingListIndex()); - } - continue; - } + const AlignedAttr *Aligned = dyn_cast(TmplAttr); + if (Aligned && Aligned->isAlignmentDependent()) { + instantiateDependentAlignedAttr(*this, TemplateArgs, Aligned, New); + continue; } + assert(!TmplAttr->isPackExpansion()); if (TmplAttr->isLateParsed() && LateAttrs) { // Late parsed attributes must be instantiated and attached after the // enclosing class has been instantiated. See Sema::InstantiateClass. diff --git a/clang/test/SemaCXX/attr-cxx0x.cpp b/clang/test/SemaCXX/attr-cxx0x.cpp index d1787f1..002800e 100644 --- a/clang/test/SemaCXX/attr-cxx0x.cpp +++ b/clang/test/SemaCXX/attr-cxx0x.cpp @@ -21,9 +21,14 @@ void f(alignas(1) char c) { // expected-error {{'alignas' attribute cannot be ap template struct alignas(A) align_class_template {}; -// FIXME: these should not error -template alignas(T...) struct align_class_temp_pack_type {}; // expected-error{{pack expansions in alignment specifiers are not supported yet}} -template alignas(A...) struct align_class_temp_pack_expr {}; // expected-error{{pack expansions in alignment specifiers are not supported yet}} +template struct alignas(T...) align_class_temp_pack_type {}; +template struct alignas(A...) align_class_temp_pack_expr {}; +struct alignas(int...) alignas_expansion_no_packs {}; // expected-error {{pack expansion does not contain any unexpanded parameter packs}} +template struct outer { + template struct alignas(alignof(A) * alignof(B)...) inner {}; + // expected-error@-1 {{pack expansion contains parameter packs 'A' and 'B' that have different lengths (1 vs. 2)}} +}; +outer::inner mismatched_packs; // expected-note {{in instantiation of}} typedef char align_typedef alignas(8); // expected-error {{'alignas' attribute only applies to variables, data members and tag types}} template using align_alias_template = align_typedef alignas(8); // expected-error {{'alignas' attribute cannot be applied to types}} @@ -35,6 +40,6 @@ static_assert(alignof(align_member) == 8, "quuux's alignment is wrong"); static_assert(sizeof(align_member) == 8, "quuux's size is wrong"); static_assert(alignof(align_class_template<8>) == 8, "template's alignment is wrong"); static_assert(alignof(align_class_template<16>) == 16, "template's alignment is wrong"); -// FIXME: enable these tests -// static_assert(alignof(align_class_temp_pack_type) == alignof(long), "template's alignment is wrong"); -// static_assert(alignof(align_class_temp_pack_expr<8, 16, 32>) == 32, "template's alignment is wrong"); +static_assert(alignof(align_class_temp_pack_type) == alignof(long), "template's alignment is wrong"); +static_assert(alignof(align_class_temp_pack_expr<8, 16, 32>) == 32, "template's alignment is wrong"); +static_assert(alignof(outer::inner) == alignof(int) * alignof(double), "template's alignment is wrong"); diff --git a/clang/test/SemaTemplate/alignas.cpp b/clang/test/SemaTemplate/alignas.cpp new file mode 100644 index 0000000..8a1f96e --- /dev/null +++ b/clang/test/SemaTemplate/alignas.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +// expected-no-diagnostics +using size_t = decltype(sizeof(0)); + +template +constexpr T max(T t, U u) { return t > u ? t : u; } + +template +constexpr auto max(T t, Ts ...ts) -> decltype(max(t, max(ts...))) { + return max(t, max(ts...)); +} + +template struct my_union { + alignas(T...) char buffer[max(sizeof(T)...)]; +}; + +struct alignas(8) A { char c; }; +struct alignas(4) B { short s; }; +struct C { char a[16]; }; + +static_assert(sizeof(my_union) == 16, ""); +static_assert(alignof(my_union) == 8, ""); -- 2.7.4