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.

llvm-svn: 175861
This commit is contained in:
Richard Smith 2013-02-22 04:55:39 +00:00
parent 4431918f40
commit bc8caaf05f
8 changed files with 293 additions and 33 deletions

View File

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

View File

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

View File

@ -1822,37 +1822,164 @@ DeclHasAttr(const Decl *D, const Attr *A) {
return false;
}
bool Sema::mergeDeclAttribute(NamedDecl *D, InheritableAttr *Attr,
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(),
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(),
NewAttr = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(),
AttrSpellingListIndex);
else if (TypeVisibilityAttr *VA = dyn_cast<TypeVisibilityAttr>(Attr))
NewAttr = mergeTypeVisibilityAttr(D, VA->getRange(), VA->getVisibility(),
NewAttr = S.mergeTypeVisibilityAttr(D, VA->getRange(), VA->getVisibility(),
AttrSpellingListIndex);
else if (DLLImportAttr *ImportA = dyn_cast<DLLImportAttr>(Attr))
NewAttr = mergeDLLImportAttr(D, ImportA->getRange(),
NewAttr = S.mergeDLLImportAttr(D, ImportA->getRange(),
AttrSpellingListIndex);
else if (DLLExportAttr *ExportA = dyn_cast<DLLExportAttr>(Attr))
NewAttr = mergeDLLExportAttr(D, ExportA->getRange(),
NewAttr = S.mergeDLLExportAttr(D, ExportA->getRange(),
AttrSpellingListIndex);
else if (FormatAttr *FA = dyn_cast<FormatAttr>(Attr))
NewAttr = mergeFormatAttr(D, FA->getRange(), FA->getType(),
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(),
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();
}

View File

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

View File

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

View File

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

View File

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

View File

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