diff options
| author | Yong He <yonghe@outlook.com> | 2025-05-29 08:05:57 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-29 08:05:57 -0700 |
| commit | faf042ecc3e688a1a3ffbe1ac44d18dd7ddf441a (patch) | |
| tree | b54abb2e65b7791d74335ead396cf762f805ab5c | |
| parent | 45d794f57d453a5564a7360400c5bfc04bf12b31 (diff) | |
Language version + tuple syntax. (#7230)
* Language version + tuple syntax.
* Fix compile error.
* regenerate documentation Table of Contents
* Fix.
* regenerate command line reference
* Fix.
* Fix.
* Fix more test failures.
* revert empty line change,
* Retrigger CI
* #version->#lang
* Update source/core/slang-type-text-util.cpp
Co-authored-by: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com>
* Remove comments.
* Fix parsing logic.
* Fix parser.
* Fix parser.
* update test comment
* Update options.
* regenerate documentation Table of Contents
* regenerate command line reference
---------
Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Co-authored-by: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com>
33 files changed, 595 insertions, 142 deletions
diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md index b976d3ed3..2d684f961 100644 --- a/docs/command-line-slangc-reference.md +++ b/docs/command-line-slangc-reference.md @@ -22,7 +22,7 @@ slangc -help-style markdown -h * [Deprecated](#Deprecated) * [compiler](#compiler) * [language](#language) -* [std-revision](#std-revision) +* [language-version](#language-version) * [archive-type](#archive-type) * [line-directive-mode](#line-directive-mode) * [debug-info-format](#debug-info-format) @@ -227,7 +227,7 @@ It is typically only set from automated builds(such as distros available on gith <a id="std"></a> ### -std -**-std <[std-revision](#std-revision)>** +**-std <[language-version](#language-version)>** Specifies the language standard that should be used. @@ -898,14 +898,14 @@ Language * `hlsl` : HLSL language * `cu`, `cuda` : CUDA -<a id="std-revision"></a> -## std-revision +<a id="language-version"></a> +## language-version -Std Revision +Language Version -* `unknown` : Unknown -* `2025`, `default` : Slang language rules for 2025 and older -* `2026` : Slang language rules for 2026 and newer +* `legacy`, `default`, `2018` : Legacy Slang language +* `2025` : Slang language rules for 2025 and older +* `2026`, `latest` : Slang language rules for 2026 and newer <a id="archive-type"></a> ## archive-type diff --git a/docs/user-guide/11-language-version.md b/docs/user-guide/11-language-version.md new file mode 100644 index 000000000..52e5ff22d --- /dev/null +++ b/docs/user-guide/11-language-version.md @@ -0,0 +1,41 @@ +# Language Version + +Like many programming languages, Slang experiences a tension between the desire for rapid innovation/evolution and stability. One of the benefits that users of Slang have so far enjoyed has been the rapid pace of innovation in the language and its standard library. However, as developers start to have larger bodies of Slang code, they may become concerned that changes to the language could break existing code. There is no magical way to keep innovating while also keeping the language static. + +Slang supports using `#language` preprocessor directive, as well as the `-std` compiler option (`CompilerOptionName::LanguageVersion`) to specify the language version that a source file is written against. The source file will then be parsed and checked by the compiler using the rules from the specified language version. + +Users are advised to provide a `#language` directive as the first non-white-space line in their source file, such as: + +``` +#language slang 2026 +``` + +The following version strings are allowed: +- `latest`: use the latest language version supported by the current compiler. +- `legacy`: use the legacy Slang language. +- `2018`: equivalent to `legacy`. +- `2025`: Slang language version 2025. +- `2026`: Slang language version 2026. + +If no `#language` line exists and no version is specified via compiler options, the default setting is `legacy`. + +## The Legacy Slang Language + +When language version is set to `legacy`, the compiler behavior will be consistent with the Slang language as in 2018-2023. Specifically: + +- All declarations have `public` visibility. +- `module` declaration is not required at the start of each module. + +## Slang 2025 + +Slang language version 2025 brings these changes on top of the legacy language: + +- All declarations have `internal` visibility. +- `module` declaration is required at the start of each module. + +## Slang 2026 + +Slang language 2026 brings these changes on top of Slang 2025: + +- Comma expression is removed when it is used inside a parenthesis. The expression `(a, b)` no longer evaluates to have `b`'s type as in C/C++. Instead, `(a,b)` means `makeTuple(a,b)` and returns a tuple consists of `a` and `b`. +- User must explicitly opt-in to enable dynamic dispatch with `dyn` keyword. More rigorous validation are enabled to make sure dynamic dispatch is not triggerred accidentally. See [SP#024](https://github.com/shader-slang/spec/blob/main/proposals/024-any-dyn-types.md) for details. diff --git a/docs/user-guide/a3-02-reference-capability-atoms.md b/docs/user-guide/a3-02-reference-capability-atoms.md index 78affcb7d..56809055b 100644 --- a/docs/user-guide/a3-02-reference-capability-atoms.md +++ b/docs/user-guide/a3-02-reference-capability-atoms.md @@ -1270,6 +1270,9 @@ Compound Capabilities `raytracing_anyhit_closesthit` > Collection of capabilities for raytracing with the shader stages of anyhit and closesthit. +`raytracing_lss` +> Collection of capabilities for linear swept spheres. + `raytracing_anyhit_closesthit_intersection` > Collection of capabilities for raytracing with the shader stages of anyhit, closesthit, and intersection. diff --git a/docs/user-guide/toc.html b/docs/user-guide/toc.html index c83f36282..5e8ecc207 100644 --- a/docs/user-guide/toc.html +++ b/docs/user-guide/toc.html @@ -151,6 +151,13 @@ <li data-link="link-time-specialization#additional-remarks"><span>Additional Remarks</span></li> </ul> </li> +<li data-link="11-language-version"><span>language slang 2026</span> +<ul class="toc_list"> +<li data-link="11-language-version#the-legacy-slang-language"><span>The Legacy Slang Language</span></li> +<li data-link="11-language-version#slang-2025"><span>Slang 2025</span></li> +<li data-link="11-language-version#slang-2026"><span>Slang 2026</span></li> +</ul> +</li> <li data-link="a1-special-topics"><span>Special Topics</span> <ul class="toc_list"> <li data-link="a1-01-matrix-layout"><span>Handling Matrix Layout Differences on Different Platforms</span> diff --git a/include/slang.h b/include/slang.h index 4164ce9e9..dc52e80b3 100644 --- a/include/slang.h +++ b/include/slang.h @@ -627,17 +627,6 @@ typedef uint32_t SlangSizeT; SLANG_TARGET_COUNT_OF, }; - typedef int SlangStdRevisionIntegral; - enum SlangStdRevision : SlangStdRevisionIntegral - { - SLANG_STD_REVISION_UNKNOWN, - SLANG_STD_REVISION_2025, - SLANG_STD_REVISION_2026, - SLANG_STD_REVISION_COUNT_OF, - - SLANG_STD_REVISION_DEFAULT = SLANG_STD_REVISION_2025, - }; - /* A "container format" describes the way that the outputs for multiple files, entry points, targets, etc. should be combined into a single artifact for output. */ @@ -1034,7 +1023,7 @@ typedef uint32_t SlangSizeT; DumpModule, EnableExperimentalDynamicDispatch, // bool, experimental - StdRevision, // intValue0: SlangStdRevision + LanguageVersion, // intValue0: SlangLanguageVersion CountOf, }; @@ -4518,7 +4507,12 @@ struct SpecializationArg enum SlangLanguageVersion { - SLANG_LANGUAGE_VERSION_2025 = 2025 + SLANG_LANGUAGE_VERSION_UNKNOWN = 0, + SLANG_LANGUAGE_VERSION_LEGACY = 2018, + SLANG_LANGUAGE_VERSION_2025 = 2025, + SLANG_LANGUAGE_VERSION_2026 = 2026, + SLANG_LANGAUGE_VERSION_DEFAULT = SLANG_LANGUAGE_VERSION_LEGACY, + SLANG_LANGUAGE_VERSION_LATEST = SLANG_LANGUAGE_VERSION_2026, }; @@ -4532,8 +4526,8 @@ struct SlangGlobalSessionDesc /// Slang API version. uint32_t apiVersion = SLANG_API_VERSION; - /// Slang language version. - uint32_t languageVersion = SLANG_LANGUAGE_VERSION_2025; + /// Specify the oldest Slang language version that any sessions will use. + uint32_t minLanguageVersion = SLANG_LANGUAGE_VERSION_2025; /// Whether to enable GLSL support. bool enableGLSL = false; diff --git a/source/core/slang-type-text-util.cpp b/source/core/slang-type-text-util.cpp index 4797395b7..9f55b69e2 100644 --- a/source/core/slang-type-text-util.cpp +++ b/source/core/slang-type-text-util.cpp @@ -99,10 +99,10 @@ 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_languageVersionInfos[] = { + {SLANG_LANGUAGE_VERSION_LEGACY, "legacy,default,2018", "Legacy Slang language"}, + {SLANG_LANGUAGE_VERSION_2025, "2025", "Slang language rules for 2025 and older"}, + {SLANG_LANGUAGE_VERSION_2026, "2026,latest", "Slang language rules for 2026 and newer"}, }; static const NamesDescriptionValue s_compilerInfos[] = { @@ -223,9 +223,9 @@ static const NamesDescriptionValue s_fileSystemTypes[] = { return makeConstArrayView(s_languageInfos); } -/* static */ ConstArrayView<NamesDescriptionValue> TypeTextUtil::getStdRevisionInfos() +/* static */ ConstArrayView<NamesDescriptionValue> TypeTextUtil::getLanguageVersionInfos() { - return makeConstArrayView(s_stdRevisionInfos); + return makeConstArrayView(s_languageVersionInfos); } /* static */ ConstArrayView<NamesDescriptionValue> TypeTextUtil::getCompilerInfos() @@ -328,9 +328,12 @@ static const NamesDescriptionValue s_fileSystemTypes[] = { return NameValueUtil::findValue(getLanguageInfos(), text, SLANG_SOURCE_LANGUAGE_UNKNOWN); } -/* static */ SlangStdRevision TypeTextUtil::findStdRevision(const UnownedStringSlice& text) +/* static */ SlangLanguageVersion TypeTextUtil::findLanguageVersion(const UnownedStringSlice& text) { - return NameValueUtil::findValue(getStdRevisionInfos(), text, SLANG_STD_REVISION_UNKNOWN); + return NameValueUtil::findValue( + getLanguageVersionInfos(), + text, + SLANG_LANGUAGE_VERSION_UNKNOWN); } /* static */ SlangPassThrough TypeTextUtil::findPassThrough(const UnownedStringSlice& slice) diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h index bc136c3ab..eddbcec5e 100644 --- a/source/core/slang-type-text-util.h +++ b/source/core/slang-type-text-util.h @@ -33,8 +33,8 @@ struct TypeTextUtil /// Get the language infos static ConstArrayView<NamesDescriptionValue> getLanguageInfos(); - /// Get the std revision infos - static ConstArrayView<NamesDescriptionValue> getStdRevisionInfos(); + /// Get the language version infos + static ConstArrayView<NamesDescriptionValue> getLanguageVersionInfos(); /// Get the compiler infos static ConstArrayView<NamesDescriptionValue> getCompilerInfos(); /// Get the archive type infos @@ -73,8 +73,8 @@ 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 language version name returns a language revision. + static SlangLanguageVersion findLanguageVersion(const UnownedStringSlice& text); /// Given a name returns the pass through static SlangPassThrough findPassThrough(const UnownedStringSlice& slice); diff --git a/source/slang-record-replay/record/parameter-recorder.cpp b/source/slang-record-replay/record/parameter-recorder.cpp index fd6f75549..712a979b8 100644 --- a/source/slang-record-replay/record/parameter-recorder.cpp +++ b/source/slang-record-replay/record/parameter-recorder.cpp @@ -6,7 +6,7 @@ void ParameterRecorder::recordStruct(SlangGlobalSessionDesc const& desc) { recordUint32(desc.structureSize); recordUint32(desc.apiVersion); - recordUint32(desc.languageVersion); + recordUint32(desc.minLanguageVersion); recordUint32(desc.enableGLSL); } diff --git a/source/slang-record-replay/replay/json-consumer.cpp b/source/slang-record-replay/replay/json-consumer.cpp index c0486341e..ba7a13015 100644 --- a/source/slang-record-replay/replay/json-consumer.cpp +++ b/source/slang-record-replay/replay/json-consumer.cpp @@ -631,7 +631,7 @@ void JsonConsumer::_writeGlobalSessionDescHelper( { _writePair(builder, indent, "structureSize", (uint32_t)desc.structureSize); _writePair(builder, indent, "apiVersion", (uint32_t)desc.apiVersion); - _writePair(builder, indent, "languageVersion", (uint32_t)desc.languageVersion); + _writePair(builder, indent, "minLanguageVersion", (uint32_t)desc.minLanguageVersion); _writePair(builder, indent, "enablGLSL", (uint32_t)desc.enableGLSL); } } diff --git a/source/slang-record-replay/replay/parameter-decoder.cpp b/source/slang-record-replay/replay/parameter-decoder.cpp index afe4f716d..f986fbfbe 100644 --- a/source/slang-record-replay/replay/parameter-decoder.cpp +++ b/source/slang-record-replay/replay/parameter-decoder.cpp @@ -84,7 +84,7 @@ size_t ParameterDecoder::decodeStruct( SlangGlobalSessionDesc& desc = sessionDesc.getValue(); readByte = decodeUint32(buffer, bufferSize, desc.structureSize); readByte += decodeUint32(buffer + readByte, bufferSize - readByte, desc.apiVersion); - readByte += decodeUint32(buffer + readByte, bufferSize - readByte, desc.languageVersion); + readByte += decodeUint32(buffer + readByte, bufferSize - readByte, desc.minLanguageVersion); uint32_t val = 0; readByte += decodeUint32(buffer + readByte, bufferSize - readByte, val); desc.enableGLSL = (val != 0); diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h index d4ac71d83..e281081a7 100644 --- a/source/slang/slang-ast-decl.h +++ b/source/slang/slang-ast-decl.h @@ -525,15 +525,14 @@ class ModuleDecl : public NamespaceDeclBase /// FIDDLE() OrderedDictionary<Decl*, RefPtr<DeclAssociationList>> mapDeclToAssociatedDecls; - /// Whether the module is defined in legacy language. - /// The legacy Slang language does not have visibility modifiers and everything is treated as - /// `public`. Newer version of the language introduces visibility and makes `internal` as the - /// default. To prevent this from breaking existing code, we need to know whether a module is - /// written in the legacy language. We detect this by checking whether the module has any - /// visibility modifiers, or if the module uses new language constructs, e.g. `module`, - /// `__include`, - /// `__implementing` etc. - FIDDLE() bool isInLegacyLanguage = true; + /// Whether Slang language version the module is defined in. + /// The legacy Slang language (2025) does not have visibility modifiers and everything is + /// treated as `public`. Newer version of the language introduces visibility and makes + /// `internal` as the default. To prevent this from breaking existing code, we need to know + /// whether a module is written in the legacy language. We detect this by checking whether the + /// module has any visibility modifiers, or if the module uses new language constructs, e.g. + /// `module`, `__include`, `__implementing` etc. + FIDDLE() SlangLanguageVersion languageVersion = SLANG_LANGAUGE_VERSION_DEFAULT; FIDDLE() DeclVisibility defaultVisibility = DeclVisibility::Internal; diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h index ccbc625c9..950fd6645 100644 --- a/source/slang/slang-ast-expr.h +++ b/source/slang/slang-ast-expr.h @@ -575,6 +575,16 @@ class ParenExpr : public Expr Expr* base = nullptr; }; +// An expression that constructs a tuple `(arg1, arg2, ...)` +// +FIDDLE() +class TupleExpr : public Expr +{ + FIDDLE(...) + List<Expr*> elements; +}; + + // An object-oriented `this` expression, used to // refer to the current instance of an enclosing type. FIDDLE() diff --git a/source/slang/slang-ast-iterator.h b/source/slang/slang-ast-iterator.h index 047c33d1a..18a46b303 100644 --- a/source/slang/slang-ast-iterator.h +++ b/source/slang/slang-ast-iterator.h @@ -76,6 +76,13 @@ struct ASTIterator dispatchIfNotNull(expr->base); } + void visitTupleExpr(TupleExpr* expr) + { + iterator->maybeDispatchCallback(expr); + for (auto element : expr->elements) + dispatchIfNotNull(element); + } + void visitAssignExpr(AssignExpr* expr) { iterator->maybeDispatchCallback(expr); diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef index 28fa211e4..ad4c9cb44 100644 --- a/source/slang/slang-capabilities.capdef +++ b/source/slang/slang-capabilities.capdef @@ -718,7 +718,7 @@ def spvShaderInvocationReorderNV : SPV_NV_shader_invocation_reorder; /// [EXT] def spvRayTracingClusterAccelerationStructureNV : SPV_NV_cluster_acceleration_structure; -/// Represents the SPIR-V capability for cluster acceleration structure. +/// Represents the SPIR-V capability for linear swept spheres. /// [EXT] def spvRayTracingLinearSweptSpheresGeometryNV : SPV_NV_linear_swept_spheres; diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index f8a80c09b..71aa71e69 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -34,21 +34,18 @@ static bool isAssociatedTypeDecl(Decl* decl) return false; } -static bool isSlang2026(CompilerOptionSet& optionSet) +static bool isSlang2026OrLater(SemanticsVisitor* visitor) { - 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); + return visitor->getShared()->m_module->getModuleDecl()->languageVersion >= + SLANG_LANGUAGE_VERSION_2026; } -static bool allowExperimentalDynamicDispatch(CompilerOptionSet& optionSet) +static bool allowExperimentalDynamicDispatch( + SemanticsVisitor* visitor, + CompilerOptionSet& optionSet) { return optionSet.getBoolOption(CompilerOptionName::EnableExperimentalDynamicDispatch) || - !isSlang2026(optionSet); + !isSlang2026OrLater(visitor); } static void validateDynInterfaceUsage( @@ -57,7 +54,7 @@ static void validateDynInterfaceUsage( CompilerOptionSet& optionSet, InterfaceDecl* decl) { - if (allowExperimentalDynamicDispatch(optionSet)) + if (allowExperimentalDynamicDispatch(visitor, optionSet)) return; if (!decl->hasModifier<DynModifier>()) @@ -134,7 +131,7 @@ static void validateDynInterfaceUseWithInheritanceDecl( if (!interfaceDeclIsDyn) return; - if (!allowExperimentalDynamicDispatch(optionSet)) + if (!allowExperimentalDynamicDispatch(visitor, optionSet)) { if (auto extensionDeclParent = as<ExtensionDecl>(decl->parentDecl)) { @@ -665,6 +662,13 @@ struct SemanticsDeclReferenceVisitor : public SemanticsDeclVisitorBase, void visitParenExpr(ParenExpr* expr) { dispatchIfNotNull(expr->base); } + void visitTupleExpr(TupleExpr* expr) + { + for (auto element : expr->elements) + dispatchIfNotNull(element); + } + + void visitAssignExpr(AssignExpr* expr) { dispatchIfNotNull(expr->left); @@ -13817,7 +13821,7 @@ void SemanticsDeclCapabilityVisitor::visitInheritanceDecl(InheritanceDecl* inher if (!implDecl) continue; - if (getModuleDecl(implDecl.getDecl())->isInLegacyLanguage) + if (getModuleDecl(implDecl.getDecl())->languageVersion == SLANG_LANGUAGE_VERSION_LEGACY) break; ensureDecl(requirementDecl, DeclCheckState::CapabilityChecked); @@ -13880,8 +13884,9 @@ DeclVisibility getDeclVisibility(Decl* decl) auto defaultVis = DeclVisibility::Default; if (auto parentModule = getModuleDecl(decl)) { - defaultVis = parentModule->isInLegacyLanguage ? DeclVisibility::Public - : parentModule->defaultVisibility; + defaultVis = parentModule->languageVersion == SLANG_LANGUAGE_VERSION_LEGACY + ? DeclVisibility::Public + : parentModule->defaultVisibility; } // Members of other agg type decls will have their default visibility capped to the diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index 0c0b1ec9d..205575a81 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -2484,6 +2484,30 @@ Expr* SemanticsExprVisitor::visitParenExpr(ParenExpr* expr) return expr; } +Expr* SemanticsExprVisitor::visitTupleExpr(TupleExpr* expr) +{ + List<Type*> elementTypes; + for (auto& element : expr->elements) + { + element = CheckTerm(element); + auto elementType = element->type.type; + if (auto concreteTypePack = as<ConcreteTypePack>(elementType)) + { + // We need to flatten the type pack into a tuple type + for (Index i = 0; i < concreteTypePack->getTypeCount(); i++) + { + elementTypes.add(concreteTypePack->getElementType(i)); + } + } + else + { + elementTypes.add(element->type.type); + } + } + expr->type = m_astBuilder->getTupleType(elementTypes.getArrayView()); + return expr; +} + void SemanticsVisitor::maybeDiagnoseThisNotLValue(Expr* expr) { // We will try to handle expressions of the form: diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 950a150c4..80436e68a 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -2920,6 +2920,8 @@ public: Expr* visitParenExpr(ParenExpr* expr); + Expr* visitTupleExpr(TupleExpr* expr); + Expr* visitAssignExpr(AssignExpr* expr); Expr* visitGenericAppExpr(GenericAppExpr* genericAppExpr); diff --git a/source/slang/slang-compiler-options.cpp b/source/slang/slang-compiler-options.cpp index 0d7f19642..5c17121cc 100644 --- a/source/slang/slang-compiler-options.cpp +++ b/source/slang/slang-compiler-options.cpp @@ -208,8 +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); + case CompilerOptionName::LanguageVersion: + return CompilerOptionValue::fromEnum(SLANG_LANGAUGE_VERSION_DEFAULT); default: return CompilerOptionValue(); } diff --git a/source/slang/slang-compiler-options.h b/source/slang/slang-compiler-options.h index 9de2a2e80..dd413bee4 100644 --- a/source/slang/slang-compiler-options.h +++ b/source/slang/slang-compiler-options.h @@ -385,6 +385,15 @@ struct CompilerOptionSet return getEnumOption<DebugInfoLevel>(CompilerOptionName::DebugInformation); } + SlangLanguageVersion getLanguageVersion() + { + if (!hasOption(CompilerOptionName::LanguageVersion)) + { + return SLANG_LANGAUGE_VERSION_DEFAULT; + } + return (SlangLanguageVersion)getIntOption(CompilerOptionName::LanguageVersion); + } + List<String> getDownstreamArgs(String downstreamToolName); void serialize(SerializedOptionsData* outData); diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index d6659e707..86dcf5d7f 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -1175,6 +1175,46 @@ SlangResult passthroughDownstreamDiagnostics( return SLANG_OK; } +bool isValidSlangLanguageVersion(SlangLanguageVersion version) +{ + switch (version) + { + case SLANG_LANGUAGE_VERSION_LEGACY: + case SLANG_LANGUAGE_VERSION_2025: + case SLANG_LANGUAGE_VERSION_2026: + return true; + default: + return false; + } +} + +bool isValidGLSLVersion(int version) +{ + switch (version) + { + case 100: + case 110: + case 120: + case 130: + case 140: + case 150: + case 300: + case 310: + case 320: + case 330: + case 400: + case 410: + case 420: + case 430: + case 440: + case 450: + case 460: + return true; + default: + return false; + } +} + SlangResult CodeGenContext::emitWithDownstreamForEntryPoints(ComPtr<IArtifact>& outArtifact) { outArtifact.setNull(); diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 0fc8e69dd..e06278599 100644 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -3887,6 +3887,9 @@ bool maybeDiagnoseWarningOrError( } } +bool isValidSlangLanguageVersion(SlangLanguageVersion version); +bool isValidGLSLVersion(int version); + } // namespace Slang #endif diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 0814bb2a0..fc7a4d5bb 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -147,8 +147,6 @@ DIAGNOSTIC( "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'") @@ -371,7 +369,13 @@ DIAGNOSTIC( undefinedIdentifierInPreprocessorExpression, "undefined identifier '$0' in preprocessor expression will evaluate to zero") DIAGNOSTIC(15206, Error, expectedIntegralVersionNumber, "Expected integer for #version number") - +DIAGNOSTIC(15207, Error, unknownLanguageVersion, "unknown language version '$0'.") +DIAGNOSTIC(15208, Error, unknownLanguage, "unknown language '$0'.") +DIAGNOSTIC( + 15208, + Error, + languageVersionDiffersFromIncludingModule, + "the source file declares a different language version than the including module.") DIAGNOSTIC(-1, Note, seeOpeningToken, "see opening '$0'") // 153xx - #include diff --git a/source/slang/slang-language-server-ast-lookup.cpp b/source/slang/slang-language-server-ast-lookup.cpp index d0ed9cdac..53a9f81b9 100644 --- a/source/slang/slang-language-server-ast-lookup.cpp +++ b/source/slang/slang-language-server-ast-lookup.cpp @@ -148,6 +148,14 @@ public: bool visitParenExpr(ParenExpr* expr) { return dispatchIfNotNull(expr->base); } + bool visitTupleExpr(TupleExpr* expr) + { + for (auto element : expr->elements) + if (dispatchIfNotNull(element)) + return true; + return false; + } + bool visitBuiltinCastExpr(BuiltinCastExpr* expr) { return dispatchIfNotNull(expr->base); } bool visitAssignExpr(AssignExpr* expr) diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index ebcdaf1e1..758b21e93 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -4668,6 +4668,29 @@ struct ExprLoweringVisitorBase : public ExprVisitor<Derived, LoweredValInfo> LoweredValInfo visitParenExpr(ParenExpr* expr) { return lowerSubExpr(expr->base); } + LoweredValInfo visitTupleExpr(TupleExpr* expr) + { + List<IRInst*> elements; + for (auto element : expr->elements) + { + auto elementVal = getSimpleVal(context, lowerSubExpr(element)); + if (auto makeValPack = as<IRMakeValuePack>(elementVal)) + { + // If the element is a value pack, we need to flatten it out + // into the tuple. + for (UInt i = 0; i < makeValPack->getOperandCount(); ++i) + { + elements.add(makeValPack->getOperand(i)); + } + continue; + } + elements.add(elementVal); + } + auto irMakeTuple = + getBuilder()->emitMakeTuple((UInt)elements.getCount(), elements.getBuffer()); + return LoweredValInfo::simple(irMakeTuple); + } + LoweredValInfo visitPackExpr(PackExpr* expr) { List<IRInst*> irArgs; diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 0182b4c22..3ce107135 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -63,7 +63,7 @@ enum class ValueCategory FileSystemType, VulkanShift, SourceEmbedStyle, - StdRevision, + LanguageVersion, CountOf, }; @@ -150,10 +150,10 @@ void initCommandOptions(CommandOptions& options) options.addCategory( CategoryKind::Value, - "std-revision", - "Std Revision", - UserValue(ValueCategory::StdRevision)); - options.addValues(TypeTextUtil::getStdRevisionInfos()); + "language-version", + "Language Version", + UserValue(ValueCategory::LanguageVersion)); + options.addValues(TypeTextUtil::getLanguageVersionInfos()); options.addCategory( @@ -455,9 +455,9 @@ 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, + {OptionKind::LanguageVersion, "-std", - "-std <std-revision>", + "-std <language-version>", "Specifies the language standard that should be used."}, {OptionKind::WarningsAsErrors, "-warnings-as-errors", @@ -2555,21 +2555,21 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv) } break; } - case OptionKind::StdRevision: + case OptionKind::LanguageVersion: { CommandLineArg name; SLANG_RETURN_ON_FAIL(m_reader.expectArg(name)); - SlangStdRevision stdRevision = - TypeTextUtil::findStdRevision(name.value.getUnownedSlice()); - if (stdRevision == SLANG_STD_REVISION_UNKNOWN) + SlangLanguageVersion stdRevision = + TypeTextUtil::findLanguageVersion(name.value.getUnownedSlice()); + if (stdRevision == SLANG_LANGUAGE_VERSION_UNKNOWN) { - m_sink->diagnose(name.loc, Diagnostics::unknownStdRevision, name.value); + m_sink->diagnose(name.loc, Diagnostics::unknownLanguageVersion, name.value); return SLANG_FAIL; } else { - linkage->m_optionSet.add(OptionKind::StdRevision, stdRevision); + linkage->m_optionSet.add(OptionKind::LanguageVersion, stdRevision); } break; } diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 565cb2e5f..9eb8c1391 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -1185,6 +1185,14 @@ bool tryParseUsingSyntaxDecl( return tryParseUsingSyntaxDeclImpl<T>(parser, syntaxDecl, outSyntax); } +static void maybeUpgradeLanguageVersionFromLegacy(SlangLanguageVersion& ver) +{ + if (ver == SLANG_LANGUAGE_VERSION_LEGACY) + { + ver = SLANG_LANGUAGE_VERSION_2025; + } +} + static Modifiers ParseModifiers(Parser* parser, LookupMask modifierLookupMask = LookupMask::Default) { Modifiers modifiers; @@ -1216,7 +1224,7 @@ static Modifiers ParseModifiers(Parser* parser, LookupMask modifierLookupMask = if (as<VisibilityModifier>(parsedModifier)) { if (auto currentModule = parser->getCurrentModuleDecl()) - currentModule->isInLegacyLanguage = false; + maybeUpgradeLanguageVersionFromLegacy(currentModule->languageVersion); } AddModifier(&modifierLink, parsedModifier); continue; @@ -1321,7 +1329,7 @@ static NodeBase* parseIncludeDecl(Parser* parser, void* /*userData*/) auto decl = parser->astBuilder->create<IncludeDecl>(); parseFileReferenceDeclBase(parser, decl); if (auto currentModule = parser->getCurrentModuleDecl()) - currentModule->isInLegacyLanguage = false; + maybeUpgradeLanguageVersionFromLegacy(currentModule->languageVersion); return decl; } @@ -1361,7 +1369,7 @@ static NodeBase* parseModuleDeclarationDecl(Parser* parser, void* /*userData*/) } parser->ReadToken(TokenType::Semicolon); if (auto currentModule = parser->getCurrentModuleDecl()) - currentModule->isInLegacyLanguage = false; + maybeUpgradeLanguageVersionFromLegacy(currentModule->languageVersion); return decl; } @@ -5153,7 +5161,20 @@ void Parser::parseSourceFile(ContainerDecl* program) currentModule = getModuleDecl(program); - // If the program already has a scope, then reuse it instead of overwriting it! + // Verify that the language version is valid. + if (sourceLanguage == SourceLanguage::Slang) + { + if (!isValidSlangLanguageVersion(currentModule->languageVersion)) + { + sink->diagnose( + program->loc, + Diagnostics::unknownLanguageVersion, + currentModule->languageVersion); + } + } + + // If the program already has a scope, then + // reuse it instead of overwriting it! if (program->ownedScope) PushScope(program->ownedScope); else @@ -5607,7 +5628,7 @@ static Decl* _tryResolveDecl(Parser* parser, Expr* expr) auto lookupResult = lookUpDirectAndTransparentMembers( parser->astBuilder, - nullptr, // no semantics visitor available yet + parser->semanticsVisitor, staticMemberExpr->name, aggTypeDecl, declRef); @@ -5627,7 +5648,7 @@ static Decl* _tryResolveDecl(Parser* parser, Expr* expr) // Do the lookup in the current scope auto lookupResult = lookUp( parser->astBuilder, - nullptr, // no semantics visitor available yet + parser->semanticsVisitor, varExpr->name, parser->currentScope); if (!lookupResult.isValid() || lookupResult.isOverloaded()) @@ -5641,11 +5662,8 @@ static Decl* _tryResolveDecl(Parser* parser, Expr* expr) static bool isTypeName(Parser* parser, Name* name) { - auto lookupResult = lookUp( - parser->astBuilder, - nullptr, // no semantics visitor available yet - name, - parser->currentScope); + auto lookupResult = + lookUp(parser->astBuilder, parser->semanticsVisitor, name, parser->currentScope); if (!lookupResult.isValid() || lookupResult.isOverloaded()) return false; @@ -7201,17 +7219,23 @@ static bool _isCast(Parser* parser, Expr* expr) return false; } -static bool tryParseExpression(Parser* parser, Expr*& outExpr, TokenType tokenTypeAfter) +template<typename FIsValidFollowToken> +static bool tryParseExpression( + Parser* parser, + Expr*& outExpr, + Precedence exprLevel, + const FIsValidFollowToken& isValidFollowToken) { auto cursor = parser->tokenReader.getCursor(); auto isRecovering = parser->isRecovering; auto oldSink = parser->sink; DiagnosticSink newSink(parser->sink->getSourceManager(), nullptr); parser->sink = &newSink; - outExpr = parser->ParseExpression(); + outExpr = parser->ParseExpression(exprLevel); parser->sink = oldSink; parser->isRecovering = isRecovering; - if (outExpr && newSink.getErrorCount() == 0 && parser->LookAheadToken(tokenTypeAfter)) + if (outExpr && newSink.getErrorCount() == 0 && + isValidFollowToken(parser->tokenReader.peekTokenType())) { return true; } @@ -7297,9 +7321,7 @@ static Expr* parseAtomicExpr(Parser* parser) tcexpr->loc = openParen.loc; tcexpr->functionExpr = varExpr; - - auto arg = parsePrefixExpr(parser); - tcexpr->arguments.add(arg); + tcexpr->arguments.add(parsePrefixExpr(parser)); return tcexpr; } @@ -7313,45 +7335,99 @@ static Expr* parseAtomicExpr(Parser* parser) Expr* base = nullptr; if (parser->LookAheadToken(TokenType::RParent)) { - // We don't support empty parentheses `()` as a valid expression. - parser->diagnose(openParen, Diagnostics::invalidEmptyParenthesisExpr); - base = parser->astBuilder->create<IncompleteExpr>(); - base->type = parser->astBuilder->getErrorType(); + if (parser->currentModule->languageVersion >= SLANG_LANGUAGE_VERSION_2026) + { + // We support empty parentheses `()` as a valid expression to construct an + // empty tuple in Slang 2026. + base = parser->astBuilder->create<TupleExpr>(); + base->loc = openParen.loc; + } + else + { + // We don't support empty parentheses `()` as a valid expression prior to + // Slang 2026. + parser->diagnose(openParen, Diagnostics::invalidEmptyParenthesisExpr); + base = parser->astBuilder->create<IncompleteExpr>(); + base->type = parser->astBuilder->getErrorType(); + } } else { - if (!tryParseExpression(parser, base, TokenType::RParent)) + // When language version is 2026 or later, we no longer parse comma as a + // C/C++-style comma operator when it is inside a paranthesis, + // but rather as a tuple element separator. + // + Precedence exprLevel = Precedence::Comma; + if (parser->sourceLanguage == SourceLanguage::Slang && + parser->currentModule->languageVersion >= SLANG_LANGUAGE_VERSION_2026) + { + // Setting exprLevel to Assignment here will allow the following + // tryParseExpression call to parse the expression until the next `)` or + // `,`, instead of consuming any ',' as a comma operator. + exprLevel = Precedence::Assignment; + } + auto isValidFollowToken = [=](TokenType tokenType) + { + if (tokenType == TokenType::RParent) + return true; + if (exprLevel == Precedence::Assignment && tokenType == TokenType::Comma) + return true; + return false; + }; + if (!tryParseExpression(parser, base, exprLevel, isValidFollowToken)) { base = parser->ParseType(); } } - parser->ReadToken(TokenType::RParent); - - // We now try and determine by what base is, if this is actually a cast or an - // expression in parentheses - if (_isCast(parser, base)) + if (parser->LookAheadToken(TokenType::Comma)) { - // Parse as a cast - - TypeCastExpr* tcexpr = parser->astBuilder->create<ExplicitCastExpr>(); - tcexpr->loc = openParen.loc; - - tcexpr->functionExpr = base; - - auto arg = parsePrefixExpr(parser); - tcexpr->arguments.add(arg); - - return tcexpr; + // We have a comma, so we are not done yet. + // If we reach here, the language version must be 2026 or later, + // where we allow (a,b,c,...) syntax as a tuple, otherwise we would have + // parsed it into `base`. + // + List<Expr*> elementExprs; + elementExprs.add(base); + while (AdvanceIf(parser, TokenType::Comma)) + { + if (parser->LookAheadToken(TokenType::RParent)) + break; + auto elementExpr = parser->ParseArgExpr(); + elementExprs.add(elementExpr); + } + parser->ReadToken(TokenType::RParent); + TupleExpr* tupleExpr = parser->astBuilder->create<TupleExpr>(); + tupleExpr->loc = openParen.loc; + tupleExpr->elements = _Move(elementExprs); + return tupleExpr; } else { - // Pass as an expression in parentheses + // We parsed an expression in the form of (expr), without + // any commas in `expr`. + // We now try and determine by what base is, if this is actually a cast or an + // expression in parentheses. + // + parser->ReadToken(TokenType::RParent); + if (_isCast(parser, base)) + { + // Parse as a cast + TypeCastExpr* tcexpr = parser->astBuilder->create<ExplicitCastExpr>(); + tcexpr->loc = openParen.loc; + + tcexpr->functionExpr = base; + tcexpr->arguments.add(parsePrefixExpr(parser)); - ParenExpr* parenExpr = parser->astBuilder->create<ParenExpr>(); - parenExpr->loc = openParen.loc; - parenExpr->base = base; - return parenExpr; + return tcexpr; + } + else + { + ParenExpr* parenExpr = parser->astBuilder->create<ParenExpr>(); + parenExpr->loc = openParen.loc; + parenExpr->base = base; + return parenExpr; + } } } } @@ -8519,7 +8595,7 @@ void parseSourceFile( Parser parser(astBuilder, tokens, sink, outerScope, options); parser.namePool = translationUnit->getNamePool(); - parser.sourceLanguage = translationUnit->sourceLanguage; + parser.sourceLanguage = sourceLanguage; return parser.parseSourceFile(parentDecl); } diff --git a/source/slang/slang-preprocessor.cpp b/source/slang/slang-preprocessor.cpp index 3f120d5eb..b29fa2716 100644 --- a/source/slang/slang-preprocessor.cpp +++ b/source/slang/slang-preprocessor.cpp @@ -1305,6 +1305,8 @@ struct Preprocessor /// Detected source language. SourceLanguage language = SourceLanguage::Unknown; + SlangLanguageVersion languageVersion = SLANG_LANGUAGE_VERSION_UNKNOWN; + /// Stores macro definition and invocation info for language server. PreprocessorContentAssistInfo* contentAssistInfo = nullptr; @@ -4360,7 +4362,7 @@ static void HandleExtensionDirective(PreprocessorDirectiveContext* context) static void HandleVersionDirective(PreprocessorDirectiveContext* context) { - [[maybe_unused]] int version; + int version = SLANG_LANGUAGE_VERSION_UNKNOWN; switch (PeekTokenType(context)) { case TokenType::IntegerLiteral: @@ -4374,8 +4376,78 @@ static void HandleVersionDirective(PreprocessorDirectiveContext* context) } SkipToEndOfLine(context); - context->m_preprocessor->language = SourceLanguage::GLSL; - // TODO, just skip the version for now + + if (isValidGLSLVersion(version)) + { + context->m_preprocessor->language = SourceLanguage::GLSL; + } + else + { + GetSink(context)->diagnose( + GetDirectiveLoc(context), + Diagnostics::unknownLanguageVersion, + version); + } + context->m_preprocessor->languageVersion = (SlangLanguageVersion)version; +} + +static void HandleLanguageDirective(PreprocessorDirectiveContext* context) +{ + int version = SLANG_LANGUAGE_VERSION_UNKNOWN; + switch (PeekTokenType(context)) + { + case TokenType::IntegerLiteral: + version = stringToInt(AdvanceToken(context).getContent()); + break; + case TokenType::Identifier: + { + auto token = AdvanceToken(context); + if (token.getContent().caseInsensitiveEquals(toSlice("slang"))) + { + context->m_preprocessor->language = SourceLanguage::Slang; + token = AdvanceToken(context); + } + else if (token.getContent() == "glsl") + { + context->m_preprocessor->language = SourceLanguage::GLSL; + token = AdvanceToken(context); + } + if (token.getContent() == "latest") + version = SLANG_LANGUAGE_VERSION_LATEST; + else if (token.getContent() == "legacy") + version = SLANG_LANGUAGE_VERSION_LEGACY; + else if (token.type == TokenType::IntegerLiteral) + version = stringToInt(token.getContent()); + else + { + GetSink(context)->diagnose( + GetDirectiveLoc(context), + Diagnostics::unknownLanguage, + token); + } + } + break; + default: + GetSink(context)->diagnose( + GetDirectiveLoc(context), + Diagnostics::expectedIntegralVersionNumber); + break; + } + + SkipToEndOfLine(context); + + if (isValidSlangLanguageVersion((SlangLanguageVersion)version)) + { + context->m_preprocessor->language = SourceLanguage::Slang; + } + else + { + GetSink(context)->diagnose( + GetDirectiveLoc(context), + Diagnostics::unknownLanguageVersion, + version); + } + context->m_preprocessor->languageVersion = (SlangLanguageVersion)version; } // Handle an invalid directive @@ -4434,6 +4506,9 @@ static const PreprocessorDirective kDirectives[] = { {"line", &HandleLineDirective, 0}, {"pragma", &HandlePragmaDirective, 0}, + {"language", &HandleLanguageDirective, 0}, + {"lang", &HandleLanguageDirective, 0}, + // GLSL {"version", &HandleVersionDirective, 0}, {"extension", &HandleExtensionDirective, 0}, @@ -4783,6 +4858,7 @@ TokenList preprocessSource( Dictionary<String, String> const& defines, Linkage* linkage, SourceLanguage& outDetectedLanguage, + SlangLanguageVersion& outLanguageVersion, PreprocessorHandler* handler) { PreprocessorDesc desc; @@ -4806,13 +4882,14 @@ TokenList preprocessSource( new preprocessor::WarningStateTracker(desc.sourceManager); desc.sink->setSourceWarningStateTracker(wst); - return preprocessSource(file, desc, outDetectedLanguage); + return preprocessSource(file, desc, outDetectedLanguage, outLanguageVersion); } TokenList preprocessSource( SourceFile* file, PreprocessorDesc const& desc, - SourceLanguage& outDetectedLanguage) + SourceLanguage& outDetectedLanguage, + SlangLanguageVersion& outLanguageVersion) { using namespace preprocessor; @@ -4908,7 +4985,8 @@ TokenList preprocessSource( #endif outDetectedLanguage = preprocessor.language; - + if (preprocessor.languageVersion != SLANG_LANGUAGE_VERSION_UNKNOWN) + outLanguageVersion = preprocessor.languageVersion; return tokens; } diff --git a/source/slang/slang-preprocessor.h b/source/slang/slang-preprocessor.h index bde79b649..587bfb594 100644 --- a/source/slang/slang-preprocessor.h +++ b/source/slang/slang-preprocessor.h @@ -65,7 +65,8 @@ struct PreprocessorDesc TokenList preprocessSource( SourceFile* file, PreprocessorDesc const& desc, - SourceLanguage& outDetectedLanguage); + SourceLanguage& outDetectedLanguage, + SlangLanguageVersion& outLanguageVersion); /// Convenience wrapper for `preprocessSource` when a `Linkage` is available TokenList preprocessSource( @@ -75,6 +76,7 @@ TokenList preprocessSource( Dictionary<String, String> const& defines, Linkage* linkage, SourceLanguage& outDetectedLanguage, + SlangLanguageVersion& outLanguageVersion, PreprocessorHandler* handler = nullptr); // The following functions are intended to be used inside of implementations diff --git a/source/slang/slang-serialize-ast.cpp b/source/slang/slang-serialize-ast.cpp index 9a61dbc5a..3a2d3818e 100644 --- a/source/slang/slang-serialize-ast.cpp +++ b/source/slang/slang-serialize-ast.cpp @@ -99,6 +99,11 @@ void serialize(Serializer const& serializer, SPIRVAsmOperand::Flavor& value) serializeEnum(serializer, value); } +void serialize(Serializer const& serializer, SlangLanguageVersion version) +{ + serializeEnum(serializer, version); +} + void serialize(Serializer const& serializer, MatrixCoord& value) { SLANG_SCOPED_SERIALIZER_TUPLE(serializer); diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index 5f2cf4ddf..6ac3ba079 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -1771,11 +1771,11 @@ LayoutRulesImpl* CPULayoutRulesFamilyImpl::getVaryingOutputRules() } LayoutRulesImpl* CPULayoutRulesFamilyImpl::getSpecializationConstantRules() { - return nullptr; + return &kCPULayoutRulesImpl_; } LayoutRulesImpl* CPULayoutRulesFamilyImpl::getShaderStorageBufferRules(CompilerOptionSet&) { - return nullptr; + return &kCPULayoutRulesImpl_; } LayoutRulesImpl* CPULayoutRulesFamilyImpl::getParameterBlockRules(CompilerOptionSet&) { @@ -1829,19 +1829,19 @@ LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getTextureBufferRules(CompilerOption LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getVaryingInputRules() { - return nullptr; + return &kCUDALayoutRulesImpl_; } LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getVaryingOutputRules() { - return nullptr; + return &kCUDALayoutRulesImpl_; } LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getSpecializationConstantRules() { - return nullptr; + return &kCUDALayoutRulesImpl_; } LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getShaderStorageBufferRules(CompilerOptionSet&) { - return nullptr; + return &kCUDALayoutRulesImpl_; } LayoutRulesImpl* CUDALayoutRulesFamilyImpl::getParameterBlockRules(CompilerOptionSet&) { diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 7fc519c03..bda847815 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -2730,7 +2730,8 @@ Expr* Linkage::parseTermString(String typeStr, Scope* scope) // We need to temporarily replace the SourceManager for this CompileRequest ScopeReplaceSourceManager scopeReplaceSourceManager(this, &localSourceManager); - SourceLanguage sourceLanguage; + SourceLanguage sourceLanguage = SourceLanguage::Slang; + SlangLanguageVersion languageVersion = m_optionSet.getLanguageVersion(); auto tokens = preprocessSource( srcFile, @@ -2738,7 +2739,8 @@ Expr* Linkage::parseTermString(String typeStr, Scope* scope) nullptr, Dictionary<String, String>(), this, - sourceLanguage); + sourceLanguage, + languageVersion); if (sourceLanguage == SourceLanguage::Unknown) sourceLanguage = SourceLanguage::Slang; @@ -3411,7 +3413,9 @@ void FrontEndCompileRequest::parseTranslationUnit(TranslationUnitRequest* transl for (auto sourceFile : translationUnit->getSourceFiles()) { - SourceLanguage sourceLanguage = SourceLanguage::Unknown; + SourceLanguage sourceLanguage = translationUnit->sourceLanguage; + SlangLanguageVersion languageVersion = + translationUnit->compileRequest->optionSet.getLanguageVersion(); auto tokens = preprocessSource( sourceFile, getSink(), @@ -3419,8 +3423,11 @@ void FrontEndCompileRequest::parseTranslationUnit(TranslationUnitRequest* transl combinedPreprocessorDefinitions, getLinkage(), sourceLanguage, + languageVersion, &preprocessorHandler); + translationUnitSyntax->languageVersion = languageVersion; + if (sourceLanguage == SourceLanguage::Unknown) sourceLanguage = translationUnit->sourceLanguage; @@ -4915,11 +4922,13 @@ Linkage::IncludeResult Linkage::findAndIncludeFile( // Create a transparent FileDecl to hold all children from the included file. auto fileDecl = module->getASTBuilder()->create<FileDecl>(); fileDecl->nameAndLoc.name = name; + fileDecl->parentDecl = module->getModuleDecl(); module->getIncludedSourceFileMap().add(sourceFile, fileDecl); FrontEndPreprocessorHandler preprocessorHandler(module, module->getASTBuilder(), sink); auto combinedPreprocessorDefinitions = translationUnit->getCombinedPreprocessorDefinitions(); - SourceLanguage sourceLanguage = SourceLanguage::Unknown; + SourceLanguage sourceLanguage = translationUnit->sourceLanguage; + SlangLanguageVersion slangLanguageVersion = module->getModuleDecl()->languageVersion; auto tokens = preprocessSource( sourceFile, sink, @@ -4927,11 +4936,19 @@ Linkage::IncludeResult Linkage::findAndIncludeFile( combinedPreprocessorDefinitions, this, sourceLanguage, + slangLanguageVersion, &preprocessorHandler); if (sourceLanguage == SourceLanguage::Unknown) sourceLanguage = translationUnit->sourceLanguage; + if (slangLanguageVersion != module->getModuleDecl()->languageVersion) + { + sink->diagnose( + tokens.begin()->getLoc(), + Diagnostics::languageVersionDiffersFromIncludingModule); + } + auto outerScope = module->getModuleDecl()->ownedScope; parseSourceFile( module->getASTBuilder(), diff --git a/tests/language-feature/lang-version.slang b/tests/language-feature/lang-version.slang new file mode 100644 index 000000000..226c04514 --- /dev/null +++ b/tests/language-feature/lang-version.slang @@ -0,0 +1,51 @@ + +//TEST:SIMPLE(filecheck=CHECK_2026): -target spirv -std 2026 +//TEST:SIMPLE(filecheck=CHECK_2026): -target spirv -DVER_2026 +//TEST:SIMPLE(filecheck=CHECK_2026): -target spirv -DVER_2026_2 +//TEST:SIMPLE(filecheck=CHECK_2026): -target spirv -DVER_LATEST1 +//TEST:SIMPLE(filecheck=CHECK_2026): -target spirv -DVER_LATEST2 +//TEST:SIMPLE(filecheck=CHECK_LEGACY): -target spirv +//TEST:SIMPLE(filecheck=ILLEGAL_SLANG_VER): -target spirv -DVER_ILLEGAL +//TEST:SIMPLE(filecheck=ILLEGAL_SLANG_VER2): -target spirv -DVER_ILLEGAL2 +//TEST:SIMPLE(filecheck=ILLEGAL_LANG): -target spirv -DLANG_ILLEGAL + +#ifdef VER_2026 +#language slang 2026 +#endif + +#ifdef VER_2026_2 +#language 2026 +#endif + +#ifdef VER_LATEST1 +#lang slang latest +#endif + +#ifdef VER_LATEST2 +#lang latest +#endif + +#ifdef VER_ILLEGAL +// ILLEGAL_SLANG_VER: ([[# @LINE+1]]): error 15207 +#lang slang illegal +#endif + +#ifdef VER_ILLEGAL2 +// ILLEGAL_SLANG_VER2: ([[# @LINE+1]]): error 15207 +#lang 9987 +#endif + +#ifdef LANG_ILLEGAL +// ILLEGAL_LANG: ([[# @LINE+1]]): error 15207 +#lang illegal +#endif + +// CHECK_2026: OpEntryPoint + +[numthreads(1,1,1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + // CHECK_LEGACY: ([[# @LINE+1]]): error 30027 + let x = (1,2,3)._1_2; + +}
\ No newline at end of file diff --git a/tests/language-feature/tuple/tuple-syntax.slang b/tests/language-feature/tuple/tuple-syntax.slang new file mode 100644 index 000000000..1d0a42b40 --- /dev/null +++ b/tests/language-feature/tuple/tuple-syntax.slang @@ -0,0 +1,42 @@ +// Test tuple construction syntax. + +#language 2026 + +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-shaderobj +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-dx12 -use-dxil -shaderobj +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -shaderobj +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-vk -shaderobj + +//TEST_INPUT:ubuffer(data=[1 2 3 4], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +void store(inout Tuple<int, float, uint> t) +{ + t._1_2 = (3.0, 4u); +} + +struct MyType +{ + int x; + int y; + __init(Tuple<int, int> t){ + x = t._0; + y = t._1; + } +} + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + var t = (1, 2.0, 3u); + store(t); + let y = t._1_2_0; + + // CHECK: 4 + outputBuffer[0] = y._1; + + // This should mean cast<MyType>(makeTuple(1,2)) + let m = (MyType)(1,2); + // CHECK: 3 + outputBuffer[1] = m.x + m.y; +} |
