From d1762c0f03060e924b024228f42835a870da8d4e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 May 2023 16:50:30 +0200 Subject: [PATCH 01/39] [WIP] Add some tests --- .../Test_Import_Non_Existing_All/package.yaml | 7 +++ .../src/Main.enso | 3 ++ .../src/Other_Module.enso | 1 + .../package.yaml | 7 +++ .../src/Main.enso | 3 ++ .../src/Other_Module.enso | 1 + .../package.yaml | 7 +++ .../src/Main.enso | 3 ++ .../src/Other_Module.enso | 1 + .../package.yaml | 7 +++ .../src/Main.enso | 3 ++ .../src/Other_Module.enso | 1 + .../package.yaml | 7 +++ .../src/Main.enso | 3 ++ .../src/Other_Module.enso | 1 + .../test/semantic/ImportsTest.scala | 52 ++++++++++++++++++- 16 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_All/package.yaml create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_All/src/Main.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_All/src/Other_Module.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From/package.yaml create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From/src/Main.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From/src/Other_Module.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/package.yaml create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/src/Main.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/src/Other_Module.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/package.yaml create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Main.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Other_Module.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/package.yaml create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/src/Main.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/src/Other_Module.enso diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/package.yaml b/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/package.yaml new file mode 100644 index 000000000000..d85372b0e700 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/package.yaml @@ -0,0 +1,7 @@ +name: Test_Import_Non_Existing_All +namespace: Enso_Test +license: APLv2 +enso-version: default +version: "0.0.1" +author: "Enso Team " +maintainer: "Enso Team " diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/src/Main.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/src/Main.enso new file mode 100644 index 000000000000..e8d792ed7de0 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/src/Main.enso @@ -0,0 +1,3 @@ +from project.Other_Module import all + +main = 42 diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/src/Other_Module.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/src/Other_Module.enso new file mode 100644 index 000000000000..be75254dbcf1 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_All/src/Other_Module.enso @@ -0,0 +1 @@ +# Left blank on purpose diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/package.yaml b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/package.yaml new file mode 100644 index 000000000000..7c8201e6e6b5 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/package.yaml @@ -0,0 +1,7 @@ +name: Test_Import_Non_Existing_From +namespace: Enso_Test +license: APLv2 +enso-version: default +version: "0.0.1" +author: "Enso Team " +maintainer: "Enso Team " diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/src/Main.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/src/Main.enso new file mode 100644 index 000000000000..7aa96498fbca --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/src/Main.enso @@ -0,0 +1,3 @@ +from project.Other_Module import Non_Existing_Symbol + +main = 42 diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/src/Other_Module.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/src/Other_Module.enso new file mode 100644 index 000000000000..be75254dbcf1 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From/src/Other_Module.enso @@ -0,0 +1 @@ +# Left blank on purpose diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/package.yaml b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/package.yaml new file mode 100644 index 000000000000..8cc304efa5f3 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/package.yaml @@ -0,0 +1,7 @@ +name: Test_Import_Non_Existing_From_Double_Nested +namespace: Enso_Test +license: APLv2 +enso-version: default +version: "0.0.1" +author: "Enso Team " +maintainer: "Enso Team " diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/src/Main.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/src/Main.enso new file mode 100644 index 000000000000..6fcd7d19642c --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/src/Main.enso @@ -0,0 +1,3 @@ +from project.Other_Module.Non_Existing_Type import Non_Existing_Symbol + +main = 42 diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/src/Other_Module.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/src/Other_Module.enso new file mode 100644 index 000000000000..be75254dbcf1 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Double_Nested/src/Other_Module.enso @@ -0,0 +1 @@ +# Left blank on purpose diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/package.yaml b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/package.yaml new file mode 100644 index 000000000000..26b7da05a6d3 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/package.yaml @@ -0,0 +1,7 @@ +name: Test_Import_Non_Existing_From_Nested +namespace: Enso_Test +license: APLv2 +enso-version: default +version: "0.0.1" +author: "Enso Team " +maintainer: "Enso Team " diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Main.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Main.enso new file mode 100644 index 000000000000..0b162ccc9030 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Main.enso @@ -0,0 +1,3 @@ +from project.Other_Module import Non_Existing_Type.Non_Existing_Type_Nested + +main = 42 diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Other_Module.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Other_Module.enso new file mode 100644 index 000000000000..be75254dbcf1 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Other_Module.enso @@ -0,0 +1 @@ +# Left blank on purpose diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/package.yaml b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/package.yaml new file mode 100644 index 000000000000..06df8a440961 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/package.yaml @@ -0,0 +1,7 @@ +name: Test_Import_Non_Existing_Simple +namespace: Enso_Test +license: APLv2 +enso-version: default +version: "0.0.1" +author: "Enso Team " +maintainer: "Enso Team " diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/src/Main.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/src/Main.enso new file mode 100644 index 000000000000..91dd12a6ae2e --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/src/Main.enso @@ -0,0 +1,3 @@ +import project.Other_Module.Non_Existing_Symbol + +main = 42 diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/src/Other_Module.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/src/Other_Module.enso new file mode 100644 index 000000000000..be75254dbcf1 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Simple/src/Other_Module.enso @@ -0,0 +1 @@ +# Left blank on purpose diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala index ff739d19d052..f1075cfc47b3 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala @@ -2,6 +2,8 @@ package org.enso.interpreter.test.semantic import org.enso.interpreter.test.{InterpreterException, PackageTest} +import scala.annotation.unused + class ImportsTest extends PackageTest { "Atoms and methods" should "be available for import" in { evalTestProject("TestSimpleImports") shouldEqual 20 @@ -56,6 +58,54 @@ class ImportsTest extends PackageTest { evalTestProject("Test_Hiding_Success") shouldEqual 20 } + "[0] Importing non-existing symbol with `import Module.Non_Existing`" should "throw exception" in { + the[InterpreterException] thrownBy evalTestProject( + "Test_Import_Non_Existing_Simple" + ) should have message "Compilation aborted due to errors." + consumeOut + .filterNot(_.contains("Compiler encountered")) + .filterNot(_.contains("In module")) + .head should include("The module Enso_Test.Test_Import_Non_Existing_Simple.Other_Module.Non_Existing_Symbol does not exist.") + } + + "[1] Importing non-existing symbol with `from Module import `" should "throw exception" in { + the[InterpreterException] thrownBy evalTestProject( + "Test_Import_Non_Existing_From" + ) should have message "Compilation aborted due to errors." + @unused + val out = consumeOut + println("[mylog] BP") + } + + "[2] Importing non-existing symbol with `from Module import .`" should "throw exception" in { + /*the[InterpreterException] thrownBy evalTestProject( + "Test_Import_Non_Existing_From_Nested" + ) should have message "Compilation aborted due to errors."*/ + evalTestProject("Test_Import_Non_Existing_From_Nested") + @unused + val out = consumeOut + println("[mylog] BP") + } + + "[3] Importing non-existing symbol with `from Module.Non_Existing import `" should "throw exception" in { + /*the[InterpreterException] thrownBy evalTestProject( + "Test_Import_Non_Existing_From_Double_Nested" + ) should have message "Compilation aborted due to errors."*/ + evalTestProject("Test_Import_Non_Existing_From_Double_Nested") + @unused + val out = consumeOut + println("[mylog] BP") + } + + "[4] Importing all non-existing symbols with `from Module import all`" should "throw exception" in { + the[InterpreterException] thrownBy evalTestProject( + "Test_Import_Non_Existing_All" + ) should have message "Compilation aborted due to errors." + @unused + val out = consumeOut + println("[mylog] BP") + } + "Imported modules" should "be renamed with renaming imports" in { evalTestProject("Test_Rename") shouldEqual 20 } @@ -70,7 +120,7 @@ class ImportsTest extends PackageTest { .head should include("The name `Atom` could not be found.") } - "Importing everything from the module" should "should not bring module into the scope when resolving names" in { + "Importing everything from the module" should "not bring module into the scope when resolving names" in { evalTestProject("Test_Import_Case") shouldEqual 0 } From ff5856bc7a55bac4e37c6598c2f73419862824c1 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 May 2023 16:50:58 +0200 Subject: [PATCH 02/39] Improve docs --- .../src/main/scala/org/enso/compiler/data/BindingsMap.scala | 5 +---- .../org/enso/compiler/pass/resolve/FullyQualifiedNames.scala | 2 +- .../main/scala/org/enso/compiler/phase/ImportResolver.scala | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 5ef93076ff8a..b37175b3716a 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -16,9 +16,6 @@ import scala.annotation.unused /** A utility structure for resolving symbols in a given module. * - * @param constructors the types defined in the current module - * @param polyglotSymbols the polyglot symbols imported into the scope - * @param moduleMethods the methods defined with current module as `this` * @param currentModule the module holding these bindings */ @@ -679,7 +676,7 @@ object BindingsMap { * * @param importDef the definition of the import * @param exports the exports associated with the import - * @param target the module this import resolves to + * @param target the module or type this import resolves to */ case class ResolvedImport( importDef: IR.Module.Scope.Import.Module, diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala index f5da8cc43603..474a0964ab85 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala @@ -22,7 +22,7 @@ import org.enso.interpreter.runtime.Module /** Partially resolves fully qualified names corresponding to the library names * * 1. Identifies potential library names e.g., `Standard.Base` - * 2. If the component has not be compiled yet, compilation is triggered + * 2. If the component has not been compiled yet, compilation is triggered * 3. Replaces the library name with a fresh name and a resolved Main module */ case object FullyQualifiedNames extends IRPass { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala index 02fbcb2eb0b5..ee8c19748aca 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala @@ -34,8 +34,8 @@ class ImportResolver(compiler: Compiler) { * * @param module the entry-point module. * @return a tuple containing a list of all modules that need to go through the full compilation pipeline and - * a list of all modules which have been inferred from bindings cache and could potentially be compiled lazilly - */ + * a list of all modules which have been inferred from bindings cache and could potentially be compiled lazily + */ def mapImports( module: Module, bindingsCachingEnabled: Boolean From b77252eae25ab8255397e20b1a502a6b7365e5ad Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 May 2023 15:38:00 +0200 Subject: [PATCH 03/39] bench_download_tool: since and until are dates, not datetimes --- tools/performance/engine-benchmarks/bench_download.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/performance/engine-benchmarks/bench_download.py b/tools/performance/engine-benchmarks/bench_download.py index 956015535cec..4cf633b9bd02 100755 --- a/tools/performance/engine-benchmarks/bench_download.py +++ b/tools/performance/engine-benchmarks/bench_download.py @@ -45,7 +45,7 @@ import zipfile from argparse import ArgumentParser, RawDescriptionHelpFormatter from csv import DictWriter -from datetime import datetime, timedelta +from datetime import datetime, timedelta, date from os import path from typing import List, Dict, Optional, Any, Union from dataclasses import dataclass @@ -621,8 +621,8 @@ def commit_to_str(commit: Commit) -> str: if __name__ == '__main__': - default_since = datetime.now() - timedelta(days=14) - default_until = datetime.now() + default_since: date = (datetime.now() - timedelta(days=14)).date() + default_until: date = datetime.now().date() default_cache_dir = path.expanduser("~/.cache/enso_bench_download") date_format_help = DATE_FORMAT.replace("%", "%%") @@ -631,14 +631,14 @@ def commit_to_str(commit: Commit) -> str: arg_parser.add_argument("-s", "--since", action="store", default=default_since, metavar="SINCE_DATE", - type=lambda s: datetime.strptime(s, DATE_FORMAT), + type=lambda s: datetime.strptime(s, DATE_FORMAT).date(), help=f"The date from which the benchmark results will be gathered. " f"Format is {date_format_help}. " f"The default is 14 days before") arg_parser.add_argument("-u", "--until", action="store", default=default_until, metavar="UNTIL_DATE", - type=lambda s: datetime.strptime(s, DATE_FORMAT), + type=lambda s: datetime.strptime(s, DATE_FORMAT).date(), help=f"The date until which the benchmark results will be gathered. " f"Format is {date_format_help}. " f"The default is today") From 6c06775acff44f28bb125dc11e2502c06a9a6302 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 6 May 2023 00:13:04 +0200 Subject: [PATCH 04/39] [WIP] Add SymbolsImportResolution compiler step --- .../scala/org/enso/compiler/Compiler.scala | 11 +- .../scala/org/enso/compiler/core/IR.scala | 9 +- .../enso/compiler/phase/ImportResolver.scala | 4 +- .../phase/SymbolsImportResolution.scala | 166 ++++++++++++++++++ 4 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala index a951ca805b6b..5daae3a9341c 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala @@ -12,7 +12,8 @@ import org.enso.compiler.pass.analyse._ import org.enso.compiler.phase.{ ExportCycleException, ExportsResolution, - ImportResolver + ImportResolver, + SymbolsImportResolution } import org.enso.editions.LibraryName import org.enso.interpreter.node.{ExpressionNode => RuntimeExpression} @@ -34,7 +35,6 @@ import java.util.concurrent.{ TimeUnit } import java.util.logging.Level - import scala.jdk.OptionConverters._ import java.util.concurrent.Future @@ -54,6 +54,8 @@ class Compiler( private val passes: Passes = new Passes(config) private val passManager: PassManager = passes.passManager private val importResolver: ImportResolver = new ImportResolver(this) + private val symbolImportsResolver: SymbolsImportResolution = + new SymbolsImportResolution(this) private val stubsGenerator: RuntimeStubsGenerator = new RuntimeStubsGenerator(builtins) private val irCachingEnabled = !context.isIrCachingDisabled @@ -445,6 +447,11 @@ class Compiler( try { new ExportsResolution().run(importedModules) } catch { case e: ExportCycleException => reportCycle(e) } + // Symbol imports resolution has to be done after the exports resolution + requiredModules.foreach(module => + symbolImportsResolver.resolveImportSymbols(module) + ) + val parsingTasks: List[CompletableFuture[Unit]] = modulesImportedWithCachedBindings.map { module => if (config.parallelParsing) { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala index 90668ed267c1..bb48e242cfc4 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala @@ -11,7 +11,6 @@ import org.enso.interpreter.epb.EpbParser import org.enso.syntax.text.{Debug, Location} import java.util.UUID - import scala.annotation.unused /** [[IR]] is a temporary and fairly unsophisticated internal representation @@ -8783,6 +8782,14 @@ object IR { override def message: String = s"The module $name does not exist." } + case class SymbolsDoNotExist( + symbolNames: List[String], + moduleName: String + ) extends Reason { + override def message: String = + s"The symbols $symbolNames (modules or types) do not exist in module $moduleName." + } + } /** An erroneous import or export statement. diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala index ee8c19748aca..72a8eb481d41 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala @@ -34,8 +34,8 @@ class ImportResolver(compiler: Compiler) { * * @param module the entry-point module. * @return a tuple containing a list of all modules that need to go through the full compilation pipeline and - * a list of all modules which have been inferred from bindings cache and could potentially be compiled lazily - */ + * a list of all modules which have been inferred from bindings cache and could potentially be compiled lazily + */ def mapImports( module: Module, bindingsCachingEnabled: Boolean diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala new file mode 100644 index 000000000000..f98d3c891e44 --- /dev/null +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -0,0 +1,166 @@ +package org.enso.compiler.phase + +import org.enso.compiler.Compiler +import org.enso.compiler.core.IR +import org.enso.compiler.core.IR.Module.Scope.Import +import org.enso.compiler.data.BindingsMap +import org.enso.compiler.data.BindingsMap.{ImportTarget, ModuleReference} +import org.enso.compiler.exception.CompilerError +import org.enso.compiler.pass.analyse.BindingAnalysis +import org.enso.interpreter.runtime.Module + +class SymbolsImportResolution(compiler: Compiler) { + def resolveImportSymbols( + module: Module + ): Unit = { + val bindingMap = module.getIr.unsafeGetMetadata( + BindingAnalysis, + "Should be analyzed before resolving import symbols" + ) + module.unsafeSetIr( + module.getIr.copy( + imports = module.getIr.imports.map(analyseImport(bindingMap, _)) + ) + ) + } + + private def analyseImport( + bindingsMap: BindingsMap, + imp: IR.Module.Scope.Import + ): IR.Module.Scope.Import = { + imp match { + case imp @ Import.Module( + importedModuleName, + _, + _, + Some(onlyNames), + _, + _, + _, + _, + _ + ) => + assert( + !bindingsMap.resolvedImports.contains(imp), + "From imports should not be resolved yet" + ) + // Get resolved module from bindings map + val importedModule = compiler + .getModule(importedModuleName.name) + .getOrElse( + throw new CompilerError( + s"Module ${importedModuleName.name} should already be imported" + ) + ) + val importedModuleBindingMap = + importedModule.getIr.unsafeGetMetadata( + BindingAnalysis, + "Should be analyzed before resolving import symbols" + ) + val symbolsResolution = + tryResolveSymbols(importedModule, importedModuleBindingMap, onlyNames) + if (symbolsResolution.unresolved.nonEmpty) { + // Replace the IR with IR.Error + IR.Error.ImportExport( + ir = imp, + reason = IR.Error.ImportExport.SymbolsDoNotExist( + symbolNames = symbolsResolution.unresolved.map(_.name), + moduleName = importedModuleName.toString + ) + ) + } else { + + val resolvedImports = + symbolsResolution.resolved.map(importTarget => { + BindingsMap.ResolvedImport( + importDef = imp, + exports = List.empty, + target = importTarget + ) + }) + + bindingsMap.resolvedImports = + bindingsMap.resolvedImports.appendedAll(resolvedImports) + imp + } + case _ => imp + } + } + + private def tryResolveSymbols( + module: Module, + bindingsMap: BindingsMap, + symbolsToImport: List[IR.Name.Literal] + ): SymbolsResolution = { + val analysedSymbols: List[Either[ImportTarget, IR.Name.Literal]] = + symbolsToImport + .map(symbolToImport => { + tryResolveFromDefinedEntities( + module, + bindingsMap, + symbolToImport + ) match { + case Some(importTarget) => Left(importTarget) + case None => + tryResolveFromExportedSymbols(bindingsMap, symbolToImport) match { + case Some(importTarget) => Left(importTarget) + case None => Right(symbolToImport) + } + } + }) + + val unresolvedSymbols: List[IR.Name.Literal] = + analysedSymbols.collect { case Right(name) => name } + val resolvedSymbols: List[ImportTarget] = + analysedSymbols.collect { case Left(importTarget) => importTarget } + + SymbolsResolution(resolvedSymbols, unresolvedSymbols) + } + + private def tryResolveFromDefinedEntities( + module: Module, + bindingMap: BindingsMap, + symbolToImport: IR.Name.Literal + ): Option[ImportTarget] = { + bindingMap.definedEntities.find(_.name == symbolToImport.name) match { + case Some(definedEntity) => + definedEntity match { + case tp: BindingsMap.Type => + Some( + BindingsMap.ResolvedType(ModuleReference.Concrete(module), tp) + ) + case moduleMethod: BindingsMap.ModuleMethod => + throw new CompilerError( + s"Resolving method ${moduleMethod} is not yet supported" + ) + case BindingsMap.PolyglotSymbol(name) => + throw new CompilerError( + s"Resolving polyglot symbol '$name' is not yet supported'" + ) + } + case None => None + } + } + + private def tryResolveFromExportedSymbols( + bindingMap: BindingsMap, + symbolToImport: IR.Name.Literal + ): Option[ImportTarget] = { + bindingMap.exportedSymbols.get(symbolToImport.name) match { + case Some(resolvedNames) => + val resolvedImportTargets: List[ImportTarget] = + resolvedNames.collect { + case resolvedModule: BindingsMap.ResolvedModule => resolvedModule + case resolvedType: BindingsMap.ResolvedType => resolvedType + } + // Take the first resolvedName that is also ImportTarget + resolvedImportTargets.headOption + case None => None + } + } + + private case class SymbolsResolution( + resolved: List[ImportTarget], + unresolved: List[IR.Name.Literal] + ) +} From 14b7e1d0e4df050d901088109a8648bb850fb133 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 6 May 2023 00:14:49 +0200 Subject: [PATCH 05/39] Add test for a malformed from import --- .../src/test/java/org/enso/compiler/ErrorCompilerTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/engine/runtime/src/test/java/org/enso/compiler/ErrorCompilerTest.java b/engine/runtime/src/test/java/org/enso/compiler/ErrorCompilerTest.java index 1d7172849ba6..2a7b79ce58ab 100644 --- a/engine/runtime/src/test/java/org/enso/compiler/ErrorCompilerTest.java +++ b/engine/runtime/src/test/java/org/enso/compiler/ErrorCompilerTest.java @@ -218,6 +218,12 @@ public void malformedImport13() throws Exception { assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path", 27, 30); } + @Test + public void malformedImport14() throws Exception { + var ir = parse("from Foo import Some.Nested.Module.Path"); + assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path", 20, 39); + } + @Test public void malformedExport1() throws Exception { var ir = parse("export"); From 2348881f00ee2bacd18757e65116b7ed1c88ec06 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 6 May 2023 00:16:17 +0200 Subject: [PATCH 06/39] Update tests --- .../src/Main.enso | 3 -- .../src/Other_Module.enso | 1 - .../package.yaml | 2 +- .../src/Main.enso | 3 ++ .../src/Other_Module.enso | 4 ++ .../test/semantic/ImportsTest.scala | 38 +++++++------------ 6 files changed, 21 insertions(+), 30 deletions(-) delete mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Main.enso delete mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Other_Module.enso rename engine/runtime/src/test/resources/{Test_Import_Non_Existing_From_Nested => Test_Import_Non_Existing_Mix}/package.yaml (78%) create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/src/Main.enso create mode 100644 engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/src/Other_Module.enso diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Main.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Main.enso deleted file mode 100644 index 0b162ccc9030..000000000000 --- a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Main.enso +++ /dev/null @@ -1,3 +0,0 @@ -from project.Other_Module import Non_Existing_Type.Non_Existing_Type_Nested - -main = 42 diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Other_Module.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Other_Module.enso deleted file mode 100644 index be75254dbcf1..000000000000 --- a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/src/Other_Module.enso +++ /dev/null @@ -1 +0,0 @@ -# Left blank on purpose diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/package.yaml b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/package.yaml similarity index 78% rename from engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/package.yaml rename to engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/package.yaml index 26b7da05a6d3..262eb5be5eaf 100644 --- a/engine/runtime/src/test/resources/Test_Import_Non_Existing_From_Nested/package.yaml +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/package.yaml @@ -1,4 +1,4 @@ -name: Test_Import_Non_Existing_From_Nested +name: Test_Import_Non_Existing_Mix namespace: Enso_Test license: APLv2 enso-version: default diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/src/Main.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/src/Main.enso new file mode 100644 index 000000000000..971fb122cb13 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/src/Main.enso @@ -0,0 +1,3 @@ +from project.Other_Module import Existing_Symbol, Non_Existing_Symbol + +main = 42 diff --git a/engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/src/Other_Module.enso b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/src/Other_Module.enso new file mode 100644 index 000000000000..e012db5711d5 --- /dev/null +++ b/engine/runtime/src/test/resources/Test_Import_Non_Existing_Mix/src/Other_Module.enso @@ -0,0 +1,4 @@ +# Left blank on purpose + +type Existing_Symbol + Value val diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala index f1075cfc47b3..b591d36d23c9 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala @@ -58,52 +58,40 @@ class ImportsTest extends PackageTest { evalTestProject("Test_Hiding_Success") shouldEqual 20 } - "[0] Importing non-existing symbol with `import Module.Non_Existing`" should "throw exception" in { + "Importing non-existing symbol with `import Module.Non_Existing`" should "throw exception" in { the[InterpreterException] thrownBy evalTestProject( "Test_Import_Non_Existing_Simple" ) should have message "Compilation aborted due to errors." consumeOut .filterNot(_.contains("Compiler encountered")) .filterNot(_.contains("In module")) - .head should include("The module Enso_Test.Test_Import_Non_Existing_Simple.Other_Module.Non_Existing_Symbol does not exist.") + .head should include( + "The module Enso_Test.Test_Import_Non_Existing_Simple.Other_Module.Non_Existing_Symbol does not exist." + ) } - "[1] Importing non-existing symbol with `from Module import `" should "throw exception" in { + "Importing non-existing symbol with `from Module import `" should "throw exception" in { the[InterpreterException] thrownBy evalTestProject( "Test_Import_Non_Existing_From" ) should have message "Compilation aborted due to errors." - @unused - val out = consumeOut - println("[mylog] BP") - } - - "[2] Importing non-existing symbol with `from Module import .`" should "throw exception" in { - /*the[InterpreterException] thrownBy evalTestProject( - "Test_Import_Non_Existing_From_Nested" - ) should have message "Compilation aborted due to errors."*/ - evalTestProject("Test_Import_Non_Existing_From_Nested") - @unused val out = consumeOut - println("[mylog] BP") + out.last should include regex "The symbols .*Non_Existing_Symbol.*do not exist in module .+Other_Module" } - "[3] Importing non-existing symbol with `from Module.Non_Existing import `" should "throw exception" in { - /*the[InterpreterException] thrownBy evalTestProject( + "Importing non-existing symbol with `from Module.Non_Existing import `" should "throw exception" in { + the[InterpreterException] thrownBy evalTestProject( "Test_Import_Non_Existing_From_Double_Nested" - ) should have message "Compilation aborted due to errors."*/ - evalTestProject("Test_Import_Non_Existing_From_Double_Nested") - @unused + ) should have message "Compilation aborted due to errors." val out = consumeOut - println("[mylog] BP") + out.last should include regex "The module.*Other_Module.Non_Existing_Type does not exist" } - "[4] Importing all non-existing symbols with `from Module import all`" should "throw exception" in { + "Importing mix of existing and non-existing symbols" should "throw exception" in { the[InterpreterException] thrownBy evalTestProject( - "Test_Import_Non_Existing_All" + "Test_Import_Non_Existing_Mix" ) should have message "Compilation aborted due to errors." - @unused val out = consumeOut - println("[mylog] BP") + out.last should include regex "The symbols .*Non_Existing_Symbol.*do not exist in module .+Other_Module" } "Imported modules" should "be renamed with renaming imports" in { From b31dfb0e00b971e04ecc303d8b0d7bc318adb8fa Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 May 2023 18:44:49 +0200 Subject: [PATCH 07/39] Add logging --- .../scala/org/enso/compiler/Compiler.scala | 23 ++++++++++++++++++- .../phase/SymbolsImportResolution.scala | 10 ++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala index 5daae3a9341c..96d35fb30ffc 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala @@ -191,7 +191,7 @@ class Compiler( logger.log( Level.SEVERE, "Could not find entry point for compilation in package [{0}.{1}]", - Array(pkg.namespace, pkg.name) + Array[Object](pkg.namespace, pkg.name) ) CompletableFuture.completedFuture(false) case Some(m) => @@ -436,6 +436,11 @@ class Compiler( module: Module, bindingsCachingEnabled: Boolean ): List[Module] = { + logger.log( + Level.FINER, + "Running imports and exports resolution for module {0}", + Array[Object](module.getName.toString) + ) val (importedModules, modulesImportedWithCachedBindings) = try { importResolver.mapImports(module, bindingsCachingEnabled) @@ -443,6 +448,12 @@ class Compiler( case e: ImportResolver.HiddenNamesConflict => reportExportConflicts(e) } + logger.log( + Level.FINER, + "Modules to be imported: [{0}]", + Array[Object](importedModules.map(_.getName.toString)) + ) + val requiredModules = try { new ExportsResolution().run(importedModules) } catch { case e: ExportCycleException => reportCycle(e) } @@ -464,8 +475,18 @@ class Compiler( } } + logger.log( + Level.FINER, + "Required modules after exports resolution: [{0}]", + Array[Object](requiredModules.map(_.getName.toString)) + ) + joinAllFutures(parsingTasks).get() + // Symbol imports resolution has to be done after the exports resolution + requiredModules.foreach(module => + symbolImportsResolver.resolveImportSymbols(module) + ) // ** Order matters for codegen ** // Consider a case when an exported symbol is referenced but the module that defines the symbol // has not yet registered the method in its scope. This will result in No_Such_Method method during runtime; diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index f98d3c891e44..425d68901087 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -1,5 +1,6 @@ package org.enso.compiler.phase +import com.oracle.truffle.api.TruffleLogger import org.enso.compiler.Compiler import org.enso.compiler.core.IR import org.enso.compiler.core.IR.Module.Scope.Import @@ -9,7 +10,11 @@ import org.enso.compiler.exception.CompilerError import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.interpreter.runtime.Module +import java.util.logging.Level + class SymbolsImportResolution(compiler: Compiler) { + private val logger: TruffleLogger = compiler.context.getLogger(getClass) + def resolveImportSymbols( module: Module ): Unit = { @@ -17,6 +22,11 @@ class SymbolsImportResolution(compiler: Compiler) { BindingAnalysis, "Should be analyzed before resolving import symbols" ) + logger.log( + Level.FINE, + "Resolving import symbols for module {0}", + Array[Object](module.getName.toString) + ) module.unsafeSetIr( module.getIr.copy( imports = module.getIr.imports.map(analyseImport(bindingMap, _)) From a46b07d5203bf768fb718c48a6c0343c2b344985 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 May 2023 18:45:33 +0200 Subject: [PATCH 08/39] SymbolsImportResolution can resolve members of types --- .../scala/org/enso/compiler/Compiler.scala | 5 - .../phase/SymbolsImportResolution.scala | 176 ++++++++++++++---- 2 files changed, 143 insertions(+), 38 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala index 96d35fb30ffc..751a9f87a620 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala @@ -458,11 +458,6 @@ class Compiler( try { new ExportsResolution().run(importedModules) } catch { case e: ExportCycleException => reportCycle(e) } - // Symbol imports resolution has to be done after the exports resolution - requiredModules.foreach(module => - symbolImportsResolver.resolveImportSymbols(module) - ) - val parsingTasks: List[CompletableFuture[Unit]] = modulesImportedWithCachedBindings.map { module => if (config.parallelParsing) { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index 425d68901087..c827d5392dda 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -40,7 +40,7 @@ class SymbolsImportResolution(compiler: Compiler) { ): IR.Module.Scope.Import = { imp match { case imp @ Import.Module( - importedModuleName, + importedName, _, _, Some(onlyNames), @@ -54,28 +54,70 @@ class SymbolsImportResolution(compiler: Compiler) { !bindingsMap.resolvedImports.contains(imp), "From imports should not be resolved yet" ) - // Get resolved module from bindings map - val importedModule = compiler - .getModule(importedModuleName.name) - .getOrElse( + logger.log( + Level.FINER, + s"Resolving symbols [{0}] from import `{1}`", + Array[Object](onlyNames.map(_.name), imp.showCode()) + ) + + // Check if the import is from a type rather than from a module, i.e., + // `from Module.Type import Constructor` versus `from Module import Some_Type`. + val shouldImportFromType = compiler.getModule(importedName.name).isEmpty + + val moduleName = if (shouldImportFromType) { + importedName.parts.dropRight(1).map(_.name).mkString(".") + } else { + importedName.name + } + val typeName: Option[String] = if (shouldImportFromType) { + Some(importedName.parts.last.name) + } else { + None + } + + logger.log( + Level.FINER, + "moduleName = {0}, typeName = {1}", + Array[Object](moduleName, typeName) + ) + + val importedModule = compiler.getModule(moduleName) match { + case Some(module) => module + case None => throw new CompilerError( - s"Module ${importedModuleName.name} should already be imported" + s"Module ${moduleName} should already be imported" ) - ) + } + val importedModuleBindingMap = importedModule.getIr.unsafeGetMetadata( BindingAnalysis, "Should be analyzed before resolving import symbols" ) val symbolsResolution = - tryResolveSymbols(importedModule, importedModuleBindingMap, onlyNames) + tryResolveSymbols( + importedModule, + importedModuleBindingMap, + onlyNames, + typeName + ) + + logger.log( + Level.FINER, + "Got result from symbolsResolution: Resolved = {0}, Unresolved = {1}", + Array[Object]( + symbolsResolution.resolved.map(_.qualifiedName.toString), + symbolsResolution.unresolved.map(_.showCode()) + ) + ) + if (symbolsResolution.unresolved.nonEmpty) { // Replace the IR with IR.Error IR.Error.ImportExport( ir = imp, reason = IR.Error.ImportExport.SymbolsDoNotExist( symbolNames = symbolsResolution.unresolved.map(_.name), - moduleName = importedModuleName.toString + moduleName = importedName.toString ) ) } else { @@ -100,7 +142,8 @@ class SymbolsImportResolution(compiler: Compiler) { private def tryResolveSymbols( module: Module, bindingsMap: BindingsMap, - symbolsToImport: List[IR.Name.Literal] + symbolsToImport: List[IR.Name.Literal], + typeName: Option[String] ): SymbolsResolution = { val analysedSymbols: List[Either[ImportTarget, IR.Name.Literal]] = symbolsToImport @@ -108,11 +151,12 @@ class SymbolsImportResolution(compiler: Compiler) { tryResolveFromDefinedEntities( module, bindingsMap, - symbolToImport + symbolToImport, + typeName ) match { case Some(importTarget) => Left(importTarget) case None => - tryResolveFromExportedSymbols(bindingsMap, symbolToImport) match { + tryResolveFromExportedSymbols(bindingsMap, symbolToImport, typeName) match { case Some(importTarget) => Left(importTarget) case None => Right(symbolToImport) } @@ -127,41 +171,107 @@ class SymbolsImportResolution(compiler: Compiler) { SymbolsResolution(resolvedSymbols, unresolvedSymbols) } + /** + * Tries to resolve a symbol (of a type or a module) from defined entities within + * the given `bindingMap`. + * @param symbolToImport Symbol to import from a type or from a module + * @param typeName If defined, a member from a type is imported, rather than a member of a module. + * @return + */ private def tryResolveFromDefinedEntities( module: Module, bindingMap: BindingsMap, - symbolToImport: IR.Name.Literal + symbolToImport: IR.Name.Literal, + typeName: Option[String] ): Option[ImportTarget] = { - bindingMap.definedEntities.find(_.name == symbolToImport.name) match { - case Some(definedEntity) => - definedEntity match { - case tp: BindingsMap.Type => - Some( - BindingsMap.ResolvedType(ModuleReference.Concrete(module), tp) - ) - case moduleMethod: BindingsMap.ModuleMethod => - throw new CompilerError( - s"Resolving method ${moduleMethod} is not yet supported" - ) - case BindingsMap.PolyglotSymbol(name) => - throw new CompilerError( - s"Resolving polyglot symbol '$name' is not yet supported'" - ) - } - case None => None + if (typeName.isDefined) { + // A member from a type is imported + val foundType: Option[BindingsMap.Type] = + bindingMap.definedEntities.find(_.name == typeName.get) match { + case Some(definedEntity) => + definedEntity match { + case tp: BindingsMap.Type => Some(tp) + case _ => throw new CompilerError(s"Expected BindingsMap.Type, got $definedEntity") + } + case None => None + } + // TODO: Resolve member + foundType match { + case Some(tp) => + logger.log( + Level.FINER, + "Resolved type {0} from definedEntities in module {1}", + Array[Object](tp, module) + ) + Some( + BindingsMap.ResolvedType(ModuleReference.Concrete(module), tp) + ) + case None => None + } + } else { + bindingMap.definedEntities.find(_.name == symbolToImport.name) match { + case Some(definedEntity) => + definedEntity match { + case tp: BindingsMap.Type => + logger.log( + Level.FINER, + "Resolved type {0} from definedEntities in module {1}", + Array[Object](tp, module) + ) + Some( + BindingsMap.ResolvedType(ModuleReference.Concrete(module), tp) + ) + case _: BindingsMap.ModuleMethod => + // TODO: Resolve to a method, not to a module + Some( + BindingsMap.ResolvedModule( + ModuleReference.Concrete(module) + ) + ) + case BindingsMap.PolyglotSymbol(_) => + // TODO: Resolve to a polyglot symbol, not to a module + Some( + BindingsMap.ResolvedModule( + ModuleReference.Concrete(module) + ) + ) + } + case None => None + } } } + /** + * Tries to resolve a symbol (of a type or a module) from exported symbols within + * the given `bindingMap`. + * @param bindingMap + * @param symbolToImport Symbol to import from a type or from a module + * @param typeName If defined, a member from a type is imported, rather than a member of a module. + * @return + */ private def tryResolveFromExportedSymbols( bindingMap: BindingsMap, - symbolToImport: IR.Name.Literal + symbolToImport: IR.Name.Literal, + typeName: Option[String] ): Option[ImportTarget] = { - bindingMap.exportedSymbols.get(symbolToImport.name) match { + val exportedNamesToSearch: Option[List[BindingsMap.ResolvedName]] = + if (typeName.isDefined) { + bindingMap.exportedSymbols.get(typeName.get) + } else { + bindingMap.exportedSymbols.get(symbolToImport.name) + } + exportedNamesToSearch match { case Some(resolvedNames) => + logger.log( + Level.FINER, + "Found symbols [{0}] from exportedSymbols in bindings map from module {1}", + Array[Object](resolvedNames, bindingMap.currentModule.getName.toString) + ) val resolvedImportTargets: List[ImportTarget] = resolvedNames.collect { case resolvedModule: BindingsMap.ResolvedModule => resolvedModule - case resolvedType: BindingsMap.ResolvedType => resolvedType + case resolvedType: BindingsMap.ResolvedType => resolvedType + case BindingsMap.ResolvedConstructor(resolvedType, _) => resolvedType } // Take the first resolvedName that is also ImportTarget resolvedImportTargets.headOption From 74325e291237e680934ec39d5eb84970f58428ff Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 May 2023 18:46:48 +0200 Subject: [PATCH 09/39] Add ImportExportsTest --- .../test/semantic/ImportExportTest.scala | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala new file mode 100644 index 000000000000..a5741bf579b1 --- /dev/null +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -0,0 +1,101 @@ +package org.enso.compiler.test.semantic + +import org.enso.compiler.core.IR +import org.enso.compiler.data.BindingsMap +import org.enso.compiler.pass.analyse.BindingAnalysis +import org.enso.interpreter.runtime.EnsoContext +import org.enso.interpreter.test.InterpreterContext +import org.enso.pkg.QualifiedName +import org.enso.interpreter.runtime +import org.enso.polyglot.{LanguageInfo, MethodNames, RuntimeOptions} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +import scala.annotation.unused + +/** + * Tests a single package with multiple modules for import/export resolution. + * Checks whether the exported symbols and defined entities metadata of the modules + * are correct. + */ +class ImportExportTest extends AnyWordSpecLike with Matchers { + private val interpreterCtx = new InterpreterContext( + contextModifiers = ctxBuilder => { + ctxBuilder + .option(RuntimeOptions.LOG_LEVEL, "WARNING") + } + ) + private val langCtx = interpreterCtx.ctx + .getBindings(LanguageInfo.ID) + .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) + .asHostObject[EnsoContext]() + + private val namespace = "my_pkg" + private val packageName = "My_Package" + private val packageQualifiedName = + QualifiedName.fromString(namespace + "." + packageName) + + langCtx.getPackageRepository.registerSyntheticPackage(namespace, packageName) + + implicit private class CreateMainModule(code: String) { + def createMainModule: IR.Module = { + val module = new runtime.Module( + packageQualifiedName.createChild("Main"), + null, + code + ) + langCtx.getPackageRepository.registerModuleCreatedInRuntime(module) + langCtx.getCompiler.run(module) + module.getIr + } + } + + implicit private class ProcessModule(moduleCode: String) { + def processModule(moduleName: QualifiedName): IR.Module = { + val module = new runtime.Module(moduleName, null, moduleCode) + langCtx.getPackageRepository.registerModuleCreatedInRuntime(module) + langCtx.getCompiler.run(module) + module.getIr + } + } + + implicit private class UnwrapBindingMap(moduleIr: IR.Module) { + def unwrapBindingMap = { + moduleIr.unsafeGetMetadata(BindingAnalysis, "Should be present") + } + } + + "Import resolution" should { + "bla" in { + val moduleCode = """ + |type Other_Module_Type + | Constructor + |""".stripMargin + val otherModuleName = packageQualifiedName.createChild("Other_Module") + val moduleIr = moduleCode.processModule(otherModuleName) + moduleIr.unwrapBindingMap.definedEntities.size shouldEqual 1 + moduleIr.unwrapBindingMap.definedEntities.head.name shouldEqual "Other_Module_Type" + val otherTypeDefinedEntity = moduleIr.unwrapBindingMap.definedEntities.head + + val mainCode = + s""" + |from $namespace.$packageName.Other_Module import Other_Module_Type + | + |main = Other_Module_Type.Constructor + |""".stripMargin + val mainIr = mainCode.processModule(packageQualifiedName.createChild("Main")) + mainIr.imports.size shouldEqual 1 + mainIr.imports.head match { + case importErr: IR.Error.ImportExport => + fail(s"Import should be resolved, but instead produced IR.Error.ImportExport with ${importErr.reason.message}") + case _ => () + } + val mainBindingsMap = mainIr.unwrapBindingMap + mainBindingsMap.resolvedImports.size shouldEqual 1 + mainBindingsMap.resolvedImports.head.target match { + + } + mainIr shouldNot be(null) + } + } +} From 5833208a5fa4c0d5345c3dfdec1e8dc1aa348d91 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 9 May 2023 11:47:05 +0200 Subject: [PATCH 10/39] Use custom GraalVM context in ImportExportTest --- .../test/semantic/ImportExportTest.scala | 74 ++++++++++++------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index a5741bf579b1..749639695fed 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -1,16 +1,18 @@ package org.enso.compiler.test.semantic import org.enso.compiler.core.IR -import org.enso.compiler.data.BindingsMap import org.enso.compiler.pass.analyse.BindingAnalysis +import org.enso.interpreter.runtime import org.enso.interpreter.runtime.EnsoContext -import org.enso.interpreter.test.InterpreterContext import org.enso.pkg.QualifiedName -import org.enso.interpreter.runtime import org.enso.polyglot.{LanguageInfo, MethodNames, RuntimeOptions} +import org.graalvm.polyglot.{Context, Engine} +import org.scalatest.BeforeAndAfter import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike +import java.io.ByteArrayOutputStream +import java.nio.file.Paths import scala.annotation.unused /** @@ -18,14 +20,36 @@ import scala.annotation.unused * Checks whether the exported symbols and defined entities metadata of the modules * are correct. */ -class ImportExportTest extends AnyWordSpecLike with Matchers { - private val interpreterCtx = new InterpreterContext( - contextModifiers = ctxBuilder => { - ctxBuilder - .option(RuntimeOptions.LOG_LEVEL, "WARNING") - } - ) - private val langCtx = interpreterCtx.ctx +class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter { + private val out = new ByteArrayOutputStream() + + private val engine = Engine + .newBuilder("enso") + .allowExperimentalOptions(true) + .build() + + private val ctx = Context + .newBuilder(LanguageInfo.ID) + .engine(engine) + .allowExperimentalOptions(true) + .allowAllAccess(true) + .allowCreateThread(false) + .out(out) + .err(out) + .option(RuntimeOptions.LOG_LEVEL, "ALL") + .option(RuntimeOptions.DISABLE_IR_CACHES, "true") + .logHandler(System.err) + .option( + RuntimeOptions.LANGUAGE_HOME_OVERRIDE, + Paths + .get("../../test/micro-distribution/component") + .toFile + .getAbsolutePath + ) + .option(RuntimeOptions.EDITION_OVERRIDE, "0.0.0-dev") + .build() + + private val langCtx: EnsoContext = ctx .getBindings(LanguageInfo.ID) .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .asHostObject[EnsoContext]() @@ -37,19 +61,6 @@ class ImportExportTest extends AnyWordSpecLike with Matchers { langCtx.getPackageRepository.registerSyntheticPackage(namespace, packageName) - implicit private class CreateMainModule(code: String) { - def createMainModule: IR.Module = { - val module = new runtime.Module( - packageQualifiedName.createChild("Main"), - null, - code - ) - langCtx.getPackageRepository.registerModuleCreatedInRuntime(module) - langCtx.getCompiler.run(module) - module.getIr - } - } - implicit private class ProcessModule(moduleCode: String) { def processModule(moduleName: QualifiedName): IR.Module = { val module = new runtime.Module(moduleName, null, moduleCode) @@ -65,6 +76,14 @@ class ImportExportTest extends AnyWordSpecLike with Matchers { } } + before { + ctx.enter() + } + + after { + ctx.leave() + } + "Import resolution" should { "bla" in { val moduleCode = """ @@ -75,6 +94,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers { val moduleIr = moduleCode.processModule(otherModuleName) moduleIr.unwrapBindingMap.definedEntities.size shouldEqual 1 moduleIr.unwrapBindingMap.definedEntities.head.name shouldEqual "Other_Module_Type" + @unused val otherTypeDefinedEntity = moduleIr.unwrapBindingMap.definedEntities.head val mainCode = @@ -92,10 +112,10 @@ class ImportExportTest extends AnyWordSpecLike with Matchers { } val mainBindingsMap = mainIr.unwrapBindingMap mainBindingsMap.resolvedImports.size shouldEqual 1 - mainBindingsMap.resolvedImports.head.target match { - - } + @unused + val resolvedImport = mainBindingsMap.resolvedImports.head.target mainIr shouldNot be(null) } } + } From 163e53751ed79c440c73ea730c0f40191c7a0716 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 9 May 2023 18:10:35 +0200 Subject: [PATCH 11/39] Add some ImportsExportsTest --- .../test/semantic/ImportExportTest.scala | 368 ++++++++++++++++-- .../test/semantic/ImportsTest.scala | 2 - 2 files changed, 342 insertions(+), 28 deletions(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 749639695fed..ddf1880615d4 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -1,19 +1,19 @@ package org.enso.compiler.test.semantic import org.enso.compiler.core.IR +import org.enso.compiler.data.BindingsMap import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.interpreter.runtime import org.enso.interpreter.runtime.EnsoContext import org.enso.pkg.QualifiedName import org.enso.polyglot.{LanguageInfo, MethodNames, RuntimeOptions} -import org.graalvm.polyglot.{Context, Engine} +import org.graalvm.polyglot.{Context, Engine, Value} import org.scalatest.BeforeAndAfter import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike import java.io.ByteArrayOutputStream import java.nio.file.Paths -import scala.annotation.unused /** * Tests a single package with multiple modules for import/export resolution. @@ -36,7 +36,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .allowCreateThread(false) .out(out) .err(out) - .option(RuntimeOptions.LOG_LEVEL, "ALL") + .option(RuntimeOptions.LOG_LEVEL, "WARNING") .option(RuntimeOptions.DISABLE_IR_CACHES, "true") .logHandler(System.err) .option( @@ -61,49 +61,60 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter langCtx.getPackageRepository.registerSyntheticPackage(namespace, packageName) - implicit private class ProcessModule(moduleCode: String) { - def processModule(moduleName: QualifiedName): IR.Module = { + implicit private class CreateModule(moduleCode: String) { + def createModule(moduleName: QualifiedName): runtime.Module = { val module = new runtime.Module(moduleName, null, moduleCode) langCtx.getPackageRepository.registerModuleCreatedInRuntime(module) langCtx.getCompiler.run(module) - module.getIr + module } } implicit private class UnwrapBindingMap(moduleIr: IR.Module) { - def unwrapBindingMap = { + def unwrapBindingMap: BindingsMap = { moduleIr.unsafeGetMetadata(BindingAnalysis, "Should be present") } } + // TODO: Add method to execute module + private def executeModule(module: runtime.Module): Value = { + ctx.eval(module.getSource) + module.getSource + } + + before { ctx.enter() } after { ctx.leave() + out.reset() } - "Import resolution" should { - "bla" in { - val moduleCode = """ - |type Other_Module_Type - | Constructor - |""".stripMargin - val otherModuleName = packageQualifiedName.createChild("Other_Module") - val moduleIr = moduleCode.processModule(otherModuleName) + "Import/export resolution with just two modules in one library" should { + "resolve one import symbol from a module" in { + val moduleCode = + """ + |type Other_Module_Type + | Constructor + |""".stripMargin + val moduleIr = moduleCode.createModule( + packageQualifiedName.createChild("Other_Module") + ).getIr moduleIr.unwrapBindingMap.definedEntities.size shouldEqual 1 moduleIr.unwrapBindingMap.definedEntities.head.name shouldEqual "Other_Module_Type" - @unused - val otherTypeDefinedEntity = moduleIr.unwrapBindingMap.definedEntities.head + val otherTypeDefinedEntity = moduleIr.unwrapBindingMap.definedEntities.head.asInstanceOf[BindingsMap.Type] + otherTypeDefinedEntity.members.size shouldEqual 1 + otherTypeDefinedEntity.members.head.name shouldEqual "Constructor" val mainCode = s""" - |from $namespace.$packageName.Other_Module import Other_Module_Type - | - |main = Other_Module_Type.Constructor - |""".stripMargin - val mainIr = mainCode.processModule(packageQualifiedName.createChild("Main")) + |from $namespace.$packageName.Other_Module import Other_Module_Type + | + |main = Other_Module_Type.Constructor + |""".stripMargin + val mainIr = mainCode.createModule(packageQualifiedName.createChild("Main")).getIr mainIr.imports.size shouldEqual 1 mainIr.imports.head match { case importErr: IR.Error.ImportExport => @@ -111,11 +122,316 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter case _ => () } val mainBindingsMap = mainIr.unwrapBindingMap - mainBindingsMap.resolvedImports.size shouldEqual 1 - @unused - val resolvedImport = mainBindingsMap.resolvedImports.head.target - mainIr shouldNot be(null) + mainBindingsMap.resolvedImports.size shouldEqual 2 + mainBindingsMap.resolvedImports(0).target.isInstanceOf[BindingsMap.ResolvedModule] shouldBe true + mainBindingsMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedType].tp shouldEqual otherTypeDefinedEntity + } + + "resolve two types from a module" in { + val moduleIr = + """ + |type Other_Module_Type + |type Another_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + .getIr + moduleIr.unwrapBindingMap.definedEntities.size shouldEqual 2 + moduleIr.unwrapBindingMap.definedEntities(0).name shouldEqual "Other_Module_Type" + moduleIr.unwrapBindingMap.definedEntities(1).name shouldEqual "Another_Type" + + val mainIr = + s""" + |from $namespace.$packageName.Other_Module import Other_Module_Type, Another_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.unwrapBindingMap.resolvedImports.size shouldEqual 3 + mainIr.unwrapBindingMap.resolvedImports(0).target.isInstanceOf[BindingsMap.ResolvedModule] shouldBe true + mainIr.unwrapBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "Other_Module_Type" + mainIr.unwrapBindingMap.resolvedImports(2).target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "Another_Type" + } + + "resolve a constructor of a type" in { + """ + |type Other_Module_Type + | Constructor + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainIr = + s""" + |from $namespace.$packageName.Other_Module.Other_Module_Type import Constructor + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + // TODO: The second resolved import should be ResolvedConstructor + mainIr.unwrapBindingMap.resolvedImports.size shouldEqual 2 + } + + "result in error when importing mixture of existing and non-existing symbols" in { + """ + |type Existing_Type + | Constructor + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainIr = + s""" + |from $namespace.$packageName.Other_Module import Existing_Type, Non_Existing_Symbol + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.size shouldEqual 1 + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + } + + "[1] result in error when importing a method from type" in { + """ + |type Other_Type + | method self = 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainIr = + s""" + |from $namespace.$packageName.Other_Module.Other_Type import method + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.size shouldEqual 1 + // TODO: Specify the message + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + } + + "resolve static method from a module" in { + """ + |static_method = + | 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + val bBindingMap = + s""" + |import $namespace.$packageName.A_Module.static_method + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + .unwrapBindingMap + val mainBindingMap = + s""" + |from $namespace.$packageName.Other_Module import static_method + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + .unwrapBindingMap + + mainBindingMap.resolvedImports.size shouldEqual 2 + mainBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.getName.path.last shouldEqual "A_Module" + mainBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" + // TODO: The same should be true for bBindingMap + } + + "??" in { + """ + |type Other_Module_Type + |static_method = + | 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainBindingMap = + s""" + |from $namespace.$packageName.Other_Module import all + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + .unwrapBindingMap + mainBindingMap.resolvedImports.size shouldEqual 3 + mainBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.getName.path.last shouldEqual "Other_Module" + mainBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "Other_Module_Type" + mainBindingMap.resolvedImports(2).target.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" + } + + "result in an error when importing all from a type that has some methods" in { + """ + |type Other_Module_Type + | Constructor + | method self = 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainIr = + s""" + |from $namespace.$packageName.Other_Module import all + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + // TODO: Check for a message + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + } + + "result in error when trying to import all from a non-type" in { + """ + |static_method = + | 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainIr = + s""" + |from $namespace.$packageName.Other_Module.static_method import all + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + // TODO: Check for a message + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + } + + "resolve all constructors from a type" in { + """ + |type Other_Module_Type + | Constructor_1 + | Constructor_2 val1 val2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainBindingMap = + s""" + |from $namespace.$packageName.A_Module import all + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + .unwrapBindingMap + mainBindingMap.resolvedImports.size shouldEqual 3 + mainBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.unsafeAsModule().getName.path.last shouldEqual "Other_Module" + mainBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedConstructor].cons.name shouldEqual "Constructor_1" + mainBindingMap.resolvedImports(2).target.asInstanceOf[BindingsMap.ResolvedConstructor].cons.name shouldEqual "Constructor_2" + } + } + + + "Transitive import/export resolution with multiple modules in one library" should { + "not resolve symbol that is not explicitly exported" in { + """ + |type A_Type + | a_method self = 1 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + val moduleB = + s""" + |import $namespace.$packageName.A_Module.A_Type + | + |type B_Type + | b_method self = 2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val mainModule = + s""" + |from $namespace.$packageName.B_Module import A_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + moduleB.getIr.unwrapBindingMap.resolvedImports.size shouldEqual 1 + moduleB.getIr.unwrapBindingMap.resolvedImports.head.target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "A_Type" + mainModule.getIr.imports.size shouldEqual 1 + mainModule.getIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + mainModule.getIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.message should include("A_Type") + } + + "resolve symbol exported from a different module" in { + """ + |type A_Type + | a_method self = 1 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + val bModule = s""" + |import $namespace.$packageName.A_Module.A_Type + |export $namespace.$packageName.A_Module.A_Type + | + |type B_Type + | b_method self = 2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val mainModule = + s""" + |from $namespace.$packageName.B_Module import A_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + mainModule.getIr.imports.size shouldEqual 1 + mainModule.getIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + mainModule.getIr.unwrapBindingMap.resolvedImports.size shouldEqual 2 + mainModule.getIr.unwrapBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.unsafeAsModule() shouldEqual bModule + mainModule.getIr.unwrapBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "A_Type" } } + "export is not transitive" in { + s""" + |import $namespace.$packageName.A_Module.A_Type + |export $namespace.$packageName.A_Module.A_Type + | + |type A_Type + | a_method self = 1 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + s""" + |import $namespace.$packageName.A_Module.A_Type + | + |type B_Type + | b_method self = 2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val mainModule = + s""" + |from $namespace.$packageName.B_Module import A_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + + mainModule.getIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + mainModule.getIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.asInstanceOf[IR.Error.ImportExport.SymbolsDoNotExist].symbolNames shouldEqual List("A_Type") + } + + "TODO: Transitive import?" in { + s""" + |type A_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + s""" + |import $namespace.$packageName.A_Module.A_Type + |export $namespace.$packageName.A_Module.A_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val mainBindingMap = + s""" + |from $namespace.$packageName.B_Module import A_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + .unwrapBindingMap + mainBindingMap.resolvedImports.size shouldEqual 2 + mainBindingMap.resolvedImports.head.target.asInstanceOf[BindingsMap.ResolvedModule].module.unsafeAsModule().getName.path.last shouldEqual "B_Module" + mainBindingMap.resolvedImports.head.target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "A_Type" + } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala index 8c7ce0fe0826..401580269f74 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala @@ -2,8 +2,6 @@ package org.enso.interpreter.test.semantic import org.enso.interpreter.test.{InterpreterException, PackageTest} -import scala.annotation.unused - class ImportsTest extends PackageTest { "Atoms and methods" should "be available for import" in { evalTestProject("TestSimpleImports") shouldEqual 20 From 4b94353a58f2150c2887590112865e5e4cdd6788 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 11 May 2023 18:05:37 +0200 Subject: [PATCH 12/39] Fix SYmbolsImportResolution so that it can: - import all symbols from module - import all symbols from a type - recognize that a type does not exist - import just some symbols from module/type --- .../phase/SymbolsImportResolution.scala | 556 +++++++++++++----- 1 file changed, 416 insertions(+), 140 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index c827d5392dda..5a50b8c6f3a3 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -3,7 +3,8 @@ package org.enso.compiler.phase import com.oracle.truffle.api.TruffleLogger import org.enso.compiler.Compiler import org.enso.compiler.core.IR -import org.enso.compiler.core.IR.Module.Scope.Import +import org.enso.compiler.core.IR.Module.Scope.{Definition, Import} +import org.enso.compiler.core.IR.Name import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.{ImportTarget, ModuleReference} import org.enso.compiler.exception.CompilerError @@ -11,6 +12,7 @@ import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.interpreter.runtime.Module import java.util.logging.Level +import scala.collection.mutable.ListBuffer class SymbolsImportResolution(compiler: Compiler) { private val logger: TruffleLogger = compiler.context.getLogger(getClass) @@ -52,123 +54,311 @@ class SymbolsImportResolution(compiler: Compiler) { ) => assert( !bindingsMap.resolvedImports.contains(imp), - "From imports should not be resolved yet" + "From import statements should not be resolved yet" ) logger.log( Level.FINER, s"Resolving symbols [{0}] from import `{1}`", Array[Object](onlyNames.map(_.name), imp.showCode()) ) - - // Check if the import is from a type rather than from a module, i.e., - // `from Module.Type import Constructor` versus `from Module import Some_Type`. - val shouldImportFromType = compiler.getModule(importedName.name).isEmpty - - val moduleName = if (shouldImportFromType) { - importedName.parts.dropRight(1).map(_.name).mkString(".") - } else { - importedName.name - } - val typeName: Option[String] = if (shouldImportFromType) { - Some(importedName.parts.last.name) - } else { - None - } - - logger.log( - Level.FINER, - "moduleName = {0}, typeName = {1}", - Array[Object](moduleName, typeName) + tryResolveSymbolsAndUpdateBindingMap( + imp, + bindingsMap, + importedName, + Some(onlyNames) ) - val importedModule = compiler.getModule(moduleName) match { - case Some(module) => module - case None => - throw new CompilerError( - s"Module ${moduleName} should already be imported" - ) - } - - val importedModuleBindingMap = - importedModule.getIr.unsafeGetMetadata( - BindingAnalysis, - "Should be analyzed before resolving import symbols" - ) - val symbolsResolution = - tryResolveSymbols( - importedModule, - importedModuleBindingMap, - onlyNames, - typeName - ) - + case imp@Import.Module( + importedName, + _, + isAll, + None, + _, + _, + _, + _, + _ + ) if isAll => logger.log( Level.FINER, - "Got result from symbolsResolution: Resolved = {0}, Unresolved = {1}", - Array[Object]( - symbolsResolution.resolved.map(_.qualifiedName.toString), - symbolsResolution.unresolved.map(_.showCode()) - ) + "Resolving all symbols from import `{0}`", + Array[Object](imp.showCode()) + ) + tryResolveSymbolsAndUpdateBindingMap( + imp, + bindingsMap, + importedName, + None ) - - if (symbolsResolution.unresolved.nonEmpty) { - // Replace the IR with IR.Error - IR.Error.ImportExport( - ir = imp, - reason = IR.Error.ImportExport.SymbolsDoNotExist( - symbolNames = symbolsResolution.unresolved.map(_.name), - moduleName = importedName.toString - ) - ) - } else { - - val resolvedImports = - symbolsResolution.resolved.map(importTarget => { - BindingsMap.ResolvedImport( - importDef = imp, - exports = List.empty, - target = importTarget - ) - }) - - bindingsMap.resolvedImports = - bindingsMap.resolvedImports.appendedAll(resolvedImports) - imp - } case _ => imp } } + /** + * Tries to resolve symbols from the [[module]], potentially from the [[typeName]] if set. + * @param module Module from which symbols are imported. + * @param bindingsMap BindingsMap associated with the [[module]]. + * @param symbolsToImport All the symbols to be imported + * @param typeName The name of the type from which the symbols are imported. If None, + * the symbols are imported from the module. + */ private def tryResolveSymbols( module: Module, bindingsMap: BindingsMap, symbolsToImport: List[IR.Name.Literal], typeName: Option[String] ): SymbolsResolution = { - val analysedSymbols: List[Either[ImportTarget, IR.Name.Literal]] = - symbolsToImport - .map(symbolToImport => { - tryResolveFromDefinedEntities( - module, - bindingsMap, - symbolToImport, - typeName - ) match { - case Some(importTarget) => Left(importTarget) - case None => - tryResolveFromExportedSymbols(bindingsMap, symbolToImport, typeName) match { - case Some(importTarget) => Left(importTarget) - case None => Right(symbolToImport) - } + val resolvedSymbols: ListBuffer[ImportTarget] = ListBuffer.empty + for (symbolToImport <- symbolsToImport) { + tryResolveFromDefinedEntities( + module, + bindingsMap, + symbolToImport, + typeName + ) match { + case Left(importTarget) => resolvedSymbols += importTarget + case Right(_) => + tryResolveFromExportedSymbols(module, symbolToImport, typeName) match { + case Left(importTarget) => resolvedSymbols += importTarget + case Right(errorReason) => + // Report failure + return SymbolsResolution( + resolvedSymbols.toList, + Some(errorReason) + ) } + } + } + SymbolsResolution( + resolvedSymbols.toList, + None + ) + } + + /** + * From the given module bindings, finds all the static module method and type definitions. + */ + private def findAllStaticMethodsAndTypesDefinedInModule( + moduleBindings: List[IR.Module.Scope.Definition] + ): List[IR.Name.Literal] = { + logger.log( + Level.FINER, + "Finding all static methods and types in module" + ) + val symbolsToImport: ListBuffer[IR.Name.Literal] = ListBuffer.empty + moduleBindings.foreach { + case Definition.Method.Explicit(methodRef, _, _, _, _) + if methodRef.methodName.isInstanceOf[Name.Literal] => + symbolsToImport += methodRef.methodName.asInstanceOf[Name.Literal] + case Definition.Method.Binding(methodRef, _, _, _, _, _) + if methodRef.methodName.isInstanceOf[Name.Literal] => + symbolsToImport += methodRef.methodName.asInstanceOf[Name.Literal] + case Definition.Type(name, _, _, _, _, _) + if name.isInstanceOf[Name.Literal] => + symbolsToImport += name.asInstanceOf[Name.Literal] + case _ => () + } + symbolsToImport.toList + } + + private def findAllExportedSymbolsFromModule( + importedModuleBindingMap: BindingsMap + ): List[BindingsMap.ImportTarget] = { + val exportedSymbols: ListBuffer[BindingsMap.ImportTarget] = ListBuffer.empty + importedModuleBindingMap.exportedSymbols.foreach { + case (_, resolvedNames) => + resolvedNames.foreach { + case resolvedType: BindingsMap.ResolvedType => exportedSymbols += resolvedType + case resolvedModule: BindingsMap.ResolvedModule => exportedSymbols += resolvedModule + case resolvedMethod: BindingsMap.ResolvedMethod => exportedSymbols += resolvedMethod + case resolvedCtor: BindingsMap.ResolvedConstructor => exportedSymbols += resolvedCtor + } + } + exportedSymbols.toList + } + + /** + * Finds all teh constructors from a type. Returns empty list if there is no such type, + * or if the type does not have any constructors. + * @param moduleBindings + * @param typeName + * @return + */ + private def findAllConstructorsFromType( + moduleBindings: List[IR.Module.Scope.Definition], + typeName: String + ): List[IR.Name.Literal] = { + logger.log( + Level.FINEST, + "Finding all constructors from type `{0}`", + Array[Object](typeName) + ) + val ctors: ListBuffer[IR.Name.Literal] = ListBuffer.empty + moduleBindings.foreach { + case Definition.Type(name, _, members, _, _, _) if name.name == typeName => + members.foreach(_.name match { + case ctorName: IR.Name.Literal => ctors += ctorName + case _ => () + } + ) + case _ => () + } + // ctors buffer might be empty, in case the type is not in the module. + // That is fine, the error will be reported later. + ctors.toList + } + + /** + * TODO: Reword + * Tries to resolve all the symbols given in [[symbolsToImport]] from the module + * `importedModule`, potentially from the type given in `typeName` and updates + * the imported module's BindingMap accordingly. + * + * In case of a failed resolution, an import error reason IR is returned and no + * metadata is updated. When the resolution is successful, the given binding map + * is updated and the [[importStatement]] is returned. + * + * + * @param importStatement The import statement that is being analyzed + * @param bindingMap The binding map of the current module from which we are analyzing + * the import. This binding map may be updated as a result of this + * operation. + * @param symbolsToImport The symbols that are being imported from the module or from the type. + * If None, all the symbols should be imported. + * @return Either the import statement given in `importStatement` in case that all the + * symbols were successfuly resolved, or an import error reason IR when some + * symbols failed to resolve. + */ + private def tryResolveSymbolsAndUpdateBindingMap( + importStatement: IR.Module.Scope.Import.Module, + bindingMap: BindingsMap, + importedName: IR.Name.Qualified, + symbolsToImport: Option[List[IR.Name.Literal]], + ): IR.Module.Scope.Import = { + // Check if the import is from a type rather than from a module, i.e., + // `from Module.Type import Constructor` versus `from Module import Some_Type`. + val shouldImportFromType = compiler.getModule(importedName.name).isEmpty + + val moduleName = if (shouldImportFromType) { + importedName.parts.dropRight(1).map(_.name).mkString(".") + } else { + importedName.name + } + val typeName: Option[String] = if (shouldImportFromType) { + Some(importedName.parts.last.name) + } else { + None + } + logger.log( + Level.FINEST, + "moduleName = {0}, typeName = {1}", + Array[Object](moduleName, typeName) + ) + + val importedModule = compiler.getModule(moduleName) match { + case Some(module) => module + case None => + throw new CompilerError( + s"Module ${moduleName} should already be imported" + ) + } + + val importedBindingMap = + importedModule.getIr.unsafeGetMetadata(BindingAnalysis, "BindingMap should be present") + + val symbolsToImportUnwrapped: List[IR.Name.Literal] = { + symbolsToImport match { + case Some(symbols) => symbols + case None => + typeName match { + case Some(typeNameUnwrapped) => + findAllConstructorsFromType( + importedModule.getIr.bindings, + typeNameUnwrapped + ) + case None => + // TODO: Find also all exported symbols from importedModule + findAllStaticMethodsAndTypesDefinedInModule( + importedModule.getIr.bindings + ) + } + } + } + + logger.log( + Level.FINER, + "Trying to resolve symbols {0} from type {1} from module {2}", + Array[Object](symbolsToImportUnwrapped, typeName, importedModule.getName) + ) + + val symbolsResolution = { + // Trying to import any symbols from a non-existing type is an error + if (typeName.isDefined && !typeExists(typeName.get, importedBindingMap)) { + SymbolsResolution( + resolved = List.empty, + errorReason = Some( + IR.Error.ImportExport.TypeDoesNotExist( + typeName.get, + moduleName + ) + ) + ) + } else { + tryResolveSymbols( + importedModule, + importedBindingMap, + symbolsToImportUnwrapped, + typeName + ) + } + } + + logger.log( + Level.FINER, + "Got result from symbolsResolution: Resolved = {0}, Error = {1}", + Array[Object]( + symbolsResolution.resolved.map(_.qualifiedName.toString), + symbolsResolution.errorReason + ) + ) + + + + if (symbolsResolution.errorReason.isDefined) { + // Replace the IR with IR.Error and don't update the binding map + IR.Error.ImportExport( + ir = importStatement, + reason = symbolsResolution.errorReason.get + ) + } else { + val resolvedImports = + symbolsResolution.resolved.map(importTarget => { + BindingsMap.ResolvedImport( + importDef = importStatement, + exports = List.empty, + target = importTarget + ) }) - val unresolvedSymbols: List[IR.Name.Literal] = - analysedSymbols.collect { case Right(name) => name } - val resolvedSymbols: List[ImportTarget] = - analysedSymbols.collect { case Left(importTarget) => importTarget } + // Append all exported symbols from the module to the resolved imports. + // These are transitively imported symbols + val importingAllSymbols = symbolsToImport.isEmpty + if (importingAllSymbols) { + val additionalExportedSymbols = findAllExportedSymbolsFromModule(importedBindingMap) + logger.log( + Level.FINEST, + "Importing all symbols from module {0}, will append all the exported symbols from the module, " + + "these are transitive imports: {1}", + Array[Object](moduleName, additionalExportedSymbols) + ) + bindingMap.resolvedImports = + bindingMap.resolvedImports.appendedAll(additionalExportedSymbols) + } - SymbolsResolution(resolvedSymbols, unresolvedSymbols) + bindingMap.resolvedImports = + bindingMap.resolvedImports.appendedAll(resolvedImports) + importStatement + } } /** @@ -176,39 +366,59 @@ class SymbolsImportResolution(compiler: Compiler) { * the given `bindingMap`. * @param symbolToImport Symbol to import from a type or from a module * @param typeName If defined, a member from a type is imported, rather than a member of a module. - * @return + * @return ImportTarget in case of successful resolution or an error reason */ private def tryResolveFromDefinedEntities( module: Module, bindingMap: BindingsMap, symbolToImport: IR.Name.Literal, typeName: Option[String] - ): Option[ImportTarget] = { + ): Either[ImportTarget, IR.Error.ImportExport.Reason] = { + logger.log( + Level.FINEST, + "Trying to resolve symbol {0} from type {1} from module {2} defined entities", + Array[Object](symbolToImport, typeName, module.getName) + ) if (typeName.isDefined) { - // A member from a type is imported - val foundType: Option[BindingsMap.Type] = + // A member `symbolToImport` from a type `typeName` is imported from `module`. + val foundType: BindingsMap.Type = bindingMap.definedEntities.find(_.name == typeName.get) match { case Some(definedEntity) => definedEntity match { - case tp: BindingsMap.Type => Some(tp) + case tp: BindingsMap.Type => tp case _ => throw new CompilerError(s"Expected BindingsMap.Type, got $definedEntity") } - case None => None - } - // TODO: Resolve member - foundType match { - case Some(tp) => + case None => + return Right( + IR.Error.ImportExport.TypeDoesNotExist(typeName.get, module.getName.toString) + ) + } + // Resolve member of a type + foundType.members.find(_.name == symbolToImport.name) match { + case Some(constructor) => logger.log( Level.FINER, - "Resolved type {0} from definedEntities in module {1}", - Array[Object](tp, module) + "Resolved constructor {0} from type {1} from definedEntities in module {2}", + Array[Object](constructor, foundType, module) ) - Some( - BindingsMap.ResolvedType(ModuleReference.Concrete(module), tp) + Left( + BindingsMap.ResolvedConstructor( + BindingsMap.ResolvedType(ModuleReference.Concrete(module), foundType), + constructor + ) + ) + case None => + // At this point, we cannot tell whether the symbol is not defined within the type at all, + // or whether it is an instance method. + Right( + IR.Error.ImportExport.NoSuchConstructor( + typeName.get, + symbolToImport.name + ) ) - case None => None } } else { + // We want to import only `symbolToImport` from `module` bindingMap.definedEntities.find(_.name == symbolToImport.name) match { case Some(definedEntity) => definedEntity match { @@ -218,25 +428,31 @@ class SymbolsImportResolution(compiler: Compiler) { "Resolved type {0} from definedEntities in module {1}", Array[Object](tp, module) ) - Some( + Left( BindingsMap.ResolvedType(ModuleReference.Concrete(module), tp) ) - case _: BindingsMap.ModuleMethod => - // TODO: Resolve to a method, not to a module - Some( - BindingsMap.ResolvedModule( - ModuleReference.Concrete(module) + case method: BindingsMap.ModuleMethod => + Left( + BindingsMap.ResolvedMethod( + ModuleReference.Concrete(module), + method ) ) case BindingsMap.PolyglotSymbol(_) => // TODO: Resolve to a polyglot symbol, not to a module - Some( + Left( BindingsMap.ResolvedModule( ModuleReference.Concrete(module) ) ) } - case None => None + case None => + Right( + IR.Error.ImportExport.SymbolsDoNotExist( + List(symbolToImport.name), + module.getName.toString + ) + ) } } } @@ -245,42 +461,102 @@ class SymbolsImportResolution(compiler: Compiler) { * Tries to resolve a symbol (of a type or a module) from exported symbols within * the given `bindingMap`. * @param bindingMap + * @param module A BindingMap of this module is searched for the symbols * @param symbolToImport Symbol to import from a type or from a module * @param typeName If defined, a member from a type is imported, rather than a member of a module. - * @return + * @return Either ImportTarget in case of successful resolution, or an import error reason IR. */ private def tryResolveFromExportedSymbols( - bindingMap: BindingsMap, + module: Module, symbolToImport: IR.Name.Literal, typeName: Option[String] - ): Option[ImportTarget] = { - val exportedNamesToSearch: Option[List[BindingsMap.ResolvedName]] = - if (typeName.isDefined) { - bindingMap.exportedSymbols.get(typeName.get) - } else { - bindingMap.exportedSymbols.get(symbolToImport.name) + ): Either[ImportTarget, IR.Error.ImportExport.Reason] = { + logger.log( + Level.FINEST, + "Trying to resolve symbol {0} from type {1} from module {2} exported symbols", + Array[Object](symbolToImport, typeName, module.getName) + ) + val bindingMap = module.getIr.unsafeGetMetadata( + BindingAnalysis, "Should be here" + ) + if (typeName.isDefined) { + bindingMap.exportedSymbols.get(typeName.get) match { + case Some(foundNamesForTypeName) => + foundNamesForTypeName.collectFirst { case x: BindingsMap.ResolvedType => x } match { + case Some(resolvedType) => + // Find `symbolToImport` member from `resolvedType` + val foundConstructor = resolvedType.tp.members.find(_.name == symbolToImport.name) + foundConstructor match { + case Some(ctor) => Left(BindingsMap.ResolvedConstructor(resolvedType, ctor)) + case None => + Right( + IR.Error.ImportExport.NoSuchConstructor( + resolvedType.tp.name, + symbolToImport.name + ) + ) + } + case None => + Right( + IR.Error.ImportExport.TypeDoesNotExist( + symbolToImport.name, + module.getName.toString + ) + ) + } + case None => + Right( + IR.Error.ImportExport.TypeDoesNotExist( + symbolToImport.name, + module.getName.toString + ) + ) } - exportedNamesToSearch match { - case Some(resolvedNames) => - logger.log( - Level.FINER, - "Found symbols [{0}] from exportedSymbols in bindings map from module {1}", - Array[Object](resolvedNames, bindingMap.currentModule.getName.toString) - ) - val resolvedImportTargets: List[ImportTarget] = - resolvedNames.collect { - case resolvedModule: BindingsMap.ResolvedModule => resolvedModule - case resolvedType: BindingsMap.ResolvedType => resolvedType - case BindingsMap.ResolvedConstructor(resolvedType, _) => resolvedType + } else { + bindingMap.exportedSymbols.get(symbolToImport.name) match { + case Some(resolvedNames) => + val resolvedImportTargets: List[ImportTarget] = + resolvedNames.collect { + case resolvedModule: BindingsMap.ResolvedModule => resolvedModule + case resolvedType: BindingsMap.ResolvedType => resolvedType + case resolvedMethod: BindingsMap.ResolvedMethod => resolvedMethod + case resolvedCtor: BindingsMap.ResolvedConstructor => resolvedCtor + } + // Take the first resolvedName that is also ImportTarget + resolvedImportTargets.headOption match { + case Some(resolvedImportTarget) => Left(resolvedImportTarget) + case None => Right( + IR.Error.ImportExport.SymbolsDoNotExist( + List(symbolToImport.name), + module.getName.toString + ) + ) } - // Take the first resolvedName that is also ImportTarget - resolvedImportTargets.headOption - case None => None + case None => + Right( + IR.Error.ImportExport.SymbolsDoNotExist( + List(symbolToImport.name), + module.getName.toString + ) + ) + } } } + private def typeExists( + typeName: String, + importedBindingMap: BindingsMap + ): Boolean = { + importedBindingMap.definedEntities.exists(entity => { + entity match { + case BindingsMap.Type(name, _, _) if name == typeName => true + case _ => false + } + }) + } + private case class SymbolsResolution( resolved: List[ImportTarget], - unresolved: List[IR.Name.Literal] + errorReason: Option[IR.Error.ImportExport.Reason] ) } From 3f1b2bf19e081697252935db05b65e2340bb4b14 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 11 May 2023 18:06:00 +0200 Subject: [PATCH 13/39] ImportResolver can can resolve a type or a static method from a module --- .../enso/compiler/phase/ImportResolver.scala | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala index 72a8eb481d41..d430d4776685 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala @@ -4,12 +4,7 @@ import org.enso.compiler.Compiler import org.enso.compiler.core.IR import org.enso.compiler.core.IR.Module.Scope.{Export, Import} import org.enso.compiler.data.BindingsMap -import org.enso.compiler.data.BindingsMap.{ - ModuleReference, - ResolvedModule, - ResolvedType, - Type -} +import org.enso.compiler.data.BindingsMap.{ImportTarget, ModuleReference, ResolvedMethod, ResolvedModule, ResolvedType, Type} import org.enso.compiler.exception.CompilerError import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.editions.LibraryName @@ -132,12 +127,12 @@ class ImportResolver(compiler: Compiler) { go(mutable.Stack(module), mutable.Set(), mutable.Set()) } - private def tryResolveAsType( + private def tryResolveAsTypeOrStaticMethod( name: IR.Name.Qualified - ): Option[ResolvedType] = { - val tp = name.parts.last.name - val mod = name.parts.dropRight(1).map(_.name).mkString(".") - compiler.getModule(mod).flatMap { mod => + ): Option[ImportTarget] = { + val nameToImportFromModule = name.parts.last.name + val moduleName = name.parts.dropRight(1).map(_.name).mkString(".") + compiler.getModule(moduleName).flatMap { mod => compiler.ensureParsed(mod) mod.getIr .unsafeGetMetadata( @@ -145,9 +140,12 @@ class ImportResolver(compiler: Compiler) { "impossible: just ensured it's parsed" ) .definedEntities - .find(_.name == tp) - .collect { case t: Type => - ResolvedType(ModuleReference.Concrete(mod), t) + .find(_.name == nameToImportFromModule) + .collect { + case t: Type => + ResolvedType(ModuleReference.Concrete(mod), t) + case m: BindingsMap.ModuleMethod => + ResolvedMethod(ModuleReference.Concrete(mod), m) } } } @@ -242,9 +240,9 @@ class ImportResolver(compiler: Compiler) { ) ) case None => - tryResolveAsType(imp.name) match { - case Some(tp) => - (imp, Some(BindingsMap.ResolvedImport(imp, exp, tp))) + tryResolveAsTypeOrStaticMethod(imp.name) match { + case Some(importTarget) => + (imp, Some(BindingsMap.ResolvedImport(imp, exp, importTarget))) case None => ( IR.Error.ImportExport( From bc0eebfca41bf7ccd260a684de954365f202a49f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 11 May 2023 18:06:26 +0200 Subject: [PATCH 14/39] Add some error messages to IR's ImportExport error reasons --- .../main/scala/org/enso/compiler/core/IR.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala index f69ef1fdbc51..4a99fba6fb93 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala @@ -8782,6 +8782,14 @@ object IR { override def message: String = s"The module $name does not exist." } + case class TypeDoesNotExist( + typeName: String, + moduleName: String + ) extends Reason { + override def message: String = + s"The type $typeName does not exist in module $moduleName" + } + case class SymbolsDoNotExist( symbolNames: List[String], moduleName: String @@ -8790,6 +8798,13 @@ object IR { s"The symbols $symbolNames (modules or types) do not exist in module $moduleName." } + case class NoSuchConstructor( + typeName: String, + constructorName: String + ) extends Reason { + override def message: String = + s"No such constructor '$constructorName' in type $typeName" + } } /** An erroneous import or export statement. From e3c22d6ab437c93d17933a16e728135fb0a5b102 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 11 May 2023 18:07:24 +0200 Subject: [PATCH 15/39] ResolvedConstructor and ResolvedMethod can also be ImportTarget --- .../org/enso/compiler/codegen/IrToTruffle.scala | 4 +++- .../scala/org/enso/compiler/data/BindingsMap.scala | 14 ++++++++++++-- .../enso/compiler/phase/ExportsResolution.scala | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index e0724a3f27e5..b390c12e3d72 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -177,7 +177,9 @@ class IrToTruffle( bindingsMap.resolvedImports.foreach { imp => imp.target match { - case BindingsMap.ResolvedType(_, _) => + case _: BindingsMap.ResolvedType => + case _: BindingsMap.ResolvedMethod => + case _: BindingsMap.ResolvedConstructor => case ResolvedModule(module) => val mod = module.unsafeAsModule() val scope: ModuleScope = imp.importDef.onlyNames diff --git a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala index d3f2d504c887..20e71ada7906 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -813,7 +813,8 @@ object BindingsMap { * @param cons a representation of the constructor. */ case class ResolvedConstructor(tpe: ResolvedType, cons: Cons) - extends ResolvedName { + extends ResolvedName + with ImportTarget { /** @inheritdoc */ override def toAbstract: ResolvedConstructor = { @@ -833,6 +834,10 @@ object BindingsMap { /** @inheritdoc */ override def module: ModuleReference = tpe.module + + override def findExportedSymbolsFor(name: String): List[ResolvedName] = ??? + + override def exportedSymbols: Map[String, List[ResolvedName]] = ??? } /** A representation of a name being resolved to a module. @@ -876,7 +881,8 @@ object BindingsMap { * @param method the method representation. */ case class ResolvedMethod(module: ModuleReference, method: ModuleMethod) - extends ResolvedName { + extends ResolvedName + with ImportTarget { /** @inheritdoc */ override def toAbstract: ResolvedMethod = { @@ -911,6 +917,10 @@ object BindingsMap { override def qualifiedName: QualifiedName = module.getName.createChild(method.name) + + override def findExportedSymbolsFor(name: String): List[ResolvedName] = ??? + + override def exportedSymbols: Map[String, List[ResolvedName]] = ??? } /** A representation of a name being resolved to a polyglot symbol. diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala index fcdbf6fb267d..c456203afe62 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala @@ -159,6 +159,8 @@ class ExportsResolution { exports.foreach { case (module, exports) => module match { case _: BindingsMap.ResolvedType => + case _: BindingsMap.ResolvedConstructor => + case _: BindingsMap.ResolvedMethod => case ResolvedModule(module) => getBindings(module.unsafeAsModule()).resolvedExports = exports.map(ex => ex.copy(symbols = ex.symbols.optimize)) From 6f4df8c49ad42020b402304da2223f888a0468f6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 11 May 2023 18:08:59 +0200 Subject: [PATCH 16/39] Add more import/export tests --- .../test/semantic/ImportExportTest.scala | 177 +++++++++++++----- 1 file changed, 125 insertions(+), 52 deletions(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index ddf1880615d4..023060126cf2 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -7,7 +7,7 @@ import org.enso.interpreter.runtime import org.enso.interpreter.runtime.EnsoContext import org.enso.pkg.QualifiedName import org.enso.polyglot.{LanguageInfo, MethodNames, RuntimeOptions} -import org.graalvm.polyglot.{Context, Engine, Value} +import org.graalvm.polyglot.{Context, Engine} import org.scalatest.BeforeAndAfter import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike @@ -76,13 +76,6 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } } - // TODO: Add method to execute module - private def executeModule(module: runtime.Module): Value = { - ctx.eval(module.getSource) - module.getSource - } - - before { ctx.enter() } @@ -92,7 +85,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter out.reset() } - "Import/export resolution with just two modules in one library" should { + "Import resolution with just two modules" should { "resolve one import symbol from a module" in { val moduleCode = """ @@ -189,7 +182,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true } - "[1] result in error when importing a method from type" in { + "result in error when importing a method from type" in { """ |type Other_Type | method self = 42 @@ -204,8 +197,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.size shouldEqual 1 - // TODO: Specify the message - mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + mainIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.asInstanceOf[IR.Error.ImportExport.NoSuchConstructor] shouldEqual IR.Error.ImportExport.NoSuchConstructor("Other_Type", "method") } "resolve static method from a module" in { @@ -215,30 +207,35 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |""" .stripMargin .createModule(packageQualifiedName.createChild("A_Module")) - val bBindingMap = + val bIr = s""" |import $namespace.$packageName.A_Module.static_method |""" .stripMargin .createModule(packageQualifiedName.createChild("B_Module")) .getIr - .unwrapBindingMap - val mainBindingMap = + val mainIr = s""" - |from $namespace.$packageName.Other_Module import static_method + |from $namespace.$packageName.A_Module import static_method |""" .stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - .unwrapBindingMap + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + bIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + val bBindingMap = bIr.unwrapBindingMap mainBindingMap.resolvedImports.size shouldEqual 2 - mainBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.getName.path.last shouldEqual "A_Module" + mainBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.getName.item shouldEqual "A_Module" mainBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" - // TODO: The same should be true for bBindingMap + // In B_Module, we only have ResolvedMethod in the resolvedImports, there is no ResolvedModule + // But that does not matter. + bBindingMap.resolvedImports.size shouldEqual 1 + bBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" } - "??" in { + "resolve types and methods when importing all from a module" in { """ |type Other_Module_Type |static_method = @@ -254,13 +251,17 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .createModule(packageQualifiedName.createChild("Main")) .getIr .unwrapBindingMap - mainBindingMap.resolvedImports.size shouldEqual 3 - mainBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.getName.path.last shouldEqual "Other_Module" - mainBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "Other_Module_Type" - mainBindingMap.resolvedImports(2).target.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" + + mainBindingMap.resolvedImports.filter(imp => { + imp.target match { + case BindingsMap.ResolvedType(_, tp) if tp.name == "Other_Module_Type" => true + case BindingsMap.ResolvedMethod(_, method) if method.name == "static_method" => true + case _ => false + } + }) should have size 2 } - "result in an error when importing all from a type that has some methods" in { + "resolve only constructors when importing all symbols from a type (1)" in { """ |type Other_Module_Type | Constructor @@ -268,15 +269,78 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |""" .stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) + val mainBindingMap = + s""" + |from $namespace.$packageName.Other_Module.Other_Module_Type import all + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + .unwrapBindingMap + + mainBindingMap.resolvedImports.filter(imp => { + imp.target match { + case BindingsMap.ResolvedConstructor(_, ctor) if ctor.name == "Constructor" => true + case _ => false + } + } + ) should have size 1 + } + + "resolve only constructors when importing all symbols from a type (2)" in { + """ + |type Other_Module_Type + | Constructor_1 + | Constructor_2 val1 val2 + | method self = 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" - |from $namespace.$packageName.Other_Module import all + |from $namespace.$packageName.Other_Module.Other_Module_Type import all |""" .stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - // TODO: Check for a message - mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + mainBindingMap.resolvedImports.filter(imp => { + imp.target match { + case BindingsMap.ResolvedConstructor(_, ctor) + if ctor.name == "Constructor_1" || ctor.name == "Constructor_2" => true + case _ => false + } + } + ) should have size 2 + } + + "resolve all constructors from a type" in { + """ + |type Other_Module_Type + | Constructor_1 + | Constructor_2 val1 val2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainIr = + s""" + |from $namespace.$packageName.Other_Module.Other_Module_Type import all + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + mainBindingMap.resolvedImports.filter(imp => { + imp.target match { + case BindingsMap.ResolvedConstructor(_, ctor) + if ctor.name == "Constructor_1" || ctor.name == "Constructor_2" => true + case _ => false + } + } + ) should have size 2 } "result in error when trying to import all from a non-type" in { @@ -293,31 +357,26 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - // TODO: Check for a message - mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + mainIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.asInstanceOf[IR.Error.ImportExport.TypeDoesNotExist].typeName shouldEqual "static_method" } - "resolve all constructors from a type" in { + "result in error when trying to import anything from a non-existing symbol" in { """ - |type Other_Module_Type - | Constructor_1 - | Constructor_2 val1 val2 + |# Left blank on purpose |""" .stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) - val mainBindingMap = + val mainIr = s""" - |from $namespace.$packageName.A_Module import all + |from $namespace.$packageName.Other_Module.Non_Existing_Symbol import all |""" .stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - .unwrapBindingMap - mainBindingMap.resolvedImports.size shouldEqual 3 - mainBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.unsafeAsModule().getName.path.last shouldEqual "Other_Module" - mainBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedConstructor].cons.name shouldEqual "Constructor_1" - mainBindingMap.resolvedImports(2).target.asInstanceOf[BindingsMap.ResolvedConstructor].cons.name shouldEqual "Constructor_2" + mainIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason + .asInstanceOf[IR.Error.ImportExport.ModuleDoesNotExist].name should include ("Non_Existing_Symbol") } + } @@ -338,17 +397,18 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |""" .stripMargin .createModule(packageQualifiedName.createChild("B_Module")) - val mainModule = + val mainModuleIr = s""" |from $namespace.$packageName.B_Module import A_Type |""" .stripMargin .createModule(packageQualifiedName.createChild("Main")) + .getIr moduleB.getIr.unwrapBindingMap.resolvedImports.size shouldEqual 1 moduleB.getIr.unwrapBindingMap.resolvedImports.head.target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "A_Type" - mainModule.getIr.imports.size shouldEqual 1 - mainModule.getIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true - mainModule.getIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.message should include("A_Type") + mainModuleIr.imports.size shouldEqual 1 + mainModuleIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + mainModuleIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.message should include("A_Type") } "resolve symbol exported from a different module" in { @@ -358,7 +418,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |""" .stripMargin .createModule(packageQualifiedName.createChild("A_Module")) - val bModule = s""" + s""" |import $namespace.$packageName.A_Module.A_Type |export $namespace.$packageName.A_Module.A_Type | @@ -367,17 +427,30 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |""" .stripMargin .createModule(packageQualifiedName.createChild("B_Module")) - val mainModule = + val mainIr = s""" |from $namespace.$packageName.B_Module import A_Type |""" .stripMargin .createModule(packageQualifiedName.createChild("Main")) - mainModule.getIr.imports.size shouldEqual 1 - mainModule.getIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false - mainModule.getIr.unwrapBindingMap.resolvedImports.size shouldEqual 2 - mainModule.getIr.unwrapBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.unsafeAsModule() shouldEqual bModule - mainModule.getIr.unwrapBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "A_Type" + .getIr + mainIr.imports.size shouldEqual 1 + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + // A_Type is exported + mainBindingMap.resolvedImports.exists(imp => { + imp.target match { + case BindingsMap.ResolvedType(_, tp) if tp.name == "A_Type" => true + case _ => false + } + }) should be(true) + // B_Type is not imported + mainBindingMap.resolvedImports.exists(imp => { + imp.target match { + case BindingsMap.ResolvedType(_, tp) if tp.name == "B_Type" => true + case _ => false + } + }) should be(false) } } From a283dc89285456ed03b1d63e97e8f16467207a32 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 14:57:00 +0200 Subject: [PATCH 17/39] Add final version of ImportExportTest --- .../test/semantic/ImportExportTest.scala | 345 +++++++++++++----- 1 file changed, 258 insertions(+), 87 deletions(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 023060126cf2..28df03e1616f 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -36,7 +36,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .allowCreateThread(false) .out(out) .err(out) - .option(RuntimeOptions.LOG_LEVEL, "WARNING") + .option(RuntimeOptions.LOG_LEVEL, "ALL") .option(RuntimeOptions.DISABLE_IR_CACHES, "true") .logHandler(System.err) .option( @@ -235,7 +235,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter bBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" } - "resolve types and methods when importing all from a module" in { + "resolve types and static module methods when importing all from a module" in { """ |type Other_Module_Type |static_method = @@ -261,6 +261,33 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter }) should have size 2 } + "not resolve types and static module methods when importing all from a module" in { + """ + |type Other_Module_Type + |static_method = + | 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainBindingMap = + s""" + |from $namespace.$packageName.Other_Module import all + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + .unwrapBindingMap + + mainBindingMap.resolvedImports.filter(imp => { + imp.target match { + case BindingsMap.ResolvedType(_, tp) if tp.name == "Other_Module_Type" => true + case BindingsMap.ResolvedMethod(_, method) if method.name == "static_method" => true + case _ => false + } + } + ) should have size 2 + } + "resolve only constructors when importing all symbols from a type (1)" in { """ |type Other_Module_Type @@ -343,6 +370,51 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter ) should have size 2 } + "resolve exactly two constructors from a type" in { + """ + |type Other_Module_Type + | Constructor_1 + | Constructor_2 val1 val2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainIr = + s""" + |from $namespace.$packageName.Other_Module.Other_Module_Type import Constructor_1, Constructor_2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + val resolvedImportTargets = mainBindingMap.resolvedImports.map(_.target) + resolvedImportTargets + .collect { case rc: BindingsMap.ResolvedConstructor => rc } + .map(_.cons.name) should contain only("Constructor_1", "Constructor_2") + } + + "result in error when trying to import mix of constructors and method from a type" in { + """ + |type Other_Module_Type + | Constructor_1 + | Constructor_2 val1 val2 + | method self = 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Other_Module")) + val mainIr = + s""" + |from $namespace.$packageName.Other_Module.Other_Module_Type import Constructor_1, method, Constructor_2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + mainIr.imports.head.asInstanceOf[IR.Error.ImportExport] + .reason.asInstanceOf[IR.Error.ImportExport.NoSuchConstructor] shouldEqual + IR.Error.ImportExport.NoSuchConstructor("Other_Module_Type", "method") + } + "result in error when trying to import all from a non-type" in { """ |static_method = @@ -377,59 +449,53 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .asInstanceOf[IR.Error.ImportExport.ModuleDoesNotExist].name should include ("Non_Existing_Symbol") } - } - - - "Transitive import/export resolution with multiple modules in one library" should { - "not resolve symbol that is not explicitly exported" in { + "[3] resolve all symbols from transitively exported type" in { """ |type A_Type + | A_Constructor | a_method self = 1 |""" .stripMargin .createModule(packageQualifiedName.createChild("A_Module")) - val moduleB = - s""" - |import $namespace.$packageName.A_Module.A_Type - | - |type B_Type - | b_method self = 2 - |""" - .stripMargin - .createModule(packageQualifiedName.createChild("B_Module")) - val mainModuleIr = + s""" + |import $namespace.$packageName.A_Module.A_Type + |export $namespace.$packageName.A_Module.A_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val mainIr = s""" - |from $namespace.$packageName.B_Module import A_Type + |from $namespace.$packageName.B_Module.A_Type import all |""" .stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - moduleB.getIr.unwrapBindingMap.resolvedImports.size shouldEqual 1 - moduleB.getIr.unwrapBindingMap.resolvedImports.head.target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "A_Type" - mainModuleIr.imports.size shouldEqual 1 - mainModuleIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true - mainModuleIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.message should include("A_Type") + mainIr.imports.size shouldEqual 1 + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + val resolvedImportTargets = + mainBindingMap.resolvedImports.map(_.target) + resolvedImportTargets + .collect { case c: BindingsMap.ResolvedConstructor => c } + .map(_.cons.name) should contain theSameElementsAs List("A_Constructor") } - "resolve symbol exported from a different module" in { + "[4] resolve constructor from transitively exported type" in { """ |type A_Type - | a_method self = 1 + | A_Constructor |""" .stripMargin .createModule(packageQualifiedName.createChild("A_Module")) s""" |import $namespace.$packageName.A_Module.A_Type |export $namespace.$packageName.A_Module.A_Type - | - |type B_Type - | b_method self = 2 |""" .stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val mainIr = s""" - |from $namespace.$packageName.B_Module import A_Type + |from $namespace.$packageName.B_Module.A_Type import A_Constructor |""" .stripMargin .createModule(packageQualifiedName.createChild("Main")) @@ -437,74 +503,179 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter mainIr.imports.size shouldEqual 1 mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false val mainBindingMap = mainIr.unwrapBindingMap - // A_Type is exported - mainBindingMap.resolvedImports.exists(imp => { - imp.target match { - case BindingsMap.ResolvedType(_, tp) if tp.name == "A_Type" => true - case _ => false - } - }) should be(true) - // B_Type is not imported - mainBindingMap.resolvedImports.exists(imp => { - imp.target match { - case BindingsMap.ResolvedType(_, tp) if tp.name == "B_Type" => true - case _ => false - } - }) should be(false) + val resolvedImportTargets = + mainBindingMap.resolvedImports.map(_.target) + resolvedImportTargets + .collect { case c: BindingsMap.ResolvedConstructor => c } + .map(_.cons.name) should contain theSameElementsAs List("A_Constructor") } - } - "export is not transitive" in { - s""" - |import $namespace.$packageName.A_Module.A_Type - |export $namespace.$packageName.A_Module.A_Type - | - |type A_Type - | a_method self = 1 - |""" - .stripMargin - .createModule(packageQualifiedName.createChild("A_Module")) - s""" - |import $namespace.$packageName.A_Module.A_Type - | - |type B_Type - | b_method self = 2 - |""" - .stripMargin - .createModule(packageQualifiedName.createChild("B_Module")) - val mainModule = + "export is not transitive" in { s""" - |from $namespace.$packageName.B_Module import A_Type + |import $namespace.$packageName.A_Module.A_Type + |export $namespace.$packageName.A_Module.A_Type + | + |type A_Type + | a_method self = 1 |""" .stripMargin - .createModule(packageQualifiedName.createChild("Main")) + .createModule(packageQualifiedName.createChild("A_Module")) + s""" + |import $namespace.$packageName.A_Module.A_Type + | + |type B_Type + | b_method self = 2 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val mainModule = + s""" + |from $namespace.$packageName.B_Module import A_Type + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("Main")) - mainModule.getIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true - mainModule.getIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.asInstanceOf[IR.Error.ImportExport.SymbolsDoNotExist].symbolNames shouldEqual List("A_Type") + mainModule.getIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + mainModule.getIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.asInstanceOf[IR.Error.ImportExport.SymbolsDoNotExist].symbolNames shouldEqual List("A_Type") + } } - "TODO: Transitive import?" in { - s""" - |type A_Type - |""" + "Import resolution for three modules" should { + ctx.enter() + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""" .stripMargin .createModule(packageQualifiedName.createChild("A_Module")) - s""" - |import $namespace.$packageName.A_Module.A_Type - |export $namespace.$packageName.A_Module.A_Type - |""" - .stripMargin + + val bIr = s""" + |from $namespace.$packageName.A_Module import all + |from $namespace.$packageName.A_Module export static_method + | + |type B_Type + | B_Constructor val + |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) - val mainBindingMap = - s""" - |from $namespace.$packageName.B_Module import A_Type - |""" - .stripMargin - .createModule(packageQualifiedName.createChild("Main")) - .getIr - .unwrapBindingMap - mainBindingMap.resolvedImports.size shouldEqual 2 - mainBindingMap.resolvedImports.head.target.asInstanceOf[BindingsMap.ResolvedModule].module.unsafeAsModule().getName.path.last shouldEqual "B_Module" - mainBindingMap.resolvedImports.head.target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "A_Type" + .getIr + ctx.leave() + + "resolve all imported symbols in B_Module from A_Module" in { + bIr.imports.size shouldEqual 1 + bIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val bBindingMap = bIr.unwrapBindingMap + val resolvedImportTargets = + bBindingMap.resolvedImports.map(_.target) + resolvedImportTargets + .collect { case rt: BindingsMap.ResolvedType => rt} + .map(_.tp.name) should contain allOf ("A_Type", "B_Type") + resolvedImportTargets + .collect { case rc: BindingsMap.ResolvedConstructor => rc} shouldBe empty + resolvedImportTargets + .collect { case meth: BindingsMap.ResolvedMethod => meth } + .map(_.method.name) should contain allOf ("static_method", "glob_var", "js_function") + resolvedImportTargets + .collect { case rm: BindingsMap.ResolvedModule => rm } + .map(_.module.getName.item) shouldEqual Iterable("A_Module") + } + + "resolve foreign static module method" in { + val mainBindingMap = + s""" + |from $namespace.$packageName.A_Module import js_function + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + .unwrapBindingMap + val resolvedImportTargets = + mainBindingMap.resolvedImports.map(_.target) + resolvedImportTargets + .collect { case meth: BindingsMap.ResolvedMethod => meth } + .map(_.method.name) shouldEqual "js_function" + resolvedImportTargets + .collect { case mod: BindingsMap.ResolvedModule => mod } + .map(_.module.getName.item) shouldEqual "A_Module" + } + + "not resolve symbol that is not explicitly exported" in { + val mainIr = + s""" + |from $namespace.$packageName.B_Module import A_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true + mainIr.imports.head + .asInstanceOf[IR.Error.ImportExport].reason.message should include("A_Type") + } + + "resolve all symbols (types and static module methods) from the module" in { + val mainIr = + s""" + |from $namespace.$packageName.A_Module import all + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.size shouldEqual 1 + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + val resolvedImportSymbols: List[String] = + mainBindingMap.resolvedImports.map(_.target.qualifiedName.item) + resolvedImportSymbols should contain theSameElementsAs List( + "A_Type", + "static_method", + "glob_var", + "js_function" + ) + } + + "resolve re-exported symbol" in { + val mainIr = + s""" + |from $namespace.$packageName.B_Module import static_method + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.size shouldEqual 1 + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + val resolvedImportSymbols: List[String] = + mainBindingMap.resolvedImports.map(_.target.qualifiedName.item) + resolvedImportSymbols shouldEqual List("static_method") + } + + "resolve re-exported symbol along with all other symbols" in { + val mainIr = + s""" + |from $namespace.$packageName.B_Module import all + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr.imports.size shouldEqual 1 + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap + val resolvedImportSymbols: List[String] = + mainBindingMap.resolvedImports.map(_.target.qualifiedName.item) + resolvedImportSymbols should contain theSameElementsAs List( + "A_Type", + "static_method", + "glob_var", + "js_function", + "B_Type" + ) + } } } From 3ef09bd6b25f6e27184351425cfc69411b610f6f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 16:59:26 +0200 Subject: [PATCH 18/39] Do not create modules outside test context --- .../test/semantic/ImportExportTest.scala | 202 +++++++++++++++--- 1 file changed, 170 insertions(+), 32 deletions(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 28df03e1616f..25c0be914c1b 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -541,39 +541,37 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } "Import resolution for three modules" should { - ctx.enter() - """ - |type A_Type - | A_Constructor - | instance_method self = 42 - | - |static_method = - | local_var = 42 - | local_var - | - |# Is not really a variable - it is a method returning a constant, so - |# it is also considered a static module method - |glob_var = 42 - | - |# This is also a static method - |foreign js js_function = \"\"\" - | return 42 - |""" - .stripMargin - .createModule(packageQualifiedName.createChild("A_Module")) - - val bIr = s""" - |from $namespace.$packageName.A_Module import all - |from $namespace.$packageName.A_Module export static_method - | - |type B_Type - | B_Constructor val - |""".stripMargin - .createModule(packageQualifiedName.createChild("B_Module")) - .getIr - ctx.leave() - "resolve all imported symbols in B_Module from A_Module" in { + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bIr = + s""" + |from $namespace.$packageName.A_Module import all + |from $namespace.$packageName.A_Module export static_method + | + |type B_Type + | B_Constructor val + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr bIr.imports.size shouldEqual 1 bIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false val bBindingMap = bIr.unwrapBindingMap @@ -593,6 +591,26 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } "resolve foreign static module method" in { + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + val mainBindingMap = s""" |from $namespace.$packageName.A_Module import js_function @@ -611,6 +629,36 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } "not resolve symbol that is not explicitly exported" in { + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + s""" + |from $namespace.$packageName.A_Module import all + |from $namespace.$packageName.A_Module export static_method + | + |type B_Type + | B_Constructor val + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + val mainIr = s""" |from $namespace.$packageName.B_Module import A_Type @@ -623,6 +671,36 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } "resolve all symbols (types and static module methods) from the module" in { + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + s""" + |from $namespace.$packageName.A_Module import all + |from $namespace.$packageName.A_Module export static_method + | + |type B_Type + | B_Constructor val + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + val mainIr = s""" |from $namespace.$packageName.A_Module import all @@ -643,6 +721,36 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } "resolve re-exported symbol" in { + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + s""" + |from $namespace.$packageName.A_Module import all + |from $namespace.$packageName.A_Module export static_method + | + |type B_Type + | B_Constructor val + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + val mainIr = s""" |from $namespace.$packageName.B_Module import static_method @@ -658,6 +766,36 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } "resolve re-exported symbol along with all other symbols" in { + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""" + .stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + s""" + |from $namespace.$packageName.A_Module import all + |from $namespace.$packageName.A_Module export static_method + | + |type B_Type + | B_Constructor val + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + val mainIr = s""" |from $namespace.$packageName.B_Module import all From 48db11deca181bdb6be32a56086c74f2d85c44a2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 17:10:51 +0200 Subject: [PATCH 19/39] Remove duplicated test --- .../test/semantic/ImportExportTest.scala | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 25c0be914c1b..4f755343563b 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -261,33 +261,6 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter }) should have size 2 } - "not resolve types and static module methods when importing all from a module" in { - """ - |type Other_Module_Type - |static_method = - | 42 - |""" - .stripMargin - .createModule(packageQualifiedName.createChild("Other_Module")) - val mainBindingMap = - s""" - |from $namespace.$packageName.Other_Module import all - |""" - .stripMargin - .createModule(packageQualifiedName.createChild("Main")) - .getIr - .unwrapBindingMap - - mainBindingMap.resolvedImports.filter(imp => { - imp.target match { - case BindingsMap.ResolvedType(_, tp) if tp.name == "Other_Module_Type" => true - case BindingsMap.ResolvedMethod(_, method) if method.name == "static_method" => true - case _ => false - } - } - ) should have size 2 - } - "resolve only constructors when importing all symbols from a type (1)" in { """ |type Other_Module_Type From 9ba4f253c84c607bcac5ea758d1ef178c6181d44 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 17:21:06 +0200 Subject: [PATCH 20/39] Append all the transitive imports to the resolvedImports --- .../phase/SymbolsImportResolution.scala | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index 5a50b8c6f3a3..7ffadf02b57b 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -322,8 +322,6 @@ class SymbolsImportResolution(compiler: Compiler) { ) ) - - if (symbolsResolution.errorReason.isDefined) { // Replace the IR with IR.Error and don't update the binding map IR.Error.ImportExport( @@ -331,8 +329,34 @@ class SymbolsImportResolution(compiler: Compiler) { reason = symbolsResolution.errorReason.get ) } else { + val importingAllSymbols = symbolsToImport.isEmpty + + val allResolvedSymbols: List[ImportTarget] = + if (importingAllSymbols) { + // Append all exported symbols from the module to the resolved imports. + // These are transitively imported symbols + val additionalExportedSymbols = findAllExportedSymbolsFromModule(importedBindingMap) + + // Filter away all the symbols that are already resolved. + val filteredAdditionalExportedSymbols: List[ImportTarget] = + additionalExportedSymbols.filterNot( + additionalExportedSymbol => + symbolsResolution.resolved.exists(_.qualifiedName == additionalExportedSymbol.qualifiedName) + ) + + logger.log( + Level.FINEST, + "Transitive imports that will be added to the module (exported symbols from imported module): {0}", + Array[Object](filteredAdditionalExportedSymbols) + ) + + symbolsResolution.resolved ++ filteredAdditionalExportedSymbols + } else { + symbolsResolution.resolved + } + val resolvedImports = - symbolsResolution.resolved.map(importTarget => { + allResolvedSymbols.map(importTarget => { BindingsMap.ResolvedImport( importDef = importStatement, exports = List.empty, @@ -340,21 +364,6 @@ class SymbolsImportResolution(compiler: Compiler) { ) }) - // Append all exported symbols from the module to the resolved imports. - // These are transitively imported symbols - val importingAllSymbols = symbolsToImport.isEmpty - if (importingAllSymbols) { - val additionalExportedSymbols = findAllExportedSymbolsFromModule(importedBindingMap) - logger.log( - Level.FINEST, - "Importing all symbols from module {0}, will append all the exported symbols from the module, " + - "these are transitive imports: {1}", - Array[Object](moduleName, additionalExportedSymbols) - ) - bindingMap.resolvedImports = - bindingMap.resolvedImports.appendedAll(additionalExportedSymbols) - } - bindingMap.resolvedImports = bindingMap.resolvedImports.appendedAll(resolvedImports) importStatement From 2c5ea96c390eac8fa08a8fc1c912bdbb8d344ac7 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 17:21:19 +0200 Subject: [PATCH 21/39] Fix compilation --- .../scala/org/enso/compiler/phase/SymbolsImportResolution.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index 7ffadf02b57b..b42586f90fab 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -172,6 +172,7 @@ class SymbolsImportResolution(compiler: Compiler) { case resolvedModule: BindingsMap.ResolvedModule => exportedSymbols += resolvedModule case resolvedMethod: BindingsMap.ResolvedMethod => exportedSymbols += resolvedMethod case resolvedCtor: BindingsMap.ResolvedConstructor => exportedSymbols += resolvedCtor + case _ => () } } exportedSymbols.toList From 903e73dcc05fc1a19261a6531decc350368b82e3 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 17:22:03 +0200 Subject: [PATCH 22/39] Fix findAllStaticMethodsAndTypes. It no longer find also instance methods. --- .../org/enso/compiler/phase/SymbolsImportResolution.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index b42586f90fab..e7aa3f48cd08 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -143,15 +143,15 @@ class SymbolsImportResolution(compiler: Compiler) { ): List[IR.Name.Literal] = { logger.log( Level.FINER, - "Finding all static methods and types in module" + "Finding all static methods and types defined in module" ) val symbolsToImport: ListBuffer[IR.Name.Literal] = ListBuffer.empty moduleBindings.foreach { case Definition.Method.Explicit(methodRef, _, _, _, _) - if methodRef.methodName.isInstanceOf[Name.Literal] => + if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => symbolsToImport += methodRef.methodName.asInstanceOf[Name.Literal] case Definition.Method.Binding(methodRef, _, _, _, _, _) - if methodRef.methodName.isInstanceOf[Name.Literal] => + if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => symbolsToImport += methodRef.methodName.asInstanceOf[Name.Literal] case Definition.Type(name, _, _, _, _, _) if name.isInstanceOf[Name.Literal] => From cbedc070f19b4be9012bf3a006cbc531709bffe1 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 17:51:58 +0200 Subject: [PATCH 23/39] Describe ignored tests --- .../org/enso/compiler/test/semantic/ImportExportTest.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 4f755343563b..c111ed467de8 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -422,7 +422,8 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .asInstanceOf[IR.Error.ImportExport.ModuleDoesNotExist].name should include ("Non_Existing_Symbol") } - "[3] resolve all symbols from transitively exported type" in { + // TODO[pm]: Fix in https://github.com/enso-org/enso/issues/6669 + "resolve all symbols from transitively exported type" ignore { """ |type A_Type | A_Constructor @@ -453,7 +454,8 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .map(_.cons.name) should contain theSameElementsAs List("A_Constructor") } - "[4] resolve constructor from transitively exported type" in { + // TODO[pm]: Fix in https://github.com/enso-org/enso/issues/6669 + "resolve constructor from transitively exported type" ignore { """ |type A_Type | A_Constructor From aacbdff213f13d6a4ce618edde20fc4906ead5e6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 17:52:47 +0200 Subject: [PATCH 24/39] Fix rest of ImportExportTest --- .../test/semantic/ImportExportTest.scala | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index c111ed467de8..e4d53f8b5af8 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -517,7 +517,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter "Import resolution for three modules" should { "resolve all imported symbols in B_Module from A_Module" in { - """ + s""" |type A_Type | A_Constructor | instance_method self = 42 @@ -537,7 +537,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .stripMargin .createModule(packageQualifiedName.createChild("A_Module")) - val bIr = + val mainIr = s""" |from $namespace.$packageName.A_Module import all |from $namespace.$packageName.A_Module export static_method @@ -545,16 +545,16 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |type B_Type | B_Constructor val |""".stripMargin - .createModule(packageQualifiedName.createChild("B_Module")) + .createModule(packageQualifiedName.createChild("Main_Module")) .getIr - bIr.imports.size shouldEqual 1 - bIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false - val bBindingMap = bIr.unwrapBindingMap + mainIr.imports.size shouldEqual 1 + mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false + val mainBindingMap = mainIr.unwrapBindingMap val resolvedImportTargets = - bBindingMap.resolvedImports.map(_.target) + mainBindingMap.resolvedImports.map(_.target) resolvedImportTargets .collect { case rt: BindingsMap.ResolvedType => rt} - .map(_.tp.name) should contain allOf ("A_Type", "B_Type") + .map(_.tp.name) shouldEqual Iterable("A_Type") resolvedImportTargets .collect { case rc: BindingsMap.ResolvedConstructor => rc} shouldBe empty resolvedImportTargets @@ -566,7 +566,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } "resolve foreign static module method" in { - """ + s""" |type A_Type | A_Constructor | instance_method self = 42 @@ -597,10 +597,10 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter mainBindingMap.resolvedImports.map(_.target) resolvedImportTargets .collect { case meth: BindingsMap.ResolvedMethod => meth } - .map(_.method.name) shouldEqual "js_function" + .map(_.method.name) shouldEqual Iterable("js_function") resolvedImportTargets .collect { case mod: BindingsMap.ResolvedModule => mod } - .map(_.module.getName.item) shouldEqual "A_Module" + .map(_.module.getName.item) shouldEqual Iterable("A_Module") } "not resolve symbol that is not explicitly exported" in { @@ -646,7 +646,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter } "resolve all symbols (types and static module methods) from the module" in { - """ + s""" |type A_Type | A_Constructor | instance_method self = 42 @@ -688,6 +688,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter val resolvedImportSymbols: List[String] = mainBindingMap.resolvedImports.map(_.target.qualifiedName.item) resolvedImportSymbols should contain theSameElementsAs List( + "A_Module", "A_Type", "static_method", "glob_var", @@ -737,7 +738,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter val mainBindingMap = mainIr.unwrapBindingMap val resolvedImportSymbols: List[String] = mainBindingMap.resolvedImports.map(_.target.qualifiedName.item) - resolvedImportSymbols shouldEqual List("static_method") + resolvedImportSymbols should contain only("B_Module", "static_method") } "resolve re-exported symbol along with all other symbols" in { @@ -783,11 +784,9 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter val resolvedImportSymbols: List[String] = mainBindingMap.resolvedImports.map(_.target.qualifiedName.item) resolvedImportSymbols should contain theSameElementsAs List( - "A_Type", + "B_Module", + "B_Type", "static_method", - "glob_var", - "js_function", - "B_Type" ) } } From a8b93b9441a3a3d6b3e3fc722d202e277c74e6bf Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 17:53:06 +0200 Subject: [PATCH 25/39] Add "assign module-level foreign static method" to BindingAnalysisTest --- .../test/pass/analyse/BindingAnalysisTest.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala index 6f57b8fce092..8c3b121f6d79 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala @@ -107,7 +107,22 @@ class BindingAnalysisTest extends CompilerTest { .filter(_.isInstanceOf[BindingsMap.ModuleMethod]) shouldEqual List( ModuleMethod("bar") ) + } + "assign module-level foreign static method" in { + implicit val ctx: ModuleContext = mkModuleContext + val ir = + s""" + |foreign js js_function = \"\"\" + | return 42 + | + |""".stripMargin.preprocessModule.analyse + ir.getMetadata(BindingAnalysis) + .get + .definedEntities + .filter(_.isInstanceOf[BindingsMap.ModuleMethod]) shouldEqual List( + ModuleMethod("js_function") + ) } } } From 7a38656789fd000bfae7871cd0980c3d70e3c9c1 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 17:55:30 +0200 Subject: [PATCH 26/39] Turn off logging in ImportExportTest --- .../org/enso/compiler/test/semantic/ImportExportTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index e4d53f8b5af8..c28c5a09c2df 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -36,7 +36,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .allowCreateThread(false) .out(out) .err(out) - .option(RuntimeOptions.LOG_LEVEL, "ALL") + .option(RuntimeOptions.LOG_LEVEL, "WARNING") .option(RuntimeOptions.DISABLE_IR_CACHES, "true") .logHandler(System.err) .option( From 22c9727c45ecf5bd988d9eff53b640f4b10fc747 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 18:22:43 +0200 Subject: [PATCH 27/39] Use `Either` for `SymbolResolution` result type --- .../phase/SymbolsImportResolution.scala | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index e7aa3f48cd08..a89aefce2f32 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -16,6 +16,7 @@ import scala.collection.mutable.ListBuffer class SymbolsImportResolution(compiler: Compiler) { private val logger: TruffleLogger = compiler.context.getLogger(getClass) + private type SymbolsResolution = Either[IR.Error.ImportExport.Reason, List[ImportTarget]] def resolveImportSymbols( module: Module @@ -121,17 +122,14 @@ class SymbolsImportResolution(compiler: Compiler) { tryResolveFromExportedSymbols(module, symbolToImport, typeName) match { case Left(importTarget) => resolvedSymbols += importTarget case Right(errorReason) => - // Report failure - return SymbolsResolution( - resolvedSymbols.toList, - Some(errorReason) + return Left( + errorReason ) } } } - SymbolsResolution( + Right( resolvedSymbols.toList, - None ) } @@ -292,16 +290,13 @@ class SymbolsImportResolution(compiler: Compiler) { Array[Object](symbolsToImportUnwrapped, typeName, importedModule.getName) ) - val symbolsResolution = { + val symbolsResolution: SymbolsResolution = { // Trying to import any symbols from a non-existing type is an error if (typeName.isDefined && !typeExists(typeName.get, importedBindingMap)) { - SymbolsResolution( - resolved = List.empty, - errorReason = Some( - IR.Error.ImportExport.TypeDoesNotExist( - typeName.get, - moduleName - ) + Left( + IR.Error.ImportExport.TypeDoesNotExist( + typeName.get, + moduleName ) ) } else { @@ -316,18 +311,17 @@ class SymbolsImportResolution(compiler: Compiler) { logger.log( Level.FINER, - "Got result from symbolsResolution: Resolved = {0}, Error = {1}", + "Got result from symbolsResolution: {0}", Array[Object]( - symbolsResolution.resolved.map(_.qualifiedName.toString), - symbolsResolution.errorReason + symbolsResolution ) ) - if (symbolsResolution.errorReason.isDefined) { + if (symbolsResolution.isLeft) { // Replace the IR with IR.Error and don't update the binding map IR.Error.ImportExport( ir = importStatement, - reason = symbolsResolution.errorReason.get + reason = symbolsResolution.left.get ) } else { val importingAllSymbols = symbolsToImport.isEmpty @@ -342,7 +336,7 @@ class SymbolsImportResolution(compiler: Compiler) { val filteredAdditionalExportedSymbols: List[ImportTarget] = additionalExportedSymbols.filterNot( additionalExportedSymbol => - symbolsResolution.resolved.exists(_.qualifiedName == additionalExportedSymbol.qualifiedName) + symbolsResolution.right.get.exists(_.qualifiedName == additionalExportedSymbol.qualifiedName) ) logger.log( @@ -351,9 +345,9 @@ class SymbolsImportResolution(compiler: Compiler) { Array[Object](filteredAdditionalExportedSymbols) ) - symbolsResolution.resolved ++ filteredAdditionalExportedSymbols + symbolsResolution.right.get ++ filteredAdditionalExportedSymbols } else { - symbolsResolution.resolved + symbolsResolution.right.get } val resolvedImports = @@ -565,8 +559,4 @@ class SymbolsImportResolution(compiler: Compiler) { }) } - private case class SymbolsResolution( - resolved: List[ImportTarget], - errorReason: Option[IR.Error.ImportExport.Reason] - ) } From 961678ad233cd64e54bcc56f9d18e2f02c5a6b0a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 18:35:56 +0200 Subject: [PATCH 28/39] Remove deprecated methods of `Either` --- .../org/enso/compiler/phase/SymbolsImportResolution.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index a89aefce2f32..0efce5d5d1c0 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -321,7 +321,7 @@ class SymbolsImportResolution(compiler: Compiler) { // Replace the IR with IR.Error and don't update the binding map IR.Error.ImportExport( ir = importStatement, - reason = symbolsResolution.left.get + reason = symbolsResolution.swap.toOption.get ) } else { val importingAllSymbols = symbolsToImport.isEmpty @@ -336,7 +336,7 @@ class SymbolsImportResolution(compiler: Compiler) { val filteredAdditionalExportedSymbols: List[ImportTarget] = additionalExportedSymbols.filterNot( additionalExportedSymbol => - symbolsResolution.right.get.exists(_.qualifiedName == additionalExportedSymbol.qualifiedName) + symbolsResolution.toOption.get.exists(_.qualifiedName == additionalExportedSymbol.qualifiedName) ) logger.log( @@ -345,9 +345,9 @@ class SymbolsImportResolution(compiler: Compiler) { Array[Object](filteredAdditionalExportedSymbols) ) - symbolsResolution.right.get ++ filteredAdditionalExportedSymbols + symbolsResolution.toOption.get ++ filteredAdditionalExportedSymbols } else { - symbolsResolution.right.get + symbolsResolution.toOption.get } val resolvedImports = From a4af67b44222191b7564d54b222b57382698954e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 18:39:52 +0200 Subject: [PATCH 29/39] Use flatMap instead of the mutable buffer --- .../compiler/phase/SymbolsImportResolution.scala | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index 0efce5d5d1c0..0f280155da4f 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -143,20 +143,17 @@ class SymbolsImportResolution(compiler: Compiler) { Level.FINER, "Finding all static methods and types defined in module" ) - val symbolsToImport: ListBuffer[IR.Name.Literal] = ListBuffer.empty - moduleBindings.foreach { + moduleBindings.flatMap(_ match { case Definition.Method.Explicit(methodRef, _, _, _, _) if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => - symbolsToImport += methodRef.methodName.asInstanceOf[Name.Literal] + List(methodRef.methodName.asInstanceOf[Name.Literal]) case Definition.Method.Binding(methodRef, _, _, _, _, _) if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => - symbolsToImport += methodRef.methodName.asInstanceOf[Name.Literal] + List(methodRef.methodName.asInstanceOf[Name.Literal]) case Definition.Type(name, _, _, _, _, _) - if name.isInstanceOf[Name.Literal] => - symbolsToImport += name.asInstanceOf[Name.Literal] - case _ => () - } - symbolsToImport.toList + if name.isInstanceOf[Name.Literal] => List(name.asInstanceOf[Name.Literal]) + case _ => List.empty + }) } private def findAllExportedSymbolsFromModule( From 85c8e5dcc9353b7b0e37a45db63416852110cd86 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 18:54:12 +0200 Subject: [PATCH 30/39] Add some default implementation for overriden methods in Resolved names in IR --- .../scala/org/enso/compiler/data/BindingsMap.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 20e71ada7906..c9fefd71d021 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -835,9 +835,11 @@ object BindingsMap { /** @inheritdoc */ override def module: ModuleReference = tpe.module - override def findExportedSymbolsFor(name: String): List[ResolvedName] = ??? + override def findExportedSymbolsFor(name: String): List[ResolvedName] = + List(this) - override def exportedSymbols: Map[String, List[ResolvedName]] = ??? + override def exportedSymbols: Map[String, List[ResolvedName]] = + Map(cons.name -> List(this)) } /** A representation of a name being resolved to a module. @@ -918,9 +920,11 @@ object BindingsMap { override def qualifiedName: QualifiedName = module.getName.createChild(method.name) - override def findExportedSymbolsFor(name: String): List[ResolvedName] = ??? + override def findExportedSymbolsFor(name: String): List[ResolvedName] = + List(this) - override def exportedSymbols: Map[String, List[ResolvedName]] = ??? + override def exportedSymbols: Map[String, List[ResolvedName]] = + Map(method.name -> List(this)) } /** A representation of a name being resolved to a polyglot symbol. From 66e662d278201516f9f436482a0208db87141f32 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 18:54:34 +0200 Subject: [PATCH 31/39] Use flatMap instead of mutable buffer --- .../phase/SymbolsImportResolution.scala | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index 0f280155da4f..418643ff280a 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -174,7 +174,7 @@ class SymbolsImportResolution(compiler: Compiler) { } /** - * Finds all teh constructors from a type. Returns empty list if there is no such type, + * Finds all the constructors from a type. Returns empty list if there is no such type, * or if the type does not have any constructors. * @param moduleBindings * @param typeName @@ -189,19 +189,14 @@ class SymbolsImportResolution(compiler: Compiler) { "Finding all constructors from type `{0}`", Array[Object](typeName) ) - val ctors: ListBuffer[IR.Name.Literal] = ListBuffer.empty - moduleBindings.foreach { + moduleBindings.flatMap(_ match { case Definition.Type(name, _, members, _, _, _) if name.name == typeName => - members.foreach(_.name match { - case ctorName: IR.Name.Literal => ctors += ctorName - case _ => () - } - ) - case _ => () - } - // ctors buffer might be empty, in case the type is not in the module. - // That is fine, the error will be reported later. - ctors.toList + members.flatMap(_.name match { + case ctorName: IR.Name.Literal => List(ctorName) + case _ => List.empty + }) + case _ => List.empty + }) } /** From d0bac0ecb24e95def3554e3e10d2535821e163a9 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 18:56:06 +0200 Subject: [PATCH 32/39] Refactoring - move methods in SymbolsImportResolution --- .../phase/SymbolsImportResolution.scala | 222 +++++++++--------- 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index 418643ff280a..5b284dbdd55c 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -95,110 +95,6 @@ class SymbolsImportResolution(compiler: Compiler) { } } - /** - * Tries to resolve symbols from the [[module]], potentially from the [[typeName]] if set. - * @param module Module from which symbols are imported. - * @param bindingsMap BindingsMap associated with the [[module]]. - * @param symbolsToImport All the symbols to be imported - * @param typeName The name of the type from which the symbols are imported. If None, - * the symbols are imported from the module. - */ - private def tryResolveSymbols( - module: Module, - bindingsMap: BindingsMap, - symbolsToImport: List[IR.Name.Literal], - typeName: Option[String] - ): SymbolsResolution = { - val resolvedSymbols: ListBuffer[ImportTarget] = ListBuffer.empty - for (symbolToImport <- symbolsToImport) { - tryResolveFromDefinedEntities( - module, - bindingsMap, - symbolToImport, - typeName - ) match { - case Left(importTarget) => resolvedSymbols += importTarget - case Right(_) => - tryResolveFromExportedSymbols(module, symbolToImport, typeName) match { - case Left(importTarget) => resolvedSymbols += importTarget - case Right(errorReason) => - return Left( - errorReason - ) - } - } - } - Right( - resolvedSymbols.toList, - ) - } - - /** - * From the given module bindings, finds all the static module method and type definitions. - */ - private def findAllStaticMethodsAndTypesDefinedInModule( - moduleBindings: List[IR.Module.Scope.Definition] - ): List[IR.Name.Literal] = { - logger.log( - Level.FINER, - "Finding all static methods and types defined in module" - ) - moduleBindings.flatMap(_ match { - case Definition.Method.Explicit(methodRef, _, _, _, _) - if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => - List(methodRef.methodName.asInstanceOf[Name.Literal]) - case Definition.Method.Binding(methodRef, _, _, _, _, _) - if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => - List(methodRef.methodName.asInstanceOf[Name.Literal]) - case Definition.Type(name, _, _, _, _, _) - if name.isInstanceOf[Name.Literal] => List(name.asInstanceOf[Name.Literal]) - case _ => List.empty - }) - } - - private def findAllExportedSymbolsFromModule( - importedModuleBindingMap: BindingsMap - ): List[BindingsMap.ImportTarget] = { - val exportedSymbols: ListBuffer[BindingsMap.ImportTarget] = ListBuffer.empty - importedModuleBindingMap.exportedSymbols.foreach { - case (_, resolvedNames) => - resolvedNames.foreach { - case resolvedType: BindingsMap.ResolvedType => exportedSymbols += resolvedType - case resolvedModule: BindingsMap.ResolvedModule => exportedSymbols += resolvedModule - case resolvedMethod: BindingsMap.ResolvedMethod => exportedSymbols += resolvedMethod - case resolvedCtor: BindingsMap.ResolvedConstructor => exportedSymbols += resolvedCtor - case _ => () - } - } - exportedSymbols.toList - } - - /** - * Finds all the constructors from a type. Returns empty list if there is no such type, - * or if the type does not have any constructors. - * @param moduleBindings - * @param typeName - * @return - */ - private def findAllConstructorsFromType( - moduleBindings: List[IR.Module.Scope.Definition], - typeName: String - ): List[IR.Name.Literal] = { - logger.log( - Level.FINEST, - "Finding all constructors from type `{0}`", - Array[Object](typeName) - ) - moduleBindings.flatMap(_ match { - case Definition.Type(name, _, members, _, _, _) if name.name == typeName => - members.flatMap(_.name match { - case ctorName: IR.Name.Literal => List(ctorName) - case _ => List.empty - }) - case _ => List.empty - }) - } - /** * TODO: Reword * Tries to resolve all the symbols given in [[symbolsToImport]] from the module @@ -209,11 +105,10 @@ class SymbolsImportResolution(compiler: Compiler) { * metadata is updated. When the resolution is successful, the given binding map * is updated and the [[importStatement]] is returned. * - * * @param importStatement The import statement that is being analyzed - * @param bindingMap The binding map of the current module from which we are analyzing - * the import. This binding map may be updated as a result of this - * operation. + * @param bindingMap The binding map of the current module from which we are analyzing + * the import. This binding map may be updated as a result of this + * operation. * @param symbolsToImport The symbols that are being imported from the module or from the type. * If None, all the symbols should be imported. * @return Either the import statement given in `importStatement` in case that all the @@ -267,7 +162,7 @@ class SymbolsImportResolution(compiler: Compiler) { importedModule.getIr.bindings, typeNameUnwrapped ) - case None => + case None => // TODO: Find also all exported symbols from importedModule findAllStaticMethodsAndTypesDefinedInModule( importedModule.getIr.bindings @@ -349,7 +244,8 @@ class SymbolsImportResolution(compiler: Compiler) { exports = List.empty, target = importTarget ) - }) + } + ) bindingMap.resolvedImports = bindingMap.resolvedImports.appendedAll(resolvedImports) @@ -357,6 +253,111 @@ class SymbolsImportResolution(compiler: Compiler) { } } + /** + * Tries to resolve symbols from the [[module]], potentially from the [[typeName]] if set. + * @param module Module from which symbols are imported. + * @param bindingsMap BindingsMap associated with the [[module]]. + * @param symbolsToImport All the symbols to be imported + * @param typeName The name of the type from which the symbols are imported. If None, + * the symbols are imported from the module. + */ + private def tryResolveSymbols( + module: Module, + bindingsMap: BindingsMap, + symbolsToImport: List[IR.Name.Literal], + typeName: Option[String] + ): SymbolsResolution = { + val resolvedSymbols: ListBuffer[ImportTarget] = ListBuffer.empty + for (symbolToImport <- symbolsToImport) { + tryResolveFromDefinedEntities( + module, + bindingsMap, + symbolToImport, + typeName + ) match { + case Left(importTarget) => resolvedSymbols += importTarget + case Right(_) => + tryResolveFromExportedSymbols(module, symbolToImport, typeName) match { + case Left(importTarget) => resolvedSymbols += importTarget + case Right(errorReason) => + return Left( + errorReason + ) + } + } + } + Right( + resolvedSymbols.toList, + ) + } + + /** + * From the given module bindings, finds all the static module method and type definitions. + */ + private def findAllStaticMethodsAndTypesDefinedInModule( + moduleBindings: List[IR.Module.Scope.Definition] + ): List[IR.Name.Literal] = { + logger.log( + Level.FINER, + "Finding all static methods and types defined in module" + ) + moduleBindings.flatMap(_ match { + case Definition.Method.Explicit(methodRef, _, _, _, _) + if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => + List(methodRef.methodName.asInstanceOf[Name.Literal]) + case Definition.Method.Binding(methodRef, _, _, _, _, _) + if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => + List(methodRef.methodName.asInstanceOf[Name.Literal]) + case Definition.Type(name, _, _, _, _, _) + if name.isInstanceOf[Name.Literal] => List(name.asInstanceOf[Name.Literal]) + case _ => List.empty + }) + } + + private def findAllExportedSymbolsFromModule( + importedModuleBindingMap: BindingsMap + ): List[BindingsMap.ImportTarget] = { + val exportedSymbols: ListBuffer[BindingsMap.ImportTarget] = ListBuffer.empty + importedModuleBindingMap.exportedSymbols.foreach { + case (_, resolvedNames) => + resolvedNames.foreach { + case resolvedType: BindingsMap.ResolvedType => exportedSymbols += resolvedType + case resolvedModule: BindingsMap.ResolvedModule => exportedSymbols += resolvedModule + case resolvedMethod: BindingsMap.ResolvedMethod => exportedSymbols += resolvedMethod + case resolvedCtor: BindingsMap.ResolvedConstructor => exportedSymbols += resolvedCtor + case _ => () + } + } + exportedSymbols.toList + } + + /** + * Finds all the constructors from a type. Returns empty list if there is no such type, + * or if the type does not have any constructors. + * @param moduleBindings + * @param typeName + * @return + */ + private def findAllConstructorsFromType( + moduleBindings: List[IR.Module.Scope.Definition], + typeName: String + ): List[IR.Name.Literal] = { + logger.log( + Level.FINEST, + "Finding all constructors from type `{0}`", + Array[Object](typeName) + ) + moduleBindings.flatMap(_ match { + case Definition.Type(name, _, members, _, _, _) if name.name == typeName => + members.flatMap(_.name match { + case ctorName: IR.Name.Literal => List(ctorName) + case _ => List.empty + }) + case _ => List.empty + }) + } + + /** * Tries to resolve a symbol (of a type or a module) from defined entities within * the given `bindingMap`. @@ -550,5 +551,4 @@ class SymbolsImportResolution(compiler: Compiler) { } }) } - } From bd6ae72f3df85079ea5d9added00cd17c4fbdcf2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 12 May 2023 18:57:17 +0200 Subject: [PATCH 33/39] scalafmtAll --- .../enso/compiler/codegen/IrToTruffle.scala | 4 +- .../compiler/phase/ExportsResolution.scala | 4 +- .../enso/compiler/phase/ImportResolver.scala | 13 +- .../phase/SymbolsImportResolution.scala | 230 +++++----- .../test/semantic/ImportExportTest.scala | 406 ++++++++++-------- 5 files changed, 362 insertions(+), 295 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index b390c12e3d72..ea9cfdba30de 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -177,8 +177,8 @@ class IrToTruffle( bindingsMap.resolvedImports.foreach { imp => imp.target match { - case _: BindingsMap.ResolvedType => - case _: BindingsMap.ResolvedMethod => + case _: BindingsMap.ResolvedType => + case _: BindingsMap.ResolvedMethod => case _: BindingsMap.ResolvedConstructor => case ResolvedModule(module) => val mod = module.unsafeAsModule() diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala index c456203afe62..62a84fcf7edb 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala @@ -158,9 +158,9 @@ class ExportsResolution { } exports.foreach { case (module, exports) => module match { - case _: BindingsMap.ResolvedType => + case _: BindingsMap.ResolvedType => case _: BindingsMap.ResolvedConstructor => - case _: BindingsMap.ResolvedMethod => + case _: BindingsMap.ResolvedMethod => case ResolvedModule(module) => getBindings(module.unsafeAsModule()).resolvedExports = exports.map(ex => ex.copy(symbols = ex.symbols.optimize)) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala index d430d4776685..3d08e0638d52 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala @@ -4,7 +4,14 @@ import org.enso.compiler.Compiler import org.enso.compiler.core.IR import org.enso.compiler.core.IR.Module.Scope.{Export, Import} import org.enso.compiler.data.BindingsMap -import org.enso.compiler.data.BindingsMap.{ImportTarget, ModuleReference, ResolvedMethod, ResolvedModule, ResolvedType, Type} +import org.enso.compiler.data.BindingsMap.{ + ImportTarget, + ModuleReference, + ResolvedMethod, + ResolvedModule, + ResolvedType, + Type +} import org.enso.compiler.exception.CompilerError import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.editions.LibraryName @@ -130,8 +137,8 @@ class ImportResolver(compiler: Compiler) { private def tryResolveAsTypeOrStaticMethod( name: IR.Name.Qualified ): Option[ImportTarget] = { - val nameToImportFromModule = name.parts.last.name - val moduleName = name.parts.dropRight(1).map(_.name).mkString(".") + val nameToImportFromModule = name.parts.last.name + val moduleName = name.parts.dropRight(1).map(_.name).mkString(".") compiler.getModule(moduleName).flatMap { mod => compiler.ensureParsed(mod) mod.getIr diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala index 5b284dbdd55c..3dcd0724bfe8 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/SymbolsImportResolution.scala @@ -16,7 +16,8 @@ import scala.collection.mutable.ListBuffer class SymbolsImportResolution(compiler: Compiler) { private val logger: TruffleLogger = compiler.context.getLogger(getClass) - private type SymbolsResolution = Either[IR.Error.ImportExport.Reason, List[ImportTarget]] + private type SymbolsResolution = + Either[IR.Error.ImportExport.Reason, List[ImportTarget]] def resolveImportSymbols( module: Module @@ -69,17 +70,17 @@ class SymbolsImportResolution(compiler: Compiler) { Some(onlyNames) ) - case imp@Import.Module( - importedName, - _, - isAll, - None, - _, - _, - _, - _, - _ - ) if isAll => + case imp @ Import.Module( + importedName, + _, + isAll, + None, + _, + _, + _, + _, + _ + ) if isAll => logger.log( Level.FINER, "Resolving all symbols from import `{0}`", @@ -95,31 +96,30 @@ class SymbolsImportResolution(compiler: Compiler) { } } - /** - * TODO: Reword - * Tries to resolve all the symbols given in [[symbolsToImport]] from the module - * `importedModule`, potentially from the type given in `typeName` and updates - * the imported module's BindingMap accordingly. - * - * In case of a failed resolution, an import error reason IR is returned and no - * metadata is updated. When the resolution is successful, the given binding map - * is updated and the [[importStatement]] is returned. - * - * @param importStatement The import statement that is being analyzed - * @param bindingMap The binding map of the current module from which we are analyzing - * the import. This binding map may be updated as a result of this - * operation. - * @param symbolsToImport The symbols that are being imported from the module or from the type. - * If None, all the symbols should be imported. - * @return Either the import statement given in `importStatement` in case that all the - * symbols were successfuly resolved, or an import error reason IR when some - * symbols failed to resolve. - */ + /** TODO: Reword + * Tries to resolve all the symbols given in [[symbolsToImport]] from the module + * `importedModule`, potentially from the type given in `typeName` and updates + * the imported module's BindingMap accordingly. + * + * In case of a failed resolution, an import error reason IR is returned and no + * metadata is updated. When the resolution is successful, the given binding map + * is updated and the [[importStatement]] is returned. + * + * @param importStatement The import statement that is being analyzed + * @param bindingMap The binding map of the current module from which we are analyzing + * the import. This binding map may be updated as a result of this + * operation. + * @param symbolsToImport The symbols that are being imported from the module or from the type. + * If None, all the symbols should be imported. + * @return Either the import statement given in `importStatement` in case that all the + * symbols were successfuly resolved, or an import error reason IR when some + * symbols failed to resolve. + */ private def tryResolveSymbolsAndUpdateBindingMap( importStatement: IR.Module.Scope.Import.Module, bindingMap: BindingsMap, importedName: IR.Name.Qualified, - symbolsToImport: Option[List[IR.Name.Literal]], + symbolsToImport: Option[List[IR.Name.Literal]] ): IR.Module.Scope.Import = { // Check if the import is from a type rather than from a module, i.e., // `from Module.Type import Constructor` versus `from Module import Some_Type`. @@ -150,7 +150,10 @@ class SymbolsImportResolution(compiler: Compiler) { } val importedBindingMap = - importedModule.getIr.unsafeGetMetadata(BindingAnalysis, "BindingMap should be present") + importedModule.getIr.unsafeGetMetadata( + BindingAnalysis, + "BindingMap should be present" + ) val symbolsToImportUnwrapped: List[IR.Name.Literal] = { symbolsToImport match { @@ -207,7 +210,7 @@ class SymbolsImportResolution(compiler: Compiler) { if (symbolsResolution.isLeft) { // Replace the IR with IR.Error and don't update the binding map IR.Error.ImportExport( - ir = importStatement, + ir = importStatement, reason = symbolsResolution.swap.toOption.get ) } else { @@ -217,13 +220,16 @@ class SymbolsImportResolution(compiler: Compiler) { if (importingAllSymbols) { // Append all exported symbols from the module to the resolved imports. // These are transitively imported symbols - val additionalExportedSymbols = findAllExportedSymbolsFromModule(importedBindingMap) + val additionalExportedSymbols = findAllExportedSymbolsFromModule( + importedBindingMap + ) // Filter away all the symbols that are already resolved. val filteredAdditionalExportedSymbols: List[ImportTarget] = - additionalExportedSymbols.filterNot( - additionalExportedSymbol => - symbolsResolution.toOption.get.exists(_.qualifiedName == additionalExportedSymbol.qualifiedName) + additionalExportedSymbols.filterNot(additionalExportedSymbol => + symbolsResolution.toOption.get.exists( + _.qualifiedName == additionalExportedSymbol.qualifiedName + ) ) logger.log( @@ -241,11 +247,10 @@ class SymbolsImportResolution(compiler: Compiler) { allResolvedSymbols.map(importTarget => { BindingsMap.ResolvedImport( importDef = importStatement, - exports = List.empty, - target = importTarget + exports = List.empty, + target = importTarget ) - } - ) + }) bindingMap.resolvedImports = bindingMap.resolvedImports.appendedAll(resolvedImports) @@ -253,14 +258,13 @@ class SymbolsImportResolution(compiler: Compiler) { } } - /** - * Tries to resolve symbols from the [[module]], potentially from the [[typeName]] if set. - * @param module Module from which symbols are imported. - * @param bindingsMap BindingsMap associated with the [[module]]. - * @param symbolsToImport All the symbols to be imported - * @param typeName The name of the type from which the symbols are imported. If None, - * the symbols are imported from the module. - */ + /** Tries to resolve symbols from the [[module]], potentially from the [[typeName]] if set. + * @param module Module from which symbols are imported. + * @param bindingsMap BindingsMap associated with the [[module]]. + * @param symbolsToImport All the symbols to be imported + * @param typeName The name of the type from which the symbols are imported. If None, + * the symbols are imported from the module. + */ private def tryResolveSymbols( module: Module, bindingsMap: BindingsMap, @@ -277,7 +281,11 @@ class SymbolsImportResolution(compiler: Compiler) { ) match { case Left(importTarget) => resolvedSymbols += importTarget case Right(_) => - tryResolveFromExportedSymbols(module, symbolToImport, typeName) match { + tryResolveFromExportedSymbols( + module, + symbolToImport, + typeName + ) match { case Left(importTarget) => resolvedSymbols += importTarget case Right(errorReason) => return Left( @@ -287,13 +295,12 @@ class SymbolsImportResolution(compiler: Compiler) { } } Right( - resolvedSymbols.toList, + resolvedSymbols.toList ) } - /** - * From the given module bindings, finds all the static module method and type definitions. - */ + /** From the given module bindings, finds all the static module method and type definitions. + */ private def findAllStaticMethodsAndTypesDefinedInModule( moduleBindings: List[IR.Module.Scope.Definition] ): List[IR.Name.Literal] = { @@ -303,13 +310,16 @@ class SymbolsImportResolution(compiler: Compiler) { ) moduleBindings.flatMap(_ match { case Definition.Method.Explicit(methodRef, _, _, _, _) - if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => + if methodRef.methodName + .isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => List(methodRef.methodName.asInstanceOf[Name.Literal]) case Definition.Method.Binding(methodRef, _, _, _, _, _) - if methodRef.methodName.isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => + if methodRef.methodName + .isInstanceOf[Name.Literal] && methodRef.typePointer.isEmpty => List(methodRef.methodName.asInstanceOf[Name.Literal]) case Definition.Type(name, _, _, _, _, _) - if name.isInstanceOf[Name.Literal] => List(name.asInstanceOf[Name.Literal]) + if name.isInstanceOf[Name.Literal] => + List(name.asInstanceOf[Name.Literal]) case _ => List.empty }) } @@ -321,23 +331,26 @@ class SymbolsImportResolution(compiler: Compiler) { importedModuleBindingMap.exportedSymbols.foreach { case (_, resolvedNames) => resolvedNames.foreach { - case resolvedType: BindingsMap.ResolvedType => exportedSymbols += resolvedType - case resolvedModule: BindingsMap.ResolvedModule => exportedSymbols += resolvedModule - case resolvedMethod: BindingsMap.ResolvedMethod => exportedSymbols += resolvedMethod - case resolvedCtor: BindingsMap.ResolvedConstructor => exportedSymbols += resolvedCtor + case resolvedType: BindingsMap.ResolvedType => + exportedSymbols += resolvedType + case resolvedModule: BindingsMap.ResolvedModule => + exportedSymbols += resolvedModule + case resolvedMethod: BindingsMap.ResolvedMethod => + exportedSymbols += resolvedMethod + case resolvedCtor: BindingsMap.ResolvedConstructor => + exportedSymbols += resolvedCtor case _ => () } } exportedSymbols.toList } - /** - * Finds all the constructors from a type. Returns empty list if there is no such type, - * or if the type does not have any constructors. - * @param moduleBindings - * @param typeName - * @return - */ + /** Finds all the constructors from a type. Returns empty list if there is no such type, + * or if the type does not have any constructors. + * @param moduleBindings + * @param typeName + * @return + */ private def findAllConstructorsFromType( moduleBindings: List[IR.Module.Scope.Definition], typeName: String @@ -348,7 +361,8 @@ class SymbolsImportResolution(compiler: Compiler) { Array[Object](typeName) ) moduleBindings.flatMap(_ match { - case Definition.Type(name, _, members, _, _, _) if name.name == typeName => + case Definition.Type(name, _, members, _, _, _) + if name.name == typeName => members.flatMap(_.name match { case ctorName: IR.Name.Literal => List(ctorName) case _ => List.empty @@ -357,14 +371,12 @@ class SymbolsImportResolution(compiler: Compiler) { }) } - - /** - * Tries to resolve a symbol (of a type or a module) from defined entities within - * the given `bindingMap`. - * @param symbolToImport Symbol to import from a type or from a module - * @param typeName If defined, a member from a type is imported, rather than a member of a module. - * @return ImportTarget in case of successful resolution or an error reason - */ + /** Tries to resolve a symbol (of a type or a module) from defined entities within + * the given `bindingMap`. + * @param symbolToImport Symbol to import from a type or from a module + * @param typeName If defined, a member from a type is imported, rather than a member of a module. + * @return ImportTarget in case of successful resolution or an error reason + */ private def tryResolveFromDefinedEntities( module: Module, bindingMap: BindingsMap, @@ -383,11 +395,15 @@ class SymbolsImportResolution(compiler: Compiler) { case Some(definedEntity) => definedEntity match { case tp: BindingsMap.Type => tp - case _ => throw new CompilerError(s"Expected BindingsMap.Type, got $definedEntity") + case _ => + throw new CompilerError( + s"Expected BindingsMap.Type, got $definedEntity" + ) } case None => return Right( - IR.Error.ImportExport.TypeDoesNotExist(typeName.get, module.getName.toString) + IR.Error.ImportExport + .TypeDoesNotExist(typeName.get, module.getName.toString) ) } // Resolve member of a type @@ -400,7 +416,8 @@ class SymbolsImportResolution(compiler: Compiler) { ) Left( BindingsMap.ResolvedConstructor( - BindingsMap.ResolvedType(ModuleReference.Concrete(module), foundType), + BindingsMap + .ResolvedType(ModuleReference.Concrete(module), foundType), constructor ) ) @@ -454,15 +471,14 @@ class SymbolsImportResolution(compiler: Compiler) { } } - /** - * Tries to resolve a symbol (of a type or a module) from exported symbols within - * the given `bindingMap`. - * @param bindingMap - * @param module A BindingMap of this module is searched for the symbols - * @param symbolToImport Symbol to import from a type or from a module - * @param typeName If defined, a member from a type is imported, rather than a member of a module. - * @return Either ImportTarget in case of successful resolution, or an import error reason IR. - */ + /** Tries to resolve a symbol (of a type or a module) from exported symbols within + * the given `bindingMap`. + * @param bindingMap + * @param module A BindingMap of this module is searched for the symbols + * @param symbolToImport Symbol to import from a type or from a module + * @param typeName If defined, a member from a type is imported, rather than a member of a module. + * @return Either ImportTarget in case of successful resolution, or an import error reason IR. + */ private def tryResolveFromExportedSymbols( module: Module, symbolToImport: IR.Name.Literal, @@ -474,17 +490,22 @@ class SymbolsImportResolution(compiler: Compiler) { Array[Object](symbolToImport, typeName, module.getName) ) val bindingMap = module.getIr.unsafeGetMetadata( - BindingAnalysis, "Should be here" + BindingAnalysis, + "Should be here" ) if (typeName.isDefined) { bindingMap.exportedSymbols.get(typeName.get) match { case Some(foundNamesForTypeName) => - foundNamesForTypeName.collectFirst { case x: BindingsMap.ResolvedType => x } match { + foundNamesForTypeName.collectFirst { + case x: BindingsMap.ResolvedType => x + } match { case Some(resolvedType) => // Find `symbolToImport` member from `resolvedType` - val foundConstructor = resolvedType.tp.members.find(_.name == symbolToImport.name) + val foundConstructor = + resolvedType.tp.members.find(_.name == symbolToImport.name) foundConstructor match { - case Some(ctor) => Left(BindingsMap.ResolvedConstructor(resolvedType, ctor)) + case Some(ctor) => + Left(BindingsMap.ResolvedConstructor(resolvedType, ctor)) case None => Right( IR.Error.ImportExport.NoSuchConstructor( @@ -514,20 +535,21 @@ class SymbolsImportResolution(compiler: Compiler) { case Some(resolvedNames) => val resolvedImportTargets: List[ImportTarget] = resolvedNames.collect { - case resolvedModule: BindingsMap.ResolvedModule => resolvedModule - case resolvedType: BindingsMap.ResolvedType => resolvedType - case resolvedMethod: BindingsMap.ResolvedMethod => resolvedMethod + case resolvedModule: BindingsMap.ResolvedModule => resolvedModule + case resolvedType: BindingsMap.ResolvedType => resolvedType + case resolvedMethod: BindingsMap.ResolvedMethod => resolvedMethod case resolvedCtor: BindingsMap.ResolvedConstructor => resolvedCtor } // Take the first resolvedName that is also ImportTarget resolvedImportTargets.headOption match { case Some(resolvedImportTarget) => Left(resolvedImportTarget) - case None => Right( - IR.Error.ImportExport.SymbolsDoNotExist( - List(symbolToImport.name), - module.getName.toString + case None => + Right( + IR.Error.ImportExport.SymbolsDoNotExist( + List(symbolToImport.name), + module.getName.toString + ) ) - ) } case None => Right( @@ -547,7 +569,7 @@ class SymbolsImportResolution(compiler: Compiler) { importedBindingMap.definedEntities.exists(entity => { entity match { case BindingsMap.Type(name, _, _) if name == typeName => true - case _ => false + case _ => false } }) } diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index c28c5a09c2df..35fc2253ad35 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -15,12 +15,14 @@ import org.scalatest.wordspec.AnyWordSpecLike import java.io.ByteArrayOutputStream import java.nio.file.Paths -/** - * Tests a single package with multiple modules for import/export resolution. - * Checks whether the exported symbols and defined entities metadata of the modules - * are correct. - */ -class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter { +/** Tests a single package with multiple modules for import/export resolution. + * Checks whether the exported symbols and defined entities metadata of the modules + * are correct. + */ +class ImportExportTest + extends AnyWordSpecLike + with Matchers + with BeforeAndAfter { private val out = new ByteArrayOutputStream() private val engine = Engine @@ -54,7 +56,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .asHostObject[EnsoContext]() - private val namespace = "my_pkg" + private val namespace = "my_pkg" private val packageName = "My_Package" private val packageQualifiedName = QualifiedName.fromString(namespace + "." + packageName) @@ -92,12 +94,16 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |type Other_Module_Type | Constructor |""".stripMargin - val moduleIr = moduleCode.createModule( - packageQualifiedName.createChild("Other_Module") - ).getIr + val moduleIr = moduleCode + .createModule( + packageQualifiedName.createChild("Other_Module") + ) + .getIr moduleIr.unwrapBindingMap.definedEntities.size shouldEqual 1 moduleIr.unwrapBindingMap.definedEntities.head.name shouldEqual "Other_Module_Type" - val otherTypeDefinedEntity = moduleIr.unwrapBindingMap.definedEntities.head.asInstanceOf[BindingsMap.Type] + val otherTypeDefinedEntity = + moduleIr.unwrapBindingMap.definedEntities.head + .asInstanceOf[BindingsMap.Type] otherTypeDefinedEntity.members.size shouldEqual 1 otherTypeDefinedEntity.members.head.name shouldEqual "Constructor" @@ -107,17 +113,27 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter | |main = Other_Module_Type.Constructor |""".stripMargin - val mainIr = mainCode.createModule(packageQualifiedName.createChild("Main")).getIr + val mainIr = + mainCode.createModule(packageQualifiedName.createChild("Main")).getIr mainIr.imports.size shouldEqual 1 mainIr.imports.head match { case importErr: IR.Error.ImportExport => - fail(s"Import should be resolved, but instead produced IR.Error.ImportExport with ${importErr.reason.message}") + fail( + s"Import should be resolved, but instead produced IR.Error.ImportExport with ${importErr.reason.message}" + ) case _ => () } val mainBindingsMap = mainIr.unwrapBindingMap mainBindingsMap.resolvedImports.size shouldEqual 2 - mainBindingsMap.resolvedImports(0).target.isInstanceOf[BindingsMap.ResolvedModule] shouldBe true - mainBindingsMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedType].tp shouldEqual otherTypeDefinedEntity + mainBindingsMap + .resolvedImports(0) + .target + .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true + mainBindingsMap + .resolvedImports(1) + .target + .asInstanceOf[BindingsMap.ResolvedType] + .tp shouldEqual otherTypeDefinedEntity } "resolve two types from a module" in { @@ -125,39 +141,52 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter """ |type Other_Module_Type |type Another_Type - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) .getIr moduleIr.unwrapBindingMap.definedEntities.size shouldEqual 2 - moduleIr.unwrapBindingMap.definedEntities(0).name shouldEqual "Other_Module_Type" - moduleIr.unwrapBindingMap.definedEntities(1).name shouldEqual "Another_Type" + moduleIr.unwrapBindingMap + .definedEntities(0) + .name shouldEqual "Other_Module_Type" + moduleIr.unwrapBindingMap + .definedEntities(1) + .name shouldEqual "Another_Type" val mainIr = s""" |from $namespace.$packageName.Other_Module import Other_Module_Type, Another_Type - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.unwrapBindingMap.resolvedImports.size shouldEqual 3 - mainIr.unwrapBindingMap.resolvedImports(0).target.isInstanceOf[BindingsMap.ResolvedModule] shouldBe true - mainIr.unwrapBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "Other_Module_Type" - mainIr.unwrapBindingMap.resolvedImports(2).target.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "Another_Type" + mainIr.unwrapBindingMap + .resolvedImports(0) + .target + .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true + mainIr.unwrapBindingMap + .resolvedImports(1) + .target + .asInstanceOf[BindingsMap.ResolvedType] + .tp + .name shouldEqual "Other_Module_Type" + mainIr.unwrapBindingMap + .resolvedImports(2) + .target + .asInstanceOf[BindingsMap.ResolvedType] + .tp + .name shouldEqual "Another_Type" } "resolve a constructor of a type" in { """ |type Other_Module_Type | Constructor - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module.Other_Module_Type import Constructor - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr // TODO: The second resolved import should be ResolvedConstructor @@ -168,14 +197,12 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter """ |type Existing_Type | Constructor - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module import Existing_Type, Non_Existing_Symbol - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.size shouldEqual 1 @@ -186,53 +213,70 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter """ |type Other_Type | method self = 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module.Other_Type import method - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.size shouldEqual 1 - mainIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.asInstanceOf[IR.Error.ImportExport.NoSuchConstructor] shouldEqual IR.Error.ImportExport.NoSuchConstructor("Other_Type", "method") + mainIr.imports.head + .asInstanceOf[IR.Error.ImportExport] + .reason + .asInstanceOf[ + IR.Error.ImportExport.NoSuchConstructor + ] shouldEqual IR.Error.ImportExport + .NoSuchConstructor("Other_Type", "method") } "resolve static method from a module" in { """ |static_method = | 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) val bIr = s""" |import $namespace.$packageName.A_Module.static_method - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) .getIr val mainIr = s""" |from $namespace.$packageName.A_Module import static_method - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false bIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false val mainBindingMap = mainIr.unwrapBindingMap - val bBindingMap = bIr.unwrapBindingMap + val bBindingMap = bIr.unwrapBindingMap mainBindingMap.resolvedImports.size shouldEqual 2 - mainBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedModule].module.getName.item shouldEqual "A_Module" - mainBindingMap.resolvedImports(1).target.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" + mainBindingMap + .resolvedImports(0) + .target + .asInstanceOf[BindingsMap.ResolvedModule] + .module + .getName + .item shouldEqual "A_Module" + mainBindingMap + .resolvedImports(1) + .target + .asInstanceOf[BindingsMap.ResolvedMethod] + .method + .name shouldEqual "static_method" // In B_Module, we only have ResolvedMethod in the resolvedImports, there is no ResolvedModule // But that does not matter. bBindingMap.resolvedImports.size shouldEqual 1 - bBindingMap.resolvedImports(0).target.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" + bBindingMap + .resolvedImports(0) + .target + .asInstanceOf[BindingsMap.ResolvedMethod] + .method + .name shouldEqual "static_method" } "resolve types and static module methods when importing all from a module" in { @@ -240,22 +284,24 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |type Other_Module_Type |static_method = | 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainBindingMap = s""" |from $namespace.$packageName.Other_Module import all - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr .unwrapBindingMap mainBindingMap.resolvedImports.filter(imp => { imp.target match { - case BindingsMap.ResolvedType(_, tp) if tp.name == "Other_Module_Type" => true - case BindingsMap.ResolvedMethod(_, method) if method.name == "static_method" => true + case BindingsMap.ResolvedType(_, tp) + if tp.name == "Other_Module_Type" => + true + case BindingsMap.ResolvedMethod(_, method) + if method.name == "static_method" => + true case _ => false } }) should have size 2 @@ -266,25 +312,24 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |type Other_Module_Type | Constructor | method self = 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainBindingMap = s""" |from $namespace.$packageName.Other_Module.Other_Module_Type import all - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr .unwrapBindingMap mainBindingMap.resolvedImports.filter(imp => { imp.target match { - case BindingsMap.ResolvedConstructor(_, ctor) if ctor.name == "Constructor" => true + case BindingsMap.ResolvedConstructor(_, ctor) + if ctor.name == "Constructor" => + true case _ => false } - } - ) should have size 1 + }) should have size 1 } "resolve only constructors when importing all symbols from a type (2)" in { @@ -293,14 +338,12 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter | Constructor_1 | Constructor_2 val1 val2 | method self = 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module.Other_Module_Type import all - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr @@ -309,11 +352,11 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter mainBindingMap.resolvedImports.filter(imp => { imp.target match { case BindingsMap.ResolvedConstructor(_, ctor) - if ctor.name == "Constructor_1" || ctor.name == "Constructor_2" => true + if ctor.name == "Constructor_1" || ctor.name == "Constructor_2" => + true case _ => false } - } - ) should have size 2 + }) should have size 2 } "resolve all constructors from a type" in { @@ -321,14 +364,12 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |type Other_Module_Type | Constructor_1 | Constructor_2 val1 val2 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module.Other_Module_Type import all - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false @@ -336,11 +377,11 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter mainBindingMap.resolvedImports.filter(imp => { imp.target match { case BindingsMap.ResolvedConstructor(_, ctor) - if ctor.name == "Constructor_1" || ctor.name == "Constructor_2" => true + if ctor.name == "Constructor_1" || ctor.name == "Constructor_2" => + true case _ => false } - } - ) should have size 2 + }) should have size 2 } "resolve exactly two constructors from a type" in { @@ -348,22 +389,20 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |type Other_Module_Type | Constructor_1 | Constructor_2 val1 val2 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module.Other_Module_Type import Constructor_1, Constructor_2 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe false - val mainBindingMap = mainIr.unwrapBindingMap + val mainBindingMap = mainIr.unwrapBindingMap val resolvedImportTargets = mainBindingMap.resolvedImports.map(_.target) resolvedImportTargets .collect { case rc: BindingsMap.ResolvedConstructor => rc } - .map(_.cons.name) should contain only("Constructor_1", "Constructor_2") + .map(_.cons.name) should contain only ("Constructor_1", "Constructor_2") } "result in error when trying to import mix of constructors and method from a type" in { @@ -372,54 +411,57 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter | Constructor_1 | Constructor_2 val1 val2 | method self = 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module.Other_Module_Type import Constructor_1, method, Constructor_2 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true - mainIr.imports.head.asInstanceOf[IR.Error.ImportExport] - .reason.asInstanceOf[IR.Error.ImportExport.NoSuchConstructor] shouldEqual - IR.Error.ImportExport.NoSuchConstructor("Other_Module_Type", "method") + mainIr.imports.head + .asInstanceOf[IR.Error.ImportExport] + .reason + .asInstanceOf[IR.Error.ImportExport.NoSuchConstructor] shouldEqual + IR.Error.ImportExport.NoSuchConstructor("Other_Module_Type", "method") } "result in error when trying to import all from a non-type" in { """ |static_method = | 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module.static_method import all - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - mainIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.asInstanceOf[IR.Error.ImportExport.TypeDoesNotExist].typeName shouldEqual "static_method" + mainIr.imports.head + .asInstanceOf[IR.Error.ImportExport] + .reason + .asInstanceOf[IR.Error.ImportExport.TypeDoesNotExist] + .typeName shouldEqual "static_method" } "result in error when trying to import anything from a non-existing symbol" in { """ |# Left blank on purpose - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Other_Module")) val mainIr = s""" |from $namespace.$packageName.Other_Module.Non_Existing_Symbol import all - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - mainIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason - .asInstanceOf[IR.Error.ImportExport.ModuleDoesNotExist].name should include ("Non_Existing_Symbol") + mainIr.imports.head + .asInstanceOf[IR.Error.ImportExport] + .reason + .asInstanceOf[IR.Error.ImportExport.ModuleDoesNotExist] + .name should include("Non_Existing_Symbol") } // TODO[pm]: Fix in https://github.com/enso-org/enso/issues/6669 @@ -428,20 +470,17 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |type A_Type | A_Constructor | a_method self = 1 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) s""" |import $namespace.$packageName.A_Module.A_Type |export $namespace.$packageName.A_Module.A_Type - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val mainIr = s""" |from $namespace.$packageName.B_Module.A_Type import all - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.size shouldEqual 1 @@ -459,20 +498,17 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter """ |type A_Type | A_Constructor - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) s""" |import $namespace.$packageName.A_Module.A_Type |export $namespace.$packageName.A_Module.A_Type - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val mainIr = s""" |from $namespace.$packageName.B_Module.A_Type import A_Constructor - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr mainIr.imports.size shouldEqual 1 @@ -492,49 +528,50 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter | |type A_Type | a_method self = 1 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) s""" |import $namespace.$packageName.A_Module.A_Type | |type B_Type | b_method self = 2 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val mainModule = s""" |from $namespace.$packageName.B_Module import A_Type - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) - mainModule.getIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true - mainModule.getIr.imports.head.asInstanceOf[IR.Error.ImportExport].reason.asInstanceOf[IR.Error.ImportExport.SymbolsDoNotExist].symbolNames shouldEqual List("A_Type") + mainModule.getIr.imports.head + .isInstanceOf[IR.Error.ImportExport] shouldBe true + mainModule.getIr.imports.head + .asInstanceOf[IR.Error.ImportExport] + .reason + .asInstanceOf[IR.Error.ImportExport.SymbolsDoNotExist] + .symbolNames shouldEqual List("A_Type") } } "Import resolution for three modules" should { "resolve all imported symbols in B_Module from A_Module" in { s""" - |type A_Type - | A_Constructor - | instance_method self = 42 - | - |static_method = - | local_var = 42 - | local_var - | - |# Is not really a variable - it is a method returning a constant, so - |# it is also considered a static module method - |glob_var = 42 - | - |# This is also a static method - |foreign js js_function = \"\"\" - | return 42 - |""" - .stripMargin + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) val mainIr = @@ -553,13 +590,17 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter val resolvedImportTargets = mainBindingMap.resolvedImports.map(_.target) resolvedImportTargets - .collect { case rt: BindingsMap.ResolvedType => rt} + .collect { case rt: BindingsMap.ResolvedType => rt } .map(_.tp.name) shouldEqual Iterable("A_Type") resolvedImportTargets - .collect { case rc: BindingsMap.ResolvedConstructor => rc} shouldBe empty + .collect { case rc: BindingsMap.ResolvedConstructor => + rc + } shouldBe empty resolvedImportTargets .collect { case meth: BindingsMap.ResolvedMethod => meth } - .map(_.method.name) should contain allOf ("static_method", "glob_var", "js_function") + .map( + _.method.name + ) should contain allOf ("static_method", "glob_var", "js_function") resolvedImportTargets .collect { case rm: BindingsMap.ResolvedModule => rm } .map(_.module.getName.item) shouldEqual Iterable("A_Module") @@ -567,29 +608,28 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter "resolve foreign static module method" in { s""" - |type A_Type - | A_Constructor - | instance_method self = 42 - | - |static_method = - | local_var = 42 - | local_var - | - |# Is not really a variable - it is a method returning a constant, so - |# it is also considered a static module method - |glob_var = 42 - | - |# This is also a static method - |foreign js js_function = \"\"\" - | return 42 - |""" - .stripMargin + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) val mainBindingMap = - s""" - |from $namespace.$packageName.A_Module import js_function - |""".stripMargin + s""" + |from $namespace.$packageName.A_Module import js_function + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr .unwrapBindingMap @@ -620,8 +660,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |# This is also a static method |foreign js js_function = \"\"\" | return 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) s""" @@ -642,28 +681,29 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter .getIr mainIr.imports.head.isInstanceOf[IR.Error.ImportExport] shouldBe true mainIr.imports.head - .asInstanceOf[IR.Error.ImportExport].reason.message should include("A_Type") + .asInstanceOf[IR.Error.ImportExport] + .reason + .message should include("A_Type") } "resolve all symbols (types and static module methods) from the module" in { s""" - |type A_Type - | A_Constructor - | instance_method self = 42 - | - |static_method = - | local_var = 42 - | local_var - | - |# Is not really a variable - it is a method returning a constant, so - |# it is also considered a static module method - |glob_var = 42 - | - |# This is also a static method - |foreign js js_function = \"\"\" - | return 42 - |""" - .stripMargin + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) s""" @@ -713,8 +753,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |# This is also a static method |foreign js js_function = \"\"\" | return 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) s""" @@ -738,7 +777,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter val mainBindingMap = mainIr.unwrapBindingMap val resolvedImportSymbols: List[String] = mainBindingMap.resolvedImports.map(_.target.qualifiedName.item) - resolvedImportSymbols should contain only("B_Module", "static_method") + resolvedImportSymbols should contain only ("B_Module", "static_method") } "resolve re-exported symbol along with all other symbols" in { @@ -758,8 +797,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter |# This is also a static method |foreign js js_function = \"\"\" | return 42 - |""" - .stripMargin + |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) s""" @@ -786,7 +824,7 @@ class ImportExportTest extends AnyWordSpecLike with Matchers with BeforeAndAfter resolvedImportSymbols should contain theSameElementsAs List( "B_Module", "B_Type", - "static_method", + "static_method" ) } } From a65e48754846edeb1c3f90ab0848ba7c3a838b5d Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 16 May 2023 13:13:02 +0200 Subject: [PATCH 34/39] Fix find methods for ResolvedConstructor and ResolvedMethod --- .../org/enso/compiler/data/BindingsMap.scala | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala index bf45cf4d4d05..e38561b74b88 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -835,8 +835,13 @@ object BindingsMap { /** @inheritdoc */ override def module: ModuleReference = tpe.module - override def findExportedSymbolsFor(name: String): List[ResolvedName] = - List(this) + override def findExportedSymbolsFor(name: String): List[ResolvedName] = { + if (cons.name == name) { + List(this) + } else { + List() + } + } override def exportedSymbols: Map[String, List[ResolvedName]] = Map(cons.name -> List(this)) @@ -920,8 +925,13 @@ object BindingsMap { override def qualifiedName: QualifiedName = module.getName.createChild(method.name) - override def findExportedSymbolsFor(name: String): List[ResolvedName] = - List(this) + override def findExportedSymbolsFor(name: String): List[ResolvedName] = { + if (method.name == name) { + List(this) + } else { + List() + } + } override def exportedSymbols: Map[String, List[ResolvedName]] = Map(method.name -> List(this)) From 36c0b74185393a2b8805b6d424cf8a425195c433 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 16 May 2023 15:34:04 +0200 Subject: [PATCH 35/39] Remove multiple imports of the same symbol --- distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso | 1 - 1 file changed, 1 deletion(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index 20e6bac5eb56..bc55fb92d8fc 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -24,7 +24,6 @@ import project.Runtime.Managed_Resource.Managed_Resource import project.System.File.File_Access.File_Access import project.System.File.File_Permissions.File_Permissions -from project.Data.Boolean import Boolean, True, False from project.System.File_Format import Auto_Detect, File_Format, format_widget import project.Metadata.Widget From ff0e1c7cb255517c170c21f7b1e720e191ce4e0d Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 17 May 2023 10:33:05 +0200 Subject: [PATCH 36/39] Add some tests for exporting and checks for `findExportedSymbolsFor` method --- .../test/semantic/ImportExportTest.scala | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 35fc2253ad35..f8e24631898b 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -553,6 +553,152 @@ class ImportExportTest } } + "Export resolution" should { + "not export module when exporting a type (1)" in { + """ + |type A_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + .getIr + .unwrapBindingMap + + // B_Module is exporting just A_Type, not A_Module + val bindingMap = s""" + |import $namespace.$packageName.A_Module.A_Type + |export $namespace.$packageName.A_Module.A_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + .unwrapBindingMap + + val resolvedImportTargets = + bindingMap.resolvedImports + .map(_.target) + val resolvedImportNames = + resolvedImportTargets + .map(_.qualifiedName.item) + bindingMap.exportedSymbols.get("A_Module") shouldBe None + bindingMap.exportedSymbols.get("A_Type") shouldNot be(None) + resolvedImportNames should contain theSameElementsAs List("A_Type") + } + + "not export module when exporting a type (2)" in { + """ + |type A_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + .getIr + .unwrapBindingMap + + // B_Module is exporting just A_Type, not A_Module + val bindingMap = + s""" + |import $namespace.$packageName.A_Module + |from $namespace.$packageName.A_Module export A_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + .unwrapBindingMap + + val resolvedImportNames = + bindingMap.resolvedImports + .map(_.target) + .map(_.qualifiedName.item) + bindingMap.exportedSymbols.get("A_Module") should be(None) + bindingMap.exportedSymbols.get("A_Type") shouldNot be(None) + resolvedImportNames should contain theSameElementsAs List("A_Module") + } + + "findExportedSymbolsFor method works for all import target types" in { + s""" + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bindingMap = s""" + |from $namespace.$packageName.A_Module import all + |from $namespace.$packageName.A_Module.A_Type import A_Constructor + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + .unwrapBindingMap + + val resolvedImportTargets = + bindingMap.resolvedImports.map(_.target) + + val resolvedAType = resolvedImportTargets.collect { case t: BindingsMap.ResolvedType => t }.head + resolvedAType.tp.name shouldEqual "A_Type" + resolvedAType.findExportedSymbolsFor("A_Type") should be(Nil) + resolvedAType.findExportedSymbolsFor("A_Constructor").head.qualifiedName.item shouldEqual "A_Constructor" + resolvedAType.findExportedSymbolsFor("NonExistingSymbol") should be(Nil) + + val resolvedStaticMethod = resolvedImportTargets.collect { case m: BindingsMap.ResolvedMethod => m }.head + resolvedStaticMethod.method.name shouldEqual "static_method" + resolvedStaticMethod.findExportedSymbolsFor("static_method") should be(Nil) + resolvedStaticMethod.findExportedSymbolsFor("NonExistingSymbol") should be(Nil) + + val resolvedConstructor = resolvedImportTargets.collect { case c: BindingsMap.ResolvedConstructor => c }.head + resolvedConstructor.cons.name shouldEqual "A_Constructor" + resolvedConstructor.findExportedSymbolsFor("A_Constructor") should be(Nil) + resolvedConstructor.findExportedSymbolsFor("NonExistingSymbol") should be(Nil) + } + + "findExportedSymbolsFor method works for ResolvedModule" in { + s""" + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + | + |# Is not really a variable - it is a method returning a constant, so + |# it is also considered a static module method + |glob_var = 42 + | + |# This is also a static method + |foreign js js_function = \"\"\" + | return 42 + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bindingMap = + s""" + |import $namespace.$packageName.A_Module + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + .unwrapBindingMap + + val resolvedImportTargets = + bindingMap.resolvedImports.map(_.target) + + val resolvedModule = resolvedImportTargets.collect { case m: BindingsMap.ResolvedModule => m }.head + resolvedModule.module.getName.item shouldEqual "A_Module" + resolvedModule.findExportedSymbolsFor("static_method").head.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "static_method" + resolvedModule.findExportedSymbolsFor("glob_var").head.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "glob_var" + resolvedModule.findExportedSymbolsFor("js_function").head.asInstanceOf[BindingsMap.ResolvedMethod].method.name shouldEqual "js_function" + resolvedModule.findExportedSymbolsFor("NonExistingSymbol") should be(Nil) + resolvedModule.findExportedSymbolsFor("instance_method") should be(Nil) + resolvedModule.findExportedSymbolsFor("A_Constructor") should be(Nil) + } + } + "Import resolution for three modules" should { "resolve all imported symbols in B_Module from A_Module" in { s""" From ea6185d89d13aaad22107710f840967b18f2cf1a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 17 May 2023 10:35:36 +0200 Subject: [PATCH 37/39] ResolvedConstructor and ResolvedMethod do not export any symbols in `findExportedSymbolsFor` method --- .../org/enso/compiler/data/BindingsMap.scala | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala index e38561b74b88..a914ff370a8c 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -650,7 +650,15 @@ object BindingsMap { sealed trait ImportTarget extends ResolvedName { override def toAbstract: ImportTarget override def toConcrete(moduleMap: ModuleMap): Option[ImportTarget] - def findExportedSymbolsFor(name: String): List[ResolvedName] + + /** Find all exported symbols for a given name. + * More specifically, this method answers the following question: "If all symbols are imported + * from this import target via `from import all` statement, is `name` one of + * these symbols?" + * @param name the name to search for + * @return a list of all exported symbols with the given name + */ + def findExportedSymbolsFor(name: String): List[ResolvedName] def resolveExportedSymbol( name: String ): Either[ResolutionError, ResolvedName] = @@ -721,7 +729,7 @@ object BindingsMap { /** A representation of a sum type * * @param name the type name - * @param members the member names + * @param members the member names - constructors only. * @param builtinType true if constructor is annotated with @Builtin_Type, false otherwise. */ case class Type( @@ -835,13 +843,8 @@ object BindingsMap { /** @inheritdoc */ override def module: ModuleReference = tpe.module - override def findExportedSymbolsFor(name: String): List[ResolvedName] = { - if (cons.name == name) { - List(this) - } else { - List() - } - } + override def findExportedSymbolsFor(name: String): List[ResolvedName] = + List() override def exportedSymbols: Map[String, List[ResolvedName]] = Map(cons.name -> List(this)) @@ -925,13 +928,8 @@ object BindingsMap { override def qualifiedName: QualifiedName = module.getName.createChild(method.name) - override def findExportedSymbolsFor(name: String): List[ResolvedName] = { - if (method.name == name) { - List(this) - } else { - List() - } - } + override def findExportedSymbolsFor(name: String): List[ResolvedName] = + List() override def exportedSymbols: Map[String, List[ResolvedName]] = Map(method.name -> List(this)) From 6a8ad2378e54901afb43d821302b385603e50754 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 17 May 2023 12:51:17 +0200 Subject: [PATCH 38/39] Add more tests --- .../test/semantic/ImportExportTest.scala | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index f8e24631898b..e34e5738eb6d 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -609,6 +609,84 @@ class ImportExportTest resolvedImportNames should contain theSameElementsAs List("A_Module") } + "not export module when exporting a type with same name" in { + """ + |type A_Module + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bindingMap = + s""" + |import $namespace.$packageName.A_Module + |from $namespace.$packageName.A_Module export A_Module + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + .unwrapBindingMap + + val exportedModule = bindingMap.exportedSymbols("A_Module") + exportedModule.size shouldEqual 1 + exportedModule.head.asInstanceOf[BindingsMap.ResolvedType].tp.name shouldEqual "A_Module" + } + + "import re-exported constructor" in { + val aBindingMap = s""" + |from $namespace.$packageName.A_Module.A_Type import A_Constructor + |from $namespace.$packageName.A_Module.A_Type export A_Constructor + | + |type A_Type + | A_Constructor + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + .getIr + .unwrapBindingMap + + val bBindingMap = s""" + |import $namespace.$packageName.A_Module + |from $namespace.$packageName.A_Module export A_Constructor + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + .getIr + .unwrapBindingMap + + val cBindingMap = s""" + |from $namespace.$packageName.B_Module import A_Constructor + |""".stripMargin + .createModule(packageQualifiedName.createChild("C_Module")) + .getIr + .unwrapBindingMap + + val dBindingMap = + s""" + |from $namespace.$packageName.B_Module import all + |""".stripMargin + .createModule(packageQualifiedName.createChild("D_Module")) + .getIr + .unwrapBindingMap + + aBindingMap.exportedSymbols.get("A_Constructor") shouldNot be(None) + + val bResolvedImportNames = bBindingMap.resolvedImports + .map(_.target) + .map(_.qualifiedName.item) + bResolvedImportNames should contain theSameElementsAs List("A_Module") + bBindingMap.exportedSymbols.size shouldEqual 1 + bBindingMap.exportedSymbols("A_Constructor").size shouldEqual 1 + bBindingMap.exportedSymbols("A_Constructor").head.asInstanceOf[BindingsMap.ResolvedConstructor].cons.name shouldEqual "A_Constructor" + + val cResolvedImportNames = cBindingMap.resolvedImports + .map(_.target) + .map(_.qualifiedName.item) + cResolvedImportNames should contain theSameElementsAs List("B_Module", "A_Constructor") + cBindingMap.exportedSymbols shouldBe empty + + val dResolvedImportNames = dBindingMap.resolvedImports + .map(_.target) + .map(_.qualifiedName.item) + dResolvedImportNames should contain theSameElementsAs List("B_Module", "A_Constructor") + dBindingMap.exportedSymbols shouldBe empty + } + "findExportedSymbolsFor method works for all import target types" in { s""" |type A_Type From 3cb14282d0677db5203896742370826cae31c6b5 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 17 May 2023 12:51:27 +0200 Subject: [PATCH 39/39] Cosmetics --- .../src/main/scala/org/enso/compiler/data/BindingsMap.scala | 3 --- .../org/enso/compiler/test/semantic/ImportExportTest.scala | 5 ----- 2 files changed, 8 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala index a914ff370a8c..8c19dac0c361 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -253,9 +253,6 @@ case class BindingsMap( /** Dumps the export statements from this module into a structure ready for * further analysis. - * - * @return a list of triples of the exported module, the name it is exported - * as and any further symbol restrictions. */ def getDirectlyExportedModules: List[ExportedModule] = resolvedImports.collect { case ResolvedImport(_, exports, mod) => diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index e34e5738eb6d..e8713b9c3bdc 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -559,8 +559,6 @@ class ImportExportTest |type A_Type |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) - .getIr - .unwrapBindingMap // B_Module is exporting just A_Type, not A_Module val bindingMap = s""" @@ -587,10 +585,7 @@ class ImportExportTest |type A_Type |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) - .getIr - .unwrapBindingMap - // B_Module is exporting just A_Type, not A_Module val bindingMap = s""" |import $namespace.$packageName.A_Module