Skip to content

Commit 77cb28e

Browse files
committed
[clang] Implement CWG2851: floating-point conversions in converted constant expressions
1 parent 5f9ae61 commit 77cb28e

File tree

5 files changed

+107
-4
lines changed

5 files changed

+107
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ Resolutions to C++ Defect Reports
166166
- Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
167167
(`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers <https://cplusplus.github.io/CWG/issues/2858.html>`_).
168168

169+
- Allow floating-point promotions and conversions in converted constant expressions.
170+
(`CWG2851 Allow floating-point conversions in converted constant expressions <https://cplusplus.github.io/CWG/issues/2851.html>`_).
171+
169172
C Language Changes
170173
------------------
171174

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ def err_expr_not_cce : Error<
8585
"%select{case value|enumerator value|non-type template argument|"
8686
"array size|explicit specifier argument|noexcept specifier argument|"
8787
"call to 'size()'|call to 'data()'}0 is not a constant expression">;
88+
def err_float_conv_cant_represent : Error<
89+
"non-type template argument evaluates to %0 which cannot be "
90+
"exactly represented in type %1"
91+
>;
8892
def ext_cce_narrowing : ExtWarn<
8993
"%select{case value|enumerator value|non-type template argument|"
9094
"array size|explicit specifier argument|noexcept specifier argument|"

