Skip to content

Commit bf02f41

Browse files
authored
Fix a regression with alignas on structure members in C (llvm#98642)
This was a 19.x regression and thus has no release note. Fixes llvm#95032
1 parent 8badfcc commit bf02f41

File tree

4 files changed

+66
-39
lines changed

4 files changed

+66
-39
lines changed

clang/lib/Sema/ParsedAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ bool ParsedAttr::slidesFromDeclToDeclSpecLegacyBehavior() const {
225225
// atributes.
226226
return false;
227227

228-
assert(isStandardAttributeSyntax());
228+
assert(isStandardAttributeSyntax() || isAlignas());
229229

230230
// We have historically allowed some type attributes with standard attribute
231231
// syntax to slide to the decl-specifier-seq, so we have to keep supporting

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6435,8 +6435,14 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
64356435
return;
64366436

64376437
// Ignore C++11 attributes on declarator chunks: they appertain to the type
6438-
// instead.
6439-
if (AL.isCXX11Attribute() && !Options.IncludeCXX11Attributes)
6438+
// instead. Note, isCXX11Attribute() will look at whether the attribute is
6439+
// [[]] or alignas, while isC23Attribute() will only look at [[]]. This is
6440+
// important for ensuring that alignas in C23 is properly handled on a
6441+
// structure member declaration because it is a type-specifier-qualifier in
6442+
// C but still applies to the declaration rather than the type.
6443+
if ((S.getLangOpts().CPlusPlus ? AL.isCXX11Attribute()
6444+
: AL.isC23Attribute()) &&
6445+
!Options.IncludeCXX11Attributes)
64406446
return;
64416447

64426448
// Unknown attributes are automatically warned on. Target-specific attributes
@@ -7500,29 +7506,37 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) {
75007506
// Ordering of attributes can be important, so we take care to process
75017507
// attributes in the order in which they appeared in the source code.
75027508

7509+
auto ProcessAttributesWithSliding =
7510+
[&](const ParsedAttributesView &Src,
7511+
const ProcessDeclAttributeOptions &Options) {
7512+
ParsedAttributesView NonSlidingAttrs;
7513+
for (ParsedAttr &AL : Src) {
7514+
// FIXME: this sliding is specific to standard attributes and should
7515+
// eventually be deprecated and removed as those are not intended to
7516+
// slide to anything.
7517+
if ((AL.isStandardAttributeSyntax() || AL.isAlignas()) &&
7518+
AL.slidesFromDeclToDeclSpecLegacyBehavior()) {
7519+
// Skip processing the attribute, but do check if it appertains to
7520+
// the declaration. This is needed for the `MatrixType` attribute,
7521+
// which, despite being a type attribute, defines a `SubjectList`
7522+
// that only allows it to be used on typedef declarations.
7523+
AL.diagnoseAppertainsTo(*this, D);
7524+
} else {
7525+
NonSlidingAttrs.addAtEnd(&AL);
7526+
}
7527+
}
7528+
ProcessDeclAttributeList(S, D, NonSlidingAttrs, Options);
7529+
};
7530+
75037531
// First, process attributes that appeared on the declaration itself (but
75047532
// only if they don't have the legacy behavior of "sliding" to the DeclSepc).
7505-
ParsedAttributesView NonSlidingAttrs;
7506-
for (ParsedAttr &AL : PD.getDeclarationAttributes()) {
7507-
if (AL.slidesFromDeclToDeclSpecLegacyBehavior()) {
7508-
// Skip processing the attribute, but do check if it appertains to the
7509-
// declaration. This is needed for the `MatrixType` attribute, which,
7510-
// despite being a type attribute, defines a `SubjectList` that only
7511-
// allows it to be used on typedef declarations.
7512-
AL.diagnoseAppertainsTo(*this, D);
7513-
} else {
7514-
NonSlidingAttrs.addAtEnd(&AL);
7515-
}
7516-
}
7517-
ProcessDeclAttributeList(S, D, NonSlidingAttrs);
7533+
ProcessAttributesWithSliding(PD.getDeclarationAttributes(), {});
75187534

75197535
// Apply decl attributes from the DeclSpec if present.
7520-
if (!PD.getDeclSpec().getAttributes().empty()) {
7521-
ProcessDeclAttributeList(S, D, PD.getDeclSpec().getAttributes(),
7522-
ProcessDeclAttributeOptions()
7523-
.WithIncludeCXX11Attributes(false)
7524-
.WithIgnoreTypeAttributes(true));
7525-
}
7536+
ProcessAttributesWithSliding(PD.getDeclSpec().getAttributes(),
7537+
ProcessDeclAttributeOptions()
7538+
.WithIncludeCXX11Attributes(false)
7539+
.WithIgnoreTypeAttributes(true));
75267540

75277541
// Walk the declarator structure, applying decl attributes that were in a type
75287542
// position to the decl itself. This handles cases like:

clang/test/C/C2y/n3254.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ struct S {
2121
// CHECK-LABEL: define dso_local i32 @foo(
2222
// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
2323
// CHECK-NEXT: [[ENTRY:.*:]]
24-
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 1
24+
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 4
2525
// CHECK-NEXT: [[S_PTR:%.*]] = alloca ptr, align 8
26-
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[BUFFER]], i8 0, i64 12, i1 false)
26+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[BUFFER]], i8 0, i64 12, i1 false)
2727
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
2828
// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[S_PTR]], align 8
2929
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S_PTR]], align 8
@@ -40,13 +40,13 @@ int foo() {
4040
// CHECK-LABEL: define dso_local signext i8 @bar(
4141
// CHECK-SAME: ) #[[ATTR0]] {
4242
// CHECK-NEXT: [[ENTRY:.*:]]
43-
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 1
43+
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 4
4444
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
4545
// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], ptr [[ARRAYDECAY]], i32 0, i32 1
46-
// CHECK-NEXT: store i8 97, ptr [[C]], align 1
46+
// CHECK-NEXT: store i8 97, ptr [[C]], align 4
4747
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
4848
// CHECK-NEXT: [[C2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[ARRAYDECAY1]], i32 0, i32 1
49-
// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[C2]], align 1
49+
// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[C2]], align 4
5050
// CHECK-NEXT: ret i8 [[TMP0]]
5151
//
5252
char bar() {
@@ -58,13 +58,13 @@ char bar() {
5858
// CHECK-LABEL: define dso_local float @baz(
5959
// CHECK-SAME: ) #[[ATTR0]] {
6060
// CHECK-NEXT: [[ENTRY:.*:]]
61-
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 1
61+
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 4
6262
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
6363
// CHECK-NEXT: [[F:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], ptr [[ARRAYDECAY]], i32 0, i32 2
64-
// CHECK-NEXT: store float 3.000000e+00, ptr [[F]], align 1
64+
// CHECK-NEXT: store float 3.000000e+00, ptr [[F]], align 4
6565
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
6666
// CHECK-NEXT: [[F2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[ARRAYDECAY1]], i32 0, i32 2
67-
// CHECK-NEXT: [[TMP0:%.*]] = load float, ptr [[F2]], align 1
67+
// CHECK-NEXT: [[TMP0:%.*]] = load float, ptr [[F2]], align 4
6868
// CHECK-NEXT: ret float [[TMP0]]
6969
//
7070
float baz() {
@@ -80,9 +80,9 @@ struct T {
8080
// CHECK-LABEL: define dso_local signext i8 @quux(
8181
// CHECK-SAME: ) #[[ATTR0]] {
8282
// CHECK-NEXT: [[ENTRY:.*:]]
83-
// CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_T:%.*]], align 1
83+
// CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_T:%.*]], align 4
8484
// CHECK-NEXT: [[S_PTR:%.*]] = alloca ptr, align 8
85-
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[T]], i8 0, i64 12, i1 false)
85+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[T]], i8 0, i64 12, i1 false)
8686
// CHECK-NEXT: [[BUFFER:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[T]], i32 0, i32 0
8787
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
8888
// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[S_PTR]], align 8
@@ -100,10 +100,10 @@ char quux() {
100100
// CHECK-LABEL: define dso_local float @quibble(
101101
// CHECK-SAME: ) #[[ATTR0]] {
102102
// CHECK-NEXT: [[ENTRY:.*:]]
103-
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 1
103+
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 4
104104
// CHECK-NEXT: [[T_PTR:%.*]] = alloca ptr, align 8
105105
// CHECK-NEXT: [[S_PTR:%.*]] = alloca ptr, align 8
106-
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[BUFFER]], i8 0, i64 12, i1 false)
106+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[BUFFER]], i8 0, i64 12, i1 false)
107107
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
108108
// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[T_PTR]], align 8
109109
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[T_PTR]], align 8
@@ -125,13 +125,13 @@ float quibble() {
125125
// CHECK-LABEL: define dso_local i32 @quorble(
126126
// CHECK-SAME: ) #[[ATTR0]] {
127127
// CHECK-NEXT: [[ENTRY:.*:]]
128-
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 1
128+
// CHECK-NEXT: [[BUFFER:%.*]] = alloca [12 x i8], align 4
129129
// CHECK-NEXT: [[S_PTR:%.*]] = alloca ptr, align 8
130130
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
131131
// CHECK-NEXT: [[BUFFER1:%.*]] = getelementptr inbounds [[STRUCT_T:%.*]], ptr [[ARRAYDECAY]], i32 0, i32 0
132132
// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER1]], i64 0, i64 0
133133
// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], ptr [[ARRAYDECAY2]], i32 0, i32 0
134-
// CHECK-NEXT: store i32 12, ptr [[X]], align 1
134+
// CHECK-NEXT: store i32 12, ptr [[X]], align 4
135135
// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER]], i64 0, i64 0
136136
// CHECK-NEXT: [[BUFFER4:%.*]] = getelementptr inbounds [[STRUCT_T]], ptr [[ARRAYDECAY3]], i32 0, i32 0
137137
// CHECK-NEXT: [[ARRAYDECAY5:%.*]] = getelementptr inbounds [12 x i8], ptr [[BUFFER4]], i64 0, i64 0

clang/test/Sema/alignas.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify -std=c11 -Dalignof=__alignof %s
22
// RUN: %clang_cc1 -fsyntax-only -verify -std=c11 -Dalignof=_Alignof -DUSING_C11_SYNTAX %s
3+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c23 -DUSING_C11_SYNTAX %s
34

45
_Alignas(3) int align_illegal; //expected-error {{requested alignment is not a power of 2}}
56
_Alignas(int) char align_big;
@@ -18,12 +19,24 @@ void f(_Alignas(1) char c) { // expected-error {{'_Alignas' attribute cannot be
1819
}
1920

2021
#ifdef USING_C11_SYNTAX
21-
// expected-warning@+4{{'_Alignof' applied to an expression is a GNU extension}}
22-
// expected-warning@+4{{'_Alignof' applied to an expression is a GNU extension}}
23-
// expected-warning@+4{{'_Alignof' applied to an expression is a GNU extension}}
22+
// expected-warning-re@+4{{'{{(_A|a)}}lignof' applied to an expression is a GNU extension}}
23+
// expected-warning-re@+4{{'{{(_A|a)}}lignof' applied to an expression is a GNU extension}}
24+
// expected-warning-re@+4{{'{{(_A|a)}}lignof' applied to an expression is a GNU extension}}
2425
#endif
2526
_Static_assert(alignof(align_big) == alignof(int), "k's alignment is wrong");
2627
_Static_assert(alignof(align_small) == 1, "j's alignment is wrong");
2728
_Static_assert(alignof(align_multiple) == 8, "l's alignment is wrong");
2829
_Static_assert(alignof(struct align_member) == 8, "quuux's alignment is wrong");
2930
_Static_assert(sizeof(struct align_member) == 8, "quuux's size is wrong");
31+
32+
struct GH95032_1 {
33+
_Alignas(16) char bytes[16];
34+
};
35+
_Static_assert(_Alignof(struct GH95032_1) == 16, "");
36+
37+
#if __STDC_VERSION__ >= 202311L
38+
struct GH95032_2 {
39+
alignas(16) char bytes[16];
40+
};
41+
static_assert(alignof(struct GH95032_2) == 16);
42+
#endif

0 commit comments

Comments
 (0)