diff options
| author | Yong He <yonghe@outlook.com> | 2024-02-06 16:30:31 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-06 16:30:31 -0800 |
| commit | ab41d548db376c6b52869004d1b6e21b88b4c9c8 (patch) | |
| tree | 61aacddad8b8c56d77cf63ab3b650fdb28bbe0e6 | |
| parent | 6365e00179179f2bc0bc25af3d51d528501498d5 (diff) | |
Improve Capability System (#3555)
* Improve capability system.
* Update documentation.
* Tuning semantics.
* LSP: hierarchical diagnostics.
* Fix test.
* Fix test.
| -rw-r--r-- | docs/user-guide/05-capabilities.md | 29 | ||||
| -rw-r--r-- | source/compiler-core/slang-diagnostic-sink.cpp | 2 | ||||
| -rw-r--r-- | source/core/slang-string-util.cpp | 8 | ||||
| -rw-r--r-- | source/slang/slang-ast-decl.h | 7 | ||||
| -rw-r--r-- | source/slang/slang-capabilities.capdef | 5 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 109 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 1 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 39 | ||||
| -rw-r--r-- | source/slang/slang-syntax.cpp | 18 | ||||
| -rw-r--r-- | source/slang/slang-syntax.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-workspace-version.cpp | 15 | ||||
| -rw-r--r-- | tests/diagnostics/extension-visibility.slang.expected | 2 | ||||
| -rw-r--r-- | tests/diagnostics/generic-type-inference-fail.slang.expected | 2 | ||||
| -rw-r--r-- | tests/language-feature/capability/capability3.slang | 47 | ||||
| -rw-r--r-- | tests/language-feature/capability/capability4.slang | 25 |
16 files changed, 286 insertions, 31 deletions
diff --git a/docs/user-guide/05-capabilities.md b/docs/user-guide/05-capabilities.md index c20a43c75..5f69a3026 100644 --- a/docs/user-guide/05-capabilities.md +++ b/docs/user-guide/05-capabilities.md @@ -63,17 +63,34 @@ For example, requirement `spvShaderClockKHR + fragment` and requirement `spvShad ## Requirements in Parent Scope -The capability requirement of a decl is always joined with the requirements declared in its parents. -For example: +The capability requirement of a decl is always merged with the requirements declared in its parents. If the decl declares requirements for additional compilation targets, they are added +to the requirement set as a separate disjunction. +For example, given: ```csharp -[require(spvShaderClockKHR)] +[require(glsl)] +[require(hlsl)] struct MyType { - [require(spvShaderClockKHR)] - void method() { ... } + [require(hlsl, hlsl_nvapi)] + [require(spirv)] + static void method() { ... } +} +``` +`MyType.method` will have requirement `glsl | hlsl + hlsl_nvapi | spirv`. + +The `[require]` attribute can also be used on module declarations, so that the requirement will +apply to all decls within the module. For example: +```csharp +[require(glsl)] +[require(hlsl)] +[require(spirv)] +module myModule; + +// myFunc has requirement glsl|hlsl|spirv +public void myFunc() +{ } ``` -`MyType.method` has requirement `spvShaderClockKHR + spvShaderClockKHR`. ## Inferrence of Capability Requirements diff --git a/source/compiler-core/slang-diagnostic-sink.cpp b/source/compiler-core/slang-diagnostic-sink.cpp index 2d3f34c5c..d077ef90f 100644 --- a/source/compiler-core/slang-diagnostic-sink.cpp +++ b/source/compiler-core/slang-diagnostic-sink.cpp @@ -147,7 +147,7 @@ static void formatDiagnostic(const HumaneSourceLoc& humaneLoc, Diagnostic const& outBuilder << getSeverityName(diagnostic.severity); - if (diagnostic.ErrorID >= 0) + if ((flags & DiagnosticSink::Flag::LanguageServer) || diagnostic.ErrorID >= 0) { outBuilder << " "; outBuilder << diagnostic.ErrorID; diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp index a6a18d4a0..c7625e1e0 100644 --- a/source/core/slang-string-util.cpp +++ b/source/core/slang-string-util.cpp @@ -727,6 +727,12 @@ int StringUtil::parseIntAndAdvancePos(UnownedStringSlice text, Index& pos) pos++; continue; } + bool isNeg = false; + if (pos < text.getLength() && text[pos] == '-') + { + pos++; + isNeg = true; + } while (pos < text.getLength()) { if (text[pos] >= '0' && text[pos] <= '9') @@ -740,6 +746,8 @@ int StringUtil::parseIntAndAdvancePos(UnownedStringSlice text, Index& pos) break; } } + if (isNeg) + result = -result; return result; } diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h index ab4fbaf17..8d598c474 100644 --- a/source/slang/slang-ast-decl.h +++ b/source/slang/slang-ast-decl.h @@ -506,7 +506,12 @@ class ImplementingDecl : public IncludeDeclBase class ModuleDeclarationDecl : public Decl { - SLANG_AST_CLASS(ModuleDeclarationDecl); + SLANG_AST_CLASS(ModuleDeclarationDecl) +}; + +class RequireCapabilityDecl : public Decl +{ + SLANG_AST_CLASS(RequireCapabilityDecl) }; // A generic declaration, parameterized on types/values diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef index eee5736e8..cf2cd5791 100644 --- a/source/slang/slang-capabilities.capdef +++ b/source/slang/slang-capabilities.capdef @@ -61,6 +61,11 @@ def spirv_1_6 : spirv_1_5; alias spirv = spirv_1_0; alias spirv_latest = spirv_1_6; +alias any_target = hlsl | glsl | c | cpp | cuda | spirv; +alias any_textual_target = hlsl | glsl | c | cpp | cuda; +alias any_gfx_target = hlsl | glsl | spirv; +alias any_cpp_target = cpp | cuda; + // Capabilities that stand for target spirv version for GLSL backend. // These are not compilation targets. def glsl_spirv_1_0 : glsl; diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index a7e197d81..3882994da 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -448,6 +448,7 @@ namespace Slang void visitDeclRefExpr(DeclRefExpr* expr) { + dispatchIfNotNull(expr->type.type); dispatchIfNotNull(expr->declRef.declRefBase); } void visitStaticMemberExpr(StaticMemberExpr* expr) @@ -528,7 +529,10 @@ namespace Slang // Stmt Visitor - void visitDeclStmt(DeclStmt* stmt) { dispatchIfNotNull(stmt->decl); } + void visitDeclStmt(DeclStmt* stmt) + { + dispatchIfNotNull(stmt->decl); + } void visitBlockStmt(BlockStmt* stmt) { @@ -687,14 +691,29 @@ namespace Slang : public SemanticsDeclVisitorBase , public DeclVisitor<SemanticsDeclCapabilityVisitor> { + CapabilitySet m_anyPlatfromCapabilitySet; + SemanticsDeclCapabilityVisitor(SemanticsContext const& outer) : SemanticsDeclVisitorBase(outer) {} + CapabilitySet& getAnyPlatformCapabilitySet() + { + if (m_anyPlatfromCapabilitySet.isEmpty()) + { + m_anyPlatfromCapabilitySet = CapabilitySet(CapabilityName::any_target); + } + return m_anyPlatfromCapabilitySet; + } + + CapabilitySet getDeclaredCapabilitySet(Decl* decl); + + void visitDecl(Decl*) {} void visitDeclGroup(DeclGroup*) {} - void checkVarDeclCommon(VarDeclBase* varDecl); + void visitAggTypeDeclBase(AggTypeDeclBase* decl); + void visitNamespaceDeclBase(NamespaceDeclBase* decl); void visitVarDecl(VarDecl* varDecl) { @@ -8678,6 +8697,10 @@ namespace Slang set.canonicalize(); handleReferenceFunc(stmt, set, stmt->loc); } + void visitRequireCapabilityDecl(RequireCapabilityDecl* decl) + { + handleReferenceFunc(decl, decl->inferredCapabilityRequirements, decl->loc); + } }; template<typename ProcessFunc> @@ -8721,6 +8744,59 @@ namespace Slang }); } + CapabilitySet SemanticsDeclCapabilityVisitor::getDeclaredCapabilitySet(Decl* decl) + { + // Merge a decls's declared capability set with all parent declarations. + // For every existing target, we want to join their requirements together. + // If the the parent defines additional targets, we want to add them to the disjunction set. + // For example: + // [require(glsl)] struct Parent { [require(glsl, glsl_ext_1)] [require(spirv)] void foo(); } + // The requirement for `foo` should be glsl+glsl_ext_1 | spirv. + // + CapabilitySet declaredCaps; + for (Decl* parent = decl; parent; parent = getParentDecl(parent)) + { + CapabilitySet localDeclaredCaps; + bool shouldBreak = false; + if (!as<AggTypeDeclBase>(parent) || parent->inferredCapabilityRequirements.isEmpty()) + { + for (auto decoration : parent->getModifiersOfType<RequireCapabilityAttribute>()) + { + for (auto& set : decoration->capabilitySet.getExpandedAtoms()) + localDeclaredCaps.unionWith(set); + } + } + else + { + localDeclaredCaps = parent->inferredCapabilityRequirements; + shouldBreak = true; + } + // Merge decl's capability declaration with the parent. + for (auto& localConjunction : localDeclaredCaps.getExpandedAtoms()) + { + if (declaredCaps.isIncompatibleWith(localConjunction)) + declaredCaps.unionWith(localConjunction); + else + declaredCaps.join(localDeclaredCaps); + } + // If the parent already has inferred capability requirements, we should stop now + // since that already covers transitive parents. + if (shouldBreak) + break; + } + return declaredCaps; + } + + void SemanticsDeclCapabilityVisitor::visitAggTypeDeclBase(AggTypeDeclBase* decl) + { + decl->inferredCapabilityRequirements = getDeclaredCapabilitySet(decl); + } + + void SemanticsDeclCapabilityVisitor::visitNamespaceDeclBase(NamespaceDeclBase* decl) + { + decl->inferredCapabilityRequirements = getDeclaredCapabilitySet(decl); + } + void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* funcDecl) { for (auto member : funcDecl->members) @@ -8733,19 +8809,17 @@ namespace Slang _propagateRequirement(this, funcDecl->inferredCapabilityRequirements, funcDecl, node, nodeCaps, refLoc); }); - // A decls's declared capability set is a transitive join of all parent declarations. - CapabilitySet declaredCaps; - for (Decl* parent = funcDecl; parent; parent = getParentDecl(parent)) + if (!isEffectivelyStatic(funcDecl)) { - CapabilitySet localDeclaredCaps; - - for (auto decoration : parent->getModifiersOfType<RequireCapabilityAttribute>()) + auto parentAggTypeDecl = getParentAggTypeDecl(funcDecl); + if (parentAggTypeDecl) { - for (auto& set : decoration->capabilitySet.getExpandedAtoms()) - localDeclaredCaps.unionWith(set); + ensureDecl(parentAggTypeDecl, DeclCheckState::CapabilityChecked); + _propagateRequirement(this, funcDecl->inferredCapabilityRequirements, funcDecl, parentAggTypeDecl, parentAggTypeDecl->inferredCapabilityRequirements, funcDecl->loc); } - declaredCaps.join(localDeclaredCaps); } + + auto declaredCaps = getDeclaredCapabilitySet(funcDecl); if (!declaredCaps.isEmpty()) { @@ -8773,7 +8847,13 @@ namespace Slang { if (!getModuleDecl(funcDecl)->isInLegacyLanguage) { - getSink()->diagnose(funcDecl->loc, Diagnostics::missingCapabilityRequirementOnPublicDecl, funcDecl); + if (funcDecl->inferredCapabilityRequirements != getAnyPlatformCapabilitySet()) + { + getSink()->diagnose( + funcDecl->loc, + Diagnostics::missingCapabilityRequirementOnPublicDecl, + funcDecl, funcDecl->inferredCapabilityRequirements); + } } } } @@ -8924,6 +9004,7 @@ namespace Slang { Decl* refDecl = nullptr; SourceLoc loc; + HashSet<Decl*> printedDecls; while (traceLevels > 0) { refDecl = nullptr; @@ -8940,7 +9021,9 @@ namespace Slang sink->diagnose(refLoc, Diagnostics::seeDefinitionOf, "statement"); } }); - if (refDecl) + if (!refDecl) + break; + if (printedDecls.add(refDecl)) { sink->diagnose(loc, Diagnostics::seeUsingOf, refDecl); decl = refDecl; diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 786dbda35..62bc73c90 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -47,6 +47,8 @@ DIAGNOSTIC(-1, Note, declaredHere, "declared here") DIAGNOSTIC(-1, Note, seeOtherDeclarationOf, "see other declaration of '$0'") DIAGNOSTIC(-1, Note, seePreviousDeclarationOf, "see previous declaration of '$0'") DIAGNOSTIC(-1, Note, includeOutput, "include $0") +DIAGNOSTIC(-1, Note, genericSignatureTried, "see declaration of $0") +DIAGNOSTIC(-1, Note, entryPointCandidate, "see candidate declaration for entry point '$0'") // // 0xxxx - Command line and interaction with host platform APIs. @@ -370,7 +372,7 @@ DIAGNOSTIC(36005, Error, invalidVisibilityModifierOnTypeOfDecl, "visibility modi DIAGNOSTIC(36100, Error, conflictingCapabilityDueToUseOfDecl, "'$0' requires capability '$1' that is conflicting with the '$2''s current capability requirement '$3'.") DIAGNOSTIC(36101, Error, conflictingCapabilityDueToStatement, "statement requires capability '$0' that is conflicting with the '$1''s current capability requirement '$2'.") DIAGNOSTIC(36102, Error, conflictingCapabilityDueToStatementEnclosingFunc, "statement requires capability '$0' that is conflicting with the current function's capability requirement '$1'.") -DIAGNOSTIC(36103, Error, missingCapabilityRequirementOnPublicDecl, "public symbol '$0' is missing capability requirement declaration.") +DIAGNOSTIC(36103, Warning, missingCapabilityRequirementOnPublicDecl, "public symbol '$0' is missing capability requirement declaration, the symbol is assumed to require inferred capabilities '$1'.") DIAGNOSTIC(36104, Error, useOfUndeclaredCapability, "'$0' uses undeclared capability '$1'.") DIAGNOSTIC(36104, Error, useOfUndeclaredCapabilityOfInterfaceRequirement, "'$0' uses capability '$1' that is missing from the interface requirement.") DIAGNOSTIC(36105, Error, unknownCapability, "unknown capability name '$0'.") @@ -536,7 +538,6 @@ DIAGNOSTIC(39999, Error, defaultOutsideSwitch, "'default' not allowed outside of DIAGNOSTIC(39999, Error, expectedAGeneric, "expected a generic when using '<...>' (found: '$0')") DIAGNOSTIC(39999, Error, genericArgumentInferenceFailed, "could not specialize generic for arguments of type $0") -DIAGNOSTIC(39999, Note, genericSignatureTried, "see declaration of $0") DIAGNOSTIC(39999, Error, ambiguousReference, "ambiguous reference to '$0'") DIAGNOSTIC(39999, Error, ambiguousExpression, "ambiguous reference") @@ -564,7 +565,6 @@ DIAGNOSTIC(39999, Error, overloadedParameterToHigherOrderFunction, "passing over DIAGNOSTIC(38000, Error, entryPointFunctionNotFound, "no function found matching entry point name '$0'") DIAGNOSTIC(38001, Error, ambiguousEntryPoint, "more than one function matches entry point name '$0'") -DIAGNOSTIC(38002, Note, entryPointCandidate, "see candidate declaration for entry point '$0'") DIAGNOSTIC(38003, Error, entryPointSymbolNotAFunction, "entry point '$0' must be declared as a function") DIAGNOSTIC(38004, Error, entryPointTypeParameterNotFound, "no type found matching entry-point type parameter name '$0'") diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 427fe1cf0..4e8c9b340 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -6945,6 +6945,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> IGNORED_CASE(NamespaceDecl) IGNORED_CASE(ModuleDeclarationDecl) IGNORED_CASE(FileDecl) + IGNORED_CASE(RequireCapabilityDecl) #undef IGNORED_CASE diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 4b6d9b2d0..45fa5a125 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -3537,6 +3537,28 @@ namespace Slang return fileDecl; } + static NodeBase* parseRequireCapabilityDecl(Parser* parser, void*) + { + auto decl = parser->astBuilder->create<RequireCapabilityDecl>(); + parser->FillPosition(decl); + List<CapabilityName> capNames; + while (parser->LookAheadToken(TokenType::Identifier)) + { + auto capNameToken = parser->ReadToken(TokenType::Identifier); + CapabilityName capName = findCapabilityName(capNameToken.getContent()); + if (capName != CapabilityName::Invalid) + capNames.add(capName); + else + parser->sink->diagnose(capNameToken, Diagnostics::unknownCapability, capNameToken.getContent()); + if (AdvanceIf(parser, "+") || AdvanceIf(parser, ",")) + continue; + break; + } + decl->inferredCapabilityRequirements = CapabilitySet(capNames); + parser->ReadToken(TokenType::Semicolon); + return decl; + } + static NodeBase* parseConstructorDecl(Parser* parser, void* /*userData*/) { ConstructorDecl* decl = parser->astBuilder->create<ConstructorDecl>(); @@ -4351,7 +4373,20 @@ namespace Slang Decl* declToModify = decl; if(auto genericDecl = as<GenericDecl>(decl)) declToModify = genericDecl->inner; - _addModifiers(declToModify, modifiers); + + if (as<ModuleDeclarationDecl>(decl)) + { + // Modifiers on module declaration should be added to the module itself. + auto moduleDecl = getModuleDecl(containerDecl); + if (moduleDecl) + { + _addModifiers(moduleDecl, modifiers); + } + } + else + { + _addModifiers(declToModify, modifiers); + } if (containerDecl) { @@ -7901,7 +7936,7 @@ namespace Slang _makeParseDecl("__ignored_block", parseIgnoredBlockDecl ), _makeParseDecl("__transparent_block", parseTransparentBlockDecl), _makeParseDecl("__file_decl", parseFileDecl), - + _makeParseDecl("__require_capability", parseRequireCapabilityDecl), // !!!!!!!!!!!!!!!!!!!!!! Modifer !!!!!!!!!!!!!!!!!!!!!! diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp index ab0dfcd74..b6fd8936e 100644 --- a/source/slang/slang-syntax.cpp +++ b/source/slang/slang-syntax.cpp @@ -17,7 +17,10 @@ void printDiagnosticArg(StringBuilder& sb, Decl* decl) { if (!decl) return; - sb << getText(decl->getName()); + if (decl->getName() && decl->getName()->text.getLength()) + sb << getText(decl->getName()); + else + printDiagnosticArg(sb, decl->astNodeType); } void printDiagnosticArg(StringBuilder& sb, DeclRefBase* declRefBase) @@ -92,6 +95,7 @@ void printDiagnosticArg(StringBuilder& sb, ASTNodeType nodeType) case ASTNodeType::EmptyDecl: sb << "empty"; break; case ASTNodeType::SyntaxDecl: sb << "syntax"; break; case ASTNodeType::DeclGroup: sb << "decl-group"; break; + case ASTNodeType::RequireCapabilityDecl: sb << "__require_capability"; break; default: sb << "decl"; break; } } @@ -862,6 +866,18 @@ Decl* getParentDecl(Decl* decl) return decl; } +Decl* getParentAggTypeDecl(Decl* decl) +{ + decl = decl->parentDecl; + while (decl) + { + if (as<AggTypeDecl>(decl)) + return decl; + decl = decl->parentDecl; + } + return nullptr; +} + Decl* getParentFunc(Decl* decl) { while (decl) diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h index fff567049..6b134e523 100644 --- a/source/slang/slang-syntax.h +++ b/source/slang/slang-syntax.h @@ -337,7 +337,7 @@ namespace Slang /// Get the parent decl, skipping any generic decls in between. Decl* getParentDecl(Decl* decl); - + Decl* getParentAggTypeDecl(Decl* decl); Decl* getParentFunc(Decl* decl); } // namespace Slang diff --git a/source/slang/slang-workspace-version.cpp b/source/slang/slang-workspace-version.cpp index 1b17f3170..0826ca557 100644 --- a/source/slang/slang-workspace-version.cpp +++ b/source/slang/slang-workspace-version.cpp @@ -194,6 +194,7 @@ void WorkspaceVersion::parseDiagnostics(String compilerOutput) { List<UnownedStringSlice> lines; StringUtil::calcLines(compilerOutput.getUnownedSlice(), lines); + for (Index lineIndex = 0; lineIndex < lines.getCount(); lineIndex++) { auto line = lines[lineIndex]; @@ -272,7 +273,19 @@ void WorkspaceVersion::parseDiagnostics(String compilerOutput) diagnostic.range.end.line--; diagnostic.range.end.character--; } - diagnosticList.messages.add(diagnostic); + if (diagnostic.code == -1 && diagnosticList.messages.getCount()) + { + // If this is a decoration message, add it as related information. + LanguageServerProtocol::DiagnosticRelatedInformation relatedInfo; + relatedInfo.location.range = diagnostic.range; + relatedInfo.location.uri = URI::fromLocalFilePath(fileName.getUnownedSlice()).uri; + relatedInfo.message = diagnostic.message; + diagnosticList.messages.getLast().relatedInformation.add(relatedInfo); + } + else + { + diagnosticList.messages.add(diagnostic); + } if (diagnosticList.messages.getCount() >= 1000) break; } diff --git a/tests/diagnostics/extension-visibility.slang.expected b/tests/diagnostics/extension-visibility.slang.expected index eed86715d..10033f24b 100644 --- a/tests/diagnostics/extension-visibility.slang.expected +++ b/tests/diagnostics/extension-visibility.slang.expected @@ -3,7 +3,7 @@ standard error = { tests/diagnostics/extension-visibility.slang(17): error 39999: could not specialize generic for arguments of type (MyThing) return helper(thing); ^ -tests/diagnostics/extension-visibility-a.slang(14): note 39999: see declaration of public func helper<T>(T) -> int +tests/diagnostics/extension-visibility-a.slang(14): note: see declaration of public func helper<T>(T) -> int public int helper<T : IThing>(T thing) ^~~~~~ } diff --git a/tests/diagnostics/generic-type-inference-fail.slang.expected b/tests/diagnostics/generic-type-inference-fail.slang.expected index 17cb28ca2..1910954d0 100644 --- a/tests/diagnostics/generic-type-inference-fail.slang.expected +++ b/tests/diagnostics/generic-type-inference-fail.slang.expected @@ -3,7 +3,7 @@ standard error = { tests/diagnostics/generic-type-inference-fail.slang(66): error 39999: could not specialize generic for arguments of type (int) var obj3 = CreateT_Assoc_Inner(1); // ERROR. ^ -tests/diagnostics/generic-type-inference-fail.slang(18): note 39999: see declaration of func CreateT_Assoc_Inner<T>(int) -> T.TAssoc +tests/diagnostics/generic-type-inference-fail.slang(18): note: see declaration of func CreateT_Assoc_Inner<T>(int) -> T.TAssoc T.TAssoc CreateT_Assoc_Inner<T:IInterface>(int inVal) ^~~~~~~~~~~~~~~~~~~ } diff --git a/tests/language-feature/capability/capability3.slang b/tests/language-feature/capability/capability3.slang new file mode 100644 index 000000000..f7ba1d793 --- /dev/null +++ b/tests/language-feature/capability/capability3.slang @@ -0,0 +1,47 @@ +//TEST:SIMPLE(filecheck=CHECK): -target spirv -emit-spirv-directly -entry main -stage compute + +// Test that capabilities can be declared on module. + +[require(glsl)] +[require(spirv)] +module test; + +void f() +{ + __require_capability glsl; +} + +// CHECK: ([[# @LINE+1]]): error 36108 +public void g() +{ + __require_capability spvAtomicFloat16AddEXT; +} + +void l() +{ + __target_switch + { + case glsl: + f(); + return; + case spirv: + __require_capability spvAtomicFloat16AddEXT; + return; + } +} + +// CHECK: ([[# @LINE+1]]): error 36104: {{.*}} +public void use() +{ + l(); // Error +} + +// CHECK-NOT: ([[# @LINE+1]]): error +[require(spirv, spvAtomicFloat16AddEXT)] +public void use1() +{ + l(); // Error +} + +void main() +{}
\ No newline at end of file diff --git a/tests/language-feature/capability/capability4.slang b/tests/language-feature/capability/capability4.slang new file mode 100644 index 000000000..fce1f78ac --- /dev/null +++ b/tests/language-feature/capability/capability4.slang @@ -0,0 +1,25 @@ +//TEST:SIMPLE(filecheck=CHECK): -target spirv -emit-spirv-directly -entry main -stage compute + +// Check that a non-static member method implictly requires capabilities +// defined in ThisType. + +[require(hlsl)] +struct Type +{ + int member; + [require(glsl)] + [mutating] + // CHECK: ([[# @LINE+1]]): error 36108: + void f() + { + } + + [require(glsl)] + // CHECK-NOT: ([[# @LINE+1]]): error 36108: + static void f1() + { + } +} + +void main() +{} |
