diff options
| author | Yong He <yonghe@outlook.com> | 2023-12-05 10:06:19 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-05 10:06:19 -0800 |
| commit | 1050e0eb96d6c8e7a6cfb253458155e1014625c3 (patch) | |
| tree | ce6c3cbde591759fa2fe2aeb05cb132e50a9a419 | |
| parent | 4fb3b10b81cf8c976ebd1ebb7fcde7708f022957 (diff) | |
Support `include` for pulling file into the current module. (#3377)
* Support `include` for pulling file into the current module.
* Add auto-completion, hover info and goto-def support.
* Disable warning for missing `module` declaration for now.
---------
Co-authored-by: Yong He <yhe@nvidia.com>
24 files changed, 701 insertions, 117 deletions
diff --git a/source/compiler-core/slang-include-system.cpp b/source/compiler-core/slang-include-system.cpp index f0a850a81..1b768d506 100644 --- a/source/compiler-core/slang-include-system.cpp +++ b/source/compiler-core/slang-include-system.cpp @@ -113,15 +113,15 @@ SlangResult IncludeSystem::findFile(String const& pathToInclude, String const& p return SLANG_E_NOT_FOUND; } -SlangResult IncludeSystem::loadFile(const PathInfo& pathInfo, ComPtr<ISlangBlob>& outBlob) +SlangResult IncludeSystem::loadFile(const PathInfo& pathInfo, ComPtr<ISlangBlob>& outBlob, SourceFile*& outSourceFile) { if (m_sourceManager) { // See if this an already loaded source file - SourceFile* sourceFile = m_sourceManager->findSourceFileRecursively(pathInfo.uniqueIdentity); + outSourceFile = m_sourceManager->findSourceFileRecursively(pathInfo.uniqueIdentity); // If not create a new one, and add to the list of known source files - if (!sourceFile) + if (!outSourceFile) { ComPtr<ISlangBlob> foundSourceBlob; if (SLANG_FAILED(m_fileSystemExt->loadFile(pathInfo.foundPath.getBuffer(), foundSourceBlob.writeRef()))) @@ -129,17 +129,17 @@ SlangResult IncludeSystem::loadFile(const PathInfo& pathInfo, ComPtr<ISlangBlob> return SLANG_E_CANNOT_OPEN; } - sourceFile = m_sourceManager->createSourceFileWithBlob(pathInfo, foundSourceBlob); - m_sourceManager->addSourceFile(pathInfo.uniqueIdentity, sourceFile); + outSourceFile = m_sourceManager->createSourceFileWithBlob(pathInfo, foundSourceBlob); + m_sourceManager->addSourceFile(pathInfo.uniqueIdentity, outSourceFile); outBlob = foundSourceBlob; return SLANG_OK; } else { - if (sourceFile->getContentBlob()) + if (outSourceFile->getContentBlob()) { - outBlob = sourceFile->getContentBlob(); + outBlob = outSourceFile->getContentBlob(); return SLANG_OK; } @@ -149,7 +149,7 @@ SlangResult IncludeSystem::loadFile(const PathInfo& pathInfo, ComPtr<ISlangBlob> return SLANG_E_CANNOT_OPEN; } - sourceFile->setContents(foundSourceBlob); + outSourceFile->setContents(foundSourceBlob); outBlob = foundSourceBlob; return SLANG_OK; @@ -158,6 +158,7 @@ SlangResult IncludeSystem::loadFile(const PathInfo& pathInfo, ComPtr<ISlangBlob> else { // If we don't have the source manager, just load + outSourceFile = nullptr; return m_fileSystemExt->loadFile(pathInfo.foundPath.getBuffer(), outBlob.writeRef()); } } diff --git a/source/compiler-core/slang-include-system.h b/source/compiler-core/slang-include-system.h index 108c4901b..cc89985dd 100644 --- a/source/compiler-core/slang-include-system.h +++ b/source/compiler-core/slang-include-system.h @@ -36,7 +36,12 @@ struct IncludeSystem SlangResult findFile(const String& pathToInclude, const String& pathIncludedFrom, PathInfo& outPathInfo); SlangResult findFile(SlangPathType fromPathType, const String& fromPath, const String& path, PathInfo& outPathInfo); String simplifyPath(const String& path); - SlangResult loadFile(const PathInfo& pathInfo, ComPtr<ISlangBlob>& outBlob); + SlangResult loadFile(const PathInfo& pathInfo, ComPtr<ISlangBlob>& outBlob, SourceFile*& outSourceFile); + inline SlangResult loadFile(const PathInfo& pathInfo, ComPtr<ISlangBlob>& outBlob) + { + SourceFile* sourceFile; + return loadFile(pathInfo, outBlob, sourceFile); + } SlangResult findAndLoadFile(const String& pathToInclude, const String& pathIncludedFrom, PathInfo& outPathInfo, ComPtr<ISlangBlob>& outBlob); @@ -45,6 +50,7 @@ struct IncludeSystem SourceManager* getSourceManager() const { return m_sourceManager; } /// Ctor + IncludeSystem() = default; IncludeSystem(SearchDirectoryList* searchDirectories, ISlangFileSystemExt* fileSystemExt, SourceManager* sourceManager = nullptr); protected: diff --git a/source/compiler-core/slang-source-loc.cpp b/source/compiler-core/slang-source-loc.cpp index 33f93074e..fe08f8dfc 100644 --- a/source/compiler-core/slang-source-loc.cpp +++ b/source/compiler-core/slang-source-loc.cpp @@ -919,6 +919,13 @@ void SourceManager::addSourceFile(const String& uniqueIdentity, SourceFile* sour m_sourceFileMap.add(uniqueIdentity, sourceFile); } +void SourceManager::addSourceFileIfNotExist(const String& uniqueIdentity, SourceFile* sourceFile) +{ + if (findSourceFileRecursively(uniqueIdentity)) + return; + m_sourceFileMap.addIfNotExists(uniqueIdentity, sourceFile); +} + HumaneSourceLoc SourceManager::getHumaneLoc(SourceLoc loc, SourceLocType type) { SourceView* sourceView = findSourceViewRecursively(loc); diff --git a/source/compiler-core/slang-source-loc.h b/source/compiler-core/slang-source-loc.h index 5c78c4293..f432b2432 100644 --- a/source/compiler-core/slang-source-loc.h +++ b/source/compiler-core/slang-source-loc.h @@ -482,6 +482,7 @@ struct SourceManager /// Add a source file, uniqueIdentity must be unique for this manager AND any parents void addSourceFile(const String& uniqueIdentity, SourceFile* sourceFile); + void addSourceFileIfNotExist(const String& uniqueIdentity, SourceFile* sourceFile); /// Get the slice pool StringSlicePool& getStringSlicePool() { return m_slicePool; } diff --git a/source/core/slang-io.cpp b/source/core/slang-io.cpp index 4ef5ecc2d..c546dd059 100644 --- a/source/core/slang-io.cpp +++ b/source/core/slang-io.cpp @@ -794,6 +794,17 @@ namespace Slang } #endif + bool Path::equals(String path1, String path2) + { + Path::getCanonical(path1, path1); + Path::getCanonical(path2, path2); +#if SLANG_WINDOWS_FAMILY + return path1.getUnownedSlice().caseInsensitiveEquals(path2.getUnownedSlice()); +#else + return path1 == path2; +#endif + } + /// Gets the path to the executable that was invoked that led to the current threads execution /// If run from a shared library/dll will be the path of the executable that loaded said library /// @param outPath Pointer to buffer to hold the path. diff --git a/source/core/slang-io.h b/source/core/slang-io.h index 30c477730..125ac135c 100644 --- a/source/core/slang-io.h +++ b/source/core/slang-io.h @@ -211,6 +211,8 @@ namespace Slang /// @param path /// @return SLANG_OK if file or directory is removed static SlangResult remove(const String& path); + + static bool equals(String path1, String path2); }; struct URI diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h index e43141cad..ddff39502 100644 --- a/source/slang/slang-ast-decl.h +++ b/source/slang/slang-ast-decl.h @@ -436,6 +436,12 @@ class ModuleDecl : public NamespaceDeclBase }; +// Represents a transparent scope of declarations that are defined in a single source file. +class FileDecl : public ContainerDecl +{ + SLANG_AST_CLASS(FileDecl); +}; + /// A declaration that brings members of another declaration or namespace into scope class UsingDecl : public Decl { @@ -449,15 +455,12 @@ class UsingDecl : public Decl Scope* scope = nullptr; }; -class ImportDecl : public Decl +class FileReferenceDeclBase : public Decl { - SLANG_AST_CLASS(ImportDecl) + SLANG_AST_CLASS(FileReferenceDeclBase) // The name of the module we are trying to import NameLoc moduleNameAndLoc; - - // The module that actually got imported - ModuleDecl* importedModuleDecl = nullptr; SourceLoc startLoc; SourceLoc endLoc; @@ -467,6 +470,36 @@ class ImportDecl : public Decl Scope* scope = nullptr; }; +class ImportDecl : public FileReferenceDeclBase +{ + SLANG_AST_CLASS(ImportDecl) + + // The module that actually got imported + ModuleDecl* importedModuleDecl = nullptr; +}; + +class IncludeDeclBase : public FileReferenceDeclBase +{ + SLANG_AST_CLASS(IncludeDeclBase) + + FileDecl* fileDecl = nullptr; +}; + +class IncludeDecl : public IncludeDeclBase +{ + SLANG_AST_CLASS(IncludeDecl) +}; + +class ImplementingDecl : public IncludeDeclBase +{ + SLANG_AST_CLASS(ImplementingDecl) +}; + +class ModuleDeclarationDecl : public Decl +{ + SLANG_AST_CLASS(ModuleDeclarationDecl); +}; + // A generic declaration, parameterized on types/values class GenericDecl : public ContainerDecl { diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 98a2b18a1..b4cc6c00a 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -85,6 +85,10 @@ namespace Slang void visitImportDecl(ImportDecl* decl); + void visitIncludeDecl(IncludeDecl* decl); + + void visitImplementingDecl(ImplementingDecl* decl); + void visitUsingDecl(UsingDecl* decl); void visitGenericTypeParamDecl(GenericTypeParamDecl* decl); @@ -1773,6 +1777,27 @@ namespace Slang _registerBuiltinDeclsRec(getSession(), moduleDecl); } + if (moduleDecl->members.getCount() > 0) + { + auto firstMember = moduleDecl->members[0]; + if (auto implDecl = as<ImplementingDecl>(firstMember)) + { + if (!getShared()->isInLanguageServer()) + { + // A primary module file can't start with an "implementing" declaration. + getSink()->diagnose(firstMember, Diagnostics::primaryModuleFileCannotStartWithImplementingDecl); + } + } + else if (!as<ModuleDeclarationDecl>(firstMember)) + { + // A primary module file must start with a `module` declaration. + // TODO: this warning is disabled for now to free users from massive change for now. +#if 0 + getSink()->diagnose(firstMember, Diagnostics::primaryModuleFileMustStartWithModuleDecl); +#endif + } + } + // We need/want to visit any `import` declarations before // anything else, to make sure that scoping works. // @@ -1784,6 +1809,30 @@ namespace Slang ensureDecl(importDecl, DeclCheckState::Checked); } + // Next, make sure all `__include` decls are processed and the referenced + // files are parsed. + auto visitIncludeDecls = [&](ContainerDecl* fileDecl) + { + for (Index i = 0; i < fileDecl->members.getCount(); i++) + { + auto decl = fileDecl->members[i]; + if (auto includeDecl = as<IncludeDecl>(decl)) + { + ensureDecl(includeDecl, DeclCheckState::Checked); + } + else if (auto implementingDecl = as<ImplementingDecl>(decl)) + { + ensureDecl(implementingDecl, DeclCheckState::Checked); + } + } + }; + visitIncludeDecls(moduleDecl); + for (Index i = 0; i < moduleDecl->members.getCount(); i++) + { + if (auto fileDecl = as<FileDecl>(moduleDecl->members[i])) + visitIncludeDecls(fileDecl); + } + // The entire goal of semantic checking is to get all of the // declarations in the module up to `DeclCheckState::Checked`. // @@ -6694,6 +6743,18 @@ namespace Slang loc); } + void SemanticsVisitor::importFileDeclIntoScope(Scope* scope, FileDecl* fileDecl) + { + // Create a new sub-scope to wire the module + // into our lookup chain. + auto subScope = getASTBuilder()->create<Scope>(); + subScope->containerDecl = fileDecl; + + subScope->nextSibling = scope->nextSibling; + scope->nextSibling = subScope; + } + + void SemanticsVisitor::importModuleIntoScope(Scope* scope, ModuleDecl* moduleDecl) { // If we've imported this one already, then @@ -6766,6 +6827,146 @@ namespace Slang } } + String getSimpleModuleName(Name* name) + { + auto text = getText(name); + auto dirPos = Math::Max(text.indexOf('/'), text.indexOf('\\')); + if (dirPos < 0) + return text; + auto slice = text.getUnownedSlice().tail(dirPos + 1); + auto dotPos = slice.indexOf('.'); + if (dotPos < 0) + return slice; + return String(slice.head(dotPos)); + } + + ModuleDeclarationDecl* findExistingModuleDeclarationDecl(ModuleDecl* decl) + { + if (decl->members.getCount() == 0) + return nullptr; + if (auto rs = as<ModuleDeclarationDecl>(decl->members[0])) + return rs; + for (auto fileDecl : decl->getMembersOfType<FileDecl>()) + { + if (fileDecl->members.getCount() == 0) + continue; + if (auto rs = as<ModuleDeclarationDecl>(fileDecl->members[0])) + return rs; + } + return nullptr; + } + + void SemanticsDeclHeaderVisitor::visitIncludeDecl(IncludeDecl* decl) + { + auto name = decl->moduleNameAndLoc.name; + + if (!getShared()->getTranslationUnitRequest()) + getSink()->diagnose(decl->moduleNameAndLoc.loc, Diagnostics::cannotProcessInclude); + + auto parentModule = getModule(decl); + auto moduleDecl = parentModule->getModuleDecl(); + + auto [fileDecl, isNew] = getLinkage()->findAndIncludeFile(getModule(decl), getShared()->getTranslationUnitRequest(), name, decl->moduleNameAndLoc.loc, getSink()); + + if (!fileDecl) + return; + + decl->fileDecl = fileDecl; + + if (!isNew) + return; + + if (fileDecl->members.getCount() == 0) + return; + auto firstMember = fileDecl->members[0]; + if (auto moduleDeclaration = as<ModuleDeclarationDecl>(firstMember)) + { + // We are trying to include a file that defines a module, the user could mean "import" instead. + getSink()->diagnose(decl->moduleNameAndLoc.loc, Diagnostics::includedFileMissingImplementingDoYouMeanImport, name, moduleDeclaration->getName()); + return; + } + + importFileDeclIntoScope(moduleDecl->ownedScope, fileDecl); + + if (auto implementing = as<ImplementingDecl>(firstMember)) + { + // The file we are including must be implementing the current module. + auto moduleName = getSimpleModuleName(implementing->moduleNameAndLoc.name); + auto expectedModuleName = moduleDecl->getName(); + bool shouldSkipDiagnostic = false; + if (moduleDecl->members.getCount()) + { + if (auto moduleDeclarationDecl = as<ModuleDeclarationDecl>(moduleDecl->members[0])) + { + expectedModuleName = moduleDeclarationDecl->getName(); + } + else if (getShared()->isInLanguageServer()) + { + auto moduleDeclarationDecls = findExistingModuleDeclarationDecl(moduleDecl); + if (moduleDeclarationDecls) + { + expectedModuleName = moduleDeclarationDecls->getName(); + } + else + { + shouldSkipDiagnostic = true; + } + } + } + if (!shouldSkipDiagnostic && !moduleName.getUnownedSlice().caseInsensitiveEquals(getText(expectedModuleName).getUnownedSlice())) + { + getSink()->diagnose(decl->moduleNameAndLoc.loc, Diagnostics::includedFileDoesNotImplementCurrentModule, expectedModuleName, moduleName); + return; + } + return; + } + + getSink()->diagnose(decl->moduleNameAndLoc.loc, Diagnostics::includedFileMissingImplementing, name); + } + + void SemanticsDeclHeaderVisitor::visitImplementingDecl(ImplementingDecl* decl) + { + // Don't need to do anything unless we are in a language server context. + if (!getShared()->isInLanguageServer()) + return; + + // Treat an `implementing` declaration as an `include` declaration when + // we are in a language server context. + + auto name = decl->moduleNameAndLoc.name; + + if (!getShared()->getTranslationUnitRequest()) + getSink()->diagnose(decl->moduleNameAndLoc.loc, Diagnostics::cannotProcessInclude); + + auto [fileDecl, isNew] = getLinkage()->findAndIncludeFile(getModule(decl), getShared()->getTranslationUnitRequest(), name, decl->moduleNameAndLoc.loc, getSink()); + + decl->fileDecl = fileDecl; + + if (!isNew) + return; + + if (!fileDecl || fileDecl->members.getCount() == 0) + { + return; + } + + auto firstMember = fileDecl->members[0]; + if (auto moduleDeclaration = as<ModuleDeclarationDecl>(firstMember)) + { + // We are trying to implement a file that defines a module, this is expected. + return; + } + + if (auto implementing = as<ImplementingDecl>(firstMember)) + { + getSink()->diagnose(decl->moduleNameAndLoc.loc, Diagnostics::implementingMustReferencePrimaryModuleFile); + return; + } + + if (auto moduleDecl = getModuleDecl(decl)) + importFileDeclIntoScope(moduleDecl->ownedScope, fileDecl); + } + void SemanticsDeclHeaderVisitor::visitUsingDecl(UsingDecl* decl) { // First, we need to look up whatever the argument of the `using` diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 31be012d3..e9654830a 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -551,6 +551,10 @@ namespace Slang /// `import` to use them instead of trying to find the files in file system. LoadedModuleDictionary* m_environmentModules = nullptr; + /// (optional) The translation unit that is being checked. + /// Needed for handling `__include`s. + TranslationUnitRequest* m_translationUnitRequest = nullptr; + DiagnosticSink* getSink() { return m_sink; @@ -568,11 +572,13 @@ namespace Slang Linkage* linkage, Module* module, DiagnosticSink* sink, - LoadedModuleDictionary* environmentModules = nullptr) + LoadedModuleDictionary* environmentModules = nullptr, + TranslationUnitRequest* translationUnit = nullptr) : m_linkage(linkage) , m_module(module) , m_sink(sink) , m_environmentModules(environmentModules) + , m_translationUnitRequest(translationUnit) {} Session* getSession() @@ -590,6 +596,11 @@ namespace Slang return m_module; } + TranslationUnitRequest* getTranslationUnitRequest() + { + return m_translationUnitRequest; + } + bool isInLanguageServer() { if (m_linkage) @@ -2368,6 +2379,8 @@ namespace Slang // void importModuleIntoScope(Scope* scope, ModuleDecl* moduleDecl); + void importFileDeclIntoScope(Scope* scope, FileDecl* fileDecl); + void suggestCompletionItems( CompletionSuggestions::ScopeKind scopeKind, LookupResult const& lookupResult); diff --git a/source/slang/slang-check.cpp b/source/slang/slang-check.cpp index 780c109da..de86a333f 100644 --- a/source/slang/slang-check.cpp +++ b/source/slang/slang-check.cpp @@ -170,7 +170,8 @@ namespace Slang translationUnit->compileRequest->getLinkage(), translationUnit->getModule(), translationUnit->compileRequest->getSink(), - &loadedModules); + &loadedModules, + translationUnit); SemanticsDeclVisitorBase visitor( (SemanticsContext(&sharedSemanticsContext)) ); diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index e8c624b01..8a56df3af 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1414,6 +1414,9 @@ namespace Slang void _addEntryPoint(EntryPoint* entryPoint); void _processFindDeclsExportSymbolsRec(Decl* decl); + // Gets the files that has been included into the module. + Dictionary<SourceFile*, FileDecl*>& getIncludedSourceFileMap() { return m_mapSourceFileToFileDecl; } + protected: void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) SLANG_OVERRIDE; @@ -1468,6 +1471,9 @@ namespace Slang // and m_mangledExportSymbols holds the NodeBase* values for each index. StringSlicePool m_mangledExportPool; List<NodeBase*> m_mangledExportSymbols; + + // Source files that have been pulled into the module with `__include`. + Dictionary<SourceFile*, FileDecl*> m_mapSourceFileToFileDecl; }; typedef Module LoadedModule; @@ -1529,6 +1535,10 @@ namespace Slang NamePool* getNamePool(); SourceManager* getSourceManager(); + Scope* getLanguageScope(); + + Dictionary<String, String> getCombinedPreprocessorDefinitions(); + protected: void _addSourceFile(SourceFile* sourceFile); /* Given an artifact, find a PathInfo. @@ -1942,6 +1952,14 @@ namespace Slang DiagnosticSink* sink, const LoadedModuleDictionary* loadedModules = nullptr); + SourceFile* findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem); + struct IncludeResult + { + FileDecl* fileDecl; + bool isNew; + }; + IncludeResult findAndIncludeFile(Module* module, TranslationUnitRequest* translationUnit, Name* name, SourceLoc const& loc, DiagnosticSink* sink); + SourceManager* getSourceManager() { return m_sourceManager; diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 75308c829..8eb0584d0 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -349,6 +349,14 @@ DIAGNOSTIC(30098, Error, nonStaticMemberFunctionNotAllowedAsDiffOperand, "non-st DIAGNOSTIC(30099, Error, sizeOfArgumentIsInvalid, "argument to sizeof is invalid") +// Include +DIAGNOSTIC(30500, Error, includedFileMissingImplementing, "missing 'implementing' declaration in the included source file '$0'.") +DIAGNOSTIC(30501, Error, includedFileMissingImplementingDoYouMeanImport, "missing 'implementing' declaration in the included source file '$0'. The file declares that it defines module '$1', do you mean 'import' instead?") +DIAGNOSTIC(30502, Error, includedFileDoesNotImplementCurrentModule, "the included source file is expected to implement module '$0', but it is implementing '$1' instead.") +DIAGNOSTIC(30503, Error, primaryModuleFileCannotStartWithImplementingDecl, "a primary source file for a module cannot start with 'implementing'.") +DIAGNOSTIC(30504, Warning, primaryModuleFileMustStartWithModuleDecl, "a primary source file for a module should start with 'module'.") +DIAGNOSTIC(30505, Error, implementingMustReferencePrimaryModuleFile, "the source file referenced by 'implementing' must be a primary module file starting with a 'module' declaration.") + // Attributes DIAGNOSTIC(31000, Error, unknownAttributeName, "unknown attribute '$0'") DIAGNOSTIC(31001, Error, attributeArgumentCountMismatch, "attribute '$0' expects $1 arguments ($2 provided)") @@ -432,6 +440,7 @@ DIAGNOSTIC(30510, Error, loopInDiffFuncRequireUnrollOrMaxIters, "loops inside a DIAGNOSTIC(39999, Fatal, cyclicReference, "cyclic reference '$0'.") DIAGNOSTIC(39999, Error, localVariableUsedBeforeDeclared, "local variable '$0' is being used before its declaration.") DIAGNOSTIC(39999, Error, variableUsedInItsOwnDefinition, "the initial-value expression for variable '$0' depends on the value of the variable itself") +DIAGNOSTIC(39001, Fatal , cannotProcessInclude, "internal compiler error: cannot process '__include' in the current semantic checking context.") // 304xx: generics DIAGNOSTIC(30400, Error, genericTypeNeedsArgs, "generic type '$0' used without argument") diff --git a/source/slang/slang-language-server-ast-lookup.cpp b/source/slang/slang-language-server-ast-lookup.cpp index dbdf1ddd0..3aa66ee02 100644 --- a/source/slang/slang-language-server-ast-lookup.cpp +++ b/source/slang/slang-language-server-ast-lookup.cpp @@ -669,7 +669,7 @@ bool _findAstNodeImpl(ASTLookupContext& context, SyntaxNode* node) if (visitor.dispatchIfNotNull(extDecl->targetType.exp)) return true; } - else if (auto importDecl = as<ImportDecl>(node)) + else if (auto importDecl = as<FileReferenceDeclBase>(node)) { if (_isLocInRange(&context, importDecl->startLoc, importDecl->endLoc)) { @@ -679,6 +679,7 @@ bool _findAstNodeImpl(ASTLookupContext& context, SyntaxNode* node) return true; } } + for (auto modifier : decl->modifiers) { if (auto hlslSemantic = as<HLSLSemantic>(modifier)) @@ -722,7 +723,7 @@ bool _findAstNodeImpl(ASTLookupContext& context, SyntaxNode* node) {} else if (container->closingSourceLoc.getRaw() >= container->loc.getRaw()) { - if (!_isLocInRange(&context, container->loc, container->closingSourceLoc)) + if (!_isLocInRange(&context, container->loc, container->closingSourceLoc) && !as<NamespaceDeclBase>(container)) { shouldInspectChildren = false; } diff --git a/source/slang/slang-language-server-completion.cpp b/source/slang/slang-language-server-completion.cpp index 58ee766cc..7b01dac34 100644 --- a/source/slang/slang-language-server-completion.cpp +++ b/source/slang/slang-language-server-completion.cpp @@ -293,16 +293,24 @@ List<LanguageServerProtocol::TextEditCompletionItem> CompletionContext::gatherFi SlangResult CompletionContext::tryCompleteImport() { - static auto importStr = UnownedStringSlice("import "); - auto lineContent = doc->getLine(line); - Index pos = lineContent.indexOf(importStr); - if (pos == -1) - return SLANG_FAIL; - auto lineBeforeImportKeyword = lineContent.head(pos).trim(); - if (lineBeforeImportKeyword.getLength() != 0 && lineBeforeImportKeyword != "__exported") - return SLANG_FAIL; - - pos += importStr.getLength(); + const char* prefixes[] = { "import ", "__include ", "implementing " }; + UnownedStringSlice lineContent; + Index pos = -1; + for (auto prefix : prefixes) + { + static auto importStr = UnownedStringSlice(prefix); + lineContent = doc->getLine(line); + pos = lineContent.indexOf(importStr); + if (pos == -1) + continue; + auto lineBeforeImportKeyword = lineContent.head(pos).trim(); + if (lineBeforeImportKeyword.getLength() != 0 && lineBeforeImportKeyword != "__exported") + continue; + pos += importStr.getLength(); + goto validLine; + } + return SLANG_FAIL; +validLine:; while (pos < lineContent.getLength() && pos < col - 1 && CharUtil::isWhitespace(lineContent[pos])) pos++; if (pos < lineContent.getLength() && lineContent[pos] == '"') diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp index c188142f5..12151441c 100644 --- a/source/slang/slang-language-server.cpp +++ b/source/slang/slang-language-server.cpp @@ -458,7 +458,7 @@ void appendDefinitionLocation(StringBuilder& sb, Workspace* workspace, const Hum sb << "Defined in " << pathSlice << "(" << loc.line << ")\n"; } -HumaneSourceLoc getModuleLoc(SourceManager* manager, ModuleDecl* moduleDecl) +HumaneSourceLoc getModuleLoc(SourceManager* manager, ContainerDecl* moduleDecl) { if (moduleDecl) { @@ -722,6 +722,27 @@ SlangResult LanguageServer::hover( hover.range.end.character = (int)utf16Col; } } + else if (auto includeDeclBase = as<IncludeDeclBase>(leafNode)) + { + auto moduleLoc = getModuleLoc(version->linkage->getSourceManager(), includeDeclBase->fileDecl); + if (moduleLoc.pathInfo.hasFoundPath()) + { + String path = moduleLoc.pathInfo.foundPath; + Path::getCanonical(path, path); + sb << path; + auto humaneLoc = version->linkage->getSourceManager()->getHumaneLoc( + includeDeclBase->startLoc, SourceLocType::Actual); + Index utf16Line, utf16Col; + doc->oneBasedUTF8LocToZeroBasedUTF16Loc(humaneLoc.line, humaneLoc.column, utf16Line, utf16Col); + hover.range.start.line = (int)utf16Line; + hover.range.start.character = (int)utf16Col; + humaneLoc = version->linkage->getSourceManager()->getHumaneLoc( + includeDeclBase->endLoc, SourceLocType::Actual); + doc->oneBasedUTF8LocToZeroBasedUTF16Loc(humaneLoc.line, humaneLoc.column, utf16Line, utf16Col); + hover.range.end.line = (int)utf16Line; + hover.range.end.character = (int)utf16Col; + } + } else if (auto decl = as<Decl>(leafNode)) { fillDeclRefHoverInfo(makeDeclRef(decl)); @@ -835,6 +856,14 @@ SlangResult LanguageServer::gotoDefinition( locations.add(LocationResult{ location, 0 }); } } + else if (auto includeDeclBase = as<IncludeDeclBase>(leafNode)) + { + auto location = getModuleLoc(version->linkage->getSourceManager(), includeDeclBase->fileDecl); + if (location.pathInfo.hasFoundPath()) + { + locations.add(LocationResult{ location, 0 }); + } + } if (locations.getCount() == 0) { m_connection->sendResult(NullResponse::get(), responseId); diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp index d71227b6c..ee4518c20 100644 --- a/source/slang/slang-lookup.cpp +++ b/source/slang/slang-lookup.cpp @@ -675,6 +675,10 @@ static void _lookUpInScopes( auto scope = request.scope; auto endScope = request.endScope; + + // The file decl that this scope is in. + FileDecl* thisFileDecl = nullptr; + for (;scope != endScope; scope = scope->parent) { // Note that we consider all "peer" scopes together, @@ -693,6 +697,18 @@ static void _lookUpInScopes( if (!containerDecl) continue; + if (auto fileDecl = as<FileDecl>(containerDecl)) + { + if (!thisFileDecl) + thisFileDecl = fileDecl; + else if (fileDecl == thisFileDecl) + { + // If we have already looked up in this file decl, + // we don't want to do so again. + continue; + } + } + // TODO: If we need default substitutions to be applied to // the `containerDecl`, then it might make sense to have // each `link` in the scope store a decl-ref instead of diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 9686c4735..609a5d1e5 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -6896,11 +6896,15 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> LoweredValInfo visit##NAME(NAME*) { return LoweredValInfo(); } IGNORED_CASE(ImportDecl) + IGNORED_CASE(IncludeDecl) + IGNORED_CASE(ImplementingDecl) IGNORED_CASE(UsingDecl) IGNORED_CASE(EmptyDecl) IGNORED_CASE(SyntaxDecl) IGNORED_CASE(AttributeDecl) IGNORED_CASE(NamespaceDecl) + IGNORED_CASE(ModuleDeclarationDecl) + IGNORED_CASE(FileDecl) #undef IGNORED_CASE diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 821e7c813..350bc9443 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -115,11 +115,6 @@ namespace Slang int genericDepth = 0; - // Have we seen any `import` declarations? If so, we need - // to parse function bodies completely, even if we are in - // "rewrite" mode. - bool haveSeenAnyImportDecls = false; - // Is the parser in a "recovering" state? // During recovery we don't emit additional errors, until we find // a token that we expected, when we exit recovery. @@ -155,6 +150,17 @@ namespace Slang { currentScope = currentScope->parent; } + + ModuleDecl* getCurrentModuleDecl() + { + for (auto scope = currentScope; scope; scope = scope->parent) + { + if (auto moduleDecl = as<ModuleDecl>(scope->containerDecl)) + return moduleDecl; + } + return nullptr; + } + Parser( ASTBuilder* inAstBuilder, TokenSpan const& _tokens, @@ -184,7 +190,7 @@ namespace Slang bool LookAheadToken(TokenType type, int offset); bool LookAheadToken(const char* string, int offset); - void parseSourceFile(ModuleDecl* program); + void parseSourceFile(ContainerDecl* parentDecl); Decl* ParseStruct(); ClassDecl* ParseClass(); GLSLInterfaceBlockDecl* ParseGLSLInterfaceBlock(); @@ -1206,12 +1212,8 @@ namespace Slang return NameLoc(parser->ReadToken(TokenType::Identifier)); } - static NodeBase* parseImportDecl( - Parser* parser, void* /*userData*/) + static void parseFileReferenceDeclBase(Parser* parser, FileReferenceDeclBase* decl) { - parser->haveSeenAnyImportDecls = true; - - auto decl = parser->astBuilder->create<ImportDecl>(); decl->scope = parser->currentScope; decl->startLoc = parser->tokenReader.peekLoc(); @@ -1245,7 +1247,55 @@ namespace Slang } decl->endLoc = parser->tokenReader.peekLoc(); parser->ReadToken(TokenType::Semicolon); + } + + static NodeBase* parseImportDecl( + Parser* parser, void* /*userData*/) + { + auto decl = parser->astBuilder->create<ImportDecl>(); + parseFileReferenceDeclBase(parser, decl); + return decl; + } + + static NodeBase* parseIncludeDecl( + Parser* parser, void* /*userData*/) + { + auto decl = parser->astBuilder->create<IncludeDecl>(); + parseFileReferenceDeclBase(parser, decl); + return decl; + } + static NodeBase* parseImplementingDecl( + Parser* parser, void* /*userData*/) + { + auto decl = parser->astBuilder->create<ImplementingDecl>(); + parseFileReferenceDeclBase(parser, decl); + return decl; + } + + static NodeBase* parseModuleDeclarationDecl( + Parser* parser, void* /*userData*/) + { + auto decl = parser->astBuilder->create<ModuleDeclarationDecl>(); + if (parser->LookAheadToken(TokenType::Identifier)) + { + auto nameToken = parser->ReadToken(TokenType::Identifier); + decl->nameAndLoc.name = parser->getNamePool()->getName(nameToken.getContent()); + decl->nameAndLoc.loc = nameToken.loc; + } + else if (parser->LookAheadToken(TokenType::StringLiteral)) + { + auto nameToken = parser->ReadToken(TokenType::StringLiteral); + decl->nameAndLoc.name = parser->getNamePool()->getName(getStringLiteralTokenValue(nameToken)); + decl->nameAndLoc.loc = nameToken.loc; + } + else + { + if (auto moduleDecl = parser->getCurrentModuleDecl()) + decl->nameAndLoc.name = moduleDecl->getName(); + decl->nameAndLoc.loc = parser->tokenReader.peekLoc(); + } + parser->ReadToken(TokenType::Semicolon); return decl; } @@ -4179,7 +4229,7 @@ namespace Slang } - void Parser::parseSourceFile(ModuleDecl* program) + void Parser::parseSourceFile(ContainerDecl* program) { SLANG_AST_BUILDER_RAII(astBuilder); @@ -6916,7 +6966,8 @@ namespace Slang TranslationUnitRequest* translationUnit, TokenSpan const& tokens, DiagnosticSink* sink, - Scope* outerScope) + Scope* outerScope, + ContainerDecl* parentDecl) { ParserOptions options = {}; options.enableEffectAnnotations = translationUnit->compileRequest->getLinkage()->getEnableEffectAnnotations(); @@ -6926,7 +6977,7 @@ namespace Slang parser.namePool = translationUnit->getNamePool(); parser.sourceLanguage = translationUnit->sourceLanguage; - return parser.parseSourceFile(translationUnit->getModuleDecl()); + return parser.parseSourceFile(parentDecl); } static void addBuiltinSyntaxImpl( @@ -7424,6 +7475,9 @@ namespace Slang _makeParseDecl("attribute_syntax", parseAttributeSyntaxDecl ), _makeParseDecl("__import", parseImportDecl ), _makeParseDecl("import", parseImportDecl ), + _makeParseDecl("__include", parseIncludeDecl ), + _makeParseDecl("module", parseModuleDeclarationDecl), + _makeParseDecl("implementing", parseImplementingDecl), _makeParseDecl("let", parseLetDecl ), _makeParseDecl("var", parseVarDecl ), _makeParseDecl("func", parseFuncDecl ), diff --git a/source/slang/slang-parser.h b/source/slang/slang-parser.h index 4f888f87c..9933f6839 100644 --- a/source/slang/slang-parser.h +++ b/source/slang/slang-parser.h @@ -14,7 +14,8 @@ namespace Slang TranslationUnitRequest* translationUnit, TokenSpan const& tokens, DiagnosticSink* sink, - Scope* outerScope); + Scope* outerScope, + ContainerDecl* parentDecl); Expr* parseTermFromSourceFile( ASTBuilder* astBuilder, diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 0331a5f8e..0f53ffcd5 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1117,6 +1117,12 @@ SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSource( if (SLANG_SUCCEEDED(Path::getCanonical(pathStr, cannonicalPath))) { pathInfo = PathInfo::makeNormal(pathStr, cannonicalPath); + ComPtr<ISlangBlob> uniqueIdentity; + getFileSystemExt()->getFileUniqueIdentity(cannonicalPath.getBuffer(), uniqueIdentity.writeRef()); + if (uniqueIdentity && uniqueIdentity->getBufferSize() != 0) + { + pathInfo.uniqueIdentity = (char*)uniqueIdentity->getBufferPointer(); + } } } auto module = loadModule( @@ -1655,6 +1661,74 @@ SourceManager* TranslationUnitRequest::getSourceManager() return compileRequest->getSourceManager(); } +Scope* TranslationUnitRequest::getLanguageScope() +{ + Scope* languageScope = nullptr; + switch (sourceLanguage) + { + case SourceLanguage::HLSL: + languageScope = getSession()->hlslLanguageScope; + break; + + case SourceLanguage::Slang: + default: + languageScope = getSession()->slangLanguageScope; + break; + } + return languageScope; +} + +Dictionary<String, String> TranslationUnitRequest::getCombinedPreprocessorDefinitions() +{ + // TODO(JS): + // Note! that a adding a define twice will cause an exception in debug builds + // that may be desirable or not... + Dictionary<String, String> combinedPreprocessorDefinitions; + for (const auto& def : compileRequest->getLinkage()->preprocessorDefinitions) + combinedPreprocessorDefinitions.add(def); + for (const auto& def : compileRequest->preprocessorDefinitions) + combinedPreprocessorDefinitions.add(def); + for (const auto& def : preprocessorDefinitions) + combinedPreprocessorDefinitions.add(def); + + // Define standard macros, if not already defined. This style assumes using `#if __SOME_VAR` style, as in + // + // ``` + // #if __SLANG_COMPILER__ + // ``` + // + // This choice is made because slang outputs a warning on using a variable in an #if if not defined + // + // Of course this means using #ifndef/#ifdef/defined() is probably not appropraite with thes variables. + { + // Used to identify level of HLSL language compatibility + combinedPreprocessorDefinitions.addIfNotExists("__HLSL_VERSION", "2020"); + + // Indicates this is being compiled by the slang *compiler* + combinedPreprocessorDefinitions.addIfNotExists("__SLANG_COMPILER__", "1"); + + // Set macro depending on source type + switch (sourceLanguage) + { + case SourceLanguage::HLSL: + // Used to indicate compiled as HLSL language + combinedPreprocessorDefinitions.addIfNotExists("__HLSL__", "1"); + break; + case SourceLanguage::Slang: + // Used to indicate compiled as Slang language + combinedPreprocessorDefinitions.addIfNotExists("__SLANG__", "1"); + break; + default: break; + } + + // If not set, define as 0. + combinedPreprocessorDefinitions.addIfNotExists("__HLSL__", "0"); + combinedPreprocessorDefinitions.addIfNotExists("__SLANG__", "0"); + } + + return combinedPreprocessorDefinitions; +} + void TranslationUnitRequest::addSourceArtifact(IArtifact* sourceArtifact) { SLANG_ASSERT(sourceArtifact); @@ -1762,7 +1836,11 @@ SlangResult TranslationUnitRequest::requireSourceFiles() // Create a new source file, using the pathInfo and blob sourceFile = sourceManager->createSourceFileWithBlob(pathInfo, blob); } - + + auto uniqueIdentity = pathInfo.getMostUniqueIdentity(); + if (uniqueIdentity.getLength()) + sourceManager->addSourceFileIfNotExist(uniqueIdentity, sourceFile); + // Finally add the source file _addSourceFile(sourceFile); } @@ -1775,6 +1853,7 @@ void TranslationUnitRequest::_addSourceFile(SourceFile* sourceFile) m_sourceFiles.add(sourceFile); getModule()->addFileDependency(sourceFile); + getModule()->getIncludedSourceFileMap().add(sourceFile, nullptr); } List<SourceFile*> const& TranslationUnitRequest::getSourceFiles() @@ -2256,52 +2335,8 @@ void FrontEndCompileRequest::parseTranslationUnit( break; } - // TODO(JS): - // Note! that a adding a define twice will cause an exception in debug builds - // that may be desirable or not... - Dictionary<String, String> combinedPreprocessorDefinitions; - for(const auto& def : getLinkage()->preprocessorDefinitions) - combinedPreprocessorDefinitions.add(def); - for(const auto& def : preprocessorDefinitions) - combinedPreprocessorDefinitions.add(def); - for(const auto& def : translationUnit->preprocessorDefinitions) - combinedPreprocessorDefinitions.add(def); - - // Define standard macros, if not already defined. This style assumes using `#if __SOME_VAR` style, as in - // - // ``` - // #if __SLANG_COMPILER__ - // ``` - // - // This choice is made because slang outputs a warning on using a variable in an #if if not defined - // - // Of course this means using #ifndef/#ifdef/defined() is probably not appropraite with thes variables. - { - // Used to identify level of HLSL language compatibility - combinedPreprocessorDefinitions.addIfNotExists("__HLSL_VERSION", "2020"); - - // Indicates this is being compiled by the slang *compiler* - combinedPreprocessorDefinitions.addIfNotExists("__SLANG_COMPILER__", "1"); - - // Set macro depending on source type - switch (translationUnit->sourceLanguage) - { - case SourceLanguage::HLSL: - // Used to indicate compiled as HLSL language - combinedPreprocessorDefinitions.addIfNotExists("__HLSL__", "1"); - break; - case SourceLanguage::Slang: - // Used to indicate compiled as Slang language - combinedPreprocessorDefinitions.addIfNotExists("__SLANG__", "1"); - break; - default: break; - } - - // If not set, define as 0. - combinedPreprocessorDefinitions.addIfNotExists("__HLSL__", "0"); - combinedPreprocessorDefinitions.addIfNotExists("__SLANG__", "0"); - } - + auto combinedPreprocessorDefinitions = translationUnit->getCombinedPreprocessorDefinitions(); + auto module = translationUnit->getModule(); ASTBuilder* astBuilder = module->getASTBuilder(); @@ -2339,6 +2374,11 @@ void FrontEndCompileRequest::parseTranslationUnit( for (auto sourceFile : translationUnit->getSourceFiles()) { + module->getIncludedSourceFileMap().addIfNotExists(sourceFile, nullptr); + } + + for (auto sourceFile : translationUnit->getSourceFiles()) + { auto tokens = preprocessSource( sourceFile, getSink(), @@ -2367,7 +2407,8 @@ void FrontEndCompileRequest::parseTranslationUnit( translationUnit, tokens, getSink(), - languageScope); + languageScope, + translationUnitSyntax); // Let's try dumping @@ -3115,7 +3156,6 @@ RefPtr<Module> Linkage::loadModule( // Some problem accessing source files return nullptr; } - int errorCountBefore = sink->getErrorCount(); frontEndReq->parseTranslationUnit(translationUnit); int errorCountAfter = sink->getErrorCount(); @@ -3158,6 +3198,34 @@ bool Linkage::isBeingImported(Module* module) return false; } + // Derive a file name for the module, by taking the given + // identifier, replacing all occurrences of `_` with `-`, + // and then appending `.slang`. + // + // For example, `foo_bar` becomes `foo-bar.slang`. +String getFileNameFromModuleName(Name* name) +{ + String fileName; + if (!getText(name).getUnownedSlice().endsWithCaseInsensitive(".slang")) + { + StringBuilder sb; + for (auto c : getText(name)) + { + if (c == '_') + c = '-'; + + sb.append(c); + } + sb.append(".slang"); + fileName = sb.produceString(); + } + else + { + fileName = getText(name); + } + return fileName; +} + RefPtr<Module> Linkage::findOrImportModule( Name* name, SourceLoc const& loc, @@ -3200,30 +3268,7 @@ RefPtr<Module> Linkage::findOrImportModule( return previouslyLoadedModule; } - // Derive a file name for the module, by taking the given - // identifier, replacing all occurrences of `_` with `-`, - // and then appending `.slang`. - // - // For example, `foo_bar` becomes `foo-bar.slang`. - - String fileName; - if (!getText(name).getUnownedSlice().endsWithCaseInsensitive(".slang")) - { - StringBuilder sb; - for (auto c : getText(name)) - { - if (c == '_') - c = '-'; - - sb.append(c); - } - sb.append(".slang"); - fileName = sb.produceString(); - } - else - { - fileName = getText(name); - } + auto fileName = getFileNameFromModuleName(name); // Next, try to find the file of the given name, // using our ordinary include-handling logic. @@ -3276,6 +3321,91 @@ RefPtr<Module> Linkage::findOrImportModule( loadedModules); } +SourceFile* Linkage::findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem) +{ + auto fileName = getFileNameFromModuleName(name); + + // Next, try to find the file of the given name, + // using our ordinary include-handling logic. + + outIncludeSystem = IncludeSystem(&searchDirectories, getFileSystemExt(), getSourceManager()); + + // Get the original path info + PathInfo pathIncludedFromInfo = getSourceManager()->getPathInfo(loc, SourceLocType::Actual); + PathInfo filePathInfo; + + ComPtr<ISlangBlob> fileContents; + + // We have to load via the found path - as that is how file was originally loaded + if (SLANG_FAILED(outIncludeSystem.findFile(fileName, pathIncludedFromInfo.foundPath, filePathInfo))) + { + return nullptr; + } + // Otherwise, try to load it. + SourceFile* sourceFile; + if (SLANG_FAILED(outIncludeSystem.loadFile(filePathInfo, fileContents, sourceFile))) + { + return nullptr; + } + return sourceFile; +} + +Linkage::IncludeResult Linkage::findAndIncludeFile(Module* module, TranslationUnitRequest* translationUnit, Name* name, SourceLoc const& loc, DiagnosticSink* sink) +{ + IncludeResult result; + result.fileDecl = nullptr; + result.isNew = false; + + IncludeSystem includeSystem; + auto sourceFile = findFile(name, loc, includeSystem); + + if (!sourceFile) + { + sink->diagnose(loc, Diagnostics::cannotOpenFile, getText(name)); + return result; + } + + // If the file has already been included, don't need to do anything further. + if (auto existingFileDecl = module->getIncludedSourceFileMap().tryGetValue(sourceFile)) + { + result.fileDecl = *existingFileDecl; + result.isNew = false; + return result; + } + + module->addFileDependency(sourceFile); + + // Create a transparent FileDecl to hold all children from the included file. + auto fileDecl = module->getASTBuilder()->create<FileDecl>(); + fileDecl->nameAndLoc.name = name; + module->getIncludedSourceFileMap().add(sourceFile, fileDecl); + + FrontEndPreprocessorHandler preprocessorHandler(module, module->getASTBuilder(), sink); + auto combinedPreprocessorDefinitions = translationUnit->getCombinedPreprocessorDefinitions(); + auto tokens = preprocessSource( + sourceFile, + sink, + &includeSystem, + combinedPreprocessorDefinitions, + this, + &preprocessorHandler); + + auto outerScope = module->getModuleDecl()->ownedScope; + parseSourceFile( + module->getASTBuilder(), + translationUnit, + tokens, + sink, + outerScope, + fileDecl); + + module->getModuleDecl()->addMember(fileDecl); + + result.fileDecl = fileDecl; + result.isNew = true; + return result; +} + // // ModuleDependencyList // diff --git a/tests/language-feature/modules/include/a.slang b/tests/language-feature/modules/include/a.slang new file mode 100644 index 000000000..5e01a15c5 --- /dev/null +++ b/tests/language-feature/modules/include/a.slang @@ -0,0 +1,8 @@ +//TEST_IGNORE_FILE: + +implementing main; + +// Cyclic include, OK. +__include a; + +int f_a() { return 1; } diff --git a/tests/language-feature/modules/include/b.slang b/tests/language-feature/modules/include/b.slang new file mode 100644 index 000000000..737178d54 --- /dev/null +++ b/tests/language-feature/modules/include/b.slang @@ -0,0 +1,6 @@ +// TEST_IGNORE_FILE: +implementing main; + +__include a; + +int f_b() { return 2 + f_a(); } diff --git a/tests/language-feature/modules/include/c.slang b/tests/language-feature/modules/include/c.slang new file mode 100644 index 000000000..9588b37be --- /dev/null +++ b/tests/language-feature/modules/include/c.slang @@ -0,0 +1,4 @@ +// TEST_IGNORE_FILE: +implementing main; + +int f_c() { return f_a() + f_b(); } diff --git a/tests/language-feature/modules/include/main.slang b/tests/language-feature/modules/include/main.slang new file mode 100644 index 000000000..e036adc3c --- /dev/null +++ b/tests/language-feature/modules/include/main.slang @@ -0,0 +1,20 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj -output-using-type +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -vk -shaderobj -output-using-type + +module main; + +__include c; +__include c; // Duplicate include, OK. +__include b; + + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + int tid = dispatchThreadID.x; + outputBuffer[tid] = f_c(); + // CHECK: 4 +} |
