Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 86b346d

Browse files
authored
[HLSL] For builtins aliases, apply implicit conversions before running custom type checking (#195365)
Fixes #195329 by making HLSL builtin aliases apply implicit conversions before running custom type checking. After this PR: - There are no more size 1 vectors being passed and returned to/from aliased Clang builtins because they get truncated to scalars due to the HLSL alias builtin not having explicit size 1 vector overloads. - HLSL alias builtins no longer accept matrices unless they have explicit matrix overloads. Matrices get implicitly truncated to scalars and resolve to the scalar Clang builtin being aliased. - Many calls with mismatched vector sizes no longer error with `arguments are of different types` and instead follow Clang's overload resolution rules with respect to HLSL's implicit conversion sequences. (e.g., `dot(float3, float2)` -> `dot(float2, float2)` with warning) - Calls with implicitly-convertible types no longer error. They are now implicitly converted, and with a warning in some cases. (e.g., `f16tof32(bool)` -> `f16tof32(uint)` without warning, but `f16tof32(short)` -> `f16tof32(uint)` with warning). Assisted-by: Claude Opus 4.6
1 parent 0e0c9a5 commit 86b346d

9 files changed

Lines changed: 132 additions & 53 deletions

File tree

clang/lib/Sema/SemaExpr.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6076,9 +6076,14 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
60766076
SourceLocation RParenLoc,
60776077
bool IsExecConfig) {
60786078
// Bail out early if calling a builtin with custom typechecking.
6079+
// For HLSL builtin aliases, argument conversion is still needed because
6080+
// overload resolution may have selected a conversion sequence (e.g.,
6081+
// vector-to-scalar truncation) that must be applied before the custom
6082+
// type checker runs.
60796083
if (FDecl)
60806084
if (unsigned ID = FDecl->getBuiltinID())
6081-
if (Context.BuiltinInfo.hasCustomTypechecking(ID))
6085+
if (Context.BuiltinInfo.hasCustomTypechecking(ID) &&
6086+
!(Context.getLangOpts().HLSL && FDecl->hasAttr<BuiltinAliasAttr>()))
60826087
return false;
60836088

60846089
// C99 6.5.2.2p7 - the arguments are implicitly converted, as if by
@@ -7205,6 +7210,18 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
72057210

72067211
// Bail out early if calling a builtin with custom type checking.
72077212
if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) {
7213+
// For HLSL builtin aliases, the call was resolved via overload resolution
7214+
// which may have selected a conversion sequence (e.g., vector-to-scalar
7215+
// truncation). Convert arguments to match the declared prototype before
7216+
// the custom type checker runs, otherwise the builtin will operate on
7217+
// the unconverted argument types.
7218+
if (getLangOpts().HLSL && FDecl && FDecl->hasAttr<BuiltinAliasAttr>()) {
7219+
if (const auto *P = FDecl->getType()->getAs<FunctionProtoType>()) {
7220+
if (ConvertArgumentsForCall(TheCall, Fn, FDecl, P, Args, RParenLoc,
7221+
IsExecConfig))
7222+
return ExprError();
7223+
}
7224+
}
72087225
ExprResult E = CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall);
72097226
if (!E.isInvalid() && Context.BuiltinInfo.isImmediate(BuiltinID))
72107227
E = CheckForImmediateInvocation(E, FDecl);

clang/test/CodeGenHLSL/builtins/fma.hlsl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@ double3 fma_double3(double3 a, double3 b, double3 c) { return fma(a, b, c); }
2525
// CHECK: ret <4 x double>
2626
double4 fma_double4(double4 a, double4 b, double4 c) { return fma(a, b, c); }
2727

