summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/compiler-core/slang-include-system.cpp17
-rw-r--r--source/compiler-core/slang-include-system.h8
-rw-r--r--source/compiler-core/slang-source-loc.cpp7
-rw-r--r--source/compiler-core/slang-source-loc.h1
-rw-r--r--source/core/slang-io.cpp11
-rw-r--r--source/core/slang-io.h2
-rw-r--r--source/slang/slang-ast-decl.h43
-rw-r--r--source/slang/slang-check-decl.cpp201
-rw-r--r--source/slang/slang-check-impl.h15
-rw-r--r--source/slang/slang-check.cpp3
-rwxr-xr-xsource/slang/slang-compiler.h18
-rw-r--r--source/slang/slang-diagnostic-defs.h9
-rw-r--r--source/slang/slang-language-server-ast-lookup.cpp5
-rw-r--r--source/slang/slang-language-server-completion.cpp28
-rw-r--r--source/slang/slang-language-server.cpp31
-rw-r--r--source/slang/slang-lookup.cpp16
-rw-r--r--source/slang/slang-lower-to-ir.cpp4
-rw-r--r--source/slang/slang-parser.cpp82
-rw-r--r--source/slang/slang-parser.h3
-rw-r--r--source/slang/slang.cpp276
-rw-r--r--tests/language-feature/modules/include/a.slang8
-rw-r--r--tests/language-feature/modules/include/b.slang6
-rw-r--r--tests/language-feature/modules/include/c.slang4
-rw-r--r--tests/language-feature/modules/include/main.slang20
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
+}