From 0137a25b6b33663f6273245e0079e5bbf4b74558 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 25 Jun 2024 16:55:39 +0200 Subject: [PATCH 001/111] Replace all `from ... export all` with explicit exports --- .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 22 +++++++++++++------ .../Standard/Database/0.0.0-dev/src/Main.enso | 4 ++-- .../Standard/Table/0.0.0-dev/src/Main.enso | 6 ++--- .../lib/Standard/Test/0.0.0-dev/src/Main.enso | 6 ++++- .../test/resources/Cycle_Test/src/Sub/B.enso | 2 +- .../src/F2.enso | 2 +- .../src/F2.enso | 3 +-- .../Base/0.0.0-dev/src/Data/Boolean.enso | 2 +- 8 files changed, 29 insertions(+), 18 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index f1ab8b3384d3..f4259565da6b 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -95,14 +95,22 @@ export project.System.Process.Exit_Code.Exit_Code export project.Warning.Warning from project.Data.Boolean export Boolean, False, True from project.Data.Index_Sub_Range.Index_Sub_Range export First, Last -from project.Data.Json.Extensions export all +from project.Data.Json.Extensions export to_json, to_js_object from project.Data.Numbers export Float, Integer, Number -from project.Data.Range.Extensions export all -from project.Data.Statistics.Extensions export all -from project.Data.Text.Extensions export all +from project.Data.Range.Extensions export up_to, down_to +from project.Data.Statistics.Extensions export compute, compute_bulk, running, running_bulk, rank_data +from project.Data.Text.Extensions export reverse, each, at, get, first, second, last, characters, find +from project.Data.Text.Extensions export find_all, match, to_regex, split, tokenize, replace, cleanse +from project.Data.Text.Extensions export words, lines, insert, from, is_digit, is_whitespace, bytes, from_bytes +from project.Data.Text.Extensions export utf_8, from_utf_8, char_vector, from_char_vector, codepoints, from_codepoints +from project.Data.Text.Extensions export starts_with, ends_with, contains, repeat, take +from project.Data.Text.Extensions export drop, to_case, pad, trim, locate, locate_all, index_of +from project.Data.Text.Extensions export last_index_of, parse_float, parse_integer, parse_json +from project.Data.Text.Extensions export parse_date, parse_date_time, parse_time_of_day, parse_time_zone +from project.Data.Text.Extensions export substring from project.Data.Text.Regex export regex -from project.Errors.Problem_Behavior.Problem_Behavior export all -from project.Function export all +from project.Errors.Problem_Behavior.Problem_Behavior export Ignore, Report_Warning, Report_Error +from project.Function export Function, identity, flip, const, curry, uncurry from project.Meta.Enso_Project export enso_project -from project.Network.Extensions export all +from project.Network.Extensions export to_uri, fetch, post from project.System.File_Format export Auto_Detect, Bytes, File_Format, Infer, JSON_Format, Plain_Text_Format diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso index d83211a988de..f12d7d868de8 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso @@ -10,5 +10,5 @@ export project.Connection.SSL_Mode.SSL_Mode export project.SQL_Query.SQL_Query export project.Update_Action.Update_Action from project.Connection.Postgres_Details.Postgres_Details export Postgres -from project.Extensions.Upload_Database_Table export all -from project.Extensions.Upload_In_Memory_Table export all +from project.Extensions.Upload_Database_Table export update_rows, select_into_database_table, delete_rows +from project.Extensions.Upload_In_Memory_Table export update_rows, select_into_database_table, delete_rows diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso index 81c293919845..1ccd29cf6326 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso @@ -25,7 +25,7 @@ export project.Table.Table export project.Value_Type.Auto export project.Value_Type.Bits export project.Value_Type.Value_Type -from project.Constants export all +from project.Constants export Previous_Value, Report_Unmatched from project.Expression export expr -from project.Extensions.Column_Vector_Extensions export all -from project.Extensions.Table_Conversions export all +from project.Extensions.Column_Vector_Extensions export to_column, compute, compute_bulk, running +from project.Extensions.Table_Conversions export to_table, from_objects, parse_to_table, write_table diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso index e417b167f049..a655811b61a5 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso @@ -12,5 +12,9 @@ export project.Faker.Faker export project.Problems export project.Suite.Suite export project.Test.Test -from project.Extensions export all +from project.Extensions export should_fail_with, should_equal, should_equal_type, should_not_equal +from project.Extensions export should_not_equal_type, should_start_with, should_end_with +from project.Extensions export should_succeed, should_be_a, should_be_true, should_be_false +from project.Extensions export should_contain_the_same_elements_as, should_only_contain_elements_in +from project.Extensions export should_contain, should_not_contain diff --git a/engine/runtime-integration-tests/src/test/resources/Cycle_Test/src/Sub/B.enso b/engine/runtime-integration-tests/src/test/resources/Cycle_Test/src/Sub/B.enso index 341c48af8f4e..0768ad1a509b 100644 --- a/engine/runtime-integration-tests/src/test/resources/Cycle_Test/src/Sub/B.enso +++ b/engine/runtime-integration-tests/src/test/resources/Cycle_Test/src/Sub/B.enso @@ -1 +1 @@ -from project.Sub.C export all +from project.Sub.C export A diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso index 01c0206e102a..5e31acfd2e4b 100644 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso +++ b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso @@ -1,3 +1,3 @@ export project.F1 from project.F1 export foo -from project.F1 export all hiding foo +from project.F1 export bar diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso index a01b3e3d4949..96d2b4366799 100644 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso +++ b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso @@ -1,3 +1,2 @@ export project.F1 -from project.F1 export all -from project.F1 export all hiding bar +from project.F1 export foo, bar diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso index db556eec1071..28a059ec0bf4 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso @@ -1,4 +1,4 @@ -from project.Data.Boolean.Boolean export all +from project.Data.Boolean.Boolean export True, False @Builtin_Type type Boolean From d7362f7e37e2c5e12cd8b46eac0df541d59d64c0 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 25 Jun 2024 17:03:42 +0200 Subject: [PATCH 002/111] Add some missing exports --- distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index f4259565da6b..7193d49e0151 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -107,7 +107,7 @@ from project.Data.Text.Extensions export starts_with, ends_with, contains, repea from project.Data.Text.Extensions export drop, to_case, pad, trim, locate, locate_all, index_of from project.Data.Text.Extensions export last_index_of, parse_float, parse_integer, parse_json from project.Data.Text.Extensions export parse_date, parse_date_time, parse_time_of_day, parse_time_zone -from project.Data.Text.Extensions export substring +from project.Data.Text.Extensions export substring, slice_text, split_find_delimiters from project.Data.Text.Regex export regex from project.Errors.Problem_Behavior.Problem_Behavior export Ignore, Report_Warning, Report_Error from project.Function export Function, identity, flip, const, curry, uncurry From 2bf2f4f0b1a582d33a2e56eb1c687b4fef6e37a3 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 26 Jun 2024 11:05:16 +0200 Subject: [PATCH 003/111] Remove isAll and hiding fields from Export IR --- .../java/org/enso/compiler/dump/IRDumper.java | 1 - .../phase/ImportResolverAlgorithm.java | 64 ------------------- .../compiler/phase/ImportResolverForIR.java | 14 ---- .../scala/org/enso/compiler/Compiler.scala | 2 - .../org/enso/compiler/data/BindingsMap.scala | 37 ++--------- .../enso/compiler/pass/desugar/Imports.scala | 6 +- .../pass/lint/ModuleNameConflicts.scala | 2 - .../enso/compiler/phase/ImportResolver.scala | 6 +- .../org/enso/compiler/ErrorCompilerTest.java | 9 +++ .../java/org/enso/compiler/core/TreeToIr.java | 20 +++--- .../core/ir/module/scope/Export.scala | 47 +++----------- .../callable/InvokeMethodImportResolver.java | 10 --- 12 files changed, 40 insertions(+), 178 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java index 9b838d29e159..871788811cff 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java @@ -484,7 +484,6 @@ private void createIRGraph(Export exportIr) { GraphVizNode.Builder.fromIr(exportIr) .addLabelLine("isSynthetic: " + exportModIr.isSynthetic()) .addLabelLine("name: " + exportModIr.name().name()) - .addLabelLine("isAll: " + exportModIr.isAll()) .build(); addNode(node); } diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java index 2e4c3c4a4301..d9b912a41d12 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java @@ -2,10 +2,8 @@ import java.io.IOException; import java.util.List; -import java.util.Objects; import org.enso.compiler.core.CompilerError; import org.enso.editions.LibraryName; -import scala.Tuple2; public abstract class ImportResolverAlgorithm< Result, Module, Import, Export, ResolvedType, ResolvedModule> { @@ -27,18 +25,11 @@ protected ImportResolverAlgorithm() {} */ protected abstract java.util.List exportsFor(Module module, String symbol); - protected abstract boolean isAll(Export ex); - /** * @return {@code null} or list of named imports */ protected abstract java.util.List onlyNames(Export ex); - /** - * @return {@code null} or list of named imports - */ - protected abstract java.util.List hiddenNames(Export ex); - protected abstract java.util.List definedEntities(Import name); /** @@ -76,61 +67,6 @@ public final Result tryResolveImport(Module module, Import imp) { private Result tryResolveImportNew(Module module, Import imp) { var impName = nameForImport(imp); var exp = exportsFor(module, impName); - var fromAllExports = exp.stream().filter(ex -> isAll(ex)).toList(); - if (fromAllExports.size() >= 2) { - // Detect potential conflicts when importing all and hiding names for the exports of the same - // module - var unqualifiedImports = fromAllExports.stream().filter(e -> onlyNames(e) == null).toList(); - var qualifiedImports = - fromAllExports.stream() - .map( - e -> { - var onlyNames = onlyNames(e); - if (onlyNames != null) { - return onlyNames.stream().toList(); - } else { - return null; - } - }) - .filter(Objects::nonNull) - .toList(); - var importsWithHiddenNames = - fromAllExports.stream() - .map( - e -> { - var hiddenNames = hiddenNames(e); - if (hiddenNames != null) { - return new Tuple2<>(e, hiddenNames); - } else { - return null; - } - }) - .filter(Objects::nonNull) - .toList(); - - for (var h : importsWithHiddenNames) { - var e = h._1; - var hidden = h._2; - var unqualifiedConflicts = - unqualifiedImports.stream().filter(x -> !x.equals(e)).filter(Objects::nonNull).toList(); - if (!unqualifiedConflicts.isEmpty()) { - throw HiddenNamesConflict.shadowUnqualifiedExport(nameForExport(e), hidden); - } - } - for (var h : importsWithHiddenNames) { - var e = h._1; - var hidden = h._2; - var qualifiedConflicts = - qualifiedImports.stream() - .filter(Objects::nonNull) - .flatMap(x -> x.stream()) - .filter(f -> hidden.stream().filter(x -> f.equals(x)).findAny().isPresent()) - .toList(); - if (!qualifiedConflicts.isEmpty()) { - throw HiddenNamesConflict.shadowQualifiedExport(nameForExport(e), qualifiedConflicts); - } - } - } var parts = partsForImport(imp); if (parts.size() < 2) { throw new CompilerError( diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java index 1416d35d2b26..1aa937b8de82 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java @@ -59,11 +59,6 @@ protected final java.util.List exportsFor(Module module, String i return exp; } - @Override - protected final boolean isAll(Export.Module ex) { - return ex.isAll(); - } - @Override protected final java.util.List onlyNames(Export.Module ex) { if (ex.onlyNames().isEmpty()) { @@ -73,15 +68,6 @@ protected final java.util.List onlyNames(Export.Module ex) { return list; } - @Override - protected final java.util.List hiddenNames(Export.Module ex) { - if (ex.hiddenNames().isEmpty()) { - return null; - } - var list = CollectionConverters.SeqHasAsJava(ex.hiddenNames().get().map(n -> n.name())).asJava(); - return list; - } - @Override protected final java.util.List definedEntities(Import.Module name) { var parts = partsForImport(name); diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala index d0b2791b950d..03b7329e7f4f 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala @@ -749,9 +749,7 @@ class Compiler( Export.Module( m, rename = None, - isAll = false, onlyNames = None, - hiddenNames = None, location = None, isSynthetic = true ) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 472e777ce6fd..a179b4c9f3fa 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -313,38 +313,15 @@ case class BindingsMap( def getDirectlyExportedModules: List[ExportedModule] = resolvedImports.collect { case ResolvedImport(_, exports, mod) => exports.map { exp => - val restriction = if (exp.isAll) { - if (exp.onlyNames.isDefined) { - SymbolRestriction.Only( - exp.onlyNames.get - .map(name => - SymbolRestriction - .AllowedResolution(name.name.toLowerCase, None) - ) - .toSet - ) - } else if (exp.hiddenNames.isDefined) { - SymbolRestriction.Hiding( - exp.hiddenNames.get.map(_.name.toLowerCase).toSet - ) - } else { - SymbolRestriction.All - } - } else { - SymbolRestriction.Only( - Set( - SymbolRestriction.AllowedResolution( - exp.getSimpleName.name.toLowerCase, - Some(mod) - ) + val restriction = SymbolRestriction.Only( + Set( + SymbolRestriction.AllowedResolution( + exp.getSimpleName.name.toLowerCase, + Some(mod) ) ) - } - val rename = if (!exp.isAll) { - Some(exp.getSimpleName.name) - } else { - None - } + ) + val rename = Some(exp.getSimpleName.name) ExportedModule(mod, rename, restriction) } }.flatten diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/desugar/Imports.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/desugar/Imports.scala index 1d6b79cba404..0f48b7cd21d9 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/desugar/Imports.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/desugar/Imports.scala @@ -77,7 +77,7 @@ case object Imports extends IRPass { name = newName.copy(parts = parts :+ mainModuleName), rename = computeRename( ex.rename, - ex.onlyNames.nonEmpty || ex.isAll, + ex.onlyNames.nonEmpty, parts(1).asInstanceOf[Name.Literal] ) ) @@ -110,10 +110,10 @@ case object Imports extends IRPass { private def computeRename( originalRename: Option[Name.Literal], - onlyNamesOrAll: Boolean, + onlyNames: Boolean, qualName: Name.Literal ): Option[Name.Literal] = - originalRename.orElse(Option.unless(onlyNamesOrAll)(qualName)) + originalRename.orElse(Option.unless(onlyNames)(qualName)) val currentProjectAlias = "project" diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/lint/ModuleNameConflicts.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/lint/ModuleNameConflicts.scala index b0f417ef8100..4d85d8471977 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/lint/ModuleNameConflicts.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/lint/ModuleNameConflicts.scala @@ -39,8 +39,6 @@ case object ModuleNameConflicts extends IRPass { case mod @ Export.Module( _, _, - false, - None, None, None, true, diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala index efa96e61ddf8..c40ba27ffb8a 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala @@ -180,9 +180,7 @@ final class ImportResolver(compiler: Compiler) extends ImportResolverForIR { case Export.Module( expName, rename, - isAll, onlyNames, - hiddenNames, _, isSynthetic, _, @@ -194,9 +192,9 @@ final class ImportResolver(compiler: Compiler) extends ImportResolverForIR { val syntheticImport = Import.Module( expName, rename, - isAll, + false, onlyNames, - hiddenNames, + None, location = None, isSynthetic = true, passData = new MetadataStorage(), diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java index ef1c05b488aa..af3142152f7e 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java @@ -593,6 +593,15 @@ public void testEmptyBody() throws Exception { assertTrue(method.body() instanceof Empty); } + @Test + public void exportAllIsNotAllowed() { + var ir = parse(""" + from project.Module export all + """); + assertSingleSyntaxError( + ir, Syntax.UnexpectedExpression$.MODULE$, "Unexpected expression", 0, 6); + } + private void assertSingleSyntaxError( Module ir, Syntax.Reason type, String msg, int start, int end) { var errors = assertIR(ir, Syntax.class, 1); diff --git a/engine/runtime-parser/src/main/java/org/enso/compiler/core/TreeToIr.java b/engine/runtime-parser/src/main/java/org/enso/compiler/core/TreeToIr.java index 90863fbf8fb0..2d103c85c29b 100644 --- a/engine/runtime-parser/src/main/java/org/enso/compiler/core/TreeToIr.java +++ b/engine/runtime-parser/src/main/java/org/enso/compiler/core/TreeToIr.java @@ -1823,32 +1823,30 @@ private String extractPackageAndName(List qualifiedName, StringBuilder pkg @SuppressWarnings("unchecked") Export translateExport(Tree.Export exp) { try { + if (exp.getHiding() != null) { + return translateSyntaxError(exp, invalidExportReason("`hiding` not allowed in `export` statement")); + } + if (exp.getAll() != null) { + return translateSyntaxError(exp, invalidExportReason("`all` not allowed in `export` statement")); + } Option rename; if (exp.getAs() == null) { rename = Option.empty(); } else { rename = Option.apply(buildName(exp.getAs().getBody(), true)); } - Option> hidingNames; - if (exp.getHiding() == null) { - hidingNames = Option.empty(); - } else { - hidingNames = Option.apply(buildNameSequence(exp.getHiding().getBody())); - } Name.Qualified qualifiedName; Option> onlyNames = Option.empty(); if (exp.getFrom() != null) { qualifiedName = buildQualifiedName(exp.getFrom().getBody(), Option.empty(), true); var onlyBodies = exp.getExport().getBody(); - if (exp.getAll() == null) { - onlyNames = Option.apply(buildNameSequence(onlyBodies)); - } + onlyNames = Option.apply(buildNameSequence(onlyBodies)); } else { qualifiedName = buildQualifiedName(exp.getExport().getBody(), Option.empty(), true); } return new Export.Module( - qualifiedName, rename, (exp.getFrom() != null), onlyNames, - hidingNames, getIdentifiedLocation(exp), false, + qualifiedName, rename, onlyNames, + getIdentifiedLocation(exp), false, meta(), diag() ); } catch (SyntaxException err) { diff --git a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/module/scope/Export.scala b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/module/scope/Export.scala index 977566da57f4..5b84b26d47f7 100644 --- a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/module/scope/Export.scala +++ b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/module/scope/Export.scala @@ -41,9 +41,7 @@ object Export { * * @param name the full path representing the export * @param rename the name this export is visible as - * @param isAll is this an unqualified export * @param onlyNames exported names selected from the exported module - * @param hiddenNames exported names hidden from the exported module * @param location the source location that the node corresponds to * @param isSynthetic is this export compiler-generated * @param passData the pass metadata associated with this node @@ -52,9 +50,7 @@ object Export { sealed case class Module( name: Name.Qualified, rename: Option[Name.Literal], - isAll: Boolean, onlyNames: Option[List[Name.Literal]], - hiddenNames: Option[List[Name.Literal]], override val location: Option[IdentifiedLocation], isSynthetic: Boolean = false, override val passData: MetadataStorage = new MetadataStorage(), @@ -68,9 +64,7 @@ object Export { * * @param name the full path representing the export * @param rename the name this export is visible as - * @param isAll is this an unqualified export * @param onlyNames exported names selected from the exported module - * @param hiddenNames exported names hidden from the exported module * @param location the source location that the node corresponds to * @param isSynthetic is this import compiler-generated * @param passData the pass metadata associated with this node @@ -79,23 +73,19 @@ object Export { * @return a copy of `this`, updated with the specified values */ def copy( - name: Name.Qualified = name, - rename: Option[Name.Literal] = rename, - isAll: Boolean = isAll, - onlyNames: Option[List[Name.Literal]] = onlyNames, - hiddenNames: Option[List[Name.Literal]] = hiddenNames, - location: Option[IdentifiedLocation] = location, - isSynthetic: Boolean = isSynthetic, - passData: MetadataStorage = passData, - diagnostics: DiagnosticStorage = diagnostics, - id: UUID @Identifier = id + name: Name.Qualified = name, + rename: Option[Name.Literal] = rename, + onlyNames: Option[List[Name.Literal]] = onlyNames, + location: Option[IdentifiedLocation] = location, + isSynthetic: Boolean = isSynthetic, + passData: MetadataStorage = passData, + diagnostics: DiagnosticStorage = diagnostics, + id: UUID @Identifier = id ): Module = { val res = Module( name, rename, - isAll, onlyNames, - hiddenNames, location, isSynthetic, passData, @@ -138,9 +128,7 @@ object Export { |Module.Scope.Export.Module( |name = $name, |rename = $rename, - |isAll = $isAll, |onlyNames = $onlyNames, - |hidingNames = $hiddenNames, |location = $location, |passData = ${this.showPassData}, |diagnostics = $diagnostics, @@ -152,25 +140,13 @@ object Export { override def children: List[IR] = name :: List( rename.toList, - onlyNames.getOrElse(List()), - hiddenNames.getOrElse(List()) + onlyNames.getOrElse(List()) ).flatten /** @inheritdoc */ override def showCode(indent: Int): String = { val renameCode = rename.map(n => s" as ${n.name}").getOrElse("") - if (isAll) { - val onlyPart = onlyNames - .map(names => " " + names.map(_.name).mkString(", ")) - .getOrElse("") - val hidingPart = hiddenNames - .map(names => s" hiding ${names.map(_.name).mkString(", ")}") - .getOrElse("") - val all = if (onlyNames.isDefined) "" else " all" - s"from ${name.name}$renameCode export$onlyPart$all$hidingPart" - } else { - s"export ${name.name}$renameCode" - } + s"export ${name.name}$renameCode" } /** Gets the name of the module visible in the importing scope, @@ -190,11 +166,8 @@ object Export { * @return whether the name could be accessed or not */ def allowsAccess(name: String): Boolean = { - if (!isAll) return false; if (onlyNames.isDefined) { onlyNames.get.exists(_.name.toLowerCase == name.toLowerCase) - } else if (hiddenNames.isDefined) { - !hiddenNames.get.exists(_.name.toLowerCase == name.toLowerCase) } else { true } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java index 2dfcc96c1683..8b9892713b33 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java @@ -57,21 +57,11 @@ protected List exportsFor(Module module, String impName) { return Collections.emptyList(); } - @Override - protected boolean isAll(Object ex) { - return false; - } - @Override protected List onlyNames(Object ex) { return null; } - @Override - protected List hiddenNames(Object ex) { - return null; - } - @Override protected List definedEntities(UnresolvedSymbol symbol) { return module.getScope().getAllTypes(symbol.getName()); From 3140aaa3ddb1be164525d2eeddf18a97a451f45c Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 26 Jun 2024 11:09:52 +0200 Subject: [PATCH 004/111] Test compiler errors for missing export fields --- .../java/org/enso/compiler/ErrorCompilerTest.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java index af3142152f7e..f28fa85d21db 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java @@ -598,8 +598,19 @@ public void exportAllIsNotAllowed() { var ir = parse(""" from project.Module export all """); + var expectedReason = new Syntax.InvalidExport("`all` not allowed in `export` statement"); assertSingleSyntaxError( - ir, Syntax.UnexpectedExpression$.MODULE$, "Unexpected expression", 0, 6); + ir, expectedReason, null, 0, 30); + } + + @Test + public void exportHidingIsNotAllowed() { + var ir = parse(""" + from project.Module export all hiding Foo + """); + var expectedReason = new Syntax.InvalidExport("`hiding` not allowed in `export` statement"); + assertSingleSyntaxError( + ir, expectedReason, null, 0, 41); } private void assertSingleSyntaxError( From 4cb2bb11e4ea884a920b46391c5614a66cd6be23 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 26 Jun 2024 11:56:24 +0200 Subject: [PATCH 005/111] Add ExportConstructorTest --- .../test/exports/ExportConstructorTest.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java new file mode 100644 index 000000000000..e28dd5ab74b0 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java @@ -0,0 +1,56 @@ +package org.enso.interpreter.test.exports; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.util.Set; +import org.enso.pkg.QualifiedName; +import org.enso.polyglot.PolyglotContext; +import org.enso.polyglot.RuntimeOptions; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ModuleUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ExportConstructorTest { + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void exportedConstructorsAreInBindingMap() throws IOException { + var booleanMod = + new SourceModule( + QualifiedName.fromString("Boolean"), + """ + type Boolean + True + False + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + from project.Boolean.Boolean export True, False + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(booleanMod, mainMod), projDir); + + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + var mainModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainModExportedSymbols.size(), is(2)); + assertThat(mainModExportedSymbols, allOf(hasKey("True"), hasKey("False"))); + } + } + +} From 284490164fddf35b5175e4b4176f2b87733d3d85 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 30 May 2024 15:49:24 +0200 Subject: [PATCH 006/111] Add ExportedSymbolsTest --- .../enso/compiler/ExportedSymbolsTest.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java new file mode 100644 index 000000000000..d1341df14c3c --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -0,0 +1,110 @@ +package org.enso.compiler; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.enso.compiler.context.CompilerContext.Module; +import org.enso.compiler.data.BindingsMap; +import org.enso.pkg.QualifiedName; +import org.enso.polyglot.PolyglotContext; +import org.enso.polyglot.RuntimeOptions; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.graalvm.polyglot.Context; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import scala.jdk.javaapi.CollectionConverters; + +public class ExportedSymbolsTest { + private Path projDir; + + @Before + public void setup() throws IOException { + this.projDir = Files.createTempDirectory("exported-symbols-test"); + } + + @After + public void tearDown() throws IOException { + ProjectUtils.deleteRecursively(projDir); + } + + @Test + public void exportedSymbolsFromSingleModule() throws IOException { + var mainSrcMod = + new SourceModule(QualifiedName.fromString("Main"), """ + type A_Type + """); + ProjectUtils.createProject("Proj", Set.of(mainSrcMod), projDir); + var ctx = createCtx(projDir); + compile(ctx); + var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSymbols.size(), is(1)); + assertThat(mainExportedSymbols.containsKey("A_Type"), is(true)); + assertThat( + mainExportedSymbols.get("A_Type").get(0), instanceOf(BindingsMap.ResolvedType.class)); + } + + @Test + public void transitivelyExportedSymbols() throws IOException { + var aMod = + new SourceModule(QualifiedName.fromString("A_Module"), """ + type A_Type + """); + var mainSrcMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + import project.A_Module.A_Type + export project.A_Module.A_Type + + type B_Type + """); + ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir); + var ctx = createCtx(projDir); + compile(ctx); + var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSymbols.size(), is(2)); + assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type")); + } + + private static Context createCtx(Path projDir) { + return ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build(); + } + + private static void compile(Context ctx) { + new PolyglotContext(ctx).getTopScope().compile(true); + } + + private static Map> getExportedSymbolsFromModule( + Context ctx, String modName) { + var ensoCtx = ContextUtils.leakContext(ctx); + var mod = ensoCtx.getPackageRepository().getLoadedModule(modName).get(); + return getExportedSymbols(mod); + } + + private static Map> getExportedSymbols(Module module) { + var bindings = new HashMap>(); + var bindingsScala = module.getBindingsMap().exportedSymbols(); + bindingsScala.foreach( + entry -> { + var symbol = entry._1; + var resolvedNames = CollectionConverters.asJava(entry._2.toSeq()); + bindings.put(symbol, resolvedNames); + return null; + }); + return bindings; + } +} From 15829b53ab032c746360b0a8ca17aa6973adc7f2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 30 May 2024 15:49:35 +0200 Subject: [PATCH 007/111] Add ExportCycleDetectionTest --- .../compiler/ExportCycleDetectionTest.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java new file mode 100644 index 000000000000..1f64ad900dfc --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java @@ -0,0 +1,74 @@ +package org.enso.compiler; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Set; +import org.enso.pkg.QualifiedName; +import org.enso.polyglot.PolyglotContext; +import org.enso.polyglot.RuntimeOptions; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.graalvm.polyglot.PolyglotException; +import org.hamcrest.Matcher; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ExportCycleDetectionTest { + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void detectSimpleCycle() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), + """ + from project.Main import B_Type + from project.Main export B_Type + type A_Type + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + from project.A_Module import A_Type + from project.A_Module export A_Type + type B_Type + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + expectProjectCompilationError( + projDir, + allOf( + containsString("Export statements form a cycle"), + containsString("local.Proj.Main exports local.Proj.A_Module"), + containsString("which exports local.Proj.Main"))); + } + + private void expectProjectCompilationError(Path projDir, Matcher errMsgMatcher) { + var out = new ByteArrayOutputStream(); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .option(RuntimeOptions.STRICT_ERRORS, "true") + .out(out) + .err(out) + .build()) { + var polyCtx = new PolyglotContext(ctx); + try { + polyCtx.getTopScope().compile(true); + fail("Expected compilation error"); + } catch (PolyglotException e) { + assertThat(e.getMessage(), containsString("Compilation aborted")); + } + } + assertThat(out.toString(), errMsgMatcher); + } +} From 2252a6f18d4685342f8111dc2ea9ecef30a21d2b Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 6 Jun 2024 12:59:42 +0200 Subject: [PATCH 008/111] Remove ExportCycleDetectionTest --- .../compiler/ExportCycleDetectionTest.java | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java deleted file mode 100644 index 1f64ad900dfc..000000000000 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.enso.compiler; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.fail; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.file.Path; -import java.util.Set; -import org.enso.pkg.QualifiedName; -import org.enso.polyglot.PolyglotContext; -import org.enso.polyglot.RuntimeOptions; -import org.enso.test.utils.ContextUtils; -import org.enso.test.utils.ProjectUtils; -import org.enso.test.utils.SourceModule; -import org.graalvm.polyglot.PolyglotException; -import org.hamcrest.Matcher; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -public class ExportCycleDetectionTest { - @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); - - @Test - public void detectSimpleCycle() throws IOException { - var aMod = - new SourceModule( - QualifiedName.fromString("A_Module"), - """ - from project.Main import B_Type - from project.Main export B_Type - type A_Type - """); - var mainMod = - new SourceModule( - QualifiedName.fromString("Main"), - """ - from project.A_Module import A_Type - from project.A_Module export A_Type - type B_Type - """); - var projDir = tempFolder.newFolder().toPath(); - ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); - expectProjectCompilationError( - projDir, - allOf( - containsString("Export statements form a cycle"), - containsString("local.Proj.Main exports local.Proj.A_Module"), - containsString("which exports local.Proj.Main"))); - } - - private void expectProjectCompilationError(Path projDir, Matcher errMsgMatcher) { - var out = new ByteArrayOutputStream(); - try (var ctx = - ContextUtils.defaultContextBuilder() - .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) - .option(RuntimeOptions.STRICT_ERRORS, "true") - .out(out) - .err(out) - .build()) { - var polyCtx = new PolyglotContext(ctx); - try { - polyCtx.getTopScope().compile(true); - fail("Expected compilation error"); - } catch (PolyglotException e) { - assertThat(e.getMessage(), containsString("Compilation aborted")); - } - } - assertThat(out.toString(), errMsgMatcher); - } -} From bad6558d993a8efb2c474d3758ae29e6f0114b60 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 11 Jun 2024 13:44:19 +0200 Subject: [PATCH 009/111] Reintroduce ExportCycleDetectionTest --- .../compiler/ExportCycleDetectionTest.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java new file mode 100644 index 000000000000..14acd8e596d6 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java @@ -0,0 +1,79 @@ +package org.enso.compiler; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Set; +import org.enso.pkg.QualifiedName; +import org.enso.polyglot.PolyglotContext; +import org.enso.polyglot.RuntimeOptions; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.graalvm.polyglot.PolyglotException; +import org.hamcrest.Matcher; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ExportCycleDetectionTest { + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void detectSimpleCycle() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), + """ + from project.B_Module export B_Type + type A_Type + """); + var bMod = + new SourceModule( + QualifiedName.fromString("B_Module"), + """ + from project.A_Module export A_Type + type B_Type + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + import project.A_Module + import project.B_Module + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, bMod, mainMod), projDir); + expectProjectCompilationError( + projDir, + allOf( + containsString("Export statements form a cycle"), + containsString("local.Proj.B_Module exports local.Proj.A_Module"), + containsString("which exports local.Proj.B_Module"))); + } + + private void expectProjectCompilationError(Path projDir, Matcher errMsgMatcher) { + var out = new ByteArrayOutputStream(); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .option(RuntimeOptions.STRICT_ERRORS, "true") + .out(out) + .err(out) + .build()) { + var polyCtx = new PolyglotContext(ctx); + try { + polyCtx.getTopScope().compile(true); + fail("Expected compilation error"); + } catch (PolyglotException e) { + assertThat(e.getMessage(), containsString("Compilation aborted")); + } + } + assertThat(out.toString(), errMsgMatcher); + } +} From 6351bc384718784f2e3ae43007a2114392555612 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 11 Jun 2024 13:46:20 +0200 Subject: [PATCH 010/111] Add test for exported symbols in synthetic modules --- .../enso/compiler/ExportedSymbolsTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index d1341df14c3c..7c7fab3cb283 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; import java.io.IOException; import java.nio.file.Files; @@ -78,6 +79,38 @@ public void transitivelyExportedSymbols() throws IOException { assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type")); } + @Test + public void submodulesAreIncludedInExportedSymbols() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), + """ + from project.A_Module.B_Module export B_Type + type A_Type + """); + var bMod = + new SourceModule( + QualifiedName.fromString("A_Module.B_Module"), """ + type B_Type + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + from project.A_Module export all + """); + ProjectUtils.createProject("Proj", Set.of(aMod, bMod, mainMod), projDir); + var ctx = createCtx(projDir); + compile(ctx); + var aModSyms = getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); + assertThat(aModSyms.size(), is(3)); + assertThat(aModSyms.keySet(), containsInAnyOrder("A_Type", "B_Module", "B_Type")); + + var mainModSyms = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainModSyms.size(), is(3)); + assertThat(mainModSyms.keySet(), containsInAnyOrder("A_Type", "B_Module", "B_Type")); + } + private static Context createCtx(Path projDir) { return ContextUtils.defaultContextBuilder() .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) From 4b34b9484e77cb38046a0402815dcd545461d726 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 11 Jun 2024 14:21:02 +0200 Subject: [PATCH 011/111] Add test for export cycle detection in three modules --- .../compiler/ExportCycleDetectionTest.java | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java index 14acd8e596d6..6579f9f52be4 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java @@ -25,7 +25,7 @@ public class ExportCycleDetectionTest { @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @Test - public void detectSimpleCycle() throws IOException { + public void detectCycleInTwoModules() throws IOException { var aMod = new SourceModule( QualifiedName.fromString("A_Module"), @@ -57,6 +57,47 @@ public void detectSimpleCycle() throws IOException { containsString("which exports local.Proj.B_Module"))); } + @Test + public void detectCycleInThreeModules() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), + """ + from project.B_Module export C_Type + type A_Type + """); + var bMod = + new SourceModule( + QualifiedName.fromString("B_Module"), + """ + from project.C_Module export C_Type + """); + var cMod = + new SourceModule( + QualifiedName.fromString("C_Module"), + """ + from project.A_Module export A_Type + type C_Type + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + import project.A_Module + import project.B_Module + import project.C_Module + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, bMod, cMod, mainMod), projDir); + expectProjectCompilationError( + projDir, + allOf( + containsString("Export statements form a cycle"), + containsString("local.Proj.B_Module exports local.Proj.C_Module"), + containsString("which exports local.Proj.A_Module"), + containsString("which exports local.Proj.B_Module"))); + } + private void expectProjectCompilationError(Path projDir, Matcher errMsgMatcher) { var out = new ByteArrayOutputStream(); try (var ctx = From 0ac44a3938f09812dafbf23d6c64bfedfc30cf8c Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 11 Jun 2024 14:23:51 +0200 Subject: [PATCH 012/111] Remove unnecessary imports from tests --- .../src/test/java/org/enso/compiler/ExportedSymbolsTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index 7c7fab3cb283..0209ed9ff833 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -66,9 +66,7 @@ public void transitivelyExportedSymbols() throws IOException { new SourceModule( QualifiedName.fromString("Main"), """ - import project.A_Module.A_Type export project.A_Module.A_Type - type B_Type """); ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir); From 6dc51f34ac1f5da23d1e1917bcd548fbdaed91e6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 27 Jun 2024 11:19:50 +0200 Subject: [PATCH 013/111] Test export from different module --- .../enso/compiler/ExportedSymbolsTest.java | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index 0209ed9ff833..9f5ee3834c7e 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -78,35 +78,22 @@ public void transitivelyExportedSymbols() throws IOException { } @Test - public void submodulesAreIncludedInExportedSymbols() throws IOException { + public void exportSymbolFromDifferentModule() throws IOException { var aMod = - new SourceModule( - QualifiedName.fromString("A_Module"), - """ - from project.A_Module.B_Module export B_Type + new SourceModule(QualifiedName.fromString("A_Module"), """ + from project.B_Module export B_Type type A_Type """); var bMod = - new SourceModule( - QualifiedName.fromString("A_Module.B_Module"), """ + new SourceModule(QualifiedName.fromString("B_Module"), """ type B_Type """); - var mainMod = - new SourceModule( - QualifiedName.fromString("Main"), - """ - from project.A_Module export all - """); - ProjectUtils.createProject("Proj", Set.of(aMod, bMod, mainMod), projDir); + ProjectUtils.createProject("Proj", Set.of(aMod, bMod), projDir); var ctx = createCtx(projDir); compile(ctx); - var aModSyms = getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); - assertThat(aModSyms.size(), is(3)); - assertThat(aModSyms.keySet(), containsInAnyOrder("A_Type", "B_Module", "B_Type")); - - var mainModSyms = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); - assertThat(mainModSyms.size(), is(3)); - assertThat(mainModSyms.keySet(), containsInAnyOrder("A_Type", "B_Module", "B_Type")); + var aModExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); + assertThat(aModExportedSymbols.size(), is(2)); + assertThat(aModExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type")); } private static Context createCtx(Path projDir) { From 87dc19d199a32c69a44e718b3fc1818f764224d9 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 27 Jun 2024 11:20:01 +0200 Subject: [PATCH 014/111] Implement ProjectUtils.deleteRecursively --- .../org/enso/test/utils/ProjectUtils.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java b/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java index 878d0b663b8d..cbac35ee5df2 100644 --- a/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java +++ b/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java @@ -2,8 +2,11 @@ import java.io.File; import java.io.IOException; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.Set; import java.util.function.Consumer; import org.enso.pkg.QualifiedName; @@ -111,4 +114,24 @@ public static void testProjectRun( public static void testProjectRun(Path projDir, Consumer resultConsumer) { testProjectRun(ContextUtils.defaultContextBuilder(), projDir, resultConsumer); } + + /** Deletes provided directory recursively. */ + public static void deleteRecursively(Path rootDir) throws IOException { + Files.walkFileTree( + rootDir, + new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } } From 51484039fe4c28615c0a2fe540d0a85e8ed019cf Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 4 Jun 2024 11:10:12 +0200 Subject: [PATCH 015/111] ProjectUtils: Main module does not have to exist when creating a project --- .../org/enso/test/utils/ProjectUtils.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java b/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java index cbac35ee5df2..5907edcd1487 100644 --- a/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java +++ b/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java @@ -41,7 +41,10 @@ public static void createProject(String projName, String mainSrc, Path projDir) * running via {@code enso --run }. * * @param projName Name of the project - * @param modules Set of modules. Must contain `Main` module. + * @param modules Set of modules. If the main module is not present in the set, an exception will + * be thrown once you try to {@link #testProjectRun(Builder, Path, Consumer) test} the project + * run. Note that set of modules without a main module makes sense only if you intend to test + * the compilation and not running. * @param projDir A directory in which the whole project structure will be created. Must exist and * be a directory. */ @@ -62,18 +65,13 @@ public static void createProject(String projName, Set modules, Pat assert yamlPath.toFile().exists(); var srcDir = Files.createDirectory(projDir.resolve("src")); assert srcDir.toFile().exists(); - boolean mainModuleFound = false; for (var module : modules) { var relativePath = String.join(File.pathSeparator, module.name().pathAsJava()); var modDirPath = srcDir.resolve(relativePath); Files.createDirectories(modDirPath); var modPath = modDirPath.resolve(module.name().item() + ".enso"); Files.writeString(modPath, module.code()); - if (module.name().equals(QualifiedName.fromString("Main"))) { - mainModuleFound = true; - } } - assert mainModuleFound; } /** @@ -87,7 +85,10 @@ public static void createProject(String projName, Set modules, Pat */ public static void testProjectRun( Context.Builder ctxBuilder, Path projDir, Consumer resultConsumer) { - assert projDir.toFile().exists() && projDir.toFile().isDirectory(); + if (!(projDir.toFile().exists() && projDir.toFile().isDirectory())) { + throw new IllegalArgumentException( + "Project directory " + projDir + " must already be created"); + } try (var ctx = ctxBuilder .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) @@ -96,6 +97,9 @@ public static void testProjectRun( .build()) { var polyCtx = new PolyglotContext(ctx); var mainSrcPath = projDir.resolve("src").resolve("Main.enso"); + if (!mainSrcPath.toFile().exists()) { + throw new IllegalArgumentException("Main module not found in " + projDir); + } var mainMod = polyCtx.evalModule(mainSrcPath.toFile()); var assocMainModType = mainMod.getAssociatedType(); var mainMethod = mainMod.getMethod(assocMainModType, "main").get(); @@ -105,7 +109,7 @@ public static void testProjectRun( } /** - * Just a wrapper for {@link ContextUtils#testProjectRun(Builder, Path, Consumer)}. + * Just a wrapper for {@link ProjectUtils#testProjectRun(Builder, Path, Consumer)}. * * @param projDir Root directory of the project. * @param resultConsumer Any action that is to be evaluated on the result of running the {@code From e4284083053d294235324a74e3a23596f5c9c328 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 27 Jun 2024 17:48:34 +0200 Subject: [PATCH 016/111] Remove BindingMap.SymbolRestriction --- .../org/enso/compiler/data/BindingsMap.scala | 329 +----------------- .../pass/resolve/FullyQualifiedNames.scala | 3 +- .../compiler/phase/ExportsResolution.scala | 53 +-- .../interpreter/caches/ImportExportCache.java | 28 +- .../interpreter/runtime/IrToTruffle.scala | 2 +- 5 files changed, 27 insertions(+), 388 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index a179b4c9f3fa..87ce84cd7514 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -313,16 +313,8 @@ case class BindingsMap( def getDirectlyExportedModules: List[ExportedModule] = resolvedImports.collect { case ResolvedImport(_, exports, mod) => exports.map { exp => - val restriction = SymbolRestriction.Only( - Set( - SymbolRestriction.AllowedResolution( - exp.getSimpleName.name.toLowerCase, - Some(mod) - ) - ) - ) val rename = Some(exp.getSimpleName.name) - ExportedModule(mod, rename, restriction) + ExportedModule(mod, rename) } }.flatten } @@ -353,328 +345,19 @@ object BindingsMap { } } - /** Represents a symbol restriction on symbols exported from a module. */ - sealed trait SymbolRestriction { - - /** Whether the export statement allows accessing the given name. - * - * @param symbol the name to check - * @param resolution the particular resolution of `symbol` - * @return whether access to the symbol is permitted by this restriction. - */ - def canAccess(symbol: String, resolution: ResolvedName): Boolean - - /** Performs static optimizations on the restriction, simplifying - * common patterns. - * - * @return a possibly simpler version of the restriction, describing - * the same set of names. - */ - def optimize: SymbolRestriction - - /** Convert any internal [[ModuleReference]]s to abstract references. - * - * @return `this` with any module references made abstract - */ - def toAbstract: SymbolRestriction - - /** Convert any internal [[ModuleReference]]s to concrete references. - * - * @param moduleMap the mapping from qualified names to modules - * @return `this` with its module reference made concrete - */ - def toConcrete(moduleMap: ModuleMap): Option[SymbolRestriction] - } - - object SymbolRestriction { - - /** A representation of allowed symbol. An allowed symbol consists of - * a name and an optional resolution refinement. - * - * @param symbol the symbol name - * @param resolution the only allowed resolution of `symbol` - */ - case class AllowedResolution( - symbol: String, - resolution: Option[ResolvedName] - ) { - - /** Checks if the `symbol` is visible under this restriction, with - * a given resolution. - * - * @param symbol the symbol - * @param resolution `symbol`'s resolution - * @return `true` if the symbol is visible, `false` otherwise - */ - def allows(symbol: String, resolution: ResolvedName): Boolean = { - val symbolMatch = this.symbol == symbol.toLowerCase - val resolutionMatch = - this.resolution.isEmpty || this.resolution.get == resolution - symbolMatch && resolutionMatch - } - - /** Convert the internal resolution to abstract form. - * - * @return `this` with its resolution converted to abstract form - */ - def toAbstract: AllowedResolution = { - this.copy(resolution = resolution.map(_.toAbstract)) - } - - /** Convert the internal resolution to concrete form. - * - * @param moduleMap the mapping from qualified names to modules - * @return `this` with its resolution made concrete - */ - def toConcrete(moduleMap: ModuleMap): Option[AllowedResolution] = { - resolution match { - case None => Some(this) - case Some(res) => - res.toConcrete(moduleMap).map(r => this.copy(resolution = Some(r))) - } - } - } - - /** A restriction representing a set of allowed symbols. - * - * @param symbols the allowed symbols. - */ - case class Only(symbols: Set[AllowedResolution]) extends SymbolRestriction { - - /** @inheritdoc */ - override def canAccess( - symbol: String, - resolution: ResolvedName - ): Boolean = symbols.exists(_.allows(symbol, resolution)) - - /** @inheritdoc */ - override def optimize: SymbolRestriction = this - - /** @inheritdoc */ - override def toAbstract: Only = { - this.copy(symbols = symbols.map(_.toAbstract)) - } - - /** @inheritdoc */ - //noinspection DuplicatedCode - override def toConcrete(moduleMap: ModuleMap): Option[Only] = { - val newSymbols = symbols.map(_.toConcrete(moduleMap)) - if (!newSymbols.exists(_.isEmpty)) { - Some(this.copy(symbols = newSymbols.map(_.get))) - } else None - } - } - - /** A restriction representing a set of excluded symbols. - * - * @param symbols the excluded symbols. - */ - case class Hiding(symbols: Set[String]) extends SymbolRestriction { - - /** @inheritdoc */ - override def canAccess( - symbol: String, - resolution: ResolvedName - ): Boolean = !symbols.contains(symbol.toLowerCase) - - /** @inheritdoc */ - override def optimize: Hiding = this - - /** @inheritdoc */ - override def toAbstract: Hiding = this - - /** @inheritdoc */ - override def toConcrete(moduleMap: ModuleMap): Option[Hiding] = Some(this) - } - - /** A restriction meaning there's no restriction at all. - */ - case object All extends SymbolRestriction { - - /** @inheritdoc */ - override def canAccess( - symbol: String, - resolution: ResolvedName - ): Boolean = true - - /** @inheritdoc */ - override def optimize: SymbolRestriction = this - - /** @inheritdoc */ - override def toAbstract: All.type = this - - /** @inheritdoc */ - override def toConcrete(moduleMap: ModuleMap): Option[All.type] = Some( - this - ) - } - - /** A complete restriction – no symbols are permitted - */ - case object Empty extends SymbolRestriction { - - /** @inheritdoc */ - override def canAccess( - symbol: String, - resolution: ResolvedName - ): Boolean = false - - /** @inheritdoc */ - override def optimize: SymbolRestriction = this - - /** @inheritdoc */ - override def toAbstract: Empty.type = this - - /** @inheritdoc */ - override def toConcrete(moduleMap: ModuleMap): Option[Empty.type] = Some( - this - ) - } - - /** An intersection of restrictions – a symbol is allowed if all components - * allow it. - * - * @param restrictions the intersected restrictions. - */ - case class Intersect(restrictions: List[SymbolRestriction]) - extends SymbolRestriction { - - /** @inheritdoc */ - override def canAccess( - symbol: String, - resolution: ResolvedName - ): Boolean = restrictions.forall(_.canAccess(symbol, resolution)) - - /** @inheritdoc */ - //noinspection DuplicatedCode - override def optimize: SymbolRestriction = { - val optimizedTerms = restrictions.map(_.optimize) - val (intersects, otherTerms) = - optimizedTerms.partition(_.isInstanceOf[Intersect]) - val allTerms = intersects.flatMap( - _.asInstanceOf[Intersect].restrictions - ) ++ otherTerms - if (allTerms.contains(Empty)) { - return Empty - } - val unions = allTerms.filter(_.isInstanceOf[Union]) - val onlys = allTerms.collect { case only: Only => only } - val hides = allTerms.collect { case hiding: Hiding => hiding } - val combinedOnlys = onlys match { - case List() => None - case items => - Some(Only(items.map(_.symbols).reduce(_.intersect(_)))) - } - val combinedHiding = hides match { - case List() => None - case items => - Some(Hiding(items.map(_.symbols).reduce(_.union(_)))) - } - val newTerms = combinedHiding.toList ++ combinedOnlys.toList ++ unions - newTerms match { - case List() => All - case List(it) => it - case items => Intersect(items) - } - } - - /** @inheritdoc */ - override def toAbstract: Intersect = { - this.copy(restrictions = restrictions.map(_.toAbstract)) - } - - /** @inheritdoc */ - //noinspection DuplicatedCode - override def toConcrete(moduleMap: ModuleMap): Option[Intersect] = { - val newRestrictions = restrictions.map(_.toConcrete(moduleMap)) - if (!newRestrictions.exists(_.isEmpty)) { - Some(this.copy(restrictions = newRestrictions.map(_.get))) - } else None - } - } - - /** A union of restrictions – a symbol is allowed if any component allows - * it. - * - * @param restrictions the component restricitons. - */ - case class Union(restrictions: List[SymbolRestriction]) - extends SymbolRestriction { - - /** @inheritdoc */ - override def canAccess( - symbol: String, - resolution: ResolvedName - ): Boolean = restrictions.exists(_.canAccess(symbol, resolution)) - - /** @inheritdoc */ - //noinspection DuplicatedCode - override def optimize: SymbolRestriction = { - val optimizedTerms = restrictions.map(_.optimize) - val (unions, otherTerms) = - optimizedTerms.partition(_.isInstanceOf[Union]) - val allTerms = unions.flatMap( - _.asInstanceOf[Union].restrictions - ) ++ otherTerms - if (allTerms.contains(All)) { - return All - } - val intersects = allTerms.filter(_.isInstanceOf[Intersect]) - val onlys = allTerms.collect { case only: Only => only } - val hides = allTerms.collect { case hiding: Hiding => hiding } - val combinedOnlys = onlys match { - case List() => None - case items => - Some(Only(items.map(_.symbols).reduce(_.union(_)))) - } - val combinedHiding = hides match { - case List() => None - case items => - Some(Hiding(items.map(_.symbols).reduce(_.intersect(_)))) - } - val newTerms = - combinedHiding.toList ++ combinedOnlys.toList ++ intersects - newTerms match { - case List() => Empty - case List(it) => it - case items => Union(items) - } - } - - /** @inheritdoc */ - override def toAbstract: Union = { - this.copy(restrictions = restrictions.map(_.toAbstract)) - } - - /** @inheritdoc */ - //noinspection DuplicatedCode - override def toConcrete(moduleMap: ModuleMap): Option[Union] = { - val newRestrictions = restrictions.map(_.toConcrete(moduleMap)) - if (!newRestrictions.exists(_.isEmpty)) { - Some(this.copy(restrictions = newRestrictions.map(_.get))) - } else None - } - } - } - /** A representation of a resolved export statement. * * @param target the module being exported. * @param exportedAs the name it is exported as. - * @param symbols any symbol restrictions connected to the export. */ - case class ExportedModule( - target: ImportTarget, - exportedAs: Option[String], - symbols: SymbolRestriction - ) { + case class ExportedModule(target: ImportTarget, exportedAs: Option[String]) { /** Convert the internal [[ModuleReference]] to an abstract reference. * * @return `this` with its module reference made abstract */ def toAbstract: ExportedModule = { - this.copy(target = target.toAbstract, symbols = symbols.toAbstract) + this.copy(target = target.toAbstract) } /** Convert the internal [[ModuleReference]] to a concrete reference. @@ -683,10 +366,8 @@ object BindingsMap { * @return `this` with its module reference made concrete */ def toConcrete(moduleMap: ModuleMap): Option[ExportedModule] = { - target.toConcrete(moduleMap).flatMap { x => - symbols - .toConcrete(moduleMap) - .map(y => this.copy(target = x, symbols = y)) + target.toConcrete(moduleMap).map { target => + this.copy(target = target) } } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala index 1d60a44120be..52c9bf7d01ca 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala @@ -95,8 +95,7 @@ case object FullyQualifiedNames extends IRPass { scopeMap.resolvedExports.foreach { case ExportedModule( resolution @ ResolvedType(exportedModuleRef, tpe), - exportedAs, - _ + exportedAs ) => val tpeName = exportedAs.getOrElse(tpe.name) val exportedModule = exportedModuleRef.unsafeAsModule() diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala index 78344651aaad..55b7f4d7b1bf 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala @@ -2,12 +2,7 @@ package org.enso.compiler.phase import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.ModuleReference.Concrete -import org.enso.compiler.data.BindingsMap.{ - ExportedModule, - ImportTarget, - ResolvedModule, - SymbolRestriction -} +import org.enso.compiler.data.BindingsMap.{ExportedModule, ImportTarget, ResolvedConstructor, ResolvedModule} import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module @@ -25,7 +20,6 @@ class ExportsResolution(private val context: CompilerContext) { private case class Edge( exporter: Node, - symbols: SymbolRestriction, exportsAs: Option[String], exportee: Node ) @@ -62,8 +56,8 @@ class ExportsResolution(private val context: CompilerContext) { } val node = nodes(module) node.exports = exports.map { - case ExportedModule(mod, rename, restriction) => - Edge(node, restriction, rename, nodes.getOrElseUpdate(mod, Node(mod))) + case ExportedModule(mod, rename) => + Edge(node, rename, nodes.getOrElseUpdate(mod, Node(mod))) } node.exports.foreach { edge => edge.exportee.exportedBy ::= edge } } @@ -132,45 +126,25 @@ class ExportsResolution(private val context: CompilerContext) { nodes.foreach { node => val explicitlyExported = node.exports.map(edge => - ExportedModule( - edge.exportee.module, - edge.exportsAs, - edge.symbols - ) + ExportedModule(edge.exportee.module, edge.exportsAs) ) val transitivelyExported: List[ExportedModule] = explicitlyExported.flatMap { - case ExportedModule(module, _, restriction) => + case ExportedModule(module, _) => exports(module).map { - case ExportedModule(export, _, parentRestriction) => - ExportedModule( - export, - None, - SymbolRestriction.Intersect( - List(restriction, parentRestriction) - ) - ) + case ExportedModule(export, _) => + ExportedModule(export, None) } } val allExported = explicitlyExported ++ transitivelyExported - val unified = allExported - .groupBy(_.target) - .map { case (mod, items) => - val name = items.collectFirst { case ExportedModule(_, Some(n), _) => - n - } - val itemsUnion = SymbolRestriction.Union(items.map(_.symbols)) - ExportedModule(mod, name, itemsUnion) - } - .toList - exports(node.module) = unified + exports(node.module) = allExported } exports.foreach { case (module, exports) => module match { case _: BindingsMap.ResolvedType => case ResolvedModule(module) => getBindings(module.unsafeAsModule()).resolvedExports = - exports.map(ex => ex.copy(symbols = ex.symbols.optimize)) + exports.map(ex => ex.copy()) } } } @@ -183,17 +157,14 @@ class ExportsResolution(private val context: CompilerContext) { .filter(_.canExport) .map(e => (e.name, List(e.resolvedIn(module)))) val exportedModules = bindings.resolvedExports.collect { - case ExportedModule(mod, Some(name), _) + case ExportedModule(mod, Some(exportedAs)) if mod.module.unsafeAsModule() != module => - (name, List(mod)) + (exportedAs, List(mod)) } val reExportedSymbols = bindings.resolvedExports.flatMap { export => export.target.exportedSymbols .flatMap { case (sym, resolutions) => - val allowedResolutions = - resolutions.filter(res => export.symbols.canAccess(sym, res)) - if (allowedResolutions.isEmpty) None - else Some((sym, allowedResolutions)) + Some((sym, resolutions)) } } bindings.exportedSymbols = List( diff --git a/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java b/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java index 410059232f29..1ee2cf7f0333 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java @@ -207,26 +207,14 @@ static Metadata read(byte[] arr) throws IOException { @Persistable(clazz = BindingsMap.ResolvedStaticMethod.class, id = 33015) @Persistable(clazz = BindingsMap.ResolvedConversionMethod.class, id = 33016) @Persistable(clazz = BindingsMap.ExportedModule.class, id = 33017) - @Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Only.class, id = 33018) - @Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Union.class, id = 33019) - @Persistable( - clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Intersect.class, - id = 33020) - @Persistable( - clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$AllowedResolution.class, - id = 33021) - @Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$All$.class, id = 33022) - @Persistable( - clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Hiding.class, - id = 33023) - @Persistable(clazz = BindingsMap.Resolution.class, id = 33024) - @Persistable(clazz = BindingsMap.ResolvedConstructor.class, id = 33025) - @Persistable(clazz = BindingsMap.ResolvedPolyglotSymbol.class, id = 33026) - @Persistable(clazz = BindingsMap.ResolvedPolyglotField.class, id = 33027) - @Persistable(clazz = BindingsMap.ModuleMethod.class, id = 33028) - @Persistable(clazz = BindingsMap.StaticMethod.class, id = 33029) - @Persistable(clazz = BindingsMap.ConversionMethod.class, id = 33030) - @Persistable(clazz = BindingsMap.Argument.class, id = 33031) + @Persistable(clazz = BindingsMap.Resolution.class, id = 33018) + @Persistable(clazz = BindingsMap.ResolvedConstructor.class, id = 33019) + @Persistable(clazz = BindingsMap.ResolvedPolyglotSymbol.class, id = 33020) + @Persistable(clazz = BindingsMap.ResolvedPolyglotField.class, id = 33021) + @Persistable(clazz = BindingsMap.ModuleMethod.class, id = 33022) + @Persistable(clazz = BindingsMap.StaticMethod.class, id = 33023) + @Persistable(clazz = BindingsMap.ConversionMethod.class, id = 33024) + @Persistable(clazz = BindingsMap.Argument.class, id = 33025) @ServiceProvider(service = Persistance.class) public static final class PersistBindingsMap extends Persistance { public PersistBindingsMap() { diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index 0a36ec02b47f..c01200e6bb44 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -198,7 +198,7 @@ class IrToTruffle( ) bindingsMap.resolvedExports - .collect { case ExportedModule(ResolvedModule(module), _, _) => module } + .collect { case ExportedModule(ResolvedModule(module), _) => module } .foreach(exp => scopeBuilder.addExport(new ImportExportScope(exp.unsafeAsModule())) ) From 69379087117ab811fe914222268f9ed261c165ca Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 27 Jun 2024 17:51:45 +0200 Subject: [PATCH 017/111] Force compilation in ExportedSymbolsTest --- .../java/org/enso/compiler/ExportedSymbolsTest.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index 9f5ee3834c7e..1ad05a69250e 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -4,7 +4,6 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; import java.io.IOException; import java.nio.file.Files; @@ -79,8 +78,8 @@ public void transitivelyExportedSymbols() throws IOException { @Test public void exportSymbolFromDifferentModule() throws IOException { - var aMod = - new SourceModule(QualifiedName.fromString("A_Module"), """ + var mainMod = + new SourceModule(QualifiedName.fromString("Main"), """ from project.B_Module export B_Type type A_Type """); @@ -88,12 +87,12 @@ public void exportSymbolFromDifferentModule() throws IOException { new SourceModule(QualifiedName.fromString("B_Module"), """ type B_Type """); - ProjectUtils.createProject("Proj", Set.of(aMod, bMod), projDir); + ProjectUtils.createProject("Proj", Set.of(mainMod, bMod), projDir); var ctx = createCtx(projDir); compile(ctx); - var aModExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); - assertThat(aModExportedSymbols.size(), is(2)); - assertThat(aModExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type")); + var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSymbols.size(), is(2)); + assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type")); } private static Context createCtx(Path projDir) { From c9f0a36e2ec867c4fe2b346890c78fff3beaa265 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 10:30:23 +0200 Subject: [PATCH 018/111] Add test for importing a constructor --- .../test/semantic/ImportExportTest.scala | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 75054eace982..7ac98a88905b 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -279,6 +279,35 @@ class ImportExportTest .name shouldEqual "static_method" } + "be able to import constructor from type" in { + """ + |type My_Type + | Constructor + |""".stripMargin + .createModule(packageQualifiedName.createChild("My_Module")) + + val mainIr = + s""" + |import $namespace.$packageName.My_Module.My_Type.Constructor + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + val bindingMap = mainIr.unwrapBindingMap + bindingMap.resolvedImports.size shouldBe 1 + bindingMap + .resolvedImports + .head + .target shouldBe a[BindingsMap.ResolvedConstructor] + + bindingMap + .resolvedImports + .head + .target + .asInstanceOf[BindingsMap.ResolvedConstructor] + .qualifiedName + .item shouldBe "Constructor" + } + "result in error when trying to import mix of constructors and methods from a type" in { """ |type Other_Module_Type From 3a00437b801ffd0d5ff8afad738150cd7ceddf9c Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 10:43:23 +0200 Subject: [PATCH 019/111] Add test for exporting a constructor --- .../test/semantic/ImportExportTest.scala | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 7ac98a88905b..92221b453d74 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -308,6 +308,33 @@ class ImportExportTest .item shouldBe "Constructor" } + "be able to export constructor from type" in { + """ + |type Boolean + | True + | False + |""".stripMargin + .createModule(packageQualifiedName.createChild("Boolean")) + + val mainIr = + s""" + |export $namespace.$packageName.Boolean.Boolean.True + |export $namespace.$packageName.Boolean.Boolean.False + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + val bindingMap = mainIr.unwrapBindingMap + bindingMap.exportedSymbols.keys should contain theSameElementsAs List("True", "False") + bindingMap + .exportedSymbols("True") + .head + .isInstanceOf[BindingsMap.ResolvedConstructor] shouldBe true + bindingMap + .exportedSymbols("False") + .head + .isInstanceOf[BindingsMap.ResolvedConstructor] shouldBe true + } + "result in error when trying to import mix of constructors and methods from a type" in { """ |type Other_Module_Type From 1c8ec12cd17e127bcded423695445daa4864636d Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 10:46:57 +0200 Subject: [PATCH 020/111] ResolvedConsytructor is an ImportTarget --- .../phase/ImportResolverAlgorithm.java | 42 +++++++-- .../compiler/phase/ImportResolverForIR.java | 94 ++++++++++++++++--- .../org/enso/compiler/data/BindingsMap.scala | 7 +- .../pass/analyse/ImportSymbolAnalysis.scala | 8 ++ .../compiler/phase/ExportsResolution.scala | 1 + .../test/exports/ExportConstructorTest.java | 3 +- .../callable/InvokeMethodImportResolver.java | 20 +++- .../runtime/scope/ModuleScope.java | 7 ++ .../interpreter/runtime/IrToTruffle.scala | 1 + 9 files changed, 161 insertions(+), 22 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java index d9b912a41d12..8fd4fb42dba1 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java @@ -6,7 +6,7 @@ import org.enso.editions.LibraryName; public abstract class ImportResolverAlgorithm< - Result, Module, Import, Export, ResolvedType, ResolvedModule> { + Result, Module, Import, Export, ResolvedType, ResolvedModule, ResolvedConstructor> { protected ImportResolverAlgorithm() {} protected abstract String nameForImport(Import imp); @@ -17,6 +17,8 @@ protected ImportResolverAlgorithm() {} protected abstract String nameForType(ResolvedType e); + protected abstract String nameForConstructor(ResolvedConstructor cons); + /** * Returns a list of all the exports from the module of the given symbol * @@ -32,6 +34,8 @@ protected ImportResolverAlgorithm() {} protected abstract java.util.List definedEntities(Import name); + protected abstract java.util.List definedConstructors(Import name); + /** * Ensure library is loaded and load a module. * @@ -47,6 +51,9 @@ protected abstract Result createResolvedImport( protected abstract Result createResolvedType( Import imp, java.util.List exp, ResolvedType m); + protected abstract Result createResolvedConstructor( + Import imp, java.util.List exp, ResolvedConstructor cons); + protected abstract Result createErrorPackageCoundNotBeLoaded( Import imp, String impName, String loadingError); @@ -77,14 +84,16 @@ private Result tryResolveImportNew(Module module, Import imp) { var m = loadLibraryModule(libraryName, impName); if (m != null) { return createResolvedImport(imp, exp, m); - } else { - var typ = tryResolveAsTypeNew(imp); - if (typ != null) { - return createResolvedType(imp, exp, typ); - } else { - return createErrorModuleDoesNotExist(imp, impName); - } } + var typ = tryResolveAsTypeNew(imp); + if (typ != null) { + return createResolvedType(imp, exp, typ); + } + var cons = tryResolveAsConstructor(imp); + if (cons != null) { + return createResolvedConstructor(imp, exp, cons); + } + return createErrorModuleDoesNotExist(imp, impName); } catch (IOException e) { return createErrorPackageCoundNotBeLoaded(imp, impName, e.getMessage()); } @@ -102,6 +111,23 @@ private ResolvedType tryResolveAsTypeNew(Import name) { return type.orElse(null); } + private ResolvedConstructor tryResolveAsConstructor(Import imp) { + var parts = partsForImport(imp); + if (parts.size() < 3) { + return null; + } + var constructors = definedConstructors(imp); + if (constructors == null) { + return null; + } + var constrName = parts.get(parts.size() - 1); + var consOpt = constructors + .stream() + .filter(cons -> nameForConstructor(cons).equals(constrName)) + .findFirst(); + return consOpt.orElse(null); + } + public static final class HiddenNamesConflict extends RuntimeException { private HiddenNamesConflict(String message) { super(message); diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java index 1aa937b8de82..23ce190b4278 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.Objects; +import java.util.stream.Collectors; import org.enso.common.CompilationStage; import org.enso.compiler.Compiler; import org.enso.compiler.context.CompilerContext; @@ -12,6 +13,8 @@ import org.enso.compiler.core.ir.module.scope.Import; import org.enso.compiler.data.BindingsMap; import org.enso.compiler.data.BindingsMap$ModuleReference$Concrete; +import org.enso.compiler.data.BindingsMap.ResolvedConstructor; +import org.enso.compiler.data.BindingsMap.ResolvedImport; import org.enso.compiler.data.BindingsMap.ResolvedType; import org.enso.editions.LibraryName; @@ -26,7 +29,8 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm< Import.Module, Export.Module, ResolvedType, - CompilerContext.Module + CompilerContext.Module, + ResolvedConstructor > { abstract Compiler getCompiler(); @@ -50,6 +54,11 @@ protected final String nameForType(BindingsMap.ResolvedType e) { return e.qualifiedName().item(); } + @Override + protected String nameForConstructor(ResolvedConstructor cons) { + return cons.qualifiedName().item(); + } + @Override protected final java.util.List exportsFor(Module module, String impName) { java.util.List exp = CollectionConverters.SeqHasAsJava(module.exports()).asJava().stream().map(e -> switch (e) { @@ -80,16 +89,7 @@ protected final java.util.List definedEntities(Import. } var mod = optionMod.get(); compiler.ensureParsed(mod); - var bindingsMap = mod.getBindingsMap(); - if (bindingsMap == null) { - compiler.context().updateModule(mod, u -> { - u.invalidateCache(); - u.ir(null); - u.compilationStage(CompilationStage.INITIAL); - }); - compiler.ensureParsed(mod, false); - bindingsMap = mod.getBindingsMap(); - } + var bindingsMap = loadBindingsMap(mod); var entitiesStream = bindingsMap.definedEntities().map(e -> switch (e) { case BindingsMap.Type t -> { assert e.name().equals(t.name()) : e.name() + " != " + t.name(); @@ -103,6 +103,57 @@ protected final java.util.List definedEntities(Import. return entities; } + /** + * Returns list of constructors for the given import. + * @param imp The import is treated as an import of a constructor from a type. + * The last part is constructor, the second to last is type, + * the third to last is module. + * @return null if the import is not a constructor import. + */ + @Override + protected java.util.List definedConstructors(Import.Module imp) { + var parts = partsForImport(imp); + if (parts.size() < 3) { + return null; + } + var typeName = parts.get(parts.size() - 2); + var modFullName = parts + .stream() + .limit(parts.size() - 2) + .collect(Collectors.joining(".")); + var compiler = getCompiler(); + var optionMod = compiler.getModule(modFullName); + if (optionMod.isEmpty()) { + return null; + } + var mod = optionMod.get(); + compiler.ensureParsed(mod); + var bindingsMap = loadBindingsMap(mod); + var foundType = scala.jdk.javaapi.CollectionConverters.asJava(bindingsMap.definedEntities()) + .stream() + .map(e -> { + if (e instanceof BindingsMap.Type tp) { + return tp; + } + return null; + }) + .filter(Objects::nonNull) + .filter(tp -> tp.name().equals(typeName)) + .findFirst(); + if (foundType.isEmpty()) { + return null; + } + var tp = foundType.get(); + var resolvedType = new BindingsMap.ResolvedType( + new BindingsMap$ModuleReference$Concrete(mod), + tp + ); + return scala.jdk.javaapi.CollectionConverters.asJava(tp.members()) + .stream() + .map(cons -> new ResolvedConstructor(resolvedType, cons)) + .collect(Collectors.toUnmodifiableList()); + } + @Override protected final CompilerContext.Module loadLibraryModule(LibraryName libraryName, String moduleName) throws IOException { var compiler = this.getCompiler(); @@ -132,6 +183,13 @@ protected final Tuple2> createResolve return new Tuple2<>(imp, someBinding); } + @Override + protected Tuple2> createResolvedConstructor(Import.Module imp, + java.util.List exp, ResolvedConstructor cons) { + scala.Option someBinding = Option.apply(new BindingsMap.ResolvedImport(imp, toScalaList(exp), cons)); + return new Tuple2<>(imp, someBinding); + } + @Override protected final Tuple2> createErrorPackageCoundNotBeLoaded(Import.Module imp, String impName, String loadingError) { org.enso.compiler.core.ir.expression.errors.ImportExport importError = new ImportExport(imp, new ImportExport.PackageCouldNotBeLoaded(impName, loadingError), imp.passData(), imp.diagnostics()); @@ -143,6 +201,20 @@ protected final Tuple2> createErrorMo return new Tuple2<>(new ImportExport(imp, new ImportExport.ModuleDoesNotExist(impName), imp.passData(), imp.diagnostics()), Option.empty()); } + private BindingsMap loadBindingsMap(CompilerContext.Module mod) { + var bindingsMap = mod.getBindingsMap(); + if (bindingsMap == null) { + getCompiler().context().updateModule(mod, u -> { + u.invalidateCache(); + u.ir(null); + u.compilationStage(CompilationStage.INITIAL); + }); + getCompiler().ensureParsed(mod, false); + bindingsMap = mod.getBindingsMap(); + } + return bindingsMap; + } + private static List toScalaList(java.util.List qualifiedConflicts) { return CollectionConverters.ListHasAsScala(qualifiedConflicts).asScala().toList(); } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 87ce84cd7514..1e7cef4fe284 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -631,7 +631,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 = { @@ -651,6 +652,10 @@ object BindingsMap { /** @inheritdoc */ override def module: ModuleReference = tpe.module + + override def findExportedSymbolsFor(name: String): List[ResolvedName] = List() + + override def exportedSymbols: Map[String, List[ResolvedName]] = Map() } /** A representation of a name being resolved to a module. diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala index 567d1c9adda4..310e9d59a60a 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala @@ -111,6 +111,14 @@ case object ImportSymbolAnalysis extends IRPass { unresolvedSymbol.name ) ) + case BindingsMap.ResolvedConstructor(_, cons) => + errors.ImportExport( + imp, + errors.ImportExport.NoSuchConstructor( + cons.name, + unresolvedSymbol.name + ) + ) } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala index 55b7f4d7b1bf..523e834c2ac2 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala @@ -142,6 +142,7 @@ class ExportsResolution(private val context: CompilerContext) { exports.foreach { case (module, exports) => module match { case _: BindingsMap.ResolvedType => + case _: ResolvedConstructor => case ResolvedModule(module) => getBindings(module.unsafeAsModule()).resolvedExports = exports.map(ex => ex.copy()) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java index e28dd5ab74b0..6c146d51af71 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java @@ -36,7 +36,8 @@ public void exportedConstructorsAreInBindingMap() throws IOException { new SourceModule( QualifiedName.fromString("Main"), """ - from project.Boolean.Boolean export True, False + export project.Boolean.Boolean.True + export project.Boolean.Boolean.False """); var projDir = tempFolder.newFolder().toPath(); ProjectUtils.createProject("Proj", Set.of(booleanMod, mainMod), projDir); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java index 8b9892713b33..74457579fd43 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java @@ -2,6 +2,7 @@ import com.oracle.truffle.api.CompilerDirectives; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.enso.compiler.phase.ImportResolverAlgorithm; @@ -11,10 +12,11 @@ import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.data.atom.AtomConstructor; import org.enso.interpreter.runtime.scope.TopLevelScope; final class InvokeMethodImportResolver - extends ImportResolverAlgorithm { + extends ImportResolverAlgorithm { private final Module module; private final TopLevelScope topScope; @@ -52,6 +54,11 @@ protected String nameForType(Type e) { return e.getName(); } + @Override + protected String nameForConstructor(AtomConstructor cons) { + return cons.getName(); + } + @Override protected List exportsFor(Module module, String impName) { return Collections.emptyList(); @@ -67,6 +74,11 @@ protected List definedEntities(UnresolvedSymbol symbol) { return module.getScope().getAllTypes(symbol.getName()); } + @Override + protected List definedConstructors(UnresolvedSymbol symbol) { + return Collections.emptyList(); + } + @Override protected Module loadLibraryModule(LibraryName libraryName, String moduleName) throws IOException { @@ -84,6 +96,12 @@ protected EnsoObject createResolvedType(UnresolvedSymbol imp, List exp, return typ; } + @Override + protected EnsoObject createResolvedConstructor(UnresolvedSymbol imp, List exp, + AtomConstructor cons) { + throw new UnsupportedOperationException("unimplemented"); + } + @Override protected EnsoObject createErrorPackageCoundNotBeLoaded( UnresolvedSymbol imp, String impName, String loadingError) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java index 6149ee178d66..4170a5917c38 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java @@ -171,6 +171,13 @@ public List getAllTypes(String name) { return tpes; } + public List getAllTypes() { + return types + .values() + .stream() + .collect(Collectors.toUnmodifiableList()); + } + @ExportMessage.Ignore public Type getType(String name, boolean ignoreAssociatedType) { if (!ignoreAssociatedType && associatedType.getName().equals(name)) { diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index c01200e6bb44..8ad9c13c4b3e 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -210,6 +210,7 @@ class IrToTruffle( bindingsMap.resolvedImports.foreach { imp => imp.target match { case BindingsMap.ResolvedType(_, _) => + case BindingsMap.ResolvedConstructor(_, _) => case ResolvedModule(module) => val mod = module .unsafeAsModule() From 7699d17afc460fd4d06dba77cde939353d68b818 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 10:47:41 +0200 Subject: [PATCH 021/111] fmt --- .../org/enso/compiler/phase/ImportResolverAlgorithm.java | 8 ++++---- .../test/java/org/enso/compiler/ErrorCompilerTest.java | 6 ++---- .../test/java/org/enso/compiler/ExportedSymbolsTest.java | 4 +++- .../interpreter/test/exports/ExportConstructorTest.java | 4 +--- .../node/callable/InvokeMethodImportResolver.java | 8 ++++---- .../org/enso/interpreter/runtime/scope/ModuleScope.java | 5 +---- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java index 8fd4fb42dba1..e1c758cea836 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java @@ -121,10 +121,10 @@ private ResolvedConstructor tryResolveAsConstructor(Import imp) { return null; } var constrName = parts.get(parts.size() - 1); - var consOpt = constructors - .stream() - .filter(cons -> nameForConstructor(cons).equals(constrName)) - .findFirst(); + var consOpt = + constructors.stream() + .filter(cons -> nameForConstructor(cons).equals(constrName)) + .findFirst(); return consOpt.orElse(null); } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java index f28fa85d21db..b46dbe084daf 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java @@ -599,8 +599,7 @@ public void exportAllIsNotAllowed() { from project.Module export all """); var expectedReason = new Syntax.InvalidExport("`all` not allowed in `export` statement"); - assertSingleSyntaxError( - ir, expectedReason, null, 0, 30); + assertSingleSyntaxError(ir, expectedReason, null, 0, 30); } @Test @@ -609,8 +608,7 @@ public void exportHidingIsNotAllowed() { from project.Module export all hiding Foo """); var expectedReason = new Syntax.InvalidExport("`hiding` not allowed in `export` statement"); - assertSingleSyntaxError( - ir, expectedReason, null, 0, 41); + assertSingleSyntaxError(ir, expectedReason, null, 0, 41); } private void assertSingleSyntaxError( diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index 1ad05a69250e..d4372f615017 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -79,7 +79,9 @@ public void transitivelyExportedSymbols() throws IOException { @Test public void exportSymbolFromDifferentModule() throws IOException { var mainMod = - new SourceModule(QualifiedName.fromString("Main"), """ + new SourceModule( + QualifiedName.fromString("Main"), + """ from project.B_Module export B_Type type A_Type """); diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java index 6c146d51af71..11a26ba4bbbb 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java @@ -19,8 +19,7 @@ import org.junit.rules.TemporaryFolder; public class ExportConstructorTest { - @Rule - public TemporaryFolder tempFolder = new TemporaryFolder(); + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @Test public void exportedConstructorsAreInBindingMap() throws IOException { @@ -53,5 +52,4 @@ public void exportedConstructorsAreInBindingMap() throws IOException { assertThat(mainModExportedSymbols, allOf(hasKey("True"), hasKey("False"))); } } - } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java index 74457579fd43..78606e3899bd 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java @@ -2,7 +2,6 @@ import com.oracle.truffle.api.CompilerDirectives; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.enso.compiler.phase.ImportResolverAlgorithm; @@ -16,7 +15,8 @@ import org.enso.interpreter.runtime.scope.TopLevelScope; final class InvokeMethodImportResolver - extends ImportResolverAlgorithm { + extends ImportResolverAlgorithm< + EnsoObject, Module, UnresolvedSymbol, Object, Type, Module, AtomConstructor> { private final Module module; private final TopLevelScope topScope; @@ -97,8 +97,8 @@ protected EnsoObject createResolvedType(UnresolvedSymbol imp, List exp, } @Override - protected EnsoObject createResolvedConstructor(UnresolvedSymbol imp, List exp, - AtomConstructor cons) { + protected EnsoObject createResolvedConstructor( + UnresolvedSymbol imp, List exp, AtomConstructor cons) { throw new UnsupportedOperationException("unimplemented"); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java index 4170a5917c38..f5de1e783971 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java @@ -172,10 +172,7 @@ public List getAllTypes(String name) { } public List getAllTypes() { - return types - .values() - .stream() - .collect(Collectors.toUnmodifiableList()); + return types.values().stream().collect(Collectors.toUnmodifiableList()); } @ExportMessage.Ignore From 3bf93eae649ebfa85024619a9a032472d41db27a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 12:21:08 +0200 Subject: [PATCH 022/111] ResolvedMethod is ImportTarget. And ImportResolverAlgorithm can import it --- .../phase/ImportResolverAlgorithm.java | 49 +++++++++++++++++- .../compiler/phase/ImportResolverForIR.java | 51 ++++++++++++++++++- .../org/enso/compiler/data/BindingsMap.scala | 10 +++- .../pass/analyse/ImportSymbolAnalysis.scala | 26 ++++++++++ .../compiler/phase/ExportsResolution.scala | 28 +++++----- .../ir/expression/errors/ImportExport.scala | 26 ++++++++++ .../callable/InvokeMethodImportResolver.java | 19 ++++++- .../interpreter/runtime/IrToTruffle.scala | 7 ++- 8 files changed, 194 insertions(+), 22 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java index e1c758cea836..dcce205a11ec 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java @@ -6,7 +6,14 @@ import org.enso.editions.LibraryName; public abstract class ImportResolverAlgorithm< - Result, Module, Import, Export, ResolvedType, ResolvedModule, ResolvedConstructor> { + Result, + Module, + Import, + Export, + ResolvedType, + ResolvedModule, + ResolvedConstructor, + ResolvedModuleMethod> { protected ImportResolverAlgorithm() {} protected abstract String nameForImport(Import imp); @@ -19,6 +26,8 @@ protected ImportResolverAlgorithm() {} protected abstract String nameForConstructor(ResolvedConstructor cons); + protected abstract String nameForModuleMethod(ResolvedModuleMethod method); + /** * Returns a list of all the exports from the module of the given symbol * @@ -36,6 +45,8 @@ protected ImportResolverAlgorithm() {} protected abstract java.util.List definedConstructors(Import name); + protected abstract java.util.List definedModuleMethods(Import imp); + /** * Ensure library is loaded and load a module. * @@ -54,13 +65,23 @@ protected abstract Result createResolvedType( protected abstract Result createResolvedConstructor( Import imp, java.util.List exp, ResolvedConstructor cons); + protected abstract Result createResolvedModuleMethod( + Import imp, java.util.List exp, ResolvedModuleMethod moduleMethod); + protected abstract Result createErrorPackageCoundNotBeLoaded( Import imp, String impName, String loadingError); protected abstract Result createErrorModuleDoesNotExist(Import imp, String impName); /** - * Resolves given {@code Import} in context of given {@code Module}. + * Resolves given {@code Import} in context of given {@code Module}. Handles only an import of a + * specific symbol with syntax {@code import project.Module.Symbol}. The {@code from + * project.Module import Symbol} is handled by this algorithm by importing only the {@code Module} + * and not resolving the {@code Symbol}. + * + *

With the first syntax of import ({@code import project.Module.Symbol}), one case only import + * module, type from a module, constructor from a type or a module method. Static, extension and + * conversion methods can be imported only with the {@code from ... import all} syntax. * * @param module the module that wants to import something * @param imp the import to resolve @@ -93,6 +114,13 @@ private Result tryResolveImportNew(Module module, Import imp) { if (cons != null) { return createResolvedConstructor(imp, exp, cons); } + var moduleMethod = tryResolveAsModuleMethod(imp); + if (moduleMethod != null) { + return createResolvedModuleMethod(imp, exp, moduleMethod); + } + // static, extension and conversion methods can only be imported with + // `from import all` syntax. That syntax is handled by this algorithm + // by importing the `Module` only and not resolving all the symbols. return createErrorModuleDoesNotExist(imp, impName); } catch (IOException e) { return createErrorPackageCoundNotBeLoaded(imp, impName, e.getMessage()); @@ -128,6 +156,23 @@ private ResolvedConstructor tryResolveAsConstructor(Import imp) { return consOpt.orElse(null); } + private ResolvedModuleMethod tryResolveAsModuleMethod(Import imp) { + var parts = partsForImport(imp); + if (parts.size() < 3) { + return null; + } + var moduleMethods = definedModuleMethods(imp); + if (moduleMethods == null) { + return null; + } + var methodName = parts.get(parts.size() - 1); + var methodOpt = + moduleMethods.stream() + .filter(method -> nameForModuleMethod(method).equals(methodName)) + .findFirst(); + return methodOpt.orElse(null); + } + public static final class HiddenNamesConflict extends RuntimeException { private HiddenNamesConflict(String message) { super(message); diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java index 23ce190b4278..e7601da95f9d 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java @@ -15,6 +15,7 @@ import org.enso.compiler.data.BindingsMap$ModuleReference$Concrete; import org.enso.compiler.data.BindingsMap.ResolvedConstructor; import org.enso.compiler.data.BindingsMap.ResolvedImport; +import org.enso.compiler.data.BindingsMap.ResolvedModuleMethod; import org.enso.compiler.data.BindingsMap.ResolvedType; import org.enso.editions.LibraryName; @@ -30,7 +31,8 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm< Export.Module, ResolvedType, CompilerContext.Module, - ResolvedConstructor + ResolvedConstructor, + ResolvedModuleMethod > { abstract Compiler getCompiler(); @@ -59,6 +61,12 @@ protected String nameForConstructor(ResolvedConstructor cons) { return cons.qualifiedName().item(); } + @Override + protected String nameForModuleMethod(ResolvedModuleMethod resolvedModuleMethod) { + return resolvedModuleMethod.methodName(); + } + + @Override protected final java.util.List exportsFor(Module module, String impName) { java.util.List exp = CollectionConverters.SeqHasAsJava(module.exports()).asJava().stream().map(e -> switch (e) { @@ -154,6 +162,39 @@ protected java.util.List definedConstructors(Import.Module .collect(Collectors.toUnmodifiableList()); } + @Override + protected java.util.List definedModuleMethods(Import.Module imp) { + var parts = partsForImport(imp); + if (parts.size() < 3) { + return null; + } + var modMethodNameIdx = parts.size() - 1; + var modMethodName = parts.get(modMethodNameIdx); + var modFullName = parts + .stream() + .limit(modMethodNameIdx) + .collect(Collectors.joining(".")); + var compiler = getCompiler(); + var optionMod = compiler.getModule(modFullName); + if (optionMod.isEmpty()) { + return null; + } + var mod = optionMod.get(); + compiler.ensureParsed(mod); + var bindingsMap = loadBindingsMap(mod); + var modMethods = scala.jdk.javaapi.CollectionConverters.asJava(bindingsMap.definedEntities()) + .stream() + .filter(definedEntity -> { + if (definedEntity instanceof BindingsMap.ModuleMethod moduleMethod) { + return moduleMethod.name().equals(modMethodName); + } + return false; + }) + .map(entity -> new ResolvedModuleMethod(new BindingsMap$ModuleReference$Concrete(mod), (BindingsMap.ModuleMethod) entity)) + .collect(Collectors.toUnmodifiableList()); + return modMethods; + } + @Override protected final CompilerContext.Module loadLibraryModule(LibraryName libraryName, String moduleName) throws IOException { var compiler = this.getCompiler(); @@ -190,6 +231,14 @@ protected Tuple2> createResolvedConstructor(Impor return new Tuple2<>(imp, someBinding); } + @Override + protected Tuple2> createResolvedModuleMethod(Import.Module imp, + java.util.List exp, ResolvedModuleMethod resolvedModuleMethod) { + scala.Option someBinding = Option.apply(new BindingsMap.ResolvedImport(imp, toScalaList(exp), resolvedModuleMethod)); + return new Tuple2<>(imp, someBinding); + } + + @Override protected final Tuple2> createErrorPackageCoundNotBeLoaded(Import.Module imp, String impName, String loadingError) { org.enso.compiler.core.ir.expression.errors.ImportExport importError = new ImportExport(imp, new ImportExport.PackageCouldNotBeLoaded(impName, loadingError), imp.passData(), imp.diagnostics()); diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 1e7cef4fe284..9b8799b2abb9 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -653,7 +653,8 @@ object BindingsMap { /** @inheritdoc */ override def module: ModuleReference = tpe.module - override def findExportedSymbolsFor(name: String): List[ResolvedName] = List() + override def findExportedSymbolsFor(name: String): List[ResolvedName] = + List() override def exportedSymbols: Map[String, List[ResolvedName]] = Map() } @@ -694,8 +695,13 @@ object BindingsMap { .exportedSymbols } - sealed trait ResolvedMethod extends ResolvedName { + sealed trait ResolvedMethod extends ResolvedName with ImportTarget { def methodName: String + + override def findExportedSymbolsFor(name: String): List[ResolvedName] = + List() + + override def exportedSymbols: Map[String, List[ResolvedName]] = Map() } /** A representation of a resolved method defined directly on module. diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala index 310e9d59a60a..73c6dbdec0b9 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala @@ -119,6 +119,32 @@ case object ImportSymbolAnalysis extends IRPass { unresolvedSymbol.name ) ) + case BindingsMap.ResolvedModuleMethod(_, method) => + errors.ImportExport( + imp, + errors.ImportExport.NoSuchModuleMethod( + method.name, + unresolvedSymbol.name + ) + ) + case BindingsMap.ResolvedStaticMethod(mod, staticMethod) => + errors.ImportExport( + imp, + errors.ImportExport.NoSuchStaticMethod( + moduleName = mod.getName.toString, + typeName = staticMethod.tpName, + methodName = unresolvedSymbol.name + ) + ) + case BindingsMap.ResolvedConversionMethod(mod, conversionMethod) => + errors.ImportExport( + imp, + errors.ImportExport.NoSuchConversionMethod( + moduleName = mod.getName.toString, + targetTypeName = conversionMethod.targetTpName, + sourceTypeName = conversionMethod.sourceTpName + ) + ) } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala index 523e834c2ac2..b125908884b9 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala @@ -2,7 +2,11 @@ package org.enso.compiler.phase import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.ModuleReference.Concrete -import org.enso.compiler.data.BindingsMap.{ExportedModule, ImportTarget, ResolvedConstructor, ResolvedModule} +import org.enso.compiler.data.BindingsMap.{ + ExportedModule, + ImportTarget, + ResolvedModule +} import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module @@ -55,9 +59,8 @@ class ExportsResolution(private val context: CompilerContext) { Nil } val node = nodes(module) - node.exports = exports.map { - case ExportedModule(mod, rename) => - Edge(node, rename, nodes.getOrElseUpdate(mod, Node(mod))) + node.exports = exports.map { case ExportedModule(mod, rename) => + Edge(node, rename, nodes.getOrElseUpdate(mod, Node(mod))) } node.exports.foreach { edge => edge.exportee.exportedBy ::= edge } } @@ -129,23 +132,20 @@ class ExportsResolution(private val context: CompilerContext) { ExportedModule(edge.exportee.module, edge.exportsAs) ) val transitivelyExported: List[ExportedModule] = - explicitlyExported.flatMap { - case ExportedModule(module, _) => - exports(module).map { - case ExportedModule(export, _) => - ExportedModule(export, None) - } + explicitlyExported.flatMap { case ExportedModule(module, _) => + exports(module).map { case ExportedModule(export, _) => + ExportedModule(export, None) + } } val allExported = explicitlyExported ++ transitivelyExported exports(node.module) = allExported } - exports.foreach { case (module, exports) => - module match { - case _: BindingsMap.ResolvedType => - case _: ResolvedConstructor => + exports.foreach { case (target, exports) => + target match { case ResolvedModule(module) => getBindings(module.unsafeAsModule()).resolvedExports = exports.map(ex => ex.copy()) + case _ => } } } diff --git a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/errors/ImportExport.scala b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/errors/ImportExport.scala index dc0c9b157295..19d76c308c8b 100644 --- a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/errors/ImportExport.scala +++ b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/errors/ImportExport.scala @@ -173,6 +173,32 @@ object ImportExport { s"No such constructor ${constructorName} in type $typeName" } + case class NoSuchModuleMethod( + moduleName: String, + methodName: String + ) extends Reason { + override def message(source: (IdentifiedLocation => String)): String = + s"No such method ${methodName} on module $moduleName" + } + + case class NoSuchStaticMethod( + moduleName: String, + typeName: String, + methodName: String + ) extends Reason { + override def message(source: (IdentifiedLocation => String)): String = + s"No such static method ${methodName} on type $typeName in module $moduleName" + } + + case class NoSuchConversionMethod( + moduleName: String, + targetTypeName: String, + sourceTypeName: String + ) extends Reason { + override def message(source: (IdentifiedLocation => String)): String = + s"No such conversion method from $sourceTypeName to $targetTypeName in module $moduleName" + } + case class ExportSymbolsFromPrivateModule( moduleName: String ) extends Reason { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java index 78606e3899bd..b2512eace722 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java @@ -9,6 +9,7 @@ import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.Module; import org.enso.interpreter.runtime.callable.UnresolvedSymbol; +import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.atom.AtomConstructor; @@ -16,7 +17,7 @@ final class InvokeMethodImportResolver extends ImportResolverAlgorithm< - EnsoObject, Module, UnresolvedSymbol, Object, Type, Module, AtomConstructor> { + EnsoObject, Module, UnresolvedSymbol, Object, Type, Module, AtomConstructor, Function> { private final Module module; private final TopLevelScope topScope; @@ -59,6 +60,11 @@ protected String nameForConstructor(AtomConstructor cons) { return cons.getName(); } + @Override + protected String nameForModuleMethod(Function function) { + return function.getName(); + } + @Override protected List exportsFor(Module module, String impName) { return Collections.emptyList(); @@ -79,6 +85,11 @@ protected List definedConstructors(UnresolvedSymbol symbol) { return Collections.emptyList(); } + @Override + protected List definedModuleMethods(UnresolvedSymbol symbol) { + return Collections.emptyList(); + } + @Override protected Module loadLibraryModule(LibraryName libraryName, String moduleName) throws IOException { @@ -102,6 +113,12 @@ protected EnsoObject createResolvedConstructor( throw new UnsupportedOperationException("unimplemented"); } + @Override + protected EnsoObject createResolvedModuleMethod( + UnresolvedSymbol imp, List exp, Function function) { + throw new UnsupportedOperationException("unimplemented"); + } + @Override protected EnsoObject createErrorPackageCoundNotBeLoaded( UnresolvedSymbol imp, String impName, String loadingError) { diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index 8ad9c13c4b3e..888c5de8125f 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -209,8 +209,11 @@ class IrToTruffle( bindingsMap.resolvedImports.foreach { imp => imp.target match { - case BindingsMap.ResolvedType(_, _) => - case BindingsMap.ResolvedConstructor(_, _) => + case _: BindingsMap.ResolvedType => + case _: BindingsMap.ResolvedConstructor => + case _: BindingsMap.ResolvedModuleMethod => + case _: BindingsMap.ResolvedStaticMethod => + case _: BindingsMap.ResolvedConversionMethod => case ResolvedModule(module) => val mod = module .unsafeAsModule() From 21218cb93e94a2643ba05cf075f903821693a129 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 12:27:00 +0200 Subject: [PATCH 023/111] Fix ExportCycleDetectionTest - reported order of module is non-deterministic. We don't care about it. --- .../org/enso/compiler/ExportCycleDetectionTest.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java index 6579f9f52be4..72079323dcce 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java @@ -53,8 +53,8 @@ public void detectCycleInTwoModules() throws IOException { projDir, allOf( containsString("Export statements form a cycle"), - containsString("local.Proj.B_Module exports local.Proj.A_Module"), - containsString("which exports local.Proj.B_Module"))); + containsString("local.Proj.A_Module"), + containsString("local.Proj.B_Module"))); } @Test @@ -89,13 +89,15 @@ public void detectCycleInThreeModules() throws IOException { """); var projDir = tempFolder.newFolder().toPath(); ProjectUtils.createProject("Proj", Set.of(aMod, bMod, cMod, mainMod), projDir); + // The reported ordering of cycle error is non-deterministic. We don't care about it. + // Just check that all modules are involved in the cycle error report. expectProjectCompilationError( projDir, allOf( containsString("Export statements form a cycle"), - containsString("local.Proj.B_Module exports local.Proj.C_Module"), - containsString("which exports local.Proj.A_Module"), - containsString("which exports local.Proj.B_Module"))); + containsString("local.Proj.A_Module"), + containsString("local.Proj.B_Module"), + containsString("local.Proj.C_Module"))); } private void expectProjectCompilationError(Path projDir, Matcher errMsgMatcher) { From c9b881d264b3c75d924bfabfd0c781ec7a076c9f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 12:29:08 +0200 Subject: [PATCH 024/111] Fix ExportStaticMethodTest. module method can be exported directly. And static method can be exported by name. --- .../interpreter/test/exports/ExportStaticMethodTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java index 8506ee78d934..f3054be54f4e 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; @@ -93,7 +94,7 @@ public void moduleMethodIsInBindingMap() throws IOException { new SourceModule( QualifiedName.fromString("Main"), """ - from project.T_Module export module_method + export project.T_Module.module_method """); var projDir = tempFolder.newFolder().toPath(); ProjectUtils.createProject("Proj", Set.of(tMod, mainMod), projDir); @@ -135,7 +136,7 @@ public void staticMethodIsInBindingMap() throws IOException { var polyCtx = new PolyglotContext(ctx); polyCtx.getTopScope().compile(true); var mainModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); - assertThat(mainModExportedSymbols.size(), is(1)); + assertThat(mainModExportedSymbols.size(), is(greaterThanOrEqualTo(1))); assertThat(mainModExportedSymbols, hasKey("static_method")); } } From 02e2eb1b11bedad5fe058a9d3235cf1258b69992 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 12:32:11 +0200 Subject: [PATCH 025/111] Enable test for module method resolution --- .../test/semantic/ImportExportTest.scala | 40 +++---------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 92221b453d74..805dac1bfbb7 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -230,53 +230,25 @@ class ImportExportTest ) } - // TODO[pm]: will be addressed in https://github.com/enso-org/enso/issues/6729 - "resolve static method from a module" ignore { + "resolve module method" in { """ - |static_method = + |module_method = | 42 |""".stripMargin .createModule(packageQualifiedName.createChild("A_Module")) - val bIr = - s""" - |import $namespace.$packageName.A_Module.static_method - |""".stripMargin - .createModule(packageQualifiedName.createChild("B_Module")) - .getIr val mainIr = s""" - |from $namespace.$packageName.A_Module import static_method + |import $namespace.$packageName.A_Module.module_method |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - mainIr.imports.head.isInstanceOf[errors.ImportExport] shouldBe false - bIr.imports.head.isInstanceOf[errors.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 - .item shouldEqual "A_Module" - mainBindingMap - .resolvedImports(1) - .target - .asInstanceOf[BindingsMap.ResolvedModuleMethod] - .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 + mainBindingMap.resolvedImports.size shouldEqual 1 + mainBindingMap.resolvedImports.head.target .asInstanceOf[BindingsMap.ResolvedModuleMethod] .method - .name shouldEqual "static_method" + .name shouldEqual "module_method" } "be able to import constructor from type" in { From 3de9c67f198c5c6bbb808ce6294ddf11ba16f972 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 12:32:31 +0200 Subject: [PATCH 026/111] fmt --- .../test/semantic/ImportExportTest.scala | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 805dac1bfbb7..1e1a97a74f7b 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -260,21 +260,17 @@ class ImportExportTest val mainIr = s""" - |import $namespace.$packageName.My_Module.My_Type.Constructor - |""".stripMargin + |import $namespace.$packageName.My_Module.My_Type.Constructor + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr val bindingMap = mainIr.unwrapBindingMap bindingMap.resolvedImports.size shouldBe 1 - bindingMap - .resolvedImports - .head - .target shouldBe a[BindingsMap.ResolvedConstructor] + bindingMap.resolvedImports.head.target shouldBe a[ + BindingsMap.ResolvedConstructor + ] - bindingMap - .resolvedImports - .head - .target + bindingMap.resolvedImports.head.target .asInstanceOf[BindingsMap.ResolvedConstructor] .qualifiedName .item shouldBe "Constructor" @@ -290,13 +286,16 @@ class ImportExportTest val mainIr = s""" - |export $namespace.$packageName.Boolean.Boolean.True - |export $namespace.$packageName.Boolean.Boolean.False - |""".stripMargin + |export $namespace.$packageName.Boolean.Boolean.True + |export $namespace.$packageName.Boolean.Boolean.False + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr val bindingMap = mainIr.unwrapBindingMap - bindingMap.exportedSymbols.keys should contain theSameElementsAs List("True", "False") + bindingMap.exportedSymbols.keys should contain theSameElementsAs List( + "True", + "False" + ) bindingMap .exportedSymbols("True") .head From f457a3e72eeb759515fc83fb74ddd14e1de12477 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 18:36:56 +0200 Subject: [PATCH 027/111] Importing symbols from methods is not allowed --- .../pass/analyse/ImportSymbolAnalysis.scala | 61 +++++++++++++++++++ .../test/semantic/ImportExportTest.scala | 4 +- .../ir/expression/errors/ImportExport.scala | 8 +++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala index 73c6dbdec0b9..1920eb501d2d 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala @@ -85,10 +85,71 @@ case object ImportSymbolAnalysis extends IRPass { } case None => List(imp) } + // Importing symbols from methods is not allowed. The following code checks that if the + // import is importing all from a method, an error is reported. + case imp @ Import.Module( + _, + _, + isAll, + _, + _, + _, + isSynthetic, + _, + _ + ) if isAll && !isSynthetic => + bindingMap.resolvedImports.find(_.importDef == imp) match { + case Some(resolvedImport) => + val importedTarget = resolvedImport.target + importedTarget match { + case BindingsMap.ResolvedModuleMethod(module, method) => + val err = createImportFromMethodError( + imp, + module.getName.toString, + method.name + ) + List(err) + case BindingsMap.ResolvedStaticMethod(module, staticMethod) => + val err = createImportFromMethodError( + imp, + module.getName.createChild(staticMethod.tpName).toString, + staticMethod.methodName + ) + List(err) + case BindingsMap.ResolvedConversionMethod( + module, + conversionMethod + ) => + val err = createImportFromMethodError( + imp, + module.getName + .createChild(conversionMethod.targetTpName) + .toString, + conversionMethod.methodName + ) + List(err) + case _ => List(imp) + } + case None => List(imp) + } case _ => List(imp) } } + private def createImportFromMethodError( + imp: Import, + moduleName: String, + methodName: String + ): errors.ImportExport = { + errors.ImportExport( + imp, + errors.ImportExport.IllegalImportFromMethod( + moduleName, + methodName + ) + ) + } + private def createErrorForUnresolvedSymbol( imp: Import, importTarget: BindingsMap.ImportTarget, diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 1e1a97a74f7b..e8c9d924240d 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -344,8 +344,8 @@ class ImportExportTest mainIr.imports.head .asInstanceOf[errors.ImportExport] .reason - .asInstanceOf[errors.ImportExport.ModuleDoesNotExist] - .name should include("static_method") + .asInstanceOf[errors.ImportExport.IllegalImportFromMethod] + .methodName should include("static_method") } "result in error when trying to import anything from a non-existing symbol" in { diff --git a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/errors/ImportExport.scala b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/errors/ImportExport.scala index 19d76c308c8b..fa73078a7585 100644 --- a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/errors/ImportExport.scala +++ b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/errors/ImportExport.scala @@ -165,6 +165,14 @@ object ImportExport { s"The symbol $symbolName (module, type, method, or constructor) does not exist in $moduleOrTypeName." } + case class IllegalImportFromMethod( + moduleName: String, + methodName: String + ) extends Reason { + override def message(source: (IdentifiedLocation => String)): String = + s"Cannot import symbols from method '$moduleName.$methodName'" + } + case class NoSuchConstructor( typeName: String, constructorName: String From ba52b804a8d690d155a04dc33ddde262bb0f594d Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 18:37:36 +0200 Subject: [PATCH 028/111] Export tests use `export ...` syntax instead of `from ... export ...`. Related to #10399 --- .../src/test/java/org/enso/compiler/ExportedSymbolsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index d4372f615017..a30d291f911e 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -82,7 +82,7 @@ public void exportSymbolFromDifferentModule() throws IOException { new SourceModule( QualifiedName.fromString("Main"), """ - from project.B_Module export B_Type + export project.B_Module.B_Type type A_Type """); var bMod = From 96d60b6b4c91c6caa9d513dad52fb256683cbf44 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 18:39:48 +0200 Subject: [PATCH 029/111] Remove syntax error from test --- .../org/enso/compiler/test/semantic/ImportExportTest.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index e8c9d924240d..3175ecb0cd38 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -1091,9 +1091,6 @@ class ImportExportTest |# 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")) From 2526360bcfff6b84b932de03886b0b59169819e1 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 19:38:57 +0200 Subject: [PATCH 030/111] Test exports resolution graph building --- .../enso/compiler/phase/exports/Edge.scala | 7 ++ .../{ => exports}/ExportsResolution.scala | 28 ++----- .../enso/compiler/phase/exports/Node.scala | 10 +++ .../test/semantic/ImportExportTest.scala | 81 +++++++++++++++++++ 4 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Edge.scala rename engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/{ => exports}/ExportsResolution.scala (91%) create mode 100644 engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Edge.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Edge.scala new file mode 100644 index 000000000000..68dbcf05b9b5 --- /dev/null +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Edge.scala @@ -0,0 +1,7 @@ +package org.enso.compiler.phase.exports + +case class Edge( + exporter: Node, + exportsAs: Option[String], + exportee: Node +) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala similarity index 91% rename from engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala rename to engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index b125908884b9..f204ebf86ba8 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -9,6 +9,7 @@ import org.enso.compiler.data.BindingsMap.{ } import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module +import org.enso.compiler.phase.exports.{Edge, Node} import scala.collection.mutable @@ -22,22 +23,9 @@ case class ExportCycleException(modules: List[Module]) class ExportsResolution(private val context: CompilerContext) { - private case class Edge( - exporter: Node, - exportsAs: Option[String], - exportee: Node - ) - - private case class Node( - module: ImportTarget - ) { - var exports: List[Edge] = List() - var exportedBy: List[Edge] = List() - } - private def getBindings(module: Module): BindingsMap = module.getBindingsMap() - private def buildGraph(modules: List[Module]): List[Node] = { + def buildGraph(modules: List[Module]): List[Node] = { val moduleTargets = modules.map(m => ResolvedModule(Concrete(m))) val nodes = mutable.Map[ImportTarget, Node]( moduleTargets.map(mod => (mod, Node(mod))): _* @@ -129,7 +117,7 @@ class ExportsResolution(private val context: CompilerContext) { nodes.foreach { node => val explicitlyExported = node.exports.map(edge => - ExportedModule(edge.exportee.module, edge.exportsAs) + ExportedModule(edge.exportee.target, edge.exportsAs) ) val transitivelyExported: List[ExportedModule] = explicitlyExported.flatMap { case ExportedModule(module, _) => @@ -138,7 +126,7 @@ class ExportsResolution(private val context: CompilerContext) { } } val allExported = explicitlyExported ++ transitivelyExported - exports(node.module) = allExported + exports(node.target) = allExported } exports.foreach { case (target, exports) => target match { @@ -197,13 +185,13 @@ class ExportsResolution(private val context: CompilerContext) { val cycles = findCycles(graph) if (cycles.nonEmpty) { throw ExportCycleException( - cycles.head.map(_.module.module.unsafeAsModule()) + cycles.head.map(_.target.module.unsafeAsModule()) ) } val tops = topsort(graph) resolveExports(tops) - val topModules = tops.map(_.module) - resolveExportedSymbols(tops.map(_.module).collect { + val topModules = tops.map(_.target) + resolveExportedSymbols(tops.map(_.target).collect { case m: ResolvedModule => m.module.unsafeAsModule() }) // Take _last_ occurrence of each module @@ -216,7 +204,7 @@ class ExportsResolution(private val context: CompilerContext) { def runSort(modules: List[Module]): List[Module] = { val graph = buildGraph(modules) val tops = topsort(graph) - val topModules = tops.map(_.module) + val topModules = tops.map(_.target) topModules.map(_.module.unsafeAsModule()).reverse.distinct.reverse } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala new file mode 100644 index 000000000000..cd54bb0f0e28 --- /dev/null +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala @@ -0,0 +1,10 @@ +package org.enso.compiler.phase.exports + +import org.enso.compiler.data.BindingsMap.ImportTarget + +case class Node( + target: ImportTarget +) { + var exports: List[Edge] = List() + var exportedBy: List[Edge] = List() +} diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 3175ecb0cd38..80fe7fe33d66 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -5,6 +5,7 @@ import org.enso.compiler.core.ir.{Module, ProcessingPass, Warning} import org.enso.compiler.core.ir.expression.errors import org.enso.compiler.core.ir.module.scope.Import import org.enso.compiler.data.BindingsMap +import org.enso.compiler.phase.ExportsResolution import org.enso.compiler.pass.analyse.{BindingAnalysis, GatherDiagnostics} import org.enso.interpreter.runtime import org.enso.interpreter.runtime.EnsoContext @@ -12,6 +13,7 @@ import org.enso.persist.Persistance import org.enso.pkg.QualifiedName import org.enso.common.LanguageInfo import org.enso.common.MethodNames +import org.enso.compiler.phase.exports.Node import org.enso.polyglot.RuntimeOptions import org.graalvm.polyglot.{Context, Engine} import org.scalatest.BeforeAndAfter @@ -89,6 +91,15 @@ class ImportExportTest } } + private def buildExportsGraph( + modules: List[org.enso.interpreter.runtime.Module] + ): List[Node] = { + val compilerCtx = langCtx.getCompiler.context + val exportsResolution = new ExportsResolution(compilerCtx) + val compilerModules = modules.map(_.asCompilerModule()) + exportsResolution.buildGraph(compilerModules) + } + before { ctx.enter() } @@ -1354,4 +1365,74 @@ class ImportExportTest diags.size shouldEqual 0 } } + + "Exports graph building" should { + // Directly export a single module + "build exports graph for a module" in { + val aModule = + """ + |# Blank on purpose + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |export $namespace.$packageName.A_Module + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val graph = buildExportsGraph(List(aModule, bModule)) + val aNode = graph.find { node => + node.target match { + case BindingsMap.ResolvedModule(modRef) + if modRef.getName.item == "A_Module" => + true + case _ => false + } + } + aNode shouldBe defined + val aNodeExporter = aNode.get.exportedBy.head.exporter + aNodeExporter.target + .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true + aNodeExporter.target + .asInstanceOf[BindingsMap.ResolvedModule] + .qualifiedName + .item shouldBe "B_Module" + } + + "build exports graph for static method" in { + val aModule = + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |from $namespace.$packageName.A_Module export static_method + | + |type B_Type + | B_Constructor val + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val graph = buildExportsGraph(List(aModule, bModule)) + val staticMethodNode = graph.find(node => + node.target.isInstanceOf[BindingsMap.ResolvedStaticMethod] + ) + staticMethodNode shouldBe defined + val staticMethodNodeExporter = + staticMethodNode.get.exportedBy.head.exporter + staticMethodNodeExporter.target + .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true + staticMethodNodeExporter.target + .asInstanceOf[BindingsMap.ResolvedModule] + .qualifiedName + .item shouldBe "B_Module" + } + } } From 175f18fac8c6e4402079cfb67e40794782d4c3ef Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 28 Jun 2024 19:41:52 +0200 Subject: [PATCH 031/111] Test exports resolution graph for type --- .../test/semantic/ImportExportTest.scala | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 80fe7fe33d66..bd3311add2dd 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -386,7 +386,6 @@ class ImportExportTest |""".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")) @@ -1088,7 +1087,7 @@ class ImportExportTest "Import resolution for three modules" should { - "not resolve symbol that is not explicitly exported" in { + "XX not resolve symbol that is not explicitly exported" in { """ |type A_Type | A_Constructor @@ -1105,15 +1104,21 @@ class ImportExportTest |""".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 + 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 + val bBindingMap = bIr.unwrapBindingMap + // TODO: bBindingsMap.resolvedExports? + bBindingMap.exportedSymbols.keys should contain theSameElementsAs List( + "static_method", + "B_Type" + ) val mainIr = s""" @@ -1434,5 +1439,46 @@ class ImportExportTest .qualifiedName .item shouldBe "B_Module" } + + "build exports graph for type" in { + val aModule = + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |static_method = + | local_var = 42 + | local_var + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |from $namespace.$packageName.A_Module export A_Type + | + |type B_Type + | B_Constructor val + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val graph = buildExportsGraph(List(aModule, bModule)) + val typeNode = graph.find(node => + node.target match { + case BindingsMap.ResolvedType(_, tp) => + tp.name == "A_Type" + case _ => false + } + ) + typeNode shouldBe defined + val typeNodeExporter = + typeNode.get.exportedBy.head.exporter + typeNodeExporter.target + .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true + typeNodeExporter.target + .asInstanceOf[BindingsMap.ResolvedModule] + .qualifiedName + .item shouldBe "B_Module" + + } } } From 6c1db95f050716aa78cfef457cb20d0133648ed9 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 11:34:58 +0200 Subject: [PATCH 032/111] Rename Node.target back to Node.module --- .../compiler/phase/exports/ExportsResolution.scala | 12 ++++++------ .../scala/org/enso/compiler/phase/exports/Node.scala | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index f204ebf86ba8..7ef01db35d79 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -117,7 +117,7 @@ class ExportsResolution(private val context: CompilerContext) { nodes.foreach { node => val explicitlyExported = node.exports.map(edge => - ExportedModule(edge.exportee.target, edge.exportsAs) + ExportedModule(edge.exportee.module, edge.exportsAs) ) val transitivelyExported: List[ExportedModule] = explicitlyExported.flatMap { case ExportedModule(module, _) => @@ -126,7 +126,7 @@ class ExportsResolution(private val context: CompilerContext) { } } val allExported = explicitlyExported ++ transitivelyExported - exports(node.target) = allExported + exports(node.module) = allExported } exports.foreach { case (target, exports) => target match { @@ -185,13 +185,13 @@ class ExportsResolution(private val context: CompilerContext) { val cycles = findCycles(graph) if (cycles.nonEmpty) { throw ExportCycleException( - cycles.head.map(_.target.module.unsafeAsModule()) + cycles.head.map(_.module.module.unsafeAsModule()) ) } val tops = topsort(graph) resolveExports(tops) - val topModules = tops.map(_.target) - resolveExportedSymbols(tops.map(_.target).collect { + val topModules = tops.map(_.module) + resolveExportedSymbols(tops.map(_.module).collect { case m: ResolvedModule => m.module.unsafeAsModule() }) // Take _last_ occurrence of each module @@ -204,7 +204,7 @@ class ExportsResolution(private val context: CompilerContext) { def runSort(modules: List[Module]): List[Module] = { val graph = buildGraph(modules) val tops = topsort(graph) - val topModules = tops.map(_.target) + val topModules = tops.map(_.module) topModules.map(_.module.unsafeAsModule()).reverse.distinct.reverse } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala index cd54bb0f0e28..5fbe1616cdfe 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala @@ -3,7 +3,7 @@ package org.enso.compiler.phase.exports import org.enso.compiler.data.BindingsMap.ImportTarget case class Node( - target: ImportTarget + module: ImportTarget ) { var exports: List[Edge] = List() var exportedBy: List[Edge] = List() From b6ed712e8028bd40b857c7a7364c07e41ca0aaf6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 11:35:25 +0200 Subject: [PATCH 033/111] Fix tests - Exports graph nodes targets only modules --- .../test/semantic/ImportExportTest.scala | 95 ++++++++++--------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index bd3311add2dd..e900682774de 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -1372,6 +1372,30 @@ class ImportExportTest } "Exports graph building" should { + def assertAModExportedByBMod(graph: List[Node]): Unit = { + withClue("There should be only A_Module and B_Module nodes") { + graph.size shouldBe 2 + } + val aModNode = graph.find(node => + node.module match { + case BindingsMap.ResolvedModule(modRef) => + modRef.getName.item == "A_Module" + case _ => false + } + ) + aModNode shouldBe defined + val aModNodeExporter = + aModNode.get.exportedBy.head.exporter + withClue("A_Module should be exported by B_Module") { + aModNodeExporter.module + .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true + aModNodeExporter.module + .asInstanceOf[BindingsMap.ResolvedModule] + .qualifiedName + .item shouldBe "B_Module" + } + } + // Directly export a single module "build exports graph for a module" in { val aModule = @@ -1386,22 +1410,7 @@ class ImportExportTest |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val graph = buildExportsGraph(List(aModule, bModule)) - val aNode = graph.find { node => - node.target match { - case BindingsMap.ResolvedModule(modRef) - if modRef.getName.item == "A_Module" => - true - case _ => false - } - } - aNode shouldBe defined - val aNodeExporter = aNode.get.exportedBy.head.exporter - aNodeExporter.target - .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true - aNodeExporter.target - .asInstanceOf[BindingsMap.ResolvedModule] - .qualifiedName - .item shouldBe "B_Module" + assertAModExportedByBMod(graph) } "build exports graph for static method" in { @@ -1426,18 +1435,7 @@ class ImportExportTest |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val graph = buildExportsGraph(List(aModule, bModule)) - val staticMethodNode = graph.find(node => - node.target.isInstanceOf[BindingsMap.ResolvedStaticMethod] - ) - staticMethodNode shouldBe defined - val staticMethodNodeExporter = - staticMethodNode.get.exportedBy.head.exporter - staticMethodNodeExporter.target - .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true - staticMethodNodeExporter.target - .asInstanceOf[BindingsMap.ResolvedModule] - .qualifiedName - .item shouldBe "B_Module" + assertAModExportedByBMod(graph) } "build exports graph for type" in { @@ -1462,23 +1460,32 @@ class ImportExportTest |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val graph = buildExportsGraph(List(aModule, bModule)) - val typeNode = graph.find(node => - node.target match { - case BindingsMap.ResolvedType(_, tp) => - tp.name == "A_Type" - case _ => false - } - ) - typeNode shouldBe defined - val typeNodeExporter = - typeNode.get.exportedBy.head.exporter - typeNodeExporter.target - .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true - typeNodeExporter.target - .asInstanceOf[BindingsMap.ResolvedModule] - .qualifiedName - .item shouldBe "B_Module" + assertAModExportedByBMod(graph) + } + + "No exports graph is constructed when only import is used" in { + val aModule = + """ + |type A_Type + | A_Constructor + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + val bModule = + s""" + |from $namespace.$packageName.A_Module import A_Type + | + |main = + | A_Type.A_Constructor + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val graph = buildExportsGraph(List(aModule, bModule)) + withClue("Only two modules are defined") { + graph.size shouldBe 2 + } + withClue("There should be no exports") { + graph.forall(node => node.exports.isEmpty) shouldBe true + } } } } From e0a682ea42715932f2f9f976a460223f4697de3a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 12:30:32 +0200 Subject: [PATCH 034/111] Move ExportsResolution to separate package --- .../src/main/scala/org/enso/compiler/Compiler.scala | 8 ++------ .../scala/org/enso/compiler/phase/BuiltinsIrBuilder.scala | 2 ++ .../enso/compiler/phase/exports/ExportsResolution.scala | 3 +-- .../enso/compiler/test/pass/resolve/GlobalNamesTest.scala | 2 +- .../enso/compiler/test/semantic/ImportExportTest.scala | 2 +- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala index 03b7329e7f4f..b4372bae5ab5 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala @@ -25,15 +25,11 @@ import org.enso.compiler.core.EnsoParser import org.enso.compiler.data.CompilerConfig import org.enso.compiler.pass.PassManager import org.enso.compiler.pass.analyse._ -import org.enso.compiler.phase.{ - ExportCycleException, - ExportsResolution, - ImportResolver, - ImportResolverAlgorithm -} +import org.enso.compiler.phase.{ImportResolver, ImportResolverAlgorithm} import org.enso.editions.LibraryName import org.enso.pkg.QualifiedName import org.enso.common.CompilationStage +import org.enso.compiler.phase.exports.{ExportCycleException, ExportsResolution} import org.enso.syntax2.Tree import java.io.PrintStream diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/BuiltinsIrBuilder.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/BuiltinsIrBuilder.scala index 0778a44a56b8..255aa98a2f99 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/BuiltinsIrBuilder.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/BuiltinsIrBuilder.scala @@ -9,6 +9,8 @@ import org.enso.compiler.context.{ } import org.enso.compiler.data.CompilerConfig import org.enso.common.CompilationStage +import org.enso.compiler.phase.exports.ExportsResolution + import scala.util.Using /** A phase responsible for initializing the builtins' IR from the provided diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 7ef01db35d79..7c7d0bc5ddda 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -1,4 +1,4 @@ -package org.enso.compiler.phase +package org.enso.compiler.phase.exports import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.ModuleReference.Concrete @@ -9,7 +9,6 @@ import org.enso.compiler.data.BindingsMap.{ } import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module -import org.enso.compiler.phase.exports.{Edge, Node} import scala.collection.mutable diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/GlobalNamesTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/GlobalNamesTest.scala index 1669d6397cc0..24d6d18ca00e 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/GlobalNamesTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/GlobalNamesTest.scala @@ -13,7 +13,7 @@ import org.enso.compiler.core.ir.expression.errors import org.enso.compiler.data.BindingsMap.{Resolution, ResolvedModule} import org.enso.compiler.pass.resolve.GlobalNames import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager} -import org.enso.compiler.phase.ExportsResolution +import org.enso.compiler.phase.exports.ExportsResolution import org.enso.compiler.test.CompilerTest import org.enso.interpreter.runtime import org.enso.interpreter.runtime.ModuleTestUtils diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index e900682774de..1dfad99d7e70 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -5,7 +5,7 @@ import org.enso.compiler.core.ir.{Module, ProcessingPass, Warning} import org.enso.compiler.core.ir.expression.errors import org.enso.compiler.core.ir.module.scope.Import import org.enso.compiler.data.BindingsMap -import org.enso.compiler.phase.ExportsResolution +import org.enso.compiler.phase.exports.ExportsResolution import org.enso.compiler.pass.analyse.{BindingAnalysis, GatherDiagnostics} import org.enso.interpreter.runtime import org.enso.interpreter.runtime.EnsoContext From 897f4a00ba4ad15abb22eb661e10bc146782add1 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 14:00:48 +0200 Subject: [PATCH 035/111] Rename Node.module to Node.target back again --- .../phase/exports/ExportsResolution.scala | 8 ++++---- .../org/enso/compiler/phase/exports/Node.scala | 2 +- .../test/semantic/ImportExportTest.scala | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 7c7d0bc5ddda..f8512c0ef001 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -184,13 +184,13 @@ class ExportsResolution(private val context: CompilerContext) { val cycles = findCycles(graph) if (cycles.nonEmpty) { throw ExportCycleException( - cycles.head.map(_.module.module.unsafeAsModule()) + cycles.head.map(_.target.module.unsafeAsModule()) ) } val tops = topsort(graph) resolveExports(tops) - val topModules = tops.map(_.module) - resolveExportedSymbols(tops.map(_.module).collect { + val topModules = tops.map(_.target) + resolveExportedSymbols(tops.map(_.target).collect { case m: ResolvedModule => m.module.unsafeAsModule() }) // Take _last_ occurrence of each module @@ -203,7 +203,7 @@ class ExportsResolution(private val context: CompilerContext) { def runSort(modules: List[Module]): List[Module] = { val graph = buildGraph(modules) val tops = topsort(graph) - val topModules = tops.map(_.module) + val topModules = tops.map(_.target) topModules.map(_.module.unsafeAsModule()).reverse.distinct.reverse } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala index 5fbe1616cdfe..cd54bb0f0e28 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala @@ -3,7 +3,7 @@ package org.enso.compiler.phase.exports import org.enso.compiler.data.BindingsMap.ImportTarget case class Node( - module: ImportTarget + target: ImportTarget ) { var exports: List[Edge] = List() var exportedBy: List[Edge] = List() diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 1dfad99d7e70..c5eb19418554 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -1377,7 +1377,7 @@ class ImportExportTest graph.size shouldBe 2 } val aModNode = graph.find(node => - node.module match { + node.target match { case BindingsMap.ResolvedModule(modRef) => modRef.getName.item == "A_Module" case _ => false @@ -1387,9 +1387,9 @@ class ImportExportTest val aModNodeExporter = aModNode.get.exportedBy.head.exporter withClue("A_Module should be exported by B_Module") { - aModNodeExporter.module + aModNodeExporter.target .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true - aModNodeExporter.module + aModNodeExporter.target .asInstanceOf[BindingsMap.ResolvedModule] .qualifiedName .item shouldBe "B_Module" @@ -1473,11 +1473,11 @@ class ImportExportTest val bModule = s""" - |from $namespace.$packageName.A_Module import A_Type - | - |main = - | A_Type.A_Constructor - |""".stripMargin + |from $namespace.$packageName.A_Module import A_Type + | + |main = + | A_Type.A_Constructor + |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val graph = buildExportsGraph(List(aModule, bModule)) withClue("Only two modules are defined") { From 0228f5062781f2aab0680b26a83a925c69a34a4a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 14:05:46 +0200 Subject: [PATCH 036/111] ExportedModule contains list of symbols --- .../org/enso/compiler/data/BindingsMap.scala | 15 ++++- .../pass/resolve/FullyQualifiedNames.scala | 3 +- .../enso/compiler/phase/exports/Edge.scala | 1 + .../phase/exports/ExportsResolution.scala | 65 +++++++++++++++---- .../interpreter/runtime/IrToTruffle.scala | 2 +- 5 files changed, 69 insertions(+), 17 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 9b8799b2abb9..097f73f89ec6 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -314,7 +314,13 @@ case class BindingsMap( resolvedImports.collect { case ResolvedImport(_, exports, mod) => exports.map { exp => val rename = Some(exp.getSimpleName.name) - ExportedModule(mod, rename) + val symbols = exp.onlyNames match { + case Some(onlyNames) => + onlyNames.map(_.name) + case None => + List(exp.getSimpleName.name) + } + ExportedModule(mod, rename, symbols) } }.flatten } @@ -349,8 +355,13 @@ object BindingsMap { * * @param target the module being exported. * @param exportedAs the name it is exported as. + * @param symbols List of symbols connected to the export. */ - case class ExportedModule(target: ImportTarget, exportedAs: Option[String]) { + case class ExportedModule( + target: ImportTarget, + exportedAs: Option[String], + symbols: List[String] + ) { /** Convert the internal [[ModuleReference]] to an abstract reference. * diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala index 52c9bf7d01ca..1d60a44120be 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala @@ -95,7 +95,8 @@ case object FullyQualifiedNames extends IRPass { scopeMap.resolvedExports.foreach { case ExportedModule( resolution @ ResolvedType(exportedModuleRef, tpe), - exportedAs + exportedAs, + _ ) => val tpeName = exportedAs.getOrElse(tpe.name) val exportedModule = exportedModuleRef.unsafeAsModule() diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Edge.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Edge.scala index 68dbcf05b9b5..e37a94ae3f8d 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Edge.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Edge.scala @@ -2,6 +2,7 @@ package org.enso.compiler.phase.exports case class Edge( exporter: Node, + symbols: List[String], exportsAs: Option[String], exportee: Node ) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index f8512c0ef001..ba0b8adc49f4 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -46,8 +46,8 @@ class ExportsResolution(private val context: CompilerContext) { Nil } val node = nodes(module) - node.exports = exports.map { case ExportedModule(mod, rename) => - Edge(node, rename, nodes.getOrElseUpdate(mod, Node(mod))) + node.exports = exports.map { case ExportedModule(mod, rename, symbols) => + Edge(node, symbols, rename, nodes.getOrElseUpdate(mod, Node(mod))) } node.exports.foreach { edge => edge.exportee.exportedBy ::= edge } } @@ -116,16 +116,45 @@ class ExportsResolution(private val context: CompilerContext) { nodes.foreach { node => val explicitlyExported = node.exports.map(edge => - ExportedModule(edge.exportee.module, edge.exportsAs) + ExportedModule( + edge.exportee.target, + edge.exportsAs, + edge.symbols + ) ) - val transitivelyExported: List[ExportedModule] = - explicitlyExported.flatMap { case ExportedModule(module, _) => - exports(module).map { case ExportedModule(export, _) => - ExportedModule(export, None) + + val transitivelyExported: List[ExportedModule] = { + explicitlyExported.flatMap { case ExportedModule(module, _, symbols) => + exports(module).map { case ExportedModule(export, _, parentSymbols) => + val exportedSymbols = symbols.intersect(parentSymbols) + ExportedModule( + export, + None, + exportedSymbols + ) } } + } + val allExported = explicitlyExported ++ transitivelyExported - exports(node.module) = allExported + val unified = allExported + .groupBy(_.target) + .map { case (mod, items) => + val name = items.collectFirst { case ExportedModule(_, Some(n), _) => + n + } + val allSymbols = items + .map(_.symbols) + .foldLeft(List[String]())(_ ++ _) + ExportedModule( + mod, + name, + allSymbols.distinct + ) + } + .toList + exports(node.target) = unified + } exports.foreach { case (target, exports) => target match { @@ -144,15 +173,25 @@ class ExportsResolution(private val context: CompilerContext) { bindings.definedEntities .filter(_.canExport) .map(e => (e.name, List(e.resolvedIn(module)))) - val exportedModules = bindings.resolvedExports.collect { - case ExportedModule(mod, Some(exportedAs)) - if mod.module.unsafeAsModule() != module => - (exportedAs, List(mod)) + val exportedModules = bindings.resolvedExports.flatMap { + case ExportedModule(mod, Some(exportedAs), symbols) => + val isThisModule = mod.module.unsafeAsModule() == module + val exportsOnlyModule = (symbols.size == 1 && symbols.head == mod.module.getName.item) + if (!isThisModule && exportsOnlyModule) { + Some((exportedAs, List(mod))) + } else { + None + } + case _ => None } val reExportedSymbols = bindings.resolvedExports.flatMap { export => export.target.exportedSymbols .flatMap { case (sym, resolutions) => - Some((sym, resolutions)) + if (export.symbols.contains(sym)) { + Some((sym, resolutions)) + } else { + None + } } } bindings.exportedSymbols = List( diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index 888c5de8125f..c2055509276e 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -198,7 +198,7 @@ class IrToTruffle( ) bindingsMap.resolvedExports - .collect { case ExportedModule(ResolvedModule(module), _) => module } + .collect { case ExportedModule(ResolvedModule(module), _, _) => module } .foreach(exp => scopeBuilder.addExport(new ImportExportScope(exp.unsafeAsModule())) ) From 02481e8b46eef626f3b9a12b7664049438b4b2b3 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 14:06:03 +0200 Subject: [PATCH 037/111] Remove WIP comments from tests --- .../test/semantic/ImportExportTest.scala | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index c5eb19418554..1bcc5e026cbb 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -13,6 +13,7 @@ import org.enso.persist.Persistance import org.enso.pkg.QualifiedName import org.enso.common.LanguageInfo import org.enso.common.MethodNames +import org.enso.compiler.data.BindingsMap.{ResolvedConstructor, ResolvedModule} import org.enso.compiler.phase.exports.Node import org.enso.polyglot.RuntimeOptions import org.graalvm.polyglot.{Context, Engine} @@ -1087,7 +1088,7 @@ class ImportExportTest "Import resolution for three modules" should { - "XX not resolve symbol that is not explicitly exported" in { + "not resolve symbol that is not explicitly exported" in { """ |type A_Type | A_Constructor @@ -1114,7 +1115,6 @@ class ImportExportTest .createModule(packageQualifiedName.createChild("B_Module")) .getIr val bBindingMap = bIr.unwrapBindingMap - // TODO: bBindingsMap.resolvedExports? bBindingMap.exportedSymbols.keys should contain theSameElementsAs List( "static_method", "B_Type" @@ -1463,6 +1463,42 @@ class ImportExportTest assertAModExportedByBMod(graph) } + "build exports graph for constructors" in { + val boolModule = + """ + |type Boolean + | True + | False + |""".stripMargin + .createModule(packageQualifiedName.createChild("Boolean")) + + val mainModule = + s""" + |export $namespace.$packageName.Boolean.Boolean.True + |export $namespace.$packageName.Boolean.Boolean.False + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + + val graph = buildExportsGraph(List(boolModule, mainModule)) + withClue("graph should contains node for: [A_Module, B_Module, True, False]") { + graph.size shouldBe 4 + } + val trueNode = graph.find(node => { + node.target match { + case ResolvedConstructor(_, cons) if (cons.name == "True") => + true + case _ => false + } + }) + trueNode shouldBe defined + val trueNodeExporter = trueNode.get.exportedBy.head.exporter + trueNodeExporter + .target + .asInstanceOf[ResolvedModule] + .qualifiedName + .item shouldBe "Main" + } + "No exports graph is constructed when only import is used" in { val aModule = """ From 46c678fd7de7692afc02376a94edf85b94986bd3 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 14:06:30 +0200 Subject: [PATCH 038/111] ResolvedMethods have self reference in their exported symbols --- .../org/enso/compiler/data/BindingsMap.scala | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 097f73f89ec6..95d2cc687b05 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -664,10 +664,17 @@ object BindingsMap { /** @inheritdoc */ override def module: ModuleReference = tpe.module - override def findExportedSymbolsFor(name: String): List[ResolvedName] = - List() + override def findExportedSymbolsFor(name: String): List[ResolvedName] = { + if (name == cons.name) { + List(this) + } else { + List() + } + } - override def exportedSymbols: Map[String, List[ResolvedName]] = Map() + override def exportedSymbols: Map[String, List[ResolvedName]] = { + Map(cons.name -> List(this)) + } } /** A representation of a name being resolved to a module. @@ -709,10 +716,17 @@ object BindingsMap { sealed trait ResolvedMethod extends ResolvedName with ImportTarget { def methodName: String - override def findExportedSymbolsFor(name: String): List[ResolvedName] = - List() + override def findExportedSymbolsFor(name: String): List[ResolvedName] = { + if (name == methodName) { + List(this) + } else { + List() + } + } - override def exportedSymbols: Map[String, List[ResolvedName]] = Map() + override def exportedSymbols: Map[String, List[ResolvedName]] = Map( + methodName -> List(this) + ) } /** A representation of a resolved method defined directly on module. From 53a6d31584ea03cf641149c2b29c87594055580b Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 14:06:51 +0200 Subject: [PATCH 039/111] fmt --- .../enso/compiler/phase/exports/ExportsResolution.scala | 9 +++++---- .../enso/compiler/test/semantic/ImportExportTest.scala | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index ba0b8adc49f4..a3f596286529 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -122,7 +122,7 @@ class ExportsResolution(private val context: CompilerContext) { edge.symbols ) ) - + val transitivelyExported: List[ExportedModule] = { explicitlyExported.flatMap { case ExportedModule(module, _, symbols) => exports(module).map { case ExportedModule(export, _, parentSymbols) => @@ -135,7 +135,7 @@ class ExportsResolution(private val context: CompilerContext) { } } } - + val allExported = explicitlyExported ++ transitivelyExported val unified = allExported .groupBy(_.target) @@ -154,7 +154,7 @@ class ExportsResolution(private val context: CompilerContext) { } .toList exports(node.target) = unified - + } exports.foreach { case (target, exports) => target match { @@ -176,7 +176,8 @@ class ExportsResolution(private val context: CompilerContext) { val exportedModules = bindings.resolvedExports.flatMap { case ExportedModule(mod, Some(exportedAs), symbols) => val isThisModule = mod.module.unsafeAsModule() == module - val exportsOnlyModule = (symbols.size == 1 && symbols.head == mod.module.getName.item) + val exportsOnlyModule = + symbols.size == 1 && symbols.head == mod.module.getName.item if (!isThisModule && exportsOnlyModule) { Some((exportedAs, List(mod))) } else { diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 1bcc5e026cbb..5495a88ed0ec 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -1480,20 +1480,21 @@ class ImportExportTest .createModule(packageQualifiedName.createChild("Main")) val graph = buildExportsGraph(List(boolModule, mainModule)) - withClue("graph should contains node for: [A_Module, B_Module, True, False]") { + withClue( + "graph should contains node for: [A_Module, B_Module, True, False]" + ) { graph.size shouldBe 4 } val trueNode = graph.find(node => { node.target match { - case ResolvedConstructor(_, cons) if (cons.name == "True") => + case ResolvedConstructor(_, cons) if cons.name == "True" => true case _ => false } }) trueNode shouldBe defined val trueNodeExporter = trueNode.get.exportedBy.head.exporter - trueNodeExporter - .target + trueNodeExporter.target .asInstanceOf[ResolvedModule] .qualifiedName .item shouldBe "Main" From 5f0fed7b89bbc51ac944f2a978d5591351768f71 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 17:33:56 +0200 Subject: [PATCH 040/111] ResolvedType have self reference in their exported symbols --- .../main/scala/org/enso/compiler/data/BindingsMap.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 95d2cc687b05..de6685ac8c0d 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -610,8 +610,11 @@ object BindingsMap { override def findExportedSymbolsFor(name: String): List[ResolvedName] = exportedSymbols.getOrElse(name, List()) - override lazy val exportedSymbols: Map[String, List[ResolvedName]] = - tp.members.map(m => (m.name, List(ResolvedConstructor(this, m)))).toMap + override lazy val exportedSymbols: Map[String, List[ResolvedName]] = { + val membersMap = + tp.members.map(m => (m.name, List(ResolvedConstructor(this, m)))).toMap + membersMap + ((tp.name, List(this))) + } } /** A result of successful name resolution. From 62cbc9c6c5ff63ca89637751391fedd906fec52a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 18:01:28 +0200 Subject: [PATCH 041/111] Remove outdated ignored tests from #6669 --- .../test/semantic/ImportExportTest.scala | 56 ------------------- 1 file changed, 56 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 5495a88ed0ec..a3dacaee6694 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -378,62 +378,6 @@ class ImportExportTest .name should include("Non_Existing_Symbol") } - // 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 - | a_method self = 1 - |""".stripMargin - .createModule(packageQualifiedName.createChild("A_Module")) - s""" - |export $namespace.$packageName.A_Module.A_Type - |""".stripMargin - .createModule(packageQualifiedName.createChild("B_Module")) - val mainIr = - s""" - |from $namespace.$packageName.B_Module.A_Type import all - |""".stripMargin - .createModule(packageQualifiedName.createChild("Main")) - .getIr - mainIr.imports.size shouldEqual 1 - mainIr.imports.head.isInstanceOf[errors.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") - } - - // TODO[pm]: Fix in https://github.com/enso-org/enso/issues/6669 - "resolve constructor from transitively exported type" ignore { - """ - |type A_Type - | A_Constructor - |""".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 mainIr = - s""" - |from $namespace.$packageName.B_Module.A_Type import A_Constructor - |""".stripMargin - .createModule(packageQualifiedName.createChild("Main")) - .getIr - mainIr.imports.size shouldEqual 1 - mainIr.imports.head.isInstanceOf[errors.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") - } - "export is not transitive" in { s""" |import $namespace.$packageName.A_Module.A_Type From 3a0a5cc4d7b529059a5105a81c6decc8a49b8672 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 1 Jul 2024 18:23:00 +0200 Subject: [PATCH 042/111] Handle renamed exports --- .../org/enso/compiler/data/BindingsMap.scala | 2 +- .../phase/exports/ExportsResolution.scala | 7 ++++++- .../enso/compiler/ExportedSymbolsTest.java | 20 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index de6685ac8c0d..8575bb690028 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -318,7 +318,7 @@ case class BindingsMap( case Some(onlyNames) => onlyNames.map(_.name) case None => - List(exp.getSimpleName.name) + List(exp.name.parts.last.name) } ExportedModule(mod, rename, symbols) } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index a3f596286529..0c8dea7e4063 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -189,7 +189,12 @@ class ExportsResolution(private val context: CompilerContext) { export.target.exportedSymbols .flatMap { case (sym, resolutions) => if (export.symbols.contains(sym)) { - Some((sym, resolutions)) + export.exportedAs match { + case Some(renamed) => + Some((renamed, resolutions)) + case _ => + Some((sym, resolutions)) + } } else { None } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index a30d291f911e..ee3ba74e96a0 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -97,6 +97,26 @@ public void exportSymbolFromDifferentModule() throws IOException { assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type")); } + @Test + public void exportRenamedSymbol() throws IOException { + var aMod = + new SourceModule(QualifiedName.fromString("A_Module"), """ + type A_Type + """); + var mainSrcMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + export project.A_Module.A_Type as Foo + """); + ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir); + var ctx = createCtx(projDir); + compile(ctx); + var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSymbols.size(), is(1)); + assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Foo")); + } + private static Context createCtx(Path projDir) { return ContextUtils.defaultContextBuilder() .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) From c0b71a8830a0504e5409d19fae2ed24cef0e7317 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 11:23:43 +0200 Subject: [PATCH 043/111] Rename and doc update --- .../org/enso/compiler/data/BindingsMap.scala | 23 +++++++++++++++---- .../phase/exports/ExportsResolution.scala | 8 +++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 8575bb690028..07458e337ee7 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -305,13 +305,14 @@ case class BindingsMap( } /** Dumps the export statements from this module into a structure ready for - * further analysis. + * further analysis. It uses only [[resolvedImports]] field, as [[resolvedExports]] + * and [[exportedSymbols]] fields are expected to be filled later. * * @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) => + resolvedImports.collect { case ResolvedImport(_, exports, target) => exports.map { exp => val rename = Some(exp.getSimpleName.name) val symbols = exp.onlyNames match { @@ -320,7 +321,7 @@ case class BindingsMap( case None => List(exp.name.parts.last.name) } - ExportedModule(mod, rename, symbols) + ExportedModule(target, rename, symbols) } }.flatten } @@ -353,15 +354,27 @@ object BindingsMap { /** A representation of a resolved export statement. * - * @param target the module being exported. + * @param target the target being exported. * @param exportedAs the name it is exported as. - * @param symbols List of symbols connected to the export. + * @param symbols List of symbols connected to the export. The symbol refers to the last part + * of the physical name of the target being exported. It is not a fully qualified + * name. */ case class ExportedModule( target: ImportTarget, exportedAs: Option[String], symbols: List[String] ) { + assert( + symbols.forall(!_.contains(".")), + "Not expected fully qualified names as symbols" + ) + if (exportedAs.isDefined) { + assert( + !exportedAs.get.contains("."), + "Not expected fully qualified name as `exportedAs`" + ) + } /** Convert the internal [[ModuleReference]] to an abstract reference. * diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 0c8dea7e4063..7ee7d641fc19 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -125,10 +125,10 @@ class ExportsResolution(private val context: CompilerContext) { val transitivelyExported: List[ExportedModule] = { explicitlyExported.flatMap { case ExportedModule(module, _, symbols) => - exports(module).map { case ExportedModule(export, _, parentSymbols) => + exports(module).map { case ExportedModule(target, _, parentSymbols) => val exportedSymbols = symbols.intersect(parentSymbols) ExportedModule( - export, + target, None, exportedSymbols ) @@ -139,7 +139,7 @@ class ExportsResolution(private val context: CompilerContext) { val allExported = explicitlyExported ++ transitivelyExported val unified = allExported .groupBy(_.target) - .map { case (mod, items) => + .map { case (target, items) => val name = items.collectFirst { case ExportedModule(_, Some(n), _) => n } @@ -147,7 +147,7 @@ class ExportsResolution(private val context: CompilerContext) { .map(_.symbols) .foldLeft(List[String]())(_ ++ _) ExportedModule( - mod, + target, name, allSymbols.distinct ) From ef7716b8dbc48fbe88bbffc14e45f75d9a88a685 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 12:44:41 +0200 Subject: [PATCH 044/111] Replace `from ... export ...` with `export ...` type of export in std base Main --- .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 111 ++++++++++++++---- 1 file changed, 90 insertions(+), 21 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index 7193d49e0151..65648d2b406c 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -93,24 +93,93 @@ export project.System.Platform export project.System.Process export project.System.Process.Exit_Code.Exit_Code export project.Warning.Warning -from project.Data.Boolean export Boolean, False, True -from project.Data.Index_Sub_Range.Index_Sub_Range export First, Last -from project.Data.Json.Extensions export to_json, to_js_object -from project.Data.Numbers export Float, Integer, Number -from project.Data.Range.Extensions export up_to, down_to -from project.Data.Statistics.Extensions export compute, compute_bulk, running, running_bulk, rank_data -from project.Data.Text.Extensions export reverse, each, at, get, first, second, last, characters, find -from project.Data.Text.Extensions export find_all, match, to_regex, split, tokenize, replace, cleanse -from project.Data.Text.Extensions export words, lines, insert, from, is_digit, is_whitespace, bytes, from_bytes -from project.Data.Text.Extensions export utf_8, from_utf_8, char_vector, from_char_vector, codepoints, from_codepoints -from project.Data.Text.Extensions export starts_with, ends_with, contains, repeat, take -from project.Data.Text.Extensions export drop, to_case, pad, trim, locate, locate_all, index_of -from project.Data.Text.Extensions export last_index_of, parse_float, parse_integer, parse_json -from project.Data.Text.Extensions export parse_date, parse_date_time, parse_time_of_day, parse_time_zone -from project.Data.Text.Extensions export substring, slice_text, split_find_delimiters -from project.Data.Text.Regex export regex -from project.Errors.Problem_Behavior.Problem_Behavior export Ignore, Report_Warning, Report_Error -from project.Function export Function, identity, flip, const, curry, uncurry -from project.Meta.Enso_Project export enso_project -from project.Network.Extensions export to_uri, fetch, post -from project.System.File_Format export Auto_Detect, Bytes, File_Format, Infer, JSON_Format, Plain_Text_Format +export project.Data.Boolean.Boolean +export project.Data.Boolean.Boolean.False +export project.Data.Boolean.Boolean.True +export project.Data.Index_Sub_Range.Index_Sub_Range.First +export project.Data.Index_Sub_Range.Index_Sub_Range.Last +export project.Data.Json.Extensions.to_json +export project.Data.Json.Extensions.to_js_object +export project.Data.Numbers.Float +export project.Data.Numbers.Integer +export project.Data.Numbers.Number +export project.Data.Range.Extensions.up_to +export project.Data.Range.Extensions.down_to +export project.Data.Statistics.Extensions.compute +export project.Data.Statistics.Extensions.compute_bulk +export project.Data.Statistics.Extensions.running +export project.Data.Statistics.Extensions.running_bulk +export project.Data.Statistics.Extensions.rank_data +export project.Data.Text.Extensions.reverse +export project.Data.Text.Extensions.each +export project.Data.Text.Extensions.at +export project.Data.Text.Extensions.get +export project.Data.Text.Extensions.first +export project.Data.Text.Extensions.second +export project.Data.Text.Extensions.last +export project.Data.Text.Extensions.characters +export project.Data.Text.Extensions.find +export project.Data.Text.Extensions.find_all +export project.Data.Text.Extensions.match +export project.Data.Text.Extensions.to_regex +export project.Data.Text.Extensions.split +export project.Data.Text.Extensions.tokenize +export project.Data.Text.Extensions.replace +export project.Data.Text.Extensions.cleanse +export project.Data.Text.Extensions.words +export project.Data.Text.Extensions.lines +export project.Data.Text.Extensions.insert +export project.Data.Text.Extensions.from +export project.Data.Text.Extensions.is_digit +export project.Data.Text.Extensions.is_whitespace +export project.Data.Text.Extensions.bytes +export project.Data.Text.Extensions.from_bytes +export project.Data.Text.Extensions.utf_8 +export project.Data.Text.Extensions.from_utf_8 +export project.Data.Text.Extensions.char_vector +export project.Data.Text.Extensions.from_char_vector +export project.Data.Text.Extensions.codepoints +export project.Data.Text.Extensions.from_codepoints +export project.Data.Text.Extensions.starts_with +export project.Data.Text.Extensions.ends_with +export project.Data.Text.Extensions.contains +export project.Data.Text.Extensions.repeat +export project.Data.Text.Extensions.take +export project.Data.Text.Extensions.drop +export project.Data.Text.Extensions.to_case +export project.Data.Text.Extensions.pad +export project.Data.Text.Extensions.trim +export project.Data.Text.Extensions.locate +export project.Data.Text.Extensions.locate_all +export project.Data.Text.Extensions.index_of +export project.Data.Text.Extensions.last_index_of +export project.Data.Text.Extensions.parse_float +export project.Data.Text.Extensions.parse_integer +export project.Data.Text.Extensions.parse_json +export project.Data.Text.Extensions.parse_date +export project.Data.Text.Extensions.parse_date_time +export project.Data.Text.Extensions.parse_time_of_day +export project.Data.Text.Extensions.parse_time_zone +export project.Data.Text.Extensions.substring +export project.Data.Text.Extensions.slice_text +export project.Data.Text.Extensions.split_find_delimiters +export project.Data.Text.Regex.regex +export project.Errors.Problem_Behavior.Problem_Behavior.Ignore +export project.Errors.Problem_Behavior.Problem_Behavior.Report_Warning +export project.Errors.Problem_Behavior.Problem_Behavior.Report_Error +export project.Function.Function +export project.Function.identity +export project.Function.flip +export project.Function.const +export project.Function.curry +export project.Function.uncurry +export project.Meta.Enso_Project.enso_project +export project.Network.Extensions.to_uri +export project.Network.Extensions.fetch +export project.Network.Extensions.post +export project.System.File_format.Auto_Detect +export project.System.File_format.Bytes +export project.System.File_format.File_Format +export project.System.File_format.Infer +export project.System.File_format.JSON_Format +export project.System.File_format.Plain_Text_Format From 7ff1bf00c353d6a1f0a870a9df07124550d7fc53 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 12:45:10 +0200 Subject: [PATCH 045/111] Replace `from ... export ...` with `export ...` type of export in ExportStaticMethodTest --- .../interpreter/test/exports/ExportStaticMethodTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java index f3054be54f4e..3bc3cc973e68 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java @@ -33,7 +33,7 @@ public void staticMethodCanBeExportedByName() throws IOException { new SourceModule( QualifiedName.fromString("A_Module"), """ - from project.T_Module export static_method + export project.T_Module.static_method """); var mainMod = new SourceModule( @@ -124,7 +124,7 @@ public void staticMethodIsInBindingMap() throws IOException { new SourceModule( QualifiedName.fromString("Main"), """ - from project.T_Module export static_method + export project.T_Module.static_method """); var projDir = tempFolder.newFolder().toPath(); ProjectUtils.createProject("Proj", Set.of(tMod, mainMod), projDir); @@ -136,7 +136,7 @@ public void staticMethodIsInBindingMap() throws IOException { var polyCtx = new PolyglotContext(ctx); polyCtx.getTopScope().compile(true); var mainModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); - assertThat(mainModExportedSymbols.size(), is(greaterThanOrEqualTo(1))); + assertThat(mainModExportedSymbols.size(), is(1)); assertThat(mainModExportedSymbols, hasKey("static_method")); } } From 7a3670dc5732c8af247663c2e018cc80cebda528 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 12:45:23 +0200 Subject: [PATCH 046/111] Replace `from ... export ...` with `export ...` type of export in Boolean.enso --- distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso index d7e04796326d..53918f761920 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso @@ -1,7 +1,8 @@ import project.Any.Any import project.Nothing.Nothing -from project.Data.Boolean.Boolean export False, True +export project.Data.Boolean.Boolean.False +export project.Data.Boolean.Boolean.True ## A type with only two possible values. From 98302ac0694a5fe35a67b84c2d99a438d4b3a2a6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 19:20:54 +0200 Subject: [PATCH 047/111] ResolvedImport has multiple import targets --- .../java/org/enso/compiler/dump/IRDumper.java | 5 +- .../pass/analyse/PrivateModuleAnalysis.java | 30 +++--- .../compiler/phase/ImportResolverForIR.java | 17 ++-- .../org/enso/compiler/data/BindingsMap.scala | 31 ++++-- .../analyse/AmbiguousImportsAnalysis.scala | 96 ++++++++++--------- .../pass/analyse/ImportSymbolAnalysis.scala | 72 +++++++------- .../enso/compiler/phase/ImportResolver.scala | 31 +++--- .../test/semantic/ImportExportTest.scala | 16 +++- .../interpreter/runtime/IrToTruffle.scala | 10 +- 9 files changed, 173 insertions(+), 135 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java index 871788811cff..ae82bbd0db1b 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java @@ -576,12 +576,13 @@ private void createPassDataGraph(IR ir) { bldr.addLabelLine("resolvedImports: "); for (int i = 0; i < bindingsMap.resolvedImports().size(); i++) { var resolvedImport = bindingsMap.resolvedImports().apply(i); - switch (resolvedImport.target()) { + var firstImpTarget = resolvedImport.targets().head(); + switch (firstImpTarget) { case ResolvedType resolvedType -> bldr.addLabelLine( " - ResolvedType(" + resolvedType.tp().name() + ")"); case BindingsMap.ResolvedModule resolvedModule -> bldr.addLabelLine( " - ResolvedModule(" + resolvedModule.qualifiedName() + ")"); - default -> throw unimpl(resolvedImport.target()); + default -> throw unimpl(firstImpTarget); } } } diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java index aabd12b82f34..1ec815d1b372 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java @@ -72,19 +72,23 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { .resolvedImports() .foreach( resolvedImp -> { - var importedModule = resolvedImp.target().module().unsafeAsModule("should succeed"); - var importedModuleName = importedModule.getName().toString(); - var importedModulePackage = importedModule.getPackage(); - if (currentPackage != null - && !currentPackage.equals(importedModulePackage) - && importedModule.isPrivate()) { - importErrors.add( - ImportExport.apply( - resolvedImp.importDef(), - new ImportExport.ImportPrivateModule(importedModuleName), - ImportExport.apply$default$3(), - ImportExport.apply$default$4())); - } + var importedTargets = resolvedImp.targets(); + importedTargets.foreach(importedTarget -> { + var importedModule = importedTarget.module().unsafeAsModule("should succeed"); + var importedModuleName = importedModule.getName().toString(); + var importedModulePackage = importedModule.getPackage(); + if (currentPackage != null + && !currentPackage.equals(importedModulePackage) + && importedModule.isPrivate()) { + importErrors.add( + ImportExport.apply( + resolvedImp.importDef(), + new ImportExport.ImportPrivateModule(importedModuleName), + ImportExport.apply$default$3(), + ImportExport.apply$default$4())); + } + return null; + }); return null; }); diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java index e7601da95f9d..d0675b4ae5b7 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java @@ -214,28 +214,29 @@ protected final CompilerContext.Module loadLibraryModule(LibraryName libraryName @Override protected final Tuple2> createResolvedImport(Import.Module imp, java.util.List exp, CompilerContext.Module m) { - scala.Option someBinding = Option.apply(new BindingsMap.ResolvedImport(imp, toScalaList(exp), new BindingsMap.ResolvedModule(new BindingsMap$ModuleReference$Concrete(m)))); - return new Tuple2<>(imp, someBinding); + var resolvedModule = new BindingsMap.ResolvedModule(new BindingsMap$ModuleReference$Concrete(m)); + var resolvedImport = new BindingsMap.ResolvedImport(imp, toScalaList(exp), toScalaList(java.util.List.of(resolvedModule))); + return new Tuple2<>(imp, scala.Some.apply(resolvedImport)); } @Override protected final Tuple2> createResolvedType(Import.Module imp, java.util.List exp, BindingsMap.ResolvedType typ) { - scala.Option someBinding = Option.apply(new BindingsMap.ResolvedImport(imp, toScalaList(exp), typ)); - return new Tuple2<>(imp, someBinding); + var resolvedImport = new BindingsMap.ResolvedImport(imp, toScalaList(exp), toScalaList(java.util.List.of(typ))); + return new Tuple2<>(imp, scala.Some.apply(resolvedImport)); } @Override protected Tuple2> createResolvedConstructor(Import.Module imp, java.util.List exp, ResolvedConstructor cons) { - scala.Option someBinding = Option.apply(new BindingsMap.ResolvedImport(imp, toScalaList(exp), cons)); - return new Tuple2<>(imp, someBinding); + var resolvedImport = new BindingsMap.ResolvedImport(imp, toScalaList(exp), toScalaList(java.util.List.of(cons))); + return new Tuple2<>(imp, scala.Some.apply(resolvedImport)); } @Override protected Tuple2> createResolvedModuleMethod(Import.Module imp, java.util.List exp, ResolvedModuleMethod resolvedModuleMethod) { - scala.Option someBinding = Option.apply(new BindingsMap.ResolvedImport(imp, toScalaList(exp), resolvedModuleMethod)); - return new Tuple2<>(imp, someBinding); + var resolvedImport = new BindingsMap.ResolvedImport(imp, toScalaList(exp), toScalaList(java.util.List.of(resolvedModuleMethod))); + return new Tuple2<>(imp, scala.Some.apply(resolvedImport)); } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 07458e337ee7..63033620fe44 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -143,7 +143,7 @@ case class BindingsMap( ): List[ResolvedName] = { resolvedImports .filter(i => importMatchesName(i, name) && !i.isSynthetic()) - .map(_.target) + .flatMap(_.targets) } private def importMatchesName(imp: ResolvedImport, name: String): Boolean = { @@ -161,9 +161,9 @@ case class BindingsMap( val resolvedNames = resolvedImports .flatMap { imp => if (imp.importDef.allowsAccess(name)) { - imp.target + imp .findExportedSymbolsFor(name) - .map((_, imp.isSynthetic(), imp.target)) + .map((_, imp.isSynthetic(), imp.targets)) } else { List() } } // synthetic imports should not be reported in the ambiguity reports @@ -312,8 +312,8 @@ case class BindingsMap( * as and any further symbol restrictions. */ def getDirectlyExportedModules: List[ExportedModule] = - resolvedImports.collect { case ResolvedImport(_, exports, target) => - exports.map { exp => + resolvedImports.collect { case ResolvedImport(_, exports, targets) => + exports.flatMap { exp => val rename = Some(exp.getSimpleName.name) val symbols = exp.onlyNames match { case Some(onlyNames) => @@ -321,7 +321,7 @@ case class BindingsMap( case None => List(exp.name.parts.last.name) } - ExportedModule(target, rename, symbols) + targets.map(ExportedModule(_, rename, symbols)) } }.flatten } @@ -421,20 +421,22 @@ object BindingsMap { * * @param importDef the definition of the import * @param exports the exports associated with the import - * @param target the module or type this import resolves to + * @param targets list of targets that this import resolves to. Note that it is valid for a single + * import to resolve to multiple entities, for example, in case of extension methods. */ case class ResolvedImport( importDef: ir.module.scope.Import.Module, exports: List[ir.module.scope.Export.Module], - target: ImportTarget + targets: List[ImportTarget] ) { + assert(targets.nonEmpty) /** Convert the internal [[ModuleReference]] to an abstract reference. * * @return `this` with its module reference made abstract */ def toAbstract: ResolvedImport = { - this.copy(target = target.toAbstract) + this.copy(targets = targets.map(_.toAbstract)) } /** Convert the internal [[ModuleReference]] to a concrete reference. @@ -443,7 +445,12 @@ object BindingsMap { * @return `this` with its module reference made concrete */ def toConcrete(moduleMap: ModuleMap): Option[ResolvedImport] = { - target.toConcrete(moduleMap).map(x => this.copy(target = x)) + val newTargets = targets.map(_.toConcrete(moduleMap)) + if (newTargets.forall(_.isDefined)) { + Some(this.copy(targets = newTargets.map(_.get))) + } else { + None + } } /** Determines if this resolved import statement was generated by the compiler. @@ -453,6 +460,10 @@ object BindingsMap { def isSynthetic(): Boolean = { importDef.isSynthetic } + + def findExportedSymbolsFor(name: String): List[ResolvedName] = { + targets.flatMap(_.findExportedSymbolsFor(name)) + } } sealed trait DefinedEntity { diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/AmbiguousImportsAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/AmbiguousImportsAnalysis.scala index 5014c99674b3..3cea24034141 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/AmbiguousImportsAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/AmbiguousImportsAnalysis.scala @@ -103,32 +103,34 @@ case object AmbiguousImportsAnalysis extends IRPass { _, _ ) => - getImportTarget(moduleImport, bindingMap) match { - case Some(importTarget) => + getImportTargets(moduleImport, bindingMap) match { + case Some(importTargets) => val encounteredErrors: ListBuffer[errors.ImportExport] = ListBuffer() onlyNames.foreach { symbol => val symbolName = symbol.name - importTarget.resolveExportedSymbol(symbolName) match { - case Right(resolvedNames) => - resolvedNames.foreach { resolvedName => - val symbolPath = resolvedName.qualifiedName.toString - tryAddEncounteredSymbol( - encounteredSymbols, - moduleImport, - symbolName, - symbolPath, - Some(resolvedName) - ) match { - case Left(error) => - encounteredErrors += error - case Right(_) => () + importTargets.foreach { importTarget => + importTarget.resolveExportedSymbol(symbolName) match { + case Right(resolvedNames) => + resolvedNames.foreach { resolvedName => + val symbolPath = resolvedName.qualifiedName.toString + tryAddEncounteredSymbol( + encounteredSymbols, + moduleImport, + symbolName, + symbolPath, + Some(resolvedName) + ) match { + case Left(error) => + encounteredErrors += error + case Right(_) => () + } } - } - case Left(resolutionError) => - throw new CompilerError( - s"Unreachable: (should have been resolved in previous passes) $resolutionError" - ) + case Left(resolutionError) => + throw new CompilerError( + s"Unreachable: (should have been resolved in previous passes) $resolutionError" + ) + } } } if (encounteredErrors.nonEmpty) { @@ -153,11 +155,11 @@ case object AmbiguousImportsAnalysis extends IRPass { _, _ ) => - getImportTarget(moduleImport, bindingMap) match { - case Some(importTarget) => + getImportTargets(moduleImport, bindingMap) match { + case Some(importTargets) => // Names of the symbols that are exported by a module or a type referred to via importTarget val exportedSymbolNames: List[String] = - importTarget.exportedSymbols.keySet.toList + importTargets.flatMap(_.exportedSymbols.keySet.toList) val symbolsToIterate = hiddenNames match { case None => exportedSymbolNames case Some(hiddenNamesLiterals) => @@ -167,25 +169,27 @@ case object AmbiguousImportsAnalysis extends IRPass { val encounteredErrors: ListBuffer[errors.ImportExport] = ListBuffer() symbolsToIterate.foreach { symbolName => - importTarget.resolveExportedSymbol(symbolName) match { - case Left(resolutionError) => - throw new CompilerError( - s"Unreachable: (should have been resolved in previous passes) $resolutionError" - ) - case Right(List(resolvedName)) => - tryAddEncounteredSymbol( - encounteredSymbols, - moduleImport, - symbolName, - resolvedName.qualifiedName.toString, - Some(resolvedName) - ) match { - case Left(error) => - encounteredErrors += error - case Right(_) => () - } - // If the symbolName is resolved to multiple objects, we ignore it. - case Right(_) => () + importTargets.foreach { importTarget => + importTarget.resolveExportedSymbol(symbolName) match { + case Left(resolutionError) => + throw new CompilerError( + s"Unreachable: (should have been resolved in previous passes) $resolutionError" + ) + case Right(List(resolvedName)) => + tryAddEncounteredSymbol( + encounteredSymbols, + moduleImport, + symbolName, + resolvedName.qualifiedName.toString, + Some(resolvedName) + ) match { + case Left(error) => + encounteredErrors += error + case Right(_) => () + } + // If the symbolName is resolved to multiple objects, we ignore it. + case Right(_) => () + } } } if (encounteredErrors.nonEmpty) { @@ -267,13 +271,13 @@ case object AmbiguousImportsAnalysis extends IRPass { } } - private def getImportTarget( + private def getImportTargets( imp: Import, bindingMap: BindingsMap - ): Option[BindingsMap.ImportTarget] = { + ): Option[List[BindingsMap.ImportTarget]] = { bindingMap.resolvedImports.find(_.importDef == imp) match { case Some(resolvedImport) => - Some(resolvedImport.target) + Some(resolvedImport.targets) case None => None } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala index 1920eb501d2d..639b296b25db 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala @@ -68,15 +68,16 @@ case object ImportSymbolAnalysis extends IRPass { ) => bindingMap.resolvedImports.find(_.importDef == imp) match { case Some(resolvedImport) => - val importedTarget = resolvedImport.target - val unresolvedSymbols = + val importedTargets = resolvedImport.targets + val unresolvedSymbols = importedTargets.flatMap { importedTarget => onlyNames.filterNot(isSymbolResolved(importedTarget, _)) + } if (unresolvedSymbols.nonEmpty) { unresolvedSymbols .map( createErrorForUnresolvedSymbol( imp, - importedTarget, + importedTargets.head, _ ) ) @@ -100,35 +101,42 @@ case object ImportSymbolAnalysis extends IRPass { ) if isAll && !isSynthetic => bindingMap.resolvedImports.find(_.importDef == imp) match { case Some(resolvedImport) => - val importedTarget = resolvedImport.target - importedTarget match { - case BindingsMap.ResolvedModuleMethod(module, method) => - val err = createImportFromMethodError( - imp, - module.getName.toString, - method.name - ) - List(err) - case BindingsMap.ResolvedStaticMethod(module, staticMethod) => - val err = createImportFromMethodError( - imp, - module.getName.createChild(staticMethod.tpName).toString, - staticMethod.methodName - ) - List(err) - case BindingsMap.ResolvedConversionMethod( - module, - conversionMethod - ) => - val err = createImportFromMethodError( - imp, - module.getName - .createChild(conversionMethod.targetTpName) - .toString, - conversionMethod.methodName - ) - List(err) - case _ => List(imp) + val importedTargets = resolvedImport.targets + val errors = importedTargets.flatMap { importedTarget => + importedTarget match { + case BindingsMap.ResolvedModuleMethod(module, method) => + val err = createImportFromMethodError( + imp, + module.getName.toString, + method.name + ) + Some(err) + case BindingsMap.ResolvedStaticMethod(module, staticMethod) => + val err = createImportFromMethodError( + imp, + module.getName.createChild(staticMethod.tpName).toString, + staticMethod.methodName + ) + Some(err) + case BindingsMap.ResolvedConversionMethod( + module, + conversionMethod + ) => + val err = createImportFromMethodError( + imp, + module.getName + .createChild(conversionMethod.targetTpName) + .toString, + conversionMethod.methodName + ) + Some(err) + case _ => None + } + } + if (errors.nonEmpty) { + errors + } else { + List(imp) } case None => List(imp) } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala index c40ba27ffb8a..9e1877930403 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala @@ -98,9 +98,12 @@ final class ImportResolver(compiler: Compiler) extends ImportResolverForIR { } ) } - currentLocal.resolvedImports - .map(_.target.module.unsafeAsModule()) - .distinct + currentLocal.resolvedImports.flatMap { resolvedImport => + val targetModules = resolvedImport.targets.map { target => + target.module.unsafeAsModule() + } + targetModules + }.distinct } @scala.annotation.tailrec @@ -129,17 +132,17 @@ final class ImportResolver(compiler: Compiler) extends ImportResolverForIR { u.loadedFromCache(true) } ) - val converted = Option(current.getBindingsMap()) - ( - converted - .map( - _.resolvedImports - .map(_.target.module.unsafeAsModule()) - .distinct - ) - .getOrElse(Nil), - false - ) + val bm = current.getBindingsMap + if (bm != null) { + val modulesFromResolvedImps = bm.resolvedImports.flatMap { resolvedImp => + resolvedImp.targets.map { target => + target.module.unsafeAsModule() + } + } + (modulesFromResolvedImps.distinct, false) + } else { + (Nil, false) + } case None => compiler.ensureParsed(current) (analyzeModule(current), true) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index a3dacaee6694..5bdfe18a076b 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -151,7 +151,8 @@ class ImportExportTest mainBindingsMap.resolvedImports.size shouldEqual 1 mainBindingsMap .resolvedImports(0) - .target + .targets + .head .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true } @@ -180,7 +181,8 @@ class ImportExportTest mainIr.unwrapBindingMap.resolvedImports.size shouldEqual 1 mainIr.unwrapBindingMap .resolvedImports(0) - .target + .targets + .head .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true } @@ -257,7 +259,11 @@ class ImportExportTest mainIr.imports.head.isInstanceOf[errors.ImportExport] shouldBe false val mainBindingMap = mainIr.unwrapBindingMap mainBindingMap.resolvedImports.size shouldEqual 1 - mainBindingMap.resolvedImports.head.target + mainBindingMap + .resolvedImports + .head + .targets + .head .asInstanceOf[BindingsMap.ResolvedModuleMethod] .method .name shouldEqual "module_method" @@ -278,11 +284,11 @@ class ImportExportTest .getIr val bindingMap = mainIr.unwrapBindingMap bindingMap.resolvedImports.size shouldBe 1 - bindingMap.resolvedImports.head.target shouldBe a[ + bindingMap.resolvedImports.head.targets.head shouldBe a[ BindingsMap.ResolvedConstructor ] - bindingMap.resolvedImports.head.target + bindingMap.resolvedImports.head.targets.head .asInstanceOf[BindingsMap.ResolvedConstructor] .qualifiedName .item shouldBe "Constructor" diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index c2055509276e..b9ff4638b4f7 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -208,11 +208,11 @@ class IrToTruffle( } bindingsMap.resolvedImports.foreach { imp => - imp.target match { - case _: BindingsMap.ResolvedType => - case _: BindingsMap.ResolvedConstructor => - case _: BindingsMap.ResolvedModuleMethod => - case _: BindingsMap.ResolvedStaticMethod => + imp.targets.foreach { + case _: BindingsMap.ResolvedType => + case _: BindingsMap.ResolvedConstructor => + case _: BindingsMap.ResolvedModuleMethod => + case _: BindingsMap.ResolvedStaticMethod => case _: BindingsMap.ResolvedConversionMethod => case ResolvedModule(module) => val mod = module From e5a7273f207bac1f48f6a812ecdc6034e947f790 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 19:21:07 +0200 Subject: [PATCH 048/111] Fix typo in exports in std base Main --- .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index 65648d2b406c..aa5749514c1b 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -177,9 +177,9 @@ export project.Meta.Enso_Project.enso_project export project.Network.Extensions.to_uri export project.Network.Extensions.fetch export project.Network.Extensions.post -export project.System.File_format.Auto_Detect -export project.System.File_format.Bytes -export project.System.File_format.File_Format -export project.System.File_format.Infer -export project.System.File_format.JSON_Format -export project.System.File_format.Plain_Text_Format +export project.System.File_Format.Auto_Detect +export project.System.File_Format.Bytes +export project.System.File_Format.File_Format +export project.System.File_Format.Infer +export project.System.File_Format.JSON_Format +export project.System.File_Format.Plain_Text_Format From a346c29b2931d180b98cbcdc04f856117f1c8b4f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 19:21:37 +0200 Subject: [PATCH 049/111] ResolvedMethod's qualifiedName is consistent with imports --- .../org/enso/compiler/data/BindingsMap.scala | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 63033620fe44..d9d4de614f1f 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -754,6 +754,10 @@ object BindingsMap { override def exportedSymbols: Map[String, List[ResolvedName]] = Map( methodName -> List(this) ) + + override def qualifiedName: QualifiedName = { + module.getName.createChild(methodName) + } } /** A representation of a resolved method defined directly on module. @@ -795,9 +799,6 @@ object BindingsMap { def unsafeGetIr(missingMessage: String): ir.module.scope.Definition = getIr.getOrElse(throw new CompilerError(missingMessage)) - override def qualifiedName: QualifiedName = - module.getName.createChild(method.name) - override def methodName: String = method.name } @@ -819,11 +820,6 @@ object BindingsMap { } } - override def qualifiedName: QualifiedName = - module.getName - .createChild(staticMethod.tpName) - .createChild(staticMethod.methodName) - override def methodName: String = staticMethod.methodName } @@ -843,11 +839,6 @@ object BindingsMap { } } - override def qualifiedName: QualifiedName = - module.getName - .createChild(conversionMethod.targetTpName) - .createChild(conversionMethod.methodName) - override def methodName: String = conversionMethod.methodName } From 0b37f9fe1012f19d4b20346330f3e8dfb616889c Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 19:22:28 +0200 Subject: [PATCH 050/111] ImportResolverAlgorithm deals with extension and conversion methods --- .../phase/ImportResolverAlgorithm.java | 80 +++++++++++++- .../compiler/phase/ImportResolverForIR.java | 104 +++++++++++++++++- .../callable/InvokeMethodImportResolver.java | 34 +++++- 3 files changed, 212 insertions(+), 6 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java index dcce205a11ec..5e9dbd2e9a3f 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.List; +import java.util.stream.Collectors; import org.enso.compiler.core.CompilerError; import org.enso.editions.LibraryName; @@ -13,7 +14,9 @@ public abstract class ImportResolverAlgorithm< ResolvedType, ResolvedModule, ResolvedConstructor, - ResolvedModuleMethod> { + ResolvedModuleMethod, + ResolvedExtensionMethod, + ResolvedConversionMethod> { protected ImportResolverAlgorithm() {} protected abstract String nameForImport(Import imp); @@ -28,6 +31,10 @@ protected ImportResolverAlgorithm() {} protected abstract String nameForModuleMethod(ResolvedModuleMethod method); + protected abstract String nameForExtensionMethod(ResolvedExtensionMethod method); + + protected abstract String nameForConversionMethod(ResolvedConversionMethod method); + /** * Returns a list of all the exports from the module of the given symbol * @@ -47,6 +54,10 @@ protected ImportResolverAlgorithm() {} protected abstract java.util.List definedModuleMethods(Import imp); + protected abstract java.util.List definedExtensionMethods(Import imp); + + protected abstract java.util.List definedConversionMethods(Import imp); + /** * Ensure library is loaded and load a module. * @@ -68,6 +79,12 @@ protected abstract Result createResolvedConstructor( protected abstract Result createResolvedModuleMethod( Import imp, java.util.List exp, ResolvedModuleMethod moduleMethod); + protected abstract Result createResolvedExtensionMethods( + Import imp, java.util.List exp, java.util.List extensionMethods); + + protected abstract Result createResolvedConversionMethods( + Import imp, java.util.List exp, java.util.List conversionMethods); + protected abstract Result createErrorPackageCoundNotBeLoaded( Import imp, String impName, String loadingError); @@ -118,9 +135,14 @@ private Result tryResolveImportNew(Module module, Import imp) { if (moduleMethod != null) { return createResolvedModuleMethod(imp, exp, moduleMethod); } - // static, extension and conversion methods can only be imported with - // `from import all` syntax. That syntax is handled by this algorithm - // by importing the `Module` only and not resolving all the symbols. + var extensionMethods = tryResolveAsExtensionMethods(imp); + if (extensionMethods != null) { + return createResolvedExtensionMethods(imp, exp, extensionMethods); + } + var conversionMethods = tryResolveAsConversionMethods(imp); + if (conversionMethods != null) { + return createResolvedConversionMethods(imp, exp, conversionMethods); + } return createErrorModuleDoesNotExist(imp, impName); } catch (IOException e) { return createErrorPackageCoundNotBeLoaded(imp, impName, e.getMessage()); @@ -173,6 +195,56 @@ private ResolvedModuleMethod tryResolveAsModuleMethod(Import imp) { return methodOpt.orElse(null); } + /** + * Tries to resolve the given import as a list of extension methods. + * Note that it is possible that a single symbol resolves to multiple extension methods. + * @return List with at least one element. null if there are no static methods in the imported module scope. + */ + private java.util.List tryResolveAsExtensionMethods(Import imp) { + var parts = partsForImport(imp); + if (parts.size() < 3) { + return null; + } + var definedExtensionMethods = definedExtensionMethods(imp); + if (definedExtensionMethods == null) { + return null; + } + var methodName = parts.get(parts.size() - 1); + var foundExtMethods = definedExtensionMethods.stream() + .filter(method -> nameForExtensionMethod(method).equals(methodName)) + .collect(Collectors.toUnmodifiableList()); + if (foundExtMethods.isEmpty()) { + return null; + } else { + return foundExtMethods; + } + } + + /** + * Tries to resolve the given import as a list of extension methods. + * Note that it is possible that a single symbol resolves to multiple extension methods. + * @return List of at least one element. null if there are no conversion methods in the imported module scope. + */ + private java.util.List tryResolveAsConversionMethods(Import imp) { + var parts = partsForImport(imp); + if (parts.size() < 3) { + return null; + } + var definedConvMethods = definedConversionMethods(imp); + if (definedConvMethods == null) { + return null; + } + var methodName = parts.get(parts.size() - 1); + var foundConvMethods = definedConvMethods.stream() + .filter(method -> nameForConversionMethod(method).equals(methodName)) + .collect(Collectors.toUnmodifiableList()); + if (foundConvMethods.isEmpty()) { + return null; + } else { + return foundConvMethods; + } + } + public static final class HiddenNamesConflict extends RuntimeException { private HiddenNamesConflict(String message) { super(message); diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java index d0675b4ae5b7..13bab020d493 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java @@ -13,9 +13,12 @@ import org.enso.compiler.core.ir.module.scope.Import; import org.enso.compiler.data.BindingsMap; import org.enso.compiler.data.BindingsMap$ModuleReference$Concrete; +import org.enso.compiler.data.BindingsMap.ImportTarget; import org.enso.compiler.data.BindingsMap.ResolvedConstructor; +import org.enso.compiler.data.BindingsMap.ResolvedConversionMethod; import org.enso.compiler.data.BindingsMap.ResolvedImport; import org.enso.compiler.data.BindingsMap.ResolvedModuleMethod; +import org.enso.compiler.data.BindingsMap.ResolvedStaticMethod; import org.enso.compiler.data.BindingsMap.ResolvedType; import org.enso.editions.LibraryName; @@ -32,7 +35,9 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm< ResolvedType, CompilerContext.Module, ResolvedConstructor, - ResolvedModuleMethod + ResolvedModuleMethod, + ResolvedStaticMethod, + ResolvedConversionMethod > { abstract Compiler getCompiler(); @@ -66,6 +71,15 @@ protected String nameForModuleMethod(ResolvedModuleMethod resolvedModuleMethod) return resolvedModuleMethod.methodName(); } + @Override + protected String nameForExtensionMethod(ResolvedStaticMethod resolvedStaticMethod) { + return resolvedStaticMethod.methodName(); + } + + @Override + protected String nameForConversionMethod(ResolvedConversionMethod resolvedConversionMethod) { + return resolvedConversionMethod.methodName(); + } @Override protected final java.util.List exportsFor(Module module, String impName) { @@ -195,6 +209,72 @@ protected java.util.List definedModuleMethods(Import.Modul return modMethods; } + @Override + protected java.util.List definedExtensionMethods(Import.Module imp) { + var parts = partsForImport(imp); + if (parts.size() < 3) { + return null; + } + var modMethodNameIdx = parts.size() - 1; + var modMethodName = parts.get(modMethodNameIdx); + var modFullName = parts + .stream() + .limit(modMethodNameIdx) + .collect(Collectors.joining(".")); + var compiler = getCompiler(); + var optionMod = compiler.getModule(modFullName); + if (optionMod.isEmpty()) { + return null; + } + var mod = optionMod.get(); + compiler.ensureParsed(mod); + var bindingsMap = loadBindingsMap(mod); + var extensionMethods = scala.jdk.javaapi.CollectionConverters.asJava(bindingsMap.definedEntities()) + .stream() + .filter(definedEntity -> { + if (definedEntity instanceof BindingsMap.StaticMethod extensionMethod) { + return extensionMethod.name().equals(modMethodName); + } + return false; + }) + .map(entity -> new ResolvedStaticMethod(new BindingsMap$ModuleReference$Concrete(mod), (BindingsMap.StaticMethod) entity)) + .collect(Collectors.toUnmodifiableList()); + return extensionMethods; + } + + @Override + protected java.util.List definedConversionMethods(Import.Module imp) { + var parts = partsForImport(imp); + if (parts.size() < 3) { + return null; + } + var modMethodNameIdx = parts.size() - 1; + var modMethodName = parts.get(modMethodNameIdx); + var modFullName = parts + .stream() + .limit(modMethodNameIdx) + .collect(Collectors.joining(".")); + var compiler = getCompiler(); + var optionMod = compiler.getModule(modFullName); + if (optionMod.isEmpty()) { + return null; + } + var mod = optionMod.get(); + compiler.ensureParsed(mod); + var bindingsMap = loadBindingsMap(mod); + var conversionMethods = scala.jdk.javaapi.CollectionConverters.asJava(bindingsMap.definedEntities()) + .stream() + .filter(definedEntity -> { + if (definedEntity instanceof BindingsMap.ConversionMethod conversionMethod) { + return conversionMethod.methodName().equals(modMethodName); + } + return false; + }) + .map(entity -> new ResolvedConversionMethod(new BindingsMap$ModuleReference$Concrete(mod), (BindingsMap.ConversionMethod) entity)) + .collect(Collectors.toUnmodifiableList()); + return conversionMethods; + } + @Override protected final CompilerContext.Module loadLibraryModule(LibraryName libraryName, String moduleName) throws IOException { var compiler = this.getCompiler(); @@ -239,6 +319,28 @@ protected Tuple2> createResolvedModuleMethod(Impo return new Tuple2<>(imp, scala.Some.apply(resolvedImport)); } + @Override + protected Tuple2> createResolvedExtensionMethods(Import.Module imp, + java.util.List exp, java.util.List extensionMethods) { + java.util.List importTargets = extensionMethods + .stream() + .map(ImportTarget.class::cast) + .collect(Collectors.toUnmodifiableList()); + var resolvedImport = new BindingsMap.ResolvedImport(imp, toScalaList(exp), toScalaList(importTargets)); + return new Tuple2<>(imp, scala.Some.apply(resolvedImport)); + } + + @Override + protected Tuple2> createResolvedConversionMethods( + Import.Module imp, java.util.List exp, + java.util.List resolvedConversionMethods) { + java.util.List importTargets = resolvedConversionMethods + .stream() + .map(ImportTarget.class::cast) + .collect(Collectors.toUnmodifiableList()); + var resolvedImport = new BindingsMap.ResolvedImport(imp, toScalaList(exp), toScalaList(importTargets)); + return new Tuple2<>(imp, scala.Some.apply(resolvedImport)); + } @Override protected final Tuple2> createErrorPackageCoundNotBeLoaded(Import.Module imp, String impName, String loadingError) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java index b2512eace722..05d3b3d05088 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java @@ -17,7 +17,7 @@ final class InvokeMethodImportResolver extends ImportResolverAlgorithm< - EnsoObject, Module, UnresolvedSymbol, Object, Type, Module, AtomConstructor, Function> { + EnsoObject, Module, UnresolvedSymbol, Object, Type, Module, AtomConstructor, Function, Function, Function> { private final Module module; private final TopLevelScope topScope; @@ -65,6 +65,16 @@ protected String nameForModuleMethod(Function function) { return function.getName(); } + @Override + protected String nameForExtensionMethod(Function function) { + return function.getName(); + } + + @Override + protected String nameForConversionMethod(Function function) { + return function.getName(); + } + @Override protected List exportsFor(Module module, String impName) { return Collections.emptyList(); @@ -90,6 +100,16 @@ protected List definedModuleMethods(UnresolvedSymbol symbol) { return Collections.emptyList(); } + @Override + protected List definedExtensionMethods(UnresolvedSymbol imp) { + return null; + } + + @Override + protected List definedConversionMethods(UnresolvedSymbol imp) { + return null; + } + @Override protected Module loadLibraryModule(LibraryName libraryName, String moduleName) throws IOException { @@ -119,6 +139,18 @@ protected EnsoObject createResolvedModuleMethod( throw new UnsupportedOperationException("unimplemented"); } + @Override + protected EnsoObject createResolvedExtensionMethods(UnresolvedSymbol imp, List exp, + List functions) { + throw new UnsupportedOperationException("unimplemented"); + } + + @Override + protected EnsoObject createResolvedConversionMethods(UnresolvedSymbol imp, List exp, + List functions) { + throw new UnsupportedOperationException("unimplemented"); + } + @Override protected EnsoObject createErrorPackageCoundNotBeLoaded( UnresolvedSymbol imp, String impName, String loadingError) { From 6e0d992d413656756fa7a9c1c9ef67fb70ae6be5 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 Jul 2024 19:25:39 +0200 Subject: [PATCH 051/111] Add import/export test for extension methods --- .../test/semantic/ImportExportTest.scala | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 5bdfe18a076b..d7196b7131c3 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -324,6 +324,60 @@ class ImportExportTest .isInstanceOf[BindingsMap.ResolvedConstructor] shouldBe true } + "import single static method with one symbol" in { + """ + |type My_Type + |My_Type.extension_method x = x + |""".stripMargin + .createModule(packageQualifiedName.createChild("Module")) + + val mainIr = s""" + |import $namespace.$packageName.Module.extension_method + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr + .imports + .head + .isInstanceOf[errors.ImportExport] shouldBe false + + val bindingsMap = mainIr.unwrapBindingMap + bindingsMap.resolvedImports.size shouldBe 1 + val resolvedImport = bindingsMap.resolvedImports.head + resolvedImport.targets.head shouldBe a[BindingsMap.ResolvedStaticMethod] + resolvedImport + .targets + .head + .asInstanceOf[BindingsMap.ResolvedStaticMethod] + .staticMethod + .methodName shouldBe "extension_method" + } + + "export multiple static methods with one symbol" in { + """ + |type My_Type + |type Other_Type + | + |My_Type.extension_method x = x + |Other_Type.extension_method y = y + |""".stripMargin + .createModule(packageQualifiedName.createChild("Module")) + + val mainIr = s""" + |export $namespace.$packageName.Module.extension_method + |""".stripMargin + .createModule(packageQualifiedName.createChild("Main")) + .getIr + mainIr + .imports + .head + .isInstanceOf[errors.ImportExport] shouldBe false + val bm = mainIr.unwrapBindingMap + bm.exportedSymbols.size shouldBe 1 + bm.exportedSymbols.get("extension_method") shouldBe defined + bm.exportedSymbols("extension_method").size shouldBe 2 + } + "result in error when trying to import mix of constructors and methods from a type" in { """ |type Other_Module_Type From 2cab5317da1e0b06f788f539f61005b0cf635c70 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 Jul 2024 13:01:24 +0200 Subject: [PATCH 052/111] Add tests for exporting and importing conversion methods --- .../exports/ExportConversionMethodTest.java | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConversionMethodTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConversionMethodTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConversionMethodTest.java new file mode 100644 index 000000000000..2db23dfdcb3b --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConversionMethodTest.java @@ -0,0 +1,143 @@ +package org.enso.interpreter.test.exports; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.util.Set; +import org.enso.compiler.data.BindingsMap.ResolvedConversionMethod; +import org.enso.pkg.QualifiedName; +import org.enso.polyglot.PolyglotContext; +import org.enso.polyglot.RuntimeOptions; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ModuleUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ExportConversionMethodTest { + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void conversionMethodCanBeImportedByName() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), + """ + type A_Type + Value + type B_Type + """); + var bMod = + new SourceModule( + QualifiedName.fromString("B_Module"), + """ + import project.A_Module.A_Type + import project.A_Module.B_Type + + B_Type.from (_:A_Type) = 42 + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + import project.B_Module.from + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, bMod, mainMod), projDir); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + + var mainResolvedImps = ModuleUtils.getResolvedImports(ctx, "local.Proj.Main"); + assertThat(mainResolvedImps.size(), is(1)); + assertThat(mainResolvedImps.get(0).targets().size(), is(1)); + assertThat(mainResolvedImps.get(0).targets().head(), is(instanceOf(ResolvedConversionMethod.class))); + } + } + + @Test + public void conversionMethodIsInBindingMap() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), """ + type A_Type + type B_Type + B_Type.from (_:A_Type) = 42 + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + export project.A_Module.from + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + + var aModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); + assertThat(aModExportedSymbols.size(), is(3)); + assertThat(aModExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type", "from")); + + var mainResolvedImps = ModuleUtils.getResolvedImports(ctx, "local.Proj.Main"); + assertThat(mainResolvedImps.size(), is(1)); + assertThat(mainResolvedImps.get(0).targets().size(), is(1)); + assertThat(mainResolvedImps.get(0).targets().head(), is(instanceOf(ResolvedConversionMethod.class))); + var mainExportedSyms = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSyms.size(), is(1)); + assertThat(mainExportedSyms, hasKey("from")); + } + } + + @Test + public void multipleConversionMethodsCanBeImported() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), """ + type A_Type + type B_Type + B_Type.from (_:A_Type) = 1 + A_Type.from (_:B_Type) = 2 + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + export project.A_Module.from + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + + var aModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); + assertThat(aModExportedSymbols.size(), is(3)); + assertThat(aModExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type", "from")); + + var mainResolvedImps = ModuleUtils.getResolvedImports(ctx, "local.Proj.Main"); + assertThat(mainResolvedImps.size(), is(1)); + assertThat(mainResolvedImps.get(0).targets().size(), is(2)); + assertThat(mainResolvedImps.get(0).targets().apply(0), is(instanceOf(ResolvedConversionMethod.class))); + assertThat(mainResolvedImps.get(0).targets().apply(1), is(instanceOf(ResolvedConversionMethod.class))); + var mainExportedSyms = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSyms.size(), is(1)); + assertThat(mainExportedSyms, hasKey("from")); + assertThat(mainExportedSyms.get("from").size(), is(2)); + } + } +} From 3022f6fa9a489f563c6776e1275be5638cc1ba64 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 Jul 2024 13:01:29 +0200 Subject: [PATCH 053/111] Add tests for exporting and importing conversion methods --- .../src/main/java/org/enso/test/utils/ModuleUtils.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java b/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java index 5161ce2146b0..0d330c99b335 100644 --- a/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java +++ b/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java @@ -5,6 +5,7 @@ import java.util.Map; import org.enso.compiler.context.CompilerContext.Module; import org.enso.compiler.data.BindingsMap.DefinedEntity; +import org.enso.compiler.data.BindingsMap.ResolvedImport; import org.enso.compiler.data.BindingsMap.ResolvedName; import org.graalvm.polyglot.Context; import scala.jdk.javaapi.CollectionConverters; @@ -26,6 +27,12 @@ public static Map> getExportedSymbolsFromModule( return getExportedSymbols(mod); } + public static List getResolvedImports(Context ctx, String modName) { + var ensoCtx = ContextUtils.leakContext(ctx); + var mod = ensoCtx.getPackageRepository().getLoadedModule(modName).get(); + return CollectionConverters.asJava(mod.getBindingsMap().resolvedImports()); + } + public static List getDefinedEntities(Context ctx, String modName) { var ensoCtx = ContextUtils.leakContext(ctx); var mod = ensoCtx.getPackageRepository().getLoadedModule(modName).get(); From a49170317b808aaa57205e4343c8097918cf13b7 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 Jul 2024 13:04:13 +0200 Subject: [PATCH 054/111] Replace `from ... export ...` with `export ...` type in micro-distribution --- .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index daf428aac959..3241d83db54b 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -3,11 +3,13 @@ export project.Panic.Panic export project.Error.Error export project.Any.Any export project.Data.Array.Array -from project.Data.Boolean export Boolean, True, False +export project.Data.Boolean.Boolean.True +export project.Data.Boolean.Boolean.False export project.Data.Text.Text export project.Data.Time.Date.Date export project.Data.List.List -from project.Data.Numbers export Number, Integer +export project.Data.Numbers.Number +export project.Data.Numbers.Integer export project.Data.Vector.Vector export project.Function.Function export project.Polyglot.Java From 9143c292091f78064429a1792480e5164a31a261 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 Jul 2024 13:07:19 +0200 Subject: [PATCH 055/111] In micro-distribution, replace some import all by specific imports --- .../lib/Standard/Base/0.0.0-dev/src/Any.enso | 2 +- .../lib/Standard/Base/0.0.0-dev/src/Error.enso | 2 +- .../lib/Standard/Base/0.0.0-dev/src/Runtime.enso | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso index dd5955e28a32..bed7a79e2b9f 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso @@ -1,4 +1,4 @@ -from project.Data.Boolean import False +import project.Data.Boolean.Boolean.False @Builtin_Type type Any diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Error.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Error.enso index b11e7da899bb..305632d875e1 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Error.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Error.enso @@ -1,5 +1,5 @@ import project.Any.Any -from project.Data.Boolean import True +import project.Data.Boolean.Boolean.True @Builtin_Type type Error diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso index 9a7ba02992fe..7b34ca372780 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso @@ -6,8 +6,9 @@ import project.Errors.Common.Forbidden_Operation import project.Function.Function import project.Panic.Panic -from project.Data.Boolean import True -from project.Runtime.Context import Input, Output +import project.Data.Boolean.Boolean.True +import project.Runtime.Context.Input +import project.Runtime.Context.Output @Builtin_Type type Context From b6c0765a6aef859df0f051d2dc4c0c12c550c1e2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 Jul 2024 13:17:17 +0200 Subject: [PATCH 056/111] Use specific import/exports in tests --- .../resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso | 4 ++-- .../resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso | 3 ++- .../src/test/resources/Test_Multiple_Exports/src/F2.enso | 2 +- .../src/test/resources/Test_Private_Modules_1/src/Main.enso | 4 ++-- .../src/test/resources/Test_Type_Exports/src/Type_Exp.enso | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso index 5e31acfd2e4b..f7cc4b255b53 100644 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso +++ b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso @@ -1,3 +1,3 @@ export project.F1 -from project.F1 export foo -from project.F1 export bar +export project.F1.foo +export project.F1.bar diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso index 96d2b4366799..f7cc4b255b53 100644 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso +++ b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso @@ -1,2 +1,3 @@ export project.F1 -from project.F1 export foo, bar +export project.F1.foo +export project.F1.bar diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Exports/src/F2.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Exports/src/F2.enso index d41d90c53a90..8f665d09ca7a 100644 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Exports/src/F2.enso +++ b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Exports/src/F2.enso @@ -1,2 +1,2 @@ export project.F1 -from project.F1 export foo +export project.F1.foo diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Private_Modules_1/src/Main.enso b/engine/runtime-integration-tests/src/test/resources/Test_Private_Modules_1/src/Main.enso index d17abbeacc10..bf09126bae4b 100644 --- a/engine/runtime-integration-tests/src/test/resources/Test_Private_Modules_1/src/Main.enso +++ b/engine/runtime-integration-tests/src/test/resources/Test_Private_Modules_1/src/Main.enso @@ -1,8 +1,8 @@ # Export some "public" stuff from this library -from project.Pub_Mod export Pub_Mod_Type +export project.Pub_Mod.Pub_Mod_Type # Can import and use private modules in the same project -from project.Priv_Mod import Type_In_Priv_Mod +import project.Priv_Mod.Type_In_Priv_Mod main = Type_In_Priv_Mod.Value 42 . data diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Type_Exports/src/Type_Exp.enso b/engine/runtime-integration-tests/src/test/resources/Test_Type_Exports/src/Type_Exp.enso index c281090a4927..ad1ba20f9429 100644 --- a/engine/runtime-integration-tests/src/test/resources/Test_Type_Exports/src/Type_Exp.enso +++ b/engine/runtime-integration-tests/src/test/resources/Test_Type_Exports/src/Type_Exp.enso @@ -1 +1 @@ -from project.Type_Def.Maybe export Some +export project.Type_Def.Maybe.Some From 2bc90425abb53939d5caa4175705f5e3169c3744 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 Jul 2024 13:23:30 +0200 Subject: [PATCH 057/111] Remove tests on export hiding --- .../package.yaml | 6 ------ .../src/F1.enso | 2 -- .../src/F2.enso | 3 --- .../src/Main.enso | 7 ------- .../package.yaml | 6 ------ .../src/F1.enso | 2 -- .../src/F2.enso | 3 --- .../src/Main.enso | 7 ------- .../test/semantic/ImportsTest.scala | 20 ------------------- 9 files changed, 56 deletions(-) delete mode 100644 engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/package.yaml delete mode 100644 engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F1.enso delete mode 100644 engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso delete mode 100644 engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/Main.enso delete mode 100644 engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/package.yaml delete mode 100644 engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F1.enso delete mode 100644 engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso delete mode 100644 engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/Main.enso diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/package.yaml b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/package.yaml deleted file mode 100644 index f00b6a028ea7..000000000000 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/package.yaml +++ /dev/null @@ -1,6 +0,0 @@ -name: Test_Multiple_Conflicting_Exports_1 -license: APLv2 -enso-version: default -version: "0.0.1" -author: "Enso Team " -maintainer: "Enso Team " diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F1.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F1.enso deleted file mode 100644 index e7c5649502ac..000000000000 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F1.enso +++ /dev/null @@ -1,2 +0,0 @@ -foo = 42 -bar = "z" diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso deleted file mode 100644 index f7cc4b255b53..000000000000 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/F2.enso +++ /dev/null @@ -1,3 +0,0 @@ -export project.F1 -export project.F1.foo -export project.F1.bar diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/Main.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/Main.enso deleted file mode 100644 index 6951b9d3291f..000000000000 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_1/src/Main.enso +++ /dev/null @@ -1,7 +0,0 @@ -from Standard.Base import IO -from project.F2 import all - -main = - IO.println F1.bar - IO.println foo - diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/package.yaml b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/package.yaml deleted file mode 100644 index 69ee40fa0774..000000000000 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/package.yaml +++ /dev/null @@ -1,6 +0,0 @@ -name: Test_Multiple_Conflicting_Exports_2 -license: APLv2 -enso-version: default -version: "0.0.1" -author: "Enso Team " -maintainer: "Enso Team " diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F1.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F1.enso deleted file mode 100644 index e7c5649502ac..000000000000 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F1.enso +++ /dev/null @@ -1,2 +0,0 @@ -foo = 42 -bar = "z" diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso deleted file mode 100644 index f7cc4b255b53..000000000000 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/F2.enso +++ /dev/null @@ -1,3 +0,0 @@ -export project.F1 -export project.F1.foo -export project.F1.bar diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/Main.enso b/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/Main.enso deleted file mode 100644 index 6951b9d3291f..000000000000 --- a/engine/runtime-integration-tests/src/test/resources/Test_Multiple_Conflicting_Exports_2/src/Main.enso +++ /dev/null @@ -1,7 +0,0 @@ -from Standard.Base import IO -from project.F2 import all - -main = - IO.println F1.bar - IO.println foo - diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala index 0c76e1cd5115..b933471abd37 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala @@ -143,26 +143,6 @@ class ImportsTest extends PackageTest { outLines(1) shouldEqual "42" } - "Compiler" should "reject qualified exports of the same module with conflicting hidden names" in { - the[InterpreterException] thrownBy evalTestProject( - "Test_Multiple_Conflicting_Exports_1" - ) should have message "Compilation aborted due to errors." - val outLines = consumeOut - outLines( - 1 - ) shouldEqual "Hidden 'foo' name of the exported module local.Test_Multiple_Conflicting_Exports_1.F1 conflicts with the qualified export" - } - - "Compiler" should "reject unqualified exports of the same module with conflicting hidden names" in { - the[InterpreterException] thrownBy evalTestProject( - "Test_Multiple_Conflicting_Exports_2" - ) should have message "Compilation aborted due to errors." - val outLines = consumeOut - outLines( - 1 - ) shouldEqual "Hidden 'bar' name of the export module local.Test_Multiple_Conflicting_Exports_2.F1 conflicts with the unqualified export" - } - "Polyglot symbols" should "not be exported" in { val ex = the[InterpreterException] thrownBy evalTestProject( "Test_Polyglot_Exports" From f3d0afcbbb82bfa1d3f248b887f5719b5f60751b Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 Jul 2024 14:29:07 +0200 Subject: [PATCH 058/111] Fix Export.showCode method --- .../org/enso/compiler/core/ir/module/scope/Export.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/module/scope/Export.scala b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/module/scope/Export.scala index 5b84b26d47f7..0a1bb8937b6b 100644 --- a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/module/scope/Export.scala +++ b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/module/scope/Export.scala @@ -146,7 +146,12 @@ object Export { /** @inheritdoc */ override def showCode(indent: Int): String = { val renameCode = rename.map(n => s" as ${n.name}").getOrElse("") - s"export ${name.name}$renameCode" + onlyNames match { + case Some(names) => + s"from ${name.name} export ${names.map(_.name).mkString(", ")}$renameCode" + case None => + s"export ${name.name}$renameCode" + } } /** Gets the name of the module visible in the importing scope, From f454ac9b91f63b07841ec60dfd7b29940397c0cb Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 3 Jul 2024 14:29:18 +0200 Subject: [PATCH 059/111] Remove export all from test --- .../test/resources/Test_Fully_Qualified_Name_1/src/Main.enso | 2 +- .../org/enso/compiler/test/pass/desugar/ImportsTest.scala | 4 +--- .../lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/resources/Test_Fully_Qualified_Name_1/src/Main.enso b/engine/runtime-integration-tests/src/test/resources/Test_Fully_Qualified_Name_1/src/Main.enso index 111e633904f6..5f1580ace58b 100644 --- a/engine/runtime-integration-tests/src/test/resources/Test_Fully_Qualified_Name_1/src/Main.enso +++ b/engine/runtime-integration-tests/src/test/resources/Test_Fully_Qualified_Name_1/src/Main.enso @@ -13,4 +13,4 @@ main = Test_Fully_Qualified_Name_1.Synthetic_Mod.A_Mod.A_Type export project.Synthetic_Mod.A_Mod.A_Type -from project.Synthetic_Mod.A_Mod export static_method +export project.Synthetic_Mod.A_Mod.static_method diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/desugar/ImportsTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/desugar/ImportsTest.scala index 8d7064cd993e..f3a74fd05df9 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/desugar/ImportsTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/desugar/ImportsTest.scala @@ -57,7 +57,6 @@ class ImportsTest extends CompilerTest { |export Bar.Foo |export Bar.Foo as Bar |from Bar.Foo export Bar, Baz - |from Bar.Foo export all | |import Foo.Bar.Baz |export Foo.Bar.Baz @@ -73,11 +72,10 @@ class ImportsTest extends CompilerTest { } "desugar project name exports correctly" in { - ir.exports.take(4).map(_.showCode()) shouldEqual List( + ir.exports.take(3).map(_.showCode()) shouldEqual List( "export Bar.Foo.Main as Foo", "export Bar.Foo.Main as Bar", "from Bar.Foo.Main export Bar, Baz", - "from Bar.Foo.Main export all" ) } diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso index 28a059ec0bf4..234a4dce6303 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso @@ -1,4 +1,5 @@ -from project.Data.Boolean.Boolean export True, False +export project.Data.Boolean.Boolean.False +export project.Data.Boolean.Boolean.True @Builtin_Type type Boolean From fb939832686a04d9c561c273e8e7a0bc620b1ee2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 11:10:07 +0200 Subject: [PATCH 060/111] Add asert checks for consistent exported symbols --- .../org/enso/compiler/data/BindingsMap.scala | 13 ++++++++++ .../phase/exports/ExportsResolution.scala | 26 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index d9d4de614f1f..0081ca5a04fa 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -430,6 +430,7 @@ object BindingsMap { targets: List[ImportTarget] ) { assert(targets.nonEmpty) + assert(areTargetsConsistent(), "All targets must be either static methods or conversion methods") /** Convert the internal [[ModuleReference]] to an abstract reference. * @@ -464,6 +465,18 @@ object BindingsMap { def findExportedSymbolsFor(name: String): List[ResolvedName] = { targets.flatMap(_.findExportedSymbolsFor(name)) } + + private def areTargetsConsistent(): Boolean = { + if (targets.size > 1) { + // If there are multiple targets, they can either all be static methods, or all be + // conversion methods. + val allStaticMethods = targets.forall(_.isInstanceOf[ResolvedStaticMethod]) + val allConversionMethods = targets.forall(_.isInstanceOf[ResolvedConversionMethod]) + allStaticMethods || allConversionMethods + } else { + true + } + } } sealed trait DefinedEntity { diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 7ee7d641fc19..1c468caf9dca 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -205,11 +205,35 @@ class ExportsResolution(private val context: CompilerContext) { exportedModules, reExportedSymbols ).flatten.groupBy(_._1).map { case (m, names) => - (m, names.flatMap(_._2).distinct) + val resolvedNames = names.flatMap(_._2).distinct + assert( + areResolvedNamesConsistent(resolvedNames), + s"Resolved names are not consistent: ${resolvedNames}" + ) + (m, resolvedNames) } } } + /** + * If there are multiple resolved names for one exported symbol, they must be consistent. + * I.e., either they are all static methods, or all conversion methods. + * We cannot, for example, export type and a module for one symbol - that would result + * in a collision. + * @return true if they are consistent, false otherwise. + */ + private def areResolvedNamesConsistent( + resolvedNames: List[ResolvedName] + ): Boolean = { + if (resolvedNames.size > 1) { + val allStaticMethods = resolvedNames.forall(_.isInstanceOf[ResolvedStaticMethod]) + val allConversionMethods = resolvedNames.forall(_.isInstanceOf[ResolvedConversionMethod]) + allStaticMethods || allConversionMethods + } else { + true + } + } + /** Performs exports resolution on a selected set of modules. * * The exports graph is validated and stored in the individual modules, From 409fb18c8b494247d16aa3c1a44869167db712e4 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 11:48:24 +0200 Subject: [PATCH 061/111] Add tests for exported symbols from synthetic modules --- .../enso/compiler/ExportedSymbolsTest.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index ee3ba74e96a0..013bcd85fae7 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; @@ -117,6 +118,76 @@ public void exportRenamedSymbol() throws IOException { assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Foo")); } + @Test + public void exportedSymbolsFromSubModule() throws IOException { + var aMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.A_Module"), """ + type A_Module + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + import project.Synthetic_Module + """); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + var ctx = createCtx(projDir); + compile(ctx); + var syntheticModExpSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Synthetic_Module"); + assertThat("Just a A_Module submodule should be exported", syntheticModExpSymbols.size(), is(1)); + assertThat("Just a A_Module submodule should be exported", syntheticModExpSymbols, hasKey("A_Module")); + } + + @Test + public void exportTypeFromModuleWithSameName() throws IOException { + var aMod = new SourceModule(QualifiedName.fromString("A_Module"), """ + type A_Module + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + export project.A_Module.A_Module + """); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + var ctx = createCtx(projDir); + compile(ctx); + var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSymbols.size(), is(1)); + assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module")); + assertThat(mainExportedSymbols.get("A_Module").size(), is(1)); + assertThat(mainExportedSymbols.get("A_Module").get(0), is(instanceOf(BindingsMap.ResolvedType.class))); + } + + @Test + public void exportModuleWithTypeWithSameName() throws IOException { + var aMod = new SourceModule(QualifiedName.fromString("A_Module"), """ + type A_Module + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + export project.A_Module + """); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + var ctx = createCtx(projDir); + compile(ctx); + var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSymbols.size(), is(1)); + assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module")); + assertThat(mainExportedSymbols.get("A_Module").size(), is(1)); + assertThat(mainExportedSymbols.get("A_Module").get(0), is(instanceOf(BindingsMap.ResolvedModule.class))); + } + + @Test + public void exportSyntheticModule() throws IOException { + var aMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.A_Module"), """ + type A_Type + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + export project.Synthetic_Module + """); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + var ctx = createCtx(projDir); + compile(ctx); + var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainExportedSymbols.size(), is(1)); + assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Synthetic_Module")); + assertThat(mainExportedSymbols.get("Synthetic_Module").size(), is(1)); + assertThat(mainExportedSymbols.get("Synthetic_Module").get(0), is(instanceOf(BindingsMap.ResolvedModule.class))); + } + private static Context createCtx(Path projDir) { return ContextUtils.defaultContextBuilder() .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) From 0c371d245e09692af3622b043a1268c9f5fa6c5f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 11:48:51 +0200 Subject: [PATCH 062/111] ExportsResolution does not re-export symbols from resolved exports --- .../phase/exports/ExportsResolution.scala | 56 ++++++++----------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 1c468caf9dca..8d6bdd1b755b 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -2,13 +2,10 @@ package org.enso.compiler.phase.exports import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.ModuleReference.Concrete -import org.enso.compiler.data.BindingsMap.{ - ExportedModule, - ImportTarget, - ResolvedModule -} +import org.enso.compiler.data.BindingsMap.{ExportedModule, ImportTarget, ResolvedConversionMethod, ResolvedModule, ResolvedName, ResolvedStaticMethod} import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module +import org.enso.compiler.core.CompilerError import scala.collection.mutable @@ -172,45 +169,36 @@ class ExportsResolution(private val context: CompilerContext) { val ownEntities = bindings.definedEntities .filter(_.canExport) - .map(e => (e.name, List(e.resolvedIn(module)))) - val exportedModules = bindings.resolvedExports.flatMap { - case ExportedModule(mod, Some(exportedAs), symbols) => - val isThisModule = mod.module.unsafeAsModule() == module - val exportsOnlyModule = - symbols.size == 1 && symbols.head == mod.module.getName.item - if (!isThisModule && exportsOnlyModule) { - Some((exportedAs, List(mod))) + .map(e => (e.name, e.resolvedIn(module))) + val exportedSymbols: List[(String, ResolvedName)] = bindings.resolvedExports.flatMap { + case ExportedModule(target, exportedAsOpt, symbols) => + val isThisModule = target.module.unsafeAsModule() == module + if (!isThisModule) { + exportedAsOpt match { + case Some(exportedAs) => + if (symbols.size > 1) { + throw new CompilerError(s"Renamed export with multiple targets (extension methods, conversion methods) is not viable") + } + Some((exportedAs, target)) + case None => + symbols.map { symbol => + (symbol, target) + } + } } else { None } - case _ => None - } - val reExportedSymbols = bindings.resolvedExports.flatMap { export => - export.target.exportedSymbols - .flatMap { case (sym, resolutions) => - if (export.symbols.contains(sym)) { - export.exportedAs match { - case Some(renamed) => - Some((renamed, resolutions)) - case _ => - Some((sym, resolutions)) - } - } else { - None - } - } } bindings.exportedSymbols = List( ownEntities, - exportedModules, - reExportedSymbols - ).flatten.groupBy(_._1).map { case (m, names) => - val resolvedNames = names.flatMap(_._2).distinct + exportedSymbols + ).flatten.groupBy(_._1).map { case (symbolName, duplicateResolutions) => + val resolvedNames = duplicateResolutions.map(_._2) assert( areResolvedNamesConsistent(resolvedNames), s"Resolved names are not consistent: ${resolvedNames}" ) - (m, resolvedNames) + (symbolName, resolvedNames) } } } From 1a8a3ae60d8f528e1e3e6e3ac69b937b2771b34e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 12:01:09 +0200 Subject: [PATCH 063/111] Update functions docs --- docs/syntax/functions.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/syntax/functions.md b/docs/syntax/functions.md index 27fabfca70ba..3a05d7f630a0 100644 --- a/docs/syntax/functions.md +++ b/docs/syntax/functions.md @@ -132,18 +132,26 @@ Number.floor self = case self of ... ``` -3. **As a Function with an Explicit `self` Argument:** A function defined with - the type of the `self` argument specified to be a type. +3. **As a module method:** A function defined outside the body of a type and + without explicit `self` argument, is considered a _module method_. ```ruby -floor (self : Number) = case self of - Integer -> ... +module_method x y = x + y ``` -If the user does not explicitly specify the `this` argument by name when +If the user does not explicitly specify the `self` argument by name when defining a method (e.g. they use the `Type.name` syntax), it is implicitly added to the start of the argument list. +Note that the following methods defined in the body of type and as an extension +method are equivalent: + +``` +type My_Type + method self = 42 +My_Type.method self = 42 +``` + ## Calling Functions and Methods Enso makes the distinction between functions and methods. Methods are entities From 54299f1402ebaf927bdb3b58fa78d269191884f5 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 12:20:32 +0200 Subject: [PATCH 064/111] Add docs for imports/exports with multiple targets --- docs/syntax/imports.md | 129 ++++++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/docs/syntax/imports.md b/docs/syntax/imports.md index 3c93162a42e8..0992260904a8 100644 --- a/docs/syntax/imports.md +++ b/docs/syntax/imports.md @@ -18,18 +18,23 @@ code from modules. - [Import Syntax](#import-syntax) - [Qualified Imports](#qualified-imports) - [Unqualified Imports](#unqualified-imports) +- [Imports with multiple targets](#imports-with-multiple-targets) - [Export Syntax](#export-syntax) - [Qualified Exports](#qualified-exports) - [Unqualified Exports](#unqualified-exports) - [Visibility of Export Bindings](#visibility-of-export-bindings) + - [Implicit exports](#implicit-exports) + - [Defined entities in a module](#defined-entities-in-a-module) + - [Synthetic module](#synthetic-module) +- [Exports with multiple targets](#exports-with-multiple-targets) ## Qualified Names In the following text, **entity** shall denote a module, a method (instance, -static, foreign), type, or a type constructor. In other words, an _entity_ is -anything that can be assigned to a variable. +static, extension, conversion, foreign), type, or a type constructor. In other +words, an _entity_ is anything that can be assigned to a variable. Both imports and exports require the use of qualified entity names. A qualified name consists of the library namespace (usually organization under which its @@ -95,6 +100,30 @@ Imports in Enso _may_ introduce ambiguous symbols, which is treated as a compilation error. Ideally, the error should be delayed until one of the ambiguous symbols is _used_ in Enso code. +## Imports with multiple targets + +Import of one symbol can resolve to multiple targets in case of extension or +conversion methods. For example, the following import in `Main.enso`: +`A_Module.enso`: + +``` +type My_Type +type Other_Type +My_Type.method = 42 +Other_Type.method = 42 +``` + +`Main.enso`: + +``` +import project.A_Module.method +``` + +imports both `My_Type.method` and `Other_Type.method` methods. + +Note that `import project.A_Module.My_Type.method` would lead to a compilation +error, as it is only possible to import constructors from a type, not methods. + ## Export Syntax In order to allow for easy composition and aggregation of code, Enso provides @@ -112,31 +141,19 @@ the name provided after the `as` keyword, if provided). ### Unqualified Exports -Unqualified exports are broken up into three main categories: +Unlike imports, exports cannot be used with the `all` and `hiding` keywords. So +the only supported syntax is to export a list of names with _restricted +exports_. -1. **Unrestricted Exports:** These export all symbols from the module or type - into the current scope. They consist of the keyword `from`, followed by a - qualified module name, followed by an optional rename part (using the `as` - keyword), then the keywords `export all`. For example: - ``` - from Standard.Base.Data.List as Builtin_List export all - ``` -2. **Restricted Exports:** These export a specified set of names, behaving as - though they were redefined in the current scope. They consist of the keyword - `from`, followed by a qualified module or type name (with optional - `as`-rename), then the word `export` followed by a coma-separated list of - names to be exported. For example: - ``` - from Standard.Base.Data.List export Cons, Nil, from_vector - ``` -3. **Hiding Exports:** These are the inverse of restricted exports, and export - _all_ symbols other than the named ones. They consist of the `from` keyword, - followed by a qualified module or type name (with optional `as`-rename), then - the words `export all hiding`, followed by a coma-separated list of names to - be excluded from the export. For example: - ``` - from Standard.Base.Data.List export all hiding from_vector, Nil - ``` +**Restricted Exports:** These export a specified set of names, behaving as +though they were redefined in the current scope. They consist of the keyword +`from`, followed by a qualified module or type name (with optional `as`-rename), +then the word `export` followed by a coma-separated list of names to be +exported. For example: + +``` +from Standard.Base.Data.List export Cons, Nil, from_vector +``` In essence, an export allows the user to "paste" the contents of the module or type being exported into the module declaring the export. This means that @@ -146,3 +163,63 @@ exports that create name clashes must be resolved at the _export_ site. Bindings exported from a module `X` are available in an identical fashion to bindings that are _defined_ in the module `X`. + +### Implicit exports + +The compiler inserts implicit exports for entities defined in a module and for +submodules of a _synthetic module_. A synthetic module is basically a directory +in the source structure. + +#### Defined entities in a module + +Entities defined in a module are automatically exported from the module. This +means that the following modules are semantically identical: + +``` +type My_Type +method x = x +``` + +``` +export project.Module.My_Type +export project.Module.method +type My_Type +method x = x +``` + +#### Synthetic module + +Consider a project named `Proj` with the following source structure: + +`Proj/src/Synthetic_Mod/Module.enso`: + +``` +type My_Type +``` + +`Proj/src/Main.enso`: + +``` +import project.Synthetic_Mod.Module.My_Type +``` + +We can import submodules of `Synthetic_Mod`, because the compiler automatically +inserts exports for them. Internally, `Synthetic_Mod` is represented as a module +with single export: + +``` +export project.Synthetic_Mod.Module +``` + +## Exports with multiple targets + +Export of a single symbol can be resolved to multiple targets (entities) in case +of extension or conversion methods. Similarly to +[imports with multiple targets](#imports-with-multiple-targets), the following +export in `A_Module.enso`: + +``` +export project.A_Module.export +``` + +exports both `My_Type.method` and `Other_Type.method` methods. From a65ec1bbce1fa48d4089221a610a2944da0c539e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 17:33:02 +0200 Subject: [PATCH 065/111] Remove resolvedExports field from BindingsMap. - ExportsResolution.findCycles algorithm works only with graph of modules. - ExportsResolution.Node has a module as target. - ExportSymbolAnalysis and FullyQualifiedNames use exportedSymbols field instead --- .../java/org/enso/compiler/dump/IRDumper.java | 16 --- .../pass/analyse/ExportSymbolAnalysis.java | 49 ++++--- .../pass/analyse/PrivateModuleAnalysis.java | 2 +- .../org/enso/compiler/data/BindingsMap.scala | 58 ++++---- .../pass/resolve/FullyQualifiedNames.scala | 69 +++++----- .../phase/exports/ExportsResolution.scala | 125 ++++++------------ .../enso/compiler/phase/exports/Node.scala | 4 +- .../test/semantic/ImportExportTest.scala | 21 +-- .../interpreter/caches/ImportExportCache.java | 3 - .../interpreter/runtime/IrToTruffle.scala | 15 ++- 10 files changed, 145 insertions(+), 217 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java index ae82bbd0db1b..685e27299f24 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/dump/IRDumper.java @@ -586,22 +586,6 @@ private void createPassDataGraph(IR ir) { } } } - - if (bindingsMap.resolvedExports().isEmpty()) { - bldr.addLabelLine("resolvedExports: []"); - } else { - bldr.addLabelLine("resolvedExports: "); - for (int i = 0; i < bindingsMap.resolvedExports().size(); i++) { - var resolvedExport = bindingsMap.resolvedExports().apply(i); - switch (resolvedExport.target()) { - case ResolvedType resolvedType -> bldr.addLabelLine( - " - ResolvedType(" + resolvedType.tp().name() + ")"); - case BindingsMap.ResolvedModule resolvedModule -> bldr.addLabelLine( - " - ResolvedModule(" + resolvedModule.qualifiedName() + ")"); - default -> throw unimpl(resolvedExport.target()); - } - } - } var bmNode = bldr.build(); addNode(bmNode); createEdge(ir, bindingsMap, "BindingsMap"); diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java index 4a3247f0705d..f980632e43c8 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java @@ -66,8 +66,8 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { var symbolName = exportMod.name().parts().last(); assert exportNameParts.size() > 1; var moduleOrTypeName = exportNameParts.apply(exportNameParts.size() - 2); - var foundResolvedExp = findResolvedExportForIr(export, bindingsMap); - if (foundResolvedExp == null) { + var foundResolvedNames = findResolvedExportForIr(export, bindingsMap); + if (foundResolvedNames == null) { exportErrors.add( ImportExport.apply( symbolName, @@ -81,19 +81,19 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { var exportedSymbols = exportMod.onlyNames().get(); exportedSymbols.foreach( exportedSymbol -> { - var foundSymbols = - foundResolvedExp - .target() - .findExportedSymbolsFor(exportedSymbol.name()); - if (foundSymbols.isEmpty()) { - exportErrors.add( - ImportExport.apply( - exportedSymbol, - new ImportExport.SymbolDoesNotExist( - exportedSymbol.name(), moduleOrTypeName.name()), - ImportExport.apply$default$3(), - ImportExport.apply$default$4())); - } + foundResolvedNames.foreach(resolvedName -> { + var bm = resolvedName.module().unsafeAsModule("Should be defined").getBindingsMap(); + if (!bm.exportedSymbols().contains(exportedSymbol.name())) { + exportErrors.add( + ImportExport.apply( + exportedSymbol, + new ImportExport.SymbolDoesNotExist( + exportedSymbol.name(), moduleOrTypeName.name()), + ImportExport.apply$default$3(), + ImportExport.apply$default$4())); + } + return null; + }); return null; }); } @@ -129,20 +129,17 @@ public Expression runExpression(Expression ir, InlineContext inlineContext) { * @param bindingsMap Bindings map of the module that contains the export IR * @return null if no resolved export was found, otherwise the resolved export */ - private BindingsMap.ExportedModule findResolvedExportForIr( + private scala.collection.immutable.List findResolvedExportForIr( Export exportIr, BindingsMap bindingsMap) { switch (exportIr) { case Export.Module exportedModIr -> { - var exportedModName = exportedModIr.name().name(); - var foundResolvedExp = - bindingsMap - .resolvedExports() - .find( - resolvedExport -> { - var resolvedExportName = resolvedExport.target().qualifiedName(); - return resolvedExportName.toString().equals(exportedModName); - }); - return foundResolvedExp.isEmpty() ? null : foundResolvedExp.get(); + var exportedSymbolName = exportedModIr.name().parts().last().name(); + var resolvedNamesOpt = bindingsMap.exportedSymbols().get(exportedSymbolName); + if (resolvedNamesOpt.isEmpty()) { + return null; + } else { + return resolvedNamesOpt.get(); + } } default -> throw new IllegalStateException("Unexpected value: " + exportIr); } diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java index 1ec815d1b372..3718a6549202 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java @@ -107,7 +107,7 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { .getDirectlyExportedModules() .foreach( expModule -> { - var expModuleRef = expModule.target().module().unsafeAsModule("should succeed"); + var expModuleRef = expModule.module().module().unsafeAsModule("should succeed"); if (expModuleRef.isPrivate() && !isCurrentModuleSynthetic) { var associatedExportIR = findExportIRByName(moduleIr, expModuleRef.getName()); assert associatedExportIR.isDefined(); diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 0081ca5a04fa..0f5176eb94c0 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -39,10 +39,6 @@ case class BindingsMap( */ var resolvedImports: List[ResolvedImport] = List() - /** Modules exported by [[currentModule]]. - */ - var resolvedExports: List[ExportedModule] = List() - /** Symbols exported by [[currentModule]]. */ var exportedSymbols: Map[String, List[ResolvedName]] = Map() @@ -69,7 +65,6 @@ case class BindingsMap( def toAbstract: BindingsMap = { val copy = this.copy(currentModule = currentModule.toAbstract) copy.resolvedImports = this.resolvedImports.map(_.toAbstract) - copy.resolvedExports = this.resolvedExports.map(_.toAbstract) copy.exportedSymbols = this.exportedSymbols.map { case (key, value) => key -> value.map(name => name.toAbstract) } @@ -97,17 +92,7 @@ case class BindingsMap( } } - val withExports: Option[BindingsMap] = withImports.flatMap { bindings => - val newExports = this.resolvedExports.map(_.toConcrete(moduleMap)) - if (newExports.exists(_.isEmpty)) { - None - } else { - bindings.resolvedExports = newExports.map(_.get) - Some(bindings) - } - } - - val withSymbols: Option[BindingsMap] = withExports.flatMap { bindings => + val withSymbols: Option[BindingsMap] = withImports.flatMap { bindings => val newSymbols = this.exportedSymbols.map { case (key, value) => val newValue = value.map(_.toConcrete(moduleMap)) if (newValue.exists(_.isEmpty)) { @@ -305,8 +290,12 @@ case class BindingsMap( } /** Dumps the export statements from this module into a structure ready for - * further analysis. It uses only [[resolvedImports]] field, as [[resolvedExports]] - * and [[exportedSymbols]] fields are expected to be filled later. + * further analysis. It uses only [[resolvedImports]] field, as + * [[exportedSymbols]] fields are expected to be filled later. + * + * For every symbol that is exported from this bindings map, gathers the module + * in which the symbol is defined and returns it in the list. For example, if there + * is an export `export project.Module.method`, there will be `Module` in the returned list. * * @return a list of triples of the exported module, the name it is exported * as and any further symbol restrictions. @@ -314,16 +303,33 @@ case class BindingsMap( def getDirectlyExportedModules: List[ExportedModule] = resolvedImports.collect { case ResolvedImport(_, exports, targets) => exports.flatMap { exp => - val rename = Some(exp.getSimpleName.name) + val exportAs = exp.rename match { + case Some(rename) => Some(rename.name) + case None => None + } val symbols = exp.onlyNames match { case Some(onlyNames) => onlyNames.map(_.name) case None => List(exp.name.parts.last.name) } - targets.map(ExportedModule(_, rename, symbols)) + targets.map { + case m : ResolvedModule => ExportedModule(m, exportAs, symbols) + case ResolvedType(modRef, _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + case ResolvedConstructor(ResolvedType(modRef, _), _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + case ResolvedModuleMethod(modRef, _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + case ResolvedStaticMethod(modRef, _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + case ResolvedConversionMethod(modRef, _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + } } - }.flatten + } + .flatten + .distinct } object BindingsMap { @@ -354,14 +360,14 @@ object BindingsMap { /** A representation of a resolved export statement. * - * @param target the target being exported. + * @param module the target being exported. * @param exportedAs the name it is exported as. * @param symbols List of symbols connected to the export. The symbol refers to the last part * of the physical name of the target being exported. It is not a fully qualified * name. */ case class ExportedModule( - target: ImportTarget, + module: ResolvedModule, exportedAs: Option[String], symbols: List[String] ) { @@ -381,7 +387,7 @@ object BindingsMap { * @return `this` with its module reference made abstract */ def toAbstract: ExportedModule = { - this.copy(target = target.toAbstract) + this.copy(module = module.toAbstract) } /** Convert the internal [[ModuleReference]] to a concrete reference. @@ -390,8 +396,8 @@ object BindingsMap { * @return `this` with its module reference made concrete */ def toConcrete(moduleMap: ModuleMap): Option[ExportedModule] = { - target.toConcrete(moduleMap).map { target => - this.copy(target = target) + module.toConcrete(moduleMap).map { target => + this.copy(module = target) } } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala index 1d60a44120be..1516402d1c20 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala @@ -16,7 +16,6 @@ import org.enso.compiler.core.ir.expression.warnings import org.enso.compiler.core.ir.MetadataStorage.MetadataPair import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.{ - ExportedModule, ModuleReference, Resolution, ResolvedType @@ -92,47 +91,45 @@ case object FullyQualifiedNames extends IRPass { // `Standard.Base.Error.Foo`, will always lead to name conflicts with // the exported type `Error`. if (isMainModule(moduleContext)) { - scopeMap.resolvedExports.foreach { - case ExportedModule( - resolution @ ResolvedType(exportedModuleRef, tpe), - exportedAs, - _ - ) => - val tpeName = exportedAs.getOrElse(tpe.name) - val exportedModule = exportedModuleRef.unsafeAsModule() - if ( - exportedModuleRef.getName.path.length == 2 && exportedModuleRef.getName.item == tpeName && !exportedModule.isSynthetic - ) { - val allStarting = moduleContext.pkgRepo - .map( - _.getLoadedModules.filter(m => - exportedModuleRef.getName != m.getName && m - .getName() - .toString - .startsWith(exportedModuleRef.getName.toString + ".") + scopeMap.exportedSymbols.foreach { case (symbolName, resolvedNames) => + resolvedNames.foreach { + case resolution @ ResolvedType(exportedModuleRef, _) => + val tpeName = symbolName + val exportedModule = exportedModuleRef.unsafeAsModule() + if ( + exportedModuleRef.getName.path.length == 2 && exportedModuleRef.getName.item == tpeName && !exportedModule.isSynthetic + ) { + val allStarting = moduleContext.pkgRepo + .map( + _.getLoadedModules.filter(m => + exportedModuleRef.getName != m.getName && m + .getName() + .toString + .startsWith(exportedModuleRef.getName.toString + ".") + ) ) - ) - .getOrElse(Nil) - if (allStarting.nonEmpty) { - ir.exports.foreach { export => - export match { - case m: Export.Module + .getOrElse(Nil) + if (allStarting.nonEmpty) { + ir.exports.foreach { export => + export match { + case m: Export.Module if m.name.name == resolution.qualifiedName.toString => - m.addDiagnostic( - warnings.Shadowed.TypeInModuleNameConflicts( - exportedModule.getName.toString, - tpeName, - allStarting.head.getName.toString, - m, - m.location + m.addDiagnostic( + warnings.Shadowed.TypeInModuleNameConflicts( + exportedModule.getName.toString, + tpeName, + allStarting.head.getName.toString, + m, + m.location + ) ) - ) - case _ => + case _ => + } } } } - } - case _ => + case _ => + } } } ir.copy(bindings = new_bindings) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 8d6bdd1b755b..55b3d48eb2d3 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -2,10 +2,9 @@ package org.enso.compiler.phase.exports import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.ModuleReference.Concrete -import org.enso.compiler.data.BindingsMap.{ExportedModule, ImportTarget, ResolvedConversionMethod, ResolvedModule, ResolvedName, ResolvedStaticMethod} +import org.enso.compiler.data.BindingsMap.{ExportedModule, ImportTarget, ResolvedConversionMethod, ResolvedImport, ResolvedModule, ResolvedModuleMethod, ResolvedName, ResolvedStaticMethod} import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module -import org.enso.compiler.core.CompilerError import scala.collection.mutable @@ -21,7 +20,7 @@ class ExportsResolution(private val context: CompilerContext) { private def getBindings(module: Module): BindingsMap = module.getBindingsMap() - def buildGraph(modules: List[Module]): List[Node] = { + def buildModuleGraph(modules: List[Module]): List[Node] = { val moduleTargets = modules.map(m => ResolvedModule(Concrete(m))) val nodes = mutable.Map[ImportTarget, Node]( moduleTargets.map(mod => (mod, Node(mod))): _* @@ -108,61 +107,6 @@ class ExportsResolution(private val context: CompilerContext) { result.reverse } - private def resolveExports(nodes: List[Node]): Unit = { - val exports = mutable.Map[ImportTarget, List[ExportedModule]]() - nodes.foreach { node => - val explicitlyExported = - node.exports.map(edge => - ExportedModule( - edge.exportee.target, - edge.exportsAs, - edge.symbols - ) - ) - - val transitivelyExported: List[ExportedModule] = { - explicitlyExported.flatMap { case ExportedModule(module, _, symbols) => - exports(module).map { case ExportedModule(target, _, parentSymbols) => - val exportedSymbols = symbols.intersect(parentSymbols) - ExportedModule( - target, - None, - exportedSymbols - ) - } - } - } - - val allExported = explicitlyExported ++ transitivelyExported - val unified = allExported - .groupBy(_.target) - .map { case (target, items) => - val name = items.collectFirst { case ExportedModule(_, Some(n), _) => - n - } - val allSymbols = items - .map(_.symbols) - .foldLeft(List[String]())(_ ++ _) - ExportedModule( - target, - name, - allSymbols.distinct - ) - } - .toList - exports(node.target) = unified - - } - exports.foreach { case (target, exports) => - target match { - case ResolvedModule(module) => - getBindings(module.unsafeAsModule()).resolvedExports = - exports.map(ex => ex.copy()) - case _ => - } - } - } - private def resolveExportedSymbols(modules: List[Module]): Unit = { modules.foreach { module => val bindings = getBindings(module) @@ -170,25 +114,33 @@ class ExportsResolution(private val context: CompilerContext) { bindings.definedEntities .filter(_.canExport) .map(e => (e.name, e.resolvedIn(module))) - val exportedSymbols: List[(String, ResolvedName)] = bindings.resolvedExports.flatMap { - case ExportedModule(target, exportedAsOpt, symbols) => - val isThisModule = target.module.unsafeAsModule() == module - if (!isThisModule) { - exportedAsOpt match { - case Some(exportedAs) => - if (symbols.size > 1) { - throw new CompilerError(s"Renamed export with multiple targets (extension methods, conversion methods) is not viable") - } - Some((exportedAs, target)) - case None => - symbols.map { symbol => - (symbol, target) + val exportedSymbols: List[(String, ResolvedName)] = bindings.resolvedImports.collect { + case ResolvedImport(_, exports, targets) => + exports.flatMap { export => + targets.flatMap { target => + val symbols = export.onlyNames match { + case Some(onlyNames) => + onlyNames.map(_.name) + case None => + List(export.name.parts.last.name) + } + val isThisModule = target.module.unsafeAsModule() == module + if (isThisModule) { + None + } else { + symbols.flatMap { symbol => + export.rename match { + case Some(rename) => + Some((rename.name, target)) + case None => + Some((symbol, target)) + } } + } } - } else { - None } - } + }.flatten + bindings.exportedSymbols = List( ownEntities, exportedSymbols @@ -205,7 +157,7 @@ class ExportsResolution(private val context: CompilerContext) { /** * If there are multiple resolved names for one exported symbol, they must be consistent. - * I.e., either they are all static methods, or all conversion methods. + * I.e., either they are all static (extension) and module methods, or all conversion methods. * We cannot, for example, export type and a module for one symbol - that would result * in a collision. * @return true if they are consistent, false otherwise. @@ -214,9 +166,13 @@ class ExportsResolution(private val context: CompilerContext) { resolvedNames: List[ResolvedName] ): Boolean = { if (resolvedNames.size > 1) { - val allStaticMethods = resolvedNames.forall(_.isInstanceOf[ResolvedStaticMethod]) + val allStaticOrModuleMethods = resolvedNames.forall { + case _: ResolvedStaticMethod => true + case _: ResolvedModuleMethod => true + case _ => false + } val allConversionMethods = resolvedNames.forall(_.isInstanceOf[ResolvedConversionMethod]) - allStaticMethods || allConversionMethods + allStaticOrModuleMethods || allConversionMethods } else { true } @@ -224,7 +180,7 @@ class ExportsResolution(private val context: CompilerContext) { /** Performs exports resolution on a selected set of modules. * - * The exports graph is validated and stored in the individual modules, + * The exports graph is validated and stored in the individual modules' binding maps, * allowing further use. * * The method returns a list containing the original modules, in @@ -237,17 +193,16 @@ class ExportsResolution(private val context: CompilerContext) { */ @throws[ExportCycleException] def run(modules: List[Module]): List[Module] = { - val graph = buildGraph(modules) + val graph = buildModuleGraph(modules) val cycles = findCycles(graph) if (cycles.nonEmpty) { throw ExportCycleException( - cycles.head.map(_.target.module.unsafeAsModule()) + cycles.head.map(_.module.module.unsafeAsModule()) ) } val tops = topsort(graph) - resolveExports(tops) - val topModules = tops.map(_.target) - resolveExportedSymbols(tops.map(_.target).collect { + val topModules = tops.map(_.module) + resolveExportedSymbols(tops.map(_.module).collect { case m: ResolvedModule => m.module.unsafeAsModule() }) // Take _last_ occurrence of each module @@ -258,9 +213,9 @@ class ExportsResolution(private val context: CompilerContext) { * neither performs cycle checks nor resolves exports. */ def runSort(modules: List[Module]): List[Module] = { - val graph = buildGraph(modules) + val graph = buildModuleGraph(modules) val tops = topsort(graph) - val topModules = tops.map(_.target) + val topModules = tops.map(_.module) topModules.map(_.module.unsafeAsModule()).reverse.distinct.reverse } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala index cd54bb0f0e28..9519beadc43e 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/Node.scala @@ -1,9 +1,9 @@ package org.enso.compiler.phase.exports -import org.enso.compiler.data.BindingsMap.ImportTarget +import org.enso.compiler.data.BindingsMap.ResolvedModule case class Node( - target: ImportTarget + module: ResolvedModule ) { var exports: List[Edge] = List() var exportedBy: List[Edge] = List() diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index d7196b7131c3..cbbaf3d4df2e 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -98,7 +98,7 @@ class ImportExportTest val compilerCtx = langCtx.getCompiler.context val exportsResolution = new ExportsResolution(compilerCtx) val compilerModules = modules.map(_.asCompilerModule()) - exportsResolution.buildGraph(compilerModules) + exportsResolution.buildModuleGraph(compilerModules) } before { @@ -484,10 +484,6 @@ class ImportExportTest mainModule.getIr.imports.head .asInstanceOf[Import.Module] .isSynthetic shouldBe true - - val resolvedExports = mainModule.getIr.unwrapBindingMap.resolvedExports - resolvedExports.size shouldBe 1 - resolvedExports.head.target.qualifiedName.item shouldBe "A_Type" } "(from) export type without import should insert synthetic import" in { @@ -505,9 +501,6 @@ class ImportExportTest mainModule.getIr.imports.head .asInstanceOf[Import.Module] .isSynthetic shouldBe true - - val resolvedExports = mainModule.getIr.unwrapBindingMap.resolvedExports - resolvedExports.size shouldBe 1 } "export module without import should insert synthetic import" in { @@ -525,10 +518,6 @@ class ImportExportTest mainModule.getIr.imports.head .asInstanceOf[Import.Module] .isSynthetic shouldBe true - - val resolvedExports = mainModule.getIr.unwrapBindingMap.resolvedExports - resolvedExports.size shouldBe 1 - resolvedExports.head.target.qualifiedName.item shouldBe "A_Module" } "export unknown type without import should result in error" in { @@ -1381,7 +1370,7 @@ class ImportExportTest graph.size shouldBe 2 } val aModNode = graph.find(node => - node.target match { + node.module match { case BindingsMap.ResolvedModule(modRef) => modRef.getName.item == "A_Module" case _ => false @@ -1391,9 +1380,9 @@ class ImportExportTest val aModNodeExporter = aModNode.get.exportedBy.head.exporter withClue("A_Module should be exported by B_Module") { - aModNodeExporter.target + aModNodeExporter.module .isInstanceOf[BindingsMap.ResolvedModule] shouldBe true - aModNodeExporter.target + aModNodeExporter.module .asInstanceOf[BindingsMap.ResolvedModule] .qualifiedName .item shouldBe "B_Module" @@ -1490,7 +1479,7 @@ class ImportExportTest graph.size shouldBe 4 } val trueNode = graph.find(node => { - node.target match { + node.module match { case ResolvedConstructor(_, cons) if cons.name == "True" => true case _ => false diff --git a/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java b/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java index 1ee2cf7f0333..adeeffa55b66 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java @@ -226,7 +226,6 @@ protected void writeObject(BindingsMap obj, Output out) throws IOException { out.writeObject(obj.definedEntities()); out.writeObject(obj.currentModule()); out.writeInline(scala.collection.immutable.List.class, obj.resolvedImports()); - out.writeInline(scala.collection.immutable.List.class, obj.resolvedExports()); out.writeInline(scala.collection.immutable.Map.class, obj.exportedSymbols()); } @@ -236,11 +235,9 @@ protected BindingsMap readObject(Input in) throws IOException, ClassNotFoundExce var de = (scala.collection.immutable.List) in.readObject(); var cm = (ModuleReference) in.readObject(); var imp = in.readInline(scala.collection.immutable.List.class); - var exp = in.readInline(scala.collection.immutable.List.class); var sym = in.readInline(scala.collection.immutable.Map.class); var map = new BindingsMap(de, cm); map.resolvedImports_$eq(imp); - map.resolvedExports_$eq(exp); map.exportedSymbols_$eq(sym); return map; } diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index b9ff4638b4f7..a9ab742c47d1 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -42,7 +42,6 @@ import org.enso.compiler.core.ir.expression.{ Section } import org.enso.compiler.data.BindingsMap.{ - ExportedModule, ResolvedConstructor, ResolvedModule } @@ -197,11 +196,15 @@ class IrToTruffle( "No binding analysis at the point of codegen." ) - bindingsMap.resolvedExports - .collect { case ExportedModule(ResolvedModule(module), _, _) => module } - .foreach(exp => - scopeBuilder.addExport(new ImportExportScope(exp.unsafeAsModule())) - ) + bindingsMap.exportedSymbols.foreach { case (_, resolvedNames) => + val resolvedModules = resolvedNames.collect { + case ResolvedModule(module) => module + } + resolvedModules.foreach { resolvedModule => + scopeBuilder.addExport(new ImportExportScope(resolvedModule.unsafeAsModule())) + } + } + val importDefs = module.imports val methodDefs = module.bindings.collect { case method: definition.Method.Explicit => method From 9538ae1c2e9a3429ac523714c9158f786ffab7c1 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 17:54:58 +0200 Subject: [PATCH 066/111] ExportSymbolAnalysis iterates only some exports --- .../pass/analyse/ExportSymbolAnalysis.java | 86 ++++++++++--------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java index f980632e43c8..8908e39cdbe7 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java @@ -17,6 +17,9 @@ /** * This pass ensures that all the symbols that are exported exist. If not, an IR error is generated. + * Iterates only exports that are not renamed and contain multiple symbols. For example: + * {@code from project.Module export Symbol_1, Symbol_2 }. + * All the other types of exports are assumed to already be resolved prior to this pass. */ public final class ExportSymbolAnalysis implements IRPass { public static final ExportSymbolAnalysis INSTANCE = new ExportSymbolAnalysis(); @@ -59,49 +62,48 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { moduleIr .exports() .foreach( - export -> - switch (export) { - case Export.Module exportMod -> { - var exportNameParts = exportMod.name().parts(); - var symbolName = exportMod.name().parts().last(); - assert exportNameParts.size() > 1; - var moduleOrTypeName = exportNameParts.apply(exportNameParts.size() - 2); - var foundResolvedNames = findResolvedExportForIr(export, bindingsMap); - if (foundResolvedNames == null) { - exportErrors.add( - ImportExport.apply( - symbolName, - new ImportExport.SymbolDoesNotExist( - symbolName.name(), moduleOrTypeName.name()), - ImportExport.apply$default$3(), - ImportExport.apply$default$4())); - } else { - if (exportMod.onlyNames().isDefined()) { - assert exportMod.onlyNames().isDefined(); - var exportedSymbols = exportMod.onlyNames().get(); - exportedSymbols.foreach( - exportedSymbol -> { - foundResolvedNames.foreach(resolvedName -> { - var bm = resolvedName.module().unsafeAsModule("Should be defined").getBindingsMap(); - if (!bm.exportedSymbols().contains(exportedSymbol.name())) { - exportErrors.add( - ImportExport.apply( - exportedSymbol, - new ImportExport.SymbolDoesNotExist( - exportedSymbol.name(), moduleOrTypeName.name()), - ImportExport.apply$default$3(), - ImportExport.apply$default$4())); - } - return null; - }); - return null; - }); - } - } - yield null; + export -> { + if (export instanceof Export.Module exportIr && + exportIr.onlyNames().isDefined() && + exportIr.rename().isEmpty()) { + var exportNameParts = exportIr.name().parts(); + var symbolName = exportNameParts.last().name(); + assert exportNameParts.size() > 1; + var moduleOrTypeName = exportNameParts.apply(exportNameParts.size() - 2); + var exportedSymbols = exportIr.onlyNames().get(); + var resolvedTargetsOpt = bindingsMap.exportedSymbols().get(symbolName); + if (resolvedTargetsOpt.isEmpty()) { + var err = ImportExport.apply( + exportIr, + new ImportExport.SymbolDoesNotExist( + symbolName, moduleOrTypeName.name()), + ImportExport.apply$default$3(), + ImportExport.apply$default$4()); + exportErrors.add(err); + return null; } - default -> export; - }); + exportedSymbols.foreach( + exportedSymbol -> { + resolvedTargetsOpt.get().foreach(resolvedTarget -> { + var bm = resolvedTarget.module().unsafeAsModule("Should be defined") + .getBindingsMap(); + if (!bm.exportedSymbols().contains(exportedSymbol.name())) { + exportErrors.add( + ImportExport.apply( + exportedSymbol, + new ImportExport.SymbolDoesNotExist( + exportedSymbol.name(), moduleOrTypeName.name()), + ImportExport.apply$default$3(), + ImportExport.apply$default$4())); + } + return null; + }); + return null; + }); + } + return null; + } + ); if (exportErrors.isEmpty()) { return moduleIr; From c6ddaf522458e4306cce5a823341b669dcb76c1f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 17:59:51 +0200 Subject: [PATCH 067/111] Fix ExportExtensionMethodTests --- .../test/exports/ExportExtensionMethodTest.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportExtensionMethodTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportExtensionMethodTest.java index f795d0f4056f..094f4a7b09e3 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportExtensionMethodTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportExtensionMethodTest.java @@ -1,6 +1,7 @@ package org.enso.interpreter.test.exports; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; @@ -36,7 +37,8 @@ public void extensionMethodCanBeExportedByName() throws IOException { new SourceModule( QualifiedName.fromString("A_Module"), """ - from project.T_Module export My_Type, extension_method + export project.T_Module.My_Type + export project.T_Module.extension_method """); var mainMod = new SourceModule( @@ -74,7 +76,9 @@ public void multipleExtensionMethodsCanBeExportedByName() throws IOException { new SourceModule( QualifiedName.fromString("A_Module"), """ - from project.T_Module export My_Type, My_Other_Type, extension_method + export project.T_Module.My_Type + export project.T_Module.My_Other_Type + export project.T_Module.extension_method """); var mainMod = new SourceModule( @@ -110,7 +114,8 @@ public void extensionMethodIsInBindingMap() throws IOException { new SourceModule( QualifiedName.fromString("Main"), """ - from project.T_Module export My_Type, extension_method + export project.T_Module.My_Type + export project.T_Module.extension_method """); var projDir = tempFolder.newFolder().toPath(); ProjectUtils.createProject("Proj", Set.of(tMod, mainMod), projDir); @@ -121,7 +126,8 @@ public void extensionMethodIsInBindingMap() throws IOException { var polyCtx = new PolyglotContext(ctx); polyCtx.getTopScope().compile(true); var mainModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); - assertThat(mainModExportedSymbols, hasKey("extension_method")); + assertThat(mainModExportedSymbols.size(), is(2)); + assertThat(mainModExportedSymbols.keySet(), containsInAnyOrder("My_Type", "extension_method")); } } From 3e9cbeb6f0bcf972f7888a659c129f392ef7e2a0 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 19:04:21 +0200 Subject: [PATCH 068/111] Fix ExportSymbolAnalysis to correctly resolve module from the export --- .../pass/analyse/ExportSymbolAnalysis.java | 103 ++++++++++++------ 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java index 8908e39cdbe7..cfb92983e825 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java @@ -5,12 +5,16 @@ import java.util.UUID; import org.enso.compiler.context.InlineContext; import org.enso.compiler.context.ModuleContext; +import org.enso.compiler.core.CompilerError; import org.enso.compiler.core.IR; import org.enso.compiler.core.ir.Expression; import org.enso.compiler.core.ir.Module; import org.enso.compiler.core.ir.expression.errors.ImportExport; import org.enso.compiler.core.ir.module.scope.Export; import org.enso.compiler.data.BindingsMap; +import org.enso.compiler.data.BindingsMap.ImportTarget; +import org.enso.compiler.data.BindingsMap.ResolvedModule; +import org.enso.compiler.data.BindingsMap.ResolvedType; import org.enso.compiler.pass.IRPass; import scala.collection.immutable.Seq; import scala.jdk.javaapi.CollectionConverters; @@ -67,39 +71,60 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { exportIr.onlyNames().isDefined() && exportIr.rename().isEmpty()) { var exportNameParts = exportIr.name().parts(); - var symbolName = exportNameParts.last().name(); + var lastName = exportNameParts.last().name(); assert exportNameParts.size() > 1; - var moduleOrTypeName = exportNameParts.apply(exportNameParts.size() - 2); - var exportedSymbols = exportIr.onlyNames().get(); - var resolvedTargetsOpt = bindingsMap.exportedSymbols().get(symbolName); - if (resolvedTargetsOpt.isEmpty()) { - var err = ImportExport.apply( - exportIr, - new ImportExport.SymbolDoesNotExist( - symbolName, moduleOrTypeName.name()), - ImportExport.apply$default$3(), - ImportExport.apply$default$4()); - exportErrors.add(err); - return null; + var preLastName = exportNameParts.apply(exportNameParts.size() - 2).name(); + + // importTarget is the entity which we will check for the presence of symbols. + // It can either be ResolvedModule or ResolvedType. + ImportTarget importTarget = null; + // In correctly formed export, either `lastName` or `preLastName` must resolve + // to a module + var resolvedModule = findInDirectlyExportedModules(lastName, bindingsMap); + if (resolvedModule != null) { + importTarget = resolvedModule; + } else { + resolvedModule = findInDirectlyExportedModules(preLastName, bindingsMap); + if (resolvedModule == null) { + var err = ImportExport.apply( + exportIr, + new ImportExport.ModuleDoesNotExist(preLastName), + ImportExport.apply$default$3(), + ImportExport.apply$default$4()); + exportErrors.add(err); + return null; + } + var resolvedTypeOpt = resolvedModule.resolveExportedSymbol(lastName); + if (resolvedTypeOpt.isLeft()) { + var err = ImportExport.apply( + exportIr, + new ImportExport.TypeDoesNotExist( + lastName, preLastName), + ImportExport.apply$default$3(), + ImportExport.apply$default$4()); + exportErrors.add(err); + return null; + } + var resolvedType = resolvedTypeOpt.toOption().get(); + if (resolvedType.size() > 1 || !(resolvedType.head() instanceof ResolvedType)) { + throw new CompilerError("Multiple resolved targets for a symbol, expected resolved type: " + resolvedType); + } + importTarget = (ImportTarget) resolvedType.head(); + } + assert importTarget != null; + + for (var exportedSymbol : CollectionConverters.asJava(exportIr.onlyNames().get())) { + var res = importTarget.resolveExportedSymbol(exportedSymbol.name()); + if (res.isLeft()) { + var err = ImportExport.apply( + exportedSymbol, + new ImportExport.SymbolDoesNotExist( + exportedSymbol.name(), importTarget.qualifiedName().toString()), + ImportExport.apply$default$3(), + ImportExport.apply$default$4()); + exportErrors.add(err); + } } - exportedSymbols.foreach( - exportedSymbol -> { - resolvedTargetsOpt.get().foreach(resolvedTarget -> { - var bm = resolvedTarget.module().unsafeAsModule("Should be defined") - .getBindingsMap(); - if (!bm.exportedSymbols().contains(exportedSymbol.name())) { - exportErrors.add( - ImportExport.apply( - exportedSymbol, - new ImportExport.SymbolDoesNotExist( - exportedSymbol.name(), moduleOrTypeName.name()), - ImportExport.apply$default$3(), - ImportExport.apply$default$4())); - } - return null; - }); - return null; - }); } return null; } @@ -119,6 +144,22 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { } } + /** + * Tries to find module with {@code name} name in modules that are _directly exported_ from + * the current module. + * @return null if not found. + */ + private ResolvedModule findInDirectlyExportedModules(String name, BindingsMap bindingsMap) { + assert !name.contains(".") : "Name must not be FQN"; + for (var exportedMod : CollectionConverters.asJava(bindingsMap.getDirectlyExportedModules())) { + var exportedModName = exportedMod.module().qualifiedName().item(); + if (name.equals(exportedModName)) { + return exportedMod.module(); + } + } + return null; + } + @Override public Expression runExpression(Expression ir, InlineContext inlineContext) { return ir; From 3f864e469676b46badcca2fb255c84bbcdace02e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 4 Jul 2024 19:08:05 +0200 Subject: [PATCH 069/111] Small refactoring --- .../pass/analyse/ExportSymbolAnalysis.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java index cfb92983e825..54a71ae9176c 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java @@ -7,7 +7,10 @@ import org.enso.compiler.context.ModuleContext; import org.enso.compiler.core.CompilerError; import org.enso.compiler.core.IR; +import org.enso.compiler.core.ir.Diagnostic; +import org.enso.compiler.core.ir.DiagnosticStorage; import org.enso.compiler.core.ir.Expression; +import org.enso.compiler.core.ir.MetadataStorage; import org.enso.compiler.core.ir.Module; import org.enso.compiler.core.ir.expression.errors.ImportExport; import org.enso.compiler.core.ir.module.scope.Export; @@ -17,6 +20,7 @@ import org.enso.compiler.data.BindingsMap.ResolvedType; import org.enso.compiler.pass.IRPass; import scala.collection.immutable.Seq; +import scala.collection.immutable.Seq$; import scala.jdk.javaapi.CollectionConverters; /** @@ -89,8 +93,8 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { var err = ImportExport.apply( exportIr, new ImportExport.ModuleDoesNotExist(preLastName), - ImportExport.apply$default$3(), - ImportExport.apply$default$4()); + emptyPassData(), + emptyDiagnostics()); exportErrors.add(err); return null; } @@ -100,8 +104,8 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { exportIr, new ImportExport.TypeDoesNotExist( lastName, preLastName), - ImportExport.apply$default$3(), - ImportExport.apply$default$4()); + emptyPassData(), + emptyDiagnostics()); exportErrors.add(err); return null; } @@ -120,8 +124,8 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { exportedSymbol, new ImportExport.SymbolDoesNotExist( exportedSymbol.name(), importTarget.qualifiedName().toString()), - ImportExport.apply$default$3(), - ImportExport.apply$default$4()); + emptyPassData(), + emptyDiagnostics()); exportErrors.add(err); } } @@ -192,4 +196,14 @@ private scala.collection.immutable.List findResolvedEx public T updateMetadataInDuplicate(T sourceIr, T copyOfIr) { return IRPass.super.updateMetadataInDuplicate(sourceIr, copyOfIr); } + + private static MetadataStorage emptyPassData() { + return new MetadataStorage(); + } + + @SuppressWarnings("unchecked") + private static DiagnosticStorage emptyDiagnostics() { + return DiagnosticStorage.apply((Seq) Seq$.MODULE$.empty()); + } + } From 0916bdeb626ee34201976e5ea6c774f8ecfdb451 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 13:24:24 +0200 Subject: [PATCH 070/111] ExportSymbolAnalysis is not a compiler pass. --- .../pass/analyse/ExportSymbolAnalysis.java | 209 ---------------- .../scala/org/enso/compiler/Compiler.scala | 15 +- .../main/scala/org/enso/compiler/Passes.scala | 1 - .../phase/exports/ExportSymbolAnalysis.java | 229 ++++++++++++++++++ .../org/enso/compiler/test/PassesTest.scala | 2 - 5 files changed, 242 insertions(+), 214 deletions(-) delete mode 100644 engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java create mode 100644 engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java deleted file mode 100644 index 54a71ae9176c..000000000000 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/ExportSymbolAnalysis.java +++ /dev/null @@ -1,209 +0,0 @@ -package org.enso.compiler.pass.analyse; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import org.enso.compiler.context.InlineContext; -import org.enso.compiler.context.ModuleContext; -import org.enso.compiler.core.CompilerError; -import org.enso.compiler.core.IR; -import org.enso.compiler.core.ir.Diagnostic; -import org.enso.compiler.core.ir.DiagnosticStorage; -import org.enso.compiler.core.ir.Expression; -import org.enso.compiler.core.ir.MetadataStorage; -import org.enso.compiler.core.ir.Module; -import org.enso.compiler.core.ir.expression.errors.ImportExport; -import org.enso.compiler.core.ir.module.scope.Export; -import org.enso.compiler.data.BindingsMap; -import org.enso.compiler.data.BindingsMap.ImportTarget; -import org.enso.compiler.data.BindingsMap.ResolvedModule; -import org.enso.compiler.data.BindingsMap.ResolvedType; -import org.enso.compiler.pass.IRPass; -import scala.collection.immutable.Seq; -import scala.collection.immutable.Seq$; -import scala.jdk.javaapi.CollectionConverters; - -/** - * This pass ensures that all the symbols that are exported exist. If not, an IR error is generated. - * Iterates only exports that are not renamed and contain multiple symbols. For example: - * {@code from project.Module export Symbol_1, Symbol_2 }. - * All the other types of exports are assumed to already be resolved prior to this pass. - */ -public final class ExportSymbolAnalysis implements IRPass { - public static final ExportSymbolAnalysis INSTANCE = new ExportSymbolAnalysis(); - private static scala.collection.immutable.List precursorPasses; - private UUID uuid; - - private ExportSymbolAnalysis() {} - - @Override - public UUID key() { - return null; - } - - @Override - public void org$enso$compiler$pass$IRPass$_setter_$key_$eq(UUID v) { - this.uuid = v; - } - - @Override - public Seq precursorPasses() { - if (precursorPasses == null) { - List passes = List.of(BindingAnalysis$.MODULE$, ImportSymbolAnalysis$.MODULE$); - precursorPasses = CollectionConverters.asScala(passes).toList(); - } - return precursorPasses; - } - - @Override - @SuppressWarnings("unchecked") - public Seq invalidatedPasses() { - Object obj = scala.collection.immutable.Nil$.MODULE$; - return (scala.collection.immutable.List) obj; - } - - @Override - public Module runModule(Module moduleIr, ModuleContext moduleContext) { - List exportErrors = new ArrayList<>(); - var bindingsMap = (BindingsMap) moduleIr.passData().get(BindingAnalysis$.MODULE$).get(); - - moduleIr - .exports() - .foreach( - export -> { - if (export instanceof Export.Module exportIr && - exportIr.onlyNames().isDefined() && - exportIr.rename().isEmpty()) { - var exportNameParts = exportIr.name().parts(); - var lastName = exportNameParts.last().name(); - assert exportNameParts.size() > 1; - var preLastName = exportNameParts.apply(exportNameParts.size() - 2).name(); - - // importTarget is the entity which we will check for the presence of symbols. - // It can either be ResolvedModule or ResolvedType. - ImportTarget importTarget = null; - // In correctly formed export, either `lastName` or `preLastName` must resolve - // to a module - var resolvedModule = findInDirectlyExportedModules(lastName, bindingsMap); - if (resolvedModule != null) { - importTarget = resolvedModule; - } else { - resolvedModule = findInDirectlyExportedModules(preLastName, bindingsMap); - if (resolvedModule == null) { - var err = ImportExport.apply( - exportIr, - new ImportExport.ModuleDoesNotExist(preLastName), - emptyPassData(), - emptyDiagnostics()); - exportErrors.add(err); - return null; - } - var resolvedTypeOpt = resolvedModule.resolveExportedSymbol(lastName); - if (resolvedTypeOpt.isLeft()) { - var err = ImportExport.apply( - exportIr, - new ImportExport.TypeDoesNotExist( - lastName, preLastName), - emptyPassData(), - emptyDiagnostics()); - exportErrors.add(err); - return null; - } - var resolvedType = resolvedTypeOpt.toOption().get(); - if (resolvedType.size() > 1 || !(resolvedType.head() instanceof ResolvedType)) { - throw new CompilerError("Multiple resolved targets for a symbol, expected resolved type: " + resolvedType); - } - importTarget = (ImportTarget) resolvedType.head(); - } - assert importTarget != null; - - for (var exportedSymbol : CollectionConverters.asJava(exportIr.onlyNames().get())) { - var res = importTarget.resolveExportedSymbol(exportedSymbol.name()); - if (res.isLeft()) { - var err = ImportExport.apply( - exportedSymbol, - new ImportExport.SymbolDoesNotExist( - exportedSymbol.name(), importTarget.qualifiedName().toString()), - emptyPassData(), - emptyDiagnostics()); - exportErrors.add(err); - } - } - } - return null; - } - ); - - if (exportErrors.isEmpty()) { - return moduleIr; - } else { - return moduleIr.copy( - moduleIr.imports(), - CollectionConverters.asScala(exportErrors).toList(), - moduleIr.bindings(), - moduleIr.location(), - moduleIr.passData(), - moduleIr.diagnostics(), - moduleIr.id()); - } - } - - /** - * Tries to find module with {@code name} name in modules that are _directly exported_ from - * the current module. - * @return null if not found. - */ - private ResolvedModule findInDirectlyExportedModules(String name, BindingsMap bindingsMap) { - assert !name.contains(".") : "Name must not be FQN"; - for (var exportedMod : CollectionConverters.asJava(bindingsMap.getDirectlyExportedModules())) { - var exportedModName = exportedMod.module().qualifiedName().item(); - if (name.equals(exportedModName)) { - return exportedMod.module(); - } - } - return null; - } - - @Override - public Expression runExpression(Expression ir, InlineContext inlineContext) { - return ir; - } - - /** - * Finds a resolved export that corresponds to the export IR. - * - * @param exportIr Export IR that is being resolved - * @param bindingsMap Bindings map of the module that contains the export IR - * @return null if no resolved export was found, otherwise the resolved export - */ - private scala.collection.immutable.List findResolvedExportForIr( - Export exportIr, BindingsMap bindingsMap) { - switch (exportIr) { - case Export.Module exportedModIr -> { - var exportedSymbolName = exportedModIr.name().parts().last().name(); - var resolvedNamesOpt = bindingsMap.exportedSymbols().get(exportedSymbolName); - if (resolvedNamesOpt.isEmpty()) { - return null; - } else { - return resolvedNamesOpt.get(); - } - } - default -> throw new IllegalStateException("Unexpected value: " + exportIr); - } - } - - @Override - public T updateMetadataInDuplicate(T sourceIr, T copyOfIr) { - return IRPass.super.updateMetadataInDuplicate(sourceIr, copyOfIr); - } - - private static MetadataStorage emptyPassData() { - return new MetadataStorage(); - } - - @SuppressWarnings("unchecked") - private static DiagnosticStorage emptyDiagnostics() { - return DiagnosticStorage.apply((Seq) Seq$.MODULE$.empty()); - } - -} diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala index b4372bae5ab5..279f6dc69316 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala @@ -29,7 +29,7 @@ import org.enso.compiler.phase.{ImportResolver, ImportResolverAlgorithm} import org.enso.editions.LibraryName import org.enso.pkg.QualifiedName import org.enso.common.CompilationStage -import org.enso.compiler.phase.exports.{ExportCycleException, ExportsResolution} +import org.enso.compiler.phase.exports.{ExportCycleException, ExportSymbolAnalysis, ExportsResolution} import org.enso.syntax2.Tree import java.io.PrintStream @@ -475,7 +475,18 @@ class Compiler( // the symbol brought to the scope has not been properly resolved yet. val sortedCachedModules = new ExportsResolution(context).runSort(modulesImportedWithCachedBindings) - sortedCachedModules ++ requiredModules + val allSortedModules = sortedCachedModules ++ requiredModules + allSortedModules.foreach { mod => + val newModIr = + ExportSymbolAnalysis.analyseModule(mod.getIr, packageRepository) + context.updateModule( + mod, + updater => { + updater.ir(newModIr) + } + ) + } + allSortedModules } private def ensureParsedAndAnalyzed(module: Module): Unit = { diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala index 38492bedccd0..39a098b243f3 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala @@ -56,7 +56,6 @@ class Passes( ) } else List()) ++ List( - ExportSymbolAnalysis.INSTANCE, ShadowedPatternFields, UnreachableMatchBranches, NestedPatternMatch, diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java new file mode 100644 index 000000000000..6ab82b7378a3 --- /dev/null +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java @@ -0,0 +1,229 @@ +package org.enso.compiler.phase.exports; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.enso.compiler.PackageRepository; +import org.enso.compiler.context.CompilerContext; +import org.enso.compiler.core.ir.Diagnostic; +import org.enso.compiler.core.ir.DiagnosticStorage; +import org.enso.compiler.core.ir.MetadataStorage; +import org.enso.compiler.core.ir.Module; +import org.enso.compiler.core.ir.Name; +import org.enso.compiler.core.ir.expression.errors.ImportExport; +import org.enso.compiler.core.ir.module.scope.Export; +import org.enso.compiler.data.BindingsMap.ResolvedType; +import org.enso.compiler.pass.IRPass; +import scala.collection.immutable.Seq; +import scala.collection.immutable.Seq$; +import scala.jdk.javaapi.CollectionConverters; + +/** + * Ensures that all the symbols that are exported exist. If not, an IR error is generated. It is not + * a {@link IRPass compiler pass} because it needs access to {@link + * org.enso.compiler.PackageRepository}. Ignores renamed exports. + */ +public final class ExportSymbolAnalysis { + private ExportSymbolAnalysis() {} + + /** + * For all the exports in the given IR of the module, ensures that all the symbols that are + * exported exists. If not, the corresponding IR is replaced with an {@link ImportExport error + * IR}. + * + * @param moduleIr IR of the module to be analyzed for existance of exported symbols. + * @return A copy of the given {@code moduleIr} optinally with some export IRs replaced with + * corresponding {@link ImportExport errors}. + */ + public static Module analyseModule(Module moduleIr, PackageRepository packageRepository) { + List exportErrors = new ArrayList<>(); + moduleIr + .exports() + .foreach( + export -> { + if (export instanceof Export.Module exportIr && exportIr.rename().isEmpty()) { + var exportNameParts = CollectionConverters.asJava(exportIr.name().parts()); + assert exportNameParts.size() > 2 + : "All the exports should already be desugared in the module discovery compiler" + + " passes"; + + String lastNameFQN; + String lastNameItem; + String preLastNameFQN; + List symbols; + if (exportIr.onlyNames().isDefined()) { + lastNameItem = exportNameParts.get(exportNameParts.size() - 1).name(); + lastNameFQN = + exportNameParts.stream().map(Name::name).collect(Collectors.joining(".")); + preLastNameFQN = + exportNameParts.stream() + .map(Name::name) + .limit(exportNameParts.size() - 2) + .collect(Collectors.joining(".")); + symbols = + CollectionConverters.asJava(exportIr.onlyNames().get()).stream() + .map(Name.class::cast) + .toList(); + } else { + lastNameItem = exportNameParts.get(exportNameParts.size() - 2).name(); + lastNameFQN = + exportNameParts.stream() + .map(Name::name) + .limit(exportNameParts.size() - 2) + .collect(Collectors.joining(".")); + preLastNameFQN = + exportNameParts.stream() + .map(Name::name) + .limit(exportNameParts.size() - 3) + .collect(Collectors.joining(".")); + symbols = List.of(exportNameParts.get(exportNameParts.size() - 1)); + } + + var mod = getLoadedModule(lastNameFQN, packageRepository); + if (mod != null) { + var errs = analyseSymbolsFromModule(mod, symbols); + exportErrors.addAll(errs); + return null; + } + mod = getLoadedModule(preLastNameFQN, packageRepository); + if (mod == null) { + var err = createModuleDoesNotExistError(exportIr, preLastNameFQN); + exportErrors.add(err); + return null; + } + // lastNameFQN must be type in module `mod` + var modFQN = preLastNameFQN; + var typeName = lastNameItem; + var resolvedType = getResolvedTypeFromModule(mod, typeName); + if (resolvedType == null) { + var err = createTypeDoesNotExistError(exportIr, modFQN, typeName); + exportErrors.add(err); + return null; + } + var errs = analyseSymbolsFromType(resolvedType, symbols); + exportErrors.addAll(errs); + } + return null; + }); + + if (exportErrors.isEmpty()) { + return moduleIr; + } else { + return moduleIr.copy( + moduleIr.imports(), + CollectionConverters.asScala(exportErrors).toList(), + moduleIr.bindings(), + moduleIr.location(), + moduleIr.passData(), + moduleIr.diagnostics(), + moduleIr.id()); + } + } + + private static CompilerContext.Module getLoadedModule(String modFQN, PackageRepository pkgRepo) { + assert modFQN.contains(".") : "modFQN is a FQN"; + var modOpt = pkgRepo.getLoadedModule(modFQN); + if (modOpt.isDefined()) { + return modOpt.get(); + } else { + return null; + } + } + + private static ResolvedType getResolvedTypeFromModule( + CompilerContext.Module module, String typeName) { + assert !typeName.contains(".") : "Not a FQN"; + var typeOpt = module.getBindingsMap().exportedSymbols().get(typeName); + if (typeOpt.isEmpty()) { + return null; + } + var resolvedNames = typeOpt.get(); + if (resolvedNames.size() > 1) { + // We expect a single ResolvedType target + return null; + } + if (resolvedNames.head() instanceof ResolvedType resolvedType) { + return resolvedType; + } + return null; + } + + /** + * @return Optionally empty list of errors. Not null. + */ + private static List analyseSymbolsFromModule( + CompilerContext.Module module, List symbols) { + var errors = new ArrayList(); + for (var symbol : symbols) { + var resolvedNamesOpt = module.getBindingsMap().exportedSymbols().get(symbol.name()); + if (resolvedNamesOpt.isEmpty()) { + errors.add( + createSymbolDoesNotExistError(symbol, symbol.name(), module.getName().toString())); + } + } + return errors; + } + + /** + * @return Optionally empty list of errors. Not null. + */ + private static List analyseSymbolsFromType(ResolvedType type, List symbols) { + var errors = new ArrayList(); + var constructors = CollectionConverters.asJava(type.tp().members()); + for (var symbol : symbols) { + var symbolIsConstructor = + constructors.stream().anyMatch(cons -> cons.name().equals(symbol.name())); + if (!symbolIsConstructor) { + errors.add(createNoSuchConstructorError(symbol, type.tp().name(), symbol.name())); + } + } + return errors; + } + + private static ImportExport createModuleDoesNotExistError(Export.Module exportIr, String modFQN) { + assert modFQN.contains("."); + return ImportExport.apply( + exportIr, new ImportExport.ModuleDoesNotExist(modFQN), emptyPassData(), emptyDiagnostics()); + } + + private static ImportExport createSymbolDoesNotExistError( + Name symbolIr, String symbolName, String modFQN) { + assert modFQN.contains("."); + assert !symbolName.contains("."); + return ImportExport.apply( + symbolIr, + new ImportExport.SymbolDoesNotExist(symbolName, modFQN), + emptyPassData(), + emptyDiagnostics()); + } + + private static ImportExport createNoSuchConstructorError( + Name symbolIr, String typeName, String consName) { + assert !consName.contains("."); + return ImportExport.apply( + symbolIr, + new ImportExport.NoSuchConstructor(typeName, consName), + emptyPassData(), + emptyDiagnostics()); + } + + private static ImportExport createTypeDoesNotExistError( + Export.Module exportIr, String modFQN, String typeName) { + assert modFQN.contains("."); + assert !typeName.contains("."); + return ImportExport.apply( + exportIr, + new ImportExport.TypeDoesNotExist(typeName, modFQN), + emptyPassData(), + emptyDiagnostics()); + } + + private static MetadataStorage emptyPassData() { + return new MetadataStorage(); + } + + @SuppressWarnings("unchecked") + private static DiagnosticStorage emptyDiagnostics() { + return DiagnosticStorage.apply((Seq) Seq$.MODULE$.empty()); + } +} diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/PassesTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/PassesTest.scala index fa35214170d0..2f25f41bdd90 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/PassesTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/PassesTest.scala @@ -9,7 +9,6 @@ import org.enso.compiler.pass.analyse.{ AliasAnalysis, AmbiguousImportsAnalysis, BindingAnalysis, - ExportSymbolAnalysis, ImportSymbolAnalysis, PrivateConstructorAnalysis, PrivateModuleAnalysis @@ -65,7 +64,6 @@ class PassesTest extends CompilerTest { AmbiguousImportsAnalysis, PrivateModuleAnalysis.INSTANCE, PrivateConstructorAnalysis.INSTANCE, - ExportSymbolAnalysis.INSTANCE, ShadowedPatternFields, UnreachableMatchBranches, NestedPatternMatch, From 03de7c630ea63cc023f2d8c8b3022cb9bf1f4d05 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 13:28:34 +0200 Subject: [PATCH 071/111] fmt --- .../pass/analyse/PrivateModuleAnalysis.java | 33 +++--- .../phase/ImportResolverAlgorithm.java | 38 ++++--- .../scala/org/enso/compiler/Compiler.scala | 24 +---- .../org/enso/compiler/data/BindingsMap.scala | 78 +++++++------- .../pass/analyse/ImportSymbolAnalysis.scala | 6 +- .../pass/resolve/FullyQualifiedNames.scala | 4 +- .../enso/compiler/phase/ImportResolver.scala | 9 +- .../phase/exports/ExportsResolution.scala | 102 ++++++++++-------- .../enso/compiler/ExportedSymbolsTest.java | 54 +++++++--- .../exports/ExportConversionMethodTest.java | 42 +++++--- .../exports/ExportExtensionMethodTest.java | 4 +- .../test/exports/ExportStaticMethodTest.java | 1 - .../test/pass/desugar/ImportsTest.scala | 2 +- .../test/semantic/ImportExportTest.scala | 24 ++--- .../callable/InvokeMethodImportResolver.java | 19 +++- .../interpreter/runtime/IrToTruffle.scala | 17 ++- 16 files changed, 253 insertions(+), 204 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java index 3718a6549202..644b05cde572 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PrivateModuleAnalysis.java @@ -73,22 +73,23 @@ public Module runModule(Module moduleIr, ModuleContext moduleContext) { .foreach( resolvedImp -> { var importedTargets = resolvedImp.targets(); - importedTargets.foreach(importedTarget -> { - var importedModule = importedTarget.module().unsafeAsModule("should succeed"); - var importedModuleName = importedModule.getName().toString(); - var importedModulePackage = importedModule.getPackage(); - if (currentPackage != null - && !currentPackage.equals(importedModulePackage) - && importedModule.isPrivate()) { - importErrors.add( - ImportExport.apply( - resolvedImp.importDef(), - new ImportExport.ImportPrivateModule(importedModuleName), - ImportExport.apply$default$3(), - ImportExport.apply$default$4())); - } - return null; - }); + importedTargets.foreach( + importedTarget -> { + var importedModule = importedTarget.module().unsafeAsModule("should succeed"); + var importedModuleName = importedModule.getName().toString(); + var importedModulePackage = importedModule.getPackage(); + if (currentPackage != null + && !currentPackage.equals(importedModulePackage) + && importedModule.isPrivate()) { + importErrors.add( + ImportExport.apply( + resolvedImp.importDef(), + new ImportExport.ImportPrivateModule(importedModuleName), + ImportExport.apply$default$3(), + ImportExport.apply$default$4())); + } + return null; + }); return null; }); diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java index 5e9dbd2e9a3f..6a9232ace8d0 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java @@ -80,10 +80,14 @@ protected abstract Result createResolvedModuleMethod( Import imp, java.util.List exp, ResolvedModuleMethod moduleMethod); protected abstract Result createResolvedExtensionMethods( - Import imp, java.util.List exp, java.util.List extensionMethods); + Import imp, + java.util.List exp, + java.util.List extensionMethods); protected abstract Result createResolvedConversionMethods( - Import imp, java.util.List exp, java.util.List conversionMethods); + Import imp, + java.util.List exp, + java.util.List conversionMethods); protected abstract Result createErrorPackageCoundNotBeLoaded( Import imp, String impName, String loadingError); @@ -196,9 +200,11 @@ private ResolvedModuleMethod tryResolveAsModuleMethod(Import imp) { } /** - * Tries to resolve the given import as a list of extension methods. - * Note that it is possible that a single symbol resolves to multiple extension methods. - * @return List with at least one element. null if there are no static methods in the imported module scope. + * Tries to resolve the given import as a list of extension methods. Note that it is possible that + * a single symbol resolves to multiple extension methods. + * + * @return List with at least one element. null if there are no static methods in the imported + * module scope. */ private java.util.List tryResolveAsExtensionMethods(Import imp) { var parts = partsForImport(imp); @@ -210,9 +216,10 @@ private java.util.List tryResolveAsExtensionMethods(Imp return null; } var methodName = parts.get(parts.size() - 1); - var foundExtMethods = definedExtensionMethods.stream() - .filter(method -> nameForExtensionMethod(method).equals(methodName)) - .collect(Collectors.toUnmodifiableList()); + var foundExtMethods = + definedExtensionMethods.stream() + .filter(method -> nameForExtensionMethod(method).equals(methodName)) + .collect(Collectors.toUnmodifiableList()); if (foundExtMethods.isEmpty()) { return null; } else { @@ -221,9 +228,11 @@ private java.util.List tryResolveAsExtensionMethods(Imp } /** - * Tries to resolve the given import as a list of extension methods. - * Note that it is possible that a single symbol resolves to multiple extension methods. - * @return List of at least one element. null if there are no conversion methods in the imported module scope. + * Tries to resolve the given import as a list of extension methods. Note that it is possible that + * a single symbol resolves to multiple extension methods. + * + * @return List of at least one element. null if there are no conversion methods in the imported + * module scope. */ private java.util.List tryResolveAsConversionMethods(Import imp) { var parts = partsForImport(imp); @@ -235,9 +244,10 @@ private java.util.List tryResolveAsConversionMethods(I return null; } var methodName = parts.get(parts.size() - 1); - var foundConvMethods = definedConvMethods.stream() - .filter(method -> nameForConversionMethod(method).equals(methodName)) - .collect(Collectors.toUnmodifiableList()); + var foundConvMethods = + definedConvMethods.stream() + .filter(method -> nameForConversionMethod(method).equals(methodName)) + .collect(Collectors.toUnmodifiableList()); if (foundConvMethods.isEmpty()) { return null; } else { diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala index 279f6dc69316..2be94b899782 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala @@ -1,21 +1,10 @@ package org.enso.compiler -import org.enso.compiler.context.{ - CompilerContext, - FreshNameSupply, - InlineContext, - ModuleContext -} +import org.enso.compiler.context.{CompilerContext, FreshNameSupply, InlineContext, ModuleContext} import org.enso.compiler.context.CompilerContext.Module import org.enso.compiler.core.CompilerError import org.enso.compiler.core.Implicits.AsMetadata -import org.enso.compiler.core.ir.{ - Diagnostic, - Expression, - Name, - Warning, - Module => IRModule -} +import org.enso.compiler.core.ir.{Diagnostic, Expression, Name, Warning, Module => IRModule} import org.enso.compiler.core.ir.MetadataStorage.MetadataPair import org.enso.compiler.core.ir.expression.Error import org.enso.compiler.core.ir.module.scope.Export @@ -33,14 +22,7 @@ import org.enso.compiler.phase.exports.{ExportCycleException, ExportSymbolAnalys import org.enso.syntax2.Tree import java.io.PrintStream -import java.util.concurrent.{ - CompletableFuture, - ExecutorService, - Future, - LinkedBlockingDeque, - ThreadPoolExecutor, - TimeUnit -} +import java.util.concurrent.{CompletableFuture, ExecutorService, Future, LinkedBlockingDeque, ThreadPoolExecutor, TimeUnit} import java.util.logging.Level /** This class encapsulates the static transformation processes that take place diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 0f5176eb94c0..8201c46d9b39 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -291,43 +291,44 @@ case class BindingsMap( /** Dumps the export statements from this module into a structure ready for * further analysis. It uses only [[resolvedImports]] field, as - * [[exportedSymbols]] fields are expected to be filled later. - * - * For every symbol that is exported from this bindings map, gathers the module - * in which the symbol is defined and returns it in the list. For example, if there - * is an export `export project.Module.method`, there will be `Module` in the returned list. + * [[exportedSymbols]] fields are expected to be filled later. + * + * For every symbol that is exported from this bindings map, gathers the module + * in which the symbol is defined and returns it in the list. For example, if there + * is an export `export project.Module.method`, there will be `Module` in the returned list. * * @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, targets) => - exports.flatMap { exp => - val exportAs = exp.rename match { - case Some(rename) => Some(rename.name) - case None => None - } - val symbols = exp.onlyNames match { - case Some(onlyNames) => - onlyNames.map(_.name) - case None => - List(exp.name.parts.last.name) - } - targets.map { - case m : ResolvedModule => ExportedModule(m, exportAs, symbols) - case ResolvedType(modRef, _) => - ExportedModule(ResolvedModule(modRef), exportAs, symbols) - case ResolvedConstructor(ResolvedType(modRef, _), _) => - ExportedModule(ResolvedModule(modRef), exportAs, symbols) - case ResolvedModuleMethod(modRef, _) => - ExportedModule(ResolvedModule(modRef), exportAs, symbols) - case ResolvedStaticMethod(modRef, _) => - ExportedModule(ResolvedModule(modRef), exportAs, symbols) - case ResolvedConversionMethod(modRef, _) => - ExportedModule(ResolvedModule(modRef), exportAs, symbols) + resolvedImports + .collect { case ResolvedImport(_, exports, targets) => + exports.flatMap { exp => + val exportAs = exp.rename match { + case Some(rename) => Some(rename.name) + case None => None + } + val symbols = exp.onlyNames match { + case Some(onlyNames) => + onlyNames.map(_.name) + case None => + List(exp.name.parts.last.name) + } + targets.map { + case m: ResolvedModule => ExportedModule(m, exportAs, symbols) + case ResolvedType(modRef, _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + case ResolvedConstructor(ResolvedType(modRef, _), _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + case ResolvedModuleMethod(modRef, _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + case ResolvedStaticMethod(modRef, _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + case ResolvedConversionMethod(modRef, _) => + ExportedModule(ResolvedModule(modRef), exportAs, symbols) + } } } - } .flatten .distinct } @@ -363,8 +364,8 @@ object BindingsMap { * @param module the target being exported. * @param exportedAs the name it is exported as. * @param symbols List of symbols connected to the export. The symbol refers to the last part - * of the physical name of the target being exported. It is not a fully qualified - * name. + * of the physical name of the target being exported. It is not a fully qualified + * name. */ case class ExportedModule( module: ResolvedModule, @@ -428,7 +429,7 @@ object BindingsMap { * @param importDef the definition of the import * @param exports the exports associated with the import * @param targets list of targets that this import resolves to. Note that it is valid for a single - * import to resolve to multiple entities, for example, in case of extension methods. + * import to resolve to multiple entities, for example, in case of extension methods. */ case class ResolvedImport( importDef: ir.module.scope.Import.Module, @@ -436,7 +437,10 @@ object BindingsMap { targets: List[ImportTarget] ) { assert(targets.nonEmpty) - assert(areTargetsConsistent(), "All targets must be either static methods or conversion methods") + assert( + areTargetsConsistent(), + "All targets must be either static methods or conversion methods" + ) /** Convert the internal [[ModuleReference]] to an abstract reference. * @@ -476,8 +480,10 @@ object BindingsMap { if (targets.size > 1) { // If there are multiple targets, they can either all be static methods, or all be // conversion methods. - val allStaticMethods = targets.forall(_.isInstanceOf[ResolvedStaticMethod]) - val allConversionMethods = targets.forall(_.isInstanceOf[ResolvedConversionMethod]) + val allStaticMethods = + targets.forall(_.isInstanceOf[ResolvedStaticMethod]) + val allConversionMethods = + targets.forall(_.isInstanceOf[ResolvedConversionMethod]) allStaticMethods || allConversionMethods } else { true diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala index 639b296b25db..89bf2e9a39dd 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala @@ -119,9 +119,9 @@ case object ImportSymbolAnalysis extends IRPass { ) Some(err) case BindingsMap.ResolvedConversionMethod( - module, - conversionMethod - ) => + module, + conversionMethod + ) => val err = createImportFromMethodError( imp, module.getName diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala index 1516402d1c20..e1b63df05bf4 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/FullyQualifiedNames.scala @@ -94,7 +94,7 @@ case object FullyQualifiedNames extends IRPass { scopeMap.exportedSymbols.foreach { case (symbolName, resolvedNames) => resolvedNames.foreach { case resolution @ ResolvedType(exportedModuleRef, _) => - val tpeName = symbolName + val tpeName = symbolName val exportedModule = exportedModuleRef.unsafeAsModule() if ( exportedModuleRef.getName.path.length == 2 && exportedModuleRef.getName.item == tpeName && !exportedModule.isSynthetic @@ -113,7 +113,7 @@ case object FullyQualifiedNames extends IRPass { ir.exports.foreach { export => export match { case m: Export.Module - if m.name.name == resolution.qualifiedName.toString => + if m.name.name == resolution.qualifiedName.toString => m.addDiagnostic( warnings.Shadowed.TypeInModuleNameConflicts( exportedModule.getName.toString, diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala index 9e1877930403..596bdf98692c 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/ImportResolver.scala @@ -134,10 +134,11 @@ final class ImportResolver(compiler: Compiler) extends ImportResolverForIR { ) val bm = current.getBindingsMap if (bm != null) { - val modulesFromResolvedImps = bm.resolvedImports.flatMap { resolvedImp => - resolvedImp.targets.map { target => - target.module.unsafeAsModule() - } + val modulesFromResolvedImps = bm.resolvedImports.flatMap { + resolvedImp => + resolvedImp.targets.map { target => + target.module.unsafeAsModule() + } } (modulesFromResolvedImps.distinct, false) } else { diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 55b3d48eb2d3..002756212502 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -2,7 +2,16 @@ package org.enso.compiler.phase.exports import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.ModuleReference.Concrete -import org.enso.compiler.data.BindingsMap.{ExportedModule, ImportTarget, ResolvedConversionMethod, ResolvedImport, ResolvedModule, ResolvedModuleMethod, ResolvedName, ResolvedStaticMethod} +import org.enso.compiler.data.BindingsMap.{ + ExportedModule, + ImportTarget, + ResolvedConversionMethod, + ResolvedImport, + ResolvedModule, + ResolvedModuleMethod, + ResolvedName, + ResolvedStaticMethod +} import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module @@ -107,6 +116,10 @@ class ExportsResolution(private val context: CompilerContext) { result.reverse } + /** Fills in the [[BindingsMap.exportedSymbols]] field for every given module. + * This field is only filled with the symbols that are resolved. The symbols + * that are not resolved are ignored at this staged. + */ private def resolveExportedSymbols(modules: List[Module]): Unit = { modules.foreach { module => val bindings = getBindings(module) @@ -114,32 +127,33 @@ class ExportsResolution(private val context: CompilerContext) { bindings.definedEntities .filter(_.canExport) .map(e => (e.name, e.resolvedIn(module))) - val exportedSymbols: List[(String, ResolvedName)] = bindings.resolvedImports.collect { - case ResolvedImport(_, exports, targets) => - exports.flatMap { export => - targets.flatMap { target => - val symbols = export.onlyNames match { - case Some(onlyNames) => - onlyNames.map(_.name) - case None => - List(export.name.parts.last.name) - } - val isThisModule = target.module.unsafeAsModule() == module - if (isThisModule) { - None - } else { - symbols.flatMap { symbol => - export.rename match { - case Some(rename) => - Some((rename.name, target)) - case None => - Some((symbol, target)) + val exportedSymbols: List[(String, ResolvedName)] = + bindings.resolvedImports.collect { + case ResolvedImport(_, exports, targets) => + exports.flatMap { export => + targets.flatMap { target => + val symbols = export.onlyNames match { + case Some(onlyNames) => + onlyNames.map(_.name) + case None => + List(export.name.parts.last.name) + } + val isThisModule = target.module.unsafeAsModule() == module + if (isThisModule) { + None + } else { + symbols.flatMap { symbol => + export.rename match { + case Some(rename) => + Some((rename.name, target)) + case None => + Some((symbol, target)) + } } } } } - } - }.flatten + }.flatten bindings.exportedSymbols = List( ownEntities, @@ -155,27 +169,27 @@ class ExportsResolution(private val context: CompilerContext) { } } - /** - * If there are multiple resolved names for one exported symbol, they must be consistent. - * I.e., either they are all static (extension) and module methods, or all conversion methods. - * We cannot, for example, export type and a module for one symbol - that would result - * in a collision. - * @return true if they are consistent, false otherwise. - */ + /** If there are multiple resolved names for one exported symbol, they must be consistent. + * I.e., either they are all static (extension) and module methods, or all conversion methods. + * We cannot, for example, export type and a module for one symbol - that would result + * in a collision. + * @return true if they are consistent, false otherwise. + */ private def areResolvedNamesConsistent( resolvedNames: List[ResolvedName] ): Boolean = { - if (resolvedNames.size > 1) { - val allStaticOrModuleMethods = resolvedNames.forall { - case _: ResolvedStaticMethod => true - case _: ResolvedModuleMethod => true - case _ => false - } - val allConversionMethods = resolvedNames.forall(_.isInstanceOf[ResolvedConversionMethod]) - allStaticOrModuleMethods || allConversionMethods - } else { - true - } + if (resolvedNames.size > 1) { + val allStaticOrModuleMethods = resolvedNames.forall { + case _: ResolvedStaticMethod => true + case _: ResolvedModuleMethod => true + case _ => false + } + val allConversionMethods = + resolvedNames.forall(_.isInstanceOf[ResolvedConversionMethod]) + allStaticOrModuleMethods || allConversionMethods + } else { + true + } } /** Performs exports resolution on a selected set of modules. @@ -200,10 +214,10 @@ class ExportsResolution(private val context: CompilerContext) { cycles.head.map(_.module.module.unsafeAsModule()) ) } - val tops = topsort(graph) + val tops = topsort(graph) val topModules = tops.map(_.module) - resolveExportedSymbols(tops.map(_.module).collect { - case m: ResolvedModule => m.module.unsafeAsModule() + resolveExportedSymbols(topModules.collect { case m: ResolvedModule => + m.module.unsafeAsModule() }) // Take _last_ occurrence of each module topModules.map(_.module.unsafeAsModule()).reverse.distinct.reverse diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java index 013bcd85fae7..7a7c22259cce 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportedSymbolsTest.java @@ -120,26 +120,39 @@ public void exportRenamedSymbol() throws IOException { @Test public void exportedSymbolsFromSubModule() throws IOException { - var aMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.A_Module"), """ + var aMod = + new SourceModule( + QualifiedName.fromString("Synthetic_Module.A_Module"), + """ type A_Module """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ import project.Synthetic_Module """); ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); var ctx = createCtx(projDir); compile(ctx); var syntheticModExpSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Synthetic_Module"); - assertThat("Just a A_Module submodule should be exported", syntheticModExpSymbols.size(), is(1)); - assertThat("Just a A_Module submodule should be exported", syntheticModExpSymbols, hasKey("A_Module")); + assertThat( + "Just a A_Module submodule should be exported", syntheticModExpSymbols.size(), is(1)); + assertThat( + "Just a A_Module submodule should be exported", syntheticModExpSymbols, hasKey("A_Module")); } @Test public void exportTypeFromModuleWithSameName() throws IOException { - var aMod = new SourceModule(QualifiedName.fromString("A_Module"), """ + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), """ type A_Module """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ export project.A_Module.A_Module """); ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); @@ -149,15 +162,20 @@ public void exportTypeFromModuleWithSameName() throws IOException { assertThat(mainExportedSymbols.size(), is(1)); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module")); assertThat(mainExportedSymbols.get("A_Module").size(), is(1)); - assertThat(mainExportedSymbols.get("A_Module").get(0), is(instanceOf(BindingsMap.ResolvedType.class))); + assertThat( + mainExportedSymbols.get("A_Module").get(0), is(instanceOf(BindingsMap.ResolvedType.class))); } @Test public void exportModuleWithTypeWithSameName() throws IOException { - var aMod = new SourceModule(QualifiedName.fromString("A_Module"), """ + var aMod = + new SourceModule( + QualifiedName.fromString("A_Module"), """ type A_Module """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), """ export project.A_Module """); ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); @@ -167,15 +185,23 @@ public void exportModuleWithTypeWithSameName() throws IOException { assertThat(mainExportedSymbols.size(), is(1)); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module")); assertThat(mainExportedSymbols.get("A_Module").size(), is(1)); - assertThat(mainExportedSymbols.get("A_Module").get(0), is(instanceOf(BindingsMap.ResolvedModule.class))); + assertThat( + mainExportedSymbols.get("A_Module").get(0), + is(instanceOf(BindingsMap.ResolvedModule.class))); } @Test public void exportSyntheticModule() throws IOException { - var aMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.A_Module"), """ + var aMod = + new SourceModule( + QualifiedName.fromString("Synthetic_Module.A_Module"), + """ type A_Type """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ export project.Synthetic_Module """); ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); @@ -185,7 +211,9 @@ public void exportSyntheticModule() throws IOException { assertThat(mainExportedSymbols.size(), is(1)); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Synthetic_Module")); assertThat(mainExportedSymbols.get("Synthetic_Module").size(), is(1)); - assertThat(mainExportedSymbols.get("Synthetic_Module").get(0), is(instanceOf(BindingsMap.ResolvedModule.class))); + assertThat( + mainExportedSymbols.get("Synthetic_Module").get(0), + is(instanceOf(BindingsMap.ResolvedModule.class))); } private static Context createCtx(Path projDir) { diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConversionMethodTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConversionMethodTest.java index 2db23dfdcb3b..c0dabd1bc243 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConversionMethodTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConversionMethodTest.java @@ -22,8 +22,7 @@ public class ExportConversionMethodTest { - @Rule - public TemporaryFolder tempFolder = new TemporaryFolder(); + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @Test public void conversionMethodCanBeImportedByName() throws IOException { @@ -41,13 +40,12 @@ public void conversionMethodCanBeImportedByName() throws IOException { """ import project.A_Module.A_Type import project.A_Module.B_Type - + B_Type.from (_:A_Type) = 42 """); var mainMod = new SourceModule( - QualifiedName.fromString("Main"), - """ + QualifiedName.fromString("Main"), """ import project.B_Module.from """); var projDir = tempFolder.newFolder().toPath(); @@ -62,7 +60,8 @@ public void conversionMethodCanBeImportedByName() throws IOException { var mainResolvedImps = ModuleUtils.getResolvedImports(ctx, "local.Proj.Main"); assertThat(mainResolvedImps.size(), is(1)); assertThat(mainResolvedImps.get(0).targets().size(), is(1)); - assertThat(mainResolvedImps.get(0).targets().head(), is(instanceOf(ResolvedConversionMethod.class))); + assertThat( + mainResolvedImps.get(0).targets().head(), is(instanceOf(ResolvedConversionMethod.class))); } } @@ -70,12 +69,15 @@ public void conversionMethodCanBeImportedByName() throws IOException { public void conversionMethodIsInBindingMap() throws IOException { var aMod = new SourceModule( - QualifiedName.fromString("A_Module"), """ + QualifiedName.fromString("A_Module"), + """ type A_Type type B_Type B_Type.from (_:A_Type) = 42 """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), """ export project.A_Module.from """); var projDir = tempFolder.newFolder().toPath(); @@ -88,14 +90,16 @@ public void conversionMethodIsInBindingMap() throws IOException { var polyCtx = new PolyglotContext(ctx); polyCtx.getTopScope().compile(true); - var aModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); + var aModExportedSymbols = + ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); assertThat(aModExportedSymbols.size(), is(3)); assertThat(aModExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type", "from")); var mainResolvedImps = ModuleUtils.getResolvedImports(ctx, "local.Proj.Main"); assertThat(mainResolvedImps.size(), is(1)); assertThat(mainResolvedImps.get(0).targets().size(), is(1)); - assertThat(mainResolvedImps.get(0).targets().head(), is(instanceOf(ResolvedConversionMethod.class))); + assertThat( + mainResolvedImps.get(0).targets().head(), is(instanceOf(ResolvedConversionMethod.class))); var mainExportedSyms = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); assertThat(mainExportedSyms.size(), is(1)); assertThat(mainExportedSyms, hasKey("from")); @@ -106,13 +110,16 @@ public void conversionMethodIsInBindingMap() throws IOException { public void multipleConversionMethodsCanBeImported() throws IOException { var aMod = new SourceModule( - QualifiedName.fromString("A_Module"), """ + QualifiedName.fromString("A_Module"), + """ type A_Type type B_Type B_Type.from (_:A_Type) = 1 A_Type.from (_:B_Type) = 2 """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), """ export project.A_Module.from """); var projDir = tempFolder.newFolder().toPath(); @@ -125,15 +132,20 @@ public void multipleConversionMethodsCanBeImported() throws IOException { var polyCtx = new PolyglotContext(ctx); polyCtx.getTopScope().compile(true); - var aModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); + var aModExportedSymbols = + ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.A_Module"); assertThat(aModExportedSymbols.size(), is(3)); assertThat(aModExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type", "from")); var mainResolvedImps = ModuleUtils.getResolvedImports(ctx, "local.Proj.Main"); assertThat(mainResolvedImps.size(), is(1)); assertThat(mainResolvedImps.get(0).targets().size(), is(2)); - assertThat(mainResolvedImps.get(0).targets().apply(0), is(instanceOf(ResolvedConversionMethod.class))); - assertThat(mainResolvedImps.get(0).targets().apply(1), is(instanceOf(ResolvedConversionMethod.class))); + assertThat( + mainResolvedImps.get(0).targets().apply(0), + is(instanceOf(ResolvedConversionMethod.class))); + assertThat( + mainResolvedImps.get(0).targets().apply(1), + is(instanceOf(ResolvedConversionMethod.class))); var mainExportedSyms = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); assertThat(mainExportedSyms.size(), is(1)); assertThat(mainExportedSyms, hasKey("from")); diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportExtensionMethodTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportExtensionMethodTest.java index 094f4a7b09e3..396e44a1f712 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportExtensionMethodTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportExtensionMethodTest.java @@ -3,7 +3,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import java.io.IOException; @@ -127,7 +126,8 @@ public void extensionMethodIsInBindingMap() throws IOException { polyCtx.getTopScope().compile(true); var mainModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); assertThat(mainModExportedSymbols.size(), is(2)); - assertThat(mainModExportedSymbols.keySet(), containsInAnyOrder("My_Type", "extension_method")); + assertThat( + mainModExportedSymbols.keySet(), containsInAnyOrder("My_Type", "extension_method")); } } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java index 3bc3cc973e68..a0e4f825ae44 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportStaticMethodTest.java @@ -2,7 +2,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/desugar/ImportsTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/desugar/ImportsTest.scala index f3a74fd05df9..10f95761cfb6 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/desugar/ImportsTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/desugar/ImportsTest.scala @@ -75,7 +75,7 @@ class ImportsTest extends CompilerTest { ir.exports.take(3).map(_.showCode()) shouldEqual List( "export Bar.Foo.Main as Foo", "export Bar.Foo.Main as Bar", - "from Bar.Foo.Main export Bar, Baz", + "from Bar.Foo.Main export Bar, Baz" ) } diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index cbbaf3d4df2e..31ba876c634c 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -259,11 +259,7 @@ class ImportExportTest mainIr.imports.head.isInstanceOf[errors.ImportExport] shouldBe false val mainBindingMap = mainIr.unwrapBindingMap mainBindingMap.resolvedImports.size shouldEqual 1 - mainBindingMap - .resolvedImports - .head - .targets - .head + mainBindingMap.resolvedImports.head.targets.head .asInstanceOf[BindingsMap.ResolvedModuleMethod] .method .name shouldEqual "module_method" @@ -332,22 +328,18 @@ class ImportExportTest .createModule(packageQualifiedName.createChild("Module")) val mainIr = s""" - |import $namespace.$packageName.Module.extension_method - |""".stripMargin + |import $namespace.$packageName.Module.extension_method + |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - mainIr - .imports - .head + mainIr.imports.head .isInstanceOf[errors.ImportExport] shouldBe false val bindingsMap = mainIr.unwrapBindingMap bindingsMap.resolvedImports.size shouldBe 1 val resolvedImport = bindingsMap.resolvedImports.head resolvedImport.targets.head shouldBe a[BindingsMap.ResolvedStaticMethod] - resolvedImport - .targets - .head + resolvedImport.targets.head .asInstanceOf[BindingsMap.ResolvedStaticMethod] .staticMethod .methodName shouldBe "extension_method" @@ -368,9 +360,7 @@ class ImportExportTest |""".stripMargin .createModule(packageQualifiedName.createChild("Main")) .getIr - mainIr - .imports - .head + mainIr.imports.head .isInstanceOf[errors.ImportExport] shouldBe false val bm = mainIr.unwrapBindingMap bm.exportedSymbols.size shouldBe 1 @@ -578,8 +568,6 @@ class ImportExportTest .createModule(packageQualifiedName.createChild("Main")) .getIr - val bindingsMap = mainIr.unwrapBindingMap - bindingsMap shouldNot be(null) mainIr.exports.size shouldEqual 1 mainIr.exports.head.isInstanceOf[errors.ImportExport] shouldBe true mainIr.exports.head diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java index 05d3b3d05088..65f8ec01666e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodImportResolver.java @@ -17,7 +17,16 @@ final class InvokeMethodImportResolver extends ImportResolverAlgorithm< - EnsoObject, Module, UnresolvedSymbol, Object, Type, Module, AtomConstructor, Function, Function, Function> { + EnsoObject, + Module, + UnresolvedSymbol, + Object, + Type, + Module, + AtomConstructor, + Function, + Function, + Function> { private final Module module; private final TopLevelScope topScope; @@ -140,14 +149,14 @@ protected EnsoObject createResolvedModuleMethod( } @Override - protected EnsoObject createResolvedExtensionMethods(UnresolvedSymbol imp, List exp, - List functions) { + protected EnsoObject createResolvedExtensionMethods( + UnresolvedSymbol imp, List exp, List functions) { throw new UnsupportedOperationException("unimplemented"); } @Override - protected EnsoObject createResolvedConversionMethods(UnresolvedSymbol imp, List exp, - List functions) { + protected EnsoObject createResolvedConversionMethods( + UnresolvedSymbol imp, List exp, List functions) { throw new UnsupportedOperationException("unimplemented"); } diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index a9ab742c47d1..634fae94a19f 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -41,10 +41,7 @@ import org.enso.compiler.core.ir.expression.{ Operator, Section } -import org.enso.compiler.data.BindingsMap.{ - ResolvedConstructor, - ResolvedModule -} +import org.enso.compiler.data.BindingsMap.{ResolvedConstructor, ResolvedModule} import org.enso.compiler.data.{BindingsMap, CompilerConfig} import org.enso.compiler.exception.BadPatternMatch import org.enso.compiler.pass.analyse.alias.Graph.{Scope => AliasScope} @@ -201,7 +198,9 @@ class IrToTruffle( case ResolvedModule(module) => module } resolvedModules.foreach { resolvedModule => - scopeBuilder.addExport(new ImportExportScope(resolvedModule.unsafeAsModule())) + scopeBuilder.addExport( + new ImportExportScope(resolvedModule.unsafeAsModule()) + ) } } @@ -212,10 +211,10 @@ class IrToTruffle( bindingsMap.resolvedImports.foreach { imp => imp.targets.foreach { - case _: BindingsMap.ResolvedType => - case _: BindingsMap.ResolvedConstructor => - case _: BindingsMap.ResolvedModuleMethod => - case _: BindingsMap.ResolvedStaticMethod => + case _: BindingsMap.ResolvedType => + case _: BindingsMap.ResolvedConstructor => + case _: BindingsMap.ResolvedModuleMethod => + case _: BindingsMap.ResolvedStaticMethod => case _: BindingsMap.ResolvedConversionMethod => case ResolvedModule(module) => val mod = module From a84c45042361479c57334dff8611b54313a94bbe Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 13:35:14 +0200 Subject: [PATCH 072/111] Move java source from scala dir --- .../org/enso/compiler/phase/exports/ExportSymbolAnalysis.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename engine/runtime-compiler/src/main/{scala => java}/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java (100%) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java similarity index 100% rename from engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java rename to engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java From 6a4555b4e3739bd8450ca1cc3562373efecf976a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 15:07:58 +0200 Subject: [PATCH 073/111] Fix indexing errors in ExportSymbolAnalysis --- .../compiler/phase/exports/ExportSymbolAnalysis.java | 6 +++--- .../compiler/test/semantic/ImportExportTest.scala | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java index 6ab82b7378a3..0b9e39308d4c 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java @@ -58,7 +58,7 @@ public static Module analyseModule(Module moduleIr, PackageRepository packageRep preLastNameFQN = exportNameParts.stream() .map(Name::name) - .limit(exportNameParts.size() - 2) + .limit(exportNameParts.size() - 1) .collect(Collectors.joining(".")); symbols = CollectionConverters.asJava(exportIr.onlyNames().get()).stream() @@ -69,12 +69,12 @@ public static Module analyseModule(Module moduleIr, PackageRepository packageRep lastNameFQN = exportNameParts.stream() .map(Name::name) - .limit(exportNameParts.size() - 2) + .limit(exportNameParts.size() - 1) .collect(Collectors.joining(".")); preLastNameFQN = exportNameParts.stream() .map(Name::name) - .limit(exportNameParts.size() - 3) + .limit(exportNameParts.size() - 2) .collect(Collectors.joining(".")); symbols = List.of(exportNameParts.get(exportNameParts.size() - 1)); } diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 31ba876c634c..26a60ae43fc3 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -601,12 +601,12 @@ class ImportExportTest mainIr.exports.head .asInstanceOf[errors.ImportExport] .reason - .isInstanceOf[errors.ImportExport.SymbolDoesNotExist] shouldBe true + .isInstanceOf[errors.ImportExport.NoSuchConstructor] shouldBe true mainIr.exports.head .asInstanceOf[errors.ImportExport] .reason - .asInstanceOf[errors.ImportExport.SymbolDoesNotExist] - .symbolName shouldEqual "Non_Existing_Ctor" + .asInstanceOf[errors.ImportExport.NoSuchConstructor] + .constructorName shouldEqual "Non_Existing_Ctor" } "fail when exporting from type" in { @@ -629,12 +629,12 @@ class ImportExportTest mainIr.exports.head .asInstanceOf[errors.ImportExport] .reason - .isInstanceOf[errors.ImportExport.SymbolDoesNotExist] shouldBe true + .isInstanceOf[errors.ImportExport.NoSuchConstructor] shouldBe true mainIr.exports.head .asInstanceOf[errors.ImportExport] .reason - .asInstanceOf[errors.ImportExport.SymbolDoesNotExist] - .symbolName shouldEqual "FOO" + .asInstanceOf[errors.ImportExport.NoSuchConstructor] + .constructorName shouldEqual "FOO" } "fail when exporting from module with `from`" in { From 9ca0c398a46f26f3e792b148c59c0dc6cf13d260 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 15:20:04 +0200 Subject: [PATCH 074/111] Export cycle is not detected for self-exports --- .../phase/exports/ExportsResolution.scala | 17 +++++- .../compiler/ExportCycleDetectionTest.java | 57 +++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 002756212502..c7cb662c69e2 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -51,8 +51,21 @@ class ExportsResolution(private val context: CompilerContext) { Nil } val node = nodes(module) - node.exports = exports.map { case ExportedModule(mod, rename, symbols) => - Edge(node, symbols, rename, nodes.getOrElseUpdate(mod, Node(mod))) + node.exports = exports.flatMap { + case ExportedModule(exportedMod, rename, symbols) => + // Don't create edges for self-exports + if (exportedMod.qualifiedName != module.qualifiedName) { + Some( + Edge( + node, + symbols, + rename, + nodes.getOrElseUpdate(exportedMod, Node(exportedMod)) + ) + ) + } else { + None + } } node.exports.foreach { edge => edge.exportee.exportedBy ::= edge } } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java index 72079323dcce..91542b5515ed 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExportCycleDetectionTest.java @@ -3,6 +3,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.fail; import java.io.ByteArrayOutputStream; @@ -13,6 +15,7 @@ import org.enso.polyglot.PolyglotContext; import org.enso.polyglot.RuntimeOptions; import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ModuleUtils; import org.enso.test.utils.ProjectUtils; import org.enso.test.utils.SourceModule; import org.graalvm.polyglot.PolyglotException; @@ -100,6 +103,60 @@ public void detectCycleInThreeModules() throws IOException { containsString("local.Proj.C_Module"))); } + @Test + public void noCycleDetectedWhenExportingSymbolsFromItself_1() throws IOException { + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + type Main_Type + export project.Main.Main_Type + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(mainMod), projDir); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + try { + polyCtx.getTopScope().compile(true); + } catch (PolyglotException e) { + fail("Compilation error not expected. But got: " + e); + } + var exportedSyms = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(exportedSyms.size(), is(1)); + assertThat(exportedSyms, hasKey("Main_Type")); + } + } + + @Test + public void noCycleDetectedWhenExportingSymbolsFromItself_2() throws IOException { + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + type Main_Type + from project.Main export Main_Type + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(mainMod), projDir); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + try { + polyCtx.getTopScope().compile(true); + } catch (PolyglotException e) { + fail("Compilation error not expected. But got: " + e); + } + var exportedSyms = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(exportedSyms.size(), is(1)); + assertThat(exportedSyms, hasKey("Main_Type")); + } + } + private void expectProjectCompilationError(Path projDir, Matcher errMsgMatcher) { var out = new ByteArrayOutputStream(); try (var ctx = From 717b47c926fbe257e1d013ee4f813091781cbae2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 15:22:29 +0200 Subject: [PATCH 075/111] Refine "Exporting non-existing" symbol tests --- .../org/enso/compiler/test/semantic/ImportExportTest.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 26a60ae43fc3..d488835b8327 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -546,12 +546,12 @@ class ImportExportTest mainIr.exports.head .asInstanceOf[errors.ImportExport] .reason - .isInstanceOf[errors.ImportExport.SymbolDoesNotExist] shouldBe true + .isInstanceOf[errors.ImportExport.NoSuchConstructor] shouldBe true mainIr.exports.head .asInstanceOf[errors.ImportExport] .reason - .asInstanceOf[errors.ImportExport.SymbolDoesNotExist] - .symbolName shouldEqual "Non_Existing_Ctor" + .asInstanceOf[errors.ImportExport.NoSuchConstructor] + .constructorName shouldEqual "Non_Existing_Ctor" } "fail when exporting from other module" in { From db4396e4947bed01091bc2829585493b2c2a5f1e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 15:39:27 +0200 Subject: [PATCH 076/111] ExportSymbolAnalysis handles exports with exactly 3 name items specially --- .../phase/exports/ExportSymbolAnalysis.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java index 0b9e39308d4c..2089b3232529 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java @@ -45,7 +45,32 @@ public static Module analyseModule(Module moduleIr, PackageRepository packageRep var exportNameParts = CollectionConverters.asJava(exportIr.name().parts()); assert exportNameParts.size() > 2 : "All the exports should already be desugared in the module discovery compiler" - + " passes"; + + " passes"; + + if (exportNameParts.size() == 3) { + // There are 3 parts of the export, which means that it should be a direct + // module export like `export namespace.project.Module`, or + // `from namespace.project.Module export Symbol`. + var modFQN = + exportNameParts.stream().map(Name::name).collect(Collectors.joining(".")); + var mod = getLoadedModule(modFQN, packageRepository); + if (mod == null) { + var err = createModuleDoesNotExistError(exportIr, modFQN); + exportErrors.add(err); + return null; + } + if (exportIr.onlyNames().isDefined()) { + var symbols = + CollectionConverters.asJava(exportIr.onlyNames().get()).stream() + .map(Name.class::cast) + .toList(); + var errs = analyseSymbolsFromModule(mod, symbols); + exportErrors.addAll(errs); + } + return null; + } + + assert exportNameParts.size() > 3; String lastNameFQN; String lastNameItem; From d1c940054ed33aa6e19c64c755b6d5067023f95b Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 16:18:01 +0200 Subject: [PATCH 077/111] ExportsResolution.resolveExportedSymbols drops duplicate symbols --- .../org/enso/compiler/phase/exports/ExportsResolution.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index c7cb662c69e2..5302c60bbcf6 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -166,7 +166,9 @@ class ExportsResolution(private val context: CompilerContext) { } } } - }.flatten + } + .flatten + .distinct bindings.exportedSymbols = List( ownEntities, From 427a70f4cb8e12ecc1faa0df95d4e5a3eb408ef0 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 17:47:50 +0200 Subject: [PATCH 078/111] Conflicting resolutions are reported as CompilerError. - This makes ShadowedModule ImportExport error deprecated. --- .../exports/ConflictingResolutionsError.java | 62 +++++++++++++++++++ .../phase/exports/ExportsResolution.scala | 19 +++--- .../test/semantic/ImportsTest.scala | 13 ++-- .../ir/expression/warnings/Shadowed.scala | 2 +- 4 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ConflictingResolutionsError.java diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ConflictingResolutionsError.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ConflictingResolutionsError.java new file mode 100644 index 000000000000..1f0ad66614ce --- /dev/null +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ConflictingResolutionsError.java @@ -0,0 +1,62 @@ +package org.enso.compiler.phase.exports; + +import java.util.List; +import java.util.stream.Collectors; +import org.enso.compiler.core.CompilerError; +import org.enso.compiler.data.BindingsMap.ResolvedConstructor; +import org.enso.compiler.data.BindingsMap.ResolvedMethod; +import org.enso.compiler.data.BindingsMap.ResolvedModule; +import org.enso.compiler.data.BindingsMap.ResolvedName; +import org.enso.compiler.data.BindingsMap.ResolvedPolyglotField; +import org.enso.compiler.data.BindingsMap.ResolvedPolyglotSymbol; +import org.enso.compiler.data.BindingsMap.ResolvedType; +import org.enso.pkg.QualifiedName; + +/** + * An error thrown when there are multiple conflicting resolutions for a symbol. Note that, it is OK + * for example to have multiple extension methods resolved for a symbol. But not if one symbol + * resolves to a type and a module. + */ +public final class ConflictingResolutionsError extends CompilerError { + private ConflictingResolutionsError(String msg) { + super(msg); + } + + public static ConflictingResolutionsError create( + QualifiedName moduleName, List conflictingResolutions) { + assert conflictingResolutions.size() > 1; + var resolutionNames = + conflictingResolutions.stream() + .map(ConflictingResolutionsError::resolvedNameToString) + .collect(Collectors.toUnmodifiableList()); + var sb = new StringBuilder(); + sb.append("Conflicting resolutions in module '"); + sb.append(moduleName); + sb.append("': "); + sb.append(resolutionNames); + sb.append(". "); + sb.append( + "Probably caused by a name conflict of a defined entity in the module and an export."); + return new ConflictingResolutionsError(sb.toString()); + } + + private static String resolvedNameToString(ResolvedName resolvedName) { + return switch (resolvedName) { + case ResolvedType type -> "Type '" + type.qualifiedName() + "'"; + case ResolvedConstructor resolvedConstructor -> { + var typeName = resolvedConstructor.tpe().qualifiedName().toString(); + var consName = resolvedConstructor.cons().name(); + yield "Constructor '" + typeName + "." + consName + "'"; + } + case ResolvedMethod resolvedMethod -> "Method '" + resolvedMethod.qualifiedName() + "'"; + case ResolvedModule resolvedModule -> "Module '" + resolvedModule.qualifiedName() + "'"; + case ResolvedPolyglotField resolvedPolyglotField -> "Polyglot field '" + + resolvedPolyglotField.qualifiedName() + + "'"; + case ResolvedPolyglotSymbol resolvedPolyglotSymbol -> "Polyglot symbol '" + + resolvedPolyglotSymbol.qualifiedName() + + "'"; + default -> throw new UnsupportedOperationException("unimplemented: " + resolvedName); + }; + } +} diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 5302c60bbcf6..8f4c2b621e46 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -16,6 +16,7 @@ import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module import scala.collection.mutable +import scala.jdk.CollectionConverters._ /** An exception signaling a loop in the export statements. * @param modules the modules forming the cycle. @@ -140,9 +141,9 @@ class ExportsResolution(private val context: CompilerContext) { bindings.definedEntities .filter(_.canExport) .map(e => (e.name, e.resolvedIn(module))) - val exportedSymbols: List[(String, ResolvedName)] = - bindings.resolvedImports.collect { - case ResolvedImport(_, exports, targets) => + val expSymbolsFromResolvedImps: List[(String, ResolvedName)] = + bindings.resolvedImports + .collect { case ResolvedImport(_, exports, targets) => exports.flatMap { export => targets.flatMap { target => val symbols = export.onlyNames match { @@ -166,19 +167,19 @@ class ExportsResolution(private val context: CompilerContext) { } } } - } + } .flatten .distinct bindings.exportedSymbols = List( ownEntities, - exportedSymbols + expSymbolsFromResolvedImps ).flatten.groupBy(_._1).map { case (symbolName, duplicateResolutions) => val resolvedNames = duplicateResolutions.map(_._2) - assert( - areResolvedNamesConsistent(resolvedNames), - s"Resolved names are not consistent: ${resolvedNames}" - ) + if (!areResolvedNamesConsistent(resolvedNames)) { + throw ConflictingResolutionsError + .create(module.getName, resolvedNames.asJava) + } (symbolName, resolvedNames) } } diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala index b933471abd37..a6aefda1c418 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ImportsTest.scala @@ -128,12 +128,13 @@ class ImportsTest extends PackageTest { } "Compiler" should "detect name conflicts preventing users from importing submodules" in { - the[InterpreterException] thrownBy evalTestProject( - "Test_Submodules_Name_Conflict" - ) should have message "Method `c_mod_method` of type C.type could not be found." - val outLines = consumeOut - outLines(1) should include - "Declaration of type C shadows module local.Test_Submodules_Name_Conflict.A.B.C making it inaccessible via a qualified name." + try { + evalTestProject("Test_Submodules_Name_Conflict") + fail("Should throw CompilerError") + } catch { + case e: InterpreterException => + e.getMessage.contains("Conflicting resolutions") shouldBe true + } } "Compiler" should "accept exports of the same module" in { diff --git a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/warnings/Shadowed.scala b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/warnings/Shadowed.scala index 26aee8bd2051..2978b0e0269e 100644 --- a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/warnings/Shadowed.scala +++ b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/expression/warnings/Shadowed.scala @@ -54,7 +54,7 @@ object Shadowed { /** A warning that a submodule is being shadowed by the type of the same name * therefore preventing the user from accessing the module via a qualified name. * - * @param typename the type name shadowing the module + * @param typeName the type name shadowing the module * @param moduleName the module being shadowed * @param shadower the expression shadowing `moduleName` * @param location the location at which the shadowing takes place From 142f0384207107e7281666c054574bea87f4061b Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 17:49:34 +0200 Subject: [PATCH 079/111] Add tests for export resolution module topological sort --- .../test/semantic/ImportExportTest.scala | 204 ++++++++++++++++-- 1 file changed, 182 insertions(+), 22 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index d488835b8327..99fa6d27dfe1 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -13,7 +13,6 @@ import org.enso.persist.Persistance import org.enso.pkg.QualifiedName import org.enso.common.LanguageInfo import org.enso.common.MethodNames -import org.enso.compiler.data.BindingsMap.{ResolvedConstructor, ResolvedModule} import org.enso.compiler.phase.exports.Node import org.enso.polyglot.RuntimeOptions import org.graalvm.polyglot.{Context, Engine} @@ -101,6 +100,20 @@ class ImportExportTest exportsResolution.buildModuleGraph(compilerModules) } + /** Just delegates to [[ExportsResolution.runSort()]] + */ + private def runExportsResolutionSort( + modules: List[org.enso.interpreter.runtime.Module] + ): List[org.enso.interpreter.runtime.Module] = { + val compilerCtx = langCtx.getCompiler.context + val exportsResolution = new ExportsResolution(compilerCtx) + val compilerModules = modules.map(_.asCompilerModule()) + val sortedCompilerModules = exportsResolution.runSort(compilerModules) + sortedCompilerModules.map( + org.enso.interpreter.runtime.Module.fromCompilerModule + ) + } + before { ctx.enter() } @@ -1354,9 +1367,6 @@ class ImportExportTest "Exports graph building" should { def assertAModExportedByBMod(graph: List[Node]): Unit = { - withClue("There should be only A_Module and B_Module nodes") { - graph.size shouldBe 2 - } val aModNode = graph.find(node => node.module match { case BindingsMap.ResolvedModule(modRef) => @@ -1391,17 +1401,48 @@ class ImportExportTest |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val graph = buildExportsGraph(List(aModule, bModule)) + withClue("There should be only A_Module and B_Module nodes") { + graph.size shouldBe 2 + } assertAModExportedByBMod(graph) } - "build exports graph for static method" in { + "build exports graph for module method with `from ... export ...` syntax" in { val aModule = """ |type A_Type | A_Constructor | instance_method self = 42 | - |static_method = + |module_method = + | local_var = 42 + | local_var + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |from $namespace.$packageName.A_Module export module_method + | + |type B_Type + | B_Constructor val + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val graph = buildExportsGraph(List(aModule, bModule)) + withClue("There should be only A_Module and B_Module nodes") { + graph.size shouldBe 2 + } + assertAModExportedByBMod(graph) + } + + "build exports graph for module method with `export ...` syntax" in { + val aModule = + """ + |type A_Type + | A_Constructor + | instance_method self = 42 + | + |module_method = | local_var = 42 | local_var |""".stripMargin @@ -1409,13 +1450,16 @@ class ImportExportTest val bModule = s""" - |from $namespace.$packageName.A_Module export static_method + |export $namespace.$packageName.A_Module.module_method | |type B_Type | B_Constructor val |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val graph = buildExportsGraph(List(aModule, bModule)) + withClue("There should be only A_Module and B_Module nodes") { + graph.size shouldBe 2 + } assertAModExportedByBMod(graph) } @@ -1441,6 +1485,9 @@ class ImportExportTest |""".stripMargin .createModule(packageQualifiedName.createChild("B_Module")) val graph = buildExportsGraph(List(aModule, bModule)) + withClue("There should be only A_Module and B_Module nodes") { + graph.size shouldBe 2 + } assertAModExportedByBMod(graph) } @@ -1462,23 +1509,14 @@ class ImportExportTest val graph = buildExportsGraph(List(boolModule, mainModule)) withClue( - "graph should contains node for: [A_Module, B_Module, True, False]" + "graph should contains node for: [Boolean, Main]" ) { - graph.size shouldBe 4 + graph.size shouldBe 2 } - val trueNode = graph.find(node => { - node.module match { - case ResolvedConstructor(_, cons) if cons.name == "True" => - true - case _ => false - } - }) - trueNode shouldBe defined - val trueNodeExporter = trueNode.get.exportedBy.head.exporter - trueNodeExporter.target - .asInstanceOf[ResolvedModule] - .qualifiedName - .item shouldBe "Main" + val boolNode = graph.find(_.module.qualifiedName.item == "Boolean") + boolNode shouldBe defined + val boolExporter = boolNode.get.exportedBy.head.exporter + boolExporter.module.qualifiedName.item shouldBe "Main" } "No exports graph is constructed when only import is used" in { @@ -1506,4 +1544,126 @@ class ImportExportTest } } } + + "Export resolution sorting" should { + "correctly sort two modules" in { + val aModule = + """ + |type A_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |export $namespace.$packageName.A_Module.A_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val sortedMods = runExportsResolutionSort(List(aModule, bModule)) + sortedMods should contain theSameElementsInOrderAs List( + aModule, + bModule + ) + } + + "correctly sort three modules with two independent modules" in { + val aModule = + """ + |type A_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |type B_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + + val cModule = + s""" + |export $namespace.$packageName.A_Module.A_Type + |export $namespace.$packageName.B_Module.B_Type + |""".stripMargin + .createModule(packageQualifiedName.createChild("C_Module")) + val sortedMods = runExportsResolutionSort(List(aModule, bModule, cModule)) + sortedMods.last shouldBe cModule + sortedMods.take(2) should contain theSameElementsAs List( + aModule, + bModule + ) + } + + "correctly sort three modules with transitive exports" in { + val aModule = + """ + |# blank on purpose + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |export $namespace.$packageName.A_Module + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + + val cModule = + s""" + |export $namespace.$packageName.B_Module + |""".stripMargin + .createModule(packageQualifiedName.createChild("C_Module")) + val sortedMods = runExportsResolutionSort(List(aModule, bModule, cModule)) + sortedMods should contain theSameElementsInOrderAs List( + aModule, + bModule, + cModule + ) + } + + "correctly sort two modules with exported module method" in { + val aModule = + """ + |module_method = + | 42 + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |export $namespace.$packageName.A_Module.module_method + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + val sortedMods = runExportsResolutionSort(List(aModule, bModule)) + sortedMods should contain theSameElementsInOrderAs List( + aModule, + bModule + ) + } + + "correctly sort three modules with exported module method and import" in { + val aModule = + """ + |module_method = + | 42 + |""".stripMargin + .createModule(packageQualifiedName.createChild("A_Module")) + + val bModule = + s""" + |export $namespace.$packageName.A_Module.module_method + |""".stripMargin + .createModule(packageQualifiedName.createChild("B_Module")) + + val cModule = + s""" + |from $namespace.$packageName.B_Module import all + |""".stripMargin + .createModule(packageQualifiedName.createChild("C_Module")) + + val sortedMods = runExportsResolutionSort(List(aModule, bModule, cModule)) + sortedMods should contain theSameElementsInOrderAs List( + aModule, + bModule, + cModule + ) + } + } } From 118ebb754d6ca32971c425fa03915bb15e5355d1 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 17:49:49 +0200 Subject: [PATCH 080/111] fmt --- .../scala/org/enso/compiler/Compiler.scala | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala index 2be94b899782..6505af59affb 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala @@ -1,10 +1,21 @@ package org.enso.compiler -import org.enso.compiler.context.{CompilerContext, FreshNameSupply, InlineContext, ModuleContext} +import org.enso.compiler.context.{ + CompilerContext, + FreshNameSupply, + InlineContext, + ModuleContext +} import org.enso.compiler.context.CompilerContext.Module import org.enso.compiler.core.CompilerError import org.enso.compiler.core.Implicits.AsMetadata -import org.enso.compiler.core.ir.{Diagnostic, Expression, Name, Warning, Module => IRModule} +import org.enso.compiler.core.ir.{ + Diagnostic, + Expression, + Name, + Warning, + Module => IRModule +} import org.enso.compiler.core.ir.MetadataStorage.MetadataPair import org.enso.compiler.core.ir.expression.Error import org.enso.compiler.core.ir.module.scope.Export @@ -18,11 +29,22 @@ import org.enso.compiler.phase.{ImportResolver, ImportResolverAlgorithm} import org.enso.editions.LibraryName import org.enso.pkg.QualifiedName import org.enso.common.CompilationStage -import org.enso.compiler.phase.exports.{ExportCycleException, ExportSymbolAnalysis, ExportsResolution} +import org.enso.compiler.phase.exports.{ + ExportCycleException, + ExportSymbolAnalysis, + ExportsResolution +} import org.enso.syntax2.Tree import java.io.PrintStream -import java.util.concurrent.{CompletableFuture, ExecutorService, Future, LinkedBlockingDeque, ThreadPoolExecutor, TimeUnit} +import java.util.concurrent.{ + CompletableFuture, + ExecutorService, + Future, + LinkedBlockingDeque, + ThreadPoolExecutor, + TimeUnit +} import java.util.logging.Level /** This class encapsulates the static transformation processes that take place From 931c4405ae6f7bafcf79fe211c3947fea85a37b2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 5 Jul 2024 18:08:19 +0200 Subject: [PATCH 081/111] Constructors can be exported from the same module. --- .../phase/exports/ExportsResolution.scala | 33 ++++++++--------- .../test/exports/ExportConstructorTest.java | 36 +++++++++++++++++++ 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 8f4c2b621e46..0129f5bbc2f5 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -152,17 +152,12 @@ class ExportsResolution(private val context: CompilerContext) { case None => List(export.name.parts.last.name) } - val isThisModule = target.module.unsafeAsModule() == module - if (isThisModule) { - None - } else { - symbols.flatMap { symbol => - export.rename match { - case Some(rename) => - Some((rename.name, target)) - case None => - Some((symbol, target)) - } + symbols.flatMap { symbol => + export.rename match { + case Some(rename) => + Some((rename.name, target)) + case None => + Some((symbol, target)) } } } @@ -174,14 +169,16 @@ class ExportsResolution(private val context: CompilerContext) { bindings.exportedSymbols = List( ownEntities, expSymbolsFromResolvedImps - ).flatten.groupBy(_._1).map { case (symbolName, duplicateResolutions) => - val resolvedNames = duplicateResolutions.map(_._2) - if (!areResolvedNamesConsistent(resolvedNames)) { - throw ConflictingResolutionsError - .create(module.getName, resolvedNames.asJava) + ).flatten.distinct + .groupBy(_._1) + .map { case (symbolName, duplicateResolutions) => + val resolvedNames = duplicateResolutions.map(_._2) + if (!areResolvedNamesConsistent(resolvedNames)) { + throw ConflictingResolutionsError + .create(module.getName, resolvedNames.asJava) + } + (symbolName, resolvedNames) } - (symbolName, resolvedNames) - } } } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java index 11a26ba4bbbb..558b9f056dec 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportConstructorTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; @@ -52,4 +53,39 @@ public void exportedConstructorsAreInBindingMap() throws IOException { assertThat(mainModExportedSymbols, allOf(hasKey("True"), hasKey("False"))); } } + + @Test + public void constructorsCanBeExportedFromTheSameModule() throws IOException { + var booleanMod = + new SourceModule( + QualifiedName.fromString("Boolean"), + """ + export project.Boolean.Boolean.True + export project.Boolean.Boolean.False + type Boolean + True + False + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + # Import just the module on purpose + import project.Boolean + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(booleanMod, mainMod), projDir); + + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + var boolModExportedSymbols = + ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Boolean"); + assertThat(boolModExportedSymbols.size(), is(3)); + assertThat(boolModExportedSymbols.keySet(), containsInAnyOrder("True", "False", "Boolean")); + } + } } From fbb64d25bbca4691f58e39fddf6415d8414d78a2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 11:43:35 +0200 Subject: [PATCH 082/111] Add test for "importAllDoesNotImportModuleItself" --- .../test/imports/ImportSymbolsTest.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java new file mode 100644 index 000000000000..ed22522ec38e --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java @@ -0,0 +1,57 @@ +package org.enso.interpreter.test.imports; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Set; +import org.enso.pkg.QualifiedName; +import org.enso.polyglot.PolyglotContext; +import org.enso.polyglot.RuntimeOptions; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.graalvm.polyglot.PolyglotException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ImportSymbolsTest { + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void importAllDoesNotImportModuleItself() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_module"), """ + a_mod_method x = x + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + from project.A_module import all + main = + A_Module.a_mod_method 42 + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + var out = new ByteArrayOutputStream(); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .out(out) + .err(out) + .build()) { + var polyCtx = new PolyglotContext(ctx); + try { + polyCtx.getTopScope().compile(true); + fail("Compilation error expected. out = " + out); + } catch (PolyglotException e) { + assertThat(e.getMessage(), containsString("The name `A_Module` could not be found")); + } + } + } +} From 57ec17b77bc2451d0afeb4a53f5f7707c71d080f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 12:51:27 +0200 Subject: [PATCH 083/111] Test that import all from type does not import the type itself --- .../test/imports/ImportSymbolsTest.java | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java index ed22522ec38e..235858e132ac 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java @@ -22,7 +22,7 @@ public class ImportSymbolsTest { @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @Test - public void importAllDoesNotImportModuleItself() throws IOException { + public void importAllFromModuleDoesNotImportModuleItself() throws IOException { var aMod = new SourceModule( QualifiedName.fromString("A_module"), """ @@ -54,4 +54,39 @@ public void importAllDoesNotImportModuleItself() throws IOException { } } } + + @Test + public void importAllFromTypeDoesNotImportTypeItself() throws IOException { + var aMod = + new SourceModule( + QualifiedName.fromString("A_module"), """ + type A_Type + Cons + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + from project.A_module.A_Type import all + main = + A_Type.Cons + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + var out = new ByteArrayOutputStream(); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .out(out) + .err(out) + .build()) { + var polyCtx = new PolyglotContext(ctx); + try { + polyCtx.getTopScope().compile(true); + fail("Compilation error expected. Captured output = " + out); + } catch (PolyglotException e) { + assertThat(e.getMessage(), containsString("The name `A_Type` could not be found")); + } + } + } } From da1b75c73c20944b1e695ba5ac4079dc087c8b7c Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 12:59:52 +0200 Subject: [PATCH 084/111] Fix resolution export sorting test. It cares only about exports, not imports. --- .../compiler/test/semantic/ImportExportTest.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 99fa6d27dfe1..b310cb0e9522 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -1659,11 +1659,13 @@ class ImportExportTest .createModule(packageQualifiedName.createChild("C_Module")) val sortedMods = runExportsResolutionSort(List(aModule, bModule, cModule)) - sortedMods should contain theSameElementsInOrderAs List( - aModule, - bModule, - cModule - ) + withClue( + "A_Module should always be before B_Module.C_Module can be anywhere" + ) { + val aModIdx = sortedMods.indexOf(aModule) + val bModIdx = sortedMods.indexOf(bModule) + aModIdx should be < bModIdx + } } } } From fd23f45d12f509754fe193de82562cef0cfd42b4 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 13:04:53 +0200 Subject: [PATCH 085/111] ResolvedType does not list itself in exportedSymbols. This reverts ResolvedType.exportedSymbols to develop. --- .../main/scala/org/enso/compiler/data/BindingsMap.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 8201c46d9b39..836a0c53ae6a 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -659,11 +659,8 @@ object BindingsMap { override def findExportedSymbolsFor(name: String): List[ResolvedName] = exportedSymbols.getOrElse(name, List()) - override lazy val exportedSymbols: Map[String, List[ResolvedName]] = { - val membersMap = - tp.members.map(m => (m.name, List(ResolvedConstructor(this, m)))).toMap - membersMap + ((tp.name, List(this))) - } + override lazy val exportedSymbols: Map[String, List[ResolvedName]] = + tp.members.map(m => (m.name, List(ResolvedConstructor(this, m)))).toMap } /** A result of successful name resolution. From 99c88648cf2c1a8c726234e554b57db7bb4147e2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 13:26:59 +0200 Subject: [PATCH 086/111] Use specific exports in Table/Main.enso --- .../lib/Standard/Table/0.0.0-dev/src/Main.enso | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso index 1ccd29cf6326..cd4ce55bb7e8 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso @@ -25,7 +25,14 @@ export project.Table.Table export project.Value_Type.Auto export project.Value_Type.Bits export project.Value_Type.Value_Type -from project.Constants export Previous_Value, Report_Unmatched -from project.Expression export expr -from project.Extensions.Column_Vector_Extensions export to_column, compute, compute_bulk, running -from project.Extensions.Table_Conversions export to_table, from_objects, parse_to_table, write_table +export project.Constants.Previous_Value +export project.Constants.Report_Unmatched +export project.Expression.expr +export project.Extensions.Column_Vector_Extensions.to_column +export project.Extensions.Column_Vector_Extensions.compute +export project.Extensions.Column_Vector_Extensions.compute_bulk +export project.Extensions.Column_Vector_Extensions.running +export project.Extensions.Table_Conversions.to_table +export project.Extensions.Table_Conversions.from_objects +export project.Extensions.Table_Conversions.parse_to_table +export project.Extensions.Table_Conversions.write_table From f8895c9420ba30ace84ca952a77676a5b57cc776 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 13:31:30 +0200 Subject: [PATCH 087/111] Use specific exports in Database/Main.enso --- .../lib/Standard/Database/0.0.0-dev/src/Main.enso | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso index f12d7d868de8..4aee2d45b5d8 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso @@ -9,6 +9,10 @@ export project.Connection.SQLite_Format.SQLite_Format export project.Connection.SSL_Mode.SSL_Mode export project.SQL_Query.SQL_Query export project.Update_Action.Update_Action -from project.Connection.Postgres_Details.Postgres_Details export Postgres -from project.Extensions.Upload_Database_Table export update_rows, select_into_database_table, delete_rows -from project.Extensions.Upload_In_Memory_Table export update_rows, select_into_database_table, delete_rows +export project.Connection.Postgres_Details.Postgres_Details.Postgres +export project.Extensions.Upload_Database_Table.update_rows +export project.Extensions.Upload_Database_Table.select_into_database_table +export project.Extensions.Upload_Database_Table.delete_rows +export project.Extensions.Upload_In_Memory_Table.update_rows +export project.Extensions.Upload_In_Memory_Table.select_into_database_table +export project.Extensions.Upload_In_Memory_Table.delete_rows From 7d241144a2aaa10199cb77128a18ddeee606b897 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 13:54:35 +0200 Subject: [PATCH 088/111] ExportSymbolAnalysis handles submodule export --- .../compiler/phase/exports/ExportSymbolAnalysis.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java index 2089b3232529..fcf9d857f221 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java @@ -107,6 +107,15 @@ public static Module analyseModule(Module moduleIr, PackageRepository packageRep var mod = getLoadedModule(lastNameFQN, packageRepository); if (mod != null) { var errs = analyseSymbolsFromModule(mod, symbols); + if (!errs.isEmpty()) { + // It is also possible that symbol refers to a submodule of this module + var subModFQN = exportNameParts.stream().map(Name::name).collect(Collectors.joining(".")); + var subMod = getLoadedModule(subModFQN, packageRepository); + if (subMod != null) { + // Everything is OK - we export the subMod directly + return null; + } + } exportErrors.addAll(errs); return null; } From eae45af6d67e643f44ec80b013ad305e90d52f3b Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 13:54:45 +0200 Subject: [PATCH 089/111] Add ExportModuleTest --- .../test/exports/ExportModuleTest.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportModuleTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportModuleTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportModuleTest.java new file mode 100644 index 000000000000..217dfab4c2c9 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportModuleTest.java @@ -0,0 +1,92 @@ +package org.enso.interpreter.test.exports; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.util.Set; +import org.enso.compiler.data.BindingsMap.ResolvedModule; +import org.enso.pkg.QualifiedName; +import org.enso.polyglot.PolyglotContext; +import org.enso.polyglot.RuntimeOptions; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ModuleUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ExportModuleTest { + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void exportSubModuleFromExistingModule() throws IOException { + var subModule = + new SourceModule( + QualifiedName.fromString("Module.SubModule"), + """ + # Blank on purpose + """); + var module = + new SourceModule( + QualifiedName.fromString("Module"), + """ + # Blank on purpose - ensures that `Module` is not just synthetic + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + export project.Module.SubModule + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(subModule, module, mainMod), projDir); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + var mainModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainModExportedSymbols.size(), is(1)); + assertThat(mainModExportedSymbols, hasKey("SubModule")); + assertThat(mainModExportedSymbols.get("SubModule").size(), is(1)); + assertThat( + mainModExportedSymbols.get("SubModule").get(0), is(instanceOf(ResolvedModule.class))); + } + } + + @Test + public void exportSubModuleFromSyntheticModule() throws IOException { + var subModule = + new SourceModule( + QualifiedName.fromString("Module.SubModule"), + """ + # Blank on purpose + """); + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ + export project.Module.SubModule + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(subModule, mainMod), projDir); + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + var mainModExportedSymbols = ModuleUtils.getExportedSymbolsFromModule(ctx, "local.Proj.Main"); + assertThat(mainModExportedSymbols.size(), is(1)); + assertThat(mainModExportedSymbols, hasKey("SubModule")); + assertThat(mainModExportedSymbols.get("SubModule").size(), is(1)); + assertThat( + mainModExportedSymbols.get("SubModule").get(0), is(instanceOf(ResolvedModule.class))); + } + } +} From 45d9c551898dbb37c647abf3ddb4d21d3d648ab4 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 13:54:51 +0200 Subject: [PATCH 090/111] fmt --- .../org/enso/compiler/phase/exports/ExportSymbolAnalysis.java | 3 ++- .../org/enso/interpreter/test/imports/ImportSymbolsTest.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java index fcf9d857f221..4e9b86f59f72 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java @@ -109,7 +109,8 @@ public static Module analyseModule(Module moduleIr, PackageRepository packageRep var errs = analyseSymbolsFromModule(mod, symbols); if (!errs.isEmpty()) { // It is also possible that symbol refers to a submodule of this module - var subModFQN = exportNameParts.stream().map(Name::name).collect(Collectors.joining(".")); + var subModFQN = + exportNameParts.stream().map(Name::name).collect(Collectors.joining(".")); var subMod = getLoadedModule(subModFQN, packageRepository); if (subMod != null) { // Everything is OK - we export the subMod directly diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java index 235858e132ac..ead865cc3b46 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java @@ -59,7 +59,8 @@ public void importAllFromModuleDoesNotImportModuleItself() throws IOException { public void importAllFromTypeDoesNotImportTypeItself() throws IOException { var aMod = new SourceModule( - QualifiedName.fromString("A_module"), """ + QualifiedName.fromString("A_module"), + """ type A_Type Cons """); From b01f52644e381365d8144381522f2f31de1b5af6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 18:02:01 +0200 Subject: [PATCH 091/111] Use specific exports in more places --- .../lib/Standard/Geo/0.0.0-dev/src/Main.enso | 2 +- .../lib/Standard/Test/0.0.0-dev/src/Main.enso | 20 ++++++++++++++----- .../Visualization/0.0.0-dev/src/Main.enso | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/distribution/lib/Standard/Geo/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Geo/0.0.0-dev/src/Main.enso index b5d1f1839753..6b1b87429557 100644 --- a/distribution/lib/Standard/Geo/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Geo/0.0.0-dev/src/Main.enso @@ -2,7 +2,7 @@ from Standard.Base import all from Standard.Table import Table -from project.Geo_Json export geo_json_to_table +export project.Geo_Json.geo_json_to_table ## UNSTABLE ICON location diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso index a655811b61a5..661c6d22bf30 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso @@ -12,9 +12,19 @@ export project.Faker.Faker export project.Problems export project.Suite.Suite export project.Test.Test -from project.Extensions export should_fail_with, should_equal, should_equal_type, should_not_equal -from project.Extensions export should_not_equal_type, should_start_with, should_end_with -from project.Extensions export should_succeed, should_be_a, should_be_true, should_be_false -from project.Extensions export should_contain_the_same_elements_as, should_only_contain_elements_in -from project.Extensions export should_contain, should_not_contain +export project.Extensions.should_fail_with +export project.Extensions.should_equal +export project.Extensions.should_equal_type +export project.Extensions.should_not_equal +export project.Extensions.should_not_equal_type +export project.Extensions.should_start_with +export project.Extensions.should_end_with +export project.Extensions.should_succeed +export project.Extensions.should_be_a +export project.Extensions.should_be_true +export project.Extensions.should_be_false +export project.Extensions.should_contain_the_same_elements_as +export project.Extensions.should_only_contain_elements_in +export project.Extensions.should_contain +export project.Extensions.should_not_contain diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Main.enso index e33984317862..14c2e0e2fb67 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Main.enso @@ -5,5 +5,5 @@ export project.AI export project.Helpers export project.Id.Id export project.Preprocessor -from project.File_Upload export file_uploading +export project.File_Upload.file_uploading From d40e0bbae0628b3a73d2886aee93c195962815e6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 18:07:25 +0200 Subject: [PATCH 092/111] Rename StaticMethod to ExtensionMethod --- .../compiler/phase/ImportResolverForIR.java | 14 ++++----- .../org/enso/compiler/data/BindingsMap.scala | 20 ++++++------- .../pass/analyse/BindingAnalysis.scala | 6 ++-- .../pass/analyse/ImportSymbolAnalysis.scala | 4 +-- .../pass/resolve/MethodDefinitions.scala | 2 +- .../enso/compiler/pass/resolve/Patterns.scala | 6 ++-- .../phase/exports/ExportsResolution.scala | 4 +-- .../pass/analyse/BindingAnalysisTest.scala | 30 +++++++++---------- .../test/semantic/ImportExportTest.scala | 4 +-- .../interpreter/caches/ImportExportCache.java | 4 +-- .../interpreter/runtime/IrToTruffle.scala | 12 ++++---- 11 files changed, 53 insertions(+), 53 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java index 13bab020d493..671be7c2435b 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverForIR.java @@ -18,7 +18,7 @@ import org.enso.compiler.data.BindingsMap.ResolvedConversionMethod; import org.enso.compiler.data.BindingsMap.ResolvedImport; import org.enso.compiler.data.BindingsMap.ResolvedModuleMethod; -import org.enso.compiler.data.BindingsMap.ResolvedStaticMethod; +import org.enso.compiler.data.BindingsMap.ResolvedExtensionMethod; import org.enso.compiler.data.BindingsMap.ResolvedType; import org.enso.editions.LibraryName; @@ -36,7 +36,7 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm< CompilerContext.Module, ResolvedConstructor, ResolvedModuleMethod, - ResolvedStaticMethod, + ResolvedExtensionMethod, ResolvedConversionMethod > { abstract Compiler getCompiler(); @@ -72,7 +72,7 @@ protected String nameForModuleMethod(ResolvedModuleMethod resolvedModuleMethod) } @Override - protected String nameForExtensionMethod(ResolvedStaticMethod resolvedStaticMethod) { + protected String nameForExtensionMethod(ResolvedExtensionMethod resolvedStaticMethod) { return resolvedStaticMethod.methodName(); } @@ -210,7 +210,7 @@ protected java.util.List definedModuleMethods(Import.Modul } @Override - protected java.util.List definedExtensionMethods(Import.Module imp) { + protected java.util.List definedExtensionMethods(Import.Module imp) { var parts = partsForImport(imp); if (parts.size() < 3) { return null; @@ -232,12 +232,12 @@ protected java.util.List definedExtensionMethods(Import.Mo var extensionMethods = scala.jdk.javaapi.CollectionConverters.asJava(bindingsMap.definedEntities()) .stream() .filter(definedEntity -> { - if (definedEntity instanceof BindingsMap.StaticMethod extensionMethod) { + if (definedEntity instanceof BindingsMap.ExtensionMethod extensionMethod) { return extensionMethod.name().equals(modMethodName); } return false; }) - .map(entity -> new ResolvedStaticMethod(new BindingsMap$ModuleReference$Concrete(mod), (BindingsMap.StaticMethod) entity)) + .map(entity -> new ResolvedExtensionMethod(new BindingsMap$ModuleReference$Concrete(mod), (BindingsMap.ExtensionMethod) entity)) .collect(Collectors.toUnmodifiableList()); return extensionMethods; } @@ -321,7 +321,7 @@ protected Tuple2> createResolvedModuleMethod(Impo @Override protected Tuple2> createResolvedExtensionMethods(Import.Module imp, - java.util.List exp, java.util.List extensionMethods) { + java.util.List exp, java.util.List extensionMethods) { java.util.List importTargets = extensionMethods .stream() .map(ImportTarget.class::cast) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala index 836a0c53ae6a..f865dd8ece55 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -322,7 +322,7 @@ case class BindingsMap( ExportedModule(ResolvedModule(modRef), exportAs, symbols) case ResolvedModuleMethod(modRef, _) => ExportedModule(ResolvedModule(modRef), exportAs, symbols) - case ResolvedStaticMethod(modRef, _) => + case ResolvedExtensionMethod(modRef, _) => ExportedModule(ResolvedModule(modRef), exportAs, symbols) case ResolvedConversionMethod(modRef, _) => ExportedModule(ResolvedModule(modRef), exportAs, symbols) @@ -481,7 +481,7 @@ object BindingsMap { // If there are multiple targets, they can either all be static methods, or all be // conversion methods. val allStaticMethods = - targets.forall(_.isInstanceOf[ResolvedStaticMethod]) + targets.forall(_.isInstanceOf[ResolvedExtensionMethod]) val allConversionMethods = targets.forall(_.isInstanceOf[ResolvedConversionMethod]) allStaticMethods || allConversionMethods @@ -496,8 +496,8 @@ object BindingsMap { def resolvedIn(module: ModuleReference): ResolvedName = this match { case t: Type => ResolvedType(module, t) - case staticMethod: StaticMethod => - ResolvedStaticMethod(module, staticMethod) + case staticMethod: ExtensionMethod => + ResolvedExtensionMethod(module, staticMethod) case conversionMethod: ConversionMethod => ResolvedConversionMethod(module, conversionMethod) case m: ModuleMethod => ResolvedModuleMethod(module, m) @@ -611,7 +611,7 @@ object BindingsMap { * My_Type.method = 42 * ``` */ - case class StaticMethod( + case class ExtensionMethod( methodName: String, tpName: String ) extends Method { @@ -826,17 +826,17 @@ object BindingsMap { /** Method resolved on a type - either static method or extension method. */ - case class ResolvedStaticMethod( + case class ResolvedExtensionMethod( module: ModuleReference, - staticMethod: StaticMethod + staticMethod: ExtensionMethod ) extends ResolvedMethod { - override def toAbstract: ResolvedStaticMethod = { + override def toAbstract: ResolvedExtensionMethod = { this.copy(module = module.toAbstract) } override def toConcrete( moduleMap: ModuleMap - ): Option[ResolvedStaticMethod] = { + ): Option[ResolvedExtensionMethod] = { module.toConcrete(moduleMap).map { module => this.copy(module = module) } @@ -931,7 +931,7 @@ object BindingsMap { s" The imported polyglot field ${name};" case BindingsMap.ResolvedModuleMethod(module, symbol) => s" The method ${symbol.name} defined in module ${module.getName}" - case BindingsMap.ResolvedStaticMethod(module, staticMethod) => + case BindingsMap.ResolvedExtensionMethod(module, staticMethod) => s" The static method ${staticMethod.methodName} defined in module ${module.getName} for type ${staticMethod.tpName}" case BindingsMap.ResolvedConversionMethod(module, conversionMethod) => s" The conversion method ${conversionMethod.targetTpName}.${conversionMethod.methodName} defined in module ${module.getName}" diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala index ecc72411c3fe..d1546bef3520 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala @@ -11,7 +11,7 @@ import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.{ ConversionMethod, ModuleMethod, - StaticMethod + ExtensionMethod } import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.desugar.{ @@ -77,7 +77,7 @@ case object BindingAnalysis extends IRPass { Some(ModuleMethod(ref.methodName.name)) else { Some( - StaticMethod(ref.methodName.name, n.name) + ExtensionMethod(ref.methodName.name, n.name) ) } case Some(literal: Name.Literal) => @@ -86,7 +86,7 @@ case object BindingAnalysis extends IRPass { Some(ModuleMethod(ref.methodName.name)) else { Some( - StaticMethod(ref.methodName.name, literal.name) + ExtensionMethod(ref.methodName.name, literal.name) ) } case None => Some(ModuleMethod(ref.methodName.name)) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala index 89bf2e9a39dd..231c996aca62 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala @@ -111,7 +111,7 @@ case object ImportSymbolAnalysis extends IRPass { method.name ) Some(err) - case BindingsMap.ResolvedStaticMethod(module, staticMethod) => + case BindingsMap.ResolvedExtensionMethod(module, staticMethod) => val err = createImportFromMethodError( imp, module.getName.createChild(staticMethod.tpName).toString, @@ -196,7 +196,7 @@ case object ImportSymbolAnalysis extends IRPass { unresolvedSymbol.name ) ) - case BindingsMap.ResolvedStaticMethod(mod, staticMethod) => + case BindingsMap.ResolvedExtensionMethod(mod, staticMethod) => errors.ImportExport( imp, errors.ImportExport.NoSuchStaticMethod( diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/MethodDefinitions.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/MethodDefinitions.scala index 1d052ae2fb4a..a3932250fe18 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/MethodDefinitions.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/MethodDefinitions.scala @@ -229,7 +229,7 @@ case object MethodDefinitions extends IRPass { "a method definition target" ) ) - case _: BindingsMap.ResolvedStaticMethod => + case _: BindingsMap.ResolvedExtensionMethod => errors.Resolution( typePointer, errors.Resolution.UnexpectedMethod( diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/Patterns.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/Patterns.scala index 8107959abb7e..27320954a248 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/Patterns.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/Patterns.scala @@ -194,7 +194,7 @@ object Patterns extends IRPass { ) ) r.setLocation(consName.location) - case Right(_: BindingsMap.ResolvedStaticMethod) => + case Right(_: BindingsMap.ResolvedExtensionMethod) => val r = errors.Resolution( consName, errors.Resolution.UnexpectedMethod( @@ -228,7 +228,7 @@ object Patterns extends IRPass { throw new CompilerError( "Impossible, should be transformed into an error before." ) - case BindingsMap.ResolvedStaticMethod(_, _) => + case BindingsMap.ResolvedExtensionMethod(_, _) => throw new CompilerError( "Impossible, should be transformed into an error before." ) @@ -303,7 +303,7 @@ object Patterns extends IRPass { errors.Resolution .UnexpectedMethod(s"method type pattern case") ) - case Right(_: BindingsMap.ResolvedStaticMethod) => + case Right(_: BindingsMap.ResolvedExtensionMethod) => errors.Resolution( tpeName, errors.Resolution.UnexpectedMethod( diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 0129f5bbc2f5..540505db4198 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -10,7 +10,7 @@ import org.enso.compiler.data.BindingsMap.{ ResolvedModule, ResolvedModuleMethod, ResolvedName, - ResolvedStaticMethod + ResolvedExtensionMethod } import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module @@ -193,7 +193,7 @@ class ExportsResolution(private val context: CompilerContext) { ): Boolean = { if (resolvedNames.size > 1) { val allStaticOrModuleMethods = resolvedNames.forall { - case _: ResolvedStaticMethod => true + case _: ResolvedExtensionMethod => true case _: ResolvedModuleMethod => true case _ => false } diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala index 3d61b6a41383..330431367179 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala @@ -11,8 +11,8 @@ import org.enso.compiler.data.BindingsMap.{ ConversionMethod, ModuleMethod, PolyglotSymbol, - ResolvedStaticMethod, - StaticMethod, + ResolvedExtensionMethod, + ExtensionMethod, Type } import org.enso.compiler.pass.analyse.BindingAnalysis @@ -68,14 +68,14 @@ class BindingAnalysisTest extends CompilerTest { metadata.definedEntities should contain theSameElementsAs List( Type("My_Type", List(), List(), false), - StaticMethod("extension_method", "My_Type") + ExtensionMethod("extension_method", "My_Type") ) metadata.resolveName("extension_method") shouldEqual Right( List( - ResolvedStaticMethod( + ResolvedExtensionMethod( ctx.moduleReference(), - StaticMethod("extension_method", "My_Type") + ExtensionMethod("extension_method", "My_Type") ) ) ) @@ -95,19 +95,19 @@ class BindingAnalysisTest extends CompilerTest { metadata.definedEntities should contain theSameElementsAs List( Type("My_Type", List(), List(), false), Type("Other_Type", List(), List(), false), - StaticMethod("extension_method", "My_Type"), - StaticMethod("extension_method", "Other_Type") + ExtensionMethod("extension_method", "My_Type"), + ExtensionMethod("extension_method", "Other_Type") ) metadata.resolveName("extension_method") shouldBe Right( List( - ResolvedStaticMethod( + ResolvedExtensionMethod( ctx.moduleReference(), - StaticMethod("extension_method", "My_Type") + ExtensionMethod("extension_method", "My_Type") ), - ResolvedStaticMethod( + ResolvedExtensionMethod( ctx.moduleReference(), - StaticMethod("extension_method", "Other_Type") + ExtensionMethod("extension_method", "Other_Type") ) ) ) @@ -189,8 +189,8 @@ class BindingAnalysisTest extends CompilerTest { ), Type("Bar", List(), List(), builtinType = false), Type("Baz", List("x", "y"), List(), builtinType = false), - StaticMethod("foo", "Baz"), - StaticMethod("baz", "Bar"), + ExtensionMethod("foo", "Baz"), + ExtensionMethod("baz", "Bar"), ConversionMethod("from", "Bar", "Foo"), ModuleMethod("foo") ) @@ -216,9 +216,9 @@ class BindingAnalysisTest extends CompilerTest { .filter( _.isInstanceOf[BindingsMap.Method] ) should contain theSameElementsAs List( - StaticMethod("foo", moduleName), + ExtensionMethod("foo", moduleName), ModuleMethod("bar"), - StaticMethod("baz", moduleName) + ExtensionMethod("baz", moduleName) ) } diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index b310cb0e9522..0661458e7fc5 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -351,9 +351,9 @@ class ImportExportTest val bindingsMap = mainIr.unwrapBindingMap bindingsMap.resolvedImports.size shouldBe 1 val resolvedImport = bindingsMap.resolvedImports.head - resolvedImport.targets.head shouldBe a[BindingsMap.ResolvedStaticMethod] + resolvedImport.targets.head shouldBe a[BindingsMap.ResolvedExtensionMethod] resolvedImport.targets.head - .asInstanceOf[BindingsMap.ResolvedStaticMethod] + .asInstanceOf[BindingsMap.ResolvedExtensionMethod] .staticMethod .methodName shouldBe "extension_method" } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java b/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java index adeeffa55b66..3a243b7e591b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/caches/ImportExportCache.java @@ -204,7 +204,7 @@ static Metadata read(byte[] arr) throws IOException { @Persistable(clazz = BindingsMap.ResolvedModule.class, id = 33012) @Persistable(clazz = BindingsMap.ResolvedType.class, id = 33013) @Persistable(clazz = BindingsMap.ResolvedModuleMethod.class, id = 33014) - @Persistable(clazz = BindingsMap.ResolvedStaticMethod.class, id = 33015) + @Persistable(clazz = BindingsMap.ResolvedExtensionMethod.class, id = 33015) @Persistable(clazz = BindingsMap.ResolvedConversionMethod.class, id = 33016) @Persistable(clazz = BindingsMap.ExportedModule.class, id = 33017) @Persistable(clazz = BindingsMap.Resolution.class, id = 33018) @@ -212,7 +212,7 @@ static Metadata read(byte[] arr) throws IOException { @Persistable(clazz = BindingsMap.ResolvedPolyglotSymbol.class, id = 33020) @Persistable(clazz = BindingsMap.ResolvedPolyglotField.class, id = 33021) @Persistable(clazz = BindingsMap.ModuleMethod.class, id = 33022) - @Persistable(clazz = BindingsMap.StaticMethod.class, id = 33023) + @Persistable(clazz = BindingsMap.ExtensionMethod.class, id = 33023) @Persistable(clazz = BindingsMap.ConversionMethod.class, id = 33024) @Persistable(clazz = BindingsMap.Argument.class, id = 33025) @ServiceProvider(service = Persistance.class) diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index 634fae94a19f..ab9f9d2f0f6d 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -214,7 +214,7 @@ class IrToTruffle( case _: BindingsMap.ResolvedType => case _: BindingsMap.ResolvedConstructor => case _: BindingsMap.ResolvedModuleMethod => - case _: BindingsMap.ResolvedStaticMethod => + case _: BindingsMap.ResolvedExtensionMethod => case _: BindingsMap.ResolvedConversionMethod => case ResolvedModule(module) => val mod = module @@ -412,7 +412,7 @@ class IrToTruffle( throw new CompilerError( "Impossible module method here, should be caught by MethodDefinitions pass." ) - case _: BindingsMap.ResolvedStaticMethod => + case _: BindingsMap.ResolvedExtensionMethod => throw new CompilerError( "Impossible static method here, should be caught by MethodDefinitions pass." ) @@ -887,7 +887,7 @@ class IrToTruffle( throw new CompilerError( "Impossible module method here, should be caught by MethodDefinitions pass." ) - case _: BindingsMap.ResolvedStaticMethod => + case _: BindingsMap.ResolvedExtensionMethod => throw new CompilerError( "Impossible static method here, should be caught by MethodDefinitions pass." ) @@ -1015,7 +1015,7 @@ class IrToTruffle( name, fun ) - case BindingsMap.ResolvedStaticMethod(module, staticMethod) => + case BindingsMap.ResolvedExtensionMethod(module, staticMethod) => val actualModule = module.unsafeAsModule() val currentScope = asScope(actualModule) actualModule.getBindingsMap.resolveName( @@ -1550,7 +1550,7 @@ class IrToTruffle( ) case Some( BindingsMap.Resolution( - BindingsMap.ResolvedStaticMethod(_, _) + BindingsMap.ResolvedExtensionMethod(_, _) ) ) => throw new CompilerError( @@ -1934,7 +1934,7 @@ class IrToTruffle( throw new CompilerError( s"Impossible here, module method ${method.name} should be caught when translating application" ) - case BindingsMap.ResolvedStaticMethod(_, staticMethod) => + case BindingsMap.ResolvedExtensionMethod(_, staticMethod) => throw new CompilerError( s"Impossible here, static method ${staticMethod.name} should be caught when translating application" ) From 171ca2e8cf136c2bec995cb72fc8a99858fe85dd Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 8 Jul 2024 18:43:54 +0200 Subject: [PATCH 093/111] IrToTruffle consructs ImportExportScope from directly exported modules --- .../enso/interpreter/runtime/IrToTruffle.scala | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index ab9f9d2f0f6d..db74c7b635ad 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -193,15 +193,11 @@ class IrToTruffle( "No binding analysis at the point of codegen." ) - bindingsMap.exportedSymbols.foreach { case (_, resolvedNames) => - val resolvedModules = resolvedNames.collect { - case ResolvedModule(module) => module - } - resolvedModules.foreach { resolvedModule => - scopeBuilder.addExport( - new ImportExportScope(resolvedModule.unsafeAsModule()) - ) - } + bindingsMap.getDirectlyExportedModules.foreach { exportedMod => + val exportedRuntimeMod = exportedMod.module.module.unsafeAsModule() + scopeBuilder.addExport( + new ImportExportScope(exportedRuntimeMod) + ) } val importDefs = module.imports @@ -214,7 +210,7 @@ class IrToTruffle( case _: BindingsMap.ResolvedType => case _: BindingsMap.ResolvedConstructor => case _: BindingsMap.ResolvedModuleMethod => - case _: BindingsMap.ResolvedExtensionMethod => + case _: BindingsMap.ResolvedExtensionMethod => case _: BindingsMap.ResolvedConversionMethod => case ResolvedModule(module) => val mod = module From a8d8face447966ebb9272e602443bf753b444df9 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 9 Jul 2024 13:37:53 +0200 Subject: [PATCH 094/111] Fix creation of nested submodule in ProjectUtils --- .../src/main/java/org/enso/test/utils/ProjectUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java b/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java index 5907edcd1487..f5fdfcc95a96 100644 --- a/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java +++ b/lib/java/test-utils/src/main/java/org/enso/test/utils/ProjectUtils.java @@ -66,7 +66,7 @@ public static void createProject(String projName, Set modules, Pat var srcDir = Files.createDirectory(projDir.resolve("src")); assert srcDir.toFile().exists(); for (var module : modules) { - var relativePath = String.join(File.pathSeparator, module.name().pathAsJava()); + var relativePath = String.join(File.separator, module.name().pathAsJava()); var modDirPath = srcDir.resolve(relativePath); Files.createDirectories(modDirPath); var modPath = modDirPath.resolve(module.name().item() + ".enso"); From 99bb5864c301a7e4f10f24808e2a74a2ec534e24 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 9 Jul 2024 13:38:14 +0200 Subject: [PATCH 095/111] Update docs of benchmarks - debugging --- docs/infrastructure/benchmarks.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/infrastructure/benchmarks.md b/docs/infrastructure/benchmarks.md index 5d6decac8678..4b63843e95e3 100644 --- a/docs/infrastructure/benchmarks.md +++ b/docs/infrastructure/benchmarks.md @@ -55,6 +55,16 @@ to 0, and to run `withDebug` command like this: withDebug --debugger benchOnly -- ``` +Another option that does not require changing the source code is to run +something like + +``` +sbt:runtime-benchmarks> run -w 1 -i 1 -f 1 -jvmArgs -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:8000 org.enso.compiler.benchmarks.module.ImportStandardLibrariesBenchmark.importStandardLibraries +``` + +This command will run the `importStandardLibraries` benchmark in fork waiting +for the debugger to attach. + ## Standard library benchmarks Unlike the Engine micro benchmarks, these benchmarks are written entirely in From 194a4aa392537f7917571dcbd999033d1039c912 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 9 Jul 2024 17:32:37 +0200 Subject: [PATCH 096/111] Workaround for non-inserting synthetic imports in Database.Main --- distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso index a818bbebfa3a..12fbb0059563 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso @@ -1,4 +1,5 @@ export project.Column_Description.Column_Description +import project.Connection export project.Connection.Client_Certificate.Client_Certificate export project.Connection.Connection_Options.Connection_Options export project.Connection.Credentials.Credentials From 54e8b0a4f750bfb283273178c6ce4c367a6c66fc Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 10:28:10 +0200 Subject: [PATCH 097/111] Test importing a re-exported symbol --- .../test/imports/ImportSymbolsTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java index ead865cc3b46..0d71f8463d27 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java @@ -2,15 +2,19 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.fail; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Set; +import org.enso.compiler.data.BindingsMap.ResolvedType; import org.enso.pkg.QualifiedName; import org.enso.polyglot.PolyglotContext; import org.enso.polyglot.RuntimeOptions; import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ModuleUtils; import org.enso.test.utils.ProjectUtils; import org.enso.test.utils.SourceModule; import org.graalvm.polyglot.PolyglotException; @@ -18,6 +22,8 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; + + public class ImportSymbolsTest { @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @@ -90,4 +96,41 @@ public void importAllFromTypeDoesNotImportTypeItself() throws IOException { } } } + + @Test + public void importEntityFromModuleThatExportsItFromOtherModule() throws IOException { + var aMod = new SourceModule( + QualifiedName.fromString("A_Module"), + """ + type A_Type + """); + var bMod = new SourceModule( + QualifiedName.fromString("B_Module"), + """ + export project.A_Module.A_Type + """); + var mainMod = new SourceModule( + QualifiedName.fromString("Main"), + """ + import project.B_Module.A_Type + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, bMod, mainMod), projDir); + try (var ctx = ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + var mainModResolvedImps = ModuleUtils.getResolvedImports(ctx, "local.Proj.Main"); + assertThat(mainModResolvedImps.size(), is(1)); + assertThat( + "There should be only one target of Main's resolved import", + mainModResolvedImps.get(0).targets().size(), + is(1)); + assertThat( + "Resolved import target of Main should point to A_Type", + mainModResolvedImps.get(0).targets().apply(0), + is(instanceOf(ResolvedType.class))); + } + } } From 2d332eef5335a7bce55a9edcec6ead208430bf87 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 12:44:17 +0200 Subject: [PATCH 098/111] #10504: Ignore failing test of importing relative symbols --- .../org/enso/interpreter/test/imports/ImportSymbolsTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java index 0d71f8463d27..bd1318849030 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java @@ -18,6 +18,7 @@ import org.enso.test.utils.ProjectUtils; import org.enso.test.utils.SourceModule; import org.graalvm.polyglot.PolyglotException; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -97,6 +98,8 @@ public void importAllFromTypeDoesNotImportTypeItself() throws IOException { } } + // TODO: Tracked by https://github.com/enso-org/enso/issues/10504 + @Ignore @Test public void importEntityFromModuleThatExportsItFromOtherModule() throws IOException { var aMod = new SourceModule( From c8e1b9116aed2a28a65678079f72aea2d54f22c2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 12:48:33 +0200 Subject: [PATCH 099/111] Add ExportResolutionOrderingTest --- .../exports/ExportResolutionOrderingTest.java | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java new file mode 100644 index 000000000000..b04640779048 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java @@ -0,0 +1,182 @@ +package org.enso.interpreter.test.exports; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import org.enso.compiler.phase.exports.ExportsResolution; +import org.enso.interpreter.runtime.Module; +import org.enso.pkg.QualifiedName; +import org.enso.polyglot.PolyglotContext; +import org.enso.polyglot.RuntimeOptions; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ModuleUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.graalvm.polyglot.Context; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import scala.jdk.javaapi.CollectionConverters; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.contains; + + +/** + * Tests ordering of modules from {@link org.enso.compiler.phase.exports.ExportsResolution#runSort(scala.collection.immutable.List)}. + * Some tests are already in {@link org.enso.compiler.test.semantic.ImportExportTest}, but there are some + * limitations, like no ability to create (and test) ordering of synthetic modules. + */ +public class ExportResolutionOrderingTest { + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void testOrderingWithSubmoduleOfSyntheticModule() throws IOException { + var aMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.A_Module"), """ + type A_Type + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + export project.Synthetic_Module.A_Module.A_Type + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + + try (var ctx = createContext(projDir)) { + compile(ctx); + var mainRuntimeMod = getLoadedModule(ctx, "local.Proj.Main"); + var aRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module.A_Module"); + var syntheticRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module"); + var sortedModules = runExportsResolutionSort( + List.of(mainRuntimeMod, aRuntimeMod, syntheticRuntimeMod), + ctx + ); + assertThat( + "Export relations should be: mainMod --> syntheticMod --> aMod", + sortedModules, + contains( + aRuntimeMod, + syntheticRuntimeMod, + mainRuntimeMod + ) + ); + } + } + + @Test + public void testOrderingWithTwoSubmodulesOfSyntheticModule() throws IOException { + var aMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.A_Module"), """ + type A_Type + """); + var bMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.B_Module"), """ + type B_Type + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + export project.Synthetic_Module.A_Module.A_Type + export project.Synthetic_Module.B_Module.B_Type + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, bMod, mainMod), projDir); + + try (var ctx = createContext(projDir)) { + compile(ctx); + var mainRuntimeMod = getLoadedModule(ctx, "local.Proj.Main"); + var aRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module.A_Module"); + var bRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module.B_Module"); + var syntheticRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module"); + var sortedModules = runExportsResolutionSort( + List.of(mainRuntimeMod, aRuntimeMod, bRuntimeMod, syntheticRuntimeMod), + ctx + ); + assertThat( + "Export relations should be: mainMod --> syntheticMod --> aMod; mainMod --> syntheticMod --> bMod", + sortedModules, + anyOf( + contains( + bRuntimeMod, + aRuntimeMod, + syntheticRuntimeMod, + mainRuntimeMod + ), + contains( + aRuntimeMod, + bRuntimeMod, + syntheticRuntimeMod, + mainRuntimeMod + ) + ) + ); + } + } + + // TODO: Should multilevel synthetic modules be supported? + @Ignore + @Test + public void testOrderingWithTwoSyntheticModules() throws IOException { + var aMod = new SourceModule(QualifiedName.fromString("Syn_1.Syn_2.A_Module"), """ + type A_Type + """); + var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + export project.Syn_1.Syn_2.A_Module.A_Type + """); + var projDir = tempFolder.newFolder().toPath(); + ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); + + try (var ctx = createContext(projDir)) { + compile(ctx); + var mainRuntimeMod = getLoadedModule(ctx, "local.Proj.Main"); + var aRuntimeMod = getLoadedModule(ctx, "local.Proj.Syn_1.Syn_2.A_Module"); + var syn1RuntimeMod = getLoadedModule(ctx, "local.Proj.Syn_1"); + var syn2RuntimeMod = getLoadedModule(ctx, "local.Proj.Syn_1.Syn_2"); + var sortedModules = runExportsResolutionSort( + List.of(mainRuntimeMod, aRuntimeMod, syn1RuntimeMod, syn2RuntimeMod), + ctx + ); + var sortedModNames = sortedModules.stream().map(m -> m.getName().toString()).toList(); + assertThat( + "Export relations should be: mainMod --> syn1Mod --> syn2Mod --> aMod, but was: " + sortedModNames, + sortedModules, + contains( + aRuntimeMod, + syn2RuntimeMod, + syn1RuntimeMod, + mainRuntimeMod + ) + ); + } + } + + private static Context createContext(Path projDir) { + return ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build(); + } + + private static void compile(Context ctx) { + var polyCtx = new PolyglotContext(ctx); + polyCtx.getTopScope().compile(true); + } + + private static Module getLoadedModule(Context ctx, String modName) { + var mod = ModuleUtils.getLoadedModule(ctx, modName); + assert mod != null; + return mod; + } + + private static List runExportsResolutionSort(List modules, Context ctx) { + var ensoCtx = ContextUtils.leakContext(ctx); + var compilerCtx = ensoCtx.getCompiler().context(); + var exportsResolution = new ExportsResolution(compilerCtx); + var compilerModules = modules.stream().map(Module::asCompilerModule).toList(); + var sortedCompilerModules = exportsResolution.runSort( + CollectionConverters.asScala(compilerModules).toList() + ); + return CollectionConverters.asJava(sortedCompilerModules) + .stream() + .map(Module::fromCompilerModule) + .toList(); + } +} From 29def8b4d5d366f80341e6bb3954451a33ef1ed8 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 12:50:14 +0200 Subject: [PATCH 100/111] #10505: Add comment to test why it is ignored --- .../interpreter/test/exports/ExportResolutionOrderingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java index b04640779048..198d6dd3dfc4 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java @@ -112,7 +112,7 @@ public void testOrderingWithTwoSubmodulesOfSyntheticModule() throws IOException } } - // TODO: Should multilevel synthetic modules be supported? + // TODO: Tracked by https://github.com/enso-org/enso/issues/10505 @Ignore @Test public void testOrderingWithTwoSyntheticModules() throws IOException { From 97670a3deba7197b370e3217bd3528ea0ab100b5 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 12:54:05 +0200 Subject: [PATCH 101/111] Add helper method to ModuleUtils --- .../java/org/enso/test/utils/ModuleUtils.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java b/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java index 0d330c99b335..2d4e065fb5d2 100644 --- a/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java +++ b/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java @@ -39,6 +39,22 @@ public static List getDefinedEntities(Context ctx, String modName return CollectionConverters.asJava(mod.getBindingsMap().definedEntities()); } + /** + * Returns the loaded module with the given name, or null if no such module exist. + * @param modName Fully qualified name of the module + * @return module with the given name, or null if no such module exist + */ + public static org.enso.interpreter.runtime.Module getLoadedModule(Context ctx, String modName) { + assert modName.contains(".") : "Module name must be fully qualified"; + var ensoCtx = ContextUtils.leakContext(ctx); + var loadedModuleOpt = ensoCtx.getPackageRepository().getLoadedModule(modName); + if (loadedModuleOpt.isDefined()) { + return org.enso.interpreter.runtime.Module.fromCompilerModule(loadedModuleOpt.get()); + } else { + return null; + } + } + private static Map> getExportedSymbols(Module module) { var bindings = new HashMap>(); var bindingsScala = module.getBindingsMap().exportedSymbols(); From 5c0c622a317f29bbf1a5001e32b4f1ccb9149688 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 12:54:40 +0200 Subject: [PATCH 102/111] Use specific exports in Deep_Export test --- test/Base_Tests/src/Semantic/Deep_Export/Internal_1.enso | 4 +--- test/Base_Tests/src/Semantic/Deep_Export/Internal_4.enso | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/test/Base_Tests/src/Semantic/Deep_Export/Internal_1.enso b/test/Base_Tests/src/Semantic/Deep_Export/Internal_1.enso index 6978b5430e5a..4503700b6959 100644 --- a/test/Base_Tests/src/Semantic/Deep_Export/Internal_1.enso +++ b/test/Base_Tests/src/Semantic/Deep_Export/Internal_1.enso @@ -1,3 +1 @@ -from project.Semantic.Deep_Export.Internal_2 import const - -from project.Semantic.Deep_Export.Internal_2 export const +export project.Semantic.Deep_Export.Internal_2.const diff --git a/test/Base_Tests/src/Semantic/Deep_Export/Internal_4.enso b/test/Base_Tests/src/Semantic/Deep_Export/Internal_4.enso index 97fb8c20fc42..01f5170c7bb2 100644 --- a/test/Base_Tests/src/Semantic/Deep_Export/Internal_4.enso +++ b/test/Base_Tests/src/Semantic/Deep_Export/Internal_4.enso @@ -1,3 +1 @@ -import project.Semantic.Deep_Export.Internal_5 - -from project.Semantic.Deep_Export.Internal_5 export const +export project.Semantic.Deep_Export.Internal_5.const From bfb6d56272e1c4985a51a74c3b239612158e8398 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 12:55:56 +0200 Subject: [PATCH 103/111] #10504: Ignore Deep_Export test --- test/Base_Tests/src/Main.enso | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/Base_Tests/src/Main.enso b/test/Base_Tests/src/Main.enso index 80e6ef962052..b8d04f2afd1d 100644 --- a/test/Base_Tests/src/Main.enso +++ b/test/Base_Tests/src/Main.enso @@ -5,7 +5,6 @@ from Standard.Test import all import project.Semantic.Any_Spec import project.Semantic.Case_Spec import project.Semantic.Conversion_Spec -import project.Semantic.Deep_Export.Spec as Deep_Export_Spec import project.Semantic.Default_Args_Spec import project.Semantic.Error_Spec import project.Semantic.Import_Loop.Spec as Import_Loop_Spec @@ -107,7 +106,6 @@ main filter=Nothing = Case_Spec.add_specs suite_builder Conversion_Spec.add_specs suite_builder Default_Args_Spec.add_specs suite_builder - Deep_Export_Spec.add_specs suite_builder Error_Spec.add_specs suite_builder Environment_Spec.add_specs suite_builder File_Spec.add_specs suite_builder From 7654182c3c0a3b4f488cb084d251749b6eb7d0ec Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 17:19:16 +0200 Subject: [PATCH 104/111] Fix ImportAndFQNConsistencyTest - use BindingsMap.exportedSymbols there. Instead of Export IR directly. --- .../ImportsAndFQNConsistencyTest.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ImportsAndFQNConsistencyTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ImportsAndFQNConsistencyTest.java index 0f35f9be46fe..dbd08eb1b4e5 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ImportsAndFQNConsistencyTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ImportsAndFQNConsistencyTest.java @@ -11,8 +11,10 @@ import java.util.stream.Collectors; import org.enso.common.LanguageInfo; import org.enso.compiler.core.ir.module.scope.Definition; -import org.enso.compiler.core.ir.module.scope.Export; import org.enso.compiler.core.ir.module.scope.definition.Method; +import org.enso.compiler.data.BindingsMap.ResolvedConstructor; +import org.enso.compiler.data.BindingsMap.ResolvedModule; +import org.enso.compiler.data.BindingsMap.ResolvedType; import org.enso.interpreter.runtime.EnsoContext; import org.enso.polyglot.RuntimeOptions; import org.enso.test.utils.ContextUtils; @@ -27,6 +29,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.junit.runners.model.Statement; +import scala.jdk.javaapi.CollectionConverters; /** * If a symbol is importable by an import statement, then it should also be accessible via FQN @@ -218,16 +221,21 @@ private static List gatherExportedSymbols(EnsoContext ensoCtx, List gatherExportedSymbols(String moduleName, EnsoContext ensoCtx) { var mod = ensoCtx.getPackageRepository().getLoadedModule(moduleName); assertThat(mod.isDefined(), is(true)); - var exports = mod.get().getIr().exports(); - assertThat(exports.size(), greaterThan(1)); + var bmExpSymbols = CollectionConverters.asJava(mod.get().getBindingsMap().exportedSymbols()); List exportedSymbols = new ArrayList<>(); - exports.foreach( - export -> { - if (export instanceof Export.Module moduleExport) { - exportedSymbols.add(moduleExport.name().name()); - } - return null; - }); + for (var entry : bmExpSymbols.entrySet()) { + var resolvedNames = entry.getValue(); + // We are not interested in exported symbols that resolve to multiple targets. + // We are interested only in ResolvedModule, ResolvedType, and ResolvedConstructor. + if (resolvedNames.size() == 1) { + var exportSymTarget = resolvedNames.apply(0); + if (exportSymTarget instanceof ResolvedType + || exportSymTarget instanceof ResolvedModule + || exportSymTarget instanceof ResolvedConstructor) { + exportedSymbols.add(exportSymTarget.qualifiedName().toString()); + } + } + } return exportedSymbols; } From add8c78d8801aa45b4a58c7a537553bc3e68a4d6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 17:19:43 +0200 Subject: [PATCH 105/111] fmt --- .../pass/analyse/BindingAnalysis.scala | 4 +- .../pass/analyse/ImportSymbolAnalysis.scala | 5 +- .../phase/exports/ExportsResolution.scala | 8 +- .../exports/ExportResolutionOrderingTest.java | 116 ++++++++---------- .../test/imports/ImportSymbolsTest.java | 28 ++--- .../pass/analyse/BindingAnalysisTest.scala | 2 +- .../test/semantic/ImportExportTest.scala | 4 +- .../java/org/enso/test/utils/ModuleUtils.java | 1 + 8 files changed, 83 insertions(+), 85 deletions(-) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala index d1546bef3520..053ddede579f 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala @@ -10,8 +10,8 @@ import org.enso.compiler.core.ir.MetadataStorage.MetadataPair import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.{ ConversionMethod, - ModuleMethod, - ExtensionMethod + ExtensionMethod, + ModuleMethod } import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.desugar.{ diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala index 231c996aca62..409227f79214 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/ImportSymbolAnalysis.scala @@ -111,7 +111,10 @@ case object ImportSymbolAnalysis extends IRPass { method.name ) Some(err) - case BindingsMap.ResolvedExtensionMethod(module, staticMethod) => + case BindingsMap.ResolvedExtensionMethod( + module, + staticMethod + ) => val err = createImportFromMethodError( imp, module.getName.createChild(staticMethod.tpName).toString, diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala index 540505db4198..6b28d135766d 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/phase/exports/ExportsResolution.scala @@ -6,11 +6,11 @@ import org.enso.compiler.data.BindingsMap.{ ExportedModule, ImportTarget, ResolvedConversionMethod, + ResolvedExtensionMethod, ResolvedImport, ResolvedModule, ResolvedModuleMethod, - ResolvedName, - ResolvedExtensionMethod + ResolvedName } import org.enso.compiler.context.CompilerContext import org.enso.compiler.context.CompilerContext.Module @@ -194,8 +194,8 @@ class ExportsResolution(private val context: CompilerContext) { if (resolvedNames.size > 1) { val allStaticOrModuleMethods = resolvedNames.forall { case _: ResolvedExtensionMethod => true - case _: ResolvedModuleMethod => true - case _ => false + case _: ResolvedModuleMethod => true + case _ => false } val allConversionMethods = resolvedNames.forall(_.isInstanceOf[ResolvedConversionMethod]) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java index 198d6dd3dfc4..d5d93dddc880 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/exports/ExportResolutionOrderingTest.java @@ -1,5 +1,9 @@ package org.enso.interpreter.test.exports; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.contains; + import java.io.IOException; import java.nio.file.Path; import java.util.List; @@ -20,26 +24,27 @@ import org.junit.rules.TemporaryFolder; import scala.jdk.javaapi.CollectionConverters; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.anyOf; -import static org.hamcrest.Matchers.contains; - - /** - * Tests ordering of modules from {@link org.enso.compiler.phase.exports.ExportsResolution#runSort(scala.collection.immutable.List)}. - * Some tests are already in {@link org.enso.compiler.test.semantic.ImportExportTest}, but there are some + * Tests ordering of modules from {@link + * org.enso.compiler.phase.exports.ExportsResolution#runSort(scala.collection.immutable.List)}. Some + * tests are already in {@link org.enso.compiler.test.semantic.ImportExportTest}, but there are some * limitations, like no ability to create (and test) ordering of synthetic modules. */ public class ExportResolutionOrderingTest { - @Rule - public TemporaryFolder tempFolder = new TemporaryFolder(); + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @Test public void testOrderingWithSubmoduleOfSyntheticModule() throws IOException { - var aMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.A_Module"), """ + var aMod = + new SourceModule( + QualifiedName.fromString("Synthetic_Module.A_Module"), + """ type A_Type """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ export project.Synthetic_Module.A_Module.A_Type """); var projDir = tempFolder.newFolder().toPath(); @@ -50,31 +55,33 @@ public void testOrderingWithSubmoduleOfSyntheticModule() throws IOException { var mainRuntimeMod = getLoadedModule(ctx, "local.Proj.Main"); var aRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module.A_Module"); var syntheticRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module"); - var sortedModules = runExportsResolutionSort( - List.of(mainRuntimeMod, aRuntimeMod, syntheticRuntimeMod), - ctx - ); + var sortedModules = + runExportsResolutionSort(List.of(mainRuntimeMod, aRuntimeMod, syntheticRuntimeMod), ctx); assertThat( "Export relations should be: mainMod --> syntheticMod --> aMod", sortedModules, - contains( - aRuntimeMod, - syntheticRuntimeMod, - mainRuntimeMod - ) - ); + contains(aRuntimeMod, syntheticRuntimeMod, mainRuntimeMod)); } } @Test public void testOrderingWithTwoSubmodulesOfSyntheticModule() throws IOException { - var aMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.A_Module"), """ + var aMod = + new SourceModule( + QualifiedName.fromString("Synthetic_Module.A_Module"), + """ type A_Type """); - var bMod = new SourceModule(QualifiedName.fromString("Synthetic_Module.B_Module"), """ + var bMod = + new SourceModule( + QualifiedName.fromString("Synthetic_Module.B_Module"), + """ type B_Type """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ export project.Synthetic_Module.A_Module.A_Type export project.Synthetic_Module.B_Module.B_Type """); @@ -87,28 +94,16 @@ public void testOrderingWithTwoSubmodulesOfSyntheticModule() throws IOException var aRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module.A_Module"); var bRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module.B_Module"); var syntheticRuntimeMod = getLoadedModule(ctx, "local.Proj.Synthetic_Module"); - var sortedModules = runExportsResolutionSort( - List.of(mainRuntimeMod, aRuntimeMod, bRuntimeMod, syntheticRuntimeMod), - ctx - ); + var sortedModules = + runExportsResolutionSort( + List.of(mainRuntimeMod, aRuntimeMod, bRuntimeMod, syntheticRuntimeMod), ctx); assertThat( - "Export relations should be: mainMod --> syntheticMod --> aMod; mainMod --> syntheticMod --> bMod", + "Export relations should be: mainMod --> syntheticMod --> aMod; mainMod --> syntheticMod" + + " --> bMod", sortedModules, anyOf( - contains( - bRuntimeMod, - aRuntimeMod, - syntheticRuntimeMod, - mainRuntimeMod - ), - contains( - aRuntimeMod, - bRuntimeMod, - syntheticRuntimeMod, - mainRuntimeMod - ) - ) - ); + contains(bRuntimeMod, aRuntimeMod, syntheticRuntimeMod, mainRuntimeMod), + contains(aRuntimeMod, bRuntimeMod, syntheticRuntimeMod, mainRuntimeMod))); } } @@ -116,10 +111,15 @@ public void testOrderingWithTwoSubmodulesOfSyntheticModule() throws IOException @Ignore @Test public void testOrderingWithTwoSyntheticModules() throws IOException { - var aMod = new SourceModule(QualifiedName.fromString("Syn_1.Syn_2.A_Module"), """ + var aMod = + new SourceModule( + QualifiedName.fromString("Syn_1.Syn_2.A_Module"), """ type A_Type """); - var mainMod = new SourceModule(QualifiedName.fromString("Main"), """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ export project.Syn_1.Syn_2.A_Module.A_Type """); var projDir = tempFolder.newFolder().toPath(); @@ -131,21 +131,15 @@ public void testOrderingWithTwoSyntheticModules() throws IOException { var aRuntimeMod = getLoadedModule(ctx, "local.Proj.Syn_1.Syn_2.A_Module"); var syn1RuntimeMod = getLoadedModule(ctx, "local.Proj.Syn_1"); var syn2RuntimeMod = getLoadedModule(ctx, "local.Proj.Syn_1.Syn_2"); - var sortedModules = runExportsResolutionSort( - List.of(mainRuntimeMod, aRuntimeMod, syn1RuntimeMod, syn2RuntimeMod), - ctx - ); + var sortedModules = + runExportsResolutionSort( + List.of(mainRuntimeMod, aRuntimeMod, syn1RuntimeMod, syn2RuntimeMod), ctx); var sortedModNames = sortedModules.stream().map(m -> m.getName().toString()).toList(); assertThat( - "Export relations should be: mainMod --> syn1Mod --> syn2Mod --> aMod, but was: " + sortedModNames, + "Export relations should be: mainMod --> syn1Mod --> syn2Mod --> aMod, but was: " + + sortedModNames, sortedModules, - contains( - aRuntimeMod, - syn2RuntimeMod, - syn1RuntimeMod, - mainRuntimeMod - ) - ); + contains(aRuntimeMod, syn2RuntimeMod, syn1RuntimeMod, mainRuntimeMod)); } } @@ -171,11 +165,9 @@ private static List runExportsResolutionSort(List modules, Conte var compilerCtx = ensoCtx.getCompiler().context(); var exportsResolution = new ExportsResolution(compilerCtx); var compilerModules = modules.stream().map(Module::asCompilerModule).toList(); - var sortedCompilerModules = exportsResolution.runSort( - CollectionConverters.asScala(compilerModules).toList() - ); - return CollectionConverters.asJava(sortedCompilerModules) - .stream() + var sortedCompilerModules = + exportsResolution.runSort(CollectionConverters.asScala(compilerModules).toList()); + return CollectionConverters.asJava(sortedCompilerModules).stream() .map(Module::fromCompilerModule) .toList(); } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java index bd1318849030..57666230e59a 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/imports/ImportSymbolsTest.java @@ -23,8 +23,6 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; - - public class ImportSymbolsTest { @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @@ -102,26 +100,28 @@ public void importAllFromTypeDoesNotImportTypeItself() throws IOException { @Ignore @Test public void importEntityFromModuleThatExportsItFromOtherModule() throws IOException { - var aMod = new SourceModule( - QualifiedName.fromString("A_Module"), - """ + var aMod = + new SourceModule(QualifiedName.fromString("A_Module"), """ type A_Type """); - var bMod = new SourceModule( - QualifiedName.fromString("B_Module"), - """ + var bMod = + new SourceModule( + QualifiedName.fromString("B_Module"), + """ export project.A_Module.A_Type """); - var mainMod = new SourceModule( - QualifiedName.fromString("Main"), - """ + var mainMod = + new SourceModule( + QualifiedName.fromString("Main"), + """ import project.B_Module.A_Type """); var projDir = tempFolder.newFolder().toPath(); ProjectUtils.createProject("Proj", Set.of(aMod, bMod, mainMod), projDir); - try (var ctx = ContextUtils.defaultContextBuilder() - .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) - .build()) { + try (var ctx = + ContextUtils.defaultContextBuilder() + .option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString()) + .build()) { var polyCtx = new PolyglotContext(ctx); polyCtx.getTopScope().compile(true); var mainModResolvedImps = ModuleUtils.getResolvedImports(ctx, "local.Proj.Main"); diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala index 330431367179..8342d7245f75 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala @@ -9,10 +9,10 @@ import org.enso.compiler.data.BindingsMap.{ Argument, Cons, ConversionMethod, + ExtensionMethod, ModuleMethod, PolyglotSymbol, ResolvedExtensionMethod, - ExtensionMethod, Type } import org.enso.compiler.pass.analyse.BindingAnalysis diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala index 0661458e7fc5..01fe22d786f0 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/semantic/ImportExportTest.scala @@ -351,7 +351,9 @@ class ImportExportTest val bindingsMap = mainIr.unwrapBindingMap bindingsMap.resolvedImports.size shouldBe 1 val resolvedImport = bindingsMap.resolvedImports.head - resolvedImport.targets.head shouldBe a[BindingsMap.ResolvedExtensionMethod] + resolvedImport.targets.head shouldBe a[ + BindingsMap.ResolvedExtensionMethod + ] resolvedImport.targets.head .asInstanceOf[BindingsMap.ResolvedExtensionMethod] .staticMethod diff --git a/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java b/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java index 2d4e065fb5d2..e715f111a08f 100644 --- a/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java +++ b/lib/java/test-utils/src/main/java/org/enso/test/utils/ModuleUtils.java @@ -41,6 +41,7 @@ public static List getDefinedEntities(Context ctx, String modName /** * Returns the loaded module with the given name, or null if no such module exist. + * * @param modName Fully qualified name of the module * @return module with the given name, or null if no such module exist */ From fd4f2ab470deec40ca1643410e7b60f3391c7b6f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 17:22:46 +0200 Subject: [PATCH 106/111] Fix error messages in ErrorCompilerTest --- .../src/test/java/org/enso/compiler/ErrorCompilerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java index b46dbe084daf..1878b045d8ac 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java @@ -408,19 +408,19 @@ public void malformedExport8() throws Exception { @Test public void malformedExport9() throws Exception { var ir = parse("from export all"); - assertSingleSyntaxError(ir, invalidExport("Expected tokens."), null, 4, 4); + assertSingleSyntaxError(ir, invalidExport("`all` not allowed in `export` statement"), null, 0, 15); } @Test public void malformedExport10() throws Exception { var ir = parse("from Foo export all hiding"); - assertSingleSyntaxError(ir, invalidExport("Expected tokens."), null, 26, 26); + assertSingleSyntaxError(ir, invalidExport("`hiding` not allowed in `export` statement"), null, 0, 26); } @Test public void malformedExport11() throws Exception { var ir = parse("from Foo export all hiding X.Y"); - assertSingleSyntaxError(ir, invalidExport("Expected identifier."), null, 27, 30); + assertSingleSyntaxError(ir, invalidExport("`hiding` not allowed in `export` statement"), null, 0, 30); } @Test From 9eabbcd28910c64dcc5adba4fd844eb88f334b46 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 17:23:02 +0200 Subject: [PATCH 107/111] Fix error messages in ErrorCompilerTest --- .../test/java/org/enso/compiler/ErrorCompilerTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java index 1878b045d8ac..c3dacedf5653 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ErrorCompilerTest.java @@ -408,19 +408,22 @@ public void malformedExport8() throws Exception { @Test public void malformedExport9() throws Exception { var ir = parse("from export all"); - assertSingleSyntaxError(ir, invalidExport("`all` not allowed in `export` statement"), null, 0, 15); + assertSingleSyntaxError( + ir, invalidExport("`all` not allowed in `export` statement"), null, 0, 15); } @Test public void malformedExport10() throws Exception { var ir = parse("from Foo export all hiding"); - assertSingleSyntaxError(ir, invalidExport("`hiding` not allowed in `export` statement"), null, 0, 26); + assertSingleSyntaxError( + ir, invalidExport("`hiding` not allowed in `export` statement"), null, 0, 26); } @Test public void malformedExport11() throws Exception { var ir = parse("from Foo export all hiding X.Y"); - assertSingleSyntaxError(ir, invalidExport("`hiding` not allowed in `export` statement"), null, 0, 30); + assertSingleSyntaxError( + ir, invalidExport("`hiding` not allowed in `export` statement"), null, 0, 30); } @Test From 8a7dcc40e1b49990c5c6c4ab1fea19fa10f26173 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 18:28:55 +0200 Subject: [PATCH 108/111] Skip ExportSymbol analysis for non-compiled modules --- .../enso/compiler/phase/exports/ExportSymbolAnalysis.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java index 4e9b86f59f72..a6a59b7c44a4 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/exports/ExportSymbolAnalysis.java @@ -188,9 +188,15 @@ private static ResolvedType getResolvedTypeFromModule( */ private static List analyseSymbolsFromModule( CompilerContext.Module module, List symbols) { + var bindingsMap = module.getBindingsMap(); + if (bindingsMap == null) { + // This can happen if the module was not yet compiled. Which may happen. + // In that case, just skip the check. + return List.of(); + } var errors = new ArrayList(); for (var symbol : symbols) { - var resolvedNamesOpt = module.getBindingsMap().exportedSymbols().get(symbol.name()); + var resolvedNamesOpt = bindingsMap.exportedSymbols().get(symbol.name()); if (resolvedNamesOpt.isEmpty()) { errors.add( createSymbolDoesNotExistError(symbol, symbol.name(), module.getName().toString())); From 5b1fd5cf1e0f115bb0b637edbae81198277adbca Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 10 Jul 2024 18:33:54 +0200 Subject: [PATCH 109/111] Update docs --- docs/syntax/imports.md | 3 +-- .../java/org/enso/compiler/phase/ImportResolverAlgorithm.java | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/syntax/imports.md b/docs/syntax/imports.md index 0992260904a8..f768dde9bb60 100644 --- a/docs/syntax/imports.md +++ b/docs/syntax/imports.md @@ -33,8 +33,7 @@ code from modules. ## Qualified Names In the following text, **entity** shall denote a module, a method (instance, -static, extension, conversion, foreign), type, or a type constructor. In other -words, an _entity_ is anything that can be assigned to a variable. +static, extension, conversion or foreign), type, or a type constructor. Both imports and exports require the use of qualified entity names. A qualified name consists of the library namespace (usually organization under which its diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java index 6a9232ace8d0..5644a0bcc5f2 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/phase/ImportResolverAlgorithm.java @@ -228,8 +228,8 @@ private java.util.List tryResolveAsExtensionMethods(Imp } /** - * Tries to resolve the given import as a list of extension methods. Note that it is possible that - * a single symbol resolves to multiple extension methods. + * Tries to resolve the given import as a list of conversion methods. Note that it is possible + * that a single symbol resolves to multiple extension methods. * * @return List of at least one element. null if there are no conversion methods in the imported * module scope. From a4aa2b6945b554e8d4871c4719a89178f0a26df1 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 11 Jul 2024 11:39:23 +0200 Subject: [PATCH 110/111] micro-distribution exports Boolean type --- .../micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso | 1 + 1 file changed, 1 insertion(+) diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index 3241d83db54b..247d1defdcae 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -3,6 +3,7 @@ export project.Panic.Panic export project.Error.Error export project.Any.Any export project.Data.Array.Array +export project.Data.Boolean.Boolean export project.Data.Boolean.Boolean.True export project.Data.Boolean.Boolean.False export project.Data.Text.Text From 4aca14caea7283d868fe8cc5e082624f2c6523e2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 11 Jul 2024 17:37:06 +0200 Subject: [PATCH 111/111] Update RuntimeSuggestionUpdatesTest --- .../RuntimeSuggestionUpdatesTest.scala | 157 +++++++++--------- 1 file changed, 83 insertions(+), 74 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeSuggestionUpdatesTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeSuggestionUpdatesTest.scala index cba002ab1eb7..9e9ca0f85970 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeSuggestionUpdatesTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeSuggestionUpdatesTest.scala @@ -9,6 +9,7 @@ import org.enso.polyglot.ModuleExports import org.enso.polyglot.Suggestion import org.enso.polyglot.data.Tree import org.enso.polyglot.runtime.Runtime.Api +import org.enso.polyglot.runtime.Runtime.Api.SuggestionAction import org.enso.text.editing.model import org.enso.text.editing.model.TextEdit import org.graalvm.polyglot.Context @@ -20,7 +21,6 @@ import java.io.{ByteArrayOutputStream, File} import java.nio.file.{Files, Paths} import java.util.UUID import java.util.logging.Level - import scala.collection.immutable.ListSet @scala.annotation.nowarn("msg=multiarg infix syntax") @@ -1015,9 +1015,12 @@ class RuntimeSuggestionUpdatesTest """from Standard.Base import all | |import Enso_Test.Test.A - |from Enso_Test.Test.A export all |import Enso_Test.Test.A.MyType - |from Enso_Test.Test.A.MyType export all + | + |export Enso_Test.Test.A.MyType # Line 5 + |export Enso_Test.Test.A.fortytwo + |export Enso_Test.Test.A.hello # This line will be removed + |export Enso_Test.Test.A.MyType.MkA | |main = IO.println "Hello World!" |""".stripMargin.linesIterator.mkString("\n") @@ -1073,6 +1076,55 @@ class RuntimeSuggestionUpdatesTest 5 ) should contain theSameElementsAs Seq( Api.Response(requestId, Api.PushContextResponse(contextId)), + Api.Response( + Api.SuggestionsDatabaseModuleUpdateNotification( + module = "Enso_Test.Test.Main", + actions = + Vector(Api.SuggestionsDatabaseAction.Clean("Enso_Test.Test.Main")), + exports = Vector( + Api.ExportsUpdate( + ModuleExports( + "Enso_Test.Test.Main", + ListSet( + ExportedSymbol.Type("Enso_Test.Test.A", "MyType"), + ExportedSymbol.Constructor("Enso_Test.Test.A", "MkA"), + ExportedSymbol.Method("Enso_Test.Test.Main", "main"), + ExportedSymbol.Method("Enso_Test.Test.A", "hello") + ) + ), + Api.ExportsAction.Add() + ) + ), + updates = Tree.Root( + Vector( + Tree.Node( + Api.SuggestionUpdate( + Suggestion.Module("Enso_Test.Test.Main", None), + Api.SuggestionAction.Add() + ), + Vector() + ), + Tree.Node( + Api.SuggestionUpdate( + Suggestion.DefinedMethod( + None, + "Enso_Test.Test.Main", + "main", + List(), + "Enso_Test.Test.Main", + ConstantsGen.ANY, + true, + None, + Seq() + ), + Api.SuggestionAction.Add() + ), + Vector() + ) + ) + ) + ) + ), Api.Response( Api.SuggestionsDatabaseModuleUpdateNotification( module = "Enso_Test.Test.A", @@ -1094,7 +1146,10 @@ class RuntimeSuggestionUpdatesTest Vector( Tree.Node( Api.SuggestionUpdate( - Suggestion.Module("Enso_Test.Test.A", None), + Suggestion.Module( + "Enso_Test.Test.A", + None + ), Api.SuggestionAction.Add() ), Vector() @@ -1105,7 +1160,7 @@ class RuntimeSuggestionUpdatesTest None, "Enso_Test.Test.A", "MyType", - List(), + Seq(), "Enso_Test.Test.A.MyType", Some(ConstantsGen.ANY), None @@ -1121,14 +1176,19 @@ class RuntimeSuggestionUpdatesTest "Enso_Test.Test.A", "MkA", List( - Suggestion - .Argument("a", ConstantsGen.ANY, false, false, None) + Suggestion.Argument( + "a", + ConstantsGen.ANY, + false, + false, + None + ) ), "Enso_Test.Test.A.MyType", None, Seq() ), - Api.SuggestionAction.Add() + SuggestionAction.Add() ), Vector() ), @@ -1138,22 +1198,21 @@ class RuntimeSuggestionUpdatesTest None, "Enso_Test.Test.A", "a", - List( - Suggestion - .Argument( - "self", - "Enso_Test.Test.A.MyType", - false, - false, - None - ) + Seq( + Suggestion.Argument( + "self", + "Enso_Test.Test.A.MyType", + false, + false, + None + ) ), "Enso_Test.Test.A.MyType", ConstantsGen.ANY, None, Seq() ), - Api.SuggestionAction.Add() + SuggestionAction.Add() ), Vector() ), @@ -1203,71 +1262,21 @@ class RuntimeSuggestionUpdatesTest ) ) ), - Api.Response( - Api.SuggestionsDatabaseModuleUpdateNotification( - module = moduleName, - actions = Vector(Api.SuggestionsDatabaseAction.Clean(moduleName)), - exports = Vector( - Api.ExportsUpdate( - ModuleExports( - "Enso_Test.Test.Main", - ListSet( - ExportedSymbol.Type("Enso_Test.Test.A", "MyType"), - ExportedSymbol.Constructor("Enso_Test.Test.A", "MkA"), - ExportedSymbol.Method(moduleName, "main"), - ExportedSymbol.Method("Enso_Test.Test.A", "hello") - ) - ), - Api.ExportsAction.Add() - ) - ), - updates = Tree.Root( - Vector( - Tree.Node( - Api.SuggestionUpdate( - Suggestion.Module( - moduleName, - None - ), - Api.SuggestionAction.Add() - ), - Vector() - ), - Tree.Node( - Api.SuggestionUpdate( - Suggestion.DefinedMethod( - None, - moduleName, - "main", - List(), - "Enso_Test.Test.Main", - ConstantsGen.ANY, - true, - None, - Seq() - ), - Api.SuggestionAction.Add() - ), - Vector() - ) - ) - ) - ) - ), Api.Response(Api.AnalyzeModuleInScopeJobFinished()), context.executionComplete(contextId) ) context.consumeOut shouldEqual List("Hello World!") - // Modify the file + // Modify the file - remove the export of `hello` method + // Remove the line `export Enso_Test.Test.A.hello` context.send( Api.Request( Api.EditFileNotification( mainFile, Seq( TextEdit( - model.Range(model.Position(3, 32), model.Position(3, 32)), - " hiding hello" + model.Range(model.Position(7, 0), model.Position(8, 0)), + "" ) ), execute = true, @@ -1298,14 +1307,14 @@ class RuntimeSuggestionUpdatesTest ) context.consumeOut shouldEqual List("Hello World!") - // Modify the file + // Modify the file - remove all exports context.send( Api.Request( Api.EditFileNotification( mainFile, Seq( TextEdit( - model.Range(model.Position(2, 0), model.Position(7, 0)), + model.Range(model.Position(5, 0), model.Position(8, 0)), "" ) ),