28-
// CHECK-LABEL: define {{.*}} <1 x double> @{{.*}}fma_double1x1{{.*}}(
29-
// CHECK: call reassoc nnan ninf nsz arcp afn <1 x double> @llvm.fma.v1f64(<1 x double>
30-
// CHECK: ret <1 x double>
31-
double1x1 fma_double1x1(double1x1 a, double1x1 b, double1x1 c) {
28+
// No double1x1 fma overload exists, so overload resolution picks the scalar
29+
// double overload. The double1x1 matrix arguments are truncated to scalar.
30+
// CHECK-LABEL: define {{.*}} double @{{.*}}fma_double1x1{{.*}}(
31+
// CHECK: %cast.mtrunc = extractelement <1 x double>
32+
// CHECK: %cast.mtrunc{{[0-9]+}} = extractelement <1 x double>
33+
// CHECK: %cast.mtrunc{{[0-9]+}} = extractelement <1 x double>
34+
// CHECK: call reassoc nnan ninf nsz arcp afn double @llvm.fma.f64(double
35+
// CHECK: ret double
36+
double fma_double1x1(double1x1 a, double1x1 b, double1x1 c) {
3237
return fma(a, b, c);
3338
}
3439

clang/test/SemaHLSL/BuiltIns/clamp-errors.hlsl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ float2 test_scalar_first_arg3(float p0, float2 p1) {
3535
// expected-error@-1 {{call to 'clamp' is ambiguous}}
3636
}
3737

38-
float3 test_clamp_vector_size_last_arg_mismatch(float3 p0, float2 p1) {
38+
// With implicit conversions, the float2 overload is selected and float3 args
39+
// are truncated to float2.
40+
float2 test_clamp_vector_size_last_arg_mismatch(float3 p0, float2 p1) {
3941
return clamp(p0, p0, p1);
40-
// expected-error@-1 {{arguments are of different types ('vector<[...], 3>' vs 'vector<[...], 2>')}}
42+
// expected-warning@-1 2{{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>' (vector of 2 'float' values)}}
4143
}
4244

4345
typedef float float5 __attribute__((ext_vector_type(5)));

clang/test/SemaHLSL/BuiltIns/dot-errors.hlsl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ float test_dot_no_second_arg(float2 p0) {
1515
// expected-error@-1 {{no matching function for call to 'dot'}}
1616
}
1717

18+
// With implicit conversions, the float2 overload is selected and float3 is
19+
// truncated to float2 with a warning (no longer a type-mismatch error).
1820
float test_dot_vector_size_mismatch(float3 p0, float2 p1) {
1921
return dot(p0, p1);
20-
// expected-error@-1 {{arguments are of different types ('vector<[...], 3>' vs 'vector<[...], 2>')}}
22+
// expected-warning@-1 {{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>' (vector of 2 'float' values)}}
2123
}
2224

2325
float test_dot_builtin_vector_size_mismatch(float3 p0, float2 p1) {

clang/test/SemaHLSL/BuiltIns/f16tof32-errors.hlsl

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,58 +77,69 @@ float f16tof32_too_many_arg(uint p0) {
7777
// expected-error@-1 {{no matching function for call to 'f16tof32'}}
7878
}
7979

80+
// Overload resolution selects uint f16tof32(uint); bool is implicitly converted to uint.
8081
float f16tof32_bool(bool p0) {
8182
return f16tof32(p0);
82-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'bool')}}
8383
}
8484

85+
// Overload resolution selects an overload; bool3 is implicitly converted.
8586
float f16tof32_bool3(bool3 p0) {
8687
return f16tof32(p0);
87-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'bool3' (aka 'vector<bool, 3>'))}}
88+
// expected-warning@-1 {{implicit conversion turns vector to scalar: 'vector<float, 3>' (vector of 3 'float' values) to 'float'}}
8889
}
8990

9091

92+
// Overload resolution selects uint f16tof32(uint); short is implicitly converted to uint.
9193
float f16tof32_int16_t(short p0) {
9294
return f16tof32(p0);
93-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'short')}}
95+
// expected-warning@-1 {{implicit conversion changes signedness: 'short' to 'uint' (aka 'unsigned int')}}
9496
}
9597

98+
// Overload resolution selects uint f16tof32(uint); unsigned short is implicitly converted to uint.
9699
float f16tof32_int16_t(unsigned short p0) {
97100
return f16tof32(p0);
98-
// expected-error@-1 {{incorrect number of bits in integer (expected 32 bits, have 16)}}
99101
}
100102

103+
// Overload resolution selects uint f16tof32(uint); int is implicitly converted to uint.
101104
float f16tof32_int(int p0) {
102105
return f16tof32(p0);
103-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'int')}}
106+
// expected-warning@-1 {{implicit conversion changes signedness: 'int' to 'uint' (aka 'unsigned int')}}
104107
}
105108

109+
// Overload resolution selects uint f16tof32(uint); long is implicitly converted to uint.
106110
float f16tof32_int64_t(long p0) {
107111
return f16tof32(p0);
108-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'long')}}
112+
// expected-warning@-1 {{implicit conversion loses integer precision: 'long' to 'uint' (aka 'unsigned int')}}
109113
}
110114

115+
// Overload resolution selects an overload; int3 is implicitly converted.
111116
float2 f16tof32_int2_to_float2_promotion(int3 p0) {
112117
return f16tof32(p0);
113-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'int3' (aka 'vector<int, 3>'))}}
118+
// expected-warning@-1 {{implicit conversion truncates vector: 'vector<float, 3>' (vector of 3 'float' values) to 'vector<float, 2>' (vector of 2 'float' values)}}
119+
// expected-warning@-2 {{implicit conversion changes signedness: 'int3' (aka 'vector<int, 3>') to 'vector<uint, 3>' (vector of 3 'uint' values)}}
114120
}
115121

122+
// Overload resolution selects uint f16tof32(uint); half is implicitly converted to uint.
116123
float f16tof32_half(half p0) {
117124
return f16tof32(p0);
118-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'half')}}
125+
// expected-warning@-1 {{implicit conversion turns floating-point number into integer: 'half' to 'uint' (aka 'unsigned int')}}
119126
}
120127

