From 27c6e9b01f7386263bde90e16812be46327015c2 Mon Sep 17 00:00:00 2001 From: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> Date: Wed, 21 May 2025 21:11:01 -0700 Subject: Initial `dyn` keyword support & `-lang 2026` compiler option (#7172) fixes: [#7143](https://github.com/shader-slang/slang/issues/7143) fixes: [#7146](https://github.com/shader-slang/slang/issues/7146) Goal of PR: * This PR is part of the larger #7115 refactor to how dynamic dispatch works. * The first step is to add the `-std ` flag. * The second step is to provide basic `dyn` keyword support in AST. This does not include `varDecl` support since most of these interactions require `some` keyword support. Future PR(s) goal: * Support `some` keyword in AST. With this we will also implement all varDecl interactions between `dyn` and `some`. * Add IR support for `some` and `dyn`. Breakdown of PR: * most of the logic is in `validateDyn.*`. This was done so that in the future when we implement more features we will have an easy time removing/adding restrictions to `dyn` interfaces. Breaking changes: * As per spec (https://github.com/shader-slang/spec/pull/14/files), any type conforming to a `dyn` interface errors if member list contains one of the following: opaque type, non copyable type, or unsized type. * Due to the breaking change, the test `tests\compute\dynamic-dispatch-bindless-texture.slang` is incorrect. This has been fixed. --- source/slang/slang-check-decl.cpp | 198 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 188 insertions(+), 10 deletions(-) (limited to 'source/slang/slang-check-decl.cpp') diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index daa761d3d..c03d8e985 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -23,6 +23,176 @@ namespace Slang { + +static bool isAssociatedTypeDecl(Decl* decl) +{ + auto d = decl; + while (auto genericDecl = as(d)) + d = genericDecl->inner; + if (as(d)) + return true; + return false; +} + +static bool isSlang2026(CompilerOptionSet& optionSet) +{ + if (!optionSet.hasOption(CompilerOptionName::Language)) + return false; + return SLANG_SOURCE_LANGUAGE_SLANG == + SlangSourceLanguage( + optionSet.getEnumOption(CompilerOptionName::Language)) && + SLANG_STD_REVISION_2026 == + optionSet.getEnumOption(CompilerOptionName::StdRevision); +} + +static bool allowExperimentalDynamicDispatch(CompilerOptionSet& optionSet) +{ + return optionSet.getBoolOption(CompilerOptionName::EnableExperimentalDynamicDispatch) || + !isSlang2026(optionSet); +} + +static void validateDynInterfaceUsage( + SemanticsDeclVisitorBase* visitor, + DiagnosticSink* sink, + CompilerOptionSet& optionSet, + InterfaceDecl* decl) +{ + if (allowExperimentalDynamicDispatch(optionSet)) + return; + + if (!decl->hasModifier()) + return; + + // validate members inside `dyn interface` + for (auto m : decl->members) + { + if (isAssociatedTypeDecl(m)) + { + sink->diagnose(m, Diagnostics::cannotHaveAssociatedTypeInDynInterface); + continue; + } + else if (auto genericDecl = as(m)) + { + if (as(genericDecl->inner)) + { + sink->diagnose(m, Diagnostics::cannotHaveGenericMethodInDynInterface); + } + continue; + } + else if (auto funcDecl = as(m)) + { + visitor->ensureDecl(m, DeclCheckState::ModifiersChecked); + for (auto modifier : funcDecl->modifiers) + { + if (as(modifier)) + { + sink->diagnose(m, Diagnostics::cannotHaveMutatingMethodInDynInterface); + } + else if (as(modifier)) + { + sink->diagnose(m, Diagnostics::cannotHaveDifferentiableMethodInDynInterface); + } + } + continue; + } + else if (auto inheritanceDecl = as(m)) + { + visitor->ensureDecl(m, DeclCheckState::ReadyForLookup); + auto inheritedInterfaceDeclRefType = + isDeclRefTypeOf(inheritanceDecl->base.type); + if (!inheritedInterfaceDeclRefType) + continue; + + auto inheritedInterfaceDecl = inheritedInterfaceDeclRefType.getDecl(); + if (!inheritedInterfaceDecl->hasModifier()) + sink->diagnose( + m, + Diagnostics::DynInterfaceCannotInheritNonDynInterface, + decl, + inheritedInterfaceDecl); + } + } + + // dyn interface cannot be generic + if (visitor->GetOuterGeneric(decl)) + { + sink->diagnose(decl, Diagnostics::cannotHaveGenericDynInterface, decl); + } +} + +static void validateDynInterfaceUseWithInheritanceDecl( + SemanticsDeclVisitorBase* visitor, + DiagnosticSink* sink, + CompilerOptionSet& optionSet, + InheritanceDecl* decl) +{ + auto interfaceDeclRef = isDeclRefTypeOf(decl->base.type); + if (!interfaceDeclRef) + return; + auto interfaceDecl = interfaceDeclRef.getDecl(); + bool interfaceDeclIsDyn = interfaceDecl->hasModifier(); + if (!interfaceDeclIsDyn) + return; + + if (!allowExperimentalDynamicDispatch(optionSet)) + { + if (auto extensionDeclParent = as(decl->parentDecl)) + { + // not allowed to extend to conform to a 'dyn interface' + sink->diagnose( + extensionDeclParent, + Diagnostics::cannotUseExtensionToMakeTypeConformToDynInterface, + interfaceDecl); + } + else if (visitor->GetOuterGeneric(decl->parentDecl)) + { + sink->diagnose( + decl, + Diagnostics::cannotConformGenericToDynInterface, + decl->parentDecl, + interfaceDecl); + } + } + if (auto aggTypeDeclParent = as(decl->parentDecl)) + { + // Ensure if we inherit from a `dyn interface` that the parent does not have: opaque + // types, unsized types, non-copyable types + for (auto m : aggTypeDeclParent->members) + { + auto varDecl = as(m); + if (!varDecl) + continue; + + visitor->ensureDecl(varDecl, DeclCheckState::ReadyForLookup); + + if (isNonCopyableType(varDecl->getType())) + { + sink->diagnose( + m, + Diagnostics::cannotHaveNonCopyableMemberWhenInheritingDynInterface, + m, + interfaceDecl); + } + + int varTypeTags = (int)visitor->getTypeTags(varDecl->getType()); + bool isUnsized = varTypeTags & (int)TypeTag::Unsized; + bool isOpaque = varTypeTags & (int)TypeTag::Opaque; + if (isUnsized) + sink->diagnose( + m, + Diagnostics::cannotHaveUnsizedMemberWhenInheritingDynInterface, + m, + interfaceDecl); + if (isOpaque) + sink->diagnose( + m, + Diagnostics::cannotHaveOpaqueMemberWhenInheritingDynInterface, + m, + interfaceDecl); + } + } +} + static ConstructorDecl* _getDefaultCtor(StructDecl* structDecl); static List _getCtorList( ASTBuilder* m_astBuilder, @@ -70,6 +240,8 @@ struct SemanticsDeclModifiersVisitor : public SemanticsDeclVisitorBase, void visitDecl(Decl* decl) { checkModifiers(decl); } void visitStructDecl(StructDecl* structDecl); + + void visitInterfaceDecl(InterfaceDecl* interfaceDecl); }; struct SemanticsDeclScopeWiringVisitor : public SemanticsDeclVisitorBase, @@ -1485,6 +1657,16 @@ IntVal* SemanticsVisitor::_validateCircularVarDefinition(VarDeclBase* varDecl) nullptr); } +void SemanticsDeclModifiersVisitor::visitInterfaceDecl(InterfaceDecl* interfaceDecl) +{ + visitDecl(interfaceDecl); + + if (interfaceDecl->hasModifier()) + addModifier(interfaceDecl, m_astBuilder->create()); + + validateDynInterfaceUsage(this, getSink(), getOptionSet(), interfaceDecl); +} + void SemanticsDeclModifiersVisitor::visitStructDecl(StructDecl* structDecl) { checkModifiers(structDecl); @@ -3114,6 +3296,12 @@ struct SemanticsDeclDifferentialConformanceVisitor void visitInheritanceDecl(InheritanceDecl* inheritanceDecl) { + validateDynInterfaceUseWithInheritanceDecl( + this, + getSink(), + getOptionSet(), + inheritanceDecl); + if (as(inheritanceDecl->parentDecl)) return; @@ -6964,16 +7152,6 @@ RefPtr SemanticsVisitor::checkInterfaceConformance( return witnessTable; } -static bool isAssociatedTypeDecl(Decl* decl) -{ - auto d = decl; - while (auto genericDecl = as(d)) - d = genericDecl->inner; - if (as(d)) - return true; - return false; -} - bool SemanticsVisitor::checkInterfaceConformance( ConformanceCheckingContext* context, Type* subType, -- cgit v1.2.3