Further update -Wbitfield-constant-conversion for 1-bit bitfield

https://reviews.llvm.org/D131255 (82afc9b169)
began warning about conversion causing data loss for a single-bit
bit-field. However, after landing the changes, there were reports about
significant false positives from some code bases.

This alters the approach taken in that patch by introducing a new
warning group (-Wsingle-bit-bitfield-constant-conversion) which is
grouped under -Wbitfield-constant-conversion to allow users to
selectively disable the single-bit warning without losing the other
constant conversion warnings.

Differential Revision: https://reviews.llvm.org/D132851
This commit is contained in:
Aaron Ballman 2022-08-31 09:23:45 -04:00
parent faad567589
commit 3b00e48679
7 changed files with 55 additions and 17 deletions

View File

@ -106,9 +106,13 @@ Improvements to Clang's diagnostics
- ``-Wformat`` now recognizes ``%b`` for the ``printf``/``scanf`` family of
functions and ``%B`` for the ``printf`` family of functions. Fixes
`Issue 56885: <https://github.com/llvm/llvm-project/issues/56885>`_.
- ``-Wbitfield-constant-conversion`` now diagnoses implicit truncation when 1 is
assigned to a 1-bit signed integer bitfield. This fixes
`Issue 53253 <https://github.com/llvm/llvm-project/issues/53253>`_.
- Introduced ``-Wsingle-bit-bitfield-constant-conversion``, grouped under
``-Wbitfield-constant-conversion``, which diagnoses implicit truncation when
``1`` is assigned to a 1-bit signed integer bitfield. This fixes
`Issue 53253 <https://github.com/llvm/llvm-project/issues/53253>`_. To reduce
potential false positives, this diagnostic will not diagnose use of the
``true`` macro (from ``<stdbool.h>>`) in C language mode despite the macro
being defined to expand to ``1``.
- ``-Wincompatible-function-pointer-types`` now defaults to an error in all C
language modes. It may be downgraded to a warning with
``-Wno-error=incompatible-function-pointer-types`` or disabled entirely with

View File

@ -47,7 +47,10 @@ def BinaryLiteral : DiagGroup<"binary-literal", [CXX14BinaryLiteral,
CXXPre14CompatBinaryLiteral,
GNUBinaryLiteral]>;
def GNUCompoundLiteralInitializer : DiagGroup<"gnu-compound-literal-initializer">;
def BitFieldConstantConversion : DiagGroup<"bitfield-constant-conversion">;
def SingleBitBitFieldConstantConversion :
DiagGroup<"single-bit-bitfield-constant-conversion">;
def BitFieldConstantConversion : DiagGroup<"bitfield-constant-conversion",
[SingleBitBitFieldConstantConversion]>;
def BitFieldEnumConversion : DiagGroup<"bitfield-enum-conversion">;
def BitFieldWidth : DiagGroup<"bitfield-width">;
def CompoundTokenSplitByMacro : DiagGroup<"compound-token-split-by-macro">;

View File

@ -3846,6 +3846,9 @@ def warn_impcast_integer_64_32 : Warning<
def warn_impcast_integer_precision_constant : Warning<
"implicit conversion from %2 to %3 changes value from %0 to %1">,
InGroup<ConstantConversion>;
def warn_impcast_single_bit_bitield_precision_constant : Warning<
"implicit truncation from %2 to a one-bit wide bit-field changes value from "
"%0 to %1">, InGroup<SingleBitBitFieldConstantConversion>;
def warn_impcast_bitfield_precision_constant : Warning<
"implicit truncation from %2 to bit-field changes value from %0 to %1">,
InGroup<BitFieldConstantConversion>;

View File

@ -13047,6 +13047,18 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
unsigned OriginalWidth = Value.getBitWidth();
// In C, the macro 'true' from stdbool.h will evaluate to '1'; To reduce
// false positives where the user is demonstrating they intend to use the
// bit-field as a Boolean, check to see if the value is 1 and we're assigning
// to a one-bit bit-field to see if the value came from a macro named 'true'.
bool OneAssignedToOneBitBitfield = FieldWidth == 1 && Value == 1;
if (OneAssignedToOneBitBitfield && !S.LangOpts.CPlusPlus) {
SourceLocation MaybeMacroLoc = OriginalInit->getBeginLoc();
if (S.SourceMgr.isInSystemMacro(MaybeMacroLoc) &&
S.findMacroSpelling(MaybeMacroLoc, "true"))
return false;
}
if (!Value.isSigned() || Value.isNegative())
if (UnaryOperator *UO = dyn_cast<UnaryOperator>(OriginalInit))
if (UO->getOpcode() == UO_Minus || UO->getOpcode() == UO_Not)
@ -13067,9 +13079,11 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
std::string PrettyValue = toString(Value, 10);
std::string PrettyTrunc = toString(TruncatedValue, 10);
S.Diag(InitLoc, diag::warn_impcast_bitfield_precision_constant)
<< PrettyValue << PrettyTrunc << OriginalInit->getType()
<< Init->getSourceRange();
S.Diag(InitLoc, OneAssignedToOneBitBitfield
? diag::warn_impcast_single_bit_bitield_precision_constant
: diag::warn_impcast_bitfield_precision_constant)
<< PrettyValue << PrettyTrunc << OriginalInit->getType()
<< Init->getSourceRange();
return true;
}

View File

@ -911,9 +911,9 @@ namespace dr674 { // dr674: 8
namespace dr675 { // dr675: dup 739
template<typename T> struct A { T n : 1; };
#if __cplusplus >= 201103L
static_assert(A<char>{1}.n < 0, ""); // expected-warning {{implicit truncation from 'int' to bit-field changes value from 1 to -1}}
static_assert(A<int>{1}.n < 0, ""); // expected-warning {{implicit truncation from 'int' to bit-field changes value from 1 to -1}}
static_assert(A<long long>{1}.n < 0, ""); // expected-warning {{implicit truncation from 'int' to bit-field changes value from 1 to -1}}
static_assert(A<char>{1}.n < 0, ""); // expected-warning {{implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1}}
static_assert(A<int>{1}.n < 0, ""); // expected-warning {{implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1}}
static_assert(A<long long>{1}.n < 0, ""); // expected-warning {{implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1}}
#endif
}

View File

@ -1,4 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin %s
// RUN: %clang_cc1 -fsyntax-only -ffreestanding -verify=expected,one-bit -triple x86_64-apple-darwin %s
// RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wno-single-bit-bitfield-constant-conversion -verify -triple x86_64-apple-darwin %s
#include <stdbool.h>
// This file tests -Wconstant-conversion, a subcategory of -Wconversion
// which is on by default.
@ -19,12 +22,14 @@ void test(void) {
int b : 1; // The only valid values are 0 and -1.
} s;
s.b = -3; // expected-warning {{implicit truncation from 'int' to bit-field changes value from -3 to -1}}
s.b = -2; // expected-warning {{implicit truncation from 'int' to bit-field changes value from -2 to 0}}
s.b = -1; // no-warning
s.b = 0; // no-warning
s.b = 1; // expected-warning {{implicit truncation from 'int' to bit-field changes value from 1 to -1}}
s.b = 2; // expected-warning {{implicit truncation from 'int' to bit-field changes value from 2 to 0}}
s.b = -3; // expected-warning {{implicit truncation from 'int' to bit-field changes value from -3 to -1}}
s.b = -2; // expected-warning {{implicit truncation from 'int' to bit-field changes value from -2 to 0}}
s.b = -1; // no-warning
s.b = 0; // no-warning
s.b = 1; // one-bit-warning {{implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1}}
s.b = true; // no-warning (we suppress it manually to reduce false positives)
s.b = false; // no-warning
s.b = 2; // expected-warning {{implicit truncation from 'int' to bit-field changes value from 2 to 0}}
}
enum Test2 { K_zero, K_one };

View File

@ -22,3 +22,12 @@ void too_big_for_char(int param) {
char ok3 = true ? 0 : 99999 + 1;
char ok4 = true ? 0 : nines() + 1;
}
void test_bitfield() {
struct S {
int one_bit : 1;
} s;
s.one_bit = 1; // expected-warning {{implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1}}
s.one_bit = true; // no-warning
}