[clang] [C23] Fix handling of alignas (PR #81637)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 13 09:51:47 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Aaron Ballman (AaronBallman)
<details>
<summary>Changes</summary>
In C++, alignas is an attribute specifier, while in C23, it's an alias of _Alignas, which is a type specifier/qualifier. This means that they parse differently in some circumstances.
Fixes https://github.com/llvm/llvm-project/issues/81472
---
Full diff: https://github.com/llvm/llvm-project/pull/81637.diff
6 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (+13)
- (modified) clang/lib/Parse/ParseDecl.cpp (+22-8)
- (modified) clang/lib/Parse/ParseDeclCXX.cpp (+6-4)
- (modified) clang/lib/Parse/ParseTentative.cpp (+2-1)
- (modified) clang/test/C/C2x/n2934.c (+39-18)
- (modified) clang/test/Parser/c2x-alignas.c (+1-8)
``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index dc2fb3b25e3a54..637892d143f276 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -114,6 +114,19 @@ C Language Changes
C23 Feature Support
^^^^^^^^^^^^^^^^^^^
+- Corrected parsing behavior for the ``alignas`` specifier/qualifier in C23. We
+ previously handled it as an attribute as in C++, but there are parsing
+ differences. The behavioral differences are:
+
+ .. code-block:: c
+
+ struct alignas(8) /* was accepted, now rejected */ S {
+ char alignas(8) /* was rejected, now accepted */ C;
+ };
+ int i alignas(8) /* was accepted, now rejected */ ;
+
+ Fixes (`#81472 <https://github.com/llvm/llvm-project/issues/81472>`_).
+
Non-comprehensive list of changes in this release
-------------------------------------------------
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 94696d8e482e5d..9640d7ee70d27f 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3518,8 +3518,24 @@ void Parser::ParseDeclarationSpecifiers(
DS.Finish(Actions, Policy);
return;
- case tok::l_square:
+ // alignment-specifier
+ case tok::kw__Alignas:
+ if (!getLangOpts().C11)
+ Diag(Tok, diag::ext_c11_feature) << Tok.getName();
+ [[fallthrough]];
case tok::kw_alignas:
+ // _Alignas and alignas (C23, not C++) should parse the same way. The C++
+ // parsing for alignas happens through the usual attribute parsing. This
+ // ensures that an alignas specifier can appear in a type position in C
+ // despite that not being valid in C++.
+ if (getLangOpts().C23 || Tok.getKind() == tok::kw__Alignas) {
+ if (Tok.getKind() == tok::kw_alignas)
+ Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();
+ ParseAlignmentSpecifier(DS.getAttributes());
+ continue;
+ }
+ [[fallthrough]];
+ case tok::l_square:
if (!isAllowedCXX11AttributeSpecifier())
goto DoneWithDeclSpec;
@@ -4234,13 +4250,6 @@ void Parser::ParseDeclarationSpecifiers(
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
break;
- // alignment-specifier
- case tok::kw__Alignas:
- if (!getLangOpts().C11)
- Diag(Tok, diag::ext_c11_feature) << Tok.getName();
- ParseAlignmentSpecifier(DS.getAttributes());
- continue;
-
// friend
case tok::kw_friend:
if (DSContext == DeclSpecContext::DSC_class)
@@ -5857,6 +5866,11 @@ bool Parser::isDeclarationSpecifier(
case tok::kw__Atomic:
return true;
+ case tok::kw_alignas:
+ // alignas is a type-specifier-qualifier in C23, which is a kind of
+ // declaration-specifier. Outside of C23 mode (including in C++), it is not.
+ return getLangOpts().C23;
+
// GNU ObjC bizarre protocol extension: <proto1,proto2> with implicit 'id'.
case tok::less:
return getLangOpts().ObjC;
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 79928ddb5af599..7d0dbc4ac69490 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4661,10 +4661,12 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
CachedTokens &OpenMPTokens,
SourceLocation *EndLoc) {
if (Tok.is(tok::kw_alignas)) {
- if (getLangOpts().C23)
- Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();
- else
- Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas);
+ // alignas is a valid token in C23 but it is not an attribute, it's a type-
+ // specifier-qualifier, which means it has different parsing behavior. We
+ // handle this in ParseDeclarationSpecifiers() instead of here in C. We
+ // should not get here for C any longer.
+ assert(getLangOpts().CPlusPlus && "'alignas' is not an attribute in C");
+ Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas);
ParseAlignmentSpecifier(Attrs, EndLoc);
return;
}
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index f1737cb8447677..1f11f4bd937cd5 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -737,7 +737,8 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
Parser::CXX11AttributeKind
Parser::isCXX11AttributeSpecifier(bool Disambiguate,
bool OuterMightBeMessageSend) {
- if (Tok.is(tok::kw_alignas))
+ // alignas is an attribute specifier in C++ but not in C23.
+ if (Tok.is(tok::kw_alignas) && !getLangOpts().C23)
return CAK_AttributeSpecifier;
if (Tok.isRegularKeywordAttribute())
diff --git a/clang/test/C/C2x/n2934.c b/clang/test/C/C2x/n2934.c
index a5ee09d031914e..55de1327e3f670 100644
--- a/clang/test/C/C2x/n2934.c
+++ b/clang/test/C/C2x/n2934.c
@@ -1,26 +1,22 @@
-// RUN: %clang_cc1 -ffreestanding -verify=c2x -std=c2x -Wpre-c2x-compat %s
-// RUN: %clang_cc1 -ffreestanding -verify=c17 -std=c17 %s
+// RUN: %clang_cc1 -ffreestanding -verify=expected,c2x -std=c2x -Wpre-c2x-compat %s
+// RUN: %clang_cc1 -ffreestanding -verify=expected,c17 -std=c17 %s
/* WG14 N2934: yes
* Revise spelling of keywords v7
*/
-thread_local struct alignas(int) S { // c2x-warning {{'alignas' is incompatible with C standards before C23}} \
- c2x-warning {{'thread_local' is incompatible with C standards before C23}} \
- c2x-error 0+ {{thread-local storage is not supported for the current target}} \
- c17-error {{unknown type name 'thread_local'}} \
- c17-error {{expected identifier or '('}} \
- c17-error {{expected ')'}} \
- c17-note {{to match this '('}}
- bool b; // c2x-warning {{'bool' is incompatible with C standards before C23}}
-} s; // c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}}
-
-static_assert(alignof(struct S) == alignof(int), ""); // c2x-warning {{'static_assert' is incompatible with C standards before C23}} \
- c2x-warning 2 {{'alignof' is incompatible with C standards before C23}} \
- c17-error 2 {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
- c17-error {{expected ')'}} \
- c17-warning {{declaration of 'struct S' will not be visible outside of this function}} \
- c17-note {{to match this '('}}
+thread_local struct S { // c2x-warning {{'thread_local' is incompatible with C standards before C23}} \
+ c2x-error 0+ {{thread-local storage is not supported for the current target}} \
+ c17-error {{unknown type name 'thread_local'}}
+ bool b; // c2x-warning {{'bool' is incompatible with C standards before C23}} \
+ c17-error {{unknown type name 'bool'}}
+} s;
+
+static_assert(alignof(int) != 0, ""); // c2x-warning {{'static_assert' is incompatible with C standards before C23}} \
+ c2x-warning {{'alignof' is incompatible with C standards before C23}} \
+ c17-error 2 {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
+ c17-error {{expected ')'}} \
+ c17-note {{to match this '('}}
#include <stdalign.h>
@@ -56,3 +52,28 @@ static_assert(alignof(struct S) == alignof(int), ""); // c2x-warning {{'static_a
#error "bool should not be defined"
#endif
#endif
+
+// Ensure we correctly parse the alignas keyword in a specifier-qualifier-list.
+// This is different than in C++ where alignas is an actual attribute rather
+// than a specifier.
+struct GH81472 {
+ char alignas(8) a1; // c2x-warning {{'alignas' is incompatible with C standards before C23}}
+ alignas(8) char a2; // c2x-warning {{'alignas' is incompatible with C standards before C23}}
+ char _Alignas(8) a3;
+ _Alignas(8) char a4;
+ char a5 alignas(8); // expected-error {{expected ';' at end of declaration list}}
+ char a6 _Alignas(8); // expected-error {{expected ';' at end of declaration list}}
+};
+
+// Ensure we reject alignas as an attribute specifier. This code is accepted in
+// C++ mode but should be rejected in C.
+// FIXME: this diagnostic could be improved
+struct alignas(8) Reject1 { // expected-error {{declaration of anonymous struct must be a definition}} \
+ expected-warning {{declaration does not declare anything}}
+ int a;
+};
+
+struct _Alignas(8) Reject2 { // expected-error {{declaration of anonymous struct must be a definition}} \
+ expected-warning {{declaration does not declare anything}}
+ int a;
+};
diff --git a/clang/test/Parser/c2x-alignas.c b/clang/test/Parser/c2x-alignas.c
index 6b02b94c0a295b..1658cb1c744966 100644
--- a/clang/test/Parser/c2x-alignas.c
+++ b/clang/test/Parser/c2x-alignas.c
@@ -1,11 +1,4 @@
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s
_Alignas(int) struct c1; // expected-warning {{'_Alignas' attribute ignored}}
-
-// FIXME: `alignas` enters into C++ parsing code and never reaches the
-// declaration specifier attribute diagnostic infrastructure.
-//
-// Fixing this will require the C23 notions of `alignas` being a keyword and
-// `_Alignas` being an alternate spelling integrated into the parsing
-// infrastructure.
-alignas(int) struct c1; // expected-error {{misplaced attributes; expected attributes here}}
+alignas(int) struct c1; // expected-warning {{'alignas' attribute ignored}}
``````````
</details>
https://github.com/llvm/llvm-project/pull/81637
More information about the cfe-commits
mailing list