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;
}