diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6603,6 +6603,9 @@ def warn_shift_result_gt_typewidth : Warning< "signed shift result (%0) requires %1 bits to represent, but %2 only has " "%3 bits">, InGroup>; +def warn_shift_on_bool_type : Warning< + "%select{left|right}0 shifting 'bool' will implicitly cast to 'int'; consider %select{%1|%2}0 " + "to avoid implicitly relying on the width of 'int'">, InGroup>; def warn_shift_result_sets_sign_bit : Warning< "signed shift result (%0) sets the sign bit of the shift expression's " "type (%1) and becomes negative">, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -11950,6 +11950,10 @@ SourceLocation Loc, BinaryOperatorKind Opc, bool IsCompAssign) { checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + if (LHS.get()->IgnoreParenImpCasts()->getType()->isBooleanType()) + Diag(Loc, diag::warn_shift_on_bool_type) << (Opc == BO_Shr) + << "'bool && (shift_count | desired_type_width - 1)'" + << "'bool & !shift_count'"; // Vector shifts promote their scalar inputs to vector type. if (LHS.get()->getType()->isVectorType() || diff --git a/clang/test/AST/Interp/shifts.cpp b/clang/test/AST/Interp/shifts.cpp --- a/clang/test/AST/Interp/shifts.cpp +++ b/clang/test/AST/Interp/shifts.cpp @@ -109,7 +109,13 @@ static_assert(m == 1, ""); constexpr unsigned char c = 0 << 8; static_assert(c == 0, ""); - static_assert(true << 1, ""); + + constexpr unsigned b = true << 1; // expected-warning {{left shifting 'bool' will implicitly cast to 'int'; consider 'bool && (shift_count | desired_type_width - 1)' to avoid implicitly relying on the width of 'int'}} \ + // cxx17-warning {{left shifting 'bool' will implicitly cast to 'int'; consider 'bool && (shift_count | desired_type_width - 1)' to avoid implicitly relying on the width of 'int'}} \ + // ref-warning {{left shifting 'bool' will implicitly cast to 'int'; consider 'bool && (shift_count | desired_type_width - 1)' to avoid implicitly relying on the width of 'int'}} \ + // ref-cxx17-warning {{left shifting 'bool' will implicitly cast to 'int'; consider 'bool && (shift_count | desired_type_width - 1)' to avoid implicitly relying on the width of 'int'}} + static_assert(b, ""); + static_assert(1 << (__INT_WIDTH__ +1) == 0, ""); // expected-error {{not an integral constant expression}} \ // expected-note {{>= width of type 'int'}} \ // cxx17-error {{not an integral constant expression}} \ diff --git a/clang/test/Analysis/svalbuilder-simplify-no-crash.c b/clang/test/Analysis/svalbuilder-simplify-no-crash.c --- a/clang/test/Analysis/svalbuilder-simplify-no-crash.c +++ b/clang/test/Analysis/svalbuilder-simplify-no-crash.c @@ -9,5 +9,6 @@ void crashing(long a, _Bool b) { (void)(a & 1 && 0); b = a & 1; - (void)(b << 1); // expected-warning{{core.UndefinedBinaryOperatorResult}} + (void)(b << 1); // expected-warning{{core.UndefinedBinaryOperatorResult}} \ + // expected-warning {{left shifting 'bool' will implicitly cast to 'int'; consider 'bool && (shift_count | desired_type_width - 1)' to avoid implicitly relying on the width of 'int'}} } diff --git a/clang/test/CXX/over/over.built/p18.cpp b/clang/test/CXX/over/over.built/p18.cpp --- a/clang/test/CXX/over/over.built/p18.cpp +++ b/clang/test/CXX/over/over.built/p18.cpp @@ -56,7 +56,7 @@ (void)(i << 3); (void)(f << 3); // expected-error {{invalid operands}} - (void)(b << 3); + (void)(b << 3); // expected-warning {{left shifting 'bool' will implicitly cast to 'int'; consider 'bool && (shift_count | desired_type_width - 1)' to avoid implicitly relying on the width of 'int'}} (void)(pi << 3); // expected-error {{invalid operands}} (void)(pt << 3); // FIXME (void)(t << 3); @@ -69,7 +69,7 @@ (void)(i >> 3); (void)(f >> 3); // expected-error {{invalid operands}} - (void)(b >> 3); + (void)(b >> 3); // expected-warning {{right shifting 'bool' will implicitly cast to 'int'; consider 'bool & !shift_count' to avoid implicitly relying on the width of 'int'}} (void)(pi >> 3); // expected-error {{invalid operands}} (void)(pt >> 3); // FIXME (void)(t >> 3); diff --git a/clang/test/Sema/warn-shift-bool.cpp b/clang/test/Sema/warn-shift-bool.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/warn-shift-bool.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int f(int x) { + const bool b = 1; + const bool c = b << x; // expected-warning {{left shifting 'bool' will implicitly cast to 'int'; consider 'bool && (shift_count | desired_type_width - 1)' to avoid implicitly relying on the width of 'int'}} + const bool d = b >> x; // expected-warning {{right shifting 'bool' will implicitly cast to 'int'; consider 'bool & !shift_count' to avoid implicitly relying on the width of 'int'}} + return 0; +}