llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Mital Ashok (MitalAshok) <details> <summary>Changes</summary> [CWG2627](https://wg21.link/CWG2627) I've implemented this to apply to C++11 to 20 as well without a warning. Should this be a SFINAE-able error before C++23? (It isn't currently) --- Full diff: https://github.com/llvm/llvm-project/pull/78112.diff 5 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+4) - (modified) clang/lib/Sema/SemaOverload.cpp (+66-41) - (modified) clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp (+24) - (modified) clang/test/CXX/drs/dr26xx.cpp (+46) - (modified) clang/www/cxx_dr_status.html (+1-1) ``````````diff diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index dc8a6fe506bce6..6fd84d9c3364d3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -226,6 +226,10 @@ C++2c Feature Support Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Implemented `CWG2627 <https://wg21.link/CWG2627>`_ which means casts from + a bit-field to an integral type is now not considered narrowing if the + width of the bit-field means that all potential values are in the range + of the target type, even if the type of the bit-field is larger. C Language Changes ------------------ diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 23b9bc0fe2d6e2..b053b9ddfa5f26 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -433,7 +433,11 @@ NarrowingKind StandardConversionSequence::getNarrowingKind( // -- from an integer type or unscoped enumeration type to an integer type // that cannot represent all the values of the original type, except where - // the source is a constant expression and the actual value after + // -- the source is a bit-field whose width w is less than that of its type + // (or, for an enumeration type, its underlying type) and the target type + // can represent all the values of a hypothetical extended integer type + // with width w and with the same signedness as the original type or + // -- the source is a constant expression and the actual value after // conversion will fit into the target type and will produce the original // value when converted back to the original type. case ICK_Integral_Conversion: @@ -441,53 +445,74 @@ NarrowingKind StandardConversionSequence::getNarrowingKind( assert(FromType->isIntegralOrUnscopedEnumerationType()); assert(ToType->isIntegralOrUnscopedEnumerationType()); const bool FromSigned = FromType->isSignedIntegerOrEnumerationType(); - const unsigned FromWidth = Ctx.getIntWidth(FromType); + unsigned FromWidth = Ctx.getIntWidth(FromType); const bool ToSigned = ToType->isSignedIntegerOrEnumerationType(); const unsigned ToWidth = Ctx.getIntWidth(ToType); - if (FromWidth > ToWidth || - (FromWidth == ToWidth && FromSigned != ToSigned) || - (FromSigned && !ToSigned)) { - // Not all values of FromType can be represented in ToType. - const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted); + constexpr auto CanRepresentAll = [](bool FromSigned, unsigned FromWidth, + bool ToSigned, unsigned ToWidth) { + return (FromWidth < ToWidth + (FromSigned == ToSigned)) && + (FromSigned <= ToSigned); + }; - // If it's value-dependent, we can't tell whether it's narrowing. - if (Initializer->isValueDependent()) - return NK_Dependent_Narrowing; + if (CanRepresentAll(FromSigned, FromWidth, ToSigned, ToWidth)) + return NK_Not_Narrowing; - std::optional<llvm::APSInt> OptInitializerValue; - if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) { - // Such conversions on variables are always narrowing. - return NK_Variable_Narrowing; - } - llvm::APSInt &InitializerValue = *OptInitializerValue; - bool Narrowing = false; - if (FromWidth < ToWidth) { - // Negative -> unsigned is narrowing. Otherwise, more bits is never - // narrowing. - if (InitializerValue.isSigned() && InitializerValue.isNegative()) - Narrowing = true; - } else { - // Add a bit to the InitializerValue so we don't have to worry about - // signed vs. unsigned comparisons. - InitializerValue = InitializerValue.extend( - InitializerValue.getBitWidth() + 1); - // Convert the initializer to and from the target width and signed-ness. - llvm::APSInt ConvertedValue = InitializerValue; - ConvertedValue = ConvertedValue.trunc(ToWidth); - ConvertedValue.setIsSigned(ToSigned); - ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth()); - ConvertedValue.setIsSigned(InitializerValue.isSigned()); - // If the result is different, this was a narrowing conversion. - if (ConvertedValue != InitializerValue) - Narrowing = true; - } - if (Narrowing) { - ConstantType = Initializer->getType(); - ConstantValue = APValue(InitializerValue); - return NK_Constant_Narrowing; + // Not all values of FromType can be represented in ToType. + const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted); + + bool DependentBitField = false; + if (auto *BF = Initializer->getSourceBitField()) { + auto *Width = BF->getBitWidth(); + DependentBitField = Width->isValueDependent(); + if (!DependentBitField) { + FromWidth = BF->getBitWidthValue(Ctx); + if (CanRepresentAll(FromSigned, FromWidth, ToSigned, ToWidth)) + return NK_Not_Narrowing; } } + + // If it's value-dependent, we can't tell whether it's narrowing. + if (Initializer->isValueDependent()) + return NK_Dependent_Narrowing; + + std::optional<llvm::APSInt> OptInitializerValue; + if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) { + // Such would be narrowing only if the source width ends up being too + // large + if (DependentBitField) + return NK_Dependent_Narrowing; + // Otherwise it is narrowing + return NK_Variable_Narrowing; + } + llvm::APSInt &InitializerValue = *OptInitializerValue; + bool Narrowing = false; + if (FromWidth < ToWidth) { + // Negative -> unsigned is narrowing. Otherwise, more bits is never + // narrowing. + if (InitializerValue.isSigned() && InitializerValue.isNegative()) + Narrowing = true; + } else { + // Add a bit to the InitializerValue so we don't have to worry about + // signed vs. unsigned comparisons. + InitializerValue = + InitializerValue.extend(InitializerValue.getBitWidth() + 1); + // Convert the initializer to and from the target width and signed-ness. + llvm::APSInt ConvertedValue = InitializerValue; + ConvertedValue = ConvertedValue.trunc(ToWidth); + ConvertedValue.setIsSigned(ToSigned); + ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth()); + ConvertedValue.setIsSigned(InitializerValue.isSigned()); + // If the result is different, this was a narrowing conversion. + if (ConvertedValue != InitializerValue) + Narrowing = true; + } + if (Narrowing) { + ConstantType = Initializer->getType(); + ConstantValue = APValue(InitializerValue); + return NK_Constant_Narrowing; + } + return NK_Not_Narrowing; } diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp index 2bceb3e267790d..a3049dc57d9fac 100644 --- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp +++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp @@ -203,6 +203,30 @@ void shrink_int() { unsigned short usc1 = { c }; // expected-error {{non-constant-expression cannot be narrowed from type 'signed char'}} expected-note {{silence}} unsigned short usc2 = { (signed char)'x' }; // OK unsigned short usc3 = { (signed char)-1 }; // expected-error {{ -1 which cannot be narrowed}} expected-note {{silence}} + +#if __BITINT_MAXWIDTH__ >= 3 + _BitInt(2) S2 = 0; + unsigned _BitInt(2) U2 = 0; + _BitInt(3) S3 = 0; + unsigned _BitInt(3) U3 = 0; + + _BitInt(2) bi0 = { S2 }; + _BitInt(2) bi1 = { U2 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(2)'}} + _BitInt(2) bi2 = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}} + _BitInt(2) bi3 = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}} + unsigned _BitInt(2) bi4 = { S2 }; // expected-error {{cannot be narrowed from type '_BitInt(2)'}} + unsigned _BitInt(2) bi5 = { U2 }; + unsigned _BitInt(2) bi6 = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}} + unsigned _BitInt(2) bi7 = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}} + _BitInt(3) bi8 = { S2 }; + _BitInt(3) bi9 = { U2 }; + _BitInt(3) bia = { S3 }; + _BitInt(3) bib = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}} + unsigned _BitInt(3) bic = { S2 }; // expected-error {{cannot be narrowed from type '_BitInt(2)'}} + unsigned _BitInt(3) bid = { U2 }; + unsigned _BitInt(3) bie = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}} + unsigned _BitInt(3) bif = { U3 }; +#endif } // Be sure that type- and value-dependent expressions in templates get the error diff --git a/clang/test/CXX/drs/dr26xx.cpp b/clang/test/CXX/drs/dr26xx.cpp index f151c9eea051a3..3b2b26c101d61b 100644 --- a/clang/test/CXX/drs/dr26xx.cpp +++ b/clang/test/CXX/drs/dr26xx.cpp @@ -6,6 +6,19 @@ // RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23 // RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23 +namespace std { +#if __cplusplus >= 202002L + struct strong_ordering { + int n; + constexpr operator int() const { return n; } + static const strong_ordering less, equal, greater; + }; + constexpr strong_ordering strong_ordering::less{-1}, + strong_ordering::equal{0}, strong_ordering::greater{1}; +#endif + + typedef __INT64_TYPE__ int64_t; +} namespace dr2621 { // dr2621: 16 #if __cplusplus >= 202002L @@ -24,6 +37,39 @@ using enum E; #endif } +namespace dr2627 { // dr2627: 18 +#if __cplusplus >= 201103L +struct C { + long long i : 8; +}; + +#if __cplusplus >= 202002L +void f() { + C x{1}, y{2}; + static_cast<void>(x.i <=> y.i); +} +#endif + +template<int N> +struct D { + __int128 i : N; +}; + +template<int N> +std::int64_t f(D<N> d) { + return std::int64_t{ d.i }; // #dr2627-f-narrowing +} + +template std::int64_t f(D<63>); +template std::int64_t f(D<64>); +template std::int64_t f(D<65>); +// since-cxx11-error-re@#dr2627-f-narrowing {{non-constant-expression cannot be narrowed from type '__int128' to 'std::int64_t' (aka '{{.+}}') in initializer list}} +// since-cxx11-note@-2 {{in instantiation of function template specialization 'dr2627::f<65>' requested here}} +// since-cxx11-note@#dr2627-f-narrowing {{insert an explicit cast to silence this issue}} + +#endif +} + namespace dr2628 { // dr2628: no // this was reverted for the 16.x release // due to regressions, see the issue for more details: diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 397bf1357d3cb3..caecdfe27a9498 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -15570,7 +15570,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2627.html">2627</a></td> <td>C++23</td> <td>Bit-fields and narrowing conversions</td> - <td class="unknown" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 18</td> </tr> <tr id="2628"> <td><a href="https://cplusplus.github.io/CWG/issues/2628.html">2628</a></td> `````````` </details> https://github.com/llvm/llvm-project/pull/78112 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits