Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -34,6 +34,7 @@ def GNUBinaryLiteral : DiagGroup<"gnu-binary-literal">; def GNUCompoundLiteralInitializer : DiagGroup<"gnu-compound-literal-initializer">; def BitFieldConstantConversion : DiagGroup<"bitfield-constant-conversion">; +def BitFieldEnumConversion : DiagGroup<"bitfield-enum-conversion">; def BitFieldWidth : DiagGroup<"bitfield-width">; def ConstantConversion : DiagGroup<"constant-conversion", [ BitFieldConstantConversion ] >; @@ -606,6 +607,7 @@ [BoolConversion, ConstantConversion, EnumConversion, + BitFieldEnumConversion, FloatConversion, Shorten64To32, IntConversion, Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -4908,6 +4908,17 @@ def warn_anon_bitfield_width_exceeds_type_width : Warning< "width of anonymous bit-field (%0 bits) exceeds width of its type; value " "will be truncated to %1 bit%s1">, InGroup; +def warn_bitfield_too_small_for_enum: Warning< + "bit-field %0 is not wide enough to store all enumerators of enum type %1">, + InGroup; +def warn_unsigned_bitfield_assigned_signed_enum: Warning< + "assigning value of signed enum type %1 to unsigned bit-field %0; " + "negative enumerators of enum %1 will be converted to positive values">, + InGroup; +def warn_signed_bitfield_enum_conversion: Warning< + "signed bit-field %0 needs an extra bit to represent the largest positive " + "values of enum type %1">, + InGroup; def warn_missing_braces : Warning< "suggest braces around initialization of subobject">, Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -8729,13 +8729,42 @@ return false; Expr *OriginalInit = Init->IgnoreParenImpCasts(); + unsigned FieldWidth = Bitfield->getBitWidthValue(S.Context); llvm::APSInt Value; - if (!OriginalInit->EvaluateAsInt(Value, S.Context, Expr::SE_AllowSideEffects)) + if (!OriginalInit->EvaluateAsInt(Value, S.Context, + Expr::SE_AllowSideEffects)) { + // The RHS is not constant. If the RHS has an enum type, make sure the + // bitfield is wide enough to hold all the values of the enum without + // truncation. + if (const auto *EnumTy = OriginalInit->getType()->getAs()) { + EnumDecl *ED = EnumTy->getDecl(); + bool SignedBitfield = BitfieldType->isSignedIntegerType(); + + // Check if this might store negative values into an unsigned bitfield. + if (!SignedBitfield && ED->getNumNegativeBits() > 0) { + S.Diag(InitLoc, diag::warn_unsigned_bitfield_assigned_signed_enum) + << Bitfield << ED; + } + + // Check for sufficient width. + if (ED->getNumPositiveBits() > FieldWidth || + ED->getNumNegativeBits() > FieldWidth) { + S.Diag(InitLoc, diag::warn_bitfield_too_small_for_enum) + << Bitfield << ED; + } else if (SignedBitfield && ED->getNumPositiveBits() == FieldWidth) { + // If the bitfield type is signed and the positive field widths are + // equal, values with the high bit set will become negative, which is + // usually unintended. + S.Diag(InitLoc, diag::warn_signed_bitfield_enum_conversion) + << Bitfield << ED; + } + } + return false; + } unsigned OriginalWidth = Value.getBitWidth(); - unsigned FieldWidth = Bitfield->getBitWidthValue(S.Context); if (!Value.isSigned() || Value.isNegative()) if (UnaryOperator *UO = dyn_cast(OriginalInit)) Index: test/Sema/warn-bitfield-enum-conversion.c =================================================================== --- /dev/null +++ test/Sema/warn-bitfield-enum-conversion.c @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple x86_64-windows-msvc -verify %s -Wbitfield-enum-conversion + +enum TwoBits { Hi1 = 3 } two_bits; +enum TwoBitsSigned { Lo2 = -2, Hi2 = 1 } two_bits_signed; +enum ThreeBits { Hi3 = 7 } three_bits; +enum ThreeBitsSigned { Lo4 = -4, Hi4 = 3 } three_bits_signed; + +struct Foo { + unsigned two_bits : 2; + int two_bits_signed : 2; + unsigned three_bits : 3; + int three_bits_signed : 3; + + enum ThreeBits three_bits_enum : 3; +}; + +void f() { + struct Foo f; + + f.two_bits = two_bits; + f.two_bits = two_bits_signed; // expected-warning {{negative enumerators}} + f.two_bits = three_bits; // expected-warning {{not wide enough}} + f.two_bits = three_bits_signed; // expected-warning {{negative enumerators}} expected-warning {{not wide enough}} + + f.two_bits_signed = two_bits; // expected-warning {{needs an extra bit}} + f.two_bits_signed = two_bits_signed; + f.two_bits_signed = three_bits; // expected-warning {{not wide enough}} + f.two_bits_signed = three_bits_signed; // expected-warning {{not wide enough}} + + f.three_bits = two_bits; + f.three_bits = two_bits_signed; // expected-warning {{negative enumerators}} + f.three_bits = three_bits; + f.three_bits = three_bits_signed; // expected-warning {{negative enumerators}} + + f.three_bits_signed = two_bits; + f.three_bits_signed = two_bits_signed; + f.three_bits_signed = three_bits; // expected-warning {{needs an extra bit}} + f.three_bits_signed = three_bits_signed; + + f.three_bits_enum = three_bits; // expected-warning {{needs an extra bit}} +}