128+
// Overload resolution selects an overload; half2 is implicitly converted.
121129
float f16tof32_half2(half2 p0) {
122130
return f16tof32(p0);
123-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'half2' (aka 'vector<half, 2>'))}}
131+
// expected-warning@-1 {{implicit conversion turns vector to scalar: 'vector<float, 2>' (vector of 2 'float' values) to 'float'}}
132+
// expected-warning@-2 {{implicit conversion turns floating-point number into integer: 'half2' (aka 'vector<half, 2>') to 'vector<uint, 2>' (vector of 2 'uint' values)}}
124133
}
125134

135+
// Overload resolution selects uint f16tof32(uint); float is implicitly converted to uint.
126136
float f16tof32_float(float p0) {
127137
return f16tof32(p0);
128-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'float')}}
138+
// expected-warning@-1 {{implicit conversion turns floating-point number into integer: 'float' to 'uint' (aka 'unsigned int')}}
129139
}
130140

141+
// Overload resolution selects uint f16tof32(uint); double is implicitly converted to uint.
131142
float f16tof32_double(double p0) {
132143
return f16tof32(p0);
133-
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'double')}}
144+
// expected-warning@-1 {{implicit conversion turns floating-point number into integer: 'double' to 'uint' (aka 'unsigned int')}}
134145
}

clang/test/SemaHLSL/BuiltIns/f32tof16-errors.hlsl

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,58 +77,66 @@ uint f32tof16_too_many_arg(uint p0) {
7777
// expected-error@-1 {{no matching function for call to 'f32tof16'}}
7878
}
7979

80+
// Overload resolution selects uint f32tof16(float); bool is implicitly converted to float.
8081
uint f32tof16_bool(bool p0) {
8182
return f32tof16(p0);
82-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'bool')}}
8383
}
8484

85+
// Overload resolution selects an overload; bool3 is implicitly converted.
8586
uint f32tof16_bool3(bool3 p0) {
8687
return f32tof16(p0);
87-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'bool3' (aka 'vector<bool, 3>'))}}
88+
// expected-warning@-1 {{implicit conversion turns vector to scalar: 'vector<unsigned int, 3>' (vector of 3 'unsigned int' values) to 'unsigned int'}}
8889
}
8990

9091

92+
// Overload resolution selects uint f32tof16(float); short is implicitly converted to float.
9193
uint f32tof16_int16_t(short p0) {
9294
return f32tof16(p0);
93-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'short')}}
9495
}
9596

97+
// Overload resolution selects uint f32tof16(float); unsigned short is implicitly converted to float.
9698
uint f32tof16_int16_t(unsigned short p0) {
9799
return f32tof16(p0);
98-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'unsigned short')}}
99100
}
100101

102+
// Overload resolution selects uint f32tof16(float); int is implicitly converted to float.
101103
uint f32tof16_int(int p0) {
102104
return f32tof16(p0);
103-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'int')}}
105+
// expected-warning@-1 {{implicit conversion from 'int' to 'float' may lose precision}}
104106
}
105107

108+
// Overload resolution selects uint f32tof16(float); long is implicitly converted to float.
106109
uint f32tof16_int64_t(long p0) {
107110
return f32tof16(p0);
108-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'long')}}
111+
// expected-warning@-1 {{implicit conversion from 'long' to 'float' may lose precision}}
109112
}
110113

114+
// Overload resolution selects an overload; int3 is implicitly converted.
111115
uint2 f32tof16_int2_to_float2_promotion(int3 p0) {
112116
return f32tof16(p0);
113-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'int3' (aka 'vector<int, 3>'))}}
117+
// expected-warning@-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)}}
118+
// expected-warning@-2 {{implicit conversion from 'int3' (aka 'vector<int, 3>') to 'vector<float, 3>' (vector of 3 'float' values) may lose precision}}
114119
}
115120

121+
// Overload resolution selects uint f32tof16(float); half is implicitly converted to float.
116122
uint f32tof16_half(half p0) {
117123
return f32tof16(p0);
118-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'half')}}
119124
}
120125

126+
// Overload resolution selects an overload; half2 is implicitly converted.
121127
uint f32tof16_half2(half2 p0) {
122128
return f32tof16(p0);
123-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'half2' (aka 'vector<half, 2>'))}}
129+
// expected-warning@-1 {{implicit conversion turns vector to scalar: 'vector<unsigned int, 2>' (vector of 2 'unsigned int' values) to 'unsigned int'}}
124130
}
125131

132+
// Overload resolution selects uint f32tof16(float); uint is implicitly converted to float.
126133
uint f32tof16_float(uint p0) {
127134
return f32tof16(p0);
128-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'uint' (aka 'unsigned int'))}}
135+
// expected-warning@-1 {{implicit conversion from 'uint' (aka 'unsigned int') to 'float' may lose precision}}
129136
}
130137

138+
// Overload resolution selects uint f32tof16(float); double is implicitly converted to float.
131139
uint f32tof16_double(double p0) {
132140
return f32tof16(p0);
133-
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'double')}}
141+
// expected-warning@-1 {{implicit conversion loses floating-point precision: 'double' to 'float'}}
134142
}

0 commit comments

Comments
 (0)