diff options
| author | Ellie Hermaszewska <ellieh@nvidia.com> | 2024-10-29 14:49:26 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-29 14:49:26 +0800 |
| commit | f65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 (patch) | |
| tree | ea1d61342cd29368e19135000ec2948813096205 /source/slang/slang-check-modifier.cpp | |
| parent | a729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff) | |
format
* format
* Minor test fixes
* enable checking cpp format in ci
Diffstat (limited to 'source/slang/slang-check-modifier.cpp')
| -rw-r--r-- | source/slang/slang-check-modifier.cpp | 2976 |
1 files changed, 1547 insertions, 1429 deletions
diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp index f05b58c34..36a73b2e9 100644 --- a/source/slang/slang-check-modifier.cpp +++ b/source/slang/slang-check-modifier.cpp @@ -1,6 +1,6 @@ // slang-check-modifier.cpp -#include "slang-check-impl.h" #include "../core/slang-char-util.h" +#include "slang-check-impl.h" // This file implements semantic checking behavior for // modifiers. @@ -12,379 +12,350 @@ namespace Slang { - IntVal* SemanticsVisitor::checkLinkTimeConstantIntVal( - Expr* expr) - { - expr = CheckExpr(expr); - return CheckIntegerConstantExpression(expr, IntegerConstantExpressionCoercionType::AnyInteger, nullptr, ConstantFoldingKind::LinkTime); - } +IntVal* SemanticsVisitor::checkLinkTimeConstantIntVal(Expr* expr) +{ + expr = CheckExpr(expr); + return CheckIntegerConstantExpression( + expr, + IntegerConstantExpressionCoercionType::AnyInteger, + nullptr, + ConstantFoldingKind::LinkTime); +} - ConstantIntVal* SemanticsVisitor::checkConstantIntVal( - Expr* expr) +ConstantIntVal* SemanticsVisitor::checkConstantIntVal(Expr* expr) +{ + // First type-check the expression as normal + expr = CheckExpr(expr); + + auto intVal = CheckIntegerConstantExpression( + expr, + IntegerConstantExpressionCoercionType::AnyInteger, + nullptr, + ConstantFoldingKind::CompileTime); + if (!intVal) + return nullptr; + + auto constIntVal = as<ConstantIntVal>(intVal); + if (!constIntVal) { - // First type-check the expression as normal - expr = CheckExpr(expr); - - auto intVal = CheckIntegerConstantExpression(expr, IntegerConstantExpressionCoercionType::AnyInteger, nullptr, ConstantFoldingKind::CompileTime); - if(!intVal) - return nullptr; - - auto constIntVal = as<ConstantIntVal>(intVal); - if(!constIntVal) - { - getSink()->diagnose(expr->loc, Diagnostics::expectedIntegerConstantNotLiteral); - return nullptr; - } - return constIntVal; + getSink()->diagnose(expr->loc, Diagnostics::expectedIntegerConstantNotLiteral); + return nullptr; } + return constIntVal; +} - ConstantIntVal* SemanticsVisitor::checkConstantEnumVal( - Expr* expr) - { - // First type-check the expression as normal - expr = CheckExpr(expr); +ConstantIntVal* SemanticsVisitor::checkConstantEnumVal(Expr* expr) +{ + // First type-check the expression as normal + expr = CheckExpr(expr); - auto intVal = CheckEnumConstantExpression(expr, ConstantFoldingKind::CompileTime); - if(!intVal) - return nullptr; + auto intVal = CheckEnumConstantExpression(expr, ConstantFoldingKind::CompileTime); + if (!intVal) + return nullptr; - auto constIntVal = as<ConstantIntVal>(intVal); - if(!constIntVal) - { - getSink()->diagnose(expr->loc, Diagnostics::expectedIntegerConstantNotLiteral); - return nullptr; - } - return constIntVal; + auto constIntVal = as<ConstantIntVal>(intVal); + if (!constIntVal) + { + getSink()->diagnose(expr->loc, Diagnostics::expectedIntegerConstantNotLiteral); + return nullptr; } + return constIntVal; +} - // Check an expression, coerce it to the `String` type, and then - // ensure that it has a literal (not just compile-time constant) value. - bool SemanticsVisitor::checkLiteralStringVal( - Expr* expr, - String* outVal) - { - // TODO: This should actually perform semantic checking, etc., - // but for now we are just going to look for a direct string - // literal AST node. +// Check an expression, coerce it to the `String` type, and then +// ensure that it has a literal (not just compile-time constant) value. +bool SemanticsVisitor::checkLiteralStringVal(Expr* expr, String* outVal) +{ + // TODO: This should actually perform semantic checking, etc., + // but for now we are just going to look for a direct string + // literal AST node. - if(auto stringLitExpr = as<StringLiteralExpr>(expr)) + if (auto stringLitExpr = as<StringLiteralExpr>(expr)) + { + if (outVal) { - if(outVal) - { - *outVal = stringLitExpr->value; - } - return true; + *outVal = stringLitExpr->value; } + return true; + } - getSink()->diagnose(expr, Diagnostics::expectedAStringLiteral); + getSink()->diagnose(expr, Diagnostics::expectedAStringLiteral); - return false; - } + return false; +} - bool SemanticsVisitor::checkCapabilityName(Expr* expr, CapabilityName& outCapabilityName) +bool SemanticsVisitor::checkCapabilityName(Expr* expr, CapabilityName& outCapabilityName) +{ + if (auto varExpr = as<VarExpr>(expr)) { - if (auto varExpr = as<VarExpr>(expr)) + if (!varExpr->name) + return false; + if (varExpr->name == getSession()->getCompletionRequestTokenName()) { - if (!varExpr->name) - return false; - if (varExpr->name == getSession()->getCompletionRequestTokenName()) - { - auto& suggestions = getLinkage()->contentAssistInfo.completionSuggestions; - suggestions.clear(); - suggestions.scopeKind = CompletionSuggestions::ScopeKind::Capabilities; - } - outCapabilityName = findCapabilityName(varExpr->name->text.getUnownedSlice()); - if (outCapabilityName == CapabilityName::Invalid) - { - getSink()->diagnose(expr, Diagnostics::unknownCapability, varExpr->name); - return false; - } - return true; + auto& suggestions = getLinkage()->contentAssistInfo.completionSuggestions; + suggestions.clear(); + suggestions.scopeKind = CompletionSuggestions::ScopeKind::Capabilities; } - getSink()->diagnose(expr, Diagnostics::expectCapability); - return false; + outCapabilityName = findCapabilityName(varExpr->name->text.getUnownedSlice()); + if (outCapabilityName == CapabilityName::Invalid) + { + getSink()->diagnose(expr, Diagnostics::unknownCapability, varExpr->name); + return false; + } + return true; } + getSink()->diagnose(expr, Diagnostics::expectCapability); + return false; +} - void SemanticsVisitor::visitModifier(Modifier*) - { - // Do nothing with modifiers for now - } +void SemanticsVisitor::visitModifier(Modifier*) +{ + // Do nothing with modifiers for now +} - static bool _isDeclAllowedAsAttribute(DeclRef<Decl> declRef) - { - if (as<AttributeDecl>(declRef.getDecl())) - return true; - auto structDecl = as<StructDecl>(declRef.getDecl()); - if (!structDecl) - return false; - auto attrUsageAttr = structDecl->findModifier<AttributeUsageAttribute>(); - if (!attrUsageAttr) - return false; +static bool _isDeclAllowedAsAttribute(DeclRef<Decl> declRef) +{ + if (as<AttributeDecl>(declRef.getDecl())) return true; - } + auto structDecl = as<StructDecl>(declRef.getDecl()); + if (!structDecl) + return false; + auto attrUsageAttr = structDecl->findModifier<AttributeUsageAttribute>(); + if (!attrUsageAttr) + return false; + return true; +} - AttributeDecl* SemanticsVisitor::lookUpAttributeDecl(Name* attributeName, Scope* scope) +AttributeDecl* SemanticsVisitor::lookUpAttributeDecl(Name* attributeName, Scope* scope) +{ + if (!attributeName) + return nullptr; + // We start by looking for an existing attribute matching + // the name `attributeName`. + // { - if (!attributeName) - return nullptr; - // We start by looking for an existing attribute matching - // the name `attributeName`. + // Look up the name and see what attributes we find. // + LookupMask lookupMask = LookupMask::Attribute; + if (attributeName == getSession()->getCompletionRequestTokenName()) { - // Look up the name and see what attributes we find. - // - LookupMask lookupMask = LookupMask::Attribute; - if (attributeName == getSession()->getCompletionRequestTokenName()) - { - lookupMask = - LookupMask((uint32_t)LookupMask::Attribute | (uint32_t)LookupMask::type); - } - - auto lookupResult = lookUp(m_astBuilder, this, attributeName, scope, lookupMask); - - if (attributeName == getSession()->getCompletionRequestTokenName()) - { - // If this is a completion request, add the lookup result to linkage. - auto& suggestions = getLinkage()->contentAssistInfo.completionSuggestions; - suggestions.clear(); - suggestions.scopeKind = CompletionSuggestions::ScopeKind::Attribute; - for (auto& item : lookupResult) - { - if (_isDeclAllowedAsAttribute(item.declRef)) - { - suggestions.candidateItems.add(item); - } - } - } + lookupMask = LookupMask((uint32_t)LookupMask::Attribute | (uint32_t)LookupMask::type); + } - // If the result was overloaded, then that means there - // are multiple attributes matching the name, and we - // aren't going to be able to narrow it down. - // - if(lookupResult.isOverloaded()) - return nullptr; + auto lookupResult = lookUp(m_astBuilder, this, attributeName, scope, lookupMask); - // If there is a single valid result, and it names - // an existing attribute declaration, then we can - // use it as the result. - // - if (lookupResult.isValid()) + if (attributeName == getSession()->getCompletionRequestTokenName()) + { + // If this is a completion request, add the lookup result to linkage. + auto& suggestions = getLinkage()->contentAssistInfo.completionSuggestions; + suggestions.clear(); + suggestions.scopeKind = CompletionSuggestions::ScopeKind::Attribute; + for (auto& item : lookupResult) { - auto decl = lookupResult.item.declRef.getDecl(); - if (auto attributeDecl = as<AttributeDecl>(decl)) + if (_isDeclAllowedAsAttribute(item.declRef)) { - return attributeDecl; + suggestions.candidateItems.add(item); } } } - // If there wasn't already an attribute matching the - // given name, then we will look for a `struct` type - // matching the name scheme for user-defined attributes. - // - // If the attribute was `[Something(...)]` then we will - // look for a `struct` named `SomethingAttribute`. + // If the result was overloaded, then that means there + // are multiple attributes matching the name, and we + // aren't going to be able to narrow it down. // - LookupResult lookupResult = lookUp(m_astBuilder, this, m_astBuilder->getGlobalSession()->getNameObj(attributeName->text + "Attribute"), scope, LookupMask::type); - // - // If we didn't find a matching type name, then we give up. - // - if (!lookupResult.isValid() || lookupResult.isOverloaded()) - return nullptr; - - - // We only allow a `struct` type to be used as an attribute - // if the type itself has an `[AttributeUsage(...)]` attribute - // attached to it. - // - auto structDecl = lookupResult.item.declRef.as<StructDecl>().getDecl(); - if(!structDecl) - return nullptr; - auto attrUsageAttr = structDecl->findModifier<AttributeUsageAttribute>(); - if (!attrUsageAttr) + if (lookupResult.isOverloaded()) return nullptr; - // We will now synthesize a new `AttributeDecl` to mirror - // what was declared on the `struct` type. + // If there is a single valid result, and it names + // an existing attribute declaration, then we can + // use it as the result. // - AttributeDecl* attrDecl = m_astBuilder->create<AttributeDecl>(); - attrDecl->nameAndLoc.name = attributeName; - attrDecl->nameAndLoc.loc = structDecl->nameAndLoc.loc; - attrDecl->loc = structDecl->loc; - - while(attrUsageAttr) + if (lookupResult.isValid()) { - AttributeTargetModifier* targetModifier = m_astBuilder->create<AttributeTargetModifier>(); - targetModifier->syntaxClass = attrUsageAttr->targetSyntaxClass; - targetModifier->loc = attrUsageAttr->loc; - addModifier(attrDecl, targetModifier); - attrUsageAttr = as<AttributeUsageAttribute>(attrUsageAttr->next); + auto decl = lookupResult.item.declRef.getDecl(); + if (auto attributeDecl = as<AttributeDecl>(decl)) + { + return attributeDecl; + } } + } - // Every attribute declaration is associated with the type - // of syntax nodes it constructs (via reflection/RTTI). - // - // User-defined attributes create instances of - // `UserDefinedAttribute`. - // - attrDecl->syntaxClass = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("UserDefinedAttribute")); + // If there wasn't already an attribute matching the + // given name, then we will look for a `struct` type + // matching the name scheme for user-defined attributes. + // + // If the attribute was `[Something(...)]` then we will + // look for a `struct` named `SomethingAttribute`. + // + LookupResult lookupResult = lookUp( + m_astBuilder, + this, + m_astBuilder->getGlobalSession()->getNameObj(attributeName->text + "Attribute"), + scope, + LookupMask::type); + // + // If we didn't find a matching type name, then we give up. + // + if (!lookupResult.isValid() || lookupResult.isOverloaded()) + return nullptr; + + + // We only allow a `struct` type to be used as an attribute + // if the type itself has an `[AttributeUsage(...)]` attribute + // attached to it. + // + auto structDecl = lookupResult.item.declRef.as<StructDecl>().getDecl(); + if (!structDecl) + return nullptr; + auto attrUsageAttr = structDecl->findModifier<AttributeUsageAttribute>(); + if (!attrUsageAttr) + return nullptr; + + // We will now synthesize a new `AttributeDecl` to mirror + // what was declared on the `struct` type. + // + AttributeDecl* attrDecl = m_astBuilder->create<AttributeDecl>(); + attrDecl->nameAndLoc.name = attributeName; + attrDecl->nameAndLoc.loc = structDecl->nameAndLoc.loc; + attrDecl->loc = structDecl->loc; + + while (attrUsageAttr) + { + AttributeTargetModifier* targetModifier = m_astBuilder->create<AttributeTargetModifier>(); + targetModifier->syntaxClass = attrUsageAttr->targetSyntaxClass; + targetModifier->loc = attrUsageAttr->loc; + addModifier(attrDecl, targetModifier); + attrUsageAttr = as<AttributeUsageAttribute>(attrUsageAttr->next); + } - // The fields of the user-defined `struct` type become - // the parameters of the new attribute. - // - // TODO: This step should skip `static` fields. - // - for(auto member : structDecl->members) + // Every attribute declaration is associated with the type + // of syntax nodes it constructs (via reflection/RTTI). + // + // User-defined attributes create instances of + // `UserDefinedAttribute`. + // + attrDecl->syntaxClass = + m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("UserDefinedAttribute")); + + // The fields of the user-defined `struct` type become + // the parameters of the new attribute. + // + // TODO: This step should skip `static` fields. + // + for (auto member : structDecl->members) + { + if (auto varMember = as<VarDecl>(member)) { - if(auto varMember = as<VarDecl>(member)) - { - ensureDecl(varMember, DeclCheckState::CanUseTypeOfValueDecl); + ensureDecl(varMember, DeclCheckState::CanUseTypeOfValueDecl); - ParamDecl* paramDecl = m_astBuilder->create<ParamDecl>(); - paramDecl->nameAndLoc = member->nameAndLoc; - paramDecl->type = varMember->type; - paramDecl->loc = member->loc; - paramDecl->setCheckState(DeclCheckState::DefinitionChecked); + ParamDecl* paramDecl = m_astBuilder->create<ParamDecl>(); + paramDecl->nameAndLoc = member->nameAndLoc; + paramDecl->type = varMember->type; + paramDecl->loc = member->loc; + paramDecl->setCheckState(DeclCheckState::DefinitionChecked); - paramDecl->parentDecl = attrDecl; - attrDecl->members.add(paramDecl); - } + paramDecl->parentDecl = attrDecl; + attrDecl->members.add(paramDecl); } + } - // We need to end by putting the new attribute declaration - // into the AST, so that it can be found via lookup. - // - auto parentDecl = structDecl->parentDecl; - // - // TODO: handle the case where `parentDecl` is generic? - // - attrDecl->parentDecl = parentDecl; - parentDecl->members.add(attrDecl); - - SLANG_ASSERT(!parentDecl->isMemberDictionaryValid()); - - // Finally, we perform any required semantic checks on - // the newly constructed attribute decl. - // - // TODO: what check state is relevant here? - // - ensureDecl(attrDecl, DeclCheckState::DefinitionChecked); + // We need to end by putting the new attribute declaration + // into the AST, so that it can be found via lookup. + // + auto parentDecl = structDecl->parentDecl; + // + // TODO: handle the case where `parentDecl` is generic? + // + attrDecl->parentDecl = parentDecl; + parentDecl->members.add(attrDecl); + + SLANG_ASSERT(!parentDecl->isMemberDictionaryValid()); + + // Finally, we perform any required semantic checks on + // the newly constructed attribute decl. + // + // TODO: what check state is relevant here? + // + ensureDecl(attrDecl, DeclCheckState::DefinitionChecked); + + return attrDecl; +} - return attrDecl; +bool SemanticsVisitor::hasIntArgs(Attribute* attr, int numArgs) +{ + if (int(attr->args.getCount()) != numArgs) + { + return false; } - - bool SemanticsVisitor::hasIntArgs(Attribute* attr, int numArgs) + for (int i = 0; i < numArgs; ++i) { - if (int(attr->args.getCount()) != numArgs) + if (!as<IntegerLiteralExpr>(attr->args[i])) { return false; } - for (int i = 0; i < numArgs; ++i) - { - if (!as<IntegerLiteralExpr>(attr->args[i])) - { - return false; - } - } - return true; } + return true; +} - bool SemanticsVisitor::hasStringArgs(Attribute* attr, int numArgs) +bool SemanticsVisitor::hasStringArgs(Attribute* attr, int numArgs) +{ + if (int(attr->args.getCount()) != numArgs) + { + return false; + } + for (int i = 0; i < numArgs; ++i) { - if (int(attr->args.getCount()) != numArgs) + if (!as<StringLiteralExpr>(attr->args[i])) { return false; } - for (int i = 0; i < numArgs; ++i) - { - if (!as<StringLiteralExpr>(attr->args[i])) - { - return false; - } - } - return true; } + return true; +} - bool SemanticsVisitor::getAttributeTargetSyntaxClasses(SyntaxClass<NodeBase> & cls, uint32_t typeFlags) +bool SemanticsVisitor::getAttributeTargetSyntaxClasses( + SyntaxClass<NodeBase>& cls, + uint32_t typeFlags) +{ + if (typeFlags == (int)UserDefinedAttributeTargets::Struct) { - if (typeFlags == (int)UserDefinedAttributeTargets::Struct) - { - cls = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("StructDecl")); - return true; - } - if (typeFlags == (int)UserDefinedAttributeTargets::Var) - { - cls = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("VarDecl")); - return true; - } - if (typeFlags == (int)UserDefinedAttributeTargets::Function) - { - cls = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("FuncDecl")); - return true; - } - if (typeFlags == (int)UserDefinedAttributeTargets::Param) - { - cls = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("ParamDecl")); - return true; - } - return false; + cls = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("StructDecl")); + return true; } - - Modifier* SemanticsVisitor::validateAttribute(Attribute* attr, AttributeDecl* attribClassDecl, ModifiableSyntaxNode* attrTarget) + if (typeFlags == (int)UserDefinedAttributeTargets::Var) { - if (auto numThreadsAttr = as<NumThreadsAttribute>(attr)) - { - SLANG_ASSERT(attr->args.getCount() == 3); - - IntVal* values[3]; + cls = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("VarDecl")); + return true; + } + if (typeFlags == (int)UserDefinedAttributeTargets::Function) + { + cls = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("FuncDecl")); + return true; + } + if (typeFlags == (int)UserDefinedAttributeTargets::Param) + { + cls = m_astBuilder->findSyntaxClass(UnownedStringSlice::fromLiteral("ParamDecl")); + return true; + } + return false; +} - for (int i = 0; i < 3; ++i) - { - IntVal* value = nullptr; +Modifier* SemanticsVisitor::validateAttribute( + Attribute* attr, + AttributeDecl* attribClassDecl, + ModifiableSyntaxNode* attrTarget) +{ + if (auto numThreadsAttr = as<NumThreadsAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 3); - auto arg = attr->args[i]; - if (arg) - { - auto intValue = checkLinkTimeConstantIntVal(arg); - if (!intValue) - { - return nullptr; - } - if (auto constIntVal = as<ConstantIntVal>(intValue)) - { - if (constIntVal->getValue() < 1) - { - getSink()->diagnose(attr, Diagnostics::nonPositiveNumThreads, constIntVal->getValue()); - return nullptr; - } - if (intValue->getType() != m_astBuilder->getIntType()) - { - intValue = m_astBuilder->getIntVal(m_astBuilder->getIntType(), constIntVal->getValue()); - } - } - // Make sure we always canonicalize the type to int. - value = intValue; - if (value->getType() != m_astBuilder->getIntType()) - value = m_astBuilder->getTypeCastIntVal(m_astBuilder->getIntType(), value); - } - else - { - value = m_astBuilder->getIntVal(m_astBuilder->getIntType(), 1); - } - values[i] = value; - } + IntVal* values[3]; - numThreadsAttr->x = values[0]; - numThreadsAttr->y = values[1]; - numThreadsAttr->z = values[2]; - } - else if (auto waveSizeAttr = as<WaveSizeAttribute>(attr)) + for (int i = 0; i < 3; ++i) { - SLANG_ASSERT(attr->args.getCount() == 1); - IntVal* value = nullptr; - auto arg = attr->args[0]; + auto arg = attr->args[i]; if (arg) { auto intValue = checkLinkTimeConstantIntVal(arg); @@ -394,1384 +365,1531 @@ namespace Slang } if (auto constIntVal = as<ConstantIntVal>(intValue)) { - bool isValidWaveSize = false; - const IntegerLiteralValue waveSize = constIntVal->getValue(); - for (int validWaveSize : { 4, 8, 16, 32, 64, 128 }) + if (constIntVal->getValue() < 1) { - if (validWaveSize == waveSize) - { - isValidWaveSize = true; - break; - } + getSink()->diagnose( + attr, + Diagnostics::nonPositiveNumThreads, + constIntVal->getValue()); + return nullptr; } - if (!isValidWaveSize) + if (intValue->getType() != m_astBuilder->getIntType()) { - getSink()->diagnose(attr, Diagnostics::invalidWaveSize, constIntVal->getValue()); - return nullptr; + intValue = m_astBuilder->getIntVal( + m_astBuilder->getIntType(), + constIntVal->getValue()); } } + // Make sure we always canonicalize the type to int. value = intValue; + if (value->getType() != m_astBuilder->getIntType()) + value = m_astBuilder->getTypeCastIntVal(m_astBuilder->getIntType(), value); } else { value = m_astBuilder->getIntVal(m_astBuilder->getIntType(), 1); } - - waveSizeAttr->numLanes = value; + values[i] = value; } - else if (auto anyValueSizeAttr = as<AnyValueSizeAttribute>(attr)) - { - // This case handles GLSL-oriented layout attributes - // that take a single integer argument. - - if (attr->args.getCount() != 1) - { - return nullptr; - } - auto value = checkConstantIntVal(attr->args[0]); - if (value == nullptr) - { - return nullptr; - } + numThreadsAttr->x = values[0]; + numThreadsAttr->y = values[1]; + numThreadsAttr->z = values[2]; + } + else if (auto waveSizeAttr = as<WaveSizeAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); - const IRIntegerValue kMaxAnyValueSize = 0x7FFF; - if (value->getValue() > kMaxAnyValueSize) - { - getSink()->diagnose(anyValueSizeAttr->loc, Diagnostics::anyValueSizeExceedsLimit, kMaxAnyValueSize); - return nullptr; - } + IntVal* value = nullptr; - anyValueSizeAttr->size = int32_t(value->getValue()); - } - else if (auto glslRequireShaderInputParameter = as<GLSLRequireShaderInputParameterAttribute>(attr)) + auto arg = attr->args[0]; + if (arg) { - if (attr->args.getCount() != 1) - { - return nullptr; - } - auto value = checkConstantIntVal(attr->args[0]); - if (value == nullptr) + auto intValue = checkLinkTimeConstantIntVal(arg); + if (!intValue) { return nullptr; } - if (value->getValue() < 0) + if (auto constIntVal = as<ConstantIntVal>(intValue)) { - return nullptr; + bool isValidWaveSize = false; + const IntegerLiteralValue waveSize = constIntVal->getValue(); + for (int validWaveSize : {4, 8, 16, 32, 64, 128}) + { + if (validWaveSize == waveSize) + { + isValidWaveSize = true; + break; + } + } + if (!isValidWaveSize) + { + getSink()->diagnose( + attr, + Diagnostics::invalidWaveSize, + constIntVal->getValue()); + return nullptr; + } } - glslRequireShaderInputParameter->parameterNumber = int32_t(value->getValue()); + value = intValue; } - else if (auto overloadRankAttr = as<OverloadRankAttribute>(attr)) + else { - if (attr->args.getCount() != 1) - { - return nullptr; - } - auto rank = checkConstantIntVal(attr->args[0]); - if (rank == nullptr) - { - return nullptr; - } - overloadRankAttr->rank = int32_t(rank->getValue()); + value = m_astBuilder->getIntVal(m_astBuilder->getIntType(), 1); } - else if (auto inputAttachmentIndexLayoutAttribute = as<GLSLInputAttachmentIndexLayoutAttribute>(attr)) - { - if (attr->args.getCount() != 1) - return nullptr; - - auto location = checkConstantIntVal(attr->args[0]); - if(!location) - return nullptr; - inputAttachmentIndexLayoutAttribute->location = location->getValue(); - } - else if (auto bindingAttr = as<GLSLBindingAttribute>(attr)) + waveSizeAttr->numLanes = value; + } + else if (auto anyValueSizeAttr = as<AnyValueSizeAttribute>(attr)) + { + // This case handles GLSL-oriented layout attributes + // that take a single integer argument. + + if (attr->args.getCount() != 1) { - // This must be vk::binding or gl::binding (as specified in core.meta.slang under vk_binding/gl_binding) - // Must have 2 int parameters. Ideally this would all be checked from the specification - // in core.meta.slang, but that's not completely implemented. So for now we check here. - if (attr->args.getCount() != 2) - { - return nullptr; - } + return nullptr; + } - // TODO(JS): Prior validation currently doesn't ensure both args are ints (as specified in core.meta.slang), so check here - // to make sure they both are - auto binding = checkConstantIntVal(attr->args[0]); - auto set = checkConstantIntVal(attr->args[1]); + auto value = checkConstantIntVal(attr->args[0]); + if (value == nullptr) + { + return nullptr; + } - if (binding == nullptr || set == nullptr) - { - return nullptr; - } + const IRIntegerValue kMaxAnyValueSize = 0x7FFF; + if (value->getValue() > kMaxAnyValueSize) + { + getSink()->diagnose( + anyValueSizeAttr->loc, + Diagnostics::anyValueSizeExceedsLimit, + kMaxAnyValueSize); + return nullptr; + } - bindingAttr->binding = int32_t(binding->getValue()); - bindingAttr->set = int32_t(set->getValue()); + anyValueSizeAttr->size = int32_t(value->getValue()); + } + else if ( + auto glslRequireShaderInputParameter = as<GLSLRequireShaderInputParameterAttribute>(attr)) + { + if (attr->args.getCount() != 1) + { + return nullptr; } - else if (auto simpleLayoutAttr = as<GLSLSimpleIntegerLayoutAttribute>(attr)) + auto value = checkConstantIntVal(attr->args[0]); + if (value == nullptr) { - // This case handles GLSL-oriented layout attributes - // that take a single integer argument. - - if (attr->args.getCount() != 1) - { - return nullptr; - } + return nullptr; + } + if (value->getValue() < 0) + { + return nullptr; + } + glslRequireShaderInputParameter->parameterNumber = int32_t(value->getValue()); + } + else if (auto overloadRankAttr = as<OverloadRankAttribute>(attr)) + { + if (attr->args.getCount() != 1) + { + return nullptr; + } + auto rank = checkConstantIntVal(attr->args[0]); + if (rank == nullptr) + { + return nullptr; + } + overloadRankAttr->rank = int32_t(rank->getValue()); + } + else if ( + auto inputAttachmentIndexLayoutAttribute = + as<GLSLInputAttachmentIndexLayoutAttribute>(attr)) + { + if (attr->args.getCount() != 1) + return nullptr; - auto value = checkConstantIntVal(attr->args[0]); - if (value == nullptr) - { - return nullptr; - } + auto location = checkConstantIntVal(attr->args[0]); + if (!location) + return nullptr; - simpleLayoutAttr->value = int32_t(value->getValue()); + inputAttachmentIndexLayoutAttribute->location = location->getValue(); + } + else if (auto bindingAttr = as<GLSLBindingAttribute>(attr)) + { + // This must be vk::binding or gl::binding (as specified in core.meta.slang under + // vk_binding/gl_binding) Must have 2 int parameters. Ideally this would all be checked from + // the specification in core.meta.slang, but that's not completely implemented. So for now + // we check here. + if (attr->args.getCount() != 2) + { + return nullptr; } - else if (auto maxVertexCountAttr = as<MaxVertexCountAttribute>(attr)) + + // TODO(JS): Prior validation currently doesn't ensure both args are ints (as specified in + // core.meta.slang), so check here to make sure they both are + auto binding = checkConstantIntVal(attr->args[0]); + auto set = checkConstantIntVal(attr->args[1]); + + if (binding == nullptr || set == nullptr) { - SLANG_ASSERT(attr->args.getCount() == 1); - auto val = checkConstantIntVal(attr->args[0]); + return nullptr; + } - if (!val) return nullptr; + bindingAttr->binding = int32_t(binding->getValue()); + bindingAttr->set = int32_t(set->getValue()); + } + else if (auto simpleLayoutAttr = as<GLSLSimpleIntegerLayoutAttribute>(attr)) + { + // This case handles GLSL-oriented layout attributes + // that take a single integer argument. - maxVertexCountAttr->value = (int32_t)val->getValue(); + if (attr->args.getCount() != 1) + { + return nullptr; } - else if (auto instanceAttr = as<InstanceAttribute>(attr)) + + auto value = checkConstantIntVal(attr->args[0]); + if (value == nullptr) { - SLANG_ASSERT(attr->args.getCount() == 1); - auto val = checkConstantIntVal(attr->args[0]); + return nullptr; + } - if (!val) return nullptr; + simpleLayoutAttr->value = int32_t(value->getValue()); + } + else if (auto maxVertexCountAttr = as<MaxVertexCountAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto val = checkConstantIntVal(attr->args[0]); - instanceAttr->value = (int32_t)val->getValue(); + if (!val) + return nullptr; + + maxVertexCountAttr->value = (int32_t)val->getValue(); + } + else if (auto instanceAttr = as<InstanceAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto val = checkConstantIntVal(attr->args[0]); + + if (!val) + return nullptr; + + instanceAttr->value = (int32_t)val->getValue(); + } + else if (auto entryPointAttr = as<EntryPointAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + + String capNameString; + if (!checkLiteralStringVal(attr->args[0], &capNameString)) + { + return nullptr; } - else if (auto entryPointAttr = as<EntryPointAttribute>(attr)) + + CapabilityName capName = findCapabilityName(capNameString.getUnownedSlice()); + if (capName != CapabilityName::Invalid) { - SLANG_ASSERT(attr->args.getCount() == 1); + if (isInternalCapabilityName(capName)) + maybeDiagnose( + getSink(), + this->getOptionSet(), + DiagnosticCategory::Capability, + attr, + Diagnostics::usingInternalCapabilityName, + attr, + capName); - String capNameString; - if (!checkLiteralStringVal(attr->args[0], &capNameString)) + // Ensure this capability only defines 1 stage per target, else diagnose an error. + // This is a fatal error, do not allow toggling this error off. + entryPointAttr->capabilitySet = CapabilitySet(capName); + HashSet<CapabilityAtom> stageToBeUsed; + for (auto& targetSet : entryPointAttr->capabilitySet.getCapabilityTargetSets()) { - return nullptr; + for (auto& stageSet : targetSet.second.shaderStageSets) + stageToBeUsed.add(stageSet.first); } - CapabilityName capName = findCapabilityName(capNameString.getUnownedSlice()); - if (capName != CapabilityName::Invalid) - { - if (isInternalCapabilityName(capName)) - maybeDiagnose(getSink(), this->getOptionSet(), DiagnosticCategory::Capability, attr, Diagnostics::usingInternalCapabilityName, attr, capName); - - // Ensure this capability only defines 1 stage per target, else diagnose an error. - // This is a fatal error, do not allow toggling this error off. - entryPointAttr->capabilitySet = CapabilitySet(capName); - HashSet<CapabilityAtom> stageToBeUsed; - for (auto& targetSet : entryPointAttr->capabilitySet.getCapabilityTargetSets()) - { - for(auto& stageSet : targetSet.second.shaderStageSets) - stageToBeUsed.add(stageSet.first); - } - - // TODO: Once profiles are removed in favor for `CapabilitySet`s we will beable to use more complex relationships, - // Until then we have an artificial limit that any capabilites used inside '[shader(...)]' must only specify 1 stage type - // uniformly across targets. - if (stageToBeUsed.getCount() > 1) - { - List<CapabilityAtom> atomsToPrint; - atomsToPrint.reserve(stageToBeUsed.getCount()); - for (auto i : stageToBeUsed) - atomsToPrint.add(i); - getSink()->diagnose(attr, Diagnostics::capabilityHasMultipleStages, capNameString, atomsToPrint); - } - return entryPointAttr; - } - else + // TODO: Once profiles are removed in favor for `CapabilitySet`s we will beable to use + // more complex relationships, Until then we have an artificial limit that any + // capabilites used inside '[shader(...)]' must only specify 1 stage type uniformly + // across targets. + if (stageToBeUsed.getCount() > 1) { - // always diagnose this error since nothing can compile with an invalid capability - getSink()->diagnose(attr, Diagnostics::unknownCapability, capNameString); - return nullptr; + List<CapabilityAtom> atomsToPrint; + atomsToPrint.reserve(stageToBeUsed.getCount()); + for (auto i : stageToBeUsed) + atomsToPrint.add(i); + getSink()->diagnose( + attr, + Diagnostics::capabilityHasMultipleStages, + capNameString, + atomsToPrint); } + return entryPointAttr; } - else if ((as<DomainAttribute>(attr)) || - (as<MaxTessFactorAttribute>(attr)) || - (as<OutputTopologyAttribute>(attr)) || - (as<PartitioningAttribute>(attr)) || - (as<PatchConstantFuncAttribute>(attr))) + else { - // Let it go thru iff single string attribute - if (!hasStringArgs(attr, 1)) - { - getSink()->diagnose(attr, Diagnostics::expectedSingleStringArg, attr->keywordName); - } + // always diagnose this error since nothing can compile with an invalid capability + getSink()->diagnose(attr, Diagnostics::unknownCapability, capNameString); + return nullptr; } - else if (auto opAttr = as<SPIRVInstructionOpAttribute>(attr)) + } + else if ( + (as<DomainAttribute>(attr)) || (as<MaxTessFactorAttribute>(attr)) || + (as<OutputTopologyAttribute>(attr)) || (as<PartitioningAttribute>(attr)) || + (as<PatchConstantFuncAttribute>(attr))) + { + // Let it go thru iff single string attribute + if (!hasStringArgs(attr, 1)) { - auto sink = getSink(); - const auto argsCount = opAttr->args.getCount(); - if (argsCount < 1 || argsCount > 2) - { - sink->diagnose(attr, Diagnostics::attributeArgumentCountMismatch, attr->keywordName, "1...2", argsCount); - } - else if (!as<IntegerLiteralExpr>(opAttr->args[0])) - { - sink->diagnose(attr, Diagnostics::attributeExpectedIntArg, attr->keywordName, 0); - } - else if (argsCount > 1 && !as<StringLiteralExpr>(opAttr->args[1])) - { - sink->diagnose(attr, Diagnostics::attributeExpectedStringArg, attr->keywordName, 1); - } + getSink()->diagnose(attr, Diagnostics::expectedSingleStringArg, attr->keywordName); } - else if (as<OutputControlPointsAttribute>(attr)) + } + else if (auto opAttr = as<SPIRVInstructionOpAttribute>(attr)) + { + auto sink = getSink(); + const auto argsCount = opAttr->args.getCount(); + if (argsCount < 1 || argsCount > 2) { - // Let it go thru iff single integral attribute - if (!hasIntArgs(attr, 1)) - { - getSink()->diagnose(attr, Diagnostics::expectedSingleIntArg, attr->keywordName); - } + sink->diagnose( + attr, + Diagnostics::attributeArgumentCountMismatch, + attr->keywordName, + "1...2", + argsCount); + } + else if (!as<IntegerLiteralExpr>(opAttr->args[0])) + { + sink->diagnose(attr, Diagnostics::attributeExpectedIntArg, attr->keywordName, 0); } - else if (auto attrUsageAttr = as<AttributeUsageAttribute>(attr)) + else if (argsCount > 1 && !as<StringLiteralExpr>(opAttr->args[1])) { - uint32_t targetClassId = (uint32_t)UserDefinedAttributeTargets::None; - if (attr->args.getCount() == 1) + sink->diagnose(attr, Diagnostics::attributeExpectedStringArg, attr->keywordName, 1); + } + } + else if (as<OutputControlPointsAttribute>(attr)) + { + // Let it go thru iff single integral attribute + if (!hasIntArgs(attr, 1)) + { + getSink()->diagnose(attr, Diagnostics::expectedSingleIntArg, attr->keywordName); + } + } + else if (auto attrUsageAttr = as<AttributeUsageAttribute>(attr)) + { + uint32_t targetClassId = (uint32_t)UserDefinedAttributeTargets::None; + if (attr->args.getCount() == 1) + { + // IntVal* outIntVal; + if (auto cInt = checkConstantEnumVal(attr->args[0])) { - //IntVal* outIntVal; - if (auto cInt = checkConstantEnumVal(attr->args[0])) - { - targetClassId = (uint32_t)(cInt->getValue()); - } - else - { - getSink()->diagnose(attr, Diagnostics::expectedSingleIntArg, attr->keywordName); - return nullptr; - } + targetClassId = (uint32_t)(cInt->getValue()); } - if (!getAttributeTargetSyntaxClasses(attrUsageAttr->targetSyntaxClass, targetClassId)) + else { - getSink()->diagnose(attr, Diagnostics::invalidAttributeTarget); + getSink()->diagnose(attr, Diagnostics::expectedSingleIntArg, attr->keywordName); return nullptr; } } - else if (const auto unrollAttr = as<UnrollAttribute>(attr)) + if (!getAttributeTargetSyntaxClasses(attrUsageAttr->targetSyntaxClass, targetClassId)) { - // Check has an argument. We need this because default behavior is to give an error - // if an attribute has arguments, but not handled explicitly (and the default param will come through - // as 1 arg if nothing is specified) - SLANG_ASSERT(attr->args.getCount() == 1); + getSink()->diagnose(attr, Diagnostics::invalidAttributeTarget); + return nullptr; } - else if (auto forceUnrollAttr = as<ForceUnrollAttribute>(attr)) + } + else if (const auto unrollAttr = as<UnrollAttribute>(attr)) + { + // Check has an argument. We need this because default behavior is to give an error + // if an attribute has arguments, but not handled explicitly (and the default param will + // come through as 1 arg if nothing is specified) + SLANG_ASSERT(attr->args.getCount() == 1); + } + else if (auto forceUnrollAttr = as<ForceUnrollAttribute>(attr)) + { + if (forceUnrollAttr->args.getCount() < 1) { - if (forceUnrollAttr->args.getCount() < 1) - { - getSink()->diagnose(attr, Diagnostics::notEnoughArguments, attr->args.getCount(), 1); - } - auto cint = checkConstantIntVal(attr->args[0]); - if (cint) - forceUnrollAttr->maxIterations = (int32_t)cint->getValue(); + getSink()->diagnose(attr, Diagnostics::notEnoughArguments, attr->args.getCount(), 1); } - else if (auto maxItersAttrs = as<MaxItersAttribute>(attr)) + auto cint = checkConstantIntVal(attr->args[0]); + if (cint) + forceUnrollAttr->maxIterations = (int32_t)cint->getValue(); + } + else if (auto maxItersAttrs = as<MaxItersAttribute>(attr)) + { + if (attr->args.getCount() < 1) { - if (attr->args.getCount() < 1) - { - getSink()->diagnose(attr, Diagnostics::notEnoughArguments, attr->args.getCount(), 1); - } - else + getSink()->diagnose(attr, Diagnostics::notEnoughArguments, attr->args.getCount(), 1); + } + else + { + auto cint = checkConstantIntVal(attr->args[0]); + if (cint) { - auto cint = checkConstantIntVal(attr->args[0]); - if (cint) - { - maxItersAttrs->value = (int32_t) cint->getValue(); - } + maxItersAttrs->value = (int32_t)cint->getValue(); } } - else if (const auto userDefAttr = as<UserDefinedAttribute>(attr)) + } + else if (const auto userDefAttr = as<UserDefinedAttribute>(attr)) + { + // check arguments against attribute parameters defined in attribClassDecl + Index paramIndex = 0; + auto params = attribClassDecl->getMembersOfType<ParamDecl>(); + for (auto paramDecl : params) { - // check arguments against attribute parameters defined in attribClassDecl - Index paramIndex = 0; - auto params = attribClassDecl->getMembersOfType<ParamDecl>(); - for (auto paramDecl : params) - { - ensureDecl(paramDecl, DeclCheckState::CanUseTypeOfValueDecl); + ensureDecl(paramDecl, DeclCheckState::CanUseTypeOfValueDecl); - if (paramIndex < attr->args.getCount()) + if (paramIndex < attr->args.getCount()) + { + auto& arg = attr->args[paramIndex]; + bool typeChecked = false; + if (auto basicType = as<BasicExpressionType>(paramDecl->getType())) { - auto & arg = attr->args[paramIndex]; - bool typeChecked = false; - if (auto basicType = as<BasicExpressionType>(paramDecl->getType())) + if (basicType->getBaseType() == BaseType::Int) { - if (basicType->getBaseType() == BaseType::Int) + if (auto cint = checkConstantIntVal(arg)) { - if (auto cint = checkConstantIntVal(arg)) - { - for (Index ci = attr->intArgVals.getCount(); ci < paramIndex + 1; ci++) - attr->intArgVals.add(nullptr); - attr->intArgVals[(uint32_t)paramIndex] = cint; - } - typeChecked = true; + for (Index ci = attr->intArgVals.getCount(); ci < paramIndex + 1; ci++) + attr->intArgVals.add(nullptr); + attr->intArgVals[(uint32_t)paramIndex] = cint; } - } - if (!typeChecked) - { - arg = CheckTerm(arg); - arg = coerce(CoercionSite::Argument, paramDecl->getType(), arg); + typeChecked = true; } } - paramIndex++; - } - if (params.getCount() < attr->args.getCount()) - { - getSink()->diagnose(attr, Diagnostics::tooManyArguments, attr->args.getCount(), params.getCount()); - } - else if (params.getCount() > attr->args.getCount()) - { - getSink()->diagnose(attr, Diagnostics::notEnoughArguments, attr->args.getCount(), params.getCount()); + if (!typeChecked) + { + arg = CheckTerm(arg); + arg = coerce(CoercionSite::Argument, paramDecl->getType(), arg); + } } + paramIndex++; } - else if (auto diffAttr = as<BackwardDifferentiableAttribute>(attr)) + if (params.getCount() < attr->args.getCount()) { - SLANG_ASSERT(attr->args.getCount() == 1); - auto cint = checkConstantIntVal(attr->args[0]); - if (cint) - diffAttr->maxOrder = (int32_t)cint->getValue(); + getSink()->diagnose( + attr, + Diagnostics::tooManyArguments, + attr->args.getCount(), + params.getCount()); } - else if (auto formatAttr = as<FormatAttribute>(attr)) + else if (params.getCount() > attr->args.getCount()) { - SLANG_ASSERT(attr->args.getCount() == 1); - - String formatName; - if(!checkLiteralStringVal(attr->args[0], &formatName)) - { - return nullptr; - } - - ImageFormat format = ImageFormat::unknown; - - if (attr->keywordName->text.getUnownedSlice() == toSlice("image")) - { - if(!findImageFormatByName(formatName.getUnownedSlice(), &format)) - { - getSink()->diagnose(attr->args[0], Diagnostics::unknownImageFormatName, formatName); - } - } - else - { - if (!findVkImageFormatByName(formatName.getUnownedSlice(), &format)) - { - getSink()->diagnose(attr->args[0], Diagnostics::unknownImageFormatName, formatName); - } - } - - formatAttr->format = format; + getSink()->diagnose( + attr, + Diagnostics::notEnoughArguments, + attr->args.getCount(), + params.getCount()); } - else if (auto allowAttr = as<AllowAttribute>(attr)) + } + else if (auto diffAttr = as<BackwardDifferentiableAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto cint = checkConstantIntVal(attr->args[0]); + if (cint) + diffAttr->maxOrder = (int32_t)cint->getValue(); + } + else if (auto formatAttr = as<FormatAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + + String formatName; + if (!checkLiteralStringVal(attr->args[0], &formatName)) { - SLANG_ASSERT(attr->args.getCount() == 1); + return nullptr; + } - String diagnosticName; - if(!checkLiteralStringVal(attr->args[0], &diagnosticName)) - { - return nullptr; - } + ImageFormat format = ImageFormat::unknown; - auto diagnosticInfo = findDiagnosticByName(diagnosticName.getUnownedSlice()); - if(!diagnosticInfo) + if (attr->keywordName->text.getUnownedSlice() == toSlice("image")) + { + if (!findImageFormatByName(formatName.getUnownedSlice(), &format)) { - getSink()->diagnose(attr->args[0], Diagnostics::unknownDiagnosticName, diagnosticName); + getSink()->diagnose(attr->args[0], Diagnostics::unknownImageFormatName, formatName); } - - allowAttr->diagnostic = diagnosticInfo; } - else if (auto dllImportAttr = as<DllImportAttribute>(attr)) + else { - SLANG_ASSERT(attr->args.getCount() == 1 || attr->args.getCount() == 2); - - String libraryName; - if (!checkLiteralStringVal(dllImportAttr->args[0], &libraryName)) + if (!findVkImageFormatByName(formatName.getUnownedSlice(), &format)) { - return nullptr; - } - dllImportAttr->modulePath = libraryName; - - String functionName; - if (dllImportAttr->args.getCount() == 2 && !checkLiteralStringVal(dllImportAttr->args[1], &functionName)) - { - return nullptr; + getSink()->diagnose(attr->args[0], Diagnostics::unknownImageFormatName, formatName); } - dllImportAttr->functionName = functionName; } - else if (auto rayPayloadAttr = as<VulkanRayPayloadAttribute>(attr)) - { - SLANG_ASSERT(attr->args.getCount() == 1); - auto val = checkConstantIntVal(attr->args[0]); - if (!val) return nullptr; + formatAttr->format = format; + } + else if (auto allowAttr = as<AllowAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); - rayPayloadAttr->location = (int32_t)val->getValue(); - } - else if (auto rayPayloadInAttr = as<VulkanRayPayloadInAttribute>(attr)) + String diagnosticName; + if (!checkLiteralStringVal(attr->args[0], &diagnosticName)) { - SLANG_ASSERT(attr->args.getCount() == 1); - auto val = checkConstantIntVal(attr->args[0]); - if (!val) return nullptr; - rayPayloadInAttr->location = (int32_t)val->getValue(); + return nullptr; } - else if (auto callablePayloadAttr = as<VulkanCallablePayloadAttribute>(attr)) - { - SLANG_ASSERT(attr->args.getCount() == 1); - auto val = checkConstantIntVal(attr->args[0]); - if (!val) return nullptr; - - callablePayloadAttr->location = (int32_t)val->getValue(); - } - else if (auto callablePayloadInAttr = as<VulkanCallablePayloadInAttribute>(attr)) + auto diagnosticInfo = findDiagnosticByName(diagnosticName.getUnownedSlice()); + if (!diagnosticInfo) { - SLANG_ASSERT(attr->args.getCount() == 1); - auto val = checkConstantIntVal(attr->args[0]); - if (!val) return nullptr; - callablePayloadInAttr->location = (int32_t)val->getValue(); + getSink()->diagnose(attr->args[0], Diagnostics::unknownDiagnosticName, diagnosticName); } - else if (auto hitObjectAttributesAttr = as<VulkanHitObjectAttributesAttribute>(attr)) - { - SLANG_ASSERT(attr->args.getCount() == 1); - auto val = checkConstantIntVal(attr->args[0]); - if (!val) return nullptr; - - hitObjectAttributesAttr->location = (int32_t)val->getValue(); - } - else if (auto constantIdAttr = as<VkConstantIdAttribute>(attr)) - { - SLANG_ASSERT(attr->args.getCount() == 1); - auto val = checkConstantIntVal(attr->args[0]); + allowAttr->diagnostic = diagnosticInfo; + } + else if (auto dllImportAttr = as<DllImportAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1 || attr->args.getCount() == 2); - if (!val) return nullptr; - constantIdAttr->location = (int32_t)val->getValue(); - } - else if (as<UserDefinedDerivativeAttribute>(attr) || as<PrimalSubstituteAttribute>(attr)) + String libraryName; + if (!checkLiteralStringVal(dllImportAttr->args[0], &libraryName)) { - SLANG_ASSERT(attr->args.getCount() == 1); - SLANG_ASSERT(as<Decl>(attrTarget)); - if (auto derivativeAttr = as<UserDefinedDerivativeAttribute>(attr)) - derivativeAttr->funcExpr = attr->args[0]; - else if (auto primalSubstAttr = as<PrimalSubstituteAttribute>(attr)) - primalSubstAttr->funcExpr = attr->args[0]; + return nullptr; } - else if (as<DerivativeOfAttribute>(attr) || as<PrimalSubstituteOfAttribute>(attr)) + dllImportAttr->modulePath = libraryName; + + String functionName; + if (dllImportAttr->args.getCount() == 2 && + !checkLiteralStringVal(dllImportAttr->args[1], &functionName)) { - SLANG_ASSERT(attr->args.getCount() == 1); - SLANG_ASSERT(as<Decl>(attrTarget)); - if (auto derivativeOfAttr = as<DerivativeOfAttribute>(attr)) - derivativeOfAttr->funcExpr = attr->args[0]; - else if (auto primalOfAttr = as<PrimalSubstituteOfAttribute>(attr)) - primalOfAttr->funcExpr = attr->args[0]; + return nullptr; } - else if (auto preferRecomputeAttr = as<PreferRecomputeAttribute>(attr)) - { - SLANG_ASSERT(attr->args.getCount() == 1); - SLANG_ASSERT(as<Decl>(attrTarget)); + dllImportAttr->functionName = functionName; + } + else if (auto rayPayloadAttr = as<VulkanRayPayloadAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto val = checkConstantIntVal(attr->args[0]); - auto val = checkConstantIntVal(attr->args[0]); - if (!val) return nullptr; + if (!val) + return nullptr; - preferRecomputeAttr->sideEffectBehavior = (PreferRecomputeAttribute::SideEffectBehavior) val->getValue(); + rayPayloadAttr->location = (int32_t)val->getValue(); + } + else if (auto rayPayloadInAttr = as<VulkanRayPayloadInAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto val = checkConstantIntVal(attr->args[0]); + if (!val) + return nullptr; + rayPayloadInAttr->location = (int32_t)val->getValue(); + } + else if (auto callablePayloadAttr = as<VulkanCallablePayloadAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto val = checkConstantIntVal(attr->args[0]); + + if (!val) + return nullptr; + + callablePayloadAttr->location = (int32_t)val->getValue(); + } + else if (auto callablePayloadInAttr = as<VulkanCallablePayloadInAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto val = checkConstantIntVal(attr->args[0]); + if (!val) + return nullptr; + callablePayloadInAttr->location = (int32_t)val->getValue(); + } + else if (auto hitObjectAttributesAttr = as<VulkanHitObjectAttributesAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto val = checkConstantIntVal(attr->args[0]); + + if (!val) + return nullptr; + + hitObjectAttributesAttr->location = (int32_t)val->getValue(); + } + else if (auto constantIdAttr = as<VkConstantIdAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + auto val = checkConstantIntVal(attr->args[0]); + + if (!val) + return nullptr; + constantIdAttr->location = (int32_t)val->getValue(); + } + else if (as<UserDefinedDerivativeAttribute>(attr) || as<PrimalSubstituteAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + SLANG_ASSERT(as<Decl>(attrTarget)); + if (auto derivativeAttr = as<UserDefinedDerivativeAttribute>(attr)) + derivativeAttr->funcExpr = attr->args[0]; + else if (auto primalSubstAttr = as<PrimalSubstituteAttribute>(attr)) + primalSubstAttr->funcExpr = attr->args[0]; + } + else if (as<DerivativeOfAttribute>(attr) || as<PrimalSubstituteOfAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + SLANG_ASSERT(as<Decl>(attrTarget)); + if (auto derivativeOfAttr = as<DerivativeOfAttribute>(attr)) + derivativeOfAttr->funcExpr = attr->args[0]; + else if (auto primalOfAttr = as<PrimalSubstituteOfAttribute>(attr)) + primalOfAttr->funcExpr = attr->args[0]; + } + else if (auto preferRecomputeAttr = as<PreferRecomputeAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + SLANG_ASSERT(as<Decl>(attrTarget)); + + auto val = checkConstantIntVal(attr->args[0]); + if (!val) + return nullptr; + + preferRecomputeAttr->sideEffectBehavior = + (PreferRecomputeAttribute::SideEffectBehavior)val->getValue(); + } + else if (auto comInterfaceAttr = as<ComInterfaceAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + String guid; + if (!checkLiteralStringVal(comInterfaceAttr->args[0], &guid)) + { + return nullptr; } - else if (auto comInterfaceAttr = as<ComInterfaceAttribute>(attr)) + StringBuilder resultGUID; + for (auto ch : guid) { - SLANG_ASSERT(attr->args.getCount() == 1); - String guid; - if (!checkLiteralStringVal(comInterfaceAttr->args[0], &guid)) + if (CharUtil::isHexDigit(ch)) { - return nullptr; + resultGUID.appendChar(ch); } - StringBuilder resultGUID; - for (auto ch : guid) + else if (ch == '-') { - if (CharUtil::isHexDigit(ch)) - { - resultGUID.appendChar(ch); - } - else if (ch == '-') - { - continue; - } - else - { - getSink()->diagnose(attr, Diagnostics::invalidGUID, guid); - return nullptr; - } + continue; } - comInterfaceAttr->guid = resultGUID.toString(); - if (comInterfaceAttr->guid.getLength() != 32) + else { getSink()->diagnose(attr, Diagnostics::invalidGUID, guid); return nullptr; } } - else if (const auto derivativeMemberAttr = as<DerivativeMemberAttribute>(attr)) + comInterfaceAttr->guid = resultGUID.toString(); + if (comInterfaceAttr->guid.getLength() != 32) { - auto varDecl = as<VarDeclBase>(attrTarget); - if (!varDecl) - { - getSink()->diagnose(attr, Diagnostics::attributeNotApplicable, attr->getKeywordName()); - return nullptr; - } + getSink()->diagnose(attr, Diagnostics::invalidGUID, guid); + return nullptr; } - else if (auto deprecatedAttr = as<DeprecatedAttribute>(attr)) + } + else if (const auto derivativeMemberAttr = as<DerivativeMemberAttribute>(attr)) + { + auto varDecl = as<VarDeclBase>(attrTarget); + if (!varDecl) { - SLANG_ASSERT(attr->args.getCount() == 1); - - String message; - if(!checkLiteralStringVal(attr->args[0], &message)) - { - return nullptr; - } - - deprecatedAttr->message = message; + getSink()->diagnose(attr, Diagnostics::attributeNotApplicable, attr->getKeywordName()); + return nullptr; } - else if (auto knownBuiltinAttr = as<KnownBuiltinAttribute>(attr)) + } + else if (auto deprecatedAttr = as<DeprecatedAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); + + String message; + if (!checkLiteralStringVal(attr->args[0], &message)) { - SLANG_ASSERT(attr->args.getCount() == 1); + return nullptr; + } - String name; - if(!checkLiteralStringVal(attr->args[0], &name)) - { - return nullptr; - } + deprecatedAttr->message = message; + } + else if (auto knownBuiltinAttr = as<KnownBuiltinAttribute>(attr)) + { + SLANG_ASSERT(attr->args.getCount() == 1); - knownBuiltinAttr->name = name; - } - else if (auto pyExportAttr = as<PyExportAttribute>(attr)) + String name; + if (!checkLiteralStringVal(attr->args[0], &name)) { - // Check name string. - SLANG_ASSERT(attr->args.getCount() == 1); + return nullptr; + } - String name; - if(!checkLiteralStringVal(attr->args[0], &name)) - { - return nullptr; - } + knownBuiltinAttr->name = name; + } + else if (auto pyExportAttr = as<PyExportAttribute>(attr)) + { + // Check name string. + SLANG_ASSERT(attr->args.getCount() == 1); - pyExportAttr->name = name; - } - else if (auto requireCapAttr = as<RequireCapabilityAttribute>(attr)) + String name; + if (!checkLiteralStringVal(attr->args[0], &name)) { - List<CapabilityName> capabilityNames; - for (auto& arg : attr->args) - { - CapabilityName capName; - if (checkCapabilityName(arg, capName)) - { - capabilityNames.add(capName); - if(isInternalCapabilityName(capName)) - maybeDiagnose(getSink(), this->getOptionSet(), DiagnosticCategory::Capability, attr, Diagnostics::usingInternalCapabilityName, attr, capName); - } - } - requireCapAttr->capabilitySet = CapabilitySet(capabilityNames); - if (requireCapAttr->capabilitySet.isInvalid()) - maybeDiagnose(getSink(), this->getOptionSet(), DiagnosticCategory::Capability, attr, Diagnostics::unexpectedCapability, attr, CapabilityName::Invalid); + return nullptr; } - else if (auto requirePreludeAttr = as<RequirePreludeAttribute>(attr)) + + pyExportAttr->name = name; + } + else if (auto requireCapAttr = as<RequireCapabilityAttribute>(attr)) + { + List<CapabilityName> capabilityNames; + for (auto& arg : attr->args) { - if (attr->args.getCount() > 2) - { - getSink()->diagnose(attr, Diagnostics::tooManyArguments, attr->args.getCount(), 0); - return nullptr; - } - else if (attr->args.getCount() < 2) - { - getSink()->diagnose(attr, Diagnostics::notEnoughArguments, attr->args.getCount(), 2); - return nullptr; - } CapabilityName capName; - if (!checkCapabilityName(attr->args[0], capName)) - { - return nullptr; - } - requirePreludeAttr->capabilitySet = CapabilitySet(capName); - if (auto stringLitExpr = as<StringLiteralExpr>(attr->args[1])) - { - requirePreludeAttr->prelude = getStringLiteralTokenValue(stringLitExpr->token); - } - else + if (checkCapabilityName(arg, capName)) { - getSink()->diagnose(attr->args[1], Diagnostics::expectedAStringLiteral); - return nullptr; - } - return attr; + capabilityNames.add(capName); + if (isInternalCapabilityName(capName)) + maybeDiagnose( + getSink(), + this->getOptionSet(), + DiagnosticCategory::Capability, + attr, + Diagnostics::usingInternalCapabilityName, + attr, + capName); + } + } + requireCapAttr->capabilitySet = CapabilitySet(capabilityNames); + if (requireCapAttr->capabilitySet.isInvalid()) + maybeDiagnose( + getSink(), + this->getOptionSet(), + DiagnosticCategory::Capability, + attr, + Diagnostics::unexpectedCapability, + attr, + CapabilityName::Invalid); + } + else if (auto requirePreludeAttr = as<RequirePreludeAttribute>(attr)) + { + if (attr->args.getCount() > 2) + { + getSink()->diagnose(attr, Diagnostics::tooManyArguments, attr->args.getCount(), 0); + return nullptr; + } + else if (attr->args.getCount() < 2) + { + getSink()->diagnose(attr, Diagnostics::notEnoughArguments, attr->args.getCount(), 2); + return nullptr; + } + CapabilityName capName; + if (!checkCapabilityName(attr->args[0], capName)) + { + return nullptr; + } + requirePreludeAttr->capabilitySet = CapabilitySet(capName); + if (auto stringLitExpr = as<StringLiteralExpr>(attr->args[1])) + { + requirePreludeAttr->prelude = getStringLiteralTokenValue(stringLitExpr->token); } else { - if(attr->args.getCount() == 0) - { - // If the attribute took no arguments, then we will - // assume it is valid as written. - } - else - { - // We should be special-casing the checking of any attribute - // with a non-zero number of arguments. - getSink()->diagnose(attr, Diagnostics::tooManyArguments, attr->args.getCount(), 0); - return nullptr; - } + getSink()->diagnose(attr->args[1], Diagnostics::expectedAStringLiteral); + return nullptr; } - return attr; } - - AttributeBase* SemanticsVisitor::checkAttribute( - UncheckedAttribute* uncheckedAttr, - ModifiableSyntaxNode* attrTarget) + else { - auto attrName = uncheckedAttr->getKeywordName(); - auto attrDecl = lookUpAttributeDecl( - attrName, - uncheckedAttr->scope); - - if(!attrDecl) + if (attr->args.getCount() == 0) { - getSink()->diagnose(uncheckedAttr, Diagnostics::unknownAttributeName, attrName); - return uncheckedAttr; + // If the attribute took no arguments, then we will + // assume it is valid as written. } - - if (!attrDecl->syntaxClass.isSubClassOf<Attribute>()) + else { - SLANG_DIAGNOSE_UNEXPECTED(getSink(), attrDecl, "attribute declaration does not reference an attribute class"); - return uncheckedAttr; + // We should be special-casing the checking of any attribute + // with a non-zero number of arguments. + getSink()->diagnose(attr, Diagnostics::tooManyArguments, attr->args.getCount(), 0); + return nullptr; } + } - // Manage scope - NodeBase* attrInstance = attrDecl->syntaxClass.createInstance(m_astBuilder); - auto attr = as<Attribute>(attrInstance); - if(!attr) - { - SLANG_DIAGNOSE_UNEXPECTED(getSink(), attrDecl, "attribute class did not yield an attribute object"); - return uncheckedAttr; - } + return attr; +} - // We are going to replace the unchecked attribute with the checked one. +AttributeBase* SemanticsVisitor::checkAttribute( + UncheckedAttribute* uncheckedAttr, + ModifiableSyntaxNode* attrTarget) +{ + auto attrName = uncheckedAttr->getKeywordName(); + auto attrDecl = lookUpAttributeDecl(attrName, uncheckedAttr->scope); - // First copy all of the state over from the original attribute. - attr->keywordName = uncheckedAttr->keywordName; - attr->originalIdentifierToken = uncheckedAttr->originalIdentifierToken; - attr->args = uncheckedAttr->args; - attr->loc = uncheckedAttr->loc; - attr->attributeDecl = attrDecl; + if (!attrDecl) + { + getSink()->diagnose(uncheckedAttr, Diagnostics::unknownAttributeName, attrName); + return uncheckedAttr; + } - // We will start with checking steps that can be applied independent - // of the concrete attribute type that was selected. These only need - // us to look at the attribute declaration itself. - // - // Start by doing argument/parameter matching - UInt argCount = attr->args.getCount(); - UInt paramCounter = 0; - bool mismatch = false; - for(auto paramDecl : attrDecl->getMembersOfType<ParamDecl>()) - { - UInt paramIndex = paramCounter++; - if( paramIndex < argCount ) + if (!attrDecl->syntaxClass.isSubClassOf<Attribute>()) + { + SLANG_DIAGNOSE_UNEXPECTED( + getSink(), + attrDecl, + "attribute declaration does not reference an attribute class"); + return uncheckedAttr; + } + + // Manage scope + NodeBase* attrInstance = attrDecl->syntaxClass.createInstance(m_astBuilder); + auto attr = as<Attribute>(attrInstance); + if (!attr) + { + SLANG_DIAGNOSE_UNEXPECTED( + getSink(), + attrDecl, + "attribute class did not yield an attribute object"); + return uncheckedAttr; + } + + // We are going to replace the unchecked attribute with the checked one. + + // First copy all of the state over from the original attribute. + attr->keywordName = uncheckedAttr->keywordName; + attr->originalIdentifierToken = uncheckedAttr->originalIdentifierToken; + attr->args = uncheckedAttr->args; + attr->loc = uncheckedAttr->loc; + attr->attributeDecl = attrDecl; + + // We will start with checking steps that can be applied independent + // of the concrete attribute type that was selected. These only need + // us to look at the attribute declaration itself. + // + // Start by doing argument/parameter matching + UInt argCount = attr->args.getCount(); + UInt paramCounter = 0; + bool mismatch = false; + for (auto paramDecl : attrDecl->getMembersOfType<ParamDecl>()) + { + UInt paramIndex = paramCounter++; + if (paramIndex < argCount) + { + // TODO: support checking the argument against the declared + // type for the parameter. + } + else + { + // We didn't have enough arguments for the + // number of parameters declared. + if (const auto defaultArg = paramDecl->initExpr) { - // TODO: support checking the argument against the declared - // type for the parameter. + // The attribute declaration provided a default, + // so we should use that. + // + // TODO: we need to figure out how to hook up + // default arguments as needed. + // For now just copy the expression over. + + attr->args.add(paramDecl->initExpr); } else { - // We didn't have enough arguments for the - // number of parameters declared. - if(const auto defaultArg = paramDecl->initExpr) - { - // The attribute declaration provided a default, - // so we should use that. - // - // TODO: we need to figure out how to hook up - // default arguments as needed. - // For now just copy the expression over. - - attr->args.add(paramDecl->initExpr); - } - else - { - mismatch = true; - } + mismatch = true; } } - UInt paramCount = paramCounter; + } + UInt paramCount = paramCounter; - if(mismatch) - { - getSink()->diagnose(attr, Diagnostics::attributeArgumentCountMismatch, attrName, paramCount, argCount); - return uncheckedAttr; - } + if (mismatch) + { + getSink()->diagnose( + attr, + Diagnostics::attributeArgumentCountMismatch, + attrName, + paramCount, + argCount); + return uncheckedAttr; + } - // The next bit of validation that we can apply semi-generically - // is to validate that the target for this attribute is a valid - // one for the chosen attribute. - // - // The attribute declaration will have one or more `AttributeTargetModifier`s - // that each specify a syntax class that the attribute can be applied to. - // If any of these match `attrTarget`, then we are good. - // - bool validTarget = false; - for(auto attrTargetMod : attrDecl->getModifiersOfType<AttributeTargetModifier>()) - { - if(attrTarget->getClass().isSubClassOf(attrTargetMod->syntaxClass)) - { - validTarget = true; - break; - } - } - if(!validTarget) + // The next bit of validation that we can apply semi-generically + // is to validate that the target for this attribute is a valid + // one for the chosen attribute. + // + // The attribute declaration will have one or more `AttributeTargetModifier`s + // that each specify a syntax class that the attribute can be applied to. + // If any of these match `attrTarget`, then we are good. + // + bool validTarget = false; + for (auto attrTargetMod : attrDecl->getModifiersOfType<AttributeTargetModifier>()) + { + if (attrTarget->getClass().isSubClassOf(attrTargetMod->syntaxClass)) { - getSink()->diagnose(attr, Diagnostics::attributeNotApplicable, attrName); - return uncheckedAttr; + validTarget = true; + break; } + } + if (!validTarget) + { + getSink()->diagnose(attr, Diagnostics::attributeNotApplicable, attrName); + return uncheckedAttr; + } - // Now apply type-specific validation to the attribute. - if(!validateAttribute(attr, attrDecl, attrTarget)) - { - return uncheckedAttr; - } + // Now apply type-specific validation to the attribute. + if (!validateAttribute(attr, attrDecl, attrTarget)) + { + return uncheckedAttr; + } - return attr; + return attr; +} + +ASTNodeType getModifierConflictGroupKind(ASTNodeType modifierType) +{ + switch (modifierType) + { + // Allowed only on parameters and global variables. + case ASTNodeType::InModifier: return modifierType; + case ASTNodeType::OutModifier: + case ASTNodeType::RefModifier: + case ASTNodeType::ConstRefModifier: + case ASTNodeType::InOutModifier: + return ASTNodeType::OutModifier; + + // Modifiers that are their own exclusive group. + case ASTNodeType::GLSLLayoutModifier: + case ASTNodeType::GLSLParsedLayoutModifier: + case ASTNodeType::GLSLLocationLayoutModifier: + case ASTNodeType::GLSLInputAttachmentIndexLayoutAttribute: + case ASTNodeType::GLSLOffsetLayoutAttribute: + case ASTNodeType::GLSLUnparsedLayoutModifier: + case ASTNodeType::GLSLLayoutModifierGroupMarker: + case ASTNodeType::GLSLLayoutModifierGroupBegin: + case ASTNodeType::GLSLLayoutModifierGroupEnd: + case ASTNodeType::GLSLBufferModifier: + case ASTNodeType::MemoryQualifierSetModifier: + case ASTNodeType::GLSLWriteOnlyModifier: + case ASTNodeType::GLSLReadOnlyModifier: + case ASTNodeType::GLSLVolatileModifier: + case ASTNodeType::GLSLRestrictModifier: + case ASTNodeType::GLSLPatchModifier: + case ASTNodeType::RayPayloadAccessSemantic: + case ASTNodeType::RayPayloadReadSemantic: + case ASTNodeType::RayPayloadWriteSemantic: + case ASTNodeType::GloballyCoherentModifier: + case ASTNodeType::PreciseModifier: + case ASTNodeType::IntrinsicOpModifier: + case ASTNodeType::InlineModifier: + case ASTNodeType::HLSLExportModifier: + case ASTNodeType::ExternCppModifier: + case ASTNodeType::ExportedModifier: + case ASTNodeType::ConstModifier: + case ASTNodeType::ConstExprModifier: + case ASTNodeType::MatrixLayoutModifier: + case ASTNodeType::RowMajorLayoutModifier: + case ASTNodeType::HLSLRowMajorLayoutModifier: + case ASTNodeType::GLSLColumnMajorLayoutModifier: + case ASTNodeType::ColumnMajorLayoutModifier: + case ASTNodeType::HLSLColumnMajorLayoutModifier: + case ASTNodeType::GLSLRowMajorLayoutModifier: + case ASTNodeType::HLSLEffectSharedModifier: + case ASTNodeType::HLSLVolatileModifier: + case ASTNodeType::GLSLPrecisionModifier: + case ASTNodeType::HLSLGroupSharedModifier: return modifierType; + + case ASTNodeType::HLSLStaticModifier: + case ASTNodeType::ActualGlobalModifier: + case ASTNodeType::HLSLUniformModifier: return ASTNodeType::HLSLStaticModifier; + + case ASTNodeType::HLSLNoInterpolationModifier: + case ASTNodeType::HLSLNoPerspectiveModifier: + case ASTNodeType::HLSLLinearModifier: + case ASTNodeType::HLSLSampleModifier: + case ASTNodeType::HLSLCentroidModifier: + case ASTNodeType::PerVertexModifier: return ASTNodeType::InterpolationModeModifier; + + case ASTNodeType::PrefixModifier: + case ASTNodeType::PostfixModifier: return ASTNodeType::PrefixModifier; + + case ASTNodeType::BuiltinModifier: + case ASTNodeType::PublicModifier: + case ASTNodeType::PrivateModifier: + case ASTNodeType::InternalModifier: return ASTNodeType::VisibilityModifier; + + default: return ASTNodeType::NodeBase; } +} - ASTNodeType getModifierConflictGroupKind(ASTNodeType modifierType) - { - switch (modifierType) - { - // Allowed only on parameters and global variables. - case ASTNodeType::InModifier: - return modifierType; - case ASTNodeType::OutModifier: - case ASTNodeType::RefModifier: - case ASTNodeType::ConstRefModifier: - case ASTNodeType::InOutModifier: - return ASTNodeType::OutModifier; - - // Modifiers that are their own exclusive group. - case ASTNodeType::GLSLLayoutModifier: - case ASTNodeType::GLSLParsedLayoutModifier: - case ASTNodeType::GLSLLocationLayoutModifier: - case ASTNodeType::GLSLInputAttachmentIndexLayoutAttribute: - case ASTNodeType::GLSLOffsetLayoutAttribute: - case ASTNodeType::GLSLUnparsedLayoutModifier: - case ASTNodeType::GLSLLayoutModifierGroupMarker: - case ASTNodeType::GLSLLayoutModifierGroupBegin: - case ASTNodeType::GLSLLayoutModifierGroupEnd: - case ASTNodeType::GLSLBufferModifier: - case ASTNodeType::MemoryQualifierSetModifier: - case ASTNodeType::GLSLWriteOnlyModifier: - case ASTNodeType::GLSLReadOnlyModifier: - case ASTNodeType::GLSLVolatileModifier: - case ASTNodeType::GLSLRestrictModifier: - case ASTNodeType::GLSLPatchModifier: - case ASTNodeType::RayPayloadAccessSemantic: - case ASTNodeType::RayPayloadReadSemantic: - case ASTNodeType::RayPayloadWriteSemantic: - case ASTNodeType::GloballyCoherentModifier: - case ASTNodeType::PreciseModifier: - case ASTNodeType::IntrinsicOpModifier: - case ASTNodeType::InlineModifier: - case ASTNodeType::HLSLExportModifier: - case ASTNodeType::ExternCppModifier: - case ASTNodeType::ExportedModifier: - case ASTNodeType::ConstModifier: - case ASTNodeType::ConstExprModifier: - case ASTNodeType::MatrixLayoutModifier: - case ASTNodeType::RowMajorLayoutModifier: - case ASTNodeType::HLSLRowMajorLayoutModifier: - case ASTNodeType::GLSLColumnMajorLayoutModifier: - case ASTNodeType::ColumnMajorLayoutModifier: - case ASTNodeType::HLSLColumnMajorLayoutModifier: - case ASTNodeType::GLSLRowMajorLayoutModifier: - case ASTNodeType::HLSLEffectSharedModifier: - case ASTNodeType::HLSLVolatileModifier: - case ASTNodeType::GLSLPrecisionModifier: - case ASTNodeType::HLSLGroupSharedModifier: - return modifierType; - - case ASTNodeType::HLSLStaticModifier: - case ASTNodeType::ActualGlobalModifier: - case ASTNodeType::HLSLUniformModifier: - return ASTNodeType::HLSLStaticModifier; - - case ASTNodeType::HLSLNoInterpolationModifier: - case ASTNodeType::HLSLNoPerspectiveModifier: - case ASTNodeType::HLSLLinearModifier: - case ASTNodeType::HLSLSampleModifier: - case ASTNodeType::HLSLCentroidModifier: - case ASTNodeType::PerVertexModifier: - return ASTNodeType::InterpolationModeModifier; - - case ASTNodeType::PrefixModifier: - case ASTNodeType::PostfixModifier: - return ASTNodeType::PrefixModifier; - - case ASTNodeType::BuiltinModifier: - case ASTNodeType::PublicModifier: - case ASTNodeType::PrivateModifier: - case ASTNodeType::InternalModifier: - return ASTNodeType::VisibilityModifier; - - default: - return ASTNodeType::NodeBase; - } - } - - bool isModifierAllowedOnDecl(bool isGLSLInput, ASTNodeType modifierType, Decl* decl) - { - switch (modifierType) - { - // In addition to the above cases, these are also present on empty - // global declarations, for instance - // layout(local_size_x=1) in; - case ASTNodeType::InModifier: - case ASTNodeType::InOutModifier: - case ASTNodeType::OutModifier: - case ASTNodeType::GLSLLayoutModifier: - case ASTNodeType::GLSLParsedLayoutModifier: - case ASTNodeType::GLSLLocationLayoutModifier: - case ASTNodeType::GLSLInputAttachmentIndexLayoutAttribute: - case ASTNodeType::GLSLOffsetLayoutAttribute: - case ASTNodeType::GLSLUnparsedLayoutModifier: - case ASTNodeType::GLSLLayoutModifierGroupMarker: - case ASTNodeType::GLSLLayoutModifierGroupBegin: - case ASTNodeType::GLSLLayoutModifierGroupEnd: - // If we are in GLSL mode, also allow these but otherwise fall to - // the regular check - if(isGLSLInput && as<EmptyDecl>(decl) && isGlobalDecl(decl)) - return true; - [[fallthrough]]; - - case ASTNodeType::RefModifier: - case ASTNodeType::ConstRefModifier: - case ASTNodeType::GLSLBufferModifier: - case ASTNodeType::GLSLPatchModifier: - case ASTNodeType::RayPayloadAccessSemantic: - case ASTNodeType::RayPayloadReadSemantic: - case ASTNodeType::RayPayloadWriteSemantic: - return (as<VarDeclBase>(decl) && isGlobalDecl(decl)) || as<ParamDecl>(decl) || as<GLSLInterfaceBlockDecl>(decl); - - case ASTNodeType::GLSLWriteOnlyModifier: - case ASTNodeType::GLSLReadOnlyModifier: - case ASTNodeType::GLSLVolatileModifier: - case ASTNodeType::GLSLRestrictModifier: - if(isGLSLInput) - return (as<VarDeclBase>(decl) && (isGlobalDecl(decl)) || as<ParamDecl>(decl) || as<GLSLInterfaceBlockDecl>(decl)) - || as<StructDecl>(getParentDecl(decl)) && isGlobalDecl(getParentDecl(decl)); - return (as<VarDeclBase>(decl) && (isGlobalDecl(decl)) || as<ParamDecl>(decl) || as<GLSLInterfaceBlockDecl>(decl)); - - case ASTNodeType::GloballyCoherentModifier: - case ASTNodeType::HLSLVolatileModifier: - if(isGLSLInput) - return as<VarDecl>(decl) && (isGlobalDecl(decl) || as<StructDecl>(getParentDecl(decl)) || as<GLSLInterfaceBlockDecl>(decl)) - || as<VarDeclBase>(decl) && isGlobalDecl(decl) || as<ParamDecl>(decl) || (as<StructDecl>(getParentDecl(decl)) && isGlobalDecl(getParentDecl(decl))); - return as<VarDecl>(decl) && (isGlobalDecl(decl) || as<StructDecl>(getParentDecl(decl)) || as<GLSLInterfaceBlockDecl>(decl)); - - // Allowed only on parameters, struct fields and global variables. - case ASTNodeType::InterpolationModeModifier: - case ASTNodeType::HLSLNoInterpolationModifier: - case ASTNodeType::HLSLNoPerspectiveModifier: - case ASTNodeType::HLSLLinearModifier: - case ASTNodeType::HLSLSampleModifier: - case ASTNodeType::HLSLCentroidModifier: - case ASTNodeType::PerVertexModifier: - case ASTNodeType::HLSLUniformModifier: - case ASTNodeType::DynamicUniformModifier: - return (as<VarDeclBase>(decl) && (isGlobalDecl(decl) || as<StructDecl>(getParentDecl(decl)))) || as<ParamDecl>(decl); - - case ASTNodeType::HLSLSemantic: - case ASTNodeType::HLSLLayoutSemantic: - case ASTNodeType::HLSLRegisterSemantic: - case ASTNodeType::HLSLPackOffsetSemantic: - case ASTNodeType::HLSLSimpleSemantic: - return (as<VarDeclBase>(decl) && (isGlobalDecl(decl) || as<StructDecl>(getParentDecl(decl)))) || as<ParamDecl>(decl) || as<FuncDecl>(decl); - - // Allowed only on functions - case ASTNodeType::IntrinsicOpModifier: - case ASTNodeType::SpecializedForTargetModifier: - case ASTNodeType::InlineModifier: - case ASTNodeType::PrefixModifier: - case ASTNodeType::PostfixModifier: - return as<CallableDecl>(decl); - - case ASTNodeType::BuiltinModifier: - case ASTNodeType::PublicModifier: - case ASTNodeType::PrivateModifier: - case ASTNodeType::InternalModifier: - case ASTNodeType::ExternModifier: - case ASTNodeType::HLSLExportModifier: - case ASTNodeType::ExternCppModifier: - return as<VarDeclBase>(decl) || as<AggTypeDeclBase>(decl) || as<NamespaceDeclBase>(decl) || as<CallableDecl>(decl) - || as<TypeDefDecl>(decl) || as<PropertyDecl>(decl) || as<SyntaxDecl>(decl) || as<AttributeDecl>(decl) - || as<InheritanceDecl>(decl); - - case ASTNodeType::ExportedModifier: - return as<ImportDecl>(decl); - - case ASTNodeType::ConstModifier: - case ASTNodeType::HLSLStaticModifier: - case ASTNodeType::ConstExprModifier: - case ASTNodeType::PreciseModifier: - return as<VarDeclBase>(decl) || as<CallableDecl>(decl); - - case ASTNodeType::ActualGlobalModifier: - case ASTNodeType::MatrixLayoutModifier: - case ASTNodeType::RowMajorLayoutModifier: - case ASTNodeType::HLSLRowMajorLayoutModifier: - case ASTNodeType::GLSLColumnMajorLayoutModifier: - case ASTNodeType::ColumnMajorLayoutModifier: - case ASTNodeType::HLSLColumnMajorLayoutModifier: - case ASTNodeType::GLSLRowMajorLayoutModifier: - case ASTNodeType::HLSLEffectSharedModifier: - return as<VarDeclBase>(decl) || as<GLSLInterfaceBlockDecl>(decl); - - case ASTNodeType::GLSLPrecisionModifier: - return as<VarDeclBase>(decl) || as<GLSLInterfaceBlockDecl>(decl) || as<CallableDecl>(decl); - case ASTNodeType::HLSLGroupSharedModifier: - // groupshared must be global or static. - if (!as<VarDeclBase>(decl)) - return false; - return isGlobalDecl(decl) || isEffectivelyStatic(decl); - default: +bool isModifierAllowedOnDecl(bool isGLSLInput, ASTNodeType modifierType, Decl* decl) +{ + switch (modifierType) + { + // In addition to the above cases, these are also present on empty + // global declarations, for instance + // layout(local_size_x=1) in; + case ASTNodeType::InModifier: + case ASTNodeType::InOutModifier: + case ASTNodeType::OutModifier: + case ASTNodeType::GLSLLayoutModifier: + case ASTNodeType::GLSLParsedLayoutModifier: + case ASTNodeType::GLSLLocationLayoutModifier: + case ASTNodeType::GLSLInputAttachmentIndexLayoutAttribute: + case ASTNodeType::GLSLOffsetLayoutAttribute: + case ASTNodeType::GLSLUnparsedLayoutModifier: + case ASTNodeType::GLSLLayoutModifierGroupMarker: + case ASTNodeType::GLSLLayoutModifierGroupBegin: + case ASTNodeType::GLSLLayoutModifierGroupEnd: + // If we are in GLSL mode, also allow these but otherwise fall to + // the regular check + if (isGLSLInput && as<EmptyDecl>(decl) && isGlobalDecl(decl)) return true; - } + [[fallthrough]]; + + case ASTNodeType::RefModifier: + case ASTNodeType::ConstRefModifier: + case ASTNodeType::GLSLBufferModifier: + case ASTNodeType::GLSLPatchModifier: + case ASTNodeType::RayPayloadAccessSemantic: + case ASTNodeType::RayPayloadReadSemantic: + case ASTNodeType::RayPayloadWriteSemantic: + return (as<VarDeclBase>(decl) && isGlobalDecl(decl)) || as<ParamDecl>(decl) || + as<GLSLInterfaceBlockDecl>(decl); + + case ASTNodeType::GLSLWriteOnlyModifier: + case ASTNodeType::GLSLReadOnlyModifier: + case ASTNodeType::GLSLVolatileModifier: + case ASTNodeType::GLSLRestrictModifier: + if (isGLSLInput) + return (as<VarDeclBase>(decl) && (isGlobalDecl(decl)) || as<ParamDecl>(decl) || + as<GLSLInterfaceBlockDecl>(decl)) || + as<StructDecl>(getParentDecl(decl)) && isGlobalDecl(getParentDecl(decl)); + return ( + as<VarDeclBase>(decl) && (isGlobalDecl(decl)) || as<ParamDecl>(decl) || + as<GLSLInterfaceBlockDecl>(decl)); + + case ASTNodeType::GloballyCoherentModifier: + case ASTNodeType::HLSLVolatileModifier: + if (isGLSLInput) + return as<VarDecl>(decl) && + (isGlobalDecl(decl) || as<StructDecl>(getParentDecl(decl)) || + as<GLSLInterfaceBlockDecl>(decl)) || + as<VarDeclBase>(decl) && isGlobalDecl(decl) || as<ParamDecl>(decl) || + (as<StructDecl>(getParentDecl(decl)) && isGlobalDecl(getParentDecl(decl))); + return as<VarDecl>(decl) && (isGlobalDecl(decl) || as<StructDecl>(getParentDecl(decl)) || + as<GLSLInterfaceBlockDecl>(decl)); + + // Allowed only on parameters, struct fields and global variables. + case ASTNodeType::InterpolationModeModifier: + case ASTNodeType::HLSLNoInterpolationModifier: + case ASTNodeType::HLSLNoPerspectiveModifier: + case ASTNodeType::HLSLLinearModifier: + case ASTNodeType::HLSLSampleModifier: + case ASTNodeType::HLSLCentroidModifier: + case ASTNodeType::PerVertexModifier: + case ASTNodeType::HLSLUniformModifier: + case ASTNodeType::DynamicUniformModifier: + return (as<VarDeclBase>(decl) && + (isGlobalDecl(decl) || as<StructDecl>(getParentDecl(decl)))) || + as<ParamDecl>(decl); + + case ASTNodeType::HLSLSemantic: + case ASTNodeType::HLSLLayoutSemantic: + case ASTNodeType::HLSLRegisterSemantic: + case ASTNodeType::HLSLPackOffsetSemantic: + case ASTNodeType::HLSLSimpleSemantic: + return (as<VarDeclBase>(decl) && + (isGlobalDecl(decl) || as<StructDecl>(getParentDecl(decl)))) || + as<ParamDecl>(decl) || as<FuncDecl>(decl); + + // Allowed only on functions + case ASTNodeType::IntrinsicOpModifier: + case ASTNodeType::SpecializedForTargetModifier: + case ASTNodeType::InlineModifier: + case ASTNodeType::PrefixModifier: + case ASTNodeType::PostfixModifier: return as<CallableDecl>(decl); + + case ASTNodeType::BuiltinModifier: + case ASTNodeType::PublicModifier: + case ASTNodeType::PrivateModifier: + case ASTNodeType::InternalModifier: + case ASTNodeType::ExternModifier: + case ASTNodeType::HLSLExportModifier: + case ASTNodeType::ExternCppModifier: + return as<VarDeclBase>(decl) || as<AggTypeDeclBase>(decl) || as<NamespaceDeclBase>(decl) || + as<CallableDecl>(decl) || as<TypeDefDecl>(decl) || as<PropertyDecl>(decl) || + as<SyntaxDecl>(decl) || as<AttributeDecl>(decl) || as<InheritanceDecl>(decl); + + case ASTNodeType::ExportedModifier: return as<ImportDecl>(decl); + + case ASTNodeType::ConstModifier: + case ASTNodeType::HLSLStaticModifier: + case ASTNodeType::ConstExprModifier: + case ASTNodeType::PreciseModifier: return as<VarDeclBase>(decl) || as<CallableDecl>(decl); + + case ASTNodeType::ActualGlobalModifier: + case ASTNodeType::MatrixLayoutModifier: + case ASTNodeType::RowMajorLayoutModifier: + case ASTNodeType::HLSLRowMajorLayoutModifier: + case ASTNodeType::GLSLColumnMajorLayoutModifier: + case ASTNodeType::ColumnMajorLayoutModifier: + case ASTNodeType::HLSLColumnMajorLayoutModifier: + case ASTNodeType::GLSLRowMajorLayoutModifier: + case ASTNodeType::HLSLEffectSharedModifier: + return as<VarDeclBase>(decl) || as<GLSLInterfaceBlockDecl>(decl); + + case ASTNodeType::GLSLPrecisionModifier: + return as<VarDeclBase>(decl) || as<GLSLInterfaceBlockDecl>(decl) || as<CallableDecl>(decl); + case ASTNodeType::HLSLGroupSharedModifier: + // groupshared must be global or static. + if (!as<VarDeclBase>(decl)) + return false; + return isGlobalDecl(decl) || isEffectivelyStatic(decl); + default: return true; } +} - Modifier* SemanticsVisitor::checkModifier( - Modifier* m, - ModifiableSyntaxNode* syntaxNode, - bool ignoreUnallowedModifier) +Modifier* SemanticsVisitor::checkModifier( + Modifier* m, + ModifiableSyntaxNode* syntaxNode, + bool ignoreUnallowedModifier) +{ + if (auto hlslUncheckedAttribute = as<UncheckedAttribute>(m)) { - if(auto hlslUncheckedAttribute = as<UncheckedAttribute>(m)) - { - // We have an HLSL `[name(arg,...)]` attribute, and we'd like - // to check that it is provides all the expected arguments - // - // First, look up the attribute name in the current scope to find - // the right syntax class to instantiate. - // + // We have an HLSL `[name(arg,...)]` attribute, and we'd like + // to check that it is provides all the expected arguments + // + // First, look up the attribute name in the current scope to find + // the right syntax class to instantiate. + // - auto checkedAttr = checkAttribute(hlslUncheckedAttribute, syntaxNode); + auto checkedAttr = checkAttribute(hlslUncheckedAttribute, syntaxNode); - if (as<UnscopedEnumAttribute>(checkedAttr)) - { - if (auto parentDecl = as<ContainerDecl>(getParentDecl(as<Decl>(syntaxNode)))) - parentDecl->invalidateMemberDictionary(); - return getASTBuilder()->create<TransparentModifier>(); - } - return checkedAttr; + if (as<UnscopedEnumAttribute>(checkedAttr)) + { + if (auto parentDecl = as<ContainerDecl>(getParentDecl(as<Decl>(syntaxNode)))) + parentDecl->invalidateMemberDictionary(); + return getASTBuilder()->create<TransparentModifier>(); } + return checkedAttr; + } - if (auto decl = as<Decl>(syntaxNode)) + if (auto decl = as<Decl>(syntaxNode)) + { + auto moduleDecl = getModuleDecl(decl); + bool isGLSLInput = getOptionSet().getBoolOption(CompilerOptionName::AllowGLSL); + if (!isGLSLInput && moduleDecl && moduleDecl->findModifier<GLSLModuleModifier>()) + isGLSLInput = true; + if (!isModifierAllowedOnDecl(isGLSLInput, m->astNodeType, decl)) { - auto moduleDecl = getModuleDecl(decl); - bool isGLSLInput = getOptionSet().getBoolOption(CompilerOptionName::AllowGLSL); - if (!isGLSLInput && moduleDecl && moduleDecl->findModifier<GLSLModuleModifier>()) - isGLSLInput = true; - if (!isModifierAllowedOnDecl(isGLSLInput, m->astNodeType, decl)) + if (!ignoreUnallowedModifier) { - if (!ignoreUnallowedModifier) - { - getSink()->diagnose(m, Diagnostics::modifierNotAllowed, m); - return nullptr; - } - return m; + getSink()->diagnose(m, Diagnostics::modifierNotAllowed, m); + return nullptr; } + return m; } + } - MemoryQualifierSetModifier::Flags::MemoryQualifiersBit memoryQualifierBit = MemoryQualifierSetModifier::Flags::kNone; - if(as<GloballyCoherentModifier>(m)) - memoryQualifierBit = MemoryQualifierSetModifier::Flags::kCoherent; - else if(as<GLSLReadOnlyModifier>(m)) - memoryQualifierBit = MemoryQualifierSetModifier::Flags::kReadOnly; - else if(as<GLSLWriteOnlyModifier>(m)) - memoryQualifierBit = MemoryQualifierSetModifier::Flags::kWriteOnly; - else if(as<GLSLVolatileModifier>(m)) - memoryQualifierBit = MemoryQualifierSetModifier::Flags::kVolatile; - else if(as<GLSLRestrictModifier>(m)) - memoryQualifierBit = MemoryQualifierSetModifier::Flags::kRestrict; - if(memoryQualifierBit != MemoryQualifierSetModifier::Flags::kNone) - { - bool newModifier = false; - MemoryQualifierSetModifier* memoryQualifiers = syntaxNode->findModifier<MemoryQualifierSetModifier>(); - if(!memoryQualifiers) - { - newModifier = true; - memoryQualifiers = getASTBuilder()->create<MemoryQualifierSetModifier>(); - } - memoryQualifiers->addQualifier(m, - memoryQualifierBit); - if (newModifier) - { - m->next = memoryQualifiers; - return memoryQualifiers; - } - return nullptr; + MemoryQualifierSetModifier::Flags::MemoryQualifiersBit memoryQualifierBit = + MemoryQualifierSetModifier::Flags::kNone; + if (as<GloballyCoherentModifier>(m)) + memoryQualifierBit = MemoryQualifierSetModifier::Flags::kCoherent; + else if (as<GLSLReadOnlyModifier>(m)) + memoryQualifierBit = MemoryQualifierSetModifier::Flags::kReadOnly; + else if (as<GLSLWriteOnlyModifier>(m)) + memoryQualifierBit = MemoryQualifierSetModifier::Flags::kWriteOnly; + else if (as<GLSLVolatileModifier>(m)) + memoryQualifierBit = MemoryQualifierSetModifier::Flags::kVolatile; + else if (as<GLSLRestrictModifier>(m)) + memoryQualifierBit = MemoryQualifierSetModifier::Flags::kRestrict; + if (memoryQualifierBit != MemoryQualifierSetModifier::Flags::kNone) + { + bool newModifier = false; + MemoryQualifierSetModifier* memoryQualifiers = + syntaxNode->findModifier<MemoryQualifierSetModifier>(); + if (!memoryQualifiers) + { + newModifier = true; + memoryQualifiers = getASTBuilder()->create<MemoryQualifierSetModifier>(); } - - if (auto hlslSemantic = as<HLSLSimpleSemantic>(m)) + memoryQualifiers->addQualifier(m, memoryQualifierBit); + if (newModifier) { - if (hlslSemantic->name.getName() == getSession()->getCompletionRequestTokenName()) - { - getLinkage()->contentAssistInfo.completionSuggestions.scopeKind = - CompletionSuggestions::ScopeKind::HLSLSemantics; - } + m->next = memoryQualifiers; + return memoryQualifiers; } + return nullptr; + } - if (const auto externModifier = as<ExternModifier>(m)) + if (auto hlslSemantic = as<HLSLSimpleSemantic>(m)) + { + if (hlslSemantic->name.getName() == getSession()->getCompletionRequestTokenName()) { - if (auto varDecl = as<VarDeclBase>(syntaxNode)) - { - if (auto parentExtension = as<ExtensionDecl>(varDecl->parentDecl)) - { - auto originalMemberLookup = lookUpMember(m_astBuilder, this, varDecl->getName(), parentExtension->targetType, parentExtension->ownedScope); - LookupResult filteredResult; - for (auto item : originalMemberLookup.items) - { - if (item.declRef.getDecl() != varDecl) - AddToLookupResult(filteredResult, item); - } - if (filteredResult.isValid() && !filteredResult.isOverloaded()) - { - auto extensionExternMemberModifier = m_astBuilder->create<ExtensionExternVarModifier>(); - extensionExternMemberModifier->originalDecl = filteredResult.item.declRef; - return extensionExternMemberModifier; - } - else if (filteredResult.isOverloaded()) - { - getSink()->diagnose(varDecl, Diagnostics::ambiguousOriginalDefintionOfExternDecl, varDecl); - } - else - { - getSink()->diagnose(varDecl, Diagnostics::missingOriginalDefintionOfExternDecl, varDecl); - } - } - // The next part of the check is to make sure the type defined here is consistent with the original definition. - // Since we haven't checked the type of this decl yet, we defer that until we have fully checked decl. - // See SemanticsDeclHeaderVisitor::checkExtensionExternVarAttribute. - } + getLinkage()->contentAssistInfo.completionSuggestions.scopeKind = + CompletionSuggestions::ScopeKind::HLSLSemantics; } + } - if (auto packOffsetModifier = as<HLSLPackOffsetSemantic>(m)) - { - if (!packOffsetModifier->registerName.getContent().startsWith("c")) - { - getSink()->diagnose(packOffsetModifier, Diagnostics::unknownRegisterClass, packOffsetModifier->registerName); - return m; - } - auto uniformOffset = stringToInt(packOffsetModifier->registerName.getContent().tail(1)) * 16; - if (packOffsetModifier->componentMask.getContentLength()) - { - switch (packOffsetModifier->componentMask.getContent()[0]) + if (const auto externModifier = as<ExternModifier>(m)) + { + if (auto varDecl = as<VarDeclBase>(syntaxNode)) + { + if (auto parentExtension = as<ExtensionDecl>(varDecl->parentDecl)) + { + auto originalMemberLookup = lookUpMember( + m_astBuilder, + this, + varDecl->getName(), + parentExtension->targetType, + parentExtension->ownedScope); + LookupResult filteredResult; + for (auto item : originalMemberLookup.items) { - case 'x': - uniformOffset += 0; - break; - case 'y': - uniformOffset += 4; - break; - case 'z': - uniformOffset += 8; - break; - case 'w': - uniformOffset += 12; - break; - default: - getSink()->diagnose(packOffsetModifier, Diagnostics::invalidComponentMask, packOffsetModifier->componentMask); - break; + if (item.declRef.getDecl() != varDecl) + AddToLookupResult(filteredResult, item); } - } - packOffsetModifier->uniformOffset = uniformOffset; - return packOffsetModifier; - } - - if(auto targetIntrinsic = as<TargetIntrinsicModifier>(m)) - { - // TODO: verify that the predicate is one we understand - if(targetIntrinsic->scrutinee.name) - { - if(auto genDecl = as<ContainerDecl>(syntaxNode)) + if (filteredResult.isValid() && !filteredResult.isOverloaded()) { - auto scrutineeResults = lookUp( - m_astBuilder, - this, - targetIntrinsic->scrutinee.name, - genDecl->ownedScope); - if(!scrutineeResults.isValid()) - { - getSink()->diagnose( - targetIntrinsic->scrutinee.loc, - Diagnostics::undefinedIdentifier2, - targetIntrinsic->scrutinee.name); - } - if(scrutineeResults.isOverloaded()) - { - getSink()->diagnose( - targetIntrinsic->scrutinee.loc, - Diagnostics::ambiguousReference, - targetIntrinsic->scrutinee.name); - } - targetIntrinsic->scrutineeDeclRef = scrutineeResults.item.declRef; + auto extensionExternMemberModifier = + m_astBuilder->create<ExtensionExternVarModifier>(); + extensionExternMemberModifier->originalDecl = filteredResult.item.declRef; + return extensionExternMemberModifier; } - } - } - - if (as<PrivateModifier>(m)) - { - if (auto decl = as<Decl>(syntaxNode)) - { - if (isGlobalDecl(decl)) + else if (filteredResult.isOverloaded()) { - getSink()->diagnose(m, Diagnostics::invalidUseOfPrivateVisibility, as<Decl>(syntaxNode)); - return m; + getSink()->diagnose( + varDecl, + Diagnostics::ambiguousOriginalDefintionOfExternDecl, + varDecl); } - } - if (as<NamespaceDeclBase>(syntaxNode)) - { - getSink()->diagnose(m, Diagnostics::invalidVisibilityModifierOnTypeOfDecl, syntaxNode->astNodeType); - return m; - } - else if (auto decl = as<Decl>(syntaxNode)) - { - // Interface requirements can't be private. - if (isInterfaceRequirement(decl)) + else { - getSink()->diagnose(m, Diagnostics::invalidUseOfPrivateVisibility, as<Decl>(syntaxNode)); + getSink()->diagnose( + varDecl, + Diagnostics::missingOriginalDefintionOfExternDecl, + varDecl); } } + // The next part of the check is to make sure the type defined here is consistent with + // the original definition. Since we haven't checked the type of this decl yet, we defer + // that until we have fully checked decl. See + // SemanticsDeclHeaderVisitor::checkExtensionExternVarAttribute. } - else if (as<InternalModifier>(m)) - { - if (as<NamespaceDeclBase>(syntaxNode)) - { - getSink()->diagnose(m, Diagnostics::invalidVisibilityModifierOnTypeOfDecl, syntaxNode->astNodeType); - return m; + } + + if (auto packOffsetModifier = as<HLSLPackOffsetSemantic>(m)) + { + if (!packOffsetModifier->registerName.getContent().startsWith("c")) + { + getSink()->diagnose( + packOffsetModifier, + Diagnostics::unknownRegisterClass, + packOffsetModifier->registerName); + return m; + } + auto uniformOffset = + stringToInt(packOffsetModifier->registerName.getContent().tail(1)) * 16; + if (packOffsetModifier->componentMask.getContentLength()) + { + switch (packOffsetModifier->componentMask.getContent()[0]) + { + case 'x': uniformOffset += 0; break; + case 'y': uniformOffset += 4; break; + case 'z': uniformOffset += 8; break; + case 'w': uniformOffset += 12; break; + default: + getSink()->diagnose( + packOffsetModifier, + Diagnostics::invalidComponentMask, + packOffsetModifier->componentMask); + break; } } + packOffsetModifier->uniformOffset = uniformOffset; + return packOffsetModifier; + } - if (auto attr = as<GLSLLayoutLocalSizeAttribute>(m)) + if (auto targetIntrinsic = as<TargetIntrinsicModifier>(m)) + { + // TODO: verify that the predicate is one we understand + if (targetIntrinsic->scrutinee.name) { - SLANG_ASSERT(attr->args.getCount() == 3); - - IntVal* values[3]; - - for (int i = 0; i < 3; ++i) + if (auto genDecl = as<ContainerDecl>(syntaxNode)) { - IntVal* value = nullptr; - - auto arg = attr->args[i]; - if (arg) + auto scrutineeResults = lookUp( + m_astBuilder, + this, + targetIntrinsic->scrutinee.name, + genDecl->ownedScope); + if (!scrutineeResults.isValid()) { - auto intValue = checkConstantIntVal(arg); - if (!intValue) - { - return nullptr; - } - if (auto cintVal = as<ConstantIntVal>(intValue)) - { - if (cintVal->getValue() < 1) - { - getSink()->diagnose(attr, Diagnostics::nonPositiveNumThreads, cintVal->getValue()); - return nullptr; - } - } - value = intValue; + getSink()->diagnose( + targetIntrinsic->scrutinee.loc, + Diagnostics::undefinedIdentifier2, + targetIntrinsic->scrutinee.name); } - else + if (scrutineeResults.isOverloaded()) { - value = m_astBuilder->getIntVal(m_astBuilder->getIntType(), 1); + getSink()->diagnose( + targetIntrinsic->scrutinee.loc, + Diagnostics::ambiguousReference, + targetIntrinsic->scrutinee.name); } - values[i] = value; + targetIntrinsic->scrutineeDeclRef = scrutineeResults.item.declRef; } - - attr->x = values[0]; - attr->y = values[1]; - attr->z = values[2]; } - - // Default behavior is to leave things as they are, - // and assume that modifiers are mostly already checked. - // - // TODO: This would be a good place to validate that - // a modifier is actually valid for the thing it is - // being applied to, and potentially to check that - // it isn't in conflict with any other modifiers - // on the same declaration. - - return m; } - void SemanticsVisitor::checkVisibility(Decl* decl) + if (as<PrivateModifier>(m)) { - if (as<AccessorDecl>(decl)) - { - return; - } - ShortList<Type*> typesToVerify; - if (auto varDecl = as<VarDeclBase>(decl)) - { - typesToVerify.add(varDecl->type); - } - else if (auto callable = as<CallableDecl>(decl)) + if (auto decl = as<Decl>(syntaxNode)) { - typesToVerify.add(callable->returnType); - typesToVerify.add(callable->errorType); - for (auto param : callable->getParameters()) + if (isGlobalDecl(decl)) { - typesToVerify.add(param->type); + getSink()->diagnose( + m, + Diagnostics::invalidUseOfPrivateVisibility, + as<Decl>(syntaxNode)); + return m; } } - else if (auto propertyDecl = as<PropertyDecl>(decl)) + if (as<NamespaceDeclBase>(syntaxNode)) { - typesToVerify.add(propertyDecl->type); + getSink()->diagnose( + m, + Diagnostics::invalidVisibilityModifierOnTypeOfDecl, + syntaxNode->astNodeType); + return m; } - else if (as<AggTypeDeclBase>(decl)) + else if (auto decl = as<Decl>(syntaxNode)) { + // Interface requirements can't be private. + if (isInterfaceRequirement(decl)) + { + getSink()->diagnose( + m, + Diagnostics::invalidUseOfPrivateVisibility, + as<Decl>(syntaxNode)); + } } - else if (auto typeDecl = as<TypeDefDecl>(decl)) - { - typesToVerify.add(typeDecl->type); - } - else + } + else if (as<InternalModifier>(m)) + { + if (as<NamespaceDeclBase>(syntaxNode)) { - return; + getSink()->diagnose( + m, + Diagnostics::invalidVisibilityModifierOnTypeOfDecl, + syntaxNode->astNodeType); + return m; } - auto thisVisibility = getDeclVisibility(decl); + } + + if (auto attr = as<GLSLLayoutLocalSizeAttribute>(m)) + { + SLANG_ASSERT(attr->args.getCount() == 3); + + IntVal* values[3]; - // First, we check that the decl's type does not have lower visibility. - for (auto type : typesToVerify) + for (int i = 0; i < 3; ++i) { - if (!type) - continue; - DeclVisibility typeVisibility = getTypeVisibility(type); - if (typeVisibility < thisVisibility) + IntVal* value = nullptr; + + auto arg = attr->args[i]; + if (arg) { - getSink()->diagnose(decl, Diagnostics::useOfLessVisibleType, decl, type); - break; + auto intValue = checkConstantIntVal(arg); + if (!intValue) + { + return nullptr; + } + if (auto cintVal = as<ConstantIntVal>(intValue)) + { + if (cintVal->getValue() < 1) + { + getSink()->diagnose( + attr, + Diagnostics::nonPositiveNumThreads, + cintVal->getValue()); + return nullptr; + } + } + value = intValue; + } + else + { + value = m_astBuilder->getIntVal(m_astBuilder->getIntType(), 1); } + values[i] = value; } - // Next, we check that the decl does not have higher visiblity than its parent. - Decl* parentDecl = decl; - for (; parentDecl; parentDecl = parentDecl->parentDecl) + attr->x = values[0]; + attr->y = values[1]; + attr->z = values[2]; + } + + // Default behavior is to leave things as they are, + // and assume that modifiers are mostly already checked. + // + // TODO: This would be a good place to validate that + // a modifier is actually valid for the thing it is + // being applied to, and potentially to check that + // it isn't in conflict with any other modifiers + // on the same declaration. + + return m; +} + +void SemanticsVisitor::checkVisibility(Decl* decl) +{ + if (as<AccessorDecl>(decl)) + { + return; + } + ShortList<Type*> typesToVerify; + if (auto varDecl = as<VarDeclBase>(decl)) + { + typesToVerify.add(varDecl->type); + } + else if (auto callable = as<CallableDecl>(decl)) + { + typesToVerify.add(callable->returnType); + typesToVerify.add(callable->errorType); + for (auto param : callable->getParameters()) { - if (as<AggTypeDeclBase>(parentDecl)) - break; + typesToVerify.add(param->type); } - if (!parentDecl) - return; - auto parentVisibility = getDeclVisibility(parentDecl); - if (thisVisibility > parentVisibility) + } + else if (auto propertyDecl = as<PropertyDecl>(decl)) + { + typesToVerify.add(propertyDecl->type); + } + else if (as<AggTypeDeclBase>(decl)) + { + } + else if (auto typeDecl = as<TypeDefDecl>(decl)) + { + typesToVerify.add(typeDecl->type); + } + else + { + return; + } + auto thisVisibility = getDeclVisibility(decl); + + // First, we check that the decl's type does not have lower visibility. + for (auto type : typesToVerify) + { + if (!type) + continue; + DeclVisibility typeVisibility = getTypeVisibility(type); + if (typeVisibility < thisVisibility) { - getSink()->diagnose(decl, Diagnostics::declCannotHaveHigherVisibility, decl, parentDecl); + getSink()->diagnose(decl, Diagnostics::useOfLessVisibleType, decl, type); + break; } } - void postProcessingOnModifiers(Modifiers& modifiers) + // Next, we check that the decl does not have higher visiblity than its parent. + Decl* parentDecl = decl; + for (; parentDecl; parentDecl = parentDecl->parentDecl) { - // compress all `require` nodes into 1 `require` modifier - RequireCapabilityAttribute* firstRequire = nullptr; - Modifier* previous = nullptr; - Modifier* next = nullptr; - for (auto m = modifiers.first; m != nullptr; m = next) - { - next = m->next; - // + if (as<AggTypeDeclBase>(parentDecl)) + break; + } + if (!parentDecl) + return; + auto parentVisibility = getDeclVisibility(parentDecl); + if (thisVisibility > parentVisibility) + { + getSink()->diagnose(decl, Diagnostics::declCannotHaveHigherVisibility, decl, parentDecl); + } +} - if (auto req = as<RequireCapabilityAttribute>(m)) +void postProcessingOnModifiers(Modifiers& modifiers) +{ + // compress all `require` nodes into 1 `require` modifier + RequireCapabilityAttribute* firstRequire = nullptr; + Modifier* previous = nullptr; + Modifier* next = nullptr; + for (auto m = modifiers.first; m != nullptr; m = next) + { + next = m->next; + // + + if (auto req = as<RequireCapabilityAttribute>(m)) + { + if (!firstRequire) { - if (!firstRequire) - { - firstRequire = req; - previous = m; - continue; - } - firstRequire->capabilitySet.unionWith(req->capabilitySet); - if(previous) - previous->next = next; + firstRequire = req; + previous = m; continue; } - - // - previous = m; + firstRequire->capabilitySet.unionWith(req->capabilitySet); + if (previous) + previous->next = next; + continue; } + + // + previous = m; } +} - void SemanticsVisitor::checkModifiers(ModifiableSyntaxNode* syntaxNode) - { - // TODO(tfoley): need to make sure this only - // performs semantic checks on a `SharedModifier` once... +void SemanticsVisitor::checkModifiers(ModifiableSyntaxNode* syntaxNode) +{ + // TODO(tfoley): need to make sure this only + // performs semantic checks on a `SharedModifier` once... - // The process of checking a modifier may produce a new modifier in its place, - // so we will build up a new linked list of modifiers that will replace - // the old list. - Modifier* resultModifiers = nullptr; - Modifier** resultModifierLink = &resultModifiers; + // The process of checking a modifier may produce a new modifier in its place, + // so we will build up a new linked list of modifiers that will replace + // the old list. + Modifier* resultModifiers = nullptr; + Modifier** resultModifierLink = &resultModifiers; - // We will keep track of the modifiers for each conflict group. - Dictionary<ASTNodeType, Modifier*> mapExclusiveGroupToModifier; + // We will keep track of the modifiers for each conflict group. + Dictionary<ASTNodeType, Modifier*> mapExclusiveGroupToModifier; - Modifier* modifier = syntaxNode->modifiers.first; - bool ignoreUnallowedModifier = false; - while (modifier) + Modifier* modifier = syntaxNode->modifiers.first; + bool ignoreUnallowedModifier = false; + while (modifier) + { + // Check if a modifier belonging to the same conflict group is already + // defined. + Modifier* existingModifier = nullptr; + auto conflictGroup = getModifierConflictGroupKind(modifier->astNodeType); + if (conflictGroup != ASTNodeType::NodeBase) { - // Check if a modifier belonging to the same conflict group is already - // defined. - Modifier* existingModifier = nullptr; - auto conflictGroup = getModifierConflictGroupKind(modifier->astNodeType); - if (conflictGroup != ASTNodeType::NodeBase) + if (mapExclusiveGroupToModifier.tryGetValue(conflictGroup, existingModifier)) { - if (mapExclusiveGroupToModifier.tryGetValue(conflictGroup, existingModifier)) - { - getSink()->diagnose(modifier->loc, Diagnostics::duplicateModifier, modifier, existingModifier); - } - mapExclusiveGroupToModifier[conflictGroup] = modifier; + getSink()->diagnose( + modifier->loc, + Diagnostics::duplicateModifier, + modifier, + existingModifier); } + mapExclusiveGroupToModifier[conflictGroup] = modifier; + } - // Because we are rewriting the list in place, we need to extract - // the next modifier here (not at the end of the loop). - auto next = modifier->next; + // Because we are rewriting the list in place, we need to extract + // the next modifier here (not at the end of the loop). + auto next = modifier->next; - // We also go ahead and clobber the `next` field on the modifier - // itself, so that the default behavior of `checkModifier()` can - // be to return a single unlinked modifier. - modifier->next = nullptr; + // We also go ahead and clobber the `next` field on the modifier + // itself, so that the default behavior of `checkModifier()` can + // be to return a single unlinked modifier. + modifier->next = nullptr; - // For any modifiers appears after "SharedModifiers", we will not diagnose - // an error if the modifier is not allowed on the declaration. - if (as<SharedModifiers>(modifier)) - ignoreUnallowedModifier = true; - - // may return a list of modifiers - auto checkedModifier = checkModifier(modifier, syntaxNode, ignoreUnallowedModifier); + // For any modifiers appears after "SharedModifiers", we will not diagnose + // an error if the modifier is not allowed on the declaration. + if (as<SharedModifiers>(modifier)) + ignoreUnallowedModifier = true; - if(checkedModifier) - { - // If checking gave us a modifier to add, then we - // had better add it. - - // Just in case `checkModifier` ever returns multiple - // modifiers, lets advance to the end of the list we - // are building. - while(*resultModifierLink) - resultModifierLink = &(*resultModifierLink)->next; - - // attach the new modifier at the end of the list, - // and now set the "link" to point to its `next` field - *resultModifierLink = checkedModifier; - resultModifierLink = &checkedModifier->next; - } + // may return a list of modifiers + auto checkedModifier = checkModifier(modifier, syntaxNode, ignoreUnallowedModifier); - // Move along to the next modifier - modifier = next; - } + if (checkedModifier) + { + // If checking gave us a modifier to add, then we + // had better add it. - // Whether we actually re-wrote anything or note, lets - // install the new list of modifiers on the declaration - syntaxNode->modifiers.first = resultModifiers; + // Just in case `checkModifier` ever returns multiple + // modifiers, lets advance to the end of the list we + // are building. + while (*resultModifierLink) + resultModifierLink = &(*resultModifierLink)->next; - postProcessingOnModifiers(syntaxNode->modifiers); - } + // attach the new modifier at the end of the list, + // and now set the "link" to point to its `next` field + *resultModifierLink = checkedModifier; + resultModifierLink = &checkedModifier->next; + } + // Move along to the next modifier + modifier = next; + } + // Whether we actually re-wrote anything or note, lets + // install the new list of modifiers on the declaration + syntaxNode->modifiers.first = resultModifiers; + postProcessingOnModifiers(syntaxNode->modifiers); } + + +} // namespace Slang |
