From 4cd81c5bf5957b2b10ddf253035f6e1596082108 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 29 Jan 2013 09:02:09 +0000 Subject: [PATCH] Implement C++11 [dcl.align]p1 and C11 6.7.5/2 rules for alignas and _Alignas. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@173779 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 4 ++ lib/Parse/ParseDecl.cpp | 3 ++ lib/Parse/ParseStmt.cpp | 19 ++++++---- lib/Sema/SemaDeclAttr.cpp | 44 ++++++++++++++++++++-- test/Parser/c1x-alignas.c | 2 +- test/Sema/alignas.c | 8 +++- test/SemaCXX/attr-cxx0x.cpp | 18 ++++++--- 7 files changed, 80 insertions(+), 18 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index e092b9a772..d004563c34 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1651,6 +1651,10 @@ def err_attribute_argument_not_int : Error< "'%0' attribute requires integer constant">; 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{" + "function parameter|variable with 'register' storage class|" + "'catch' variable|bit-field}1">; 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/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 438c6f8cf5..20d8f711ae 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3890,6 +3890,9 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::kw_explicit: case tok::kw__Noreturn: + // alignment-specifier + case tok::kw__Alignas: + // friend keyword. case tok::kw_friend: diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 0a1eccaa76..8b026e859f 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -2156,14 +2156,13 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc, bool FnTry) { /// ParseCXXCatchBlock - Parse a C++ catch block, called handler in the standard /// -/// handler: -/// 'catch' '(' exception-declaration ')' compound-statement +/// handler: +/// 'catch' '(' exception-declaration ')' compound-statement /// -/// exception-declaration: -/// type-specifier-seq declarator -/// type-specifier-seq abstract-declarator -/// type-specifier-seq -/// '...' +/// exception-declaration: +/// attribute-specifier-seq[opt] type-specifier-seq declarator +/// attribute-specifier-seq[opt] type-specifier-seq abstract-declarator[opt] +/// '...' /// StmtResult Parser::ParseCXXCatchBlock(bool FnCatch) { assert(Tok.is(tok::kw_catch) && "Expected 'catch'"); @@ -2184,9 +2183,15 @@ StmtResult Parser::ParseCXXCatchBlock(bool FnCatch) { // without default arguments. Decl *ExceptionDecl = 0; if (Tok.isNot(tok::ellipsis)) { + ParsedAttributesWithRange Attributes(AttrFactory); + MaybeParseCXX11Attributes(Attributes); + DeclSpec DS(AttrFactory); + DS.takeAttributesFrom(Attributes); + if (ParseCXXTypeSpecifierSeq(DS)) return StmtError(); + Declarator ExDecl(DS, Declarator::CXXCatchContext); ParseDeclarator(ExDecl); ExceptionDecl = Actions.ActOnExceptionDeclarator(getCurScope(), ExDecl); diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 0e51ba3ba3..01a3505cf7 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3272,9 +3272,47 @@ static void handleAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { return; } - //FIXME: The C++0x version of this attribute has more limited applicabilty - // than GNU's, and should error out when it is used to specify a - // weaker alignment, rather than being silently ignored. + // 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()) { + // 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 + // parameter, the formal parameter of a catch clause, or a variable + // declared with the register storage class specifier. An + // alignment-specifier may also be applied to the declaration of a class + // or enumeration type. + // C11 6.7.5/2: + // An alignment attribute shall not be specified in a declaration of + // a typedef, or a bit-field, or a function, or a parameter, or an + // object declared with the register storage-class specifier. + int DiagKind = -1; + if (isa(D)) { + DiagKind = 0; + } else if (VarDecl *VD = dyn_cast(D)) { + if (VD->getStorageClass() == SC_Register) + DiagKind = 1; + if (VD->isExceptionVariable()) + DiagKind = 2; + } else if (FieldDecl *FD = dyn_cast(D)) { + if (FD->isBitField()) + DiagKind = 3; + } else if (!isa(D)) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) + << Attr.getName() << ExpectedVariableFunctionOrTag; + return; + } + if (DiagKind != -1) { + S.Diag(Attr.getLoc(), diag::err_alignas_attribute_wrong_decl_type) + << Attr.getName() << 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, diff --git a/test/Parser/c1x-alignas.c b/test/Parser/c1x-alignas.c index 81cd681630..5b29df262d 100644 --- a/test/Parser/c1x-alignas.c +++ b/test/Parser/c1x-alignas.c @@ -5,7 +5,7 @@ _Alignas(4) char c1; unsigned _Alignas(long) char c2; char _Alignas(16) c3; -char c4 _Alignas(32); // expected-error {{expected ';' after top level declarator}} +char c4 _Alignas(32); // expected-error {{expected ';' after top level declarator}} expected-warning {{declaration does not declare anything}} char _Alignas(_Alignof(int)) c5; diff --git a/test/Sema/alignas.c b/test/Sema/alignas.c index d9a0164010..dbc637bf87 100644 --- a/test/Sema/alignas.c +++ b/test/Sema/alignas.c @@ -8,13 +8,17 @@ _Alignas(1) unsigned _Alignas(8) int _Alignas(1) align_multiple; struct align_member { _Alignas(8) int member; + _Alignas(1) char bitfield : 1; // expected-error {{'_Alignas' attribute cannot be applied to a bit-field}} }; -typedef _Alignas(8) char align_typedef; // FIXME: this should be rejected +typedef _Alignas(8) char align_typedef; // expected-error {{'_Alignas' attribute only applies to variables, functions and tag types}} + +void f(_Alignas(1) char c) { // expected-error {{'_Alignas' attribute cannot be applied to a function parameter}} + _Alignas(1) register char k; // expected-error {{'_Alignas' attribute cannot be applied to a variable with 'register' storage class}} +} _Static_assert(alignof(align_big) == alignof(int), "k's alignment is wrong"); _Static_assert(alignof(align_small) == 1, "j's alignment is wrong"); _Static_assert(alignof(align_multiple) == 8, "l's alignment is wrong"); _Static_assert(alignof(struct align_member) == 8, "quuux's alignment is wrong"); _Static_assert(sizeof(struct align_member) == 8, "quuux's size is wrong"); -_Static_assert(alignof(align_typedef) == 8, "typedef's alignment is wrong"); diff --git a/test/SemaCXX/attr-cxx0x.cpp b/test/SemaCXX/attr-cxx0x.cpp index 4281895f40..26cece610c 100644 --- a/test/SemaCXX/attr-cxx0x.cpp +++ b/test/SemaCXX/attr-cxx0x.cpp @@ -1,32 +1,40 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++11 %s 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_multiple alignas(1) alignas(8) alignas(1); +alignas(4) int align_before; struct align_member { int member alignas(8); + int bitfield alignas(1) : 1; // expected-error {{}} }; +void f(alignas(1) char c) { // expected-error {{'alignas' attribute cannot be applied to a function parameter}} + alignas(1) register char k; // expected-error {{'alignas' attribute cannot be applied to a variable with 'register' storage class}} + try { + } catch (alignas(4) int n) { // expected-error {{'alignas' attribute cannot be applied to a 'catch' variable}} + } +} + + 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}} -typedef char align_typedef alignas(8); -template using align_alias_template = align_typedef; +typedef char align_typedef alignas(8); // expected-error {{'alignas' attribute only applies to variables, functions and tag types}} +template using align_alias_template = align_typedef alignas(8); // expected-error {{'alignas' attribute ignored when parsing type}}; static_assert(alignof(align_big) == alignof(int), "k's alignment is wrong"); static_assert(alignof(align_small) == 1, "j's alignment is wrong"); static_assert(alignof(align_multiple) == 8, "l's alignment is wrong"); 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_typedef) == 8, "typedef's alignment 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_alias_template) == 8, "alias template's alignment is wrong");