bool Sema::DiagnoseHLSLDecl()

in tools/clang/lib/Sema/SemaHLSL.cpp [12523:13087]


bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth,
                            TypeSourceInfo *TInfo, bool isParameter) {
  assert(getLangOpts().HLSL &&
         "otherwise this is called without checking language first");

  // If we have a template declaration but haven't enabled templates, error.
  if (DC->isDependentContext() && !getLangOpts().EnableTemplates) return false;

  DeclSpec::SCS storage = D.getDeclSpec().getStorageClassSpec();
  assert(!DC->isClosure() && "otherwise parser accepted closure syntax instead of failing with a syntax error");

  bool result = true;
  bool isTypedef = storage == DeclSpec::SCS_typedef;
  bool isFunction = D.isFunctionDeclarator() && !DC->isRecord();
  bool isLocalVar = DC->isFunctionOrMethod() && !isFunction && !isTypedef;
  bool isGlobal = !isParameter && !isTypedef && !isFunction && (DC->isTranslationUnit() || DC->isNamespace() || DC->getDeclKind() == Decl::HLSLBuffer);
  bool isMethod = DC->isRecord() && D.isFunctionDeclarator() && !isTypedef;
  bool isField = DC->isRecord() && !D.isFunctionDeclarator() && !isTypedef;

  bool isConst = D.getDeclSpec().getTypeQualifiers() & DeclSpec::TQ::TQ_const;
  bool isVolatile = D.getDeclSpec().getTypeQualifiers() & DeclSpec::TQ::TQ_volatile;
  bool isStatic = storage == DeclSpec::SCS::SCS_static;
  bool isExtern = storage == DeclSpec::SCS::SCS_extern;

  bool hasSignSpec = D.getDeclSpec().getTypeSpecSign() != DeclSpec::TSS::TSS_unspecified;

  // Function declarations are not allowed in parameter declaration
  // TODO : Remove this check once we support function declarations/pointers in HLSL
  if (isParameter && isFunction) {
      Diag(D.getLocStart(), diag::err_hlsl_func_in_func_decl);
      D.setInvalidType();
      return false;
  }

  assert(
    (1 == (isLocalVar ? 1 : 0) + (isGlobal ? 1 : 0) + (isField ? 1 : 0) +
    (isTypedef ? 1 : 0) + (isFunction ? 1 : 0) + (isMethod ? 1 : 0) +
    (isParameter ? 1 : 0))
    && "exactly one type of declarator is being processed");

  // qt/pType captures either the type being modified, or the return type in the
  // case of a function (or method).
  QualType qt = TInfo->getType();
  const Type* pType = qt.getTypePtrOrNull();
  HLSLExternalSource *hlslSource = HLSLExternalSource::FromSema(this);

  // Early checks - these are not simple attribution errors, but constructs that
  // are fundamentally unsupported,
  // and so we avoid errors that might indicate they can be repaired.
  if (DC->isRecord()) {
    unsigned int nestedDiagId = 0;
    if (isTypedef) {
      nestedDiagId = diag::err_hlsl_unsupported_nested_typedef;
    }

    if (isField && pType && pType->isIncompleteArrayType()) {
      nestedDiagId = diag::err_hlsl_unsupported_incomplete_array;
    }

    if (nestedDiagId) {
      Diag(D.getLocStart(), nestedDiagId);
      D.setInvalidType();
      return false;
    }
  }

  // String and subobject declarations are supported only as top level global variables.
  // Const and static modifiers are implied - add them if missing.
  if ((hlsl::IsStringType(qt) || hlslSource->IsSubobjectType(qt)) && !D.isInvalidType()) {
    // string are supported only as top level global variables
    if (!DC->isTranslationUnit()) {
      Diag(D.getLocStart(), diag::err_hlsl_object_not_global) << (int)hlsl::IsStringType(qt);
      result = false;
    }
    if (isExtern) {
      Diag(D.getLocStart(), diag::err_hlsl_object_extern_not_supported) << (int)hlsl::IsStringType(qt);
      result = false;
    }
    const char *PrevSpec = nullptr;
    unsigned DiagID = 0;
    if (!isStatic) {
      D.getMutableDeclSpec().SetStorageClassSpec(*this, DeclSpec::SCS_static, D.getLocStart(), PrevSpec, DiagID, Context.getPrintingPolicy());
      isStatic = true;
    }
    if (!isConst) {
      D.getMutableDeclSpec().SetTypeQual(DeclSpec::TQ_const, D.getLocStart(), PrevSpec, DiagID, getLangOpts());
      isConst = true;
    }
  }

  const char* declarationType =
    (isLocalVar) ? "local variable" :
    (isTypedef) ? "typedef" :
    (isFunction) ? "function" :
    (isMethod) ? "method" :
    (isGlobal) ? "global variable" :
    (isParameter) ? "parameter" :
    (isField) ? "field" : "<unknown>";

  if (pType && D.isFunctionDeclarator()) {
    const FunctionProtoType *pFP = pType->getAs<FunctionProtoType>();
    if (pFP) {
      qt = pFP->getReturnType();
      pType = qt.getTypePtrOrNull();

      // prohibit string as a return type
      if (hlsl::IsStringType(qt)) {
        static const unsigned selectReturnValueIdx = 2;
        Diag(D.getLocStart(), diag::err_hlsl_unsupported_string_decl) << selectReturnValueIdx;
        D.setInvalidType();
      }
    }
  }

  // Check for deprecated effect object type here, warn, and invalidate decl
  bool bDeprecatedEffectObject = false;
  bool bIsObject = false;
  if (hlsl::IsObjectType(this, qt, &bDeprecatedEffectObject)) {
    bIsObject = true;
    if (bDeprecatedEffectObject) {
      Diag(D.getLocStart(), diag::warn_hlsl_effect_object);
      D.setInvalidType();
      return false;
    }
    // Add methods if not ready.
    hlslSource->AddHLSLObjectMethodsIfNotReady(qt);
  } else if (qt->isArrayType()) {
    QualType eltQt(qt->getArrayElementTypeNoTypeQual(), 0);
    while (eltQt->isArrayType())
      eltQt = QualType(eltQt->getArrayElementTypeNoTypeQual(), 0);

    if (hlsl::IsObjectType(this, eltQt, &bDeprecatedEffectObject)) {
      // Add methods if not ready.
      hlslSource->AddHLSLObjectMethodsIfNotReady(eltQt);
      bIsObject = true;
    }
  }

  if (isExtern) {
    if (!(isFunction || isGlobal)) {
      Diag(D.getLocStart(), diag::err_hlsl_varmodifierna) << "'extern'"
                                                          << declarationType;
      result = false;
    }
  }

  if (isStatic) {
    if (!(isLocalVar || isGlobal || isFunction || isMethod || isField)) {
      Diag(D.getLocStart(), diag::err_hlsl_varmodifierna) << "'static'"
                                                          << declarationType;
      result = false;
    }
  }

  if (isVolatile) {
    if (!(isLocalVar || isTypedef)) {
      Diag(D.getLocStart(), diag::err_hlsl_varmodifierna) << "'volatile'"
                                                          << declarationType;
      result = false;
    }
  }

  if (isConst) {
    if (isField && !isStatic) {
      Diag(D.getLocStart(), diag::err_hlsl_varmodifierna) << "'const'"
                                                          << declarationType;
      result = false;
    }
  }

  ArBasicKind basicKind = hlslSource->GetTypeElementKind(qt);

  if (hasSignSpec) {
     ArTypeObjectKind objKind = hlslSource->GetTypeObjectKind(qt);
     // vectors or matrices can only have unsigned integer types.
     if (objKind == AR_TOBJ_MATRIX || objKind == AR_TOBJ_VECTOR || objKind == AR_TOBJ_BASIC || objKind == AR_TOBJ_ARRAY) {
         if (!IS_BASIC_UNSIGNABLE(basicKind)) {
             Diag(D.getLocStart(), diag::err_sema_invalid_sign_spec)
                 << g_ArBasicTypeNames[basicKind];
             result = false;
         }
     }
     else {
         Diag(D.getLocStart(), diag::err_sema_invalid_sign_spec) << g_ArBasicTypeNames[basicKind];
         result = false;
     }
  }

  // Validate attributes
  clang::AttributeList
    *pUniform = nullptr,
    *pUsage = nullptr,
    *pNoInterpolation = nullptr,
    *pLinear = nullptr,
    *pNoPerspective = nullptr,
    *pSample = nullptr,
    *pCentroid = nullptr,
    *pCenter = nullptr,
    *pAnyLinear = nullptr,                   // first linear attribute found
    *pTopology = nullptr,
    *pMeshModifier = nullptr;
  bool usageIn = false;
  bool usageOut = false;

  for (clang::AttributeList *pAttr = D.getDeclSpec().getAttributes().getList();
       pAttr != NULL; pAttr = pAttr->getNext()) {
    if (pAttr->isInvalid() || pAttr->isUsedAsTypeAttr())
      continue;

    switch (pAttr->getKind()) {
    case AttributeList::AT_HLSLPrecise: // precise is applicable everywhere.
      break;
    case AttributeList::AT_HLSLShared:
      if (!isGlobal) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
            << pAttr->getName() << declarationType << pAttr->getRange();
        result = false;
      }
      if (isStatic) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifiersna)
            << "'static'" << pAttr->getName() << declarationType
            << pAttr->getRange();
        result = false;
      }
      break;
    case AttributeList::AT_HLSLGroupShared:
      if (!isGlobal) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
            << pAttr->getName() << declarationType << pAttr->getRange();
        result = false;
      }
      if (isExtern) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifiersna)
            << "'extern'" << pAttr->getName() << declarationType
            << pAttr->getRange();
        result = false;
      }
      break;
    case AttributeList::AT_HLSLGloballyCoherent:
      if (!bIsObject) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
            << pAttr->getName() << "non-UAV type";
        result = false;
      }
      break;
    case AttributeList::AT_HLSLUniform:
      if (!(isGlobal || isParameter)) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
            << pAttr->getName() << declarationType << pAttr->getRange();
        result = false;
      }
      if (isStatic) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifiersna)
            << "'static'" << pAttr->getName() << declarationType
            << pAttr->getRange();
        result = false;
      }
      pUniform = pAttr;
      break;

    case AttributeList::AT_HLSLIn:
    case AttributeList::AT_HLSLOut:
    case AttributeList::AT_HLSLInOut:
      if (!isParameter) {
        Diag(pAttr->getLoc(), diag::err_hlsl_usage_not_on_parameter)
            << pAttr->getName() << pAttr->getRange();
        result = false;
      }
      if (!IsUsageAttributeCompatible(pAttr->getKind(), usageIn, usageOut)) {
        Diag(pAttr->getLoc(), diag::err_hlsl_duplicate_parameter_usages)
            << pAttr->getName() << pAttr->getRange();
        result = false;
      }
      pUsage = pAttr;
      break;

    case AttributeList::AT_HLSLNoInterpolation:
      if (!(isParameter || isField || isFunction)) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
            << pAttr->getName() << declarationType << pAttr->getRange();
        result = false;
      }
      if (pNoInterpolation) {
        Diag(pAttr->getLoc(), diag::warn_hlsl_duplicate_specifier)
            << pAttr->getName() << pAttr->getRange();
      }
      pNoInterpolation = pAttr;
      break;

    case AttributeList::AT_HLSLLinear:
    case AttributeList::AT_HLSLCenter:
    case AttributeList::AT_HLSLNoPerspective:
    case AttributeList::AT_HLSLSample:
    case AttributeList::AT_HLSLCentroid:
      if (!(isParameter || isField || isFunction)) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
            << pAttr->getName() << declarationType << pAttr->getRange();
        result = false;
      }

      if (nullptr == pAnyLinear)
        pAnyLinear = pAttr;

      switch (pAttr->getKind()) {
      case AttributeList::AT_HLSLLinear:
        if (pLinear) {
          Diag(pAttr->getLoc(), diag::warn_hlsl_duplicate_specifier)
              << pAttr->getName() << pAttr->getRange();
        }
        pLinear = pAttr;
        break;
      case AttributeList::AT_HLSLCenter:
        if (pCenter) {
          Diag(pAttr->getLoc(), diag::warn_hlsl_duplicate_specifier)
            << pAttr->getName() << pAttr->getRange();
        }
        pCenter = pAttr;
        break;
      case AttributeList::AT_HLSLNoPerspective:
        if (pNoPerspective) {
          Diag(pAttr->getLoc(), diag::warn_hlsl_duplicate_specifier)
              << pAttr->getName() << pAttr->getRange();
        }
        pNoPerspective = pAttr;
        break;
      case AttributeList::AT_HLSLSample:
        if (pSample) {
          Diag(pAttr->getLoc(), diag::warn_hlsl_duplicate_specifier)
              << pAttr->getName() << pAttr->getRange();
        }
        pSample = pAttr;
        break;
      case AttributeList::AT_HLSLCentroid:
        if (pCentroid) {
          Diag(pAttr->getLoc(), diag::warn_hlsl_duplicate_specifier)
              << pAttr->getName() << pAttr->getRange();
        }
        pCentroid = pAttr;
        break;
      default:
        // Only relevant to the four attribs included in this block.
        break;
      }
      break;

    case AttributeList::AT_HLSLPoint:
    case AttributeList::AT_HLSLLine:
    case AttributeList::AT_HLSLLineAdj:
    case AttributeList::AT_HLSLTriangle:
    case AttributeList::AT_HLSLTriangleAdj:
      if (!(isParameter)) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
          << pAttr->getName() << declarationType << pAttr->getRange();
        result = false;
      }

      if (pTopology) {
        if (pTopology->getKind() == pAttr->getKind()) {
          Diag(pAttr->getLoc(), diag::warn_hlsl_duplicate_specifier)
            << pAttr->getName() << pAttr->getRange();
        } else {
          Diag(pAttr->getLoc(), diag::err_hlsl_varmodifiersna)
            << pAttr->getName() << pTopology->getName()
            << declarationType << pAttr->getRange();
          result = false;
        }
      }
      pTopology = pAttr;
      break;

    case AttributeList::AT_HLSLExport:
      if (!isFunction) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
          << pAttr->getName() << declarationType << pAttr->getRange();
        result = false;
      }
      if (isStatic) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifiersna)
          << "'static'" << pAttr->getName() << declarationType
          << pAttr->getRange();
        result = false;
      }
      break;

    case AttributeList::AT_HLSLIndices:
    case AttributeList::AT_HLSLVertices:
    case AttributeList::AT_HLSLPrimitives:
    case AttributeList::AT_HLSLPayload:
      if (!(isParameter)) {
        Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
          << pAttr->getName() << declarationType << pAttr->getRange();
        result = false;
      }
      if (pMeshModifier) {
        if (pMeshModifier->getKind() == pAttr->getKind()) {
          Diag(pAttr->getLoc(), diag::warn_hlsl_duplicate_specifier)
            << pAttr->getName() << pAttr->getRange();
        } else {
          Diag(pAttr->getLoc(), diag::err_hlsl_varmodifiersna)
            << pAttr->getName() << pMeshModifier->getName()
            << declarationType << pAttr->getRange();
          result = false;
        }
      }
      pMeshModifier = pAttr;
      break;

    default:
      break;
    }
  }

  if (pNoInterpolation && pAnyLinear) {
    Diag(pNoInterpolation->getLoc(), diag::err_hlsl_varmodifiersna)
        << pNoInterpolation->getName() << pAnyLinear->getName()
        << declarationType << pNoInterpolation->getRange();
    result = false;
  }
  if (pSample && pCentroid) {
    Diag(pCentroid->getLoc(), diag::warn_hlsl_specifier_overridden)
        << pCentroid->getName() << pSample->getName() << pCentroid->getRange();
  }
  if (pCenter && pCentroid) {
    Diag(pCenter->getLoc(), diag::warn_hlsl_specifier_overridden)
      << pCenter->getName() << pCentroid->getName() << pCenter->getRange();
  }
  if (pSample && pCenter) {
    Diag(pCenter->getLoc(), diag::warn_hlsl_specifier_overridden)
      << pCenter->getName() << pSample->getName() << pCenter->getRange();
  }
  clang::AttributeList *pNonUniformAttr = pAnyLinear ? pAnyLinear : (
    pNoInterpolation ? pNoInterpolation : pTopology);
  if (pUniform && pNonUniformAttr) {
    Diag(pUniform->getLoc(), diag::err_hlsl_varmodifiersna)
        << pNonUniformAttr->getName()
        << pUniform->getName() << declarationType << pUniform->getRange();
    result = false;
  }
  if (pAnyLinear && pTopology) {
    Diag(pAnyLinear->getLoc(), diag::err_hlsl_varmodifiersna)
      << pTopology->getName()
      << pAnyLinear->getName() << declarationType << pAnyLinear->getRange();
    result = false;
  }
  if (pNoInterpolation && pTopology) {
    Diag(pNoInterpolation->getLoc(), diag::err_hlsl_varmodifiersna)
      << pTopology->getName()
      << pNoInterpolation->getName() << declarationType << pNoInterpolation->getRange();
    result = false;
  }
  if (pUniform && pUsage) {
    if (pUsage->getKind() != AttributeList::Kind::AT_HLSLIn) {
      Diag(pUniform->getLoc(), diag::err_hlsl_varmodifiersna)
          << pUsage->getName() << pUniform->getName() << declarationType
          << pUniform->getRange();
      result = false;
    }
  }
  if (pMeshModifier) {
    if (pMeshModifier->getKind() == AttributeList::Kind::AT_HLSLPayload) {
      if (!usageIn) {
        Diag(D.getLocStart(), diag::err_hlsl_missing_in_attr)
            << pMeshModifier->getName();
        result = false;
      }
    } else {
      if (!usageOut) {
        Diag(D.getLocStart(), diag::err_hlsl_missing_out_attr)
            << pMeshModifier->getName();
        result = false;
      }
    }
  }

  // Validate that stream-ouput objects are marked as inout
  if (isParameter && !(usageIn && usageOut) &&
      (basicKind == ArBasicKind::AR_OBJECT_LINESTREAM ||
       basicKind == ArBasicKind::AR_OBJECT_POINTSTREAM ||
       basicKind == ArBasicKind::AR_OBJECT_TRIANGLESTREAM)) {
    Diag(D.getLocStart(), diag::err_hlsl_missing_inout_attr);
    result = false;
  }

  // SPIRV change starts
