Skip to content

Commit 5f26072

Browse files
committed
[Clang] Consider preferred_type in bitfield warnings (#116760)
Very simply extends the bitfield sema checks for assignment to fields with a preferred type specified to consider the preferred type if the decl storage type is not explicitly an enum type. This does mean that if the preferred and explicit types have different storage requirements we may not warn in all possible cases, but that's a scenario for which the warnings are much more complex and confusing.
1 parent 2310e3e commit 5f26072

File tree

4 files changed

+546
-7
lines changed

4 files changed

+546
-7
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6404,20 +6404,23 @@ def warn_bitfield_width_exceeds_type_width: Warning<
64046404
def err_bitfield_too_wide : Error<
64056405
"%select{bit-field %1|anonymous bit-field}0 is too wide (%2 bits)">;
64066406
def warn_bitfield_too_small_for_enum : Warning<
6407-
"bit-field %0 is not wide enough to store all enumerators of %1">,
6407+
"bit-field %0 is not wide enough to store all enumerators of %select{|preferred type }1%2">,
64086408
InGroup<BitFieldEnumConversion>, DefaultIgnore;
64096409
def note_widen_bitfield : Note<
64106410
"widen this field to %0 bits to store all values of %1">;
64116411
def warn_unsigned_bitfield_assigned_signed_enum : Warning<
6412-
"assigning value of signed enum type %1 to unsigned bit-field %0; "
6412+
"assigning value of %select{|preferred }1signed enum type %2 to unsigned bit-field %0; "
64136413
"negative enumerators of enum %1 will be converted to positive values">,
64146414
InGroup<BitFieldEnumConversion>, DefaultIgnore;
64156415
def warn_signed_bitfield_enum_conversion : Warning<
64166416
"signed bit-field %0 needs an extra bit to represent the largest positive "
6417-
"enumerators of %1">,
6417+
"enumerators of %select{|preferred type }1%2">,
64186418
InGroup<BitFieldEnumConversion>, DefaultIgnore;
64196419
def note_change_bitfield_sign : Note<
64206420
"consider making the bitfield type %select{unsigned|signed}0">;
6421+
def note_bitfield_preferred_type : Note<
6422+
"preferred type for bitfield %0 specified here"
6423+
>;
64216424

64226425
def warn_missing_braces : Warning<
64236426
"suggest braces around initialization of subobject">,

clang/lib/Sema/SemaChecking.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10488,7 +10488,14 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
1048810488
// The RHS is not constant. If the RHS has an enum type, make sure the
1048910489
// bitfield is wide enough to hold all the values of the enum without
1049010490
// truncation.
10491-
if (const auto *EnumTy = OriginalInit->getType()->getAs<EnumType>()) {
10491+
const auto *EnumTy = OriginalInit->getType()->getAs<EnumType>();
10492+
const PreferredTypeAttr *PTAttr = nullptr;
10493+
if (!EnumTy) {
10494+
PTAttr = Bitfield->getAttr<PreferredTypeAttr>();
10495+
if (PTAttr)
10496+
EnumTy = PTAttr->getType()->getAs<EnumType>();
10497+
}
10498+
if (EnumTy) {
1049210499
EnumDecl *ED = EnumTy->getDecl();
1049310500
bool SignedBitfield = BitfieldType->isSignedIntegerType();
1049410501

@@ -10509,14 +10516,18 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
1050910516
ED->getNumPositiveBits() == FieldWidth) {
1051010517
DiagID = diag::warn_signed_bitfield_enum_conversion;
1051110518
}
10512-
10519+
unsigned PreferredTypeDiagIndex = PTAttr != nullptr;
1051310520
if (DiagID) {
10514-
S.Diag(InitLoc, DiagID) << Bitfield << ED;
10521+
S.Diag(InitLoc, DiagID) << Bitfield << PreferredTypeDiagIndex << ED;
1051510522
TypeSourceInfo *TSI = Bitfield->getTypeSourceInfo();
1051610523
SourceRange TypeRange =
1051710524
TSI ? TSI->getTypeLoc().getSourceRange() : SourceRange();
1051810525
S.Diag(Bitfield->getTypeSpecStartLoc(), diag::note_change_bitfield_sign)
1051910526
<< SignedEnum << TypeRange;
10527+
if (PTAttr) {
10528+
S.Diag(PTAttr->getLocation(), diag::note_bitfield_preferred_type)
10529+
<< ED;
10530+
}
1052010531
}
1052110532

1052210533
// Compute the required bitwidth. If the enum has negative values, we need
@@ -10530,9 +10541,13 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
1053010541
if (BitsNeeded > FieldWidth) {
1053110542
Expr *WidthExpr = Bitfield->getBitWidth();
1053210543
S.Diag(InitLoc, diag::warn_bitfield_too_small_for_enum)
10533-
<< Bitfield << ED;
10544+
<< Bitfield << PreferredTypeDiagIndex << ED;
1053410545
S.Diag(WidthExpr->getExprLoc(), diag::note_widen_bitfield)
1053510546
<< BitsNeeded << ED << WidthExpr->getSourceRange();
10547+
if (PTAttr) {
10548+
S.Diag(PTAttr->getLocation(), diag::note_bitfield_preferred_type)
10549+
<< ED;
10550+
}
1053610551
}
1053710552
}
1053810553

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c11 -Wno-unused-value -Wno-unused-but-set-variable -Wbitfield-width -Wbitfield-enum-conversion
2+
3+
enum A {
4+
A_a,
5+
A_b,
6+
A_c,
7+
A_d
8+
};
9+
10+
struct S {
11+
enum A a1 : 1; // #1
12+
enum A a2 : 2;
13+
enum A a3 : 8;
14+
__attribute__((preferred_type(enum A))) // #preferred_a4
15+
unsigned a4 : 1; // #2
16+
__attribute__((preferred_type(enum A)))
17+
unsigned a5 : 2;
18+
__attribute__((preferred_type(enum A)))
19+
unsigned a6 : 8;
20+
__attribute__((preferred_type(enum A))) // #preferred_a7
21+
int a7 : 1; // #3
22+
__attribute__((preferred_type(enum A))) // #preferred_a8
23+
int a8 : 2; // #4
24+
__attribute__((preferred_type(enum A)))
25+
int a9 : 8;
26+
};
27+
28+
void read_enum(struct S *s) {
29+
enum A x;
30+
x = s->a1;
31+
x = s->a2;
32+
x = s->a3;
33+
x = s->a4;
34+
x = s->a5;
35+
x = s->a6;
36+
x = s->a7;
37+
x = s->a8;
38+
x = s->a9;
39+
}
40+
41+
void write_enum(struct S *s, enum A x) {
42+
s->a1 = x;
43+
// expected-warning@-1 {{bit-field 'a1' is not wide enough to store all enumerators of 'A'}}
44+
// expected-note@#1 {{widen this field to 2 bits to store all values of 'A'}}
45+
s->a2 = x;
46+
s->a3 = x;
47+
s->a4 = x;
48+
// expected-warning@-1 {{bit-field 'a4' is not wide enough to store all enumerators of 'A'}}
49+
// expected-note@#2 {{widen this field to 2 bits to store all values of 'A'}}
50+
s->a5 = x;
51+
s->a6 = x;
52+
s->a7 = x;
53+
// expected-warning@-1 {{bit-field 'a7' is not wide enough to store all enumerators of 'A'}}
54+
// expected-note@#3 {{widen this field to 2 bits to store all values of 'A'}}
55+
s->a8 = x;
56+
// expected-warning@-1 {{signed bit-field 'a8' needs an extra bit to represent the largest positive enumerators of 'A'}}
57+
// expected-note@#4 {{consider making the bitfield type unsigned}}
58+
s->a9 = x;
59+
}
60+
61+
void write_enum_int(struct S *s, int x) {
62+
s->a1 = x;
63+
s->a2 = x;
64+
s->a3 = x;
65+
s->a4 = x;
66+
// expected-warning@-1 {{bit-field 'a4' is not wide enough to store all enumerators of preferred type 'A'}}
67+
// expected-note@#2 {{widen this field to 2 bits to store all values of 'A'}}
68+
// expected-note@#preferred_a4 {{preferred type for bitfield 'A' specified here}}
69+
s->a5 = x;
70+
s->a6 = x;
71+
s->a7 = x;
72+
// expected-warning@-1 {{bit-field 'a7' is not wide enough to store all enumerators of preferred type 'A'}}
73+
// expected-note@#3 {{widen this field to 2 bits to store all values of 'A'}}
74+
// expected-note@#preferred_a7 {{preferred type for bitfield 'A' specified here}}
75+
s->a8 = x;
76+
// expected-warning@-1 {{signed bit-field 'a8' needs an extra bit to represent the largest positive enumerators of preferred type 'A'}}
77+
// expected-note@#4 {{consider making the bitfield type unsigned}}
78+
// expected-note@#preferred_a8 {{preferred type for bitfield 'A' specified here}}
79+
s->a9 = x;
80+
}
81+
82+
void write_low_constant(struct S *s) {
83+
s->a1 = A_a;
84+
s->a2 = A_a;
85+
s->a3 = A_a;
86+
s->a4 = A_a;
87+
s->a5 = A_a;
88+
s->a6 = A_a;
89+
s->a7 = A_a;
90+
s->a8 = A_a;
91+
s->a9 = A_a;
92+
};
93+
94+
void write_high_constant(struct S *s) {
95+
s->a1 = A_d;
96+
// expected-warning@-1 {{implicit truncation from 'int' to bit-field changes value from 3 to 1}}
97+
s->a2 = A_d;
98+
s->a3 = A_d;
99+
s->a4 = A_d;
100+
// expected-warning@-1 {{implicit truncation from 'int' to bit-field changes value from 3 to 1}}
101+
s->a5 = A_d;
102+
s->a6 = A_d;
103+
s->a7 = A_d;
104+
// expected-warning@-1 {{implicit truncation from 'int' to bit-field changes value from 3 to -1}}
105+
s->a8 = A_d;
106+
// expected-warning@-1 {{implicit truncation from 'int' to bit-field changes value from 3 to -1}}
107+
s->a9 = A_d;
108+
};

0 commit comments

Comments
 (0)