[clang] [HLSL] For builtins aliases, apply implicit conversions before running custom type checking (PR #195365)

Deric C. via cfe-commits cfe-commits at lists.llvm.org
Fri May 1 16:03:20 PDT 2026


https://github.com/Icohedron updated https://github.com/llvm/llvm-project/pull/195365

>From fb1d1c363acd8f0ade9d4c688b4e21dab0371305 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Fri, 1 May 2026 14:26:07 -0700
Subject: [PATCH 1/2] Apply implicit conversions before running custom type
 checking

Assisted-by: Claude Opus 4.6
---
 clang/lib/Sema/SemaExpr.cpp                   | 19 ++++++-
 clang/test/CodeGenHLSL/builtins/fma.hlsl      | 13 +++--
 .../test/SemaHLSL/BuiltIns/clamp-errors.hlsl  |  6 ++-
 clang/test/SemaHLSL/BuiltIns/dot-errors.hlsl  |  4 +-
 .../SemaHLSL/BuiltIns/f16tof32-errors.hlsl    | 33 ++++++++----
 .../SemaHLSL/BuiltIns/f32tof16-errors.hlsl    | 30 +++++++----
 clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl  | 52 ++++++++++++-------
 clang/test/SemaHLSL/BuiltIns/lerp-errors.hlsl |  8 ++-
 clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl  |  4 +-
 9 files changed, 118 insertions(+), 51 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c494669420282..98062afae4577 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6076,9 +6076,14 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
                               SourceLocation RParenLoc,
                               bool IsExecConfig) {
   // Bail out early if calling a builtin with custom typechecking.
+  // For HLSL builtin aliases, argument conversion is still needed because
+  // overload resolution may have selected a conversion sequence (e.g.,
+  // vector-to-scalar truncation) that must be applied before the custom
+  // type checker runs.
   if (FDecl)
     if (unsigned ID = FDecl->getBuiltinID())
-      if (Context.BuiltinInfo.hasCustomTypechecking(ID))
+      if (Context.BuiltinInfo.hasCustomTypechecking(ID) &&
+          !(Context.getLangOpts().HLSL && FDecl->hasAttr<BuiltinAliasAttr>()))
         return false;
 
   // C99 6.5.2.2p7 - the arguments are implicitly converted, as if by
@@ -7205,6 +7210,18 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
 
   // Bail out early if calling a builtin with custom type checking.
   if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) {
+    // For HLSL builtin aliases, the call was resolved via overload resolution
+    // which may have selected a conversion sequence (e.g., vector-to-scalar
+    // truncation). Convert arguments to match the declared prototype before
+    // the custom type checker runs, otherwise the builtin will operate on
+    // the unconverted argument types.
+    if (getLangOpts().HLSL && FDecl && FDecl->hasAttr<BuiltinAliasAttr>()) {
+      if (const auto *P = FDecl->getType()->getAs<FunctionProtoType>()) {
+        if (ConvertArgumentsForCall(TheCall, Fn, FDecl, P, Args, RParenLoc,
+                                    IsExecConfig))
+          return ExprError();
+      }
+    }
     ExprResult E = CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall);
     if (!E.isInvalid() && Context.BuiltinInfo.isImmediate(BuiltinID))
       E = CheckForImmediateInvocation(E, FDecl);
diff --git a/clang/test/CodeGenHLSL/builtins/fma.hlsl b/clang/test/CodeGenHLSL/builtins/fma.hlsl
index 3d9549197035d..93a2705c51634 100644
--- a/clang/test/CodeGenHLSL/builtins/fma.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/fma.hlsl
@@ -25,10 +25,15 @@ double3 fma_double3(double3 a, double3 b, double3 c) { return fma(a, b, c); }
 // CHECK: ret <4 x double>
 double4 fma_double4(double4 a, double4 b, double4 c) { return fma(a, b, c); }
 
