DXC’s implementation of HLSL has some interesting quirks that impact overload resolution. How many of these can you get right?
bool
argument to WaveReadLaneFirst
bool fn(bool B) {
return WaveReadLaneFirst(B);
}
A. bool(bool)
B. int(int)
C. float(float)
D. Error!
Answer!
A!
HLSL’s WaveReadLaneFirst
function has a bool overload, so this resolves the
exact match bool(bool)
.
See it here in the AST dump:
`-CallExpr <col:10, col:29> 'bool'
|-ImplicitCastExpr <col:10> 'bool (*)(bool)' <FunctionToPointerDecay>
| `-DeclRefExpr <col:10> 'bool (bool)' lvalue Function 'WaveReadLaneFirst' 'bool (bool)'
`-ImplicitCastExpr <col:28> 'bool' <LValueToRValue>
`-DeclRefExpr <col:28> 'bool' lvalue ParmVar 'B' 'bool'
bool
argument to user-defined function
bool f(int) { return true; }
bool f(float) { return false; }
bool fn(bool B) {
return f(B);
}
A. bool(bool)
B. int(int)
C. float(float)
D. Error!
Answer!
D!
Conversion of a bool
to an int
is equally weighted by DXC to conversion of
bool
to a float
, so for this case DXC reports an error and cannot choose the
correct function.
See it here in Compiler Explorer.
bool
argument to tan
bool fn(bool B) {
return tan(B);
}
A. bool(bool)
B. int(int)
C. float(float)
D. Error!
Answer!
C!
This is our first real curveball. HLSL’s tan
function has overloads for
float
-like types (half
, float
, double
). If instead of being a built-in
function this had been a user-defined function with those overloads, the call
would be ambiguous producing an error.
DXC’s implementation has a subtle and probably unintentional special behavior for overload resolution of built-in functions. In the case that a function is ambiguous it chooses the first overload from the internal type listings.
In this case the float
-like type list, which has float
as its first element.
See it here in the AST dump:
`-CallExpr <col:10, col:15> 'float'
|-ImplicitCastExpr <col:10> 'float (*)(float)' <FunctionToPointerDecay>
| `-DeclRefExpr <col:10> 'float (float)' lvalue Function 'tan' 'float (float)'
`-ImplicitCastExpr <col:14> 'float' <IntegralToFloating>
`-ImplicitCastExpr <col:14> 'bool' <LValueToRValue>
`-DeclRefExpr <col:14> 'bool' lvalue ParmVar 'B' 'bool'
bool
argument to WaveActiveSum
bool fn(bool B) {
return WaveActiveSum(B);
}
A. bool(bool)
B. int(int)
C. float(float)
D. Error!
Answer!
C!
For a similar reason to the one above, this resolves to a float
overload.
WaveActiveSum
is defined in DXC to take “numeric” types as overloads. bool
is not a numeric type, so there is no exact match. The first type in the numeric
type list is float
, and since bool
can convert to float
that is the
selected overload.
See it here in the AST dump:
`-CallExpr <col:10, col:29> 'bool'
|-ImplicitCastExpr <col:10> 'bool (*)(bool)' <FunctionToPointerDecay>
| `-DeclRefExpr <col:10> 'bool (bool)' lvalue Function 'WaveActiveSum' 'bool (bool)'
`-ImplicitCastExpr <col:28> 'bool' <LValueToRValue>
`-DeclRefExpr <col:28> 'bool' lvalue ParmVar 'B' 'bool'