[clang] b9cf7f1 - [C23] Fix handling of alignas (#81637)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 15 04:58:04 PST 2024
Author: Aaron Ballman
Date: 2024-02-15T07:58:01-05:00
New Revision: b9cf7f1066cc7ec11c6074d10b0f2ec9a3ae72d9
URL: https://github.com/llvm/llvm-project/commit/b9cf7f1066cc7ec11c6074d10b0f2ec9a3ae72d9
DIFF: https://github.com/llvm/llvm-project/commit/b9cf7f1066cc7ec11c6074d10b0f2ec9a3ae72d9.diff
LOG: [C23] Fix handling of alignas (#81637)
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
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Parse/ParseTentative.cpp
clang/test/C/C2x/n2934.c
clang/test/Parser/c2x-alignas.c
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a745f20199ceba..b5e6eae9f1bf7d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -122,6 +122,19 @@ C23 Feature Support
- No longer diagnose use of binary literals as an extension in C23 mode. Fixes
`#72017 <https://github.com/llvm/llvm-project/issues/72017>`_.
+- Corrected parsing behavior for the ``alignas`` specifier/qualifier in C23. We
+ previously handled it as an attribute as in C++, but there are parsing
+
diff erences. The behavioral
diff erences 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
diff erent 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 47c85030f4f6c5..299e46f4be9a2c 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
diff erent 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}}
More information about the cfe-commits
mailing list