-// CHECK-LABEL: define {{.*}} <1 x double> @{{.*}}fma_double1x1{{.*}}(
-// CHECK: call reassoc nnan ninf nsz arcp afn <1 x double> @llvm.fma.v1f64(<1 x double>
-// CHECK: ret <1 x double>
-double1x1 fma_double1x1(double1x1 a, double1x1 b, double1x1 c) {
+// No double1x1 fma overload exists, so overload resolution picks the scalar
+// double overload. The double1x1 matrix arguments are truncated to scalar.
+// CHECK-LABEL: define {{.*}} double @{{.*}}fma_double1x1{{.*}}(
+// CHECK: %cast.mtrunc = extractelement <1 x double>
+// CHECK: %cast.mtrunc{{[0-9]+}} = extractelement <1 x double>
+// CHECK: %cast.mtrunc{{[0-9]+}} = extractelement <1 x double>
+// CHECK: call reassoc nnan ninf nsz arcp afn double @llvm.fma.f64(double
+// CHECK: ret double
+double fma_double1x1(double1x1 a, double1x1 b, double1x1 c) {
   return fma(a, b, c);
 }
 
diff --git a/clang/test/SemaHLSL/BuiltIns/clamp-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/clamp-errors.hlsl
index bbe567b6d6ac1..2924639b92ca9 100644
--- a/clang/test/SemaHLSL/BuiltIns/clamp-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/clamp-errors.hlsl
@@ -35,9 +35,11 @@ float2 test_scalar_first_arg3(float p0, float2 p1) {
   // expected-error at -1 {{call to 'clamp' is ambiguous}}
 }
 
-float3 test_clamp_vector_size_last_arg_mismatch(float3 p0, float2 p1) {
+// With implicit conversions, the float2 overload is selected and float3 args
+// are truncated to float2.
+float2 test_clamp_vector_size_last_arg_mismatch(float3 p0, float2 p1) {
   return clamp(p0, p0, p1);
-  // expected-error at -1 {{arguments are of different types ('vector<[...], 3>' vs 'vector<[...], 2>')}}
+  // expected-warning at -1 2{{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>' (vector of 2 'float' values)}}
 }
 
 typedef float float5 __attribute__((ext_vector_type(5)));
diff --git a/clang/test/SemaHLSL/BuiltIns/dot-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/dot-errors.hlsl
index f514a04eb9f49..0f62ba3dbdbf1 100644
--- a/clang/test/SemaHLSL/BuiltIns/dot-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/dot-errors.hlsl
@@ -15,9 +15,11 @@ float test_dot_no_second_arg(float2 p0) {
   // expected-error at -1 {{no matching function for call to 'dot'}}
 }
 
+// With implicit conversions, the float2 overload is selected and float3 is
+// truncated to float2 with a warning (no longer a type-mismatch error).
 float test_dot_vector_size_mismatch(float3 p0, float2 p1) {
   return dot(p0, p1);
-  // expected-error at -1 {{arguments are of different types ('vector<[...], 3>' vs 'vector<[...], 2>')}}
+  // expected-warning at -1 {{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>' (vector of 2 'float' values)}}
 }
 
 float test_dot_builtin_vector_size_mismatch(float3 p0, float2 p1) {
diff --git a/clang/test/SemaHLSL/BuiltIns/f16tof32-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/f16tof32-errors.hlsl
index 32abf5113fdd4..a39a9970a176c 100644
--- a/clang/test/SemaHLSL/BuiltIns/f16tof32-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/f16tof32-errors.hlsl
@@ -77,58 +77,69 @@ float f16tof32_too_many_arg(uint p0) {
   // expected-error at -1 {{no matching function for call to 'f16tof32'}}
 }
 
+// Overload resolution selects uint f16tof32(uint); bool is implicitly converted to uint.
 float f16tof32_bool(bool p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'bool')}}
 }
 
+// Overload resolution selects an overload; bool3 is implicitly converted.
 float f16tof32_bool3(bool3 p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'bool3' (aka 'vector<bool, 3>'))}}
+  // expected-warning at -1 {{implicit conversion turns vector to scalar: 'vector<float, 3>' (vector of 3 'float' values) to 'float'}}
 }
 
 
+// Overload resolution selects uint f16tof32(uint); short is implicitly converted to uint.
 float f16tof32_int16_t(short p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'short')}}
+  // expected-warning at -1 {{implicit conversion changes signedness: 'short' to 'uint' (aka 'unsigned int')}}
 }
 
+// Overload resolution selects uint f16tof32(uint); unsigned short is implicitly converted to uint.
 float f16tof32_int16_t(unsigned short p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{incorrect number of bits in integer (expected 32 bits, have 16)}}
 }
 
+// Overload resolution selects uint f16tof32(uint); int is implicitly converted to uint.
 float f16tof32_int(int p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'int')}}
+  // expected-warning at -1 {{implicit conversion changes signedness: 'int' to 'uint' (aka 'unsigned int')}}
 }
 
+// Overload resolution selects uint f16tof32(uint); long is implicitly converted to uint.
 float f16tof32_int64_t(long p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'long')}}
+  // expected-warning at -1 {{implicit conversion loses integer precision: 'long' to 'uint' (aka 'unsigned int')}}
 }
 
+// Overload resolution selects an overload; int3 is implicitly converted.
 float2 f16tof32_int2_to_float2_promotion(int3 p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'int3' (aka 'vector<int, 3>'))}}
+  // expected-warning at -1 {{implicit conversion truncates vector: 'vector<float, 3>' (vector of 3 'float' values) to 'vector<float, 2>' (vector of 2 'float' values)}}
+  // expected-warning at -2 {{implicit conversion changes signedness: 'int3' (aka 'vector<int, 3>') to 'vector<uint, 3>' (vector of 3 'uint' values)}}
 }
 
+// Overload resolution selects uint f16tof32(uint); half is implicitly converted to uint.
 float f16tof32_half(half p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'half')}}
+  // expected-warning at -1 {{implicit conversion turns floating-point number into integer: 'half' to 'uint' (aka 'unsigned int')}}
 }
 
+// Overload resolution selects an overload; half2 is implicitly converted.
 float f16tof32_half2(half2 p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'half2' (aka 'vector<half, 2>'))}}
+  // expected-warning at -1 {{implicit conversion turns vector to scalar: 'vector<float, 2>' (vector of 2 'float' values) to 'float'}}
+  // expected-warning at -2 {{implicit conversion turns floating-point number into integer: 'half2' (aka 'vector<half, 2>') to 'vector<uint, 2>' (vector of 2 'uint' values)}}
 }
 
+// Overload resolution selects uint f16tof32(uint); float is implicitly converted to uint.
 float f16tof32_float(float p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'float')}}
+  // expected-warning at -1 {{implicit conversion turns floating-point number into integer: 'float' to 'uint' (aka 'unsigned int')}}
 }
 
+// Overload resolution selects uint f16tof32(uint); double is implicitly converted to uint.
 float f16tof32_double(double p0) {
   return f16tof32(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of unsigned integer types (was 'double')}}
+  // expected-warning at -1 {{implicit conversion turns floating-point number into integer: 'double' to 'uint' (aka 'unsigned int')}}
 }
diff --git a/clang/test/SemaHLSL/BuiltIns/f32tof16-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/f32tof16-errors.hlsl
index 04479935754a6..b2b52f9b153ab 100644
--- a/clang/test/SemaHLSL/BuiltIns/f32tof16-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/f32tof16-errors.hlsl
@@ -77,58 +77,66 @@ uint f32tof16_too_many_arg(uint p0) {
   // expected-error at -1 {{no matching function for call to 'f32tof16'}}
 }
 
+// Overload resolution selects uint f32tof16(float); bool is implicitly converted to float.
 uint f32tof16_bool(bool p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'bool')}}
 }
 
+// Overload resolution selects an overload; bool3 is implicitly converted.
 uint f32tof16_bool3(bool3 p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'bool3' (aka 'vector<bool, 3>'))}}
+  // expected-warning at -1 {{implicit conversion turns vector to scalar: 'vector<unsigned int, 3>' (vector of 3 'unsigned int' values) to 'unsigned int'}}
 }
 
 
+// Overload resolution selects uint f32tof16(float); short is implicitly converted to float.
 uint f32tof16_int16_t(short p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'short')}}
 }
 
+// Overload resolution selects uint f32tof16(float); unsigned short is implicitly converted to float.
 uint f32tof16_int16_t(unsigned short p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'unsigned short')}}
 }
 
+// Overload resolution selects uint f32tof16(float); int is implicitly converted to float.
 uint f32tof16_int(int p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'int')}}
+  // expected-warning at -1 {{implicit conversion from 'int' to 'float' may lose precision}}
 }
 
+// Overload resolution selects uint f32tof16(float); long is implicitly converted to float.
 uint f32tof16_int64_t(long p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'long')}}
+  // expected-warning at -1 {{implicit conversion from 'long' to 'float' may lose precision}}
 }
 
+// Overload resolution selects an overload; int3 is implicitly converted.
 uint2 f32tof16_int2_to_float2_promotion(int3 p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'int3' (aka 'vector<int, 3>'))}}
