mirror of
https://github.com/enso-org/enso.git
synced 2024-08-16 16:30:33 +03:00
Replace all from ... export all
with explicit exports (#10369)
Replace all exports with explicit exports.
This commit is contained in:
parent
b189ceae47
commit
0f9852aab2
@ -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.
|
||||
|
||||
|
@ -93,14 +93,88 @@ 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.Json.Extensions export all
|
||||
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.Text.Regex export regex
|
||||
from project.Function export all
|
||||
from project.Meta.Enso_Project export enso_project
|
||||
from project.Network.Extensions export all
|
||||
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.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.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
|
||||
|
@ -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
|
||||
@ -9,5 +10,9 @@ 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.Extensions.Upload_Database_Table export all
|
||||
from project.Extensions.Upload_In_Memory_Table export all
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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 all
|
||||
from project.Expression export expr
|
||||
from project.Extensions.Column_Vector_Extensions export all
|
||||
from project.Extensions.Table_Conversions export all
|
||||
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
|
||||
|
@ -12,5 +12,19 @@ export project.Faker.Faker
|
||||
export project.Problems
|
||||
export project.Suite.Suite
|
||||
export project.Test.Test
|
||||
from project.Extensions export all
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -55,6 +55,16 @@ to 0, and to run `withDebug` command like this:
|
||||
withDebug --debugger benchOnly -- <fully qualified benchmark name>
|
||||
```
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -18,18 +18,22 @@ 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)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
## 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 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
|
||||
@ -95,6 +99,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 +140,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 +162,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.
|
||||
|
@ -484,7 +484,6 @@ public class IRDumper {
|
||||
GraphVizNode.Builder.fromIr(exportIr)
|
||||
.addLabelLine("isSynthetic: " + exportModIr.isSynthetic())
|
||||
.addLabelLine("name: " + exportModIr.name().name())
|
||||
.addLabelLine("isAll: " + exportModIr.isAll())
|
||||
.build();
|
||||
addNode(node);
|
||||
}
|
||||
@ -577,28 +576,13 @@ public class IRDumper {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
default -> throw unimpl(firstImpTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,155 +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.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.pass.IRPass;
|
||||
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.
|
||||
*/
|
||||
public final class ExportSymbolAnalysis implements IRPass {
|
||||
public static final ExportSymbolAnalysis INSTANCE = new ExportSymbolAnalysis();
|
||||
private static scala.collection.immutable.List<IRPass> 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<IRPass> precursorPasses() {
|
||||
if (precursorPasses == null) {
|
||||
List<IRPass> passes = List.of(BindingAnalysis$.MODULE$, ImportSymbolAnalysis$.MODULE$);
|
||||
precursorPasses = CollectionConverters.asScala(passes).toList();
|
||||
}
|
||||
return precursorPasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Seq<IRPass> invalidatedPasses() {
|
||||
Object obj = scala.collection.immutable.Nil$.MODULE$;
|
||||
return (scala.collection.immutable.List<IRPass>) obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Module runModule(Module moduleIr, ModuleContext moduleContext) {
|
||||
List<Export> exportErrors = new ArrayList<>();
|
||||
var bindingsMap = (BindingsMap) moduleIr.passData().get(BindingAnalysis$.MODULE$).get();
|
||||
|
||||
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 foundResolvedExp = findResolvedExportForIr(export, bindingsMap);
|
||||
if (foundResolvedExp == 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 -> {
|
||||
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()));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
yield null;
|
||||
}
|
||||
default -> export;
|
||||
});
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@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 BindingsMap.ExportedModule 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();
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: " + exportIr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IR> T updateMetadataInDuplicate(T sourceIr, T copyOfIr) {
|
||||
return IRPass.super.updateMetadataInDuplicate(sourceIr, copyOfIr);
|
||||
}
|
||||
}
|
@ -72,19 +72,24 @@ public final class PrivateModuleAnalysis implements IRPass {
|
||||
.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;
|
||||
});
|
||||
|
||||
@ -103,7 +108,7 @@ public final class PrivateModuleAnalysis implements IRPass {
|
||||
.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();
|
||||
|
@ -2,13 +2,21 @@ package org.enso.compiler.phase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import org.enso.compiler.core.CompilerError;
|
||||
import org.enso.editions.LibraryName;
|
||||
import scala.Tuple2;
|
||||
|
||||
public abstract class ImportResolverAlgorithm<
|
||||
Result, Module, Import, Export, ResolvedType, ResolvedModule> {
|
||||
Result,
|
||||
Module,
|
||||
Import,
|
||||
Export,
|
||||
ResolvedType,
|
||||
ResolvedModule,
|
||||
ResolvedConstructor,
|
||||
ResolvedModuleMethod,
|
||||
ResolvedExtensionMethod,
|
||||
ResolvedConversionMethod> {
|
||||
protected ImportResolverAlgorithm() {}
|
||||
|
||||
protected abstract String nameForImport(Import imp);
|
||||
@ -19,6 +27,14 @@ public abstract class ImportResolverAlgorithm<
|
||||
|
||||
protected abstract String nameForType(ResolvedType e);
|
||||
|
||||
protected abstract String nameForConstructor(ResolvedConstructor cons);
|
||||
|
||||
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
|
||||
*
|
||||
@ -27,20 +43,21 @@ public abstract class ImportResolverAlgorithm<
|
||||
*/
|
||||
protected abstract java.util.List<Export> exportsFor(Module module, String symbol);
|
||||
|
||||
protected abstract boolean isAll(Export ex);
|
||||
|
||||
/**
|
||||
* @return {@code null} or list of named imports
|
||||
*/
|
||||
protected abstract java.util.List<String> onlyNames(Export ex);
|
||||
|
||||
/**
|
||||
* @return {@code null} or list of named imports
|
||||
*/
|
||||
protected abstract java.util.List<String> hiddenNames(Export ex);
|
||||
|
||||
protected abstract java.util.List<ResolvedType> definedEntities(Import name);
|
||||
|
||||
protected abstract java.util.List<ResolvedConstructor> definedConstructors(Import name);
|
||||
|
||||
protected abstract java.util.List<ResolvedModuleMethod> definedModuleMethods(Import imp);
|
||||
|
||||
protected abstract java.util.List<ResolvedExtensionMethod> definedExtensionMethods(Import imp);
|
||||
|
||||
protected abstract java.util.List<ResolvedConversionMethod> definedConversionMethods(Import imp);
|
||||
|
||||
/**
|
||||
* Ensure library is loaded and load a module.
|
||||
*
|
||||
@ -56,13 +73,36 @@ public abstract class ImportResolverAlgorithm<
|
||||
protected abstract Result createResolvedType(
|
||||
Import imp, java.util.List<Export> exp, ResolvedType m);
|
||||
|
||||
protected abstract Result createResolvedConstructor(
|
||||
Import imp, java.util.List<Export> exp, ResolvedConstructor cons);
|
||||
|
||||
protected abstract Result createResolvedModuleMethod(
|
||||
Import imp, java.util.List<Export> exp, ResolvedModuleMethod moduleMethod);
|
||||
|
||||
protected abstract Result createResolvedExtensionMethods(
|
||||
Import imp,
|
||||
java.util.List<Export> exp,
|
||||
java.util.List<ResolvedExtensionMethod> extensionMethods);
|
||||
|
||||
protected abstract Result createResolvedConversionMethods(
|
||||
Import imp,
|
||||
java.util.List<Export> exp,
|
||||
java.util.List<ResolvedConversionMethod> conversionMethods);
|
||||
|
||||
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}.
|
||||
*
|
||||
* <p>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
|
||||
@ -76,61 +116,6 @@ public abstract class ImportResolverAlgorithm<
|
||||
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(
|
||||
@ -141,14 +126,28 @@ public abstract class ImportResolverAlgorithm<
|
||||
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);
|
||||
}
|
||||
var moduleMethod = tryResolveAsModuleMethod(imp);
|
||||
if (moduleMethod != null) {
|
||||
return createResolvedModuleMethod(imp, exp, moduleMethod);
|
||||
}
|
||||
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());
|
||||
}
|
||||
@ -166,6 +165,96 @@ public abstract class ImportResolverAlgorithm<
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<ResolvedExtensionMethod> 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 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.
|
||||
*/
|
||||
private java.util.List<ResolvedConversionMethod> 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);
|
||||
|
@ -3,6 +3,7 @@ package org.enso.compiler.phase;
|
||||
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,12 @@ import org.enso.compiler.core.ir.module.scope.Export;
|
||||
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.ResolvedExtensionMethod;
|
||||
import org.enso.compiler.data.BindingsMap.ResolvedType;
|
||||
import org.enso.editions.LibraryName;
|
||||
|
||||
@ -26,7 +33,11 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm<
|
||||
Import.Module,
|
||||
Export.Module,
|
||||
ResolvedType,
|
||||
CompilerContext.Module
|
||||
CompilerContext.Module,
|
||||
ResolvedConstructor,
|
||||
ResolvedModuleMethod,
|
||||
ResolvedExtensionMethod,
|
||||
ResolvedConversionMethod
|
||||
> {
|
||||
abstract Compiler getCompiler();
|
||||
|
||||
@ -50,6 +61,26 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm<
|
||||
return e.qualifiedName().item();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String nameForConstructor(ResolvedConstructor cons) {
|
||||
return cons.qualifiedName().item();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String nameForModuleMethod(ResolvedModuleMethod resolvedModuleMethod) {
|
||||
return resolvedModuleMethod.methodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String nameForExtensionMethod(ResolvedExtensionMethod resolvedStaticMethod) {
|
||||
return resolvedStaticMethod.methodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String nameForConversionMethod(ResolvedConversionMethod resolvedConversionMethod) {
|
||||
return resolvedConversionMethod.methodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final java.util.List<Export.Module> exportsFor(Module module, String impName) {
|
||||
java.util.List<Export.Module> exp = CollectionConverters.SeqHasAsJava(module.exports()).asJava().stream().map(e -> switch (e) {
|
||||
@ -59,11 +90,6 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm<
|
||||
return exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean isAll(Export.Module ex) {
|
||||
return ex.isAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final java.util.List<String> onlyNames(Export.Module ex) {
|
||||
if (ex.onlyNames().isEmpty()) {
|
||||
@ -73,15 +99,6 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm<
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final java.util.List<String> 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<BindingsMap.ResolvedType> definedEntities(Import.Module name) {
|
||||
var parts = partsForImport(name);
|
||||
@ -94,16 +111,7 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm<
|
||||
}
|
||||
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();
|
||||
@ -117,6 +125,156 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm<
|
||||
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<ResolvedConstructor> 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 java.util.List<ResolvedModuleMethod> 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 java.util.List<ResolvedExtensionMethod> 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.ExtensionMethod extensionMethod) {
|
||||
return extensionMethod.name().equals(modMethodName);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map(entity -> new ResolvedExtensionMethod(new BindingsMap$ModuleReference$Concrete(mod), (BindingsMap.ExtensionMethod) entity))
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
return extensionMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected java.util.List<ResolvedConversionMethod> 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();
|
||||
@ -136,14 +294,52 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm<
|
||||
|
||||
@Override
|
||||
protected final Tuple2<Import, Option<BindingsMap.ResolvedImport>> createResolvedImport(Import.Module imp, java.util.List<Export.Module> exp, CompilerContext.Module m) {
|
||||
scala.Option<org.enso.compiler.data.BindingsMap.ResolvedImport> 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<Import, Option<BindingsMap.ResolvedImport>> createResolvedType(Import.Module imp, java.util.List<Export.Module> exp, BindingsMap.ResolvedType typ) {
|
||||
scala.Option<org.enso.compiler.data.BindingsMap.ResolvedImport> 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<Import, Option<ResolvedImport>> createResolvedConstructor(Import.Module imp,
|
||||
java.util.List<Export.Module> exp, ResolvedConstructor cons) {
|
||||
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<Import, Option<ResolvedImport>> createResolvedModuleMethod(Import.Module imp,
|
||||
java.util.List<Export.Module> exp, ResolvedModuleMethod resolvedModuleMethod) {
|
||||
var resolvedImport = new BindingsMap.ResolvedImport(imp, toScalaList(exp), toScalaList(java.util.List.of(resolvedModuleMethod)));
|
||||
return new Tuple2<>(imp, scala.Some.apply(resolvedImport));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Tuple2<Import, Option<ResolvedImport>> createResolvedExtensionMethods(Import.Module imp,
|
||||
java.util.List<Export.Module> exp, java.util.List<ResolvedExtensionMethod> extensionMethods) {
|
||||
java.util.List<ImportTarget> 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<Import, Option<ResolvedImport>> createResolvedConversionMethods(
|
||||
Import.Module imp, java.util.List<Export.Module> exp,
|
||||
java.util.List<ResolvedConversionMethod> resolvedConversionMethods) {
|
||||
java.util.List<ImportTarget> 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
|
||||
@ -157,6 +353,20 @@ abstract class ImportResolverForIR extends ImportResolverAlgorithm<
|
||||
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 <T> List<T> toScalaList(java.util.List<T> qualifiedConflicts) {
|
||||
return CollectionConverters.ListHasAsScala(qualifiedConflicts).asScala().toList();
|
||||
}
|
||||
|
@ -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<ResolvedName> 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);
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,270 @@
|
||||
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<Export> 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";
|
||||
|
||||
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;
|
||||
String preLastNameFQN;
|
||||
List<Name> 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() - 1)
|
||||
.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() - 1)
|
||||
.collect(Collectors.joining("."));
|
||||
preLastNameFQN =
|
||||
exportNameParts.stream()
|
||||
.map(Name::name)
|
||||
.limit(exportNameParts.size() - 2)
|
||||
.collect(Collectors.joining("."));
|
||||
symbols = List.of(exportNameParts.get(exportNameParts.size() - 1));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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<ImportExport> analyseSymbolsFromModule(
|
||||
CompilerContext.Module module, List<Name> 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<ImportExport>();
|
||||
for (var symbol : symbols) {
|
||||
var resolvedNamesOpt = bindingsMap.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<ImportExport> analyseSymbolsFromType(ResolvedType type, List<Name> symbols) {
|
||||
var errors = new ArrayList<ImportExport>();
|
||||
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<Diagnostic>) Seq$.MODULE$.empty());
|
||||
}
|
||||
}
|
@ -25,15 +25,15 @@ 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,
|
||||
ExportSymbolAnalysis,
|
||||
ExportsResolution
|
||||
}
|
||||
import org.enso.syntax2.Tree
|
||||
|
||||
import java.io.PrintStream
|
||||
@ -479,7 +479,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 = {
|
||||
@ -749,9 +760,7 @@ class Compiler(
|
||||
Export.Module(
|
||||
m,
|
||||
rename = None,
|
||||
isAll = false,
|
||||
onlyNames = None,
|
||||
hiddenNames = None,
|
||||
location = None,
|
||||
isSynthetic = true
|
||||
)
|
||||
|
@ -56,7 +56,6 @@ class Passes(
|
||||
)
|
||||
} else List())
|
||||
++ List(
|
||||
ExportSymbolAnalysis.INSTANCE,
|
||||
ShadowedPatternFields,
|
||||
UnreachableMatchBranches,
|
||||
NestedPatternMatch,
|
||||
|
@ -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)) {
|
||||
@ -143,7 +128,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 +146,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
|
||||
@ -305,49 +290,47 @@ 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
|
||||
* [[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, 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
|
||||
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 ResolvedExtensionMethod(modRef, _) =>
|
||||
ExportedModule(ResolvedModule(modRef), exportAs, symbols)
|
||||
case ResolvedConversionMethod(modRef, _) =>
|
||||
ExportedModule(ResolvedModule(modRef), exportAs, symbols)
|
||||
}
|
||||
} else {
|
||||
SymbolRestriction.Only(
|
||||
Set(
|
||||
SymbolRestriction.AllowedResolution(
|
||||
exp.getSimpleName.name.toLowerCase,
|
||||
Some(mod)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
val rename = if (!exp.isAll) {
|
||||
Some(exp.getSimpleName.name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
ExportedModule(mod, rename, restriction)
|
||||
}
|
||||
}.flatten
|
||||
.flatten
|
||||
.distinct
|
||||
}
|
||||
|
||||
object BindingsMap {
|
||||
@ -376,328 +359,36 @@ 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 module the target being exported.
|
||||
* @param exportedAs the name it is exported as.
|
||||
* @param symbols any symbol restrictions 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,
|
||||
module: ResolvedModule,
|
||||
exportedAs: Option[String],
|
||||
symbols: SymbolRestriction
|
||||
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.
|
||||
*
|
||||
* @return `this` with its module reference made abstract
|
||||
*/
|
||||
def toAbstract: ExportedModule = {
|
||||
this.copy(target = target.toAbstract, symbols = symbols.toAbstract)
|
||||
this.copy(module = module.toAbstract)
|
||||
}
|
||||
|
||||
/** Convert the internal [[ModuleReference]] to a concrete reference.
|
||||
@ -706,10 +397,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))
|
||||
module.toConcrete(moduleMap).map { target =>
|
||||
this.copy(module = target)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -739,20 +428,26 @@ 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)
|
||||
assert(
|
||||
areTargetsConsistent(),
|
||||
"All targets must be either static methods or conversion methods"
|
||||
)
|
||||
|
||||
/** 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.
|
||||
@ -761,7 +456,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.
|
||||
@ -771,6 +471,24 @@ object BindingsMap {
|
||||
def isSynthetic(): Boolean = {
|
||||
importDef.isSynthetic
|
||||
}
|
||||
|
||||
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[ResolvedExtensionMethod])
|
||||
val allConversionMethods =
|
||||
targets.forall(_.isInstanceOf[ResolvedConversionMethod])
|
||||
allStaticMethods || allConversionMethods
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed trait DefinedEntity {
|
||||
@ -778,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)
|
||||
@ -893,7 +611,7 @@ object BindingsMap {
|
||||
* My_Type.method = 42
|
||||
* ```
|
||||
*/
|
||||
case class StaticMethod(
|
||||
case class ExtensionMethod(
|
||||
methodName: String,
|
||||
tpName: String
|
||||
) extends Method {
|
||||
@ -973,7 +691,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 = {
|
||||
@ -993,6 +712,18 @@ object BindingsMap {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def module: ModuleReference = tpe.module
|
||||
|
||||
override def findExportedSymbolsFor(name: String): List[ResolvedName] = {
|
||||
if (name == cons.name) {
|
||||
List(this)
|
||||
} else {
|
||||
List()
|
||||
}
|
||||
}
|
||||
|
||||
override def exportedSymbols: Map[String, List[ResolvedName]] = {
|
||||
Map(cons.name -> List(this))
|
||||
}
|
||||
}
|
||||
|
||||
/** A representation of a name being resolved to a module.
|
||||
@ -1031,8 +762,24 @@ 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] = {
|
||||
if (name == methodName) {
|
||||
List(this)
|
||||
} else {
|
||||
List()
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@ -1074,35 +821,27 @@ 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
|
||||
}
|
||||
|
||||
/** 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)
|
||||
}
|
||||
}
|
||||
|
||||
override def qualifiedName: QualifiedName =
|
||||
module.getName
|
||||
.createChild(staticMethod.tpName)
|
||||
.createChild(staticMethod.methodName)
|
||||
|
||||
override def methodName: String = staticMethod.methodName
|
||||
}
|
||||
|
||||
@ -1122,11 +861,6 @@ object BindingsMap {
|
||||
}
|
||||
}
|
||||
|
||||
override def qualifiedName: QualifiedName =
|
||||
module.getName
|
||||
.createChild(conversionMethod.targetTpName)
|
||||
.createChild(conversionMethod.methodName)
|
||||
|
||||
override def methodName: String = conversionMethod.methodName
|
||||
}
|
||||
|
||||
@ -1197,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}"
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
StaticMethod
|
||||
ExtensionMethod,
|
||||
ModuleMethod
|
||||
}
|
||||
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))
|
||||
|
@ -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,
|
||||
_
|
||||
)
|
||||
)
|
||||
@ -85,10 +86,81 @@ 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 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.ResolvedExtensionMethod(
|
||||
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)
|
||||
}
|
||||
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,
|
||||
@ -111,6 +183,40 @@ case object ImportSymbolAnalysis extends IRPass {
|
||||
unresolvedSymbol.name
|
||||
)
|
||||
)
|
||||
case BindingsMap.ResolvedConstructor(_, cons) =>
|
||||
errors.ImportExport(
|
||||
imp,
|
||||
errors.ImportExport.NoSuchConstructor(
|
||||
cons.name,
|
||||
unresolvedSymbol.name
|
||||
)
|
||||
)
|
||||
case BindingsMap.ResolvedModuleMethod(_, method) =>
|
||||
errors.ImportExport(
|
||||
imp,
|
||||
errors.ImportExport.NoSuchModuleMethod(
|
||||
method.name,
|
||||
unresolvedSymbol.name
|
||||
)
|
||||
)
|
||||
case BindingsMap.ResolvedExtensionMethod(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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -39,8 +39,6 @@ case object ModuleNameConflicts extends IRPass {
|
||||
case mod @ Export.Module(
|
||||
_,
|
||||
_,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
|
@ -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
|
||||
if m.name.name == resolution.qualifiedName.toString =>
|
||||
m.addDiagnostic(
|
||||
warnings.Shadowed.TypeInModuleNameConflicts(
|
||||
exportedModule.getName.toString,
|
||||
tpeName,
|
||||
allStarting.head.getName.toString,
|
||||
m,
|
||||
m.location
|
||||
.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
|
||||
)
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
ir.copy(bindings = new_bindings)
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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,18 @@ 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)
|
||||
@ -180,9 +184,7 @@ final class ImportResolver(compiler: Compiler) extends ImportResolverForIR {
|
||||
case Export.Module(
|
||||
expName,
|
||||
rename,
|
||||
isAll,
|
||||
onlyNames,
|
||||
hiddenNames,
|
||||
_,
|
||||
isSynthetic,
|
||||
_,
|
||||
@ -194,9 +196,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(),
|
||||
|
@ -0,0 +1,8 @@
|
||||
package org.enso.compiler.phase.exports
|
||||
|
||||
case class Edge(
|
||||
exporter: Node,
|
||||
symbols: List[String],
|
||||
exportsAs: Option[String],
|
||||
exportee: Node
|
||||
)
|
@ -1,17 +1,22 @@
|
||||
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
|
||||
import org.enso.compiler.data.BindingsMap.{
|
||||
ExportedModule,
|
||||
ImportTarget,
|
||||
ResolvedConversionMethod,
|
||||
ResolvedExtensionMethod,
|
||||
ResolvedImport,
|
||||
ResolvedModule,
|
||||
SymbolRestriction
|
||||
ResolvedModuleMethod,
|
||||
ResolvedName
|
||||
}
|
||||
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.
|
||||
@ -23,23 +28,9 @@ case class ExportCycleException(modules: List[Module])
|
||||
|
||||
class ExportsResolution(private val context: CompilerContext) {
|
||||
|
||||
private case class Edge(
|
||||
exporter: Node,
|
||||
symbols: SymbolRestriction,
|
||||
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 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))): _*
|
||||
@ -61,9 +52,21 @@ class ExportsResolution(private val context: CompilerContext) {
|
||||
Nil
|
||||
}
|
||||
val node = nodes(module)
|
||||
node.exports = exports.map {
|
||||
case ExportedModule(mod, rename, restriction) =>
|
||||
Edge(node, restriction, 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 }
|
||||
}
|
||||
@ -127,88 +130,84 @@ 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.module,
|
||||
edge.exportsAs,
|
||||
edge.symbols
|
||||
)
|
||||
)
|
||||
val transitivelyExported: List[ExportedModule] =
|
||||
explicitlyExported.flatMap {
|
||||
case ExportedModule(module, _, restriction) =>
|
||||
exports(module).map {
|
||||
case ExportedModule(export, _, parentRestriction) =>
|
||||
ExportedModule(
|
||||
export,
|
||||
None,
|
||||
SymbolRestriction.Intersect(
|
||||
List(restriction, parentRestriction)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
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.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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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)
|
||||
val ownEntities =
|
||||
bindings.definedEntities
|
||||
.filter(_.canExport)
|
||||
.map(e => (e.name, List(e.resolvedIn(module))))
|
||||
val exportedModules = bindings.resolvedExports.collect {
|
||||
case ExportedModule(mod, Some(name), _)
|
||||
if mod.module.unsafeAsModule() != module =>
|
||||
(name, 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))
|
||||
.map(e => (e.name, e.resolvedIn(module)))
|
||||
val expSymbolsFromResolvedImps: 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)
|
||||
}
|
||||
symbols.flatMap { symbol =>
|
||||
export.rename match {
|
||||
case Some(rename) =>
|
||||
Some((rename.name, target))
|
||||
case None =>
|
||||
Some((symbol, target))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.flatten
|
||||
.distinct
|
||||
|
||||
bindings.exportedSymbols = List(
|
||||
ownEntities,
|
||||
exportedModules,
|
||||
reExportedSymbols
|
||||
).flatten.groupBy(_._1).map { case (m, names) =>
|
||||
(m, names.flatMap(_._2).distinct)
|
||||
expSymbolsFromResolvedImps
|
||||
).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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 _: ResolvedExtensionMethod => 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.
|
||||
*
|
||||
* 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
|
||||
@ -221,18 +220,17 @@ 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(_.module.module.unsafeAsModule())
|
||||
)
|
||||
}
|
||||
val tops = topsort(graph)
|
||||
resolveExports(tops)
|
||||
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
|
||||
@ -242,7 +240,7 @@ 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(_.module)
|
||||
topModules.map(_.module.unsafeAsModule()).reverse.distinct.reverse
|
@ -0,0 +1,10 @@
|
||||
package org.enso.compiler.phase.exports
|
||||
|
||||
import org.enso.compiler.data.BindingsMap.ResolvedModule
|
||||
|
||||
case class Node(
|
||||
module: ResolvedModule
|
||||
) {
|
||||
var exports: List[Edge] = List()
|
||||
var exportedBy: List[Edge] = List()
|
||||
}
|
@ -408,19 +408,22 @@ public class ErrorCompilerTest extends CompilerTest {
|
||||
@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
|
||||
@ -593,6 +596,24 @@ public class ErrorCompilerTest extends CompilerTest {
|
||||
assertTrue(method.body() instanceof Empty);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exportAllIsNotAllowed() {
|
||||
var ir = parse("""
|
||||
from project.Module export all
|
||||
""");
|
||||
var expectedReason = new Syntax.InvalidExport("`all` not allowed in `export` statement");
|
||||
assertSingleSyntaxError(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(
|
||||
Module ir, Syntax.Reason type, String msg, int start, int end) {
|
||||
var errors = assertIR(ir, Syntax.class, 1);
|
||||
|
@ -0,0 +1,179 @@
|
||||
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.hamcrest.Matchers.hasKey;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
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.ModuleUtils;
|
||||
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 detectCycleInTwoModules() 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.A_Module"),
|
||||
containsString("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);
|
||||
// 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.A_Module"),
|
||||
containsString("local.Proj.B_Module"),
|
||||
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<String> 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
package org.enso.compiler;
|
||||
|
||||
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.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"),
|
||||
"""
|
||||
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"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exportSymbolFromDifferentModule() throws IOException {
|
||||
var mainMod =
|
||||
new SourceModule(
|
||||
QualifiedName.fromString("Main"),
|
||||
"""
|
||||
export project.B_Module.B_Type
|
||||
type A_Type
|
||||
""");
|
||||
var bMod =
|
||||
new SourceModule(QualifiedName.fromString("B_Module"), """
|
||||
type B_Type
|
||||
""");
|
||||
ProjectUtils.createProject("Proj", Set.of(mainMod, bMod), 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"));
|
||||
}
|
||||
|
||||
@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"));
|
||||
}
|
||||
|
||||
@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())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static void compile(Context ctx) {
|
||||
new PolyglotContext(ctx).getTopScope().compile(true);
|
||||
}
|
||||
|
||||
private static Map<String, List<BindingsMap.ResolvedName>> getExportedSymbolsFromModule(
|
||||
Context ctx, String modName) {
|
||||
var ensoCtx = ContextUtils.leakContext(ctx);
|
||||
var mod = ensoCtx.getPackageRepository().getLoadedModule(modName).get();
|
||||
return getExportedSymbols(mod);
|
||||
}
|
||||
|
||||
private static Map<String, List<BindingsMap.ResolvedName>> getExportedSymbols(Module module) {
|
||||
var bindings = new HashMap<String, List<BindingsMap.ResolvedName>>();
|
||||
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;
|
||||
}
|
||||
}
|
@ -11,8 +11,10 @@ import java.util.List;
|
||||
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.runner.RunWith;
|
||||
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 @@ main = 42
|
||||
private static List<String> 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<String> 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;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,91 @@
|
||||
package org.enso.interpreter.test.exports;
|
||||
|
||||
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;
|
||||
|
||||
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"),
|
||||
"""
|
||||
export project.Boolean.Boolean.True
|
||||
export project.Boolean.Boolean.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")));
|
||||
}
|
||||
}
|
||||
|
||||
@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"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -36,7 +36,8 @@ public class ExportExtensionMethodTest {
|
||||
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 +75,9 @@ public class ExportExtensionMethodTest {
|
||||
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 +113,8 @@ public class ExportExtensionMethodTest {
|
||||
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 +125,9 @@ public class ExportExtensionMethodTest {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,174 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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: Tracked by https://github.com/enso-org/enso/issues/10505
|
||||
@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<Module> runExportsResolutionSort(List<Module> 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();
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ public class ExportStaticMethodTest {
|
||||
new SourceModule(
|
||||
QualifiedName.fromString("A_Module"),
|
||||
"""
|
||||
from project.T_Module export static_method
|
||||
export project.T_Module.static_method
|
||||
""");
|
||||
var mainMod =
|
||||
new SourceModule(
|
||||
@ -93,7 +93,7 @@ public class ExportStaticMethodTest {
|
||||
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);
|
||||
@ -123,7 +123,7 @@ public class ExportStaticMethodTest {
|
||||
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);
|
||||
|
@ -0,0 +1,139 @@
|
||||
package org.enso.interpreter.test.imports;
|
||||
|
||||
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;
|
||||
import org.junit.Ignore;
|
||||
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 importAllFromModuleDoesNotImportModuleItself() 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Tracked by https://github.com/enso-org/enso/issues/10504
|
||||
@Ignore
|
||||
@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)));
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
from project.Sub.C export all
|
||||
from project.Sub.C export A
|
||||
|
@ -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
|
||||
|
@ -1,6 +0,0 @@
|
||||
name: Test_Multiple_Conflicting_Exports_1
|
||||
license: APLv2
|
||||
enso-version: default
|
||||
version: "0.0.1"
|
||||
author: "Enso Team <contact@enso.org>"
|
||||
maintainer: "Enso Team <contact@enso.org>"
|
@ -1,2 +0,0 @@
|
||||
foo = 42
|
||||
bar = "z"
|
@ -1,3 +0,0 @@
|
||||
export project.F1
|
||||
from project.F1 export foo
|
||||
from project.F1 export all hiding foo
|
@ -1,7 +0,0 @@
|
||||
from Standard.Base import IO
|
||||
from project.F2 import all
|
||||
|
||||
main =
|
||||
IO.println F1.bar
|
||||
IO.println foo
|
||||
|
@ -1,6 +0,0 @@
|
||||
name: Test_Multiple_Conflicting_Exports_2
|
||||
license: APLv2
|
||||
enso-version: default
|
||||
version: "0.0.1"
|
||||
author: "Enso Team <contact@enso.org>"
|
||||
maintainer: "Enso Team <contact@enso.org>"
|
@ -1,2 +0,0 @@
|
||||
foo = 42
|
||||
bar = "z"
|
@ -1,3 +0,0 @@
|
||||
export project.F1
|
||||
from project.F1 export all
|
||||
from project.F1 export all hiding bar
|
@ -1,7 +0,0 @@
|
||||
from Standard.Base import IO
|
||||
from project.F2 import all
|
||||
|
||||
main =
|
||||
IO.println F1.bar
|
||||
IO.println foo
|
||||
|
@ -1,2 +1,2 @@
|
||||
export project.F1
|
||||
from project.F1 export foo
|
||||
export project.F1.foo
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
from project.Type_Def.Maybe export Some
|
||||
export project.Type_Def.Maybe.Some
|
||||
|
@ -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,
|
||||
|
@ -9,10 +9,10 @@ import org.enso.compiler.data.BindingsMap.{
|
||||
Argument,
|
||||
Cons,
|
||||
ConversionMethod,
|
||||
ExtensionMethod,
|
||||
ModuleMethod,
|
||||
PolyglotSymbol,
|
||||
ResolvedStaticMethod,
|
||||
StaticMethod,
|
||||
ResolvedExtensionMethod,
|
||||
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)
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -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"
|
||||
"from Bar.Foo.Main export Bar, Baz"
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.exports.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,29 @@ 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.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()
|
||||
}
|
||||
@ -139,7 +164,8 @@ class ImportExportTest
|
||||
mainBindingsMap.resolvedImports.size shouldEqual 1
|
||||
mainBindingsMap
|
||||
.resolvedImports(0)
|
||||
.target
|
||||
.targets
|
||||
.head
|
||||
.isInstanceOf[BindingsMap.ResolvedModule] shouldBe true
|
||||
}
|
||||
|
||||
@ -168,7 +194,8 @@ class ImportExportTest
|
||||
mainIr.unwrapBindingMap.resolvedImports.size shouldEqual 1
|
||||
mainIr.unwrapBindingMap
|
||||
.resolvedImports(0)
|
||||
.target
|
||||
.targets
|
||||
.head
|
||||
.isInstanceOf[BindingsMap.ResolvedModule] shouldBe true
|
||||
}
|
||||
|
||||
@ -230,53 +257,130 @@ 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
|
||||
mainBindingMap.resolvedImports.size shouldEqual 1
|
||||
mainBindingMap.resolvedImports.head.targets.head
|
||||
.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
|
||||
.asInstanceOf[BindingsMap.ResolvedModuleMethod]
|
||||
.method
|
||||
.name shouldEqual "static_method"
|
||||
.name shouldEqual "module_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.targets.head shouldBe a[
|
||||
BindingsMap.ResolvedConstructor
|
||||
]
|
||||
|
||||
bindingMap.resolvedImports.head.targets.head
|
||||
.asInstanceOf[BindingsMap.ResolvedConstructor]
|
||||
.qualifiedName
|
||||
.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
|
||||
}
|
||||
|
||||
"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.ResolvedExtensionMethod
|
||||
]
|
||||
resolvedImport.targets.head
|
||||
.asInstanceOf[BindingsMap.ResolvedExtensionMethod]
|
||||
.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 {
|
||||
@ -317,8 +421,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 {
|
||||
@ -339,63 +443,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"""
|
||||
|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 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
|
||||
@ -442,10 +489,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 {
|
||||
@ -463,9 +506,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 {
|
||||
@ -483,10 +523,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 {
|
||||
@ -525,12 +561,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 {
|
||||
@ -547,8 +583,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
|
||||
@ -582,12 +616,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 {
|
||||
@ -610,12 +644,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 {
|
||||
@ -1064,21 +1098,23 @@ 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"))
|
||||
|
||||
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
|
||||
bBindingMap.exportedSymbols.keys should contain theSameElementsAs List(
|
||||
"static_method",
|
||||
"B_Type"
|
||||
)
|
||||
|
||||
val mainIr =
|
||||
s"""
|
||||
@ -1330,4 +1366,308 @@ class ImportExportTest
|
||||
diags.size shouldEqual 0
|
||||
}
|
||||
}
|
||||
|
||||
"Exports graph building" should {
|
||||
def assertAModExportedByBMod(graph: List[Node]): Unit = {
|
||||
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 =
|
||||
"""
|
||||
|# 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))
|
||||
withClue("There should be only A_Module and B_Module nodes") {
|
||||
graph.size shouldBe 2
|
||||
}
|
||||
assertAModExportedByBMod(graph)
|
||||
}
|
||||
|
||||
"build exports graph for module method with `from ... export ...` syntax" in {
|
||||
val aModule =
|
||||
"""
|
||||
|type A_Type
|
||||
| A_Constructor
|
||||
| instance_method self = 42
|
||||
|
|
||||
|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
|
||||
.createModule(packageQualifiedName.createChild("A_Module"))
|
||||
|
||||
val bModule =
|
||||
s"""
|
||||
|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)
|
||||
}
|
||||
|
||||
"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))
|
||||
withClue("There should be only A_Module and B_Module nodes") {
|
||||
graph.size shouldBe 2
|
||||
}
|
||||
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: [Boolean, Main]"
|
||||
) {
|
||||
graph.size shouldBe 2
|
||||
}
|
||||
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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"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))
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
""
|
||||
)
|
||||
),
|
||||
|
@ -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 {
|
||||
@ -143,26 +144,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"
|
||||
|
@ -1823,32 +1823,30 @@ final class TreeToIr {
|
||||
@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<Name.Literal> rename;
|
||||
if (exp.getAs() == null) {
|
||||
rename = Option.empty();
|
||||
} else {
|
||||
rename = Option.apply(buildName(exp.getAs().getBody(), true));
|
||||
}
|
||||
Option<List<Name.Literal>> hidingNames;
|
||||
if (exp.getHiding() == null) {
|
||||
hidingNames = Option.empty();
|
||||
} else {
|
||||
hidingNames = Option.apply(buildNameSequence(exp.getHiding().getBody()));
|
||||
}
|
||||
Name.Qualified qualifiedName;
|
||||
Option<List<Name.Literal>> 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) {
|
||||
|
@ -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
|
||||
@ -173,6 +181,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 {
|
||||
|
@ -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
|
||||
|
@ -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,24 +140,17 @@ 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"
|
||||
onlyNames match {
|
||||
case Some(names) =>
|
||||
s"from ${name.name} export ${names.map(_.name).mkString(", ")}$renameCode"
|
||||
case None =>
|
||||
s"export ${name.name}$renameCode"
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,11 +171,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
|
||||
}
|
||||
|
@ -204,29 +204,17 @@ public final class ImportExportCache
|
||||
@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 = 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.ExtensionMethod.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<BindingsMap> {
|
||||
public PersistBindingsMap() {
|
||||
@ -238,7 +226,6 @@ public final class ImportExportCache
|
||||
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());
|
||||
}
|
||||
|
||||
@ -248,11 +235,9 @@ public final class ImportExportCache
|
||||
var de = (scala.collection.immutable.List<DefinedEntity>) 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;
|
||||
}
|
||||
|
@ -9,12 +9,24 @@ import org.enso.editions.LibraryName;
|
||||
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;
|
||||
import org.enso.interpreter.runtime.scope.TopLevelScope;
|
||||
|
||||
final class InvokeMethodImportResolver
|
||||
extends ImportResolverAlgorithm<EnsoObject, Module, UnresolvedSymbol, Object, Type, Module> {
|
||||
extends ImportResolverAlgorithm<
|
||||
EnsoObject,
|
||||
Module,
|
||||
UnresolvedSymbol,
|
||||
Object,
|
||||
Type,
|
||||
Module,
|
||||
AtomConstructor,
|
||||
Function,
|
||||
Function,
|
||||
Function> {
|
||||
|
||||
private final Module module;
|
||||
private final TopLevelScope topScope;
|
||||
@ -53,13 +65,28 @@ final class InvokeMethodImportResolver
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Object> exportsFor(Module module, String impName) {
|
||||
return Collections.emptyList();
|
||||
protected String nameForConstructor(AtomConstructor cons) {
|
||||
return cons.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAll(Object ex) {
|
||||
return false;
|
||||
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<Object> exportsFor(Module module, String impName) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -68,13 +95,28 @@ final class InvokeMethodImportResolver
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> hiddenNames(Object ex) {
|
||||
protected List<Type> definedEntities(UnresolvedSymbol symbol) {
|
||||
return module.getScope().getAllTypes(symbol.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AtomConstructor> definedConstructors(UnresolvedSymbol symbol) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Function> definedModuleMethods(UnresolvedSymbol symbol) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Function> definedExtensionMethods(UnresolvedSymbol imp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Type> definedEntities(UnresolvedSymbol symbol) {
|
||||
return module.getScope().getAllTypes(symbol.getName());
|
||||
protected List<Function> definedConversionMethods(UnresolvedSymbol imp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -94,6 +136,30 @@ final class InvokeMethodImportResolver
|
||||
return typ;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnsoObject createResolvedConstructor(
|
||||
UnresolvedSymbol imp, List<Object> exp, AtomConstructor cons) {
|
||||
throw new UnsupportedOperationException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnsoObject createResolvedModuleMethod(
|
||||
UnresolvedSymbol imp, List<Object> exp, Function function) {
|
||||
throw new UnsupportedOperationException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnsoObject createResolvedExtensionMethods(
|
||||
UnresolvedSymbol imp, List<Object> exp, List<Function> functions) {
|
||||
throw new UnsupportedOperationException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnsoObject createResolvedConversionMethods(
|
||||
UnresolvedSymbol imp, List<Object> exp, List<Function> functions) {
|
||||
throw new UnsupportedOperationException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnsoObject createErrorPackageCoundNotBeLoaded(
|
||||
UnresolvedSymbol imp, String impName, String loadingError) {
|
||||
|
@ -171,6 +171,10 @@ public final class ModuleScope implements EnsoObject {
|
||||
return tpes;
|
||||
}
|
||||
|
||||
public List<Type> getAllTypes() {
|
||||
return types.values().stream().collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
@ExportMessage.Ignore
|
||||
public Type getType(String name, boolean ignoreAssociatedType) {
|
||||
if (!ignoreAssociatedType && associatedType.getName().equals(name)) {
|
||||
|
@ -41,11 +41,7 @@ import org.enso.compiler.core.ir.expression.{
|
||||
Operator,
|
||||
Section
|
||||
}
|
||||
import org.enso.compiler.data.BindingsMap.{
|
||||
ExportedModule,
|
||||
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}
|
||||
@ -197,19 +193,25 @@ 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.getDirectlyExportedModules.foreach { exportedMod =>
|
||||
val exportedRuntimeMod = exportedMod.module.module.unsafeAsModule()
|
||||
scopeBuilder.addExport(
|
||||
new ImportExportScope(exportedRuntimeMod)
|
||||
)
|
||||
}
|
||||
|
||||
val importDefs = module.imports
|
||||
val methodDefs = module.bindings.collect {
|
||||
case method: definition.Method.Explicit => method
|
||||
}
|
||||
|
||||
bindingsMap.resolvedImports.foreach { imp =>
|
||||
imp.target match {
|
||||
case BindingsMap.ResolvedType(_, _) =>
|
||||
imp.targets.foreach {
|
||||
case _: BindingsMap.ResolvedType =>
|
||||
case _: BindingsMap.ResolvedConstructor =>
|
||||
case _: BindingsMap.ResolvedModuleMethod =>
|
||||
case _: BindingsMap.ResolvedExtensionMethod =>
|
||||
case _: BindingsMap.ResolvedConversionMethod =>
|
||||
case ResolvedModule(module) =>
|
||||
val mod = module
|
||||
.unsafeAsModule()
|
||||
@ -406,7 +408,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."
|
||||
)
|
||||
@ -884,7 +886,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."
|
||||
)
|
||||
@ -1012,7 +1014,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(
|
||||
@ -1547,7 +1549,7 @@ class IrToTruffle(
|
||||
)
|
||||
case Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedStaticMethod(_, _)
|
||||
BindingsMap.ResolvedExtensionMethod(_, _)
|
||||
)
|
||||
) =>
|
||||
throw new CompilerError(
|
||||
@ -1931,7 +1933,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"
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ import java.util.List;
|
||||
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,12 +27,35 @@ public class ModuleUtils {
|
||||
return getExportedSymbols(mod);
|
||||
}
|
||||
|
||||
public static List<ResolvedImport> 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<DefinedEntity> getDefinedEntities(Context ctx, String modName) {
|
||||
var ensoCtx = ContextUtils.leakContext(ctx);
|
||||
var mod = ensoCtx.getPackageRepository().getLoadedModule(modName).get();
|
||||
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<String, List<ResolvedName>> getExportedSymbols(Module module) {
|
||||
var bindings = new HashMap<String, List<ResolvedName>>();
|
||||
var bindingsScala = module.getBindingsMap().exportedSymbols();
|
||||
|
@ -2,8 +2,11 @@ package org.enso.test.utils;
|
||||
|
||||
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;
|
||||
@ -38,7 +41,10 @@ public class ProjectUtils {
|
||||
* running via {@code enso --run <projDir>}.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
@ -59,18 +65,13 @@ prefer-local-libraries: true
|
||||
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 relativePath = String.join(File.separator, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +85,10 @@ prefer-local-libraries: true
|
||||
*/
|
||||
public static void testProjectRun(
|
||||
Context.Builder ctxBuilder, Path projDir, Consumer<Value> 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())
|
||||
@ -93,6 +97,9 @@ prefer-local-libraries: true
|
||||
.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();
|
||||
@ -102,7 +109,7 @@ prefer-local-libraries: true
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -111,4 +118,24 @@ prefer-local-libraries: true
|
||||
public static void testProjectRun(Path projDir, Consumer<Value> 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
from project.Data.Boolean import False
|
||||
import project.Data.Boolean.Boolean.False
|
||||
|
||||
@Builtin_Type
|
||||
type Any
|
||||
|
@ -1,4 +1,5 @@
|
||||
from project.Data.Boolean.Boolean export all
|
||||
export project.Data.Boolean.Boolean.False
|
||||
export project.Data.Boolean.Boolean.True
|
||||
|
||||
@Builtin_Type
|
||||
type Boolean
|
||||
|
@ -1,5 +1,5 @@
|
||||
import project.Any.Any
|
||||
from project.Data.Boolean import True
|
||||
import project.Data.Boolean.Boolean.True
|
||||
|
||||
@Builtin_Type
|
||||
type Error
|
||||
|
@ -3,11 +3,14 @@ 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
|
||||
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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user