On Fri, Sep 8, 2017 at 4:48 AM, Sam McCall via cfe-commits <cfe-commits@lists.llvm.org> wrote: > Nice fix! It catches a lot of new cases on our codebase, all technically > correct so far. > > A couple of issues though: > A) Rollout - until we've completely cleaned up, we need to disable > -Wtautological-compare entirely, which is a valuable check. I imagine anyone > else using -Werror is in the same boat. > What do you think about putting the new warnings behind a subcategory? (e.g. > -Wtautological-compare-unsigned-zero, implied by -Wtautological-compare) > It's an ugly artifact of the history here, but allows this fix to be rolled > out in a controlled way. > > B) Enums (not new I guess). Typical case: if (enum < 0 || enum > MAX) > The warning strongly suggests that the enum < 0 check has no effect (for > enums with nonnegative ranges). > Clang doesn't seem to optimize such checks out though, and they seem likely > to catch bugs in some cases. Yes, only if there's UB elsewhere, but I assume > not optimizing out these checks indicates a deliberate decision to stay > somewhat compatible with a technically-incorrect mental model. > If this is the case, should we move these to a -Wtautological-compare-enum > subcategory?
Just another data point: we're seeing this a lot in Chromium, e.g. enum MediaDeviceType { MEDIA_DEVICE_TYPE_AUDIO_INPUT, MEDIA_DEVICE_TYPE_VIDEO_INPUT, MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, NUM_MEDIA_DEVICE_TYPES, }; inline bool IsValidMediaDeviceType(MediaDeviceType type) { return type >= 0 && type < NUM_MEDIA_DEVICE_TYPES; } ../../content/common/media/media_devices.h:53:15: warning: comparison of unsigned enum expression >= 0 is always true [-Wtautological-unsigned-zero-compare] return type >= 0 && type < NUM_MEDIA_DEVICE_TYPES; ~~~~ ^ ~ The problem is that the signedness of the enum varies per platform. For example, when targeting Windows the underlying type is 'int' by default, and then the comparison isn't tautological. > On Fri, Sep 8, 2017 at 12:14 AM, Roman Lebedev via cfe-commits > <cfe-commits@lists.llvm.org> wrote: >> >> Author: lebedevri >> Date: Thu Sep 7 15:14:25 2017 >> New Revision: 312750 >> >> URL: http://llvm.org/viewvc/llvm-project?rev=312750&view=rev >> Log: >> [Sema] -Wtautological-compare: handle comparison of unsigned with 0S. >> >> Summary: >> This is a first half(?) of a fix for the following bug: >> https://bugs.llvm.org/show_bug.cgi?id=34147 (gcc -Wtype-limits) >> >> GCC's -Wtype-limits does warn on comparison of unsigned value >> with signed zero (as in, with 0), but clang only warns if the >> zero is unsigned (i.e. 0U). >> >> Also, be careful not to double-warn, or falsely warn on >> comparison of signed/fp variable and signed 0. >> >> Yes, all these testcases are needed. >> >> Testing: $ ninja check-clang-sema check-clang-semacxx >> Also, no new warnings for clang stage-2 build. >> >> Reviewers: rjmccall, rsmith, aaron.ballman >> >> Reviewed By: rjmccall >> >> Subscribers: cfe-commits >> >> Tags: #clang >> >> Differential Revision: https://reviews.llvm.org/D37565 >> >> Modified: >> cfe/trunk/docs/ReleaseNotes.rst >> cfe/trunk/lib/Sema/SemaChecking.cpp >> cfe/trunk/test/Sema/compare.c >> cfe/trunk/test/Sema/outof-range-constant-compare.c >> cfe/trunk/test/SemaCXX/compare.cpp >> >> Modified: cfe/trunk/docs/ReleaseNotes.rst >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ReleaseNotes.rst?rev=312750&r1=312749&r2=312750&view=diff >> >> ============================================================================== >> --- cfe/trunk/docs/ReleaseNotes.rst (original) >> +++ cfe/trunk/docs/ReleaseNotes.rst Thu Sep 7 15:14:25 2017 >> @@ -71,6 +71,13 @@ Improvements to Clang's diagnostics >> errors/warnings, as the system frameworks might add a method with the >> same >> selector which could make the message send to ``id`` ambiguous. >> >> +- ``-Wtautological-compare`` now warns when comparing an unsigned integer >> and 0 >> + regardless of whether the constant is signed or unsigned." >> + >> +- ``-Wtautological-compare`` now warns about comparing a signed integer >> and 0 >> + when the signed integer is coerced to an unsigned type for the >> comparison. >> + ``-Wsign-compare`` was adjusted not to warn in this case. >> + >> Non-comprehensive list of changes in this release >> ------------------------------------------------- >> >> >> Modified: cfe/trunk/lib/Sema/SemaChecking.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=312750&r1=312749&r2=312750&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Sema/SemaChecking.cpp (original) >> +++ cfe/trunk/lib/Sema/SemaChecking.cpp Thu Sep 7 15:14:25 2017 >> @@ -8567,32 +8567,51 @@ bool HasEnumType(Expr *E) { >> return E->getType()->isEnumeralType(); >> } >> >> -void CheckTrivialUnsignedComparison(Sema &S, BinaryOperator *E) { >> +bool isNonBooleanUnsignedValue(Expr *E) { >> + // We are checking that the expression is not known to have boolean >> value, >> + // is an integer type; and is either unsigned after implicit casts, >> + // or was unsigned before implicit casts. >> + return !E->isKnownToHaveBooleanValue() && E->getType()->isIntegerType() >> && >> + (!E->getType()->isSignedIntegerType() || >> + !E->IgnoreParenImpCasts()->getType()->isSignedIntegerType()); >> +} >> + >> +bool CheckTautologicalComparisonWithZero(Sema &S, BinaryOperator *E) { >> // Disable warning in template instantiations. >> if (S.inTemplateInstantiation()) >> - return; >> + return false; >> + >> + // bool values are handled by DiagnoseOutOfRangeComparison(). >> >> BinaryOperatorKind op = E->getOpcode(); >> if (E->isValueDependent()) >> - return; >> + return false; >> >> - if (op == BO_LT && IsZero(S, E->getRHS())) { >> + Expr *LHS = E->getLHS(); >> + Expr *RHS = E->getRHS(); >> + >> + bool Match = true; >> + >> + if (op == BO_LT && isNonBooleanUnsignedValue(LHS) && IsZero(S, RHS)) { >> S.Diag(E->getOperatorLoc(), >> diag::warn_lunsigned_always_true_comparison) >> - << "< 0" << "false" << HasEnumType(E->getLHS()) >> - << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); >> - } else if (op == BO_GE && IsZero(S, E->getRHS())) { >> + << "< 0" << "false" << HasEnumType(LHS) >> + << LHS->getSourceRange() << RHS->getSourceRange(); >> + } else if (op == BO_GE && isNonBooleanUnsignedValue(LHS) && IsZero(S, >> RHS)) { >> S.Diag(E->getOperatorLoc(), >> diag::warn_lunsigned_always_true_comparison) >> - << ">= 0" << "true" << HasEnumType(E->getLHS()) >> - << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); >> - } else if (op == BO_GT && IsZero(S, E->getLHS())) { >> + << ">= 0" << "true" << HasEnumType(LHS) >> + << LHS->getSourceRange() << RHS->getSourceRange(); >> + } else if (op == BO_GT && isNonBooleanUnsignedValue(RHS) && IsZero(S, >> LHS)) { >> S.Diag(E->getOperatorLoc(), >> diag::warn_runsigned_always_true_comparison) >> - << "0 >" << "false" << HasEnumType(E->getRHS()) >> - << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); >> - } else if (op == BO_LE && IsZero(S, E->getLHS())) { >> + << "0 >" << "false" << HasEnumType(RHS) >> + << LHS->getSourceRange() << RHS->getSourceRange(); >> + } else if (op == BO_LE && isNonBooleanUnsignedValue(RHS) && IsZero(S, >> LHS)) { >> S.Diag(E->getOperatorLoc(), >> diag::warn_runsigned_always_true_comparison) >> - << "0 <=" << "true" << HasEnumType(E->getRHS()) >> - << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); >> - } >> + << "0 <=" << "true" << HasEnumType(RHS) >> + << LHS->getSourceRange() << RHS->getSourceRange(); >> + } else >> + Match = false; >> + >> + return Match; >> } >> >> void DiagnoseOutOfRangeComparison(Sema &S, BinaryOperator *E, Expr >> *Constant, >> @@ -8612,7 +8631,7 @@ void DiagnoseOutOfRangeComparison(Sema & >> >> bool OtherIsBooleanType = Other->isKnownToHaveBooleanValue(); >> >> - // 0 values are handled later by CheckTrivialUnsignedComparison(). >> + // 0 values are handled later by CheckTautologicalComparisonWithZero(). >> if ((Value == 0) && (!OtherIsBooleanType)) >> return; >> >> @@ -8849,16 +8868,22 @@ void AnalyzeComparison(Sema &S, BinaryOp >> (IsRHSIntegralLiteral && IsLHSIntegralLiteral); >> } else if (!T->hasUnsignedIntegerRepresentation()) >> IsComparisonConstant = E->isIntegerConstantExpr(S.Context); >> - >> + >> + // We don't care about value-dependent expressions or expressions >> + // whose result is a constant. >> + if (IsComparisonConstant) >> + return AnalyzeImpConvsInComparison(S, E); >> + >> + // If this is a tautological comparison, suppress -Wsign-compare. >> + if (CheckTautologicalComparisonWithZero(S, E)) >> + return AnalyzeImpConvsInComparison(S, E); >> + >> // We don't do anything special if this isn't an unsigned integral >> // comparison: we're only interested in integral comparisons, and >> // signed comparisons only happen in cases we don't care to warn about. >> - // >> - // We also don't care about value-dependent expressions or expressions >> - // whose result is a constant. >> - if (!T->hasUnsignedIntegerRepresentation() || IsComparisonConstant) >> + if (!T->hasUnsignedIntegerRepresentation()) >> return AnalyzeImpConvsInComparison(S, E); >> - >> + >> // Check to see if one of the (unmodified) operands is of different >> // signedness. >> Expr *signedOperand, *unsignedOperand; >> @@ -8871,7 +8896,6 @@ void AnalyzeComparison(Sema &S, BinaryOp >> signedOperand = RHS; >> unsignedOperand = LHS; >> } else { >> - CheckTrivialUnsignedComparison(S, E); >> return AnalyzeImpConvsInComparison(S, E); >> } >> >> @@ -8883,11 +8907,9 @@ void AnalyzeComparison(Sema &S, BinaryOp >> AnalyzeImplicitConversions(S, LHS, E->getOperatorLoc()); >> AnalyzeImplicitConversions(S, RHS, E->getOperatorLoc()); >> >> - // If the signed range is non-negative, -Wsign-compare won't fire, >> - // but we should still check for comparisons which are always true >> - // or false. >> + // If the signed range is non-negative, -Wsign-compare won't fire. >> if (signedRange.NonNegative) >> - return CheckTrivialUnsignedComparison(S, E); >> + return; >> >> // For (in)equality comparisons, if the unsigned operand is a >> // constant which cannot collide with a overflowed signed operand, >> >> Modified: cfe/trunk/test/Sema/compare.c >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/compare.c?rev=312750&r1=312749&r2=312750&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/Sema/compare.c (original) >> +++ cfe/trunk/test/Sema/compare.c Thu Sep 7 15:14:25 2017 >> @@ -77,7 +77,7 @@ int ints(long a, unsigned long b) { >> ((int) a == (unsigned int) B) + >> ((short) a == (unsigned short) B) + >> ((signed char) a == (unsigned char) B) + >> - (a < (unsigned long) B) + // expected-warning {{comparison of >> integers of different signs}} >> + (a < (unsigned long) B) + // expected-warning {{comparison of >> unsigned expression < 0 is always false}} >> (a < (unsigned int) B) + >> (a < (unsigned short) B) + >> (a < (unsigned char) B) + >> @@ -85,8 +85,8 @@ int ints(long a, unsigned long b) { >> ((int) a < B) + >> ((short) a < B) + >> ((signed char) a < B) + >> - ((long) a < (unsigned long) B) + // expected-warning >> {{comparison of integers of different signs}} >> - ((int) a < (unsigned int) B) + // expected-warning {{comparison >> of integers of different signs}} >> + ((long) a < (unsigned long) B) + // expected-warning >> {{comparison of unsigned expression < 0 is always false}} >> + ((int) a < (unsigned int) B) + // expected-warning {{comparison >> of unsigned expression < 0 is always false}} >> ((short) a < (unsigned short) B) + >> ((signed char) a < (unsigned char) B) + >> >> >> Modified: cfe/trunk/test/Sema/outof-range-constant-compare.c >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/outof-range-constant-compare.c?rev=312750&r1=312749&r2=312750&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/Sema/outof-range-constant-compare.c (original) >> +++ cfe/trunk/test/Sema/outof-range-constant-compare.c Thu Sep 7 15:14:25 >> 2017 >> @@ -6,6 +6,59 @@ int value(void); >> int main() >> { >> int a = value(); >> + >> + if (a == 0x0000000000000000L) >> + return 0; >> + if (a != 0x0000000000000000L) >> + return 0; >> + if (a < 0x0000000000000000L) >> + return 0; >> + if (a <= 0x0000000000000000L) >> + return 0; >> + if (a > 0x0000000000000000L) >> + return 0; >> + if (a >= 0x0000000000000000L) >> + return 0; >> + >> + if (0x0000000000000000L == a) >> + return 0; >> + if (0x0000000000000000L != a) >> + return 0; >> + if (0x0000000000000000L < a) >> + return 0; >> + if (0x0000000000000000L <= a) >> + return 0; >> + if (0x0000000000000000L > a) >> + return 0; >> + if (0x0000000000000000L >= a) >> + return 0; >> + >> + if (a == 0x0000000000000000UL) >> + return 0; >> + if (a != 0x0000000000000000UL) >> + return 0; >> + if (a < 0x0000000000000000UL) // expected-warning {{comparison of >> unsigned expression < 0 is always false}} >> + return 0; >> + if (a <= 0x0000000000000000UL) >> + return 0; >> + if (a > 0x0000000000000000UL) >> + return 0; >> + if (a >= 0x0000000000000000UL) // expected-warning {{comparison of >> unsigned expression >= 0 is always true}} >> + return 0; >> + >> + if (0x0000000000000000UL == a) >> + return 0; >> + if (0x0000000000000000UL != a) >> + return 0; >> + if (0x0000000000000000UL < a) >> + return 0; >> + if (0x0000000000000000UL <= a) // expected-warning {{comparison of 0 >> <= unsigned expression is always true}} >> + return 0; >> + if (0x0000000000000000UL > a) // expected-warning {{comparison of 0 > >> unsigned expression is always false}} >> + return 0; >> + if (0x0000000000000000UL >= a) >> + return 0; >> + >> if (a == 0x1234567812345678L) // expected-warning {{comparison of >> constant 1311768465173141112 with expression of type 'int' is always false}} >> return 0; >> if (a != 0x1234567812345678L) // expected-warning {{comparison of >> constant 1311768465173141112 with expression of type 'int' is always true}} >> @@ -74,6 +127,7 @@ int main() >> return 0; >> if (0x1234567812345678L >= s) // expected-warning {{comparison of >> constant 1311768465173141112 with expression of type 'short' is always >> true}} >> return 0; >> + >> long l = value(); >> if (l == 0x1234567812345678L) >> return 0; >> @@ -106,13 +160,13 @@ int main() >> return 0; >> if (un != 0x0000000000000000L) >> return 0; >> - if (un < 0x0000000000000000L) >> + if (un < 0x0000000000000000L) // expected-warning {{comparison of >> unsigned expression < 0 is always false}} >> return 0; >> if (un <= 0x0000000000000000L) >> return 0; >> if (un > 0x0000000000000000L) >> return 0; >> - if (un >= 0x0000000000000000L) >> + if (un >= 0x0000000000000000L) // expected-warning {{comparison of >> unsigned expression >= 0 is always true}} >> return 0; >> >> if (0x0000000000000000L == un) >> @@ -121,19 +175,92 @@ int main() >> return 0; >> if (0x0000000000000000L < un) >> return 0; >> - if (0x0000000000000000L <= un) >> + if (0x0000000000000000L <= un) // expected-warning {{comparison of 0 >> <= unsigned expression is always true}} >> return 0; >> - if (0x0000000000000000L > un) >> + if (0x0000000000000000L > un) // expected-warning {{comparison of 0 > >> unsigned expression is always false}} >> return 0; >> if (0x0000000000000000L >= un) >> return 0; >> + >> + if (un == 0x0000000000000000UL) >> + return 0; >> + if (un != 0x0000000000000000UL) >> + return 0; >> + if (un < 0x0000000000000000UL) // expected-warning {{comparison of >> unsigned expression < 0 is always false}} >> + return 0; >> + if (un <= 0x0000000000000000UL) >> + return 0; >> + if (un > 0x0000000000000000UL) >> + return 0; >> + if (un >= 0x0000000000000000UL) // expected-warning {{comparison of >> unsigned expression >= 0 is always true}} >> + return 0; >> + >> + if (0x0000000000000000UL == un) >> + return 0; >> + if (0x0000000000000000UL != un) >> + return 0; >> + if (0x0000000000000000UL < un) >> + return 0; >> + if (0x0000000000000000UL <= un) // expected-warning {{comparison of 0 >> <= unsigned expression is always true}} >> + return 0; >> + if (0x0000000000000000UL > un) // expected-warning {{comparison of 0 >> > unsigned expression is always false}} >> + return 0; >> + if (0x0000000000000000UL >= un) >> + return 0; >> + >> float fl = 0; >> - if (fl == 0x0000000000000000L) // no warning >> - return 0; >> + if (fl == 0x0000000000000000L) >> + return 0; >> + if (fl != 0x0000000000000000L) >> + return 0; >> + if (fl < 0x0000000000000000L) >> + return 0; >> + if (fl <= 0x0000000000000000L) >> + return 0; >> + if (fl > 0x0000000000000000L) >> + return 0; >> + if (fl >= 0x0000000000000000L) >> + return 0; >> >> - float dl = 0; >> - if (dl == 0x0000000000000000L) // no warning >> - return 0; >> + if (0x0000000000000000L == fl) >> + return 0; >> + if (0x0000000000000000L != fl) >> + return 0; >> + if (0x0000000000000000L < fl) >> + return 0; >> + if (0x0000000000000000L <= fl) >> + return 0; >> + if (0x0000000000000000L > fl) >> + return 0; >> + if (0x0000000000000000L >= fl) >> + return 0; >> + >> + double dl = 0; >> + if (dl == 0x0000000000000000L) >> + return 0; >> + if (dl != 0x0000000000000000L) >> + return 0; >> + if (dl < 0x0000000000000000L) >> + return 0; >> + if (dl <= 0x0000000000000000L) >> + return 0; >> + if (dl > 0x0000000000000000L) >> + return 0; >> + if (dl >= 0x0000000000000000L) >> + return 0; >> + >> + if (0x0000000000000000L == dl) >> + return 0; >> + if (0x0000000000000000L != dl) >> + return 0; >> + if (0x0000000000000000L < dl) >> + return 0; >> + if (0x0000000000000000L <= dl) >> + return 0; >> + if (0x0000000000000000L > dl) >> + return 0; >> + if (0x0000000000000000L >= dl) >> + return 0; >> >> enum E { >> yes, >> >> Modified: cfe/trunk/test/SemaCXX/compare.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/compare.cpp?rev=312750&r1=312749&r2=312750&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/SemaCXX/compare.cpp (original) >> +++ cfe/trunk/test/SemaCXX/compare.cpp Thu Sep 7 15:14:25 2017 >> @@ -73,7 +73,7 @@ int test0(long a, unsigned long b) { >> ((int) a == (unsigned int) B) + >> ((short) a == (unsigned short) B) + >> ((signed char) a == (unsigned char) B) + >> - (a < (unsigned long) B) + // expected-warning {{comparison of >> integers of different signs}} >> + (a < (unsigned long) B) + // expected-warning {{comparison of >> unsigned expression < 0 is always false}} >> (a < (unsigned int) B) + >> (a < (unsigned short) B) + >> (a < (unsigned char) B) + >> @@ -81,8 +81,8 @@ int test0(long a, unsigned long b) { >> ((int) a < B) + >> ((short) a < B) + >> ((signed char) a < B) + >> - ((long) a < (unsigned long) B) + // expected-warning >> {{comparison of integers of different signs}} >> - ((int) a < (unsigned int) B) + // expected-warning {{comparison >> of integers of different signs}} >> + ((long) a < (unsigned long) B) + // expected-warning >> {{comparison of unsigned expression < 0 is always false}} >> + ((int) a < (unsigned int) B) + // expected-warning {{comparison >> of unsigned expression < 0 is always false}} >> ((short) a < (unsigned short) B) + >> ((signed char) a < (unsigned char) B) + >> >> >> >> _______________________________________________ >> cfe-commits mailing list >> cfe-commits@lists.llvm.org >> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits > > > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits > _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits