[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