Przeglądaj źródła

Verify valid intrinsic parameter casts (#3050)

In a couple of cases, intrinsics with parameters that weren't supposed
to allow casts were getting at least one parameter cast to a type not in
the accepted list.

Where an intrinsic referred to another parameter to determine
acceptable types, all parameters fed into a single parameter
representing the common type. If the first parameter was acceptable, but
the second wasn't, the second would get merged with the first regardless
of allowable casts. By checking the parameters before the merging is
performed, these failures are detected.

If an intrinsic call with valid arguments is followed by one
with invalid arguments, the second will be accepted and casts added to
convert to the existing intrinsic because it has already been added to
the list of possible overloads. By disallowing the function matching
code to match intrinsics, this can't happen. What's more, it prevents
confusing error messages when the overload is invalid but existing
overloads have already been seen. The failed overload is all that is
processed so the error is the same as if it were the first use of that
intrinsic.

Even when failures are correctly detected, the messages produced were
less than helpful. By fully defining HLSL intrinsic candidates even if
they fail and adding them to the candidateset, with an indication of
which parameter was faulty, the existing code will report about these
faulty candidates when no others are available.

Reshuffle some type lists to make literals not the default, which leads
to confusing error messages.

Add a bunch of tests for various ways intrinsics can fail due to
parameters.

Fixes #2693
Fixes #818
Greg Roth 5 lat temu
rodzic
commit
b605355c3a

+ 89 - 48
tools/clang/lib/Sema/SemaHLSL.cpp

@@ -39,6 +39,7 @@
 #include "dxc/HLSL/HLOperations.h"
 #include "dxc/DXIL/DxilShaderModel.h"
 #include <array>
+#include <algorithm>
 #include <float.h>
 
 enum ArBasicKind {
@@ -1005,11 +1006,11 @@ static const ArBasicKind g_DoubleOnlyCT[] =
 
 static const ArBasicKind g_NumericCT[] =
 {
-  AR_BASIC_LITERAL_FLOAT,
   AR_BASIC_FLOAT32,
   AR_BASIC_FLOAT32_PARTIAL_PRECISION,
   AR_BASIC_FLOAT16,
   AR_BASIC_FLOAT64,
+  AR_BASIC_LITERAL_FLOAT,
   AR_BASIC_MIN10FLOAT,
   AR_BASIC_MIN16FLOAT,
   AR_BASIC_LITERAL_INT,
@@ -1027,9 +1028,9 @@ static const ArBasicKind g_NumericCT[] =
 
 static const ArBasicKind g_Numeric32CT[] =
 {
-  AR_BASIC_LITERAL_FLOAT,
   AR_BASIC_FLOAT32,
   AR_BASIC_FLOAT32_PARTIAL_PRECISION,
+  AR_BASIC_LITERAL_FLOAT,
   AR_BASIC_LITERAL_INT,
   AR_BASIC_INT32,
   AR_BASIC_UINT32,
@@ -1038,9 +1039,9 @@ static const ArBasicKind g_Numeric32CT[] =
 
 static const ArBasicKind g_Numeric32OnlyCT[] =
 {
-  AR_BASIC_LITERAL_FLOAT,
   AR_BASIC_FLOAT32,
   AR_BASIC_FLOAT32_PARTIAL_PRECISION,
+  AR_BASIC_LITERAL_FLOAT,
   AR_BASIC_LITERAL_INT,
   AR_BASIC_INT32,
   AR_BASIC_UINT32,
@@ -1050,16 +1051,16 @@ static const ArBasicKind g_Numeric32OnlyCT[] =
 
 static const ArBasicKind g_AnyCT[] =
 {
-  AR_BASIC_LITERAL_FLOAT,
   AR_BASIC_FLOAT32,
   AR_BASIC_FLOAT32_PARTIAL_PRECISION,
   AR_BASIC_FLOAT16,
   AR_BASIC_FLOAT64,
+  AR_BASIC_LITERAL_FLOAT,
   AR_BASIC_MIN10FLOAT,
   AR_BASIC_MIN16FLOAT,
-  AR_BASIC_LITERAL_INT,
   AR_BASIC_INT16,
   AR_BASIC_UINT16,
+  AR_BASIC_LITERAL_INT,
   AR_BASIC_INT32,
   AR_BASIC_UINT32,
   AR_BASIC_MIN12INT,
@@ -4184,14 +4185,15 @@ public:
   /// <param name="objectElement">Type element on the class intrinsic belongs to; possibly null (eg, 'float' in 'Texture2D<float>').</param>
   /// <param name="Args">Invocation arguments to match.</param>
   /// <param name="argTypes">After exectuion, type of arguments.</param>
-  /// <param name="argCount">After execution, number of arguments in argTypes.</param>
+  /// <param name="badArgIdx">The first argument to mismatch if any</param>
   /// <remarks>On success, argTypes includes the clang Types to use for the signature, with the first being the return type.</remarks>
   bool MatchArguments(
     const _In_ HLSL_INTRINSIC *pIntrinsic,
     _In_ QualType objectElement,
     _In_ QualType functionTemplateTypeArg,
     _In_ ArrayRef<Expr *> Args, 
-    _Out_ std::vector<QualType> *);
+    _Out_ std::vector<QualType> *,
+    _Out_ size_t &badArgIdx);
 
   /// <summary>Validate object element on intrinsic to catch case like integer on Sample.</summary>
   /// <param name="pIntrinsic">Intrinsic function to validate.</param>
@@ -4264,7 +4266,7 @@ public:
       g_Intrinsics, _countof(g_Intrinsics), StringRef(), nameIdentifier, Args.size());
     IntrinsicDefIter end = IntrinsicDefIter::CreateEnd(
       g_Intrinsics, _countof(g_Intrinsics), IntrinsicTableDefIter::CreateEnd(m_intrinsicTables));
-    while (cursor != end)
+    for (;cursor != end; ++cursor)
     {
       // If this is the intrinsic we're interested in, build up a representation
       // of the types we need.
@@ -4274,12 +4276,12 @@ public:
       DXASSERT(
         pIntrinsic->uNumArgs <= g_MaxIntrinsicParamCount + 1,
         "otherwise g_MaxIntrinsicParamCount needs to be updated for wider signatures");
+
       std::vector<QualType> functionArgTypes;
-      if (!MatchArguments(pIntrinsic, QualType(), QualType(), Args, &functionArgTypes))
-      {
-        ++cursor;
-        continue;
-      }
+      size_t badArgIdx;
+      bool argsMatch = MatchArguments(pIntrinsic, QualType(), QualType(), Args, &functionArgTypes, badArgIdx);
+      if (!functionArgTypes.size())
+        return false;
 
       // Get or create the overload we're interested in.
       FunctionDecl* intrinsicFuncDecl = nullptr;
@@ -4297,12 +4299,21 @@ public:
         intrinsicFuncDecl = (*insertResult.first).getFunctionDecl();
       }
 
-      OverloadCandidate& candidate = CandidateSet.addCandidate();
+      OverloadCandidate& candidate = CandidateSet.addCandidate(Args.size());
       candidate.Function = intrinsicFuncDecl;
       candidate.FoundDecl.setDecl(intrinsicFuncDecl);
-      candidate.Viable = true;
-
-      return true;
+      candidate.Viable = argsMatch;
+      CandidateSet.isNewCandidate(intrinsicFuncDecl); // used to insert into set
+      if (argsMatch)
+        return true;
+      if (badArgIdx) {
+        candidate.FailureKind = ovl_fail_bad_conversion;
+        QualType ParamType = functionArgTypes[badArgIdx];
+        candidate.Conversions[badArgIdx-1].setBad(BadConversionSequence::no_conversion, Args[badArgIdx-1], ParamType);
+      } else {
+        // A less informative error. Needed when the failure relates to the return type
+        candidate.FailureKind = ovl_fail_bad_final_conversion;
+      }
     }
 
     return false;
@@ -5380,22 +5391,23 @@ bool HLSLExternalSource::MatchArguments(
   QualType objectElement,
   QualType functionTemplateTypeArg,
   ArrayRef<Expr *> Args,
-  std::vector<QualType> *argTypesVector)
+  std::vector<QualType> *argTypesVector,
+  size_t &badArgIdx)
 {
   DXASSERT_NOMSG(pIntrinsic != nullptr);
   DXASSERT_NOMSG(argTypesVector != nullptr);
   std::vector<QualType> &argTypes = *argTypesVector;
   argTypes.clear();
-  argTypes.resize(1 + Args.size()); // +1 for return type
   const bool isVariadic = IsVariadicIntrinsicFunction(pIntrinsic);
 
   static const UINT UnusedSize = 0xFF;
   static const BYTE MaxIntrinsicArgs = g_MaxIntrinsicParamCount + 1;
-#define CAB(_) { if (!(_)) return false; }
+#define CAB(cond,arg) { if (!(cond)) { badArgIdx = (arg); return false; } }
 
   ArTypeObjectKind Template[MaxIntrinsicArgs];  // Template type for each argument, AR_TOBJ_UNKNOWN if unspecified.
   ArBasicKind ComponentType[MaxIntrinsicArgs];  // Component type for each argument, AR_BASIC_UNKNOWN if unspecified.
   UINT uSpecialSize[IA_SPECIAL_SLOTS];          // row/col matching types, UNUSED_INDEX32 if unspecified.
+  badArgIdx = MaxIntrinsicArgs;
 
   // Reset infos
   std::fill(Template, Template + _countof(Template), AR_TOBJ_UNKNOWN);
@@ -5408,7 +5420,7 @@ bool HLSLExternalSource::MatchArguments(
   // Populate the template for each argument.
   ArrayRef<Expr*>::iterator iterArg = Args.begin();
   ArrayRef<Expr*>::iterator end = Args.end();
-  unsigned int iArg = 1;
+  size_t iArg = 1;
   for (; iterArg != end; ++iterArg) {
     Expr* pCallArg = *iterArg;
 
@@ -5417,7 +5429,9 @@ bool HLSLExternalSource::MatchArguments(
       break;
 
     // Check bounds for non-variadic functions.
-    if (iArg >= _countof(Template) || iArg > pIntrinsic->uNumArgs) {
+    if (iArg >= MaxIntrinsicArgs || iArg > pIntrinsic->uNumArgs) {
+      // Currently never reached
+      badArgIdx = iArg;
       return false;
     }
 
@@ -5443,6 +5457,7 @@ bool HLSLExternalSource::MatchArguments(
       }
       m_sema->Diag(pCallArg->getExprLoc(),
                    diag::err_hlsl_ray_desc_required);
+      badArgIdx = iArg;
       return false;
     }
 
@@ -5453,6 +5468,7 @@ bool HLSLExternalSource::MatchArguments(
       if (TypeInfoShapeKind != AR_TOBJ_COMPOUND) {
         m_sema->Diag(pCallArg->getExprLoc(),
                      diag::err_hlsl_no_struct_user_defined_type);
+        badArgIdx = iArg;
         return false;
       }
       objectElement = Ty;
@@ -5496,7 +5512,7 @@ bool HLSLExternalSource::MatchArguments(
     case AR_TOBJ_STRING:
       break;
     default:
-      return false; // no struct, arrays or void
+      badArgIdx = std::min(badArgIdx, iArg); // no struct, arrays or void
     }
 
     DXASSERT(
@@ -5507,18 +5523,21 @@ bool HLSLExternalSource::MatchArguments(
     if ((AR_TOBJ_UNKNOWN == Template[pIntrinsicArg->uTemplateId]) ||
         ((AR_TOBJ_SCALAR == Template[pIntrinsicArg->uTemplateId]) &&
          (AR_TOBJ_VECTOR == TypeInfoShapeKind || AR_TOBJ_MATRIX == TypeInfoShapeKind))) {
+      // Unrestricted or truncation of tuples to scalars are allowed
       Template[pIntrinsicArg->uTemplateId] = TypeInfoShapeKind;
     }
     else if (AR_TOBJ_SCALAR == TypeInfoShapeKind) {
       if (AR_TOBJ_SCALAR != Template[pIntrinsicArg->uTemplateId] &&
           AR_TOBJ_VECTOR != Template[pIntrinsicArg->uTemplateId] &&
           AR_TOBJ_MATRIX != Template[pIntrinsicArg->uTemplateId]) {
-        return false;
+        // Scalars to tuples can be splatted, scalar to anything else is not allowed
+        badArgIdx = std::min(badArgIdx, iArg);
       }
     }
     else {
       if (TypeInfoShapeKind != Template[pIntrinsicArg->uTemplateId]) {
-        return false;
+        // Outside of simple splats and truncations, templates must match
+        badArgIdx = std::min(badArgIdx, iArg);
       }
     }
 
@@ -5526,6 +5545,18 @@ bool HLSLExternalSource::MatchArguments(
       pIntrinsicArg->uComponentTypeId < MaxIntrinsicArgs,
       "otherwise intrinsic table was modified and MaxIntrinsicArgs was not updated (or uComponentTypeId is out of bounds)");
 
+    // Verify TypeInfoEltKind can be cast to something legal for this param
+    if (AR_BASIC_UNKNOWN != TypeInfoEltKind) {
+      for (const ArBasicKind *pCT = g_LegalIntrinsicCompTypes[pIntrinsicArg->uLegalComponentTypes];
+           AR_BASIC_UNKNOWN != *pCT; pCT++) {
+        if (TypeInfoEltKind == *pCT)
+          break;
+        else if (*pCT == AR_BASIC_NOCAST) {
+          badArgIdx = std::min(badArgIdx, iArg);
+        }
+      }
+    }
+
     // Merge ComponentTypes
     if (AR_BASIC_UNKNOWN == ComponentType[pIntrinsicArg->uComponentTypeId]) {
       ComponentType[pIntrinsicArg->uComponentTypeId] = TypeInfoEltKind;
@@ -5535,7 +5566,7 @@ bool HLSLExternalSource::MatchArguments(
             ComponentType[pIntrinsicArg->uComponentTypeId],
             TypeInfoEltKind,
             &ComponentType[pIntrinsicArg->uComponentTypeId])) {
-        return false;
+        badArgIdx = std::min(badArgIdx, iArg);
       }
     }
 
@@ -5543,14 +5574,14 @@ bool HLSLExternalSource::MatchArguments(
     if (AR_TOBJ_SCALAR != TypeInfoShapeKind) {
       if (pIntrinsicArg->uRows >= IA_SPECIAL_BASE) {
         UINT uSpecialId = pIntrinsicArg->uRows - IA_SPECIAL_BASE;
-        CAB(uSpecialId < IA_SPECIAL_SLOTS);
+        CAB(uSpecialId < IA_SPECIAL_SLOTS, iArg);
         if (uSpecialSize[uSpecialId] > TypeInfoRows) {
           uSpecialSize[uSpecialId] = TypeInfoRows;
         }
       }
       else {
         if (TypeInfoRows < pIntrinsicArg->uRows) {
-          return false;
+          badArgIdx = std::min(badArgIdx, iArg);
         }
       }
     }
@@ -5559,7 +5590,7 @@ bool HLSLExternalSource::MatchArguments(
     if (AR_TOBJ_SCALAR != TypeInfoShapeKind) {
       if (pIntrinsicArg->uCols >= IA_SPECIAL_BASE) {
         UINT uSpecialId = pIntrinsicArg->uCols - IA_SPECIAL_BASE;
-        CAB(uSpecialId < IA_SPECIAL_SLOTS);
+        CAB(uSpecialId < IA_SPECIAL_SLOTS, iArg);
 
         if (uSpecialSize[uSpecialId] > TypeInfoCols) {
           uSpecialSize[uSpecialId] = TypeInfoCols;
@@ -5567,7 +5598,7 @@ bool HLSLExternalSource::MatchArguments(
       }
       else {
         if (TypeInfoCols < pIntrinsicArg->uCols) {
-          return false;
+          badArgIdx = std::min(badArgIdx, iArg);
         }
       }
     }
@@ -5576,7 +5607,7 @@ bool HLSLExternalSource::MatchArguments(
     if (pIntrinsicArg->qwUsage & AR_QUAL_OUT) {
       if (pCallArg->getType().isConstQualified()) {
         // Can't use a const type in an out or inout parameter.
-        return false;
+        badArgIdx = std::min(badArgIdx, iArg);
       }
     }
 
@@ -5590,7 +5621,7 @@ bool HLSLExternalSource::MatchArguments(
   if (pIntrinsic->pArgs[0].qwUsage
     && pIntrinsic->pArgs[0].uTemplateId != INTRIN_TEMPLATE_FROM_TYPE
     && pIntrinsic->pArgs[0].uTemplateId != INTRIN_TEMPLATE_FROM_FUNCTION) {
-    CAB(pIntrinsic->pArgs[0].uTemplateId < MaxIntrinsicArgs);
+    CAB(pIntrinsic->pArgs[0].uTemplateId < MaxIntrinsicArgs, 0);
     if (AR_TOBJ_UNKNOWN == Template[pIntrinsic->pArgs[0].uTemplateId]) {
       Template[pIntrinsic->pArgs[0].uTemplateId] =
         g_LegalIntrinsicTemplates[pIntrinsic->pArgs[0].uLegalTemplates][0];
@@ -5647,9 +5678,11 @@ bool HLSLExternalSource::MatchArguments(
         }
       }
 
-      if (AR_TOBJ_UNKNOWN == *pTT)
-        return false;
+      if (AR_TOBJ_UNKNOWN == *pTT) {
+        Template[i] = g_LegalIntrinsicTemplates[pArgument->uLegalTemplates][0];
+        badArgIdx = std::min(badArgIdx, i);
       }
+    }
     else if (pTT) {
       Template[i] = *pTT;
     }
@@ -5664,14 +5697,17 @@ bool HLSLExternalSource::MatchArguments(
       }
 
       // has to be a strict match
-      if (*pCT == AR_BASIC_NOCAST)
-        return false;
+      if (*pCT == AR_BASIC_NOCAST) {
+        badArgIdx = std::min(badArgIdx, i);
+        // the match has failed, but the types are useful for errors. Present the cannonical overload for error
+        ComponentType[i] = g_LegalIntrinsicCompTypes[pArgument->uLegalComponentTypes][0];
+      }
 
       // If it is an object, see if it can be cast to the first thing in the 
       // list, otherwise move on to next intrinsic.
       if (AR_TOBJ_OBJECT == Template[i] && AR_BASIC_UNKNOWN == *pCT) {
         if (!CombineObjectTypes(g_LegalIntrinsicCompTypes[pArgument->uLegalComponentTypes][0], ComponentType[i], nullptr)) {
-          return false;
+          badArgIdx = std::min(badArgIdx, i);
         }
       }
 
@@ -5684,6 +5720,8 @@ bool HLSLExternalSource::MatchArguments(
     }
   }
 
+  argTypes.resize(1 + Args.size()); // +1 for return type
+
   // Default to a void return type.
   argTypes[0] = m_context->VoidTy;
 
@@ -5720,7 +5758,7 @@ bool HLSLExternalSource::MatchArguments(
         // now it only supports scalars
         if (pArgument->uRows >= IA_SPECIAL_BASE) {
           UINT uSpecialId = pArgument->uRows - IA_SPECIAL_BASE;
-          CAB(uSpecialId < IA_SPECIAL_SLOTS);
+          CAB(uSpecialId < IA_SPECIAL_SLOTS, i);
 
           uRows = uSpecialSize[uSpecialId];
         }
@@ -5730,7 +5768,7 @@ bool HLSLExternalSource::MatchArguments(
 
         if (pArgument->uCols >= IA_SPECIAL_BASE) {
           UINT uSpecialId = pArgument->uCols - IA_SPECIAL_BASE;
-          CAB(uSpecialId < IA_SPECIAL_SLOTS);
+          CAB(uSpecialId < IA_SPECIAL_SLOTS, i);
 
           uCols = uSpecialSize[uSpecialId];
         }
@@ -5743,7 +5781,7 @@ bool HLSLExternalSource::MatchArguments(
         if ((1 == uCols) && (1 == uRows)) {
           pNewType = objectElement;
           if (pNewType.isNull()) {
-            return false;
+            badArgIdx = std::min(badArgIdx, i);
           }
         }
         else {
@@ -5753,13 +5791,13 @@ bool HLSLExternalSource::MatchArguments(
           // given type
 
           // VH(E_NOTIMPL);
-          return false;
+          badArgIdx = std::min(badArgIdx, i);
         }
       }
       else {
         DXASSERT_NOMSG(!pArgument->uRows && !pArgument->uCols);
         if (objectElement.isNull()) {
-          return false;
+          badArgIdx = std::min(badArgIdx, i);
         }
         pNewType = objectElement;
       }
@@ -5790,7 +5828,7 @@ bool HLSLExternalSource::MatchArguments(
     }
     else if (pArgument->uLegalComponentTypes == LICOMPTYPE_USER_DEFINED_TYPE) {
       if (objectElement.isNull()) {
-        return false;
+        badArgIdx = std::min(badArgIdx, i);
       }
       pNewType = objectElement;
     }
@@ -5807,11 +5845,13 @@ bool HLSLExternalSource::MatchArguments(
       // can use more specials, etc.
       if (pArgument->uComponentTypeId == INTRIN_COMPTYPE_FROM_TYPE_ELT0) {
         if (objectElement.isNull()) {
+          badArgIdx = std::min(badArgIdx, i);
           return false;
         }
         pEltType = GetTypeElementKind(objectElement);
         if (!IsValidBasicKind(pEltType)) {
           // This can happen with Texture2D<Struct> or other invalid declarations
+          badArgIdx = std::min(badArgIdx, i);
           return false;
         }
       }
@@ -5825,7 +5865,7 @@ bool HLSLExternalSource::MatchArguments(
       // Rows
       if (pArgument->uRows >= IA_SPECIAL_BASE) {
         UINT uSpecialId = pArgument->uRows - IA_SPECIAL_BASE;
-        CAB(uSpecialId < IA_SPECIAL_SLOTS);
+        CAB(uSpecialId < IA_SPECIAL_SLOTS, i);
         uRows = uSpecialSize[uSpecialId];
       }
       else {
@@ -5835,7 +5875,7 @@ bool HLSLExternalSource::MatchArguments(
       // Cols
       if (pArgument->uCols >= IA_SPECIAL_BASE) {
         UINT uSpecialId = pArgument->uCols - IA_SPECIAL_BASE;
-        CAB(uSpecialId < IA_SPECIAL_SLOTS);
+        CAB(uSpecialId < IA_SPECIAL_SLOTS, i);
         uCols = uSpecialSize[uSpecialId];
       }
       else {
@@ -5843,7 +5883,7 @@ bool HLSLExternalSource::MatchArguments(
       }
 
       // Verify that the final results are in bounds.
-      CAB(uCols > 0 && uCols <= MaxVectorSize && uRows > 0 && uRows <= MaxVectorSize);
+      CAB(uCols > 0 && uCols <= MaxVectorSize && uRows > 0 && uRows <= MaxVectorSize, i);
 
       // Const
       UINT64 qwQual = pArgument->qwUsage & (AR_QUAL_ROWMAJOR | AR_QUAL_COLMAJOR);
@@ -5875,7 +5915,7 @@ bool HLSLExternalSource::MatchArguments(
              "have as many arguments and types as the intrinsic template");
   }
 
-  return true;
+  return badArgIdx == MaxIntrinsicArgs;
 #undef CAB
 }
 
@@ -9268,7 +9308,8 @@ Sema::TemplateDeductionResult HLSLExternalSource::DeduceTemplateArgumentsForHLSL
 
   while (cursor != end)
   {
-    if (!MatchArguments(*cursor, objectElement, functionTemplateTypeArg, Args, &argTypes))
+    size_t badArgIdx;
+    if (!MatchArguments(*cursor, objectElement, functionTemplateTypeArg, Args, &argTypes, badArgIdx))
     {
       ++cursor;
       continue;

+ 2 - 1
tools/clang/lib/Sema/SemaOverload.cpp

@@ -29,6 +29,7 @@
 #include "clang/Sema/Template.h"
 #include "clang/Sema/TemplateDeduction.h"
 #include "clang/Sema/SemaHLSL.h" // HLSL Change
+#include "clang/AST/HlslTypes.h" // HLSL Change
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
@@ -5741,7 +5742,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
     // argument doesn't participate in overload resolution.
   }
 
-  if (!CandidateSet.isNewCandidate(Function))
+  if (!CandidateSet.isNewCandidate(Function) || hlsl::IsIntrinsicOp(Function))
     return;
 
   // C++ [over.match.oper]p3:

+ 6 - 5
tools/clang/test/HLSL/wave.hlsl

@@ -41,11 +41,12 @@ float4 main() : SV_Target {
   f3 = WaveActiveSum(f3);
   f3 = WaveActiveProduct(f3);
   // WaveActiveBit* with an invalid signature suggests the use of WaveActiveBallot instead.
-  // expected-note@? {{'WaveActiveBallot' declared here}}
-  // expected-note@? {{'WaveActiveBallot' declared here}}
-  WaveActiveBitAnd(f1); // expected-error {{use of undeclared identifier 'WaveActiveBitAnd'}}
-  WaveActiveBitOr(f1);  // expected-error {{use of undeclared identifier 'WaveActiveBitOr'}}
-  WaveActiveBitXor(f1); // expected-error {{use of undeclared identifier 'WaveActiveBitXor'}}
+  // expected-note@? {{candidate function}}
+  // expected-note@? {{candidate function}}
+  // expected-note@? {{candidate function}}
+  WaveActiveBitAnd(f1); // expected-error {{no matching function for call to 'WaveActiveBitAnd'}}
+  WaveActiveBitOr(f1);  // expected-error {{no matching function for call to 'WaveActiveBitOr'}}
+  WaveActiveBitXor(f1); // expected-error {{no matching function for call to 'WaveActiveBitXor'}}
   u3 = WaveActiveBitAnd(u3);
   u3 = WaveActiveBitOr(u3);
   u3 = WaveActiveBitXor(u3);

+ 11 - 4
tools/clang/test/HLSLFileCheck/hlsl/functions/overloading/intrinsic_overloading.hlsl

@@ -1,17 +1,24 @@
 // RUN: %dxc -E main -T vs_6_0 %s | FileCheck %s
 
 // Test that intrinsics can be overloaded without
-// shadowing the original definition.
+// shadowing the original definition using incorrect type shapes
+// and also disallowed content casts
 
 // CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 0, i32 42)
 // CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 1, i32 1)
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 2, i32 1)
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 3, i32 0)
 
 struct Struct { int x; };
 int abs(Struct s) { return 42; }
+int asint(bool b) { return int(b); }
 
-int2 main() : OUT
+int4 main() : OUT
 {
     Struct s = { -1 };
-    return int2(abs(s), // Should call struct overload
-        abs(-1)); // Should call intrinsic
+    bool b = true;
+    return int4(abs(s), // Should call struct overload
+        abs(-1), // Should call intrinsic
+        asint(b), // Should call bool overload
+        asint(0.0));// Should call intrinsic
 }

+ 130 - 0
tools/clang/test/HLSLFileCheck/hlsl/functions/overloading/intrinsic_overloading_errors.hlsl

@@ -0,0 +1,130 @@
+// RUN: %dxc -T lib_6_4 %s | FileCheck %s
+
+// This tests intrinsic overloads using parameter combinations that should fail, but didn't
+// Uses intrinsics with *_NOCAST parameters in various ways that should be prohibited
+
+//CHECK: error: no matching function for call to 'fma'
+//CHECK: note: candidate function not viable: no known conversion from 'float' to 'double' for 3rd argument
+// Verifies that $type parameters in intrinsic parameters doesn't allow them to be cast when they shouldn't
+export
+double MismatchedIntrins(double d1, double d2, float f) {
+  return fma(d1, d2, f);
+}
+
+// Another example of the above as cited in #2693. Not fixed with the above due to arg count
+
+//CHECK: error: no matching function for call to 'asfloat16'
+//CHECK: note: candidate function not viable: no known conversion from 'uint' to 'half' for 1st argument
+RWByteAddressBuffer buf;
+void asfloat_16()
+{
+    buf.Store(0, asfloat16(uint(1)));
+}
+
+// Check that a valid intrinsic call doesn't allow a subsequent invalid call to pass
+//CHECK: error: no matching function for call to 'fma'
+//CHECK: note: candidate function not viable: no known conversion from 'float' to 'double' for 1st argument
+export
+double InvalidAfterValidIntrins(double d1, double d2, double d3,
+                                float f1, float f2, float f3) {
+  // This is valid and would let the next, invalid call slip through
+  double ret = fma(d1, d2, d3);
+  ret += fma(f1, f2, f3);
+  return ret;
+}
+
+// Taken from issue #818. Used to produce ambiguous call errors even though the overloads are invalid
+// CHECK: error: no matching function for call to 'asfloat'
+// CHECK: note: candidate function not viable: no known conversion from 'const vector<double, 4>' to 'vector<float, 4>' for 1st argument
+// CHECK: error: no matching function for call to 'asfloat'
+// CHECK: note: candidate function not viable: no known conversion from 'const vector<int64_t, 4>' to 'vector<float, 4>' for 1st argument
+// CHECK: error: no matching function for call to 'asfloat'
+// CHECK: note: candidate function not viable: no known conversion from 'const vector<uint64_t, 4>' to 'vector<float, 4>' for 1st argument
+float4 g_f;
+int4 g_i;
+uint4 g_u;
+double4 g_f64;
+int64_t4 g_i64;
+uint64_t4 g_u64;
+
+float4 AsFloatOverloads() {
+  float4 f1 = 0;
+  f1 += asfloat(g_f);
+  f1 += asfloat(g_i);
+  f1 += asfloat(g_u);
+  f1 += asfloat(g_f64);
+  f1 += asfloat(g_i64);
+  f1 += asfloat(g_u64);
+  return 1;
+}
+
+// Test using a struct somewhere it shouldn't be
+// CHECK: error: no matching function for call to 'abs'
+// CHECK: note: candidate function not viable: no known conversion from 'Struct' to 'float' for 1st argument
+struct Struct { int x; };
+export
+int StructOverload()
+{
+  Struct s = { -1 };
+  return abs(s);
+}
+// Test using a array somewhere it shouldn't be
+// CHECK: error: no matching function for call to 'sign'
+// CHECK: note: candidate function not viable: no known conversion from 'float [3]' to 'float' for 1st argument
+export
+int ArrayOverload()
+{
+  float arr[] = {1, -2, -3};
+  return sign(arr);
+}
+
+// CHECK: error: no matching function for call to 'saturate'
+// Test using a string somewhere it really shouldn't be
+// CHECK: note: candidate function not viable: no known conversion from 'literal string' to 'float' for 1st argument
+export
+int StringOverload()
+{
+  return saturate("sponge");
+}
+
+// Test using a too short vector on an explicit intrinsic arg
+// CHECK: error: no matching function for call to 'cross'
+// CHECK: note: candidate function not viable: no known conversion from 'vector<float, 2>' to 'vector<float, 3>' for 2nd argument
+export
+float3 TooShortCast()
+{
+  float3 hot = {4,5,1};
+  float2 bun = {0,0};
+  return cross(hot, bun);
+}
+
+// Test const on an out param
+// CHECK: error: no matching function for call to 'frexp'
+// CHECK: note: candidate function not viable: 2nd argument ('const float') would lose const qualifier
+export
+float ConstCast()
+{
+  float pi = 3.1415926535897932;
+  const float mmmm = 0;
+  return frexp(pi, mmmm);
+}
+#if 0
+// Tests for #2507, not yet fixed.
+// To fix 2507, we need to change how we retrieve intrinsics.
+// Currently, they are searched linearly with number of args being the first rejection criteria
+// This doesn't allow detecting usage of one that matches by name, but differs by number
+
+// Test instrinsic with too few arguments
+export
+float TooFewArgs()
+{
+  return atan2(0.0);
+}
+
+// Test instrinsic with too many arguments
+export
+float TooManyArgs()
+{
+  return atan(1.1, 2.2);
+}
+#endif