#ifdef ENABLE_SPIRV_CODEGEN
  // Validate that Vulkan specific feature is only used when targeting SPIR-V
  if (!getLangOpts().SPIRV) {
    if (basicKind == ArBasicKind::AR_OBJECT_VK_SUBPASS_INPUT ||
        basicKind == ArBasicKind::AR_OBJECT_VK_SUBPASS_INPUT_MS ||
        basicKind == ArBasicKind::AR_OBJECT_VK_SPV_INTRINSIC_TYPE ||
        basicKind == ArBasicKind::AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID) {
      Diag(D.getLocStart(), diag::err_hlsl_vulkan_specific_feature)
          << g_ArBasicTypeNames[basicKind];
      result = false;
    }
  }
#endif // ENABLE_SPIRV_CODEGEN
  // SPIRV change ends

  // Disallow bitfields where not enabled explicitly or by HV
  if (BitWidth && !getLangOpts().EnableBitfields) {
    Diag(BitWidth->getExprLoc(), diag::err_hlsl_bitfields);
    result = false;
  }

  // Validate unusual annotations.
  hlsl::DiagnoseUnusualAnnotationsForHLSL(*this, D.UnusualAnnotations);
  if (isField)
    hlsl::DiagnosePayloadAccessQualifierAnnotations(*this, D, qt,
                                                    D.UnusualAnnotations);
  auto && unusualIter = D.UnusualAnnotations.begin();
  auto && unusualEnd = D.UnusualAnnotations.end();
  for (; unusualIter != unusualEnd; ++unusualIter) {
    switch ((*unusualIter)->getKind()) {
    case hlsl::UnusualAnnotation::UA_ConstantPacking: {
      hlsl::ConstantPacking *constantPacking =
          cast<hlsl::ConstantPacking>(*unusualIter);
      if (!isGlobal || HLSLBuffers.size() == 0) {
        Diag(constantPacking->Loc, diag::err_hlsl_packoffset_requires_cbuffer);
        continue;
      }
      if (constantPacking->ComponentOffset > 0) {
        // Validate that this will fit.
        if (!qt.isNull()) {
          hlsl::DiagnosePackingOffset(this, constantPacking->Loc, qt,
                                      constantPacking->ComponentOffset);
        }
      }
      break;
    }
    case hlsl::UnusualAnnotation::UA_RegisterAssignment: {
      hlsl::RegisterAssignment *registerAssignment =
          cast<hlsl::RegisterAssignment>(*unusualIter);
      if (registerAssignment->IsValid) {
        if (!qt.isNull()) {
          hlsl::DiagnoseRegisterType(this, registerAssignment->Loc, qt,
                                     registerAssignment->RegisterType);
        }
      }
      break;
    }
    case hlsl::UnusualAnnotation::UA_SemanticDecl: {
      hlsl::SemanticDecl *semanticDecl = cast<hlsl::SemanticDecl>(*unusualIter);
      if (isTypedef || isLocalVar) {
        Diag(semanticDecl->Loc, diag::err_hlsl_varmodifierna)
            << "semantic" << declarationType;
      }
      break;    
    }
    case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier: {
      hlsl::PayloadAccessAnnotation *annotation = cast<hlsl::PayloadAccessAnnotation>(*unusualIter);
      if (!isField) {
          Diag(annotation->Loc, diag::err_hlsl_unsupported_payload_access_qualifier);
      }
      break;
    }
    }
  }

  if (!result) {
    D.setInvalidType();
  }

  return result;
}