Skip to content

Commit b9cf7f1

Browse files
authored
[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 #81472
1 parent c448bd8 commit b9cf7f1

File tree

6 files changed

+83
-39
lines changed

6 files changed

+83
-39
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,19 @@ C23 Feature Support
122122
- No longer diagnose use of binary literals as an extension in C23 mode. Fixes
123123
`#72017 <https://github.com/llvm/llvm-project/issues/72017>`_.
124124

125+
- Corrected parsing behavior for the ``alignas`` specifier/qualifier in C23. We
126+
previously handled it as an attribute as in C++, but there are parsing
127+
differences. The behavioral differences are:
128+
129+
.. code-block:: c
130+
131+
struct alignas(8) /* was accepted, now rejected */ S {
132+
char alignas(8) /* was rejected, now accepted */ C;
133+
};
134+
int i alignas(8) /* was accepted, now rejected */ ;
135+
136+
Fixes (`#81472 <https://github.com/llvm/llvm-project/issues/81472>`_).
137+
125138
Non-comprehensive list of changes in this release
126139
-------------------------------------------------
127140

clang/lib/Parse/ParseDecl.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3518,8 +3518,24 @@ void Parser::ParseDeclarationSpecifiers(
35183518
DS.Finish(Actions, Policy);
35193519
return;
35203520

3521-
case tok::l_square:
3521+
// alignment-specifier
3522+
case tok::kw__Alignas:
3523+
if (!getLangOpts().C11)
3524+
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
3525+
[[fallthrough]];
35223526
case tok::kw_alignas:
3527+
// _Alignas and alignas (C23, not C++) should parse the same way. The C++
3528+
// parsing for alignas happens through the usual attribute parsing. This
3529+
// ensures that an alignas specifier can appear in a type position in C
3530+
// despite that not being valid in C++.
3531+
if (getLangOpts().C23 || Tok.getKind() == tok::kw__Alignas) {
3532+
if (Tok.getKind() == tok::kw_alignas)
3533+
Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();
3534+
ParseAlignmentSpecifier(DS.getAttributes());
3535+
continue;
3536+
}
3537+
[[fallthrough]];
3538+
case tok::l_square:
35233539
if (!isAllowedCXX11AttributeSpecifier())
35243540
goto DoneWithDeclSpec;
35253541

@@ -4234,13 +4250,6 @@ void Parser::ParseDeclarationSpecifiers(
42344250
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
42354251
break;
42364252

4237-
// alignment-specifier
4238-
case tok::kw__Alignas:
4239-
if (!getLangOpts().C11)
4240-
Diag(Tok, diag::ext_c11_feature) << Tok.getName();
4241-
ParseAlignmentSpecifier(DS.getAttributes());
4242-
continue;
4243-
42444253
// friend
42454254
case tok::kw_friend:
42464255
if (DSContext == DeclSpecContext::DSC_class)
@@ -5857,6 +5866,11 @@ bool Parser::isDeclarationSpecifier(
58575866
case tok::kw__Atomic:
58585867
return true;
58595868

5869+
case tok::kw_alignas:
5870+
// alignas is a type-specifier-qualifier in C23, which is a kind of
5871+
// declaration-specifier. Outside of C23 mode (including in C++), it is not.
5872+
return getLangOpts().C23;
5873+
58605874
// GNU ObjC bizarre protocol extension: <proto1,proto2> with implicit 'id'.
58615875
case tok::less:
58625876
return getLangOpts().ObjC;

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4661,10 +4661,12 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
46614661
CachedTokens &OpenMPTokens,
46624662
SourceLocation *EndLoc) {
46634663
if (Tok.is(tok::kw_alignas)) {
4664-
if (getLangOpts().C23)
4665-
Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();
4666-
else
4667-
Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas);
4664+
// alignas is a valid token in C23 but it is not an attribute, it's a type-
4665+
// specifier-qualifier, which means it has different parsing behavior. We
4666+
// handle this in ParseDeclarationSpecifiers() instead of here in C. We
4667+
// should not get here for C any longer.
4668+
assert(getLangOpts().CPlusPlus && "'alignas' is not an attribute in C");
4669+
Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas);
46684670
ParseAlignmentSpecifier(Attrs, EndLoc);
46694671
return;
46704672
}

clang/lib/Parse/ParseTentative.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,8 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
737737
Parser::CXX11AttributeKind
738738
Parser::isCXX11AttributeSpecifier(bool Disambiguate,
739739
bool OuterMightBeMessageSend) {
740-
if (Tok.is(tok::kw_alignas))
740+
// alignas is an attribute specifier in C++ but not in C23.
741+
if (Tok.is(tok::kw_alignas) && !getLangOpts().C23)
741742
return CAK_AttributeSpecifier;
742743

743744
if (Tok.isRegularKeywordAttribute())

clang/test/C/C2x/n2934.c

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
1-
// RUN: %clang_cc1 -ffreestanding -verify=c2x -std=c2x -Wpre-c2x-compat %s
2-
// RUN: %clang_cc1 -ffreestanding -verify=c17 -std=c17 %s
1+
// RUN: %clang_cc1 -ffreestanding -verify=expected,c2x -std=c2x -Wpre-c2x-compat %s
2+
// RUN: %clang_cc1 -ffreestanding -verify=expected,c17 -std=c17 %s
33

44
/* WG14 N2934: yes
55
* Revise spelling of keywords v7
66
*/
77

8-
thread_local struct alignas(int) S { // c2x-warning {{'alignas' is incompatible with C standards before C23}} \
9-
c2x-warning {{'thread_local' is incompatible with C standards before C23}} \
10-
c2x-error 0+ {{thread-local storage is not supported for the current target}} \
11-
c17-error {{unknown type name 'thread_local'}} \
12-
c17-error {{expected identifier or '('}} \
13-
c17-error {{expected ')'}} \
14-
c17-note {{to match this '('}}
15-
bool b; // c2x-warning {{'bool' is incompatible with C standards before C23}}
16-
} s; // c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}}
17-
18-
static_assert(alignof(struct S) == alignof(int), ""); // c2x-warning {{'static_assert' is incompatible with C standards before C23}} \
19-
c2x-warning 2 {{'alignof' is incompatible with C standards before C23}} \
20-
c17-error 2 {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
21-
c17-error {{expected ')'}} \
22-
c17-warning {{declaration of 'struct S' will not be visible outside of this function}} \
23-
c17-note {{to match this '('}}
8+
thread_local struct S { // c2x-warning {{'thread_local' is incompatible with C standards before C23}} \
9+
c2x-error 0+ {{thread-local storage is not supported for the current target}} \
10+
c17-error {{unknown type name 'thread_local'}}
11+
bool b; // c2x-warning {{'bool' is incompatible with C standards before C23}} \
12+
c17-error {{unknown type name 'bool'}}
13+
} s;
14+
15+
static_assert(alignof(int) != 0, ""); // c2x-warning {{'static_assert' is incompatible with C standards before C23}} \
16+
c2x-warning {{'alignof' is incompatible with C standards before C23}} \
17+
c17-error 2 {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
18+
c17-error {{expected ')'}} \
19+
c17-note {{to match this '('}}
2420

2521
#include <stdalign.h>
2622

@@ -56,3 +52,28 @@ static_assert(alignof(struct S) == alignof(int), ""); // c2x-warning {{'static_a
5652
#error "bool should not be defined"
5753
#endif
5854
#endif
55+
56+
// Ensure we correctly parse the alignas keyword in a specifier-qualifier-list.
57+
// This is different than in C++ where alignas is an actual attribute rather
58+
// than a specifier.
59+
struct GH81472 {
60+
char alignas(8) a1; // c2x-warning {{'alignas' is incompatible with C standards before C23}}
61+
alignas(8) char a2; // c2x-warning {{'alignas' is incompatible with C standards before C23}}
62+
char _Alignas(8) a3;
63+
_Alignas(8) char a4;
64+
char a5 alignas(8); // expected-error {{expected ';' at end of declaration list}}
65+
char a6 _Alignas(8); // expected-error {{expected ';' at end of declaration list}}
66+
};
67+
68+
// Ensure we reject alignas as an attribute specifier. This code is accepted in
69+
// C++ mode but should be rejected in C.
70+
// FIXME: this diagnostic could be improved
71+
struct alignas(8) Reject1 { // expected-error {{declaration of anonymous struct must be a definition}} \
72+
expected-warning {{declaration does not declare anything}}
73+
int a;
74+
};
75+
76+
struct _Alignas(8) Reject2 { // expected-error {{declaration of anonymous struct must be a definition}} \
77+
expected-warning {{declaration does not declare anything}}
78+
int a;
79+
};

clang/test/Parser/c2x-alignas.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
11
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s
22

33
_Alignas(int) struct c1; // expected-warning {{'_Alignas' attribute ignored}}
4-
5-
// FIXME: `alignas` enters into C++ parsing code and never reaches the
6-
// declaration specifier attribute diagnostic infrastructure.
7-
//
8-
// Fixing this will require the C23 notions of `alignas` being a keyword and
9-
// `_Alignas` being an alternate spelling integrated into the parsing
10-
// infrastructure.
11-
alignas(int) struct c1; // expected-error {{misplaced attributes; expected attributes here}}
4+
alignas(int) struct c1; // expected-warning {{'alignas' attribute ignored}}

0 commit comments

Comments
 (0)