+  // expected-warning at -1 {{implicit conversion truncates vector: 'vector<unsigned int, 3>' (vector of 3 'unsigned int' values) to 'vector<unsigned int, 2>' (vector of 2 'unsigned int' values)}}
+  // expected-warning at -2 {{implicit conversion from 'int3' (aka 'vector<int, 3>') to 'vector<float, 3>' (vector of 3 'float' values) may lose precision}}
 }
 
+// Overload resolution selects uint f32tof16(float); half is implicitly converted to float.
 uint f32tof16_half(half p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'half')}}
 }
 
+// Overload resolution selects an overload; half2 is implicitly converted.
 uint f32tof16_half2(half2 p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'half2' (aka 'vector<half, 2>'))}}
+  // expected-warning at -1 {{implicit conversion turns vector to scalar: 'vector<unsigned int, 2>' (vector of 2 'unsigned int' values) to 'unsigned int'}}
 }
 
+// Overload resolution selects uint f32tof16(float); uint is implicitly converted to float.
 uint f32tof16_float(uint p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'uint' (aka 'unsigned int'))}}
+  // expected-warning at -1 {{implicit conversion from 'uint' (aka 'unsigned int') to 'float' may lose precision}}
 }
 
+// Overload resolution selects uint f32tof16(float); double is implicitly converted to float.
 uint f32tof16_double(double p0) {
   return f32tof16(p0);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'double')}}
+  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'double' to 'float'}}
 }
