forked from OSchip/llvm-project
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:
parent
4431918f40
commit
bc8caaf05f
|
@ -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<
|
||||
|
|
|
@ -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").
|
||||
|
|
|
@ -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);
|
||||
else if (VisibilityAttr *VA = dyn_cast<VisibilityAttr>(Attr))
|
||||
NewAttr = mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(),
|
||||
AttrSpellingListIndex);
|
||||
else if (TypeVisibilityAttr *VA = dyn_cast<TypeVisibilityAttr>(Attr))
|
||||
NewAttr = mergeTypeVisibilityAttr(D, VA->getRange(), VA->getVisibility(),
|
||||
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 = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(),
|
||||
AttrSpellingListIndex);
|
||||
else if (TypeVisibilityAttr *VA = dyn_cast<TypeVisibilityAttr>(Attr))
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
|
@ -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}}
|
|
@ -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}}
|
Loading…
Reference in New Issue