summaryrefslogtreecommitdiff
path: root/source/slang/slang-check-decl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-check-decl.cpp')
-rw-r--r--source/slang/slang-check-decl.cpp335
1 files changed, 220 insertions, 115 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 72b5c19db..0a9853012 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -995,12 +995,10 @@ struct SemanticsDeclCapabilityVisitor : public SemanticsDeclVisitorBase,
CapabilitySet getDeclaredCapabilitySet(Decl* decl);
-
void visitDecl(Decl*) {}
void visitDeclGroup(DeclGroup*) {}
void checkVarDeclCommon(VarDeclBase* varDecl);
- void visitAggTypeDeclBase(AggTypeDeclBase* decl);
- void visitNamespaceDeclBase(NamespaceDeclBase* decl);
+ void visitContainerDecl(ContainerDecl* decl);
void visitVarDecl(VarDecl* varDecl) { checkVarDeclCommon(varDecl); }
@@ -1013,7 +1011,8 @@ struct SemanticsDeclCapabilityVisitor : public SemanticsDeclVisitorBase,
void diagnoseUndeclaredCapability(
Decl* decl,
const DiagnosticInfo& diagnosticInfo,
- const CapabilityAtomSet& failedAtomsInsideAvailableSet);
+ const CapabilityAtomSet& failedAtomsInsideAvailableSet,
+ bool printProvenance);
};
@@ -14382,26 +14381,29 @@ struct CapabilityDeclReferenceVisitor
auto targetCaseCount = stmt->targetCases.getCount();
for (Index targetCaseIndex = 0; targetCaseIndex < targetCaseCount; targetCaseIndex++)
{
- // We may recieve a `default:` case for a `__target_switch`. If this is the case,
- // we must resolve the target capability for a non empty set of
- // `calling_functions_targets`:
- // ``` default_target = calling_functions_targets-{other_case_targets} ```
+ // The logic here is to collect a list of `case` statment capabilities
+ // so that down-the-line we can specialize according to the compile-capabilities.
//
- // * `calling_functions_capability` = `requirement attribute` of the calling
- // function; if missing
- // we can assume it is `any_target`
+ // The additional goal we have is to merge all case-capabilities into 1 set
+ // so that we can propegate them to the parent-function so that a user may break-down
+ // a functon into `stage`/`target` specific code.
//
- // * `{other_case_targets}` = set of all capabilities all `case` statments target
- // inside the `__target_switch`
-
- // If we do not handle `default:`, the codegen will fail when trying to find a
- // specific codegen target not handled explicitly by a `case` statment. We must also
- // ensure the `default` case is last so we have priority to hit `case` statments and
- // can preprocess `case` statments before the `default` case.
+ // A few important details
+ // 1. Case statments (other than `default:`) may have overlapping capabilities. This is
+ // to allow "more specialized" `case` statments to support specializing code on
+ // higher-feature-levels to support writing 1 function to handle cases such as sm_5_0
+ // and sm_6_0 support all in 1 function.
+ //
+ // 2. All `case:` statments are explicit with their own-capabilities, `default:`
+ // statments are not. `default:` statments have the value `CapabilityName::Invalid`. If
+ // we find a `default` statment we assign it all shader target/stage capabilities that
+ // the other `case` statments did not specify which is legal for the current calling
+ // function (based on the parent function `require` decl).
CapabilitySet targetCap;
if (CapabilityName(stmt->targetCases[targetCaseIndex]->capability) ==
CapabilityName::Invalid)
{
+ // swap the `default` case to the end so that we process it last
if (targetCaseCount - 1 != targetCaseIndex)
{
for (Index i = targetCaseIndex; i < targetCaseCount - 1; i++)
@@ -14594,30 +14596,22 @@ CapabilitySet SemanticsDeclCapabilityVisitor::getDeclaredCapabilitySet(Decl* dec
return declaredCaps;
}
-void SemanticsDeclCapabilityVisitor::visitAggTypeDeclBase(AggTypeDeclBase* decl)
-{
- decl->inferredCapabilityRequirements = getDeclaredCapabilitySet(decl);
-}
-
-void SemanticsDeclCapabilityVisitor::visitNamespaceDeclBase(NamespaceDeclBase* decl)
+void SemanticsDeclCapabilityVisitor::visitContainerDecl(ContainerDecl* decl)
{
+ // Any potential child must get it's capabilities from `getDeclaredCapabilitySet`.
decl->inferredCapabilityRequirements = getDeclaredCapabilitySet(decl);
}
-template<typename ProcessFunc, typename ParentDiagnosticFunc>
-static inline void _dispatchCapabilitiesVisitorOfFunctionDecl(
- SemanticsVisitor* visitor,
- FunctionDeclBase* funcDecl,
- const ProcessFunc& processFunc,
- const ParentDiagnosticFunc& parentDiagnosticFunc)
+void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* funcDecl)
{
- visitor->setParentFuncOfVisitor(funcDecl);
+ setParentFuncOfVisitor(funcDecl);
+ // visit the members of our funcDecl
for (auto member : funcDecl->getDirectMemberDecls())
{
- visitor->ensureDecl(member, DeclCheckState::CapabilityChecked);
+ ensureDecl(member, DeclCheckState::CapabilityChecked);
_propagateRequirement(
- visitor,
+ this,
funcDecl->inferredCapabilityRequirements,
funcDecl,
member,
@@ -14625,22 +14619,35 @@ static inline void _dispatchCapabilitiesVisitorOfFunctionDecl(
member->loc);
}
+ // visit the body of our funcDecl, propagate capabilities.
visitReferencedDecls(
- *visitor,
+ *this,
funcDecl->body,
funcDecl->loc,
funcDecl->findModifier<RequireCapabilityAttribute>(),
- processFunc,
- parentDiagnosticFunc);
+ [this, funcDecl](SyntaxNode* node, const CapabilitySet& nodeCaps, SourceLoc refLoc)
+ {
+ _propagateRequirement(
+ this,
+ funcDecl->inferredCapabilityRequirements,
+ funcDecl,
+ node,
+ nodeCaps,
+ refLoc);
+ },
+ [this, funcDecl](DiagnosticCategory category)
+ { _propagateSeeDefinitionOf(this, funcDecl, category); });
+ // non-static function join's capabilities with parent
+ // to become a superset of the parent.
if (!isEffectivelyStatic(funcDecl))
{
auto parentAggTypeDecl = getParentAggTypeDecl(funcDecl);
if (parentAggTypeDecl)
{
- visitor->ensureDecl(parentAggTypeDecl, DeclCheckState::CapabilityChecked);
+ ensureDecl(parentAggTypeDecl, DeclCheckState::CapabilityChecked);
_propagateRequirement(
- visitor,
+ this,
funcDecl->inferredCapabilityRequirements,
funcDecl,
parentAggTypeDecl,
@@ -14648,34 +14655,13 @@ static inline void _dispatchCapabilitiesVisitorOfFunctionDecl(
funcDecl->loc);
}
}
-}
-
-void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* funcDecl)
-{
- // If the function is an entrypoint and specifies a target stage, add the capabilities to
- // our function capabilities.
- _dispatchCapabilitiesVisitorOfFunctionDecl(
- this,
- funcDecl,
- [this, funcDecl](SyntaxNode* node, const CapabilitySet& nodeCaps, SourceLoc refLoc)
- {
- _propagateRequirement(
- this,
- funcDecl->inferredCapabilityRequirements,
- funcDecl,
- node,
- nodeCaps,
- refLoc);
- },
- [this, funcDecl](DiagnosticCategory category)
- { _propagateSeeDefinitionOf(this, funcDecl, category); });
+ // Get require of decl + add parents
auto declaredCaps = getDeclaredCapabilitySet(funcDecl);
-
auto vis = getDeclVisibility(funcDecl);
- // If 0 capabilities were annotated on a function, capabilities are inferred from the
- // function body
+ // If 0 capabilities were annotated on this function,
+ // capabilities are inferred from the children.
if (declaredCaps.isEmpty())
{
declaredCaps = funcDecl->inferredCapabilityRequirements;
@@ -14687,30 +14673,37 @@ void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* fun
addModifier(funcDecl, declaredCapModifier);
if (vis == DeclVisibility::Public)
{
- // For public decls, we need to enforce that the function
- // only uses capabilities that it declares.
- // At a minimum we will propagate shader requirements to our
- // function from calling children in all cases so the parent
- // can enforce shader targets correctly and propagate to `main`
+ // We need to enforce that the function-body
+ // only uses capabilities that the function-decl declares.
+ //
+ // A small exception to this rule is that the body must
+ // implement all shader stages/targets of the functionDecl
+ // requirements. The body can support *more* stages/targets,
+ // these will just be not accessible (which is fine since a user
+ // may only need hlsl support for a function, using an STD-LIB function
+ // implemented for all targets/stages).
CapabilityAtomSet failedAvailableCapabilityConjunction;
- if (!CapabilitySet::checkCapabilityRequirement(
- declaredCaps,
- funcDecl->inferredCapabilityRequirements,
- failedAvailableCapabilityConjunction))
- {
- diagnoseUndeclaredCapability(
- funcDecl,
- Diagnostics::useOfUndeclaredCapability,
- failedAvailableCapabilityConjunction);
- funcDecl->inferredCapabilityRequirements = declaredCaps;
- }
- else
- funcDecl->inferredCapabilityRequirements.nonDestructiveJoin(declaredCaps);
+ CheckCapabilityRequirementResult checkCapabilityResult;
+ CapabilitySet::checkCapabilityRequirement(
+ CheckCapabilityRequirementOptions::AvailableCanHaveSubsetOfAbstractAtoms,
+ declaredCaps,
+ funcDecl->inferredCapabilityRequirements,
+ failedAvailableCapabilityConjunction,
+ checkCapabilityResult);
+ diagnoseUndeclaredCapability(
+ funcDecl,
+ Diagnostics::useOfUndeclaredCapability,
+ failedAvailableCapabilityConjunction,
+ true);
+
+ // declared capabilities must be a superset.
+ funcDecl->inferredCapabilityRequirements = declaredCaps;
}
else
{
// For internal decls, their inferred capability should be joined
- // with the declared capabilities.
+ // with the declared capabilities since we are assuming the stdlib
+ // is not wrong.
funcDecl->inferredCapabilityRequirements.join(declaredCaps);
}
}
@@ -14718,8 +14711,29 @@ void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* fun
void SemanticsDeclCapabilityVisitor::visitInheritanceDecl(InheritanceDecl* inheritanceDecl)
{
- // Check that the implementation of an interface requirement is not using more capabilities
- // than what's declared on the interface method.
+ auto inheritanceParentDecl = inheritanceDecl->parentDecl;
+ ensureDecl(inheritanceParentDecl, DeclCheckState::CapabilityChecked);
+
+ // Propegate capabilities of inheritance `base` to
+ // `InheritanceDecl`
+ visitReferencedDecls(
+ *this,
+ inheritanceDecl->base,
+ inheritanceDecl->loc,
+ nullptr,
+ [this, inheritanceDecl](SyntaxNode* node, const CapabilitySet& nodeCaps, SourceLoc refLoc)
+ {
+ _propagateRequirement(
+ this,
+ inheritanceDecl->inferredCapabilityRequirements,
+ inheritanceDecl,
+ node,
+ nodeCaps,
+ refLoc);
+ },
+ [this, inheritanceDecl](DiagnosticCategory category)
+ { _propagateSeeDefinitionOf(this, inheritanceDecl, category); });
+
if (inheritanceDecl->witnessTable)
{
for (auto& kv : inheritanceDecl->witnessTable->m_requirementDictionary)
@@ -14727,29 +14741,116 @@ void SemanticsDeclCapabilityVisitor::visitInheritanceDecl(InheritanceDecl* inher
if (kv.value.getFlavor() != RequirementWitness::Flavor::declRef)
continue;
auto requirementDecl = kv.key;
- auto implDecl = kv.value.getDeclRef();
- if (!implDecl)
+ auto implDeclRef = kv.value.getDeclRef();
+ if (!implDeclRef)
continue;
- if (getModuleDecl(implDecl.getDecl())->languageVersion == SLANG_LANGUAGE_VERSION_LEGACY)
+ if (getModuleDecl(implDeclRef.getDecl())->languageVersion ==
+ SLANG_LANGUAGE_VERSION_LEGACY)
break;
ensureDecl(requirementDecl, DeclCheckState::CapabilityChecked);
- ensureDecl(implDecl.declRefBase, DeclCheckState::CapabilityChecked);
+ ensureDecl(implDeclRef.declRefBase, DeclCheckState::CapabilityChecked);
+
+ // Only if capabilities are opted-into, should we error.
+ auto implDecl = implDeclRef.getDecl();
+ if (!requirementDecl->hasModifier<ExplicitlyDeclaredCapabilityModifier>() &&
+ !implDecl->hasModifier<ExplicitlyDeclaredCapabilityModifier>())
+ continue;
CapabilityAtomSet failedAvailableCapabilityConjunction;
- if (!CapabilitySet::checkCapabilityRequirement(
- requirementDecl->inferredCapabilityRequirements,
- implDecl.getDecl()->inferredCapabilityRequirements,
- failedAvailableCapabilityConjunction))
+ CheckCapabilityRequirementResult checkCapabilityResult;
+ CapabilitySet::checkCapabilityRequirement(
+ CheckCapabilityRequirementOptions::MustHaveEqualAbstractAtoms,
+ requirementDecl->inferredCapabilityRequirements,
+ implDecl->inferredCapabilityRequirements,
+ failedAvailableCapabilityConjunction,
+ checkCapabilityResult);
+
+ if (checkCapabilityResult ==
+ CheckCapabilityRequirementResult::AvailableIsNotASuperSetToRequired)
{
diagnoseUndeclaredCapability(
- implDecl.getDecl(),
+ implDecl,
Diagnostics::useOfUndeclaredCapabilityOfInterfaceRequirement,
+ failedAvailableCapabilityConjunction,
+ false);
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ requirementDecl,
+ Diagnostics::seeDeclarationOf,
+ requirementDecl);
+ }
+ else if (
+ checkCapabilityResult ==
+ CheckCapabilityRequirementResult::RequiredIsMissingAbstractAtoms)
+ {
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ implDecl,
+ Diagnostics::requirmentHasSubsetOfAbstractAtomsToImplementation,
+ implDecl,
failedAvailableCapabilityConjunction);
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ requirementDecl,
+ Diagnostics::seeDeclarationOf,
+ requirementDecl);
}
}
}
+
+ // validate that super-type is a super set of capabilities
+ CapabilityAtomSet failedAvailableCapabilityConjunction;
+ CheckCapabilityRequirementResult checkCapabilityResult;
+ CapabilitySet::checkCapabilityRequirement(
+ CheckCapabilityRequirementOptions::MustHaveEqualAbstractAtoms,
+ inheritanceDecl->inferredCapabilityRequirements,
+ inheritanceParentDecl->inferredCapabilityRequirements,
+ failedAvailableCapabilityConjunction,
+ checkCapabilityResult);
+
+ if (checkCapabilityResult ==
+ CheckCapabilityRequirementResult::AvailableIsNotASuperSetToRequired)
+ {
+ diagnoseUndeclaredCapability(
+ inheritanceParentDecl,
+ Diagnostics::useOfUndeclaredCapabilityOfInheritanceDecl,
+ failedAvailableCapabilityConjunction,
+ false);
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ inheritanceDecl->base,
+ Diagnostics::seeDeclarationOf,
+ inheritanceDecl->base);
+ }
+ else if (
+ checkCapabilityResult == CheckCapabilityRequirementResult::RequiredIsMissingAbstractAtoms)
+ {
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ inheritanceParentDecl,
+ Diagnostics::subTypeHasSubsetOfAbstractAtomsToSuperType,
+ inheritanceParentDecl,
+ failedAvailableCapabilityConjunction);
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ inheritanceDecl->base,
+ Diagnostics::seeDeclarationOf,
+ inheritanceDecl->base);
+ }
}
DeclVisibility getDeclVisibility(Decl* decl)
@@ -15044,13 +15145,11 @@ void diagnoseCapabilityProvenance(
void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
Decl* decl,
const DiagnosticInfo& diagnosticInfo,
- const CapabilityAtomSet& failedAtomsInsideAvailableSet)
+ const CapabilityAtomSet& failedAtomsInsideAvailableSet,
+ bool printProvenance)
{
if (decl->inferredCapabilityRequirements.isEmpty())
return;
- if (failedAtomsInsideAvailableSet.isEmpty() ||
- failedAtomsInsideAvailableSet.contains((UInt)CapabilityAtom::Invalid))
- return;
// There are two causes for why type checking failed on failedAvailableSet.
// The first scenario is that failedAvailableSet defines a set of capabilities on a
@@ -15075,7 +15174,7 @@ void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
getSink(),
this->getOptionSet(),
DiagnosticCategory::Capability,
- decl->loc,
+ decl,
Diagnostics::declHasDependenciesNotCompatibleOnTarget,
decl,
outFailedAtom);
@@ -15090,16 +15189,19 @@ void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
getAtomSetOfTargets(),
failedAtomSet);
- HashSet<Decl*> printedDecls;
- for (auto atom : targetsNotUsedSet)
+ if (printProvenance)
{
- CapabilityAtom formattedAtom = asAtom(atom);
- diagnoseCapabilityProvenance(
- this->getOptionSet(),
- getSink(),
- decl,
- formattedAtom,
- printedDecls);
+ HashSet<Decl*> printedDecls;
+ for (auto atom : targetsNotUsedSet)
+ {
+ CapabilityAtom formattedAtom = asAtom(atom);
+ diagnoseCapabilityProvenance(
+ this->getOptionSet(),
+ getSink(),
+ decl,
+ formattedAtom,
+ printedDecls);
+ }
}
return;
}
@@ -15130,7 +15232,7 @@ void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
getSink(),
this->getOptionSet(),
DiagnosticCategory::Capability,
- decl->loc,
+ decl,
Diagnostics::declHasDependenciesNotCompatibleOnStage,
decl,
formattedAtom);
@@ -15141,18 +15243,21 @@ void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
getSink(),
this->getOptionSet(),
DiagnosticCategory::Capability,
- decl->loc,
+ decl,
diagnosticInfo,
decl,
formattedAtom);
}
- // Print provenances.
- diagnoseCapabilityProvenance(
- this->getOptionSet(),
- getSink(),
- decl,
- formattedAtom,
- printedDecls);
+ if (printProvenance)
+ {
+ // Print provenances.
+ diagnoseCapabilityProvenance(
+ this->getOptionSet(),
+ getSink(),
+ decl,
+ formattedAtom,
+ printedDecls);
+ }
}
}