diff --git a/clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl
index a454f2df4d508..2061e31d639d5 100644
--- a/clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl
@@ -8,63 +8,69 @@
 // RUN:   -verify-ignore-unexpected=note
 
 float bad_float(float a, float b, float c) {
+  // Overload resolution selects 'double fma(double, double, double)'; args promoted to double.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar, vector, or matrix of double type (was 'float')}}
+  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'double' to 'float'}}
 }
 
 float2 bad_float2(float2 a, float2 b, float2 c) {
+  // Overload resolution selects 'double2 fma(double2, double2, double2)'; args promoted to double2.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar, vector, or matrix of double type (was 'float2' (aka 'vector<float, 2>'))}}
+  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'vector<double, 2>' (vector of 2 'double' values) to 'vector<float, 2>' (vector of 2 'float' values)}}
 }
 
 float2x2 bad_float2x2(float2x2 a, float2x2 b, float2x2 c) {
+  // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; args promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar, vector, or matrix of double type (was 'float2x2' (aka 'matrix<float, 2, 2>'))}}
+  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'matrix<double, 2, 2>' to 'matrix<float, 2, 2>'}}
 }
 
 half bad_half(half a, half b, half c) {
+  // Overload resolution selects 'double fma(double, double, double)'; args promoted to double.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar, vector, or matrix of double type (was 'half')}}
+  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'double' to 'half'}}
 }
 
 half2 bad_half2(half2 a, half2 b, half2 c) {
+  // Overload resolution selects 'double2 fma(double2, double2, double2)'; args promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar, vector, or matrix of double type (was 'half2' (aka 'vector<half, 2>'))}}
+  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'vector<double, 2>' (vector of 2 'double' values) to 'vector<half, 2>' (vector of 2 'half' values)}}
 }
 
 half2x2 bad_half2x2(half2x2 a, half2x2 b, half2x2 c) {
+  // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; args promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar, vector, or matrix of double type (was 'half2x2' (aka 'matrix<half, 2, 2>'))}}
+  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'matrix<double, 2, 2>' to 'matrix<half, 2, 2>'}}
 }
 
 double mixed_bad_second(double a, float b, double c) {
+  // Overload resolution selects 'double fma(double, double, double)'; float promoted to double.
   return fma(a, b, c);
-  // expected-error at -1 {{arguments are of different types ('double' vs 'float')}}
 }
 
 double mixed_bad_third(double a, double b, half c) {
+  // Overload resolution selects 'double fma(double, double, double)'; half promoted to double.
   return fma(a, b, c);
-  // expected-error at -1 {{arguments are of different types ('double' vs 'half')}}
 }
 
 double2 mixed_bad_second_vec(double2 a, float2 b, double2 c) {
+  // Overload resolution selects 'double2 fma(double2, double2, double2)'; float2 promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{arguments are of different types ('vector<double, [...]>' vs 'vector<float, [...]>')}}
 }
 
 double2 mixed_bad_third_vec(double2 a, double2 b, float2 c) {
+  // Overload resolution selects 'double2 fma(double2, double2, double2)'; float2 promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{arguments are of different types ('vector<double, [...]>' vs 'vector<float, [...]>')}}
 }
 
 double2x2 mixed_bad_second_mat(double2x2 a, float2x2 b, double2x2 c) {
+  // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; float2x2 promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{arguments are of different types ('matrix<double, [2 * ...]>' vs 'matrix<float, [2 * ...]>')}}
 }
 
 double2x2 mixed_bad_third_mat(double2x2 a, double2x2 b, half2x2 c) {
+  // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; half2x2 promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{arguments are of different types ('matrix<double, [2 * ...]>' vs 'matrix<half, [2 * ...]>')}}
 }
 
 double shape_mismatch_second(double a, double2 b, double c) {
@@ -84,30 +90,40 @@ double2x2 shape_mismatch_scalar_mat(double2x2 a, double b, double2x2 c) {
 
 double2x2 shape_mismatch_vec_mat(double2x2 a, double2 b, double2x2 c) {
   return fma(a, b, c);
-  // expected-error at -1 {{arguments are of different types ('double2x2' (aka 'matrix<double, 2, 2>') vs 'double2' (aka 'vector<double, 2>'))}}
+  // Overload resolution selects the scalar 'double fma(double, double, double)'
+  // overload; vector/matrix arguments are truncated to scalar.
+  // expected-warning at -3 {{implicit conversion turns matrix to scalar: 'double2x2' (aka 'matrix<double, 2, 2>') to 'double'}}
+  // expected-warning at -4 {{implicit conversion turns vector to scalar: 'double2' (aka 'vector<double, 2>') to 'double'}}
+  // expected-warning at -5 {{implicit conversion turns matrix to scalar: 'double2x2' (aka 'matrix<double, 2, 2>') to 'double'}}
 }
 
 int bad_int(int a, int b, int c) {
+  // Overload resolution selects 'double fma(double, double, double)'; ints promoted to double.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'int')}}
+  // expected-warning at -1 {{implicit conversion turns floating-point number into integer: 'double' to 'int'}}
 }
 
 int2 bad_int2(int2 a, int2 b, int2 c) {
+  // Overload resolution selects 'double2 fma(double2, double2, double2)'; int2 promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'int2' (aka 'vector<int, 2>'))}}
+  // expected-warning at -1 {{implicit conversion turns floating-point number into integer: 'vector<double, 2>' (vector of 2 'double' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
 }
 
 bool bad_bool(bool a, bool b, bool c) {
+  // Overload resolution selects 'double fma(double, double, double)'; bools promoted to double.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'bool')}}
+  // expected-warning at -1 {{implicit conversion turns floating-point number into integer: 'double' to 'bool'}}
+  // expected-warning at -2 {{implicit conversion turns floating-point number into bool: 'double' to 'bool'}}
 }
 
 bool2 bad_bool2(bool2 a, bool2 b, bool2 c) {
+  // Overload resolution selects 'double2 fma(double2, double2, double2)'; bool2 promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'bool2' (aka 'vector<bool, 2>'))}}
+  // expected-warning at -1 {{implicit conversion turns floating-point number into integer: 'vector<double, 2>' (vector of 2 'double' values) to 'vector<bool, 2>' (vector of 2 'bool' values)}}
 }
 
 bool2x2 bad_bool2x2(bool2x2 a, bool2x2 b, bool2x2 c) {
+  // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; bool2x2 promoted.
   return fma(a, b, c);
-  // expected-error at -1 {{1st argument must be a scalar or vector of floating-point types (was 'bool2x2' (aka 'matrix<bool, 2, 2>'))}}
+  // expected-warning at -1 {{implicit conversion turns floating-point number into integer: 'matrix<double, 2, 2>' to 'matrix<bool, 2, 2>'}}
 }
diff --git a/clang/test/SemaHLSL/BuiltIns/lerp-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/lerp-errors.hlsl
index 22720a4a37d02..bc82991eb3e6b 100644
--- a/clang/test/SemaHLSL/BuiltIns/lerp-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/lerp-errors.hlsl
@@ -25,14 +25,18 @@ float2 test_lerp_vector_trunc_warn1(float3 p0) {
   // expected-warning at -1 {{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>' (vector of 2 'float' values)}}
 }
 
+// With implicit conversions, the float2 overload is selected and float3 args
+// are implicitly truncated to float2 (warnings instead of type-mismatch error).
 float2 test_lerp_vector_size_mismatch1(float3 p0, float2 p1) {
   return lerp(p0, p0, p1);
-  // expected-error at -1 {{all arguments to 'lerp' must have the same type}}
+  // expected-warning at -1 2{{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>' (vector of 2 'float' values)}}
 }
 
+// With implicit conversions, the float2 overload is selected and float3 args
+// are implicitly truncated to float2 (warnings instead of type-mismatch error).
 float2 test_lerp_vector_size_mismatch2(float3 p0, float2 p1) {
   return lerp(p0, p1, p0);
-  // expected-error at -1 {{all arguments to 'lerp' must have the same type}}
+  // expected-warning at -1 2{{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>' (vector of 2 'float' values)}}
 }
 
 float2 test_lerp_builtin_vector_size_mismatch_Arg1(float3 p0, float2 p1) {
diff --git a/clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl
index 0e9dda7055f98..3d39b5e7dfea6 100644
--- a/clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl
@@ -20,9 +20,11 @@ float2 test_mad_no_second_arg(float2 p0) {
   // expected-error at -1 {{no matching function for call to 'mad'}}
 }
 
+// With implicit conversions, the float2 overload is selected and float3 args
+// are implicitly truncated to float2 (warnings instead of type-mismatch error).
 float2 test_mad_vector_size_mismatch(float3 p0, float2 p1) {
   return mad(p0, p0, p1);
-  // expected-error at -1 {{arguments are of different types ('vector<[...], 3>' vs 'vector<[...], 2>')}}
+  // expected-warning at -1 2{{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>' (vector of 2 'float' values)}}
 }
 
 float2 test_mad_builtin_vector_size_mismatch(float3 p0, float2 p1) {

>From 35949a64b0e44ec36ee4e8bbeb024170701418a4 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Fri, 1 May 2026 16:02:14 -0700
Subject: [PATCH 2/2] Add -Wdouble-promotion to fma-errors sema test

Assisted-by: Claude Opus 4.6
---
 clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl | 28 ++++++++++++++------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl
index 2061e31d639d5..e6a2dd6c052be 100644
--- a/clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/fma-errors.hlsl
@@ -1,76 +1,88 @@
 // RUN: %clang_cc1 -finclude-default-header -fnative-half-type -x hlsl \
-// RUN:   -triple dxil-pc-shadermodel6.6-library %s \
+// RUN:   -triple dxil-pc-shadermodel6.6-library %s -Wdouble-promotion \
 // RUN:   -emit-llvm-only -disable-llvm-passes -verify \
 // RUN:   -verify-ignore-unexpected=note
 // RUN: %clang_cc1 -finclude-default-header -fnative-half-type -x hlsl \
-// RUN:   -triple spirv-unknown-vulkan-compute %s \
+// RUN:   -triple spirv-unknown-vulkan-compute %s -Wdouble-promotion \
 // RUN:   -emit-llvm-only -disable-llvm-passes -verify \
 // RUN:   -verify-ignore-unexpected=note
 
 float bad_float(float a, float b, float c) {
   // Overload resolution selects 'double fma(double, double, double)'; args promoted to double.
   return fma(a, b, c);
-  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'double' to 'float'}}
+  // expected-warning at -1 3{{implicit conversion increases floating-point precision: 'float' to 'double'}}
+  // expected-warning at -2 {{implicit conversion loses floating-point precision: 'double' to 'float'}}
 }
 
 float2 bad_float2(float2 a, float2 b, float2 c) {
   // Overload resolution selects 'double2 fma(double2, double2, double2)'; args promoted to double2.
   return fma(a, b, c);
-  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'vector<double, 2>' (vector of 2 'double' values) to 'vector<float, 2>' (vector of 2 'float' values)}}
+  // expected-warning at -1 3{{implicit conversion increases floating-point precision: 'float2' (aka 'vector<float, 2>') to 'vector<double, 2>' (vector of 2 'double' values)}}
+  // expected-warning at -2 {{implicit conversion loses floating-point precision: 'vector<double, 2>' (vector of 2 'double' values) to 'vector<float, 2>' (vector of 2 'float' values)}}
 }
 
 float2x2 bad_float2x2(float2x2 a, float2x2 b, float2x2 c) {
   // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; args promoted.
   return fma(a, b, c);
-  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'matrix<double, 2, 2>' to 'matrix<float, 2, 2>'}}
+  // expected-warning at -1 3{{implicit conversion increases floating-point precision: 'float2x2' (aka 'matrix<float, 2, 2>') to 'matrix<double, 2, 2>'}}
+  // expected-warning at -2 {{implicit conversion loses floating-point precision: 'matrix<double, 2, 2>' to 'matrix<float, 2, 2>'}}
 }
 
 half bad_half(half a, half b, half c) {
   // Overload resolution selects 'double fma(double, double, double)'; args promoted to double.
   return fma(a, b, c);
-  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'double' to 'half'}}
+  // expected-warning at -1 3{{implicit conversion increases floating-point precision: 'half' to 'double'}}
+  // expected-warning at -2 {{implicit conversion loses floating-point precision: 'double' to 'half'}}
 }
 
 half2 bad_half2(half2 a, half2 b, half2 c) {
   // Overload resolution selects 'double2 fma(double2, double2, double2)'; args promoted.
   return fma(a, b, c);
-  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'vector<double, 2>' (vector of 2 'double' values) to 'vector<half, 2>' (vector of 2 'half' values)}}
+  // expected-warning at -1 3{{implicit conversion increases floating-point precision: 'half2' (aka 'vector<half, 2>') to 'vector<double, 2>' (vector of 2 'double' values)}}
+  // expected-warning at -2 {{implicit conversion loses floating-point precision: 'vector<double, 2>' (vector of 2 'double' values) to 'vector<half, 2>' (vector of 2 'half' values)}}
 }
 
 half2x2 bad_half2x2(half2x2 a, half2x2 b, half2x2 c) {
   // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; args promoted.
   return fma(a, b, c);
-  // expected-warning at -1 {{implicit conversion loses floating-point precision: 'matrix<double, 2, 2>' to 'matrix<half, 2, 2>'}}
+  // expected-warning at -1 3{{implicit conversion increases floating-point precision: 'half2x2' (aka 'matrix<half, 2, 2>') to 'matrix<double, 2, 2>'}}
+  // expected-warning at -2 {{implicit conversion loses floating-point precision: 'matrix<double, 2, 2>' to 'matrix<half, 2, 2>'}}
 }
 
 double mixed_bad_second(double a, float b, double c) {
   // Overload resolution selects 'double fma(double, double, double)'; float promoted to double.
   return fma(a, b, c);
+  // expected-warning at -1 {{implicit conversion increases floating-point precision: 'float' to 'double'}}
 }
 
 double mixed_bad_third(double a, double b, half c) {
   // Overload resolution selects 'double fma(double, double, double)'; half promoted to double.
   return fma(a, b, c);
+  // expected-warning at -1 {{implicit conversion increases floating-point precision: 'half' to 'double'}}
 }
 
 double2 mixed_bad_second_vec(double2 a, float2 b, double2 c) {
   // Overload resolution selects 'double2 fma(double2, double2, double2)'; float2 promoted.
   return fma(a, b, c);
+  // expected-warning at -1 {{implicit conversion increases floating-point precision: 'float2' (aka 'vector<float, 2>') to 'vector<double, 2>' (vector of 2 'double' values)}}
 }
 
 double2 mixed_bad_third_vec(double2 a, double2 b, float2 c) {
   // Overload resolution selects 'double2 fma(double2, double2, double2)'; float2 promoted.
   return fma(a, b, c);
+  // expected-warning at -1 {{implicit conversion increases floating-point precision: 'float2' (aka 'vector<float, 2>') to 'vector<double, 2>' (vector of 2 'double' values)}}
 }
 
 double2x2 mixed_bad_second_mat(double2x2 a, float2x2 b, double2x2 c) {
   // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; float2x2 promoted.
   return fma(a, b, c);
+  // expected-warning at -1 {{implicit conversion increases floating-point precision: 'float2x2' (aka 'matrix<float, 2, 2>') to 'matrix<double, 2, 2>'}}
 }
 
 double2x2 mixed_bad_third_mat(double2x2 a, double2x2 b, half2x2 c) {
   // Overload resolution selects 'double2x2 fma(double2x2, double2x2, double2x2)'; half2x2 promoted.
   return fma(a, b, c);
+  // expected-warning at -1 {{implicit conversion increases floating-point precision: 'half2x2' (aka 'matrix<half, 2, 2>') to 'matrix<double, 2, 2>'}}
 }
 
 double shape_mismatch_second(double a, double2 b, double c) {



More information about the cfe-commits mailing list