diff options
Diffstat (limited to 'source')
26 files changed, 434 insertions, 119 deletions
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(), |