clang/lib/Sema/SemaOverload.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6072,6 +6072,10 @@ static bool CheckConvertedConstantConversions(Sema &S,
60726072
case ICK_Integral_Promotion:
60736073
case ICK_Integral_Conversion: // Narrowing conversions are checked elsewhere.
60746074
case ICK_Zero_Queue_Conversion:
6075+
// Per CWG2851, floating-point promotions and conversions are allowed.
6076+
// The value of a conversion is checked afterwards.
6077+
case ICK_Floating_Promotion:
6078+
case ICK_Floating_Conversion:
60756079
return true;
60766080

60776081
case ICK_Boolean_Conversion:
@@ -6091,9 +6095,7 @@ static bool CheckConvertedConstantConversions(Sema &S,
60916095
// only permitted if the source type is std::nullptr_t.
60926096
return SCS.getFromType()->isNullPtrType();
60936097

6094-
case ICK_Floating_Promotion:
60956098
case ICK_Complex_Promotion:
6096-
case ICK_Floating_Conversion:
60976099
case ICK_Complex_Conversion:
60986100
case ICK_Floating_Integral:
60996101
case ICK_Compatible_Conversion:
@@ -6229,7 +6231,37 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
62296231
if (Result.isInvalid())
62306232
return Result;
62316233

6232-
// Check for a narrowing implicit conversion.
6234+
if (SCS->Second == ICK_Floating_Conversion) {
6235+
// Unlike with narrowing conversions, the value must fit
6236+
// exactly even if it is in range
6237+
assert(CCE == Sema::CCEKind::CCEK_TemplateArg &&
6238+
"Only non-type template args should use floating-point conversions");
6239+
6240+
// Initializer is From, except it is a full-expression
6241+
const Expr *Initializer =
6242+
IgnoreNarrowingConversion(S.Context, Result.get());
6243+
6244+
// If it's value-dependent, we can't tell whether it will fit
6245+
if (Initializer->isValueDependent())
6246+
return Result;
6247+
6248+
// Not-constant diagnosed afterwards
6249+
if (!Initializer->isCXX11ConstantExpr(S.Context, &PreNarrowingValue))
6250+
return Result;
6251+
6252+
llvm::APFloat PostNarrowingValue = PreNarrowingValue.getFloat();
6253+
bool LosesInfo = true;
6254+
PostNarrowingValue.convert(S.Context.getFloatTypeSemantics(T),
6255+
llvm::APFloat::rmNearestTiesToEven, &LosesInfo);
6256+
6257+
if (LosesInfo)
6258+
S.Diag(From->getBeginLoc(), diag::err_float_conv_cant_represent)
6259+
<< PreNarrowingValue.getAsString(S.Context, From->getType()) << T;
6260+
6261+
return Result;
6262+
}
6263+
6264+
// Check for a narrowing integer conversion.
62336265
bool ReturnPreNarrowingValue = false;
62346266
QualType PreNarrowingType;
62356267
switch (SCS->getNarrowingKind(S.Context, Result.get(), PreNarrowingValue,

clang/test/CXX/drs/dr28xx.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// RUN: %clang_cc1 -std=c++14 -verify=expected %s
44
// RUN: %clang_cc1 -std=c++17 -verify=expected %s
55
// RUN: %clang_cc1 -std=c++20 -verify=expected,since-cxx20 %s
6+
// RUN: %clang_cc1 -std=c++20 -mlong-double-64 -verify=expected,since-cxx20 %s
67
// RUN: %clang_cc1 -std=c++23 -verify=expected,since-cxx20,since-cxx23 %s
78
// RUN: %clang_cc1 -std=c++2c -verify=expected,since-cxx20,since-cxx23,since-cxx26 %s
89

@@ -67,6 +68,69 @@ void B<int>::g() requires true;
6768

6869
} // namespace cwg2847
6970

71+
namespace cwg2851 { // cwg2851: 19
72+
73+
#if __cplusplus >= 202002L
74+
template<typename T, T v> struct Val { static constexpr T value = v; };
75+
76+
77+
// Floating-point promotions
78+
79+
static_assert(Val<long double, 0.0>::value == 0.0L);
80+
static_assert(Val<long double, 0.0f>::value == 0.0L);
81+
static_assert(Val<double, 0.0f>::value == 0.0);
82+
static_assert(Val<long double, -0.0>::value == -0.0L);
83+
84+
static_assert(!__is_same(Val<long double, -0.0>, Val<long double, 0.0L>));
85+
static_assert(__is_same(Val<long double, 0.5>, Val<long double, 0.5L>));
86+
87+
static_assert(__is_same(Val<long double, __builtin_inff()>, Val<long double, __builtin_infl()>));
88+
89+
static_assert(__is_same(Val<long double, __builtin_nanf("")>, Val<long double, static_cast<long double>(__builtin_nanf(""))>));
90+
static_assert(__is_same(Val<long double, __builtin_nansf("")>, Val<long double, static_cast<long double>(__builtin_nansf(""))>));
91+
static_assert(__is_same(Val<long double, __builtin_nanf("0x1")>, Val<long double, static_cast<long double>(__builtin_nanf("0x1"))>));
92+
static_assert(__is_same(Val<long double, __builtin_nansf("0x1")>, Val<long double, static_cast<long double>(__builtin_nansf("0x1"))>));
93+
94+
95+
// Floating-point conversions where the source value can be represented exactly in the destination type
96+
97+
static_assert(Val<float, 0.0L>::value == 0.0L);
98+
static_assert(__is_same(Val<float, 0.0>, Val<float, 0.0L>));
99+
static_assert(__is_same(Val<float, 0.0>, Val<float, 0.0f>));
100+
static_assert(!__is_same(Val<float, -0.0L>, Val<float, 0.0f>));
101+
static_assert(__is_same(Val<float, 0.5L>, Val<float, 0.5f>));
102+
static_assert(__is_same(Val<float, 0.5L>, Val<float, 0.5f>));
103+
104+
static_assert(__is_same(Val<float, double{__FLT_DENORM_MIN__}>, Val<float, __FLT_DENORM_MIN__>));
105+
Val<float, double{__FLT_DENORM_MIN__} / 2.0> _1;
106+
// since-cxx20-error-re@-1 {{non-type template argument evaluates to {{.+}} which cannot be exactly represented in type 'float'}}
107+
Val<float, static_cast<long double>(__FLT_DENORM_MIN__) / 2.0L> _2;
108+
// since-cxx20-error-re@-1 {{non-type template argument evaluates to {{.+}} which cannot be exactly represented in type 'float'}}
109+
Val<float, __DBL_MAX__> _3;
110+
// since-cxx20-error-re@-1 {{non-type template argument evaluates to {{.+}} which cannot be exactly represented in type 'float'}}
111+
112+
static_assert(__is_same(Val<float, __builtin_infl()>, Val<float, __builtin_inff()>));
113+
114+
static_assert(__is_same(Val<float, __builtin_nanl("")>, Val<float, static_cast<float>(__builtin_nanl(""))>));
115+
static_assert(__is_same(Val<float, __builtin_nansl("")>, Val<float, static_cast<float>(__builtin_nansl(""))>));
116+
#if __SIZEOF_LONG_DOUBLE__ > 8
117+
// since-cxx20-error@-2 {{non-type template argument evaluates to nan which cannot be exactly represented in type 'float'}}
118+
#endif
119+
// Payload is shifted right so these payloads will be preserved
120+
static_assert(__is_same(Val<float, __builtin_nan("0xFF00000000")>, Val<float, static_cast<float>(__builtin_nan("0xFF00000000"))>));
121+
static_assert(__is_same(Val<float, __builtin_nans("0xFF00000000")>, Val<float, static_cast<float>(__builtin_nans("0xFF00000000"))>));
122+
static_assert(__is_same(Val<float, __builtin_nanl("0x1")>, Val<float, static_cast<float>(__builtin_nanl("0x1"))>));
123+
// since-cxx20-error@-1 {{non-type template argument evaluates to nan which cannot be exactly represented in type 'float'}}
124+
static_assert(__is_same(Val<float, __builtin_nansl("0x1")>, Val<float, static_cast<float>(__builtin_nansl("0x1"))>));
125+
// since-cxx20-error@-1 {{non-type template argument evaluates to nan which cannot be exactly represented in type 'float'}}
126+
static_assert(__is_same(Val<float, __builtin_nanl("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")>, Val<float, static_cast<float>(__builtin_nanl("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"))>));
127+
// since-cxx20-error@-1 {{non-type template argument evaluates to nan which cannot be exactly represented in type 'float'}}
128+
static_assert(__is_same(Val<float, __builtin_nansl("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")>, Val<float, static_cast<float>(__builtin_nansl("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"))>));
129+
// since-cxx20-error@-1 {{non-type template argument evaluates to nan which cannot be exactly represented in type 'float'}}
130+
#endif
131+
132+
}
133+
70134
namespace cwg2858 { // cwg2858: 19 tentatively ready 2024-04-05
71135

72136
#if __cplusplus > 202302L

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16915,7 +16915,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1691516915
<td><a href="https://cplusplus.github.io/CWG/issues/2851.html">2851</a></td>
1691616916
<td>DR</td>
1691716917
<td>Allow floating-point conversions in converted constant expressions</td>
16918-
<td class="unknown" align="center">Unknown</td>
16918+
<td class="unreleased" align="center">Clang 19</td>
1691916919
</tr>
1692016920
<tr class="open" id="2852">
1692116921
<td><a href="https://cplusplus.github.io/CWG/issues/2852.html">2852</a></td>

0 commit comments

Comments
 (0)