[clang] [HLSL] Implement floating literal suffixes (PR #87270)

Chris B via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 1 11:33:14 PDT 2024


https://github.com/llvm-beanz created https://github.com/llvm/llvm-project/pull/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 https://github.com/microsoft/hlsl-specs/pull/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

>From 63ae8d6a4748e8d8046975f39df315f778c6e512 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Mon, 1 Apr 2024 13:24:00 -0500
Subject: [PATCH] [HLSL] Implement floating literal suffixes

This change implements the HLSL floating literal suffixes for half and
double literals. The PR for the HLSL language specification for this
behavior is https://github.com/microsoft/hlsl-specs/pull/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

../clang/test/SemaHLSL/literal_suffixes_no_16bit.hlsl
---
 clang/lib/Sema/SemaExpr.cpp                   |  5 +-
 clang/test/SemaHLSL/literal_suffixes.hlsl     | 59 +++++++++++++++++++
 .../SemaHLSL/literal_suffixes_no_16bit.hlsl   | 59 +++++++++++++++++++
 3 files changed, 122 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/SemaHLSL/literal_suffixes.hlsl
 create mode 100644 clang/test/SemaHLSL/literal_suffixes_no_16bit.hlsl

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 80b4257d9d83ed..541f7b8b446fef 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4117,7 +4117,8 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
   } else if (Literal.isFloatingLiteral()) {
     QualType Ty;
     if (Literal.isHalf){
-      if (getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()))
+      if (getLangOpts().HLSL ||
+          getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()))
         Ty = Context.HalfTy;
       else {
         Diag(Tok.getLocation(), diag::err_half_const_requires_fp16);
@@ -4125,6 +4126,8 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
       }
     } else if (Literal.isFloat)
       Ty = Context.FloatTy;
+    else if (getLangOpts().HLSL && Literal.isLong)
+      Ty = Context.DoubleTy;
     else if (Literal.isLong)
       Ty = Context.LongDoubleTy;
     else if (Literal.isFloat16)
diff --git a/clang/test/SemaHLSL/literal_suffixes.hlsl b/clang/test/SemaHLSL/literal_suffixes.hlsl
new file mode 100644
index 00000000000000..25a4d3b5103c48
--- /dev/null
+++ b/clang/test/SemaHLSL/literal_suffixes.hlsl
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -fnative-half-type -Wconversion -verify %s
+
+void literal_assignments() {
+  half h;
+
+  h = 2.0h; // No conversion, no diagnostic expected.
+
+  // Literal conversions that don't lose precision also don't cause diagnostics.
+  // Conversion from double (no diagnostic expected)
+  h = 2.0l;
+  h = 2.0;
+  h = 2.0f;
+
+  // Literal assignments with conversions that lose precision produce
+  // diagnostics under `-Wconversion`.
+
+  // Lose precision on assignment.
+  h = 3.1415926535897932384626433h; // No diagnostic expected because this isn't a conversion.
+
+  // Lose precision on assignment converting float to half.
+  h = 3.1415926535897932384626433f; // expected-warning {{implicit conversion loses floating-point precision: 'float' to 'half'}}
+
+  // Lose precision on assignment converting float to half.
+  h = 3.1415926535897932384626433f * 2.0f; // expected-warning {{implicit conversion loses floating-point precision: 'float' to 'half'}}
+
+  // Lose precision on assignment converting double to half.
+  h = 3.1415926535897932384626433l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}
+
+  // Lose precision on assignment converting double to half.
+  h = 3.1415926535897932384626433l * 2.0l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}
+
+  // Literal assinments of values out of the representable range produce
+  // warnings.
+
+  h = 66000.h; // expected-warning {{magnitude of floating-point constant too large for type 'half'; maximum is 65504}}
+  h = -66000.h; // expected-warning {{magnitude of floating-point constant too large for type 'half'; maximum is 65504}}
+
+  // The `h` suffix is invalid on integer literals.
+  h = 66000h; // expected-error {{invalid suffix 'h' on integer constant}}
+}
+
+template <typename T, typename U>
+struct is_same {
+  static const bool value = false;
+};
+
+template <typename T>
+struct is_same<T, T> {
+  static const bool value = true;
+};
+
+// The no-suffix behavior is currently wrong. The behavior in DXC is complicated
+// and undocumented. We have a language change planned to address this, and an
+// issue tracking: https://github.com/llvm/llvm-project/issues/85714.
+_Static_assert(is_same<double, __decltype(1.0)>::value, "1.0f literal is double (should be float)");
+
+_Static_assert(is_same<half, __decltype(1.0h)>::value, "1.0h literal is half");
+_Static_assert(is_same<float, __decltype(1.0f)>::value, "1.0f literal is float");
+_Static_assert(is_same<double, __decltype(1.0l)>::value, "1.0l literal is double");
diff --git a/clang/test/SemaHLSL/literal_suffixes_no_16bit.hlsl b/clang/test/SemaHLSL/literal_suffixes_no_16bit.hlsl
new file mode 100644
index 00000000000000..73e57041329e88
--- /dev/null
+++ b/clang/test/SemaHLSL/literal_suffixes_no_16bit.hlsl
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -Wconversion -verify %s
+
+void literal_assignments() {
+  half h;
+
+  h = 2.0h; // No conversion, no diagnostic expected.
+
+  // Literal conversions that don't lose precision also don't cause diagnostics.
+  // Conversion from double (no diagnostic expected)
+  h = 2.0l;
+  h = 2.0;
+  h = 2.0f;
+
+  // Literal assignments with conversions that lose precision produce
+  // diagnostics under `-Wconversion`.
+
+  // Lose precision on assignment.
+  h = 3.1415926535897932384626433h; // No diagnostic expected because this isn't a conversion.
+
+  // Lose precision on assignment converting float to half.
+  h = 3.1415926535897932384626433f; // No diagnostic expected because half and float are the same size.
+
+  // Lose precision on assignment converting float to half.
+  h = 3.1415926535897932384626433f * 2.0f; // No diagnostic expected because half and float are the same size.
+
+  // Lose precision on assignment converting double to half.
+  h = 3.1415926535897932384626433l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}
+
+  // Lose precision on assignment converting double to half.
+  h = 3.1415926535897932384626433l * 2.0l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}
+
+  // Literal assinments of values out of the representable range produce
+  // warnings.
+
+  h = 66000.h; // No diagnostic expected because half is 32-bit.
+  h = -66000.h; // No diagnostic expected because half is 32-bit.
+
+  // The `h` suffix is invalid on integer literals.
+  h = 66000h; // expected-error {{invalid suffix 'h' on integer constant}}
+}
+
+template <typename T, typename U>
+struct is_same {
+  static const bool value = false;
+};
+
+template <typename T>
+struct is_same<T, T> {
+  static const bool value = true;
+};
+
+// The no-suffix behavior is currently wrong. The behavior in DXC is complicated
+// and undocumented. We have a language change planned to address this, and an
+// issue tracking: https://github.com/llvm/llvm-project/issues/85714.
+_Static_assert(is_same<double, __decltype(1.0)>::value, "1.0f literal is double (should be float)");
+
+_Static_assert(is_same<half, __decltype(1.0h)>::value, "1.0h literal is half");
+_Static_assert(is_same<float, __decltype(1.0f)>::value, "1.0f literal is float");
+_Static_assert(is_same<double, __decltype(1.0l)>::value, "1.0l literal is double");



More information about the cfe-commits mailing list