summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorArielG-NV <159081215+ArielG-NV@users.noreply.github.com>2025-05-21 21:11:01 -0700
committerGitHub <noreply@github.com>2025-05-22 04:11:01 +0000
commit27c6e9b01f7386263bde90e16812be46327015c2 (patch)
treea640d6882da0c9ee90ef64872b1b94c721039cdf /source
parent21346ded32be9091389ca53815c1ba56feff8a01 (diff)
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 <std-revision>` 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.
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-type-text-util.cpp16
-rw-r--r--source/core/slang-type-text-util.h5
-rw-r--r--source/slang/slang-ast-modifier.h6
-rw-r--r--source/slang/slang-check-decl.cpp198
-rw-r--r--source/slang/slang-check-modifier.cpp3
-rw-r--r--source/slang/slang-compiler-options.cpp2
-rw-r--r--source/slang/slang-diagnostic-defs.h56
-rw-r--r--source/slang/slang-options.cpp36
-rw-r--r--source/slang/slang-parser.cpp2
9 files changed, 314 insertions, 10 deletions
diff --git a/source/core/slang-type-text-util.cpp b/source/core/slang-type-text-util.cpp
index 2261ed614..4797395b7 100644
--- a/source/core/slang-type-text-util.cpp
+++ b/source/core/slang-type-text-util.cpp
@@ -99,6 +99,12 @@ static const NamesDescriptionValue s_languageInfos[] = {
{SLANG_SOURCE_LANGUAGE_CUDA, "cu,cuda", "CUDA"},
};
+static const NamesDescriptionValue s_stdRevisionInfos[] = {
+ {SLANG_STD_REVISION_UNKNOWN, "unknown", "Unknown"},
+ {SLANG_STD_REVISION_2025, "2025,default", "Slang language rules for 2025 and older"},
+ {SLANG_STD_REVISION_2026, "2026", "Slang language rules for 2026 and newer"},
+};
+
static const NamesDescriptionValue s_compilerInfos[] = {
{SLANG_PASS_THROUGH_NONE, "none", "Unknown"},
{SLANG_PASS_THROUGH_FXC, "fxc", "FXC HLSL compiler"},
@@ -217,6 +223,11 @@ static const NamesDescriptionValue s_fileSystemTypes[] = {
return makeConstArrayView(s_languageInfos);
}
+/* static */ ConstArrayView<NamesDescriptionValue> TypeTextUtil::getStdRevisionInfos()
+{
+ return makeConstArrayView(s_stdRevisionInfos);
+}
+
/* static */ ConstArrayView<NamesDescriptionValue> TypeTextUtil::getCompilerInfos()
{
return makeConstArrayView(s_compilerInfos);
@@ -317,6 +328,11 @@ static const NamesDescriptionValue s_fileSystemTypes[] = {
return NameValueUtil::findValue(getLanguageInfos(), text, SLANG_SOURCE_LANGUAGE_UNKNOWN);
}
+/* static */ SlangStdRevision TypeTextUtil::findStdRevision(const UnownedStringSlice& text)
+{
+ return NameValueUtil::findValue(getStdRevisionInfos(), text, SLANG_STD_REVISION_UNKNOWN);
+}
+
/* static */ SlangPassThrough TypeTextUtil::findPassThrough(const UnownedStringSlice& slice)
{
return NameValueUtil::findValue(getCompilerInfos(), slice, SLANG_PASS_THROUGH_NONE);
diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h
index 730888254..bc136c3ab 100644
--- a/source/core/slang-type-text-util.h
+++ b/source/core/slang-type-text-util.h
@@ -33,6 +33,8 @@ struct TypeTextUtil
/// Get the language infos
static ConstArrayView<NamesDescriptionValue> getLanguageInfos();
+ /// Get the std revision infos
+ static ConstArrayView<NamesDescriptionValue> getStdRevisionInfos();
/// Get the compiler infos
static ConstArrayView<NamesDescriptionValue> getCompilerInfos();
/// Get the archive type infos
@@ -71,6 +73,9 @@ struct TypeTextUtil
/// Given a source language name returns a source language. Name here is distinct from extension
static SlangSourceLanguage findSourceLanguage(const UnownedStringSlice& text);
+ /// Given a std revision returns a std revision.
+ static SlangStdRevision findStdRevision(const UnownedStringSlice& text);
+
/// Given a name returns the pass through
static SlangPassThrough findPassThrough(const UnownedStringSlice& slice);
static SlangResult findPassThrough(
diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h
index ac1960685..1a65cf4a9 100644
--- a/source/slang/slang-ast-modifier.h
+++ b/source/slang/slang-ast-modifier.h
@@ -85,6 +85,12 @@ class ExternModifier : public Modifier
};
FIDDLE()
+class DynModifier : public Modifier
+{
+ FIDDLE(...)
+};
+
+FIDDLE()
class HLSLExportModifier : public Modifier
{
FIDDLE(...)
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<GenericDecl>(d))
+ d = genericDecl->inner;
+ if (as<AssocTypeDecl>(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<SlangSourceLanguage>(CompilerOptionName::Language)) &&
+ SLANG_STD_REVISION_2026 ==
+ optionSet.getEnumOption<SlangStdRevision>(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<DynModifier>())
+ 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<GenericDecl>(m))
+ {
+ if (as<FuncDecl>(genericDecl->inner))
+ {
+ sink->diagnose(m, Diagnostics::cannotHaveGenericMethodInDynInterface);
+ }
+ continue;
+ }
+ else if (auto funcDecl = as<FuncDecl>(m))
+ {
+ visitor->ensureDecl(m, DeclCheckState::ModifiersChecked);
+ for (auto modifier : funcDecl->modifiers)
+ {
+ if (as<MutatingAttribute>(modifier))
+ {
+ sink->diagnose(m, Diagnostics::cannotHaveMutatingMethodInDynInterface);
+ }
+ else if (as<DifferentiableAttribute>(modifier))
+ {
+ sink->diagnose(m, Diagnostics::cannotHaveDifferentiableMethodInDynInterface);
+ }
+ }
+ continue;
+ }
+ else if (auto inheritanceDecl = as<InheritanceDecl>(m))
+ {
+ visitor->ensureDecl(m, DeclCheckState::ReadyForLookup);
+ auto inheritedInterfaceDeclRefType =
+ isDeclRefTypeOf<InterfaceDecl>(inheritanceDecl->base.type);
+ if (!inheritedInterfaceDeclRefType)
+ continue;
+
+ auto inheritedInterfaceDecl = inheritedInterfaceDeclRefType.getDecl();
+ if (!inheritedInterfaceDecl->hasModifier<DynModifier>())
+ 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<InterfaceDecl>(decl->base.type);
+ if (!interfaceDeclRef)
+ return;
+ auto interfaceDecl = interfaceDeclRef.getDecl();
+ bool interfaceDeclIsDyn = interfaceDecl->hasModifier<DynModifier>();
+ if (!interfaceDeclIsDyn)
+ return;
+
+ if (!allowExperimentalDynamicDispatch(optionSet))
+ {
+ if (auto extensionDeclParent = as<ExtensionDecl>(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<AggTypeDecl>(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<VarDecl>(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<ConstructorDecl*> _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<AnyValueSizeAttribute>())
+ addModifier(interfaceDecl, m_astBuilder->create<DynModifier>());
+
+ 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<InterfaceDecl>(inheritanceDecl->parentDecl))
return;
@@ -6964,16 +7152,6 @@ RefPtr<WitnessTable> SemanticsVisitor::checkInterfaceConformance(
return witnessTable;
}
-static bool isAssociatedTypeDecl(Decl* decl)
-{
- auto d = decl;
- while (auto genericDecl = as<GenericDecl>(d))
- d = genericDecl->inner;
- if (as<AssocTypeDecl>(d))
- return true;
- return false;
-}
-
bool SemanticsVisitor::checkInterfaceConformance(
ConformanceCheckingContext* context,
Type* subType,
diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp
index f9ad3a02f..900f503db 100644
--- a/source/slang/slang-check-modifier.cpp
+++ b/source/slang/slang-check-modifier.cpp
@@ -1532,6 +1532,8 @@ bool isModifierAllowedOnDecl(bool isGLSLInput, ASTNodeType modifierType, Decl* d
if (!as<VarDeclBase>(decl))
return false;
return isGlobalDecl(decl) || isEffectivelyStatic(decl);
+ case ASTNodeType::DynModifier:
+ return as<InterfaceDecl>(decl) || as<VarDecl>(decl) || as<ParamDecl>(decl);
default:
return true;
}
@@ -1671,6 +1673,7 @@ Modifier* SemanticsVisitor::checkModifier(
{
auto moduleDecl = getModuleDecl(decl);
bool isGLSLInput = getOptionSet().getBoolOption(CompilerOptionName::AllowGLSL);
+
if (!isGLSLInput && moduleDecl && moduleDecl->findModifier<GLSLModuleModifier>())
isGLSLInput = true;
if (!isModifierAllowedOnDecl(isGLSLInput, m->astNodeType, decl))
diff --git a/source/slang/slang-compiler-options.cpp b/source/slang/slang-compiler-options.cpp
index ca8397f3e..0d7f19642 100644
--- a/source/slang/slang-compiler-options.cpp
+++ b/source/slang/slang-compiler-options.cpp
@@ -208,6 +208,8 @@ CompilerOptionValue Slang::CompilerOptionSet::getDefault(CompilerOptionName name
{
case CompilerOptionName::Optimization:
return CompilerOptionValue::fromEnum(OptimizationLevel::Default);
+ case CompilerOptionName::StdRevision:
+ return CompilerOptionValue::fromEnum(SlangStdRevision::SLANG_STD_REVISION_DEFAULT);
default:
return CompilerOptionValue();
}
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index cafd61062..bf90c6608 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -146,6 +146,9 @@ DIAGNOSTIC(
entryPointsNeedToBeAssociatedWithTranslationUnits,
"when using multiple source files, entry points must be specified after their corresponding "
"source file(s)")
+
+DIAGNOSTIC(21, Error, unknownStdRevision, "unknown language standard revision '$0'")
+
DIAGNOSTIC(22, Error, unknownDownstreamCompiler, "unknown downstream compiler '$0'")
DIAGNOSTIC(26, Error, unknownOptimiziationLevel, "unknown optimization level '$0'")
@@ -827,6 +830,59 @@ DIAGNOSTIC(
DIAGNOSTIC(33070, Error, expectedFunction, "expected a function, got '$0'")
DIAGNOSTIC(33071, Error, expectedAStringLiteral, "expected a string literal")
+// `dyn` and `some` errors
+DIAGNOSTIC(33072, Error, cannotHaveGenericDynInterface, "dyn interfaces cannot be generic: '$0'.")
+DIAGNOSTIC(
+ 33073,
+ Error,
+ cannotHaveAssociatedTypeInDynInterface,
+ "dyn interfaces cannot have associatedType members.")
+DIAGNOSTIC(
+ 33074,
+ Error,
+ cannotHaveGenericMethodInDynInterface,
+ "dyn interfaces cannot have generic methods.")
+DIAGNOSTIC(
+ 33075,
+ Error,
+ cannotHaveMutatingMethodInDynInterface,
+ "dyn interfaces cannot have [mutating] methods.")
+DIAGNOSTIC(
+ 33076,
+ Error,
+ cannotHaveDifferentiableMethodInDynInterface,
+ "dyn interfaces cannot have [Differentiable] methods.")
+DIAGNOSTIC(
+ 33077,
+ Error,
+ DynInterfaceCannotInheritNonDynInterface,
+ "dyn interface '$0' may only inherit 'dyn' interfaces. '$1' is not a dyn interface.")
+DIAGNOSTIC(
+ 33078,
+ Error,
+ cannotUseExtensionToMakeTypeConformToDynInterface,
+ "cannot use a extension to conform to a dyn interface '$0'.")
+DIAGNOSTIC(
+ 33079,
+ Error,
+ cannotHaveUnsizedMemberWhenInheritingDynInterface,
+ "cannot have unsized member '$0' when inheriting from dyn interface '$1'.")
+DIAGNOSTIC(
+ 33080,
+ Error,
+ cannotHaveOpaqueMemberWhenInheritingDynInterface,
+ "cannot have opaque member '$0' when inheriting from dyn interface '$1'.")
+DIAGNOSTIC(
+ 33081,
+ Error,
+ cannotHaveNonCopyableMemberWhenInheritingDynInterface,
+ "cannot have non-copyable member '$0' when inheriting from dyn interface '$1'.")
+DIAGNOSTIC(
+ 33082,
+ Error,
+ cannotConformGenericToDynInterface,
+ "cannot conform generic type '$0' to dyn interface '$1'.")
+
DIAGNOSTIC(
-1,
Note,
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index d73bc4307..0182b4c22 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -63,6 +63,7 @@ enum class ValueCategory
FileSystemType,
VulkanShift,
SourceEmbedStyle,
+ StdRevision,
CountOf,
};
@@ -149,6 +150,14 @@ void initCommandOptions(CommandOptions& options)
options.addCategory(
CategoryKind::Value,
+ "std-revision",
+ "Std Revision",
+ UserValue(ValueCategory::StdRevision));
+ options.addValues(TypeTextUtil::getStdRevisionInfos());
+
+
+ options.addCategory(
+ CategoryKind::Value,
"archive-type",
"Archive Type",
UserValue(ValueCategory::ArchiveType));
@@ -446,6 +455,10 @@ void initCommandOptions(CommandOptions& options)
"Display the build version. This is the contents of git describe --tags.\n"
"It is typically only set from automated builds(such as distros available on github).A "
"user build will by default be 'unknown'."},
+ {OptionKind::StdRevision,
+ "-std",
+ "-std <std-revision>",
+ "Specifies the language standard that should be used."},
{OptionKind::WarningsAsErrors,
"-warnings-as-errors",
"-warnings-as-errors all or -warnings-as-errors <id>[,<id>...]",
@@ -841,6 +854,10 @@ void initCommandOptions(CommandOptions& options)
"-enable-experimental-passes",
nullptr,
"Enable experimental compiler passes"},
+ {OptionKind::EnableExperimentalDynamicDispatch,
+ "-enable-experimental-dynamic-dispatch",
+ nullptr,
+ "Enable experimental dynamic dispatch features"},
{OptionKind::EmbedDownstreamIR,
"-embed-downstream-ir",
nullptr,
@@ -2146,6 +2163,7 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
case OptionKind::ValidateUniformity:
case OptionKind::AllowGLSL:
case OptionKind::EnableExperimentalPasses:
+ case OptionKind::EnableExperimentalDynamicDispatch:
case OptionKind::EmitIr:
case OptionKind::DumpIntermediates:
case OptionKind::DumpReproOnError:
@@ -2537,6 +2555,24 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
}
break;
}
+ case OptionKind::StdRevision:
+ {
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(name));
+
+ SlangStdRevision stdRevision =
+ TypeTextUtil::findStdRevision(name.value.getUnownedSlice());
+ if (stdRevision == SLANG_STD_REVISION_UNKNOWN)
+ {
+ m_sink->diagnose(name.loc, Diagnostics::unknownStdRevision, name.value);
+ return SLANG_FAIL;
+ }
+ else
+ {
+ linkage->m_optionSet.add(OptionKind::StdRevision, stdRevision);
+ }
+ break;
+ }
case OptionKind::Stage:
{
CommandLineArg name;
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 7ff0a6a2f..b2a006adc 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -9159,6 +9159,8 @@ static const SyntaxParseInfo g_parseSyntaxEntries[] = {
_makeParseModifier("param", getSyntaxClass<ParamModifier>()),
_makeParseModifier("extern", getSyntaxClass<ExternModifier>()),
+ _makeParseModifier("dyn", getSyntaxClass<DynModifier>()),
+
_makeParseModifier("row_major", getSyntaxClass<HLSLRowMajorLayoutModifier>()),
_makeParseModifier("column_major", getSyntaxClass<HLSLColumnMajorLayoutModifier>()),