Skip to content

Commit

Permalink
[Sema] Add check for bitfield assignments to integral types (#69049)
Browse files Browse the repository at this point in the history
This change introduces the bitfield conversion check after fixes to the
test case. The original submission unfortunately did not comprehend
differences in architecture for the diagnostic messages, leading to
unanticipated failures in the arm build bots.

Original PR: #68276

Clang does not check for bitfield assignment widths, while gcc checks
this.

gcc produced a warning like so for it's -Wconversion flag:
```
$ gcc -Wconversion -c test.c
test.c: In function 'foo':
test.c:10:15: warning: conversion from 'int' to 'signed char:7' may
change value [-Wconversion]
   10 |      vxx.bf = x; // no warning
      |               ^
```

This change simply adds this check for integral types under the
-Wbitfield-conversion compiler option.
  • Loading branch information
vabridgers committed Oct 18, 2023
1 parent 410f413 commit 708808e
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 1 deletion.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ New Compiler Flags
the preprocessed text to the output. This can greatly reduce the size of the
preprocessed output, which can be helpful when trying to reduce a test case.

* ``-Wbitfield-conversion`` was added to detect assignments of integral
types to a bitfield that may change the value.

Deprecated Compiler Flags
-------------------------

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def SingleBitBitFieldConstantConversion :
def BitFieldConstantConversion : DiagGroup<"bitfield-constant-conversion",
[SingleBitBitFieldConstantConversion]>;
def BitFieldEnumConversion : DiagGroup<"bitfield-enum-conversion">;
def BitFieldConversion : DiagGroup<"bitfield-conversion">;
def BitFieldWidth : DiagGroup<"bitfield-width">;
def CompoundTokenSplitByMacro : DiagGroup<"compound-token-split-by-macro">;
def CompoundTokenSplitBySpace : DiagGroup<"compound-token-split-by-space">;
Expand Down Expand Up @@ -933,6 +934,7 @@ def Conversion : DiagGroup<"conversion",
ConstantConversion,
EnumConversion,
BitFieldEnumConversion,
BitFieldConversion,
FloatConversion,
Shorten64To32,
IntConversion,
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6171,6 +6171,9 @@ def warn_signed_bitfield_enum_conversion : Warning<
"signed bit-field %0 needs an extra bit to represent the largest positive "
"enumerators of %1">,
InGroup<BitFieldEnumConversion>, DefaultIgnore;
def warn_bitfield_too_small_for_integral_type : Warning<
"conversion from %2 (%3 bits) to bit-field %0 (%1 bits) may change value">,
InGroup<BitFieldConversion>, DefaultIgnore;
def note_change_bitfield_sign : Note<
"consider making the bitfield type %select{unsigned|signed}0">;

Expand Down
13 changes: 12 additions & 1 deletion clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14331,6 +14331,18 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
S.Diag(WidthExpr->getExprLoc(), diag::note_widen_bitfield)
<< BitsNeeded << ED << WidthExpr->getSourceRange();
}
} else if (OriginalInit->getType()->isIntegralType(S.Context)) {
IntRange LikelySourceRange =
GetExprRange(S.Context, Init, S.isConstantEvaluatedContext(),
/*Approximate=*/true);

if (LikelySourceRange.Width > FieldWidth) {
Expr *WidthExpr = Bitfield->getBitWidth();
S.Diag(InitLoc, diag::warn_bitfield_too_small_for_integral_type)
<< Bitfield << FieldWidth << OriginalInit->getType()
<< LikelySourceRange.Width;
S.Diag(WidthExpr->getExprLoc(), diag::note_declared_at);
}
}

return false;
Expand Down Expand Up @@ -15228,7 +15240,6 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,

if (LikelySourceRange.Width > TargetRange.Width) {
// If the source is a constant, use a default-on diagnostic.
// TODO: this should happen for bitfield stores, too.
Expr::EvalResult Result;
if (E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects,
S.isConstantEvaluatedContext())) {
Expand Down
42 changes: 42 additions & 0 deletions clang/test/SemaCXX/bitfield-width.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// RUN: %clang_cc1 -Wconversion -fsyntax-only -verify %s
// RUN: %clang_cc1 -Wbitfield-conversion -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple armebv7-unknown-linux -Wbitfield-conversion \
// RUN: -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple arm64-unknown-linux -Wbitfield-conversion \
// RUN: -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple arm-unknown-linux -Wbitfield-conversion \
// RUN: -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple aarch64-unknown-linux -Wbitfield-conversion \
// RUN: -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple mipsel-unknown-linux -Wbitfield-conversion \
// RUN: -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple mips64el-unknown-linux -Wbitfield-conversion \
// RUN: -fsyntax-only -verify %s

typedef struct _xx {
int bf:9; // expected-note 3{{declared here}}
} xx, *pxx;

xx vxx;

void foo1(int x) {
vxx.bf = x; // expected-warning{{conversion from 'int' (32 bits) to bit-field 'bf' (9 bits) may change value}}
}
void foo2(short x) {
vxx.bf = x; // expected-warning{{conversion from 'short' (16 bits) to bit-field 'bf' (9 bits) may change value}}
}
void foo3(char x) {
vxx.bf = x; // no warning expected
}
void foo4(short x) {
vxx.bf = 0xff & x; // no warning expected
}
void foo5(short x) {
vxx.bf = 0x1ff & x; // no warning expected
}
void foo6(short x) {
vxx.bf = 0x3ff & x; // expected-warning{{conversion from 'int' (10 bits) to bit-field 'bf' (9 bits) may change value}}
}
int fee(void) {
return 0;
}

0 comments on commit 708808e

Please sign in to comment.