llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Mital Ashok (MitalAshok) <details> <summary>Changes</summary> https://cplusplus.github.io/CWG/issues/2851.html The only time the target type for a converted constant expression is a floating-point type is in a NTTP, so this only affects C++20+. --- Full diff: https://github.com/llvm/llvm-project/pull/90387.diff 4 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+3) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4) - (modified) clang/lib/Sema/SemaOverload.cpp (+35-3) - (modified) clang/test/CXX/drs/dr28xx.cpp (+35) ``````````diff diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a1390d6536b28c..7c8d83bd73613b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -164,6 +164,9 @@ Resolutions to C++ Defect Reports - Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers. (`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers <https://cplusplus.github.io/CWG/issues/2858.html>`_). +- Allow floating-point promotions and conversions in converted constant expressions. + (`CWG2851 Allow floating-point conversions in converted constant expressions <https://cplusplus.github.io/CWG/issues/2851.html>`_). + C Language Changes ------------------ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index fdca82934cb4dc..08ebb28ff17b25 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -85,6 +85,10 @@ def err_expr_not_cce : Error< "%select{case value|enumerator value|non-type template argument|" "array size|explicit specifier argument|noexcept specifier argument|" "call to 'size()'|call to 'data()'}0 is not a constant expression">; +def err_float_conv_cant_represent : Error< + "non-type template argument evaluates to %0 which can not be " + "represented in type %1" +>; def ext_cce_narrowing : ExtWarn< "%select{case value|enumerator value|non-type template argument|" "array size|explicit specifier argument|noexcept specifier argument|" diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 04cd9e78739d20..c35517db946b1c 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6072,6 +6072,10 @@ static bool CheckConvertedConstantConversions(Sema &S, case ICK_Integral_Promotion: case ICK_Integral_Conversion: // Narrowing conversions are checked elsewhere. case ICK_Zero_Queue_Conversion: + // Per CWG2851, floating-point promotions and conversions are allowed. + // The value of a conversion is checked afterwards. + case ICK_Floating_Promotion: + case ICK_Floating_Conversion: return true; case ICK_Boolean_Conversion: @@ -6091,9 +6095,7 @@ static bool CheckConvertedConstantConversions(Sema &S, // only permitted if the source type is std::nullptr_t. return SCS.getFromType()->isNullPtrType(); - case ICK_Floating_Promotion: case ICK_Complex_Promotion: - case ICK_Floating_Conversion: case ICK_Complex_Conversion: case ICK_Floating_Integral: case ICK_Compatible_Conversion: @@ -6229,7 +6231,37 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From, if (Result.isInvalid()) return Result; - // Check for a narrowing implicit conversion. + if (SCS->Second == ICK_Floating_Conversion) { + // Unlike with narrowing conversions, the value must fit + // exactly even if it is in range + assert(CCE == Sema::CCEKind::CCEK_TemplateArg && + "Only non-type template args should use floating-point conversions"); + + // Initializer is From, except it is a full-expression + const Expr *Initializer = + IgnoreNarrowingConversion(S.Context, Result.get()); + + // If it's value-dependent, we can't tell whether it will fit + if (Initializer->isValueDependent()) + return Result; + + if (!Initializer->isCXX11ConstantExpr(S.Context, &PreNarrowingValue)) { + S.Diag(Initializer->getBeginLoc(), diag::err_expr_not_cce) << CCE; + return Result; + } + + llvm::APFloat PostNarrowingValue = PreNarrowingValue.getFloat(); + bool LosesInfo = true; + PostNarrowingValue.convert(S.Context.getFloatTypeSemantics(T), + llvm::APFloat::rmNearestTiesToEven, &LosesInfo); + if (LosesInfo) + S.Diag(From->getBeginLoc(), diag::err_float_conv_cant_represent) + << PreNarrowingValue.getAsString(S.Context, From->getType()) << T; + + return Result; + } + + // Check for a narrowing integer conversion. bool ReturnPreNarrowingValue = false; QualType PreNarrowingType; switch (SCS->getNarrowingKind(S.Context, Result.get(), PreNarrowingValue, diff --git a/clang/test/CXX/drs/dr28xx.cpp b/clang/test/CXX/drs/dr28xx.cpp index 4d9b0c76758d53..cf9676ddb73385 100644 --- a/clang/test/CXX/drs/dr28xx.cpp +++ b/clang/test/CXX/drs/dr28xx.cpp @@ -59,6 +59,41 @@ void B<int>::g() requires true; } // namespace cwg2847 +namespace cwg2851 { // cwg2851: 19 + +#if __cplusplus >= 202002L +template<typename T, T v> struct Val { static constexpr T value = v; }; + +// Floating-point promotions +static_assert(Val<long double, 0.0>::value == 0.0L); +static_assert(Val<long double, 0.0f>::value == 0.0L); +static_assert(Val<double, 0.0f>::value == 0.0); +static_assert(Val<long double, -0.0>::value == -0.0L); +static_assert(!__is_same(Val<long double, -0.0>, Val<long double, 0.0L>)); +static_assert(__is_same(Val<long double, 0.5>, Val<long double, 0.5L>)); +static_assert(__is_same(Val<long double, __builtin_nan("")>, Val<long double, __builtin_nanl("")>)); +static_assert(__is_same(Val<long double, __builtin_inf()>, Val<long double, __builtin_infl()>)); + +// Floating-point conversions where the source value can be represented exactly in the destination type +static_assert(Val<float, 0.0L>::value == 0.0L); +static_assert(__is_same(Val<float, 0.0>, Val<float, 0.0L>)); +static_assert(__is_same(Val<float, 0.0>, Val<float, 0.0f>)); +static_assert(!__is_same(Val<float, -0.0L>, Val<float, 0.0f>)); +static_assert(__is_same(Val<float, 0.5L>, Val<float, 0.5f>)); +static_assert(__is_same(Val<float, 0.5L>, Val<float, 0.5f>)); +static_assert(__is_same(Val<float, double{__FLT_DENORM_MIN__}>, Val<float, __FLT_DENORM_MIN__>)); +Val<float, double{__FLT_DENORM_MIN__} / 2.0> _1; +// since-cxx20-error-re@-1 {{non-type template argument evaluates to {{.+}} which can not be represented in type 'float'}} +Val<float, static_cast<long double>(__FLT_DENORM_MIN__) / 2.0L> _2; +// since-cxx20-error-re@-1 {{non-type template argument evaluates to {{.+}} which can not be represented in type 'float'}} +Val<float, __DBL_MAX__> _3; +// since-cxx20-error-re@-1 {{non-type template argument evaluates to {{.+}} which can not be represented in type 'float'}} +static_assert(__is_same(Val<float, __builtin_nan("")>, Val<float, __builtin_nanf("")>)); +static_assert(__is_same(Val<float, __builtin_inf()>, Val<float, __builtin_inff()>)); +#endif + +} + namespace cwg2858 { // cwg2858: 19 #if __cplusplus > 202302L `````````` </details> https://github.com/llvm/llvm-project/pull/90387 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits