https://github.com/joyeecheung updated https://github.com/llvm/llvm-project/pull/211172
>From 6b34f89458ca4413b5face04569cbb12ca4eb698 Mon Sep 17 00:00:00 2001 From: Joyee Cheung <[email protected]> Date: Wed, 22 Jul 2026 01:04:12 +0200 Subject: [PATCH 1/3] [Clang] Fix conversion from floats to bool-backed enums per CWG1094 After CWG 1094, `[expr.static.cast]p8` has been updated to include: > A value of floating-point type can also be explicitly converted to an > enumeration type. The resulting value is the same as converting the > original value to the underlying type of the enumeration > ([conv.fpint]), and subsequently to the enumeration type. `[conv.fpint]p1` states: > If the destination type is bool, see [conv.bool]. `[conv.bool]` states: > A zero value, null pointer value, or null member pointer value is > converted to false; any other value is converted to true. Previously `TryStaticCast` always used integral conversion for floating-point operands, even if the destination enum type is backed by a bool, so e.g. 0.5 would be incorrectly truncated and then converted to the enum value corresponding to false. This patch fixes it by using `CK_FloatingToBoolean` to handle bool-backed enums and updates the Bytecode compiler accordingly. Also update the spec reference as the relevant paragraph has been moved to p8. --- clang/docs/ReleaseNotes.md | 4 +++ clang/lib/AST/ByteCode/Compiler.cpp | 2 +- clang/lib/Sema/SemaCast.cpp | 24 ++++++++++++------ clang/test/CXX/drs/cwg10xx.cpp | 39 +++++++++++++++++++++++++++++ clang/test/CodeGen/enum-bool.cpp | 31 +++++++++++++++++++++++ clang/www/cxx_dr_status.html | 2 +- 6 files changed, 93 insertions(+), 9 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 80e770a40838a..ea19d46bc6b3a 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -90,6 +90,10 @@ features cannot lower the translation-unit ABI level; #### Resolutions to C++ Defect Reports +- Clang now converts floating-point values to boolean first when converting + them to an enumeration type with a fixed `bool` underlying type. This + resolves [CWG1094](https://wg21.link/cwg1094). + ### C Language Changes #### C2y Feature Support diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 35667a9132680..21df08b950624 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -638,7 +638,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) { if (E->getType()->isVectorType()) return this->emitVectorConversion(E->getSubExpr(), E); if (!SubExpr->getType()->isRealFloatingType() || - !E->getType()->isBooleanType()) + !E->getType()->hasBooleanRepresentation()) return false; if (const auto *FL = dyn_cast<FloatingLiteral>(SubExpr)) return this->emitConstBool(FL->getValue().isNonZero(), E); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 133837623a7a6..8da73df832d77 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1489,16 +1489,26 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, SrcExpr = ExprError(); return TC_Failed; } + // [expr.static.cast]p8 + // If the enumeration type has a fixed underlying type, the value is + // first converted to that type by integral promotion ([conv.prom]) or + // integral conversion ([conv.integral]), if necessary, and then to the + // enumeration type. + const auto *ED = DestType->castAsEnumDecl(); + bool DestIsFixedBoolean = + ED->isFixed() && ED->getIntegerType()->isBooleanType(); if (SrcType->isIntegralOrEnumerationType()) { - // [expr.static.cast]p10 If the enumeration type has a fixed underlying - // type, the value is first converted to that type by integral conversion - const auto *ED = DestType->castAsEnumDecl(); - Kind = ED->isFixed() && ED->getIntegerType()->isBooleanType() - ? CK_IntegralToBoolean - : CK_IntegralCast; + Kind = DestIsFixedBoolean ? CK_IntegralToBoolean : CK_IntegralCast; return TC_Success; } else if (SrcType->isRealFloatingType()) { - Kind = CK_FloatingToIntegral; + // [expr.static.cast]p8 + // A value of floating-point type can also be explicitly converted + // to ... the underlying type of the enumeration ([conv.fpint]), and + // subsequently to the enumeration type. + // + // [conv.fpint] forwards to [conv.bool] for conversion to bool, so + // handle that case with CK_FloatingToBoolean. + Kind = DestIsFixedBoolean ? CK_FloatingToBoolean : CK_FloatingToIntegral; return TC_Success; } } diff --git a/clang/test/CXX/drs/cwg10xx.cpp b/clang/test/CXX/drs/cwg10xx.cpp index 1f190fb5310be..bf9ba9057af9c 100644 --- a/clang/test/CXX/drs/cwg10xx.cpp +++ b/clang/test/CXX/drs/cwg10xx.cpp @@ -6,6 +6,14 @@ // RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,since-cxx11,since-cxx14 // RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,since-cxx11,since-cxx14 +// RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,cxx98 +// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,since-cxx11 +// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,since-cxx11,since-cxx14 +// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,since-cxx11,since-cxx14 +// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,since-cxx11,since-cxx14 +// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,since-cxx11,since-cxx14 +// RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,since-cxx11,since-cxx14 + namespace std { __extension__ typedef __SIZE_TYPE__ size_t; @@ -111,3 +119,34 @@ namespace cwg1070 { // cwg1070: 3.5 C c = {}; #endif } // namespace cwg1070 + +#if __cplusplus >= 201103L +namespace cwg1094 { // cwg1094: 24 +enum class E : bool { Zero, One }; +constexpr E from_double(double d) { return static_cast<E>(d); } + +static_assert(static_cast<E>(0.0) == E::Zero, ""); +static_assert(static_cast<E>(-0.0) == E::Zero, ""); +static_assert(static_cast<E>(0.5) == E::One, ""); +static_assert(static_cast<E>(-0.5) == E::One, ""); +static_assert(static_cast<E>(2.5) == E::One, ""); +static_assert(static_cast<E>(__builtin_nan("")) == E::One, ""); + +static_assert(from_double(0.0) == E::Zero, ""); +static_assert(from_double(-0.0) == E::Zero, ""); +static_assert(from_double(0.5) == E::One, ""); +static_assert(from_double(-0.5) == E::One, ""); +static_assert(from_double(2.5) == E::One, ""); +static_assert(from_double(__builtin_nan("")) == E::One, ""); + +enum class G : unsigned char { Zero, One, Two }; +static_assert(static_cast<G>(0.0) == G::Zero, ""); +static_assert(static_cast<G>(-0.0) == G::Zero, ""); +static_assert(static_cast<G>(0.5) == G::Zero, ""); +static_assert(static_cast<G>(-0.5) == G::Zero, ""); +static_assert(static_cast<G>(2.5) == G::Two, ""); +static_assert(static_cast<G>(__builtin_nan("")) == G::Zero, ""); +// since-cxx11-error@-1 {{static assertion expression is not an integral constant expression}} +// since-cxx11-note@-2 {{value NaN is outside the range of representable values of type 'G'}} +} // namespace cwg1094 +#endif diff --git a/clang/test/CodeGen/enum-bool.cpp b/clang/test/CodeGen/enum-bool.cpp index 4bf3b91361d28..8336374a1d14a 100644 --- a/clang/test/CodeGen/enum-bool.cpp +++ b/clang/test/CodeGen/enum-bool.cpp @@ -22,6 +22,21 @@ E b(int x) { return (E)x; } // CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381B1bEi // CHECK: ret i1 %tobool +E c(float x) { return static_cast<E>(x); } +// CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381B1cEf +// CHECK: ret i1 %tobool + +E d(float x) { return (E)x; } +// CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381B1dEf +// CHECK: ret i1 %tobool + +E e(double x) { return static_cast<E>(x); } +// CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381B1eEd +// CHECK: ret i1 %tobool + +E f(double x) { return (E)x; } +// CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381B1fEd +// CHECK: ret i1 %tobool } // namespace B namespace C { enum class E { Zero, One }; @@ -45,5 +60,21 @@ E b(int x) { return (E)x; } // CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381D1bEi // CHECK: ret i1 %tobool +E c(float x) { return static_cast<E>(x); } +// CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381D1cEf +// CHECK: ret i1 %tobool + +E d(float x) { return (E)x; } +// CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381D1dEf +// CHECK: ret i1 %tobool + +E e(double x) { return static_cast<E>(x); } +// CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381D1eEd +// CHECK: ret i1 %tobool + +E f(double x) { return (E)x; } +// CHECK-LABEL: define{{.*}} zeroext i1 @_ZN6dr23381D1fEd +// CHECK: ret i1 %tobool + } // namespace D } // namespace dr2338 diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index af91ac559d274..67445eea9a752 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -7435,7 +7435,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td>[<a href="https://wg21.link/expr.static.cast">expr.static.cast</a>]</td> <td>C++11</td> <td>Converting floating-point values to scoped enumeration types</td> - <td class="unknown" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 24</td> </tr> <tr id="1095"> <td><a href="https://cplusplus.github.io/CWG/issues/1095.html">1095</a></td> >From cef4e6b806634a4fdfc0f568762a8c04d43c9b6f Mon Sep 17 00:00:00 2001 From: Joyee Cheung <[email protected]> Date: Wed, 22 Jul 2026 11:21:04 +0200 Subject: [PATCH 2/3] Add C++26 to spec anchors Co-authored-by: Corentin Jabot <[email protected]> --- clang/lib/Sema/SemaCast.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 8da73df832d77..7bdc59256e27a 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1489,7 +1489,7 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, SrcExpr = ExprError(); return TC_Failed; } - // [expr.static.cast]p8 + // C++26 [expr.static.cast]p8 // If the enumeration type has a fixed underlying type, the value is // first converted to that type by integral promotion ([conv.prom]) or // integral conversion ([conv.integral]), if necessary, and then to the @@ -1501,7 +1501,7 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, Kind = DestIsFixedBoolean ? CK_IntegralToBoolean : CK_IntegralCast; return TC_Success; } else if (SrcType->isRealFloatingType()) { - // [expr.static.cast]p8 + //C++26 [expr.static.cast]p8 // A value of floating-point type can also be explicitly converted // to ... the underlying type of the enumeration ([conv.fpint]), and // subsequently to the enumeration type. >From 94e4c870cafd789f36c9269b9bb51b528e45d7b1 Mon Sep 17 00:00:00 2001 From: Joyee Cheung <[email protected]> Date: Wed, 22 Jul 2026 11:22:43 +0200 Subject: [PATCH 3/3] Remove comment --- clang/lib/Sema/SemaCast.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 7bdc59256e27a..6ec3e89532681 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1505,9 +1505,6 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, // A value of floating-point type can also be explicitly converted // to ... the underlying type of the enumeration ([conv.fpint]), and // subsequently to the enumeration type. - // - // [conv.fpint] forwards to [conv.bool] for conversion to bool, so - // handle that case with CK_FloatingToBoolean. Kind = DestIsFixedBoolean ? CK_FloatingToBoolean : CK_FloatingToIntegral; return TC_Success; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
