diff options
Diffstat (limited to 'source/slang/slang-check-decl.cpp')
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 335 |
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); + } } } |
