From: Richard Smith Date: Fri, 1 Feb 2013 08:12:08 +0000 (+0000) Subject: Implement [dcl.align]p5 and C11 6.7.5/4: alignas cannot underalign. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=be507b6e72df8ab5e7d8c31eb4453e1bdf5fcfaf;p=clang Implement [dcl.align]p5 and C11 6.7.5/4: alignas cannot underalign. Also support alignas(0), which C++11 and C11 require us to ignore. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@174157 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 6d70756286..e955367761 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -161,8 +161,9 @@ def Aligned : InheritableAttr { let Subjects = [NonBitField, NormalVar, Tag]; let Args = [AlignedArgument<"Alignment">]; let Accessors = [Accessor<"isGNU", [GNU<"aligned">, CXX11<"gnu","aligned">]>, - Accessor<"isAlignas", - [Keyword<"alignas">, Keyword<"_Alignas">]>, + Accessor<"isC11", [Keyword<"_Alignas">]>, + Accessor<"isAlignas", [Keyword<"alignas">, + Keyword<"_Alignas">]>, Accessor<"isDeclspec",[Declspec<"align">]>]; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 28082267e3..fe47c3a9c8 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1657,6 +1657,8 @@ def err_alignas_attribute_wrong_decl_type : Error< "%0 attribute cannot be applied to a %select{" "function parameter|variable with 'register' storage class|" "'catch' variable|bit-field}1">; +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< "%0 attribute first argument must be of int or bool type">; def err_attribute_argument_outof_range : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 4a27792092..d0d3ff937d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2351,6 +2351,7 @@ public: bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, const FunctionDecl *FD = 0); bool CheckNoReturnAttr(const AttributeList &attr); + void CheckAlignasUnderalignment(Decl *D); /// \brief Stmt attributes - this routine is the top level dispatcher. StmtResult ProcessStmtAttributes(Stmt *Stmt, AttributeList *Attrs, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 834041c65c..4404c6f9a0 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4611,6 +4611,9 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); + if (NewVD->hasAttrs()) + CheckAlignasUnderalignment(NewVD); + if (getLangOpts().CUDA) { // CUDA B.2.5: "__shared__ and __constant__ variables have implied static // storage [duration]." @@ -10158,10 +10161,14 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, // FIXME: We need to pass in the attributes given an AST // representation, not a parser representation. - if (D) + if (D) { // FIXME: What to pass instead of TUScope? ProcessDeclAttributes(TUScope, NewFD, *D); + if (NewFD->hasAttrs()) + CheckAlignasUnderalignment(NewFD); + } + // In auto-retain/release, infer strong retension for fields of // retainable type. if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(NewFD)) @@ -10680,6 +10687,8 @@ void Sema::ActOnFields(Scope* S, if (!Completed) Record->completeDefinition(); + if (Record->hasAttrs()) + CheckAlignasUnderalignment(Record); } else { ObjCIvarDecl **ClsFields = reinterpret_cast(RecFields.data()); @@ -11427,6 +11436,10 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, DeclsInPrototypeScope.push_back(Enum); CheckForDuplicateEnumValues(*this, Elements, NumElements, Enum, EnumType); + + // Now that the enum type is defined, ensure it's not been underaligned. + if (Enum->hasAttrs()) + CheckAlignasUnderalignment(Enum); } Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr, diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 8500bff6bc..9409b30e5b 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3272,10 +3272,32 @@ 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()); +} + +void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, + unsigned SpellingListIndex) { + // FIXME: Handle pack-expansions here. + if (DiagnoseUnexpandedParameterPack(E)) + return; + + AlignedAttr TmpAttr(AttrRange, Context, true, E, SpellingListIndex); + SourceLocation AttrLoc = AttrRange.getBegin(); + // C++11 alignas(...) and C11 _Alignas(...) have additional requirements. - // FIXME: Use a more reliable mechanism to determine how the attribute was - // spelled. - if (Attr.isKeywordAttribute()) { + if (TmpAttr.isAlignas()) { // C++11 [dcl.align]p1: // An alignment-specifier may be applied to a variable or to a class // data member, but it shall not be applied to a bit-field, a function @@ -3299,45 +3321,25 @@ static void handleAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (FD->isBitField()) DiagKind = 3; } else if (!isa(D)) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) - << Attr.getName() << ExpectedVariableFunctionOrTag; + Diag(AttrLoc, diag::err_attribute_wrong_decl_type) + << (TmpAttr.isC11() ? "'_Alignas'" : "'alignas'") + << ExpectedVariableFunctionOrTag; return; } if (DiagKind != -1) { - S.Diag(Attr.getLoc(), diag::err_alignas_attribute_wrong_decl_type) - << Attr.getName() << DiagKind; + Diag(AttrLoc, diag::err_alignas_attribute_wrong_decl_type) + << (TmpAttr.isC11() ? "'_Alignas'" : "'alignas'") + << DiagKind; 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. - - 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()); -} - -void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, - unsigned SpellingListIndex) { - // FIXME: Handle pack-expansions here. - if (DiagnoseUnexpandedParameterPack(E)) - return; - if (E->isTypeDependent() || E->isValueDependent()) { // Save dependent expressions in the AST to be instantiated. - D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, E, - SpellingListIndex)); + D->addAttr(::new (Context) AlignedAttr(TmpAttr)); return; } - SourceLocation AttrLoc = AttrRange.getBegin(); // FIXME: Cache the number on the Attr object? llvm::APSInt Alignment(32); ExprResult ICE @@ -3346,17 +3348,20 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, /*AllowFold*/ false); if (ICE.isInvalid()) return; - if (!llvm::isPowerOf2_64(Alignment.getZExtValue())) { + + // C++11 [dcl.align]p2: + // -- if the constant expression evaluates to zero, the alignment + // specifier shall have no effect + // C11 6.7.5p6: + // An alignment specification of zero has no effect. + if (!(TmpAttr.isAlignas() && !Alignment) && + !llvm::isPowerOf2_64(Alignment.getZExtValue())) { Diag(AttrLoc, diag::err_attribute_aligned_not_power_of_two) << E->getSourceRange(); return; } - AlignedAttr *Attr = ::new (Context) AlignedAttr(AttrRange, Context, true, - ICE.take(), - SpellingListIndex); - - if (Attr->isDeclspec()) { + if (TmpAttr.isDeclspec()) { // We've already verified it's a power of 2, now let's make sure it's // 8192 or less. if (Alignment.getZExtValue() > 8192) { @@ -3366,7 +3371,8 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, } } - D->addAttr(Attr); + D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, + ICE.take(), SpellingListIndex)); } void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *TS, @@ -3378,6 +3384,42 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *TS, return; } +void Sema::CheckAlignasUnderalignment(Decl *D) { + assert(D->hasAttrs() && "no attributes on decl"); + + QualType Ty; + if (ValueDecl *VD = dyn_cast(D)) + Ty = VD->getType(); + else + Ty = Context.getTagDeclType(cast(D)); + if (Ty->isDependentType()) + return; + + // C++11 [dcl.align]p5, C11 6.7.5/4: + // The combined effect of all alignment attributes in a declaration shall + // not specify an alignment that is less strict than the alignment that + // would otherwise be required for the entity being declared. + AlignedAttr *AlignasAttr = 0; + unsigned Align = 0; + for (specific_attr_iterator + I = D->specific_attr_begin(), + E = D->specific_attr_end(); I != E; ++I) { + if (I->isAlignmentDependent()) + return; + if (I->isAlignas()) + AlignasAttr = *I; + Align = std::max(Align, I->getAlignment(Context)); + } + + if (AlignasAttr && Align) { + CharUnits RequestedAlign = Context.toCharUnitsFromBits(Align); + CharUnits NaturalAlign = Context.getTypeAlignInChars(Ty); + if (NaturalAlign > RequestedAlign) + Diag(AlignasAttr->getLocation(), diag::err_alignas_underaligned) + << Ty << (unsigned)NaturalAlign.getQuantity(); + } +} + /// handleModeAttr - This attribute modifies the width of a decl with primitive /// type. /// diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index c146e9dd44..2d3d0bd5b6 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -347,6 +347,9 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { } 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. if (Var->isStaticDataMember()) @@ -459,6 +462,9 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) { SemaRef.InstantiateAttrs(TemplateArgs, D, Field, LateAttrs, StartingScope); + if (Field->hasAttrs()) + SemaRef.CheckAlignasUnderalignment(Field); + if (Invalid) Field->setInvalidDecl(); diff --git a/test/CXX/dcl.dcl/dcl.attr/dcl.align/p5.cpp b/test/CXX/dcl.dcl/dcl.attr/dcl.align/p5.cpp new file mode 100644 index 0000000000..10be98d16e --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.attr/dcl.align/p5.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -verify %s + +alignas(1) int n1; // expected-error {{requested alignment is less than minimum alignment of 4 for type 'int'}} +alignas(1) alignas(2) int n2; // expected-error {{less than minimum alignment}} +alignas(1) alignas(2) alignas(4) int n3; // ok +alignas(1) alignas(2) alignas(0) int n4; // expected-error {{less than minimum alignment}} +alignas(1) alignas(2) int n5 alignas(4); // ok +alignas(1) alignas(4) int n6 alignas(2); // ok +alignas(1) int n7 alignas(2), // expected-error {{less than minimum alignment}} + n8 alignas(4); // ok +alignas(8) int n9 alignas(2); // ok, overaligned + +enum alignas(1) E1 {}; // expected-error {{requested alignment is less than minimum alignment of 4 for type 'E1'}} +enum alignas(1) E2 : char {}; // ok +enum alignas(4) E3 { e3 = 0 }; // ok +enum alignas(4) E4 { e4 = 1ull << 33 }; // expected-error {{requested alignment is less than minimum alignment of 8 for type 'E4'}} + +struct S1 { + alignas(8) int n; +}; +struct alignas(2) S2 { // expected-error {{requested alignment is less than minimum alignment of 4 for type 'S2'}} + int n; +}; +struct alignas(2) S3 { // expected-error {{requested alignment is less than minimum alignment of 8 for type 'S3'}} + S1 s1; +}; +struct alignas(2) S4 : S1 { // expected-error {{requested alignment is less than minimum alignment of 8 for type 'S4'}} +}; +struct S5 : S1 { + alignas(2) S1 s1; // expected-error {{requested alignment is less than minimum alignment of 8 for type 'S1'}} +}; +struct S6 { + S1 s1; +}; +struct S7 : S1 { +}; +struct alignas(2) alignas(8) alignas(1) S8 : S1 { +}; + +S1 s1 alignas(4); // expected-error {{requested alignment is less than minimum alignment of 8 for type 'S1'}} +S6 s6 alignas(4); // expected-error {{requested alignment is less than minimum alignment of 8 for type 'S6'}} +S7 s7 alignas(4); // expected-error {{requested alignment is less than minimum alignment of 8 for type 'S7'}} + +template +struct alignas(N) X { // expected-error 3{{requested alignment is less than minimum}} + alignas(M) T t; // expected-error 3{{requested alignment is less than minimum}} +}; + +template struct X<1, 1, char>; +template struct X<4, 1, char>; +template struct X<1, 2, char>; // expected-note {{instantiation}} +template struct X<1, 1, short>; // expected-note {{instantiation}} +template struct X<2, 1, short>; // expected-note {{instantiation}} +template struct X<2, 2, short>; +template struct X<16, 8, S1>; +template struct X<4, 4, S1>; // expected-note {{instantiation}} + +template +struct Y { + enum alignas(N) E : T {}; // expected-error {{requested alignment is less than minimum}} +}; +template struct Y<1, char>; +template struct Y<2, char>; +template struct Y<1, short>; // expected-note {{instantiation}} +template struct Y<2, short>; + +template +void f() { + alignas(N) T v; // expected-error {{requested alignment is less than minimum}} +}; +template void f<1, char>(); +template void f<2, char>(); +template void f<1, short>(); // expected-note {{instantiation}} +template void f<2, short>(); diff --git a/test/Sema/alignas.c b/test/Sema/alignas.c index b5e87cf4b7..a79c738d12 100644 --- a/test/Sema/alignas.c +++ b/test/Sema/alignas.c @@ -3,7 +3,7 @@ _Alignas(3) int align_illegal; //expected-error {{requested alignment is not a power of 2}} _Alignas(int) char align_big; -_Alignas(1) int align_small; // FIXME: this should be rejected +_Alignas(1) int align_small; // expected-error {{requested alignment is less than minimum}} _Alignas(1) unsigned _Alignas(8) int _Alignas(1) align_multiple; struct align_member { diff --git a/test/SemaCXX/attr-cxx0x.cpp b/test/SemaCXX/attr-cxx0x.cpp index d038e637f4..09103c8907 100644 --- a/test/SemaCXX/attr-cxx0x.cpp +++ b/test/SemaCXX/attr-cxx0x.cpp @@ -2,7 +2,7 @@ int align_illegal alignas(3); //expected-error {{requested alignment is not a power of 2}} char align_big alignas(int); -int align_small alignas(1); // FIXME: this should be rejected +int align_small alignas(1); // expected-error {{requested alignment is less than minimum}} int align_multiple alignas(1) alignas(8) alignas(1); alignas(4) int align_before;