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'