diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-02-22 04:55:39 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-02-22 04:55:39 +0000 |
commit | 671b3219c2be00ef8f26234ec993816c3ba56a4f (patch) | |
tree | 9e1b879c18a31c7bd10d6f6e1d8653b21a437056 | |
parent | 1f12c47ae90c03633496d96e79a61762097a4681 (diff) |
Implement C++11 [dcl.align]p6-p8, and C11 6.7.5/7. This had to be split out of
the normal attribute-merging path, because we can't merge alignment attributes
without knowing the complete set of alignment attributes which apply to a
particular declaration.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@175861 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 9 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 196 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiateDecl.cpp | 9 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.attr/dcl.align/p6.cpp | 83 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.attr/dcl.align/p7.cpp | 16 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.attr/dcl.align/p8.cpp | 6 |
8 files changed, 292 insertions, 32 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 557626c001..e87cdd7673 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1652,9 +1652,16 @@ def err_attribute_argument_not_int : Error< def err_aligned_attribute_argument_not_int : Error< "'aligned' attribute requires integer constant">; def err_alignas_attribute_wrong_decl_type : Error< - "%0 attribute cannot be applied to a %select{" + "'%select{alignas|_Alignas}0' attribute cannot be applied to a %select{" "function parameter|variable with 'register' storage class|" "'catch' variable|bit-field}1">; +def err_alignas_missing_on_definition : Error< + "'%select{alignas|_Alignas}0' must be specified on definition if it is " + "specified on any declaration">; +def note_alignas_on_declaration : Note< + "declared with '%select{alignas|_Alignas}0' attribute here">; +def err_alignas_mismatch : Error< + "redeclaration has different alignment requirement (%1 vs %0)">; def err_alignas_underaligned : Error< "requested alignment is less than minimum alignment of %1 for type %0">; def err_attribute_first_argument_not_int_or_bool : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 2d86787494..da82325819 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1705,8 +1705,6 @@ public: unsigned AttrSpellingListIndex); SectionAttr *mergeSectionAttr(Decl *D, SourceRange Range, StringRef Name, unsigned AttrSpellingListIndex); - bool mergeDeclAttribute(NamedDecl *New, InheritableAttr *Attr, - bool Override); /// \brief Describes the kind of merge to perform for availability /// attributes (including "deprecated", "unavailable", and "availability"). diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 03d18d402d..7e44e6b4bd 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1822,37 +1822,164 @@ DeclHasAttr(const Decl *D, const Attr *A) { return false; } -bool Sema::mergeDeclAttribute(NamedDecl *D, InheritableAttr *Attr, - bool Override) { +static bool isAttributeTargetADefinition(Decl *D) { + if (VarDecl *VD = dyn_cast<VarDecl>(D)) + return VD->isThisDeclarationADefinition(); + if (TagDecl *TD = dyn_cast<TagDecl>(D)) + return TD->isCompleteDefinition() || TD->isBeingDefined(); + return true; +} + +/// Merge alignment attributes from \p Old to \p New, taking into account the +/// special semantics of C11's _Alignas specifier and C++11's alignas attribute. +/// +/// \return \c true if any attributes were added to \p New. +static bool mergeAlignedAttrs(Sema &S, NamedDecl *New, Decl *Old) { + // Look for alignas attributes on Old, and pick out whichever attribute + // specifies the strictest alignment requirement. + AlignedAttr *OldAlignasAttr = 0; + AlignedAttr *OldStrictestAlignAttr = 0; + unsigned OldAlign = 0; + for (specific_attr_iterator<AlignedAttr> + I = Old->specific_attr_begin<AlignedAttr>(), + E = Old->specific_attr_end<AlignedAttr>(); I != E; ++I) { + // FIXME: We have no way of representing inherited dependent alignments + // in a case like: + // template<int A, int B> struct alignas(A) X; + // template<int A, int B> struct alignas(B) X {}; + // For now, we just ignore any alignas attributes which are not on the + // definition in such a case. + if (I->isAlignmentDependent()) + return false; + + if (I->isAlignas()) + OldAlignasAttr = *I; + + unsigned Align = I->getAlignment(S.Context); + if (Align > OldAlign) { + OldAlign = Align; + OldStrictestAlignAttr = *I; + } + } + + // Look for alignas attributes on New. + AlignedAttr *NewAlignasAttr = 0; + unsigned NewAlign = 0; + for (specific_attr_iterator<AlignedAttr> + I = New->specific_attr_begin<AlignedAttr>(), + E = New->specific_attr_end<AlignedAttr>(); I != E; ++I) { + if (I->isAlignmentDependent()) + return false; + + if (I->isAlignas()) + NewAlignasAttr = *I; + + unsigned Align = I->getAlignment(S.Context); + if (Align > NewAlign) + NewAlign = Align; + } + + if (OldAlignasAttr && NewAlignasAttr && OldAlign != NewAlign) { + // Both declarations have 'alignas' attributes. We require them to match. + // C++11 [dcl.align]p6 and C11 6.7.5/7 both come close to saying this, but + // fall short. (If two declarations both have alignas, they must both match + // every definition, and so must match each other if there is a definition.) + + // If either declaration only contains 'alignas(0)' specifiers, then it + // specifies the natural alignment for the type. + if (OldAlign == 0 || NewAlign == 0) { + QualType Ty; + if (ValueDecl *VD = dyn_cast<ValueDecl>(New)) + Ty = VD->getType(); + else + Ty = S.Context.getTagDeclType(cast<TagDecl>(New)); + + if (OldAlign == 0) + OldAlign = S.Context.getTypeAlign(Ty); + if (NewAlign == 0) + NewAlign = S.Context.getTypeAlign(Ty); + } + + if (OldAlign != NewAlign) { + S.Diag(NewAlignasAttr->getLocation(), diag::err_alignas_mismatch) + << (unsigned)S.Context.toCharUnitsFromBits(OldAlign).getQuantity() + << (unsigned)S.Context.toCharUnitsFromBits(NewAlign).getQuantity(); + S.Diag(OldAlignasAttr->getLocation(), diag::note_previous_declaration); + } + } + + if (OldAlignasAttr && !NewAlignasAttr && isAttributeTargetADefinition(New)) { + // C++11 [dcl.align]p6: + // if any declaration of an entity has an alignment-specifier, + // every defining declaration of that entity shall specify an + // equivalent alignment. + // C11 6.7.5/7: + // If the definition of an object does not have an alignment + // specifier, any other declaration of that object shall also + // have no alignment specifier. + S.Diag(New->getLocation(), diag::err_alignas_missing_on_definition) + << OldAlignasAttr->isC11(); + S.Diag(OldAlignasAttr->getLocation(), diag::note_alignas_on_declaration) + << OldAlignasAttr->isC11(); + } + + bool AnyAdded = false; + + // Ensure we have an attribute representing the strictest alignment. + if (OldAlign > NewAlign) { + AlignedAttr *Clone = OldStrictestAlignAttr->clone(S.Context); + Clone->setInherited(true); + New->addAttr(Clone); + AnyAdded = true; + } + + // Ensure we have an alignas attribute if the old declaration had one. + if (OldAlignasAttr && !NewAlignasAttr && + !(AnyAdded && OldStrictestAlignAttr->isAlignas())) { + AlignedAttr *Clone = OldAlignasAttr->clone(S.Context); + Clone->setInherited(true); + New->addAttr(Clone); + AnyAdded = true; + } + + return AnyAdded; +} + +static bool mergeDeclAttribute(Sema &S, NamedDecl *D, InheritableAttr *Attr, + bool Override) { InheritableAttr *NewAttr = NULL; unsigned AttrSpellingListIndex = Attr->getSpellingListIndex(); if (AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attr)) - NewAttr = mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), - AA->getIntroduced(), AA->getDeprecated(), - AA->getObsoleted(), AA->getUnavailable(), - AA->getMessage(), Override, - AttrSpellingListIndex); + NewAttr = S.mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), + AA->getIntroduced(), AA->getDeprecated(), + AA->getObsoleted(), AA->getUnavailable(), + AA->getMessage(), Override, + AttrSpellingListIndex); else if (VisibilityAttr *VA = dyn_cast<VisibilityAttr>(Attr)) - NewAttr = mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), - AttrSpellingListIndex); + NewAttr = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), + AttrSpellingListIndex); else if (TypeVisibilityAttr *VA = dyn_cast<TypeVisibilityAttr>(Attr)) - NewAttr = mergeTypeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), - AttrSpellingListIndex); + NewAttr = S.mergeTypeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), + AttrSpellingListIndex); else if (DLLImportAttr *ImportA = dyn_cast<DLLImportAttr>(Attr)) - NewAttr = mergeDLLImportAttr(D, ImportA->getRange(), - AttrSpellingListIndex); + NewAttr = S.mergeDLLImportAttr(D, ImportA->getRange(), + AttrSpellingListIndex); else if (DLLExportAttr *ExportA = dyn_cast<DLLExportAttr>(Attr)) - NewAttr = mergeDLLExportAttr(D, ExportA->getRange(), - AttrSpellingListIndex); + NewAttr = S.mergeDLLExportAttr(D, ExportA->getRange(), + AttrSpellingListIndex); else if (FormatAttr *FA = dyn_cast<FormatAttr>(Attr)) - NewAttr = mergeFormatAttr(D, FA->getRange(), FA->getType(), - FA->getFormatIdx(), FA->getFirstArg(), - AttrSpellingListIndex); + NewAttr = S.mergeFormatAttr(D, FA->getRange(), FA->getType(), + FA->getFormatIdx(), FA->getFirstArg(), + AttrSpellingListIndex); else if (SectionAttr *SA = dyn_cast<SectionAttr>(Attr)) - NewAttr = mergeSectionAttr(D, SA->getRange(), SA->getName(), - AttrSpellingListIndex); + NewAttr = S.mergeSectionAttr(D, SA->getRange(), SA->getName(), + AttrSpellingListIndex); + else if (isa<AlignedAttr>(Attr)) + // AlignedAttrs are handled separately, because we need to handle all + // such attributes on a declaration at the same time. + NewAttr = 0; else if (!DeclHasAttr(D, Attr)) - NewAttr = cast<InheritableAttr>(Attr->clone(Context)); + NewAttr = cast<InheritableAttr>(Attr->clone(S.Context)); if (NewAttr) { NewAttr->setInherited(true); @@ -1903,11 +2030,31 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) { ++I; continue; // regular attr merging will take care of validating this. } - // C's _Noreturn is allowed to be added to a function after it is defined. + if (isa<C11NoReturnAttr>(NewAttribute)) { + // C's _Noreturn is allowed to be added to a function after it is defined. ++I; continue; + } else if (const AlignedAttr *AA = dyn_cast<AlignedAttr>(NewAttribute)) { + if (AA->isAlignas()) { + // C++11 [dcl.align]p6: + // if any declaration of an entity has an alignment-specifier, + // every defining declaration of that entity shall specify an + // equivalent alignment. + // C11 6.7.5/7: + // If the definition of an object does not have an alignment + // specifier, any other declaration of that object shall also + // have no alignment specifier. + S.Diag(Def->getLocation(), diag::err_alignas_missing_on_definition) + << AA->isC11(); + S.Diag(NewAttribute->getLocation(), diag::note_alignas_on_declaration) + << AA->isC11(); + NewAttributes.erase(NewAttributes.begin() + I); + --E; + continue; + } } + S.Diag(NewAttribute->getLocation(), diag::warn_attribute_precede_definition); S.Diag(Def->getLocation(), diag::note_previous_definition); @@ -1956,10 +2103,13 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, } } - if (mergeDeclAttribute(New, *i, Override)) + if (mergeDeclAttribute(*this, New, *i, Override)) foundAny = true; } + if (mergeAlignedAttrs(*this, New, Old)) + foundAny = true; + if (!foundAny) New->dropAttrs(); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index e8c42e6f7c..0a30163f99 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3368,8 +3368,7 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, } if (DiagKind != -1) { Diag(AttrLoc, diag::err_alignas_attribute_wrong_decl_type) - << (TmpAttr.isC11() ? "'_Alignas'" : "'alignas'") - << DiagKind; + << TmpAttr.isC11() << DiagKind; return; } } diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 1515a358ff..defa968475 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -322,6 +322,11 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { Var->setReferenced(D->isReferenced()); } + SemaRef.InstantiateAttrs(TemplateArgs, D, Var, LateAttrs, StartingScope); + + if (Var->hasAttrs()) + SemaRef.CheckAlignasUnderalignment(Var); + // FIXME: In theory, we could have a previous declaration for variables that // are not static data members. // FIXME: having to fake up a LookupResult is dumb. @@ -345,10 +350,6 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { if (Owner->isFunctionOrMethod()) SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Var); } - SemaRef.InstantiateAttrs(TemplateArgs, D, Var, LateAttrs, StartingScope); - - if (Var->hasAttrs()) - SemaRef.CheckAlignasUnderalignment(Var); // Link instantiations of static data members back to the template from // which they were instantiated. diff --git a/test/CXX/dcl.dcl/dcl.attr/dcl.align/p6.cpp b/test/CXX/dcl.dcl/dcl.attr/dcl.align/p6.cpp new file mode 100644 index 0000000000..8cf5b266bb --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.attr/dcl.align/p6.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +alignas(4) extern int n1; // expected-note {{previous declaration}} +alignas(8) int n1; // expected-error {{redeclaration has different alignment requirement (8 vs 4)}} + +alignas(8) int n2; // expected-note {{previous declaration}} +alignas(4) extern int n2; // expected-error {{different alignment requirement (4 vs 8)}} + +alignas(8) extern int n3; // expected-note {{previous declaration}} +alignas(4) extern int n3; // expected-error {{different alignment requirement (4 vs 8)}} + +extern int n4; +alignas(8) extern int n4; + +alignas(8) extern int n5; +extern int n5; + +int n6; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}} +alignas(8) extern int n6; // expected-note {{declared with 'alignas' attribute here}} + +extern int n7; +alignas(8) int n7; + +alignas(8) extern int n8; // expected-note {{declared with 'alignas' attribute here}} +int n8; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}} + +int n9; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}} +alignas(4) extern int n9; // expected-note {{declared with 'alignas' attribute here}} + + +enum alignas(2) E : char; // expected-note {{declared with 'alignas' attribute here}} +enum E : char {}; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}} + +enum alignas(4) F : char; // expected-note {{previous declaration is here}} +enum alignas(2) F : char; // expected-error {{redeclaration has different alignment requirement (2 vs 4)}} + +enum G : char; +enum alignas(8) G : char {}; +enum G : char; + +enum H : char {}; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}} +enum alignas(1) H : char; // expected-note {{declared with 'alignas' attribute here}} + + +struct S; +struct alignas(16) S; // expected-note {{declared with 'alignas' attribute here}} +struct S; +struct S { int n; }; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}} + +struct alignas(2) T; +struct alignas(2) T { char c; }; // expected-note {{previous declaration is here}} +struct T; +struct alignas(4) T; // expected-error {{redeclaration has different alignment requirement (4 vs 2)}} + +struct U; +struct alignas(2) U {}; + +struct V {}; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}} +struct alignas(1) V; // expected-note {{declared with 'alignas' attribute here}} + +template<int M, int N> struct alignas(M) W; +template<int M, int N> struct alignas(N) W {}; +W<4,4> w44; // ok +// FIXME: We should reject this. +W<1,2> w12; +static_assert(alignof(W<4,4>) == 4, ""); + +template<int M, int N, int O, int P> struct X { + alignas(M) alignas(N) static char Buffer[32]; // expected-note {{previous declaration is here}} +}; +template<int M, int N, int O, int P> +alignas(O) alignas(P) char X<M, N, O, P>::Buffer[32]; // expected-error {{redeclaration has different alignment requirement (8 vs 2)}} +char *x1848 = X<1,8,4,8>::Buffer; // ok +char *x1248 = X<1,2,4,8>::Buffer; // expected-note {{in instantiation of}} + +template<int M, int N, int O, int P> struct Y { + enum alignas(M) alignas(N) E : char; +}; +template<int M, int N, int O, int P> +enum alignas(O) alignas(P) Y<M,N,O,P>::E : char { e }; +int y1848 = Y<1,8,4,8>::e; +// FIXME: We should reject this. +int y1248 = Y<1,2,4,8>::e; diff --git a/test/CXX/dcl.dcl/dcl.attr/dcl.align/p7.cpp b/test/CXX/dcl.dcl/dcl.attr/dcl.align/p7.cpp new file mode 100644 index 0000000000..93b1c6461a --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.attr/dcl.align/p7.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +template<typename T, typename A, int N> struct X { + alignas(T) alignas(A) T buffer[N]; +}; + +static_assert(alignof(X<char, int, sizeof(int)>) == alignof(int), ""); +static_assert(alignof(X<int, char, 1>) == alignof(int), ""); + + +template<typename T, typename A, int N> struct Y { + alignas(A) T buffer[N]; // expected-error {{requested alignment is less than minimum alignment of 4 for type 'int [1]'}} +}; + +static_assert(alignof(Y<char, int, sizeof(int)>) == alignof(int), ""); +static_assert(alignof(Y<int, char, 1>) == alignof(int), ""); // expected-note {{in instantiation of}} diff --git a/test/CXX/dcl.dcl/dcl.attr/dcl.align/p8.cpp b/test/CXX/dcl.dcl/dcl.attr/dcl.align/p8.cpp new file mode 100644 index 0000000000..686aac2802 --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.attr/dcl.align/p8.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +alignas(double) void f(); // expected-error {{'alignas' attribute only applies to variables, data members and tag types}} +alignas(double) unsigned char c[sizeof(double)]; // expected-note {{previous}} +extern unsigned char c[sizeof(double)]; +alignas(float) extern unsigned char c[sizeof(double)]; // expected-error {{different alignment}} |