Skip to content

Commit 0a0fccf

Browse files
authored
[HLSL] Implement floating literal suffixes (#87270)
This change implements the HLSL floating literal suffixes for half and double literals. The PR for the HLSL language specification for this behavior is microsoft/hlsl-specs#175. The TL;DR is that the `h` suffix on floating literals means `half`, and the `l` suffix means `double`. The expected behavior and diagnostics are different if native half is supported. When native half is not enabled half is 32-bit so implicit conversions to float do not lose precision and do not warn. In all cases `half` and `float` are distinct types. Resolves #85712
1 parent 225e14e commit 0a0fccf

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

clang/lib/Sema/SemaExpr.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4117,7 +4117,8 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
41174117
} else if (Literal.isFloatingLiteral()) {
41184118
QualType Ty;
41194119
if (Literal.isHalf){
4120-
if (getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()))
4120+
if (getLangOpts().HLSL ||
4121+
getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()))
41214122
Ty = Context.HalfTy;
41224123
else {
41234124
Diag(Tok.getLocation(), diag::err_half_const_requires_fp16);
@@ -4126,7 +4127,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
41264127
} else if (Literal.isFloat)
41274128
Ty = Context.FloatTy;
41284129
else if (Literal.isLong)
4129-
Ty = Context.LongDoubleTy;
4130+
Ty = !getLangOpts().HLSL ? Context.LongDoubleTy : Context.DoubleTy;
41304131
else if (Literal.isFloat16)
41314132
Ty = Context.Float16Ty;
41324133
else if (Literal.isFloat128)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -fnative-half-type -Wconversion -verify %s
2+
3+
void literal_assignments() {
4+
half h;
5+
6+
h = 2.0h; // No conversion, no diagnostic expected.
7+
8+
// Literal conversions that don't lose precision also don't cause diagnostics.
9+
// Conversion from double (no diagnostic expected)
10+
h = 2.0l;
11+
h = 2.0;
12+
h = 2.0f;
13+
14+
// Literal assignments with conversions that lose precision produce
15+
// diagnostics under `-Wconversion`.
16+
17+
// Lose precision on assignment.
18+
h = 3.1415926535897932384626433h; // No diagnostic expected because this isn't a conversion.
19+
20+
// Lose precision on assignment converting float to half.
21+
h = 3.1415926535897932384626433f; // expected-warning {{implicit conversion loses floating-point precision: 'float' to 'half'}}
22+
23+
// Lose precision on assignment converting float to half.
24+
h = 3.1415926535897932384626433f * 2.0f; // expected-warning {{implicit conversion loses floating-point precision: 'float' to 'half'}}
25+
26+
// Lose precision on assignment converting double to half.
27+
h = 3.1415926535897932384626433l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}
28+
29+
// Lose precision on assignment converting double to half.
30+
h = 3.1415926535897932384626433l * 2.0l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}
31+
32+
// Literal assinments of values out of the representable range produce
33+
// warnings.
34+
35+
h = 66000.h; // expected-warning {{magnitude of floating-point constant too large for type 'half'; maximum is 65504}}
36+
h = -66000.h; // expected-warning {{magnitude of floating-point constant too large for type 'half'; maximum is 65504}}
37+
38+
// The `h` suffix is invalid on integer literals.
39+
h = 66000h; // expected-error {{invalid suffix 'h' on integer constant}}
40+
}
41+
42+
template <typename T, typename U>
43+
struct is_same {
44+
static const bool value = false;
45+
};
46+
47+
template <typename T>
48+
struct is_same<T, T> {
49+
static const bool value = true;
50+
};
51+
52+
// The no-suffix behavior is currently wrong. The behavior in DXC is complicated
53+
// and undocumented. We have a language change planned to address this, and an
54+
// issue tracking: https://github.com/llvm/llvm-project/issues/85714.
55+
_Static_assert(is_same<double, __decltype(1.0)>::value, "1.0f literal is double (should be float)");
56+
57+
_Static_assert(is_same<half, __decltype(1.0h)>::value, "1.0h literal is half");
58+
_Static_assert(is_same<float, __decltype(1.0f)>::value, "1.0f literal is float");
59+
_Static_assert(is_same<double, __decltype(1.0l)>::value, "1.0l literal is double");
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -Wconversion -verify %s
2+
3+
void literal_assignments() {
4+
half h;
5+
6+
h = 2.0h; // No conversion, no diagnostic expected.
7+
8+
// Literal conversions that don't lose precision also don't cause diagnostics.
9+
// Conversion from double (no diagnostic expected)
10+
h = 2.0l;
11+
h = 2.0;
12+
h = 2.0f;
13+
14+
// Literal assignments with conversions that lose precision produce
15+
// diagnostics under `-Wconversion`.
16+
17+
// Lose precision on assignment.
18+
h = 3.1415926535897932384626433h; // No diagnostic expected because this isn't a conversion.
19+
20+
// Lose precision on assignment converting float to half.
21+
h = 3.1415926535897932384626433f; // No diagnostic expected because half and float are the same size.
22+
23+
// Lose precision on assignment converting float to half.
24+
h = 3.1415926535897932384626433f * 2.0f; // No diagnostic expected because half and float are the same size.
25+
26+
// Lose precision on assignment converting double to half.
27+
h = 3.1415926535897932384626433l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}
28+
29+
// Lose precision on assignment converting double to half.
30+
h = 3.1415926535897932384626433l * 2.0l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}
31+
32+
// Literal assinments of values out of the representable range produce
33+
// warnings.
34+
35+
h = 66000.h; // No diagnostic expected because half is 32-bit.
36+
h = -66000.h; // No diagnostic expected because half is 32-bit.
37+
38+
// The `h` suffix is invalid on integer literals.
39+
h = 66000h; // expected-error {{invalid suffix 'h' on integer constant}}
40+
}
41+
42+
template <typename T, typename U>
43+
struct is_same {
44+
static const bool value = false;
45+
};
46+
47+
template <typename T>
48+
struct is_same<T, T> {
49+
static const bool value = true;
50+
};
51+
52+
// The no-suffix behavior is currently wrong. The behavior in DXC is complicated
53+
// and undocumented. We have a language change planned to address this, and an
54+
// issue tracking: https://github.com/llvm/llvm-project/issues/85714.
55+
_Static_assert(is_same<double, __decltype(1.0)>::value, "1.0f literal is double (should be float)");
56+
57+
_Static_assert(is_same<half, __decltype(1.0h)>::value, "1.0h literal is half");
58+
_Static_assert(is_same<float, __decltype(1.0f)>::value, "1.0f literal is float");
59+
_Static_assert(is_same<double, __decltype(1.0l)>::value, "1.0l literal is double");

0 commit comments

Comments
 (0)