mirror of
https://github.com/enso-org/enso.git
synced 2024-08-16 16:30:33 +03:00
Extension methods can be exported by name (#10274)
Ultimately, we want to forbid the `from ... export all` syntax. This PR starts by providing a way to explicitly export extension and conversion methods by name. Stdlib code will be modified in upcoming PR. # Important Notes A single name can refer to multiple extension or conversion methods. Exports are not qualified. For example, ``` type My_Type type Other_Type My_Type.ext_method x = x Other_Type.ext_method x = x ``` ``` from project.Mod export ext_method ``` will export both `My_Type.ext_method` and `Other_Type.ext_method`.
This commit is contained in:
parent
0c2630122d
commit
9010cf93be
@ -333,6 +333,7 @@ lazy val enso = (project in file("."))
|
|||||||
`connected-lock-manager`,
|
`connected-lock-manager`,
|
||||||
`connected-lock-manager-server`,
|
`connected-lock-manager-server`,
|
||||||
testkit,
|
testkit,
|
||||||
|
`test-utils`,
|
||||||
`common-polyglot-core-utils`,
|
`common-polyglot-core-utils`,
|
||||||
`std-base`,
|
`std-base`,
|
||||||
`std-database`,
|
`std-database`,
|
||||||
|
@ -100,15 +100,17 @@ binds the function name. This means that:
|
|||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
Enso makes a distinction between functions and methods. In Enso, a method is a
|
Enso makes a distinction between functions and methods. In Enso, a method is a
|
||||||
function where the first argument (known as the `this` argument) is associated
|
function where the first argument (known as the `self` argument) is associated
|
||||||
with a given atom. Methods are dispatched dynamically based on the type of the
|
with a given atom. Methods are dispatched dynamically based on the type of the
|
||||||
`this` argument, while functions are not.
|
`self` argument, while functions are not.
|
||||||
|
|
||||||
Methods can be defined in Enso in two ways:
|
Methods can be defined in Enso in two ways:
|
||||||
|
|
||||||
1. **In the Body of a Type:** A function defined in the body of a `type`
|
1. **In the Body of a Type:** A function defined in the body of a `type`
|
||||||
definition is automatically converted to a method on all the atoms defined in
|
definition is automatically converted to a method on all the atoms defined in
|
||||||
the body of that type definition.
|
the body of that type definition. If the function has `self` parameter, it is
|
||||||
|
called an _instance method_. If the function does not have `self` parameter,
|
||||||
|
it is called a _static method_.
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
type Maybe a
|
type Maybe a
|
||||||
@ -120,9 +122,9 @@ type Maybe a
|
|||||||
Maybe.Just _ -> True
|
Maybe.Just _ -> True
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **As an Extension Method:** A function defined _explicitly_ on an atom counts
|
2. **As an Extension Method:** A function defined _explicitly_ on a type counts
|
||||||
as an extension method on that atom. It can be defined on a typeset to apply
|
as an extension method on that type. An _extension_ method can be _static_ or
|
||||||
to all the atoms within that typeset.
|
_instance_, depending on whether the `self` argument is present or not.
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Number.floor self = case self of
|
Number.floor self = case self of
|
||||||
@ -130,11 +132,11 @@ Number.floor self = case self of
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **As a Function with an Explicit `this` Argument:** A function defined with
|
3. **As a Function with an Explicit `self` Argument:** A function defined with
|
||||||
the type of the `this` argument specified to be a type.
|
the type of the `self` argument specified to be a type.
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
floor (this : Number) = case this of
|
floor (self : Number) = case self of
|
||||||
Integer -> ...
|
Integer -> ...
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -151,7 +153,8 @@ diagnostics, we distinguish between how functions and methods are called.
|
|||||||
|
|
||||||
- To call a function `f` on arguments `a` and `b`, we write `f a b`.
|
- To call a function `f` on arguments `a` and `b`, we write `f a b`.
|
||||||
- To call a method `f` defined on a type `A` (value `a`, here) on argument `b`,
|
- To call a method `f` defined on a type `A` (value `a`, here) on argument `b`,
|
||||||
we write `a.f b`.
|
we write `a.f b`. This instance method can also be called via a _static_
|
||||||
|
method with `A.f a b`, which is equivalent to `a.f b`.
|
||||||
|
|
||||||
## Code Blocks
|
## Code Blocks
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ import org.enso.compiler.core.ir.module.scope.definition.Method;
|
|||||||
import org.enso.compiler.core.ir.module.scope.imports.Polyglot;
|
import org.enso.compiler.core.ir.module.scope.imports.Polyglot;
|
||||||
import org.enso.compiler.data.BindingsMap;
|
import org.enso.compiler.data.BindingsMap;
|
||||||
import org.enso.compiler.data.BindingsMap.ResolvedConstructor;
|
import org.enso.compiler.data.BindingsMap.ResolvedConstructor;
|
||||||
import org.enso.compiler.data.BindingsMap.ResolvedMethod;
|
import org.enso.compiler.data.BindingsMap.ResolvedModuleMethod;
|
||||||
import org.enso.compiler.data.BindingsMap.ResolvedPolyglotField;
|
import org.enso.compiler.data.BindingsMap.ResolvedPolyglotField;
|
||||||
import org.enso.compiler.data.BindingsMap.ResolvedPolyglotSymbol;
|
import org.enso.compiler.data.BindingsMap.ResolvedPolyglotSymbol;
|
||||||
import org.enso.compiler.data.BindingsMap.ResolvedType;
|
import org.enso.compiler.data.BindingsMap.ResolvedType;
|
||||||
@ -513,9 +513,9 @@ public class IRDumper {
|
|||||||
bldr.addLabelLine(
|
bldr.addLabelLine(
|
||||||
"target: ResolvedConstructor(" + resolvedConstructor.cons().name() + ")");
|
"target: ResolvedConstructor(" + resolvedConstructor.cons().name() + ")");
|
||||||
}
|
}
|
||||||
case ResolvedMethod resolvedMethod -> {
|
case ResolvedModuleMethod resolvedModuleMethod -> {
|
||||||
bldr.addLabelLine(
|
bldr.addLabelLine(
|
||||||
"target: ResolvedMethod(" + resolvedMethod.method().name() + ")");
|
"target: ResolvedMethod(" + resolvedModuleMethod.method().name() + ")");
|
||||||
}
|
}
|
||||||
case ResolvedPolyglotField resolvedPolyglotField -> {
|
case ResolvedPolyglotField resolvedPolyglotField -> {
|
||||||
bldr.addLabelLine(
|
bldr.addLabelLine(
|
||||||
@ -563,7 +563,7 @@ public class IRDumper {
|
|||||||
switch (entity) {
|
switch (entity) {
|
||||||
case BindingsMap.Type tp -> bldr.addLabelLine(" - Type(" + tp.name() + ")");
|
case BindingsMap.Type tp -> bldr.addLabelLine(" - Type(" + tp.name() + ")");
|
||||||
case BindingsMap.ModuleMethod method -> bldr.addLabelLine(
|
case BindingsMap.ModuleMethod method -> bldr.addLabelLine(
|
||||||
" - Method(" + method.name() + ")");
|
" - ModuleMethod(" + method.name() + ")");
|
||||||
case BindingsMap.PolyglotSymbol polySym -> bldr.addLabelLine(
|
case BindingsMap.PolyglotSymbol polySym -> bldr.addLabelLine(
|
||||||
" - PolyglotSymbol(" + polySym.name() + ")");
|
" - PolyglotSymbol(" + polySym.name() + ")");
|
||||||
default -> throw unimpl(entity);
|
default -> throw unimpl(entity);
|
||||||
|
@ -125,10 +125,11 @@ public class PrivateSymbolsAnalysis implements IRPass {
|
|||||||
private Pattern processCasePattern(Pattern pattern, BindingsMap bindingsMap) {
|
private Pattern processCasePattern(Pattern pattern, BindingsMap bindingsMap) {
|
||||||
if (pattern instanceof Pattern.Constructor cons) {
|
if (pattern instanceof Pattern.Constructor cons) {
|
||||||
var consName = cons.constructor();
|
var consName = cons.constructor();
|
||||||
var resolvedCons = tryResolveName(consName, bindingsMap);
|
var resolvedNames = tryResolveName(consName, bindingsMap);
|
||||||
if (resolvedCons != null && isProjectPrivate(resolvedCons)) {
|
for (var resolvedName : resolvedNames) {
|
||||||
|
if (isProjectPrivate(resolvedName)) {
|
||||||
var curProjName = getProjName(bindingsMap.currentModule().getName());
|
var curProjName = getProjName(bindingsMap.currentModule().getName());
|
||||||
var resolvedProjName = getProjName(resolvedCons.module().getName());
|
var resolvedProjName = getProjName(resolvedName.module().getName());
|
||||||
if (!curProjName.equals(resolvedProjName)) {
|
if (!curProjName.equals(resolvedProjName)) {
|
||||||
var reason =
|
var reason =
|
||||||
new org.enso.compiler.core.ir.expression.errors.Pattern.PrivateConstructor(
|
new org.enso.compiler.core.ir.expression.errors.Pattern.PrivateConstructor(
|
||||||
@ -138,12 +139,14 @@ public class PrivateSymbolsAnalysis implements IRPass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return pattern.mapExpressions(e -> processExpression(e, bindingsMap));
|
return pattern.mapExpressions(e -> processExpression(e, bindingsMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression processName(Name name, BindingsMap bindingsMap) {
|
private Expression processName(Name name, BindingsMap bindingsMap) {
|
||||||
var resolvedName = tryResolveName(name, bindingsMap);
|
var resolvedNames = tryResolveName(name, bindingsMap);
|
||||||
if (resolvedName != null && isProjectPrivate(resolvedName)) {
|
for (var resolvedName : resolvedNames) {
|
||||||
|
if (isProjectPrivate(resolvedName)) {
|
||||||
var curProjName = getProjName(bindingsMap.currentModule().getName());
|
var curProjName = getProjName(bindingsMap.currentModule().getName());
|
||||||
var resolvedProjName = getProjName(resolvedName.module().getName());
|
var resolvedProjName = getProjName(resolvedName.module().getName());
|
||||||
if (!curProjName.equals(resolvedProjName)) {
|
if (!curProjName.equals(resolvedProjName)) {
|
||||||
@ -154,6 +157,7 @@ public class PrivateSymbolsAnalysis implements IRPass {
|
|||||||
name, reason, name.passData(), name.diagnostics());
|
name, reason, name.passData(), name.diagnostics());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return name.mapExpressions(e -> processExpression(e, bindingsMap));
|
return name.mapExpressions(e -> processExpression(e, bindingsMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,26 +184,28 @@ public class PrivateSymbolsAnalysis implements IRPass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolvedName tryResolveName(Name name, BindingsMap bindingsMap) {
|
private List<ResolvedName> tryResolveName(Name name, BindingsMap bindingsMap) {
|
||||||
return switch (name) {
|
return switch (name) {
|
||||||
case Name.Literal lit -> {
|
case Name.Literal lit -> {
|
||||||
var resolved = bindingsMap.resolveName(lit.name());
|
var resolved = bindingsMap.resolveName(lit.name());
|
||||||
if (resolved.isRight()) {
|
if (resolved.isRight()) {
|
||||||
yield (ResolvedName) resolved.getOrElse(() -> null);
|
var resolvedNames = resolved.toOption().get();
|
||||||
|
yield CollectionConverters.asJava(resolvedNames);
|
||||||
} else {
|
} else {
|
||||||
yield null;
|
yield List.of();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Name.Qualified qual -> {
|
case Name.Qualified qual -> {
|
||||||
var nameParts = qual.parts().map(Name::name);
|
var nameParts = qual.parts().map(Name::name);
|
||||||
var resolved = bindingsMap.resolveQualifiedName(nameParts);
|
var resolved = bindingsMap.resolveQualifiedName(nameParts);
|
||||||
if (resolved.isRight()) {
|
if (resolved.isRight()) {
|
||||||
yield (ResolvedName) resolved.getOrElse(() -> null);
|
var resolvedNames = resolved.toOption().get();
|
||||||
|
yield CollectionConverters.asJava(resolvedNames);
|
||||||
} else {
|
} else {
|
||||||
yield null;
|
yield List.of();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default -> null;
|
default -> List.of();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import org.enso.pkg.QualifiedName
|
|||||||
|
|
||||||
import java.io.ObjectOutputStream
|
import java.io.ObjectOutputStream
|
||||||
import scala.annotation.unused
|
import scala.annotation.unused
|
||||||
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
/** A utility structure for resolving symbols in a given module.
|
/** A utility structure for resolving symbols in a given module.
|
||||||
*
|
*
|
||||||
@ -174,15 +175,18 @@ case class BindingsMap(
|
|||||||
}).map(_._1)
|
}).map(_._1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resolves a name in the context of current module.
|
/** Resolves the symbol with the given name in the context of this import target.
|
||||||
|
* Note that it is valid to have multiple resolved names for a single symbol name,
|
||||||
|
* for example, if the symbol is a name of an extension method and there are multiple
|
||||||
|
* extension methods with the same name defined on multiple types.
|
||||||
*
|
*
|
||||||
* @param name the name to resolve.
|
* @param name (Unqualified) name of the symbol to resolve
|
||||||
* @return a resolution for `name` or an error, if the name could not be
|
* @return A list of all resolutions for the given name or an error if no resolution
|
||||||
* resolved.
|
* was found
|
||||||
*/
|
*/
|
||||||
def resolveName(
|
def resolveName(
|
||||||
name: String
|
name: String
|
||||||
): Either[ResolutionError, ResolvedName] = {
|
): Either[ResolutionError, List[ResolvedName]] = {
|
||||||
val local = findLocalCandidates(name)
|
val local = findLocalCandidates(name)
|
||||||
if (local.nonEmpty) {
|
if (local.nonEmpty) {
|
||||||
return BindingsMap.handleAmbiguity(local)
|
return BindingsMap.handleAmbiguity(local)
|
||||||
@ -200,14 +204,14 @@ case class BindingsMap(
|
|||||||
scope: ResolvedName,
|
scope: ResolvedName,
|
||||||
submoduleNames: List[String],
|
submoduleNames: List[String],
|
||||||
finalItem: String
|
finalItem: String
|
||||||
): Either[ResolutionError, ResolvedName] = scope match {
|
): Either[ResolutionError, List[ResolvedName]] = scope match {
|
||||||
case scoped: ImportTarget =>
|
case scoped: ImportTarget =>
|
||||||
var currentScope = scoped
|
var currentScope = scoped
|
||||||
for (modName <- submoduleNames) {
|
for (modName <- submoduleNames) {
|
||||||
val resolution = currentScope.resolveExportedSymbol(modName)
|
val resolutions = currentScope.resolveExportedSymbol(modName)
|
||||||
resolution match {
|
resolutions match {
|
||||||
case Left(err) => return Left(err)
|
case Left(err) => return Left(err)
|
||||||
case Right(t: ImportTarget) =>
|
case Right(List(t: ImportTarget)) =>
|
||||||
currentScope = t
|
currentScope = t
|
||||||
case _ => return Left(ResolutionNotFound)
|
case _ => return Left(ResolutionNotFound)
|
||||||
}
|
}
|
||||||
@ -215,7 +219,7 @@ case class BindingsMap(
|
|||||||
currentScope.resolveExportedSymbol(finalItem)
|
currentScope.resolveExportedSymbol(finalItem)
|
||||||
case s @ ResolvedPolyglotSymbol(_, _) =>
|
case s @ ResolvedPolyglotSymbol(_, _) =>
|
||||||
val found = s.findExportedSymbolFor(finalItem)
|
val found = s.findExportedSymbolFor(finalItem)
|
||||||
Right(found)
|
Right(List(found))
|
||||||
case _ => Left(ResolutionNotFound)
|
case _ => Left(ResolutionNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,37 +227,64 @@ case class BindingsMap(
|
|||||||
*
|
*
|
||||||
* @param name the name to resolve
|
* @param name the name to resolve
|
||||||
* @return a resolution for `name`
|
* @return a resolution for `name`
|
||||||
|
* @see [[resolveName]]
|
||||||
*/
|
*/
|
||||||
def resolveQualifiedName(
|
def resolveQualifiedName(
|
||||||
name: List[String]
|
name: List[String]
|
||||||
): Either[ResolutionError, ResolvedName] =
|
): Either[ResolutionError, List[ResolvedName]] =
|
||||||
name match {
|
name match {
|
||||||
case List() => Left(ResolutionNotFound)
|
case List() => Left(ResolutionNotFound)
|
||||||
case List(item) => resolveName(item)
|
case List(item) => resolveName(item)
|
||||||
case firstModuleName :: rest =>
|
case firstModuleName :: rest =>
|
||||||
resolveName(firstModuleName).flatMap { firstModule =>
|
val firstResolvedNamesOpt = resolveName(firstModuleName)
|
||||||
|
firstResolvedNamesOpt match {
|
||||||
|
case err @ Left(_) => err
|
||||||
|
case Right(firstResolvedNames) =>
|
||||||
// This special handling is needed, because when resolving a local module name, we do not necessarily only look at _exported_ symbols, but overall locally defined symbols.
|
// This special handling is needed, because when resolving a local module name, we do not necessarily only look at _exported_ symbols, but overall locally defined symbols.
|
||||||
val isQualifiedLocalImport =
|
val isQualifiedLocalImport =
|
||||||
firstModule == ResolvedModule(currentModule)
|
firstResolvedNames == List(ResolvedModule(currentModule))
|
||||||
if (isQualifiedLocalImport) {
|
if (isQualifiedLocalImport) {
|
||||||
resolveLocalName(rest)
|
resolveLocalName(rest)
|
||||||
} else {
|
} else {
|
||||||
val consName = rest.last
|
val consName = rest.last
|
||||||
val modNames = rest.init
|
val modNames = rest.init
|
||||||
resolveQualifiedNameIn(firstModule, modNames, consName)
|
|
||||||
|
val allResolvedNames: ArrayBuffer[ResolvedName] =
|
||||||
|
ArrayBuffer.empty
|
||||||
|
firstResolvedNames.foreach { firstResolvedName =>
|
||||||
|
val res =
|
||||||
|
resolveQualifiedNameIn(firstResolvedName, modNames, consName)
|
||||||
|
res match {
|
||||||
|
case Left(resolutionErr) => return Left(resolutionErr)
|
||||||
|
case Right(resolved) =>
|
||||||
|
allResolvedNames ++= resolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Right(allResolvedNames.toList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def resolveLocalName(
|
private def resolveLocalName(
|
||||||
name: List[String]
|
name: List[String]
|
||||||
): Either[ResolutionError, ResolvedName] = name match {
|
): Either[ResolutionError, List[ResolvedName]] = name match {
|
||||||
case List() => Left(ResolutionNotFound)
|
case List() => Left(ResolutionNotFound)
|
||||||
case List(singleItem) =>
|
case List(singleItem) =>
|
||||||
handleAmbiguity(findLocalCandidates(singleItem))
|
handleAmbiguity(findLocalCandidates(singleItem))
|
||||||
case firstName :: rest =>
|
case firstName :: rest =>
|
||||||
handleAmbiguity(findLocalCandidates(firstName))
|
handleAmbiguity(findLocalCandidates(firstName))
|
||||||
.flatMap(resolveQualifiedNameIn(_, rest.init, rest.last))
|
.flatMap(resolvedNames => {
|
||||||
|
val allResolvedNames: ArrayBuffer[ResolvedName] = ArrayBuffer.empty
|
||||||
|
resolvedNames.foreach { resolvedName =>
|
||||||
|
val res = resolveQualifiedNameIn(resolvedName, rest.init, rest.last)
|
||||||
|
res match {
|
||||||
|
case Left(resolutionErr) => return Left(resolutionErr)
|
||||||
|
case Right(resolved) =>
|
||||||
|
allResolvedNames ++= resolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Right(allResolvedNames.toList)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private def findExportedSymbolsFor(
|
private def findExportedSymbolsFor(
|
||||||
@ -269,7 +300,7 @@ case class BindingsMap(
|
|||||||
*/
|
*/
|
||||||
def resolveExportedName(
|
def resolveExportedName(
|
||||||
name: String
|
name: String
|
||||||
): Either[ResolutionError, ResolvedName] = {
|
): Either[ResolutionError, List[ResolvedName]] = {
|
||||||
handleAmbiguity(findExportedSymbolsFor(name))
|
handleAmbiguity(findExportedSymbolsFor(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,11 +354,25 @@ object BindingsMap {
|
|||||||
|
|
||||||
private def handleAmbiguity(
|
private def handleAmbiguity(
|
||||||
candidates: List[ResolvedName]
|
candidates: List[ResolvedName]
|
||||||
): Either[ResolutionError, ResolvedName] = {
|
): Either[ResolutionError, List[ResolvedName]] = {
|
||||||
candidates.distinct match {
|
candidates.distinct match {
|
||||||
case List() => Left(ResolutionNotFound)
|
case List() => Left(ResolutionNotFound)
|
||||||
case List(it) => Right(it)
|
case List(it) => Right(List(it))
|
||||||
case items => Left(ResolutionAmbiguous(items))
|
case items =>
|
||||||
|
val areAllResolvedMethods =
|
||||||
|
items.forall(_.isInstanceOf[ResolvedMethod])
|
||||||
|
if (areAllResolvedMethods) {
|
||||||
|
items
|
||||||
|
.map(_.asInstanceOf[ResolvedMethod])
|
||||||
|
.groupBy(_.methodName)
|
||||||
|
.values
|
||||||
|
.toList match {
|
||||||
|
case List(single) => Right(single)
|
||||||
|
case _ => Left(ResolutionAmbiguous(items))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Left(ResolutionAmbiguous(items))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -673,10 +718,20 @@ object BindingsMap {
|
|||||||
override def toAbstract: ImportTarget
|
override def toAbstract: ImportTarget
|
||||||
override def toConcrete(moduleMap: ModuleMap): Option[ImportTarget]
|
override def toConcrete(moduleMap: ModuleMap): Option[ImportTarget]
|
||||||
def findExportedSymbolsFor(name: String): List[ResolvedName]
|
def findExportedSymbolsFor(name: String): List[ResolvedName]
|
||||||
|
|
||||||
|
/** Resolves the symbol with the given name in the context of this import target.
|
||||||
|
* Note that it is valid to have multiple resolved names for a single symbol name,
|
||||||
|
* for example, if the symbol is a name of an extension method and there are multiple
|
||||||
|
* extension methods with the same name defined on multiple types.
|
||||||
|
*
|
||||||
|
* @param name (Unqualified) name of the symbol to resolve
|
||||||
|
* @see [[BindingsMap.resolveName()]]
|
||||||
|
*/
|
||||||
def resolveExportedSymbol(
|
def resolveExportedSymbol(
|
||||||
name: String
|
name: String
|
||||||
): Either[ResolutionError, ResolvedName] =
|
): Either[ResolutionError, List[ResolvedName]] =
|
||||||
BindingsMap.handleAmbiguity(findExportedSymbolsFor(name))
|
BindingsMap.handleAmbiguity(findExportedSymbolsFor(name))
|
||||||
|
|
||||||
def exportedSymbols: Map[String, List[ResolvedName]]
|
def exportedSymbols: Map[String, List[ResolvedName]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,14 +775,21 @@ object BindingsMap {
|
|||||||
|
|
||||||
sealed trait DefinedEntity {
|
sealed trait DefinedEntity {
|
||||||
def name: String
|
def name: String
|
||||||
|
|
||||||
def resolvedIn(module: ModuleReference): ResolvedName = this match {
|
def resolvedIn(module: ModuleReference): ResolvedName = this match {
|
||||||
case t: Type => ResolvedType(module, t)
|
case t: Type => ResolvedType(module, t)
|
||||||
case m: ModuleMethod => ResolvedMethod(module, m)
|
case staticMethod: StaticMethod =>
|
||||||
|
ResolvedStaticMethod(module, staticMethod)
|
||||||
|
case conversionMethod: ConversionMethod =>
|
||||||
|
ResolvedConversionMethod(module, conversionMethod)
|
||||||
|
case m: ModuleMethod => ResolvedModuleMethod(module, m)
|
||||||
case p: PolyglotSymbol => ResolvedPolyglotSymbol(module, p)
|
case p: PolyglotSymbol => ResolvedPolyglotSymbol(module, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
def resolvedIn(module: Module): ResolvedName = resolvedIn(
|
def resolvedIn(module: Module): ResolvedName = resolvedIn(
|
||||||
ModuleReference.Concrete(module)
|
ModuleReference.Concrete(module)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Determines if this entity can be exported during export resolution pass
|
// Determines if this entity can be exported during export resolution pass
|
||||||
def canExport: Boolean
|
def canExport: Boolean
|
||||||
}
|
}
|
||||||
@ -811,12 +873,43 @@ object BindingsMap {
|
|||||||
override def canExport: Boolean = false
|
override def canExport: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A representation of a method defined on the current module.
|
sealed trait Method extends DefinedEntity {
|
||||||
|
override def canExport: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A representation of a method defined on the module, that is, a method
|
||||||
|
* that is not defined on any type, but directly on a module.
|
||||||
*
|
*
|
||||||
* @param name the name of the method.
|
* @param name the name of the method.
|
||||||
*/
|
*/
|
||||||
case class ModuleMethod(override val name: String) extends DefinedEntity {
|
case class ModuleMethod(override val name: String) extends Method {}
|
||||||
override def canExport: Boolean = true
|
|
||||||
|
/** Static or extension method. Note that from the perspective of the runtime, there is no difference
|
||||||
|
* between a static or an extension method. In the following snippet, both methods are considered
|
||||||
|
* a duplicate:
|
||||||
|
* ```
|
||||||
|
* type My_Type
|
||||||
|
* method = 42
|
||||||
|
* My_Type.method = 42
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
case class StaticMethod(
|
||||||
|
methodName: String,
|
||||||
|
tpName: String
|
||||||
|
) extends Method {
|
||||||
|
|
||||||
|
override def name: String = methodName
|
||||||
|
}
|
||||||
|
|
||||||
|
case class ConversionMethod(
|
||||||
|
methodName: String,
|
||||||
|
sourceTpName: String,
|
||||||
|
targetTpName: String
|
||||||
|
) extends Method {
|
||||||
|
override def name: String = methodName
|
||||||
|
|
||||||
|
override def toString: String =
|
||||||
|
targetTpName + ".from (other:" + sourceTpName + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A name resolved to a sum type.
|
/** A name resolved to a sum type.
|
||||||
@ -938,23 +1031,27 @@ object BindingsMap {
|
|||||||
.exportedSymbols
|
.exportedSymbols
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A representation of a name being resolved to a method call.
|
sealed trait ResolvedMethod extends ResolvedName {
|
||||||
|
def methodName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A representation of a resolved method defined directly on module.
|
||||||
*
|
*
|
||||||
* @param module the module defining the method.
|
* @param module the module defining the method.
|
||||||
* @param method the method representation.
|
* @param method the method representation.
|
||||||
*/
|
*/
|
||||||
case class ResolvedMethod(module: ModuleReference, method: ModuleMethod)
|
case class ResolvedModuleMethod(module: ModuleReference, method: ModuleMethod)
|
||||||
extends ResolvedName {
|
extends ResolvedMethod {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def toAbstract: ResolvedMethod = {
|
override def toAbstract: ResolvedModuleMethod = {
|
||||||
this.copy(module = module.toAbstract)
|
this.copy(module = module.toAbstract)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def toConcrete(
|
override def toConcrete(
|
||||||
moduleMap: ModuleMap
|
moduleMap: ModuleMap
|
||||||
): Option[ResolvedMethod] = {
|
): Option[ResolvedModuleMethod] = {
|
||||||
module.toConcrete(moduleMap).map(module => this.copy(module = module))
|
module.toConcrete(moduleMap).map(module => this.copy(module = module))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -979,6 +1076,58 @@ object BindingsMap {
|
|||||||
|
|
||||||
override def qualifiedName: QualifiedName =
|
override def qualifiedName: QualifiedName =
|
||||||
module.getName.createChild(method.name)
|
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(
|
||||||
|
module: ModuleReference,
|
||||||
|
staticMethod: StaticMethod
|
||||||
|
) extends ResolvedMethod {
|
||||||
|
override def toAbstract: ResolvedStaticMethod = {
|
||||||
|
this.copy(module = module.toAbstract)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toConcrete(
|
||||||
|
moduleMap: ModuleMap
|
||||||
|
): Option[ResolvedStaticMethod] = {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
case class ResolvedConversionMethod(
|
||||||
|
module: ModuleReference,
|
||||||
|
conversionMethod: ConversionMethod
|
||||||
|
) extends ResolvedMethod {
|
||||||
|
override def toAbstract: ResolvedConversionMethod = {
|
||||||
|
this.copy(module = module.toAbstract)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toConcrete(
|
||||||
|
moduleMap: ModuleMap
|
||||||
|
): Option[ResolvedConversionMethod] = {
|
||||||
|
module.toConcrete(moduleMap).map { module =>
|
||||||
|
this.copy(module = module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def qualifiedName: QualifiedName =
|
||||||
|
module.getName
|
||||||
|
.createChild(conversionMethod.targetTpName)
|
||||||
|
.createChild(conversionMethod.methodName)
|
||||||
|
|
||||||
|
override def methodName: String = conversionMethod.methodName
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A representation of a name being resolved to a polyglot symbol.
|
/** A representation of a name being resolved to a polyglot symbol.
|
||||||
@ -1046,8 +1195,12 @@ object BindingsMap {
|
|||||||
s" The imported polyglot symbol ${symbol.name};"
|
s" The imported polyglot symbol ${symbol.name};"
|
||||||
case BindingsMap.ResolvedPolyglotField(_, name) =>
|
case BindingsMap.ResolvedPolyglotField(_, name) =>
|
||||||
s" The imported polyglot field ${name};"
|
s" The imported polyglot field ${name};"
|
||||||
case BindingsMap.ResolvedMethod(module, symbol) =>
|
case BindingsMap.ResolvedModuleMethod(module, symbol) =>
|
||||||
s" The method ${symbol.name} defined in module ${module.getName}"
|
s" The method ${symbol.name} defined in module ${module.getName}"
|
||||||
|
case BindingsMap.ResolvedStaticMethod(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}"
|
||||||
case BindingsMap.ResolvedType(module, typ) =>
|
case BindingsMap.ResolvedType(module, typ) =>
|
||||||
s" Type ${typ.name} defined in module ${module.getName}"
|
s" Type ${typ.name} defined in module ${module.getName}"
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import org.enso.compiler.core.ir.module.scope.Import
|
|||||||
import org.enso.compiler.core.ir.module.scope.imports
|
import org.enso.compiler.core.ir.module.scope.imports
|
||||||
import org.enso.compiler.data.BindingsMap
|
import org.enso.compiler.data.BindingsMap
|
||||||
import org.enso.compiler.core.CompilerError
|
import org.enso.compiler.core.CompilerError
|
||||||
|
import org.enso.compiler.data.BindingsMap.ResolvedName
|
||||||
import org.enso.compiler.pass.IRPass
|
import org.enso.compiler.pass.IRPass
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
@ -106,22 +107,23 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
case Some(importTarget) =>
|
case Some(importTarget) =>
|
||||||
val encounteredErrors: ListBuffer[errors.ImportExport] =
|
val encounteredErrors: ListBuffer[errors.ImportExport] =
|
||||||
ListBuffer()
|
ListBuffer()
|
||||||
val imp =
|
onlyNames.foreach { symbol =>
|
||||||
onlyNames.foldLeft(moduleImport: Import) { case (imp, symbol) =>
|
|
||||||
val symbolName = symbol.name
|
val symbolName = symbol.name
|
||||||
importTarget.resolveExportedSymbol(symbolName) match {
|
importTarget.resolveExportedSymbol(symbolName) match {
|
||||||
case Right(resolvedName) =>
|
case Right(resolvedNames) =>
|
||||||
|
resolvedNames.foreach { resolvedName =>
|
||||||
val symbolPath = resolvedName.qualifiedName.toString
|
val symbolPath = resolvedName.qualifiedName.toString
|
||||||
tryAddEncounteredSymbol(
|
tryAddEncounteredSymbol(
|
||||||
encounteredSymbols,
|
encounteredSymbols,
|
||||||
imp,
|
moduleImport,
|
||||||
symbolName,
|
symbolName,
|
||||||
symbolPath
|
symbolPath,
|
||||||
|
Some(resolvedName)
|
||||||
) match {
|
) match {
|
||||||
case Left(error) =>
|
case Left(error) =>
|
||||||
encounteredErrors += error
|
encounteredErrors += error
|
||||||
imp
|
case Right(_) => ()
|
||||||
case Right(imp) => imp
|
}
|
||||||
}
|
}
|
||||||
case Left(resolutionError) =>
|
case Left(resolutionError) =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
@ -132,7 +134,7 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
if (encounteredErrors.nonEmpty) {
|
if (encounteredErrors.nonEmpty) {
|
||||||
Left(encounteredErrors.toList)
|
Left(encounteredErrors.toList)
|
||||||
} else {
|
} else {
|
||||||
Right(imp)
|
Right(moduleImport)
|
||||||
}
|
}
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
@ -164,32 +166,32 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
}
|
}
|
||||||
val encounteredErrors: ListBuffer[errors.ImportExport] =
|
val encounteredErrors: ListBuffer[errors.ImportExport] =
|
||||||
ListBuffer()
|
ListBuffer()
|
||||||
val imp =
|
symbolsToIterate.foreach { symbolName =>
|
||||||
symbolsToIterate.foldLeft(moduleImport: Import) {
|
|
||||||
case (imp, symbolName) =>
|
|
||||||
importTarget.resolveExportedSymbol(symbolName) match {
|
importTarget.resolveExportedSymbol(symbolName) match {
|
||||||
case Left(resolutionError) =>
|
case Left(resolutionError) =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
s"Unreachable: (should have been resolved in previous passes) $resolutionError"
|
s"Unreachable: (should have been resolved in previous passes) $resolutionError"
|
||||||
)
|
)
|
||||||
case Right(resolvedName) =>
|
case Right(List(resolvedName)) =>
|
||||||
tryAddEncounteredSymbol(
|
tryAddEncounteredSymbol(
|
||||||
encounteredSymbols,
|
encounteredSymbols,
|
||||||
imp,
|
moduleImport,
|
||||||
symbolName,
|
symbolName,
|
||||||
resolvedName.qualifiedName.toString
|
resolvedName.qualifiedName.toString,
|
||||||
|
Some(resolvedName)
|
||||||
) match {
|
) match {
|
||||||
case Left(error) =>
|
case Left(error) =>
|
||||||
encounteredErrors += error
|
encounteredErrors += error
|
||||||
imp
|
case Right(_) => ()
|
||||||
case Right(imp) => imp
|
|
||||||
}
|
}
|
||||||
|
// If the symbolName is resolved to multiple objects, we ignore it.
|
||||||
|
case Right(_) => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (encounteredErrors.nonEmpty) {
|
if (encounteredErrors.nonEmpty) {
|
||||||
Left(encounteredErrors.toList)
|
Left(encounteredErrors.toList)
|
||||||
} else {
|
} else {
|
||||||
Right(imp)
|
Right(moduleImport)
|
||||||
}
|
}
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
@ -213,7 +215,8 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
encounteredSymbols,
|
encounteredSymbols,
|
||||||
moduleImport,
|
moduleImport,
|
||||||
rename.name,
|
rename.name,
|
||||||
symbolPath
|
symbolPath,
|
||||||
|
None
|
||||||
) match {
|
) match {
|
||||||
case Left(error) => Left(List(error))
|
case Left(error) => Left(List(error))
|
||||||
case Right(imp) => Right(imp)
|
case Right(imp) => Right(imp)
|
||||||
@ -235,7 +238,8 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
encounteredSymbols,
|
encounteredSymbols,
|
||||||
moduleImport,
|
moduleImport,
|
||||||
importPath.parts.last.name,
|
importPath.parts.last.name,
|
||||||
importPath.name
|
importPath.name,
|
||||||
|
None
|
||||||
) match {
|
) match {
|
||||||
case Left(err) => Left(List(err))
|
case Left(err) => Left(List(err))
|
||||||
case Right(imp) => Right(imp)
|
case Right(imp) => Right(imp)
|
||||||
@ -252,7 +256,8 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
encounteredSymbols,
|
encounteredSymbols,
|
||||||
polyImport,
|
polyImport,
|
||||||
symbolName,
|
symbolName,
|
||||||
symbolPath
|
symbolPath,
|
||||||
|
None
|
||||||
) match {
|
) match {
|
||||||
case Left(err) => Left(List(err))
|
case Left(err) => Left(List(err))
|
||||||
case Right(imp) => Right(imp)
|
case Right(imp) => Right(imp)
|
||||||
@ -288,7 +293,8 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
encounteredSymbols: EncounteredSymbols,
|
encounteredSymbols: EncounteredSymbols,
|
||||||
currentImport: Import,
|
currentImport: Import,
|
||||||
symbolName: String,
|
symbolName: String,
|
||||||
symbolPath: String
|
symbolPath: String,
|
||||||
|
resolvedName: Option[ResolvedName]
|
||||||
): Either[errors.ImportExport, Import] = {
|
): Either[errors.ImportExport, Import] = {
|
||||||
if (encounteredSymbols.containsSymbol(symbolName)) {
|
if (encounteredSymbols.containsSymbol(symbolName)) {
|
||||||
val encounteredFullName =
|
val encounteredFullName =
|
||||||
@ -304,18 +310,31 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
)
|
)
|
||||||
Right(currentImport.addDiagnostic(warn))
|
Right(currentImport.addDiagnostic(warn))
|
||||||
} else {
|
} else {
|
||||||
Left(
|
// The symbol was encountered before and the physical path is different.
|
||||||
createErrorForAmbiguousImport(
|
val ambiguousImpErr = createErrorForAmbiguousImport(
|
||||||
originalImport,
|
originalImport,
|
||||||
encounteredFullName,
|
encounteredFullName,
|
||||||
currentImport,
|
currentImport,
|
||||||
symbolName,
|
symbolName,
|
||||||
symbolPath
|
symbolPath
|
||||||
)
|
)
|
||||||
)
|
encounteredSymbols.getResolvedNameForSymbol(symbolName) match {
|
||||||
|
case Some(resolvedMethod: BindingsMap.ResolvedMethod)
|
||||||
|
if resolvedMethod.methodName == symbolName =>
|
||||||
|
// This is a valid ambiguous case - in previously encountered import, the symbol was resolved
|
||||||
|
// to either an extension, static, or conversion method.
|
||||||
|
Right(currentImport)
|
||||||
|
case _ =>
|
||||||
|
Left(ambiguousImpErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
encounteredSymbols.addSymbol(currentImport, symbolName, symbolPath)
|
encounteredSymbols.addSymbol(
|
||||||
|
currentImport,
|
||||||
|
symbolName,
|
||||||
|
symbolPath,
|
||||||
|
resolvedName
|
||||||
|
)
|
||||||
Right(currentImport)
|
Right(currentImport)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,14 +369,26 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param symbolPath Fully qualified name of the symbol, i.e., its physical path.
|
||||||
|
* @param resolvedName The optinal resolved name of the symbol.
|
||||||
|
* @param originalImport The import IR from which the symbol was originally imported.
|
||||||
|
* i.e. the first encountered import IR that imports the symbol.
|
||||||
|
*/
|
||||||
|
private case class SymbolTarget(
|
||||||
|
symbolPath: String,
|
||||||
|
resolvedName: Option[ResolvedName],
|
||||||
|
originalImport: Import
|
||||||
|
)
|
||||||
|
|
||||||
/** For every encountered symbol name, we keep track of the original import from which it was imported,
|
/** For every encountered symbol name, we keep track of the original import from which it was imported,
|
||||||
* along with the entity path. The entity path is vital to decide whether an imported symbol is duplicated
|
* along with the entity path. The entity path is vital to decide whether an imported symbol is duplicated
|
||||||
* or ambiguous.
|
* or ambiguous.
|
||||||
|
* Note that there are some exceptions that are allowed to be ambiguous, like extension methods.
|
||||||
*/
|
*/
|
||||||
private class EncounteredSymbols(
|
private class EncounteredSymbols(
|
||||||
private val encounteredSymbols: mutable.Map[
|
private val encounteredSymbols: mutable.Map[
|
||||||
String,
|
String,
|
||||||
(Import, String)
|
SymbolTarget
|
||||||
] = mutable.HashMap.empty
|
] = mutable.HashMap.empty
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -370,9 +401,13 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
def addSymbol(
|
def addSymbol(
|
||||||
imp: Import,
|
imp: Import,
|
||||||
symbol: String,
|
symbol: String,
|
||||||
symbolPath: String
|
symbolPath: String,
|
||||||
|
resolvedName: Option[ResolvedName]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
encounteredSymbols.put(symbol, (imp, symbolPath))
|
encounteredSymbols.put(
|
||||||
|
symbol,
|
||||||
|
SymbolTarget(symbolPath, resolvedName, imp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the entity path for the symbol.
|
/** Returns the entity path for the symbol.
|
||||||
@ -381,8 +416,19 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
symbol: String
|
symbol: String
|
||||||
): String = {
|
): String = {
|
||||||
encounteredSymbols.get(symbol) match {
|
encounteredSymbols.get(symbol) match {
|
||||||
case Some((_, fullName)) =>
|
case Some(symbolTarget) =>
|
||||||
fullName
|
symbolTarget.symbolPath
|
||||||
|
case None =>
|
||||||
|
throw new IllegalStateException("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getResolvedNameForSymbol(
|
||||||
|
symbol: String
|
||||||
|
): Option[ResolvedName] = {
|
||||||
|
encounteredSymbols.get(symbol) match {
|
||||||
|
case Some(symbolTarget) =>
|
||||||
|
symbolTarget.resolvedName
|
||||||
case None =>
|
case None =>
|
||||||
throw new IllegalStateException("unreachable")
|
throw new IllegalStateException("unreachable")
|
||||||
}
|
}
|
||||||
@ -393,7 +439,7 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
|||||||
def getOriginalImportForSymbol(
|
def getOriginalImportForSymbol(
|
||||||
symbol: String
|
symbol: String
|
||||||
): Option[Import] = {
|
): Option[Import] = {
|
||||||
encounteredSymbols.get(symbol).map(_._1)
|
encounteredSymbols.get(symbol).map(_.originalImport)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import org.enso.compiler.core.ir.{
|
|||||||
Name
|
Name
|
||||||
}
|
}
|
||||||
import org.enso.compiler.core.ir.module.scope.definition
|
import org.enso.compiler.core.ir.module.scope.definition
|
||||||
import org.enso.compiler.data.BindingsMap.{Resolution, ResolvedMethod}
|
import org.enso.compiler.data.BindingsMap.{Resolution, ResolvedModuleMethod}
|
||||||
import org.enso.compiler.core.CompilerError
|
import org.enso.compiler.core.CompilerError
|
||||||
import org.enso.compiler.core.ir.expression.{Application, Operator}
|
import org.enso.compiler.core.ir.expression.{Application, Operator}
|
||||||
import org.enso.compiler.pass.IRPass
|
import org.enso.compiler.pass.IRPass
|
||||||
@ -486,7 +486,7 @@ object AutomaticParallelism extends IRPass {
|
|||||||
// the called function. It is then sequenced with statuses of the
|
// the called function. It is then sequenced with statuses of the
|
||||||
// arguments.
|
// arguments.
|
||||||
app.function.getMetadata(MethodCalls) match {
|
app.function.getMetadata(MethodCalls) match {
|
||||||
case Some(Resolution(method: ResolvedMethod)) =>
|
case Some(Resolution(method: ResolvedModuleMethod)) =>
|
||||||
val methodIr = method.unsafeGetIr("Invalid method call resolution.")
|
val methodIr = method.unsafeGetIr("Invalid method call resolution.")
|
||||||
val isParallelize = methodIr
|
val isParallelize = methodIr
|
||||||
.getMetadata(ModuleAnnotations)
|
.getMetadata(ModuleAnnotations)
|
||||||
|
@ -8,6 +8,11 @@ import org.enso.compiler.core.ir.module.scope.definition
|
|||||||
import org.enso.compiler.core.ir.module.scope.imports
|
import org.enso.compiler.core.ir.module.scope.imports
|
||||||
import org.enso.compiler.core.ir.MetadataStorage.MetadataPair
|
import org.enso.compiler.core.ir.MetadataStorage.MetadataPair
|
||||||
import org.enso.compiler.data.BindingsMap
|
import org.enso.compiler.data.BindingsMap
|
||||||
|
import org.enso.compiler.data.BindingsMap.{
|
||||||
|
ConversionMethod,
|
||||||
|
ModuleMethod,
|
||||||
|
StaticMethod
|
||||||
|
}
|
||||||
import org.enso.compiler.pass.IRPass
|
import org.enso.compiler.pass.IRPass
|
||||||
import org.enso.compiler.pass.desugar.{
|
import org.enso.compiler.pass.desugar.{
|
||||||
ComplexType,
|
ComplexType,
|
||||||
@ -60,33 +65,61 @@ case object BindingAnalysis extends IRPass {
|
|||||||
val importedPolyglot = ir.imports.collect { case poly: imports.Polyglot =>
|
val importedPolyglot = ir.imports.collect { case poly: imports.Polyglot =>
|
||||||
BindingsMap.PolyglotSymbol(poly.getVisibleName)
|
BindingsMap.PolyglotSymbol(poly.getVisibleName)
|
||||||
}
|
}
|
||||||
val moduleMethods = ir.bindings
|
val staticMethods: List[BindingsMap.Method] = ir.bindings.collect {
|
||||||
.collect { case method: definition.Method.Explicit =>
|
case method: definition.Method.Explicit =>
|
||||||
val ref = method.methodReference
|
val ref = method.methodReference
|
||||||
ref.typePointer match {
|
ref.typePointer match {
|
||||||
case Some(Name.Qualified(List(), _, _, _)) =>
|
case Some(Name.Qualified(List(), _, _, _)) =>
|
||||||
Some(ref.methodName.name)
|
Some(ModuleMethod(ref.methodName.name))
|
||||||
case Some(Name.Qualified(List(n), _, _, _)) =>
|
case Some(Name.Qualified(List(n), _, _, _)) =>
|
||||||
val shadowed = definedSumTypes.exists(_.name == n.name)
|
val shadowed = definedSumTypes.exists(_.name == n.name)
|
||||||
if (!shadowed && n.name == moduleContext.getName().item)
|
if (!shadowed && n.name == moduleContext.getName().item)
|
||||||
Some(ref.methodName.name)
|
Some(ModuleMethod(ref.methodName.name))
|
||||||
else None
|
else {
|
||||||
|
Some(
|
||||||
|
StaticMethod(ref.methodName.name, n.name)
|
||||||
|
)
|
||||||
|
}
|
||||||
case Some(literal: Name.Literal) =>
|
case Some(literal: Name.Literal) =>
|
||||||
val shadowed = definedSumTypes.exists(_.name == literal.name)
|
val shadowed = definedSumTypes.exists(_.name == literal.name)
|
||||||
if (!shadowed && literal.name == moduleContext.getName().item)
|
if (!shadowed && literal.name == moduleContext.getName().item)
|
||||||
Some(ref.methodName.name)
|
Some(ModuleMethod(ref.methodName.name))
|
||||||
else None
|
else {
|
||||||
case None => Some(ref.methodName.name)
|
Some(
|
||||||
|
StaticMethod(ref.methodName.name, literal.name)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case None => Some(ModuleMethod(ref.methodName.name))
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
|
case conversion: definition.Method.Conversion =>
|
||||||
|
val targetTpNameOpt = conversion.typeName match {
|
||||||
|
case Some(targetTpName) =>
|
||||||
|
Some(targetTpName.name)
|
||||||
|
case None => None
|
||||||
}
|
}
|
||||||
.flatten
|
val sourceTpNameOpt = conversion.sourceTypeName match {
|
||||||
.map(BindingsMap.ModuleMethod)
|
case name: Name =>
|
||||||
|
Some(name.name)
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
(sourceTpNameOpt, targetTpNameOpt) match {
|
||||||
|
case (Some(sourceTpName), Some(targetTpName)) =>
|
||||||
|
Some(
|
||||||
|
ConversionMethod(
|
||||||
|
conversion.methodName.name,
|
||||||
|
sourceTpName,
|
||||||
|
targetTpName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}.flatten
|
||||||
ir.updateMetadata(
|
ir.updateMetadata(
|
||||||
new MetadataPair(
|
new MetadataPair(
|
||||||
this,
|
this,
|
||||||
BindingsMap(
|
BindingsMap(
|
||||||
definedSumTypes ++ importedPolyglot ++ moduleMethods,
|
definedSumTypes ++ importedPolyglot ++ staticMethods,
|
||||||
moduleContext.moduleReference()
|
moduleContext.moduleReference()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -199,16 +199,22 @@ case object FullyQualifiedNames extends IRPass {
|
|||||||
case tp: Definition.Type =>
|
case tp: Definition.Type =>
|
||||||
tp.copy(members =
|
tp.copy(members =
|
||||||
tp.members.map(
|
tp.members.map(
|
||||||
_.mapExpressions(
|
_.mapExpressions(expr => {
|
||||||
|
val selfTypeResolution =
|
||||||
|
bindings.resolveName(tp.name.name) match {
|
||||||
|
case Right(List(resolvedName)) =>
|
||||||
|
Some(Resolution(resolvedName))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
processExpression(
|
processExpression(
|
||||||
_,
|
expr,
|
||||||
bindings,
|
bindings,
|
||||||
tp.params.map(_.name),
|
tp.params.map(_.name),
|
||||||
freshNameSupply,
|
freshNameSupply,
|
||||||
bindings.resolveName(tp.name.name).toOption.map(Resolution),
|
selfTypeResolution,
|
||||||
pkgRepo
|
pkgRepo
|
||||||
)
|
)
|
||||||
)
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ import org.enso.compiler.data.BindingsMap
|
|||||||
import org.enso.compiler.data.BindingsMap.{
|
import org.enso.compiler.data.BindingsMap.{
|
||||||
Resolution,
|
Resolution,
|
||||||
ResolutionNotFound,
|
ResolutionNotFound,
|
||||||
ResolvedMethod,
|
ResolvedModule,
|
||||||
ResolvedModule
|
ResolvedModuleMethod
|
||||||
}
|
}
|
||||||
import org.enso.compiler.core.CompilerError
|
import org.enso.compiler.core.CompilerError
|
||||||
import org.enso.compiler.core.ConstantsNames
|
import org.enso.compiler.core.ConstantsNames
|
||||||
@ -122,15 +122,21 @@ case object GlobalNames extends IRPass {
|
|||||||
case tp: Definition.Type =>
|
case tp: Definition.Type =>
|
||||||
tp.copy(members =
|
tp.copy(members =
|
||||||
tp.members.map(
|
tp.members.map(
|
||||||
_.mapExpressions(
|
_.mapExpressions { expr =>
|
||||||
|
val selfTypeResolution =
|
||||||
|
bindings.resolveName(tp.name.name) match {
|
||||||
|
case Right(List(resolvedName)) =>
|
||||||
|
Some(Resolution(resolvedName))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
processExpression(
|
processExpression(
|
||||||
_,
|
expr,
|
||||||
bindings,
|
bindings,
|
||||||
tp.params,
|
tp.params,
|
||||||
freshNameSupply,
|
freshNameSupply,
|
||||||
bindings.resolveName(tp.name.name).toOption.map(Resolution)
|
selfTypeResolution
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -181,10 +187,17 @@ case object GlobalNames extends IRPass {
|
|||||||
lit,
|
lit,
|
||||||
errors.Resolution.ResolverError(error)
|
errors.Resolution.ResolverError(error)
|
||||||
)
|
)
|
||||||
case Right(r @ BindingsMap.ResolvedMethod(mod, method)) =>
|
case Right(values)
|
||||||
|
if values.exists(_.isInstanceOf[ResolvedModuleMethod]) =>
|
||||||
|
val resolvedModuleMethod = values.collectFirst {
|
||||||
|
case r: ResolvedModuleMethod => r
|
||||||
|
}.get
|
||||||
if (isInsideApplication) {
|
if (isInsideApplication) {
|
||||||
lit.updateMetadata(
|
lit.updateMetadata(
|
||||||
new MetadataPair(this, BindingsMap.Resolution(r))
|
new MetadataPair(
|
||||||
|
this,
|
||||||
|
BindingsMap.Resolution(resolvedModuleMethod)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val self = freshNameSupply
|
val self = freshNameSupply
|
||||||
@ -193,14 +206,15 @@ case object GlobalNames extends IRPass {
|
|||||||
new MetadataPair(
|
new MetadataPair(
|
||||||
this,
|
this,
|
||||||
BindingsMap.Resolution(
|
BindingsMap.Resolution(
|
||||||
BindingsMap.ResolvedModule(mod)
|
BindingsMap
|
||||||
|
.ResolvedModule(resolvedModuleMethod.module)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// The synthetic applications gets the location so that instrumentation
|
// The synthetic applications gets the location so that instrumentation
|
||||||
// identifies the node correctly
|
// identifies the node correctly
|
||||||
val fun = lit.copy(
|
val fun = lit.copy(
|
||||||
name = method.name,
|
name = resolvedModuleMethod.method.name,
|
||||||
location = None
|
location = None
|
||||||
)
|
)
|
||||||
val app = Application.Prefix(
|
val app = Application.Prefix(
|
||||||
@ -222,10 +236,12 @@ case object GlobalNames extends IRPass {
|
|||||||
fun.passData.remove(ExpressionAnnotations)
|
fun.passData.remove(ExpressionAnnotations)
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
case Right(value) =>
|
case Right(values) =>
|
||||||
|
values.foldLeft(lit)((lit, value) =>
|
||||||
lit.updateMetadata(
|
lit.updateMetadata(
|
||||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -297,7 +313,7 @@ case object GlobalNames extends IRPass {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
processedFun.getMetadata(this) match {
|
processedFun.getMetadata(this) match {
|
||||||
case Some(Resolution(ResolvedMethod(mod, _))) if !isLocalVar(fun) =>
|
case Some(Resolution(ResolvedModuleMethod(mod, _))) if !isLocalVar(fun) =>
|
||||||
val self = freshNameSupply
|
val self = freshNameSupply
|
||||||
.newName()
|
.newName()
|
||||||
.updateMetadata(
|
.updateMetadata(
|
||||||
@ -408,7 +424,7 @@ case object GlobalNames extends IRPass {
|
|||||||
)
|
)
|
||||||
.resolveExportedName(consName.name)
|
.resolveExportedName(consName.name)
|
||||||
resolution match {
|
resolution match {
|
||||||
case Right(res) => Some(res)
|
case Right(List(res)) => Some(res)
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
case _ => None
|
case _ => None
|
||||||
|
@ -7,7 +7,11 @@ import org.enso.compiler.core.ir.Name
|
|||||||
import org.enso.compiler.core.ir.MetadataStorage.MetadataPair
|
import org.enso.compiler.core.ir.MetadataStorage.MetadataPair
|
||||||
import org.enso.compiler.core.ir.expression.Application
|
import org.enso.compiler.core.ir.expression.Application
|
||||||
import org.enso.compiler.data.BindingsMap
|
import org.enso.compiler.data.BindingsMap
|
||||||
import org.enso.compiler.data.BindingsMap.{Resolution, ResolvedModule}
|
import org.enso.compiler.data.BindingsMap.{
|
||||||
|
Resolution,
|
||||||
|
ResolvedModule,
|
||||||
|
ResolvedModuleMethod
|
||||||
|
}
|
||||||
import org.enso.compiler.pass.IRPass
|
import org.enso.compiler.pass.IRPass
|
||||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||||
|
|
||||||
@ -63,9 +67,9 @@ object MethodCalls extends IRPass {
|
|||||||
app.function match {
|
app.function match {
|
||||||
case name: Name if name.isMethod =>
|
case name: Name if name.isMethod =>
|
||||||
app.arguments match {
|
app.arguments match {
|
||||||
case first :: _ =>
|
case selfArgument :: _ =>
|
||||||
val targetBindings =
|
val targetBindings =
|
||||||
first.value.getMetadata(GlobalNames) match {
|
selfArgument.value.getMetadata(GlobalNames) match {
|
||||||
case Some(Resolution(ResolvedModule(module))) =>
|
case Some(Resolution(ResolvedModule(module))) =>
|
||||||
val moduleIr = module.unsafeAsModule().getIr
|
val moduleIr = module.unsafeAsModule().getIr
|
||||||
Option
|
Option
|
||||||
@ -77,13 +81,23 @@ object MethodCalls extends IRPass {
|
|||||||
}
|
}
|
||||||
targetBindings match {
|
targetBindings match {
|
||||||
case Some(bindings) =>
|
case Some(bindings) =>
|
||||||
val resolution =
|
val resolutionsOpt =
|
||||||
bindings.exportedSymbols.get(name.name)
|
bindings.exportedSymbols.get(name.name)
|
||||||
resolution match {
|
val resolvedModuleMethodOpt = resolutionsOpt match {
|
||||||
case Some(List(resolution)) =>
|
case Some(resolutions) =>
|
||||||
|
resolutions.collectFirst { case x: ResolvedModuleMethod =>
|
||||||
|
x
|
||||||
|
}
|
||||||
|
case None => None
|
||||||
|
}
|
||||||
|
resolvedModuleMethodOpt match {
|
||||||
|
case Some(resolvedModuleMethod) =>
|
||||||
val newName =
|
val newName =
|
||||||
name.updateMetadata(
|
name.updateMetadata(
|
||||||
new MetadataPair(this, Resolution(resolution))
|
new MetadataPair(
|
||||||
|
this,
|
||||||
|
Resolution(resolvedModuleMethod)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val newArgs =
|
val newArgs =
|
||||||
app.arguments.map(
|
app.arguments.map(
|
||||||
|
@ -190,43 +190,60 @@ case object MethodDefinitions extends IRPass {
|
|||||||
typePointer,
|
typePointer,
|
||||||
errors.Resolution.ResolverError(err)
|
errors.Resolution.ResolverError(err)
|
||||||
)
|
)
|
||||||
case Right(_: BindingsMap.ResolvedConstructor) =>
|
case Right(resolvedItems) =>
|
||||||
|
assert(resolvedItems.size == 1, "Expected a single resolution")
|
||||||
|
resolvedItems.head match {
|
||||||
|
case _: BindingsMap.ResolvedConstructor =>
|
||||||
errors.Resolution(
|
errors.Resolution(
|
||||||
typePointer,
|
typePointer,
|
||||||
errors.Resolution.UnexpectedConstructor(
|
errors.Resolution.UnexpectedConstructor(
|
||||||
"a method definition target"
|
"a method definition target"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case Right(value: BindingsMap.ResolvedModule) =>
|
case value: BindingsMap.ResolvedModule =>
|
||||||
typePointer.updateMetadata(
|
typePointer.updateMetadata(
|
||||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||||
)
|
)
|
||||||
case Right(value: BindingsMap.ResolvedType) =>
|
case value: BindingsMap.ResolvedType =>
|
||||||
typePointer.updateMetadata(
|
typePointer.updateMetadata(
|
||||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||||
)
|
)
|
||||||
case Right(_: BindingsMap.ResolvedPolyglotSymbol) =>
|
case _: BindingsMap.ResolvedPolyglotSymbol =>
|
||||||
errors.Resolution(
|
errors.Resolution(
|
||||||
typePointer,
|
typePointer,
|
||||||
errors.Resolution.UnexpectedPolyglot(
|
errors.Resolution.UnexpectedPolyglot(
|
||||||
"a method definition target"
|
"a method definition target"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case Right(_: BindingsMap.ResolvedPolyglotField) =>
|
case _: BindingsMap.ResolvedPolyglotField =>
|
||||||
errors.Resolution(
|
errors.Resolution(
|
||||||
typePointer,
|
typePointer,
|
||||||
errors.Resolution.UnexpectedPolyglot(
|
errors.Resolution.UnexpectedPolyglot(
|
||||||
"a method definition target"
|
"a method definition target"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
case _: BindingsMap.ResolvedModuleMethod =>
|
||||||
errors.Resolution(
|
errors.Resolution(
|
||||||
typePointer,
|
typePointer,
|
||||||
errors.Resolution.UnexpectedMethod(
|
errors.Resolution.UnexpectedMethod(
|
||||||
"a method definition target"
|
"a method definition target"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
case _: BindingsMap.ResolvedStaticMethod =>
|
||||||
|
errors.Resolution(
|
||||||
|
typePointer,
|
||||||
|
errors.Resolution.UnexpectedMethod(
|
||||||
|
"a static method definition target"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case _: BindingsMap.ResolvedConversionMethod =>
|
||||||
|
errors.Resolution(
|
||||||
|
typePointer,
|
||||||
|
errors.Resolution.UnexpectedMethod(
|
||||||
|
"a conversion method definition target"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case tp: errors.Resolution => tp
|
case tp: errors.Resolution => tp
|
||||||
case _ =>
|
case _ =>
|
||||||
|
@ -78,6 +78,51 @@ object Patterns extends IRPass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Just delegates to the same-named method from [[BindingsMap]]
|
||||||
|
* and expects a single resolution.
|
||||||
|
*/
|
||||||
|
private def resolveSingleQualifiedName(
|
||||||
|
bindingsMap: BindingsMap,
|
||||||
|
parts: List[String]
|
||||||
|
): Either[BindingsMap.ResolutionError, BindingsMap.ResolvedName] = {
|
||||||
|
bindingsMap.resolveQualifiedName(parts) match {
|
||||||
|
case Left(err) => Left(err)
|
||||||
|
case Right(resolvedNames) =>
|
||||||
|
assert(resolvedNames.size == 1, "Expected a single resolution")
|
||||||
|
Right(resolvedNames.head)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc [[resolveSingleQualifiedName]]
|
||||||
|
*/
|
||||||
|
private def resolveSingleQualifiedNameIn(
|
||||||
|
bindingsMap: BindingsMap,
|
||||||
|
scope: BindingsMap.ResolvedName,
|
||||||
|
submoduleNames: List[String],
|
||||||
|
finalItem: String
|
||||||
|
): Either[BindingsMap.ResolutionError, BindingsMap.ResolvedName] = {
|
||||||
|
bindingsMap.resolveQualifiedNameIn(scope, submoduleNames, finalItem) match {
|
||||||
|
case Left(err) => Left(err)
|
||||||
|
case Right(resolvedNames) =>
|
||||||
|
assert(resolvedNames.size == 1, "Expected a single resolution")
|
||||||
|
Right(resolvedNames.head)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc [[resolveSingleQualifiedName]]
|
||||||
|
*/
|
||||||
|
private def resolveSingleName(
|
||||||
|
bindingsMap: BindingsMap,
|
||||||
|
name: String
|
||||||
|
): Either[BindingsMap.ResolutionError, BindingsMap.ResolvedName] = {
|
||||||
|
bindingsMap.resolveName(name) match {
|
||||||
|
case Left(err) => Left(err)
|
||||||
|
case Right(resolvedNames) =>
|
||||||
|
assert(resolvedNames.size == 1, "Expected a single resolution")
|
||||||
|
Right(resolvedNames.head)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def doExpression(
|
private def doExpression(
|
||||||
expr: Expression,
|
expr: Expression,
|
||||||
bindings: BindingsMap,
|
bindings: BindingsMap,
|
||||||
@ -93,7 +138,8 @@ object Patterns extends IRPass {
|
|||||||
qual.parts match {
|
qual.parts match {
|
||||||
case (_: Name.SelfType) :: (others :+ item) =>
|
case (_: Name.SelfType) :: (others :+ item) =>
|
||||||
selfTypeResolution.map(
|
selfTypeResolution.map(
|
||||||
bindings.resolveQualifiedNameIn(
|
resolveSingleQualifiedNameIn(
|
||||||
|
bindings,
|
||||||
_,
|
_,
|
||||||
others.map(_.name),
|
others.map(_.name),
|
||||||
item.name
|
item.name
|
||||||
@ -102,11 +148,11 @@ object Patterns extends IRPass {
|
|||||||
case _ =>
|
case _ =>
|
||||||
val parts = qual.parts.map(_.name)
|
val parts = qual.parts.map(_.name)
|
||||||
Some(
|
Some(
|
||||||
bindings.resolveQualifiedName(parts)
|
resolveSingleQualifiedName(bindings, parts)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case lit: Name.Literal =>
|
case lit: Name.Literal =>
|
||||||
Some(bindings.resolveName(lit.name))
|
Some(resolveSingleName(bindings, lit.name))
|
||||||
case _: Name.SelfType =>
|
case _: Name.SelfType =>
|
||||||
selfTypeResolution.map(Right(_))
|
selfTypeResolution.map(Right(_))
|
||||||
case _ => None
|
case _ => None
|
||||||
@ -140,14 +186,34 @@ object Patterns extends IRPass {
|
|||||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||||
)
|
)
|
||||||
|
|
||||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
case Right(_: BindingsMap.ResolvedModuleMethod) =>
|
||||||
val r = errors.Resolution(
|
val r = errors.Resolution(
|
||||||
consName,
|
consName,
|
||||||
errors.Resolution.UnexpectedMethod(
|
errors.Resolution.UnexpectedMethod(
|
||||||
"a pattern match"
|
"method inside pattern match"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
r.setLocation(consName.location)
|
r.setLocation(consName.location)
|
||||||
|
case Right(_: BindingsMap.ResolvedStaticMethod) =>
|
||||||
|
val r = errors.Resolution(
|
||||||
|
consName,
|
||||||
|
errors.Resolution.UnexpectedMethod(
|
||||||
|
"static method inside pattern match"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
r.setLocation(consName.location)
|
||||||
|
case Right(_: BindingsMap.ResolvedConversionMethod) =>
|
||||||
|
val r = errors.Resolution(
|
||||||
|
consName,
|
||||||
|
errors.Resolution.UnexpectedMethod(
|
||||||
|
"conversion method inside pattern match"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
r.setLocation(consName.location)
|
||||||
|
case Right(_) =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible, should be transformed into an error before."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.getOrElse(consName)
|
.getOrElse(consName)
|
||||||
|
|
||||||
@ -158,7 +224,15 @@ object Patterns extends IRPass {
|
|||||||
case BindingsMap.ResolvedModule(_) => 0
|
case BindingsMap.ResolvedModule(_) => 0
|
||||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) => 0
|
case BindingsMap.ResolvedPolyglotSymbol(_, _) => 0
|
||||||
case BindingsMap.ResolvedPolyglotField(_, _) => 0
|
case BindingsMap.ResolvedPolyglotField(_, _) => 0
|
||||||
case BindingsMap.ResolvedMethod(_, _) =>
|
case BindingsMap.ResolvedModuleMethod(_, _) =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible, should be transformed into an error before."
|
||||||
|
)
|
||||||
|
case BindingsMap.ResolvedStaticMethod(_, _) =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible, should be transformed into an error before."
|
||||||
|
)
|
||||||
|
case BindingsMap.ResolvedConversionMethod(_, _) =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible, should be transformed into an error before."
|
"Impossible, should be transformed into an error before."
|
||||||
)
|
)
|
||||||
@ -186,10 +260,10 @@ object Patterns extends IRPass {
|
|||||||
case qual: Name.Qualified =>
|
case qual: Name.Qualified =>
|
||||||
val parts = qual.parts.map(_.name)
|
val parts = qual.parts.map(_.name)
|
||||||
Some(
|
Some(
|
||||||
bindings.resolveQualifiedName(parts)
|
resolveSingleQualifiedName(bindings, parts)
|
||||||
)
|
)
|
||||||
case lit: Name.Literal =>
|
case lit: Name.Literal =>
|
||||||
Some(bindings.resolveName(lit.name))
|
Some(resolveSingleName(bindings, lit.name))
|
||||||
case _: Name.SelfType =>
|
case _: Name.SelfType =>
|
||||||
selfTypeResolution.map(Right(_))
|
selfTypeResolution.map(Right(_))
|
||||||
case _ => None
|
case _ => None
|
||||||
@ -223,15 +297,31 @@ object Patterns extends IRPass {
|
|||||||
tpeName,
|
tpeName,
|
||||||
errors.Resolution.UnexpectedPolyglot(s"type pattern case")
|
errors.Resolution.UnexpectedPolyglot(s"type pattern case")
|
||||||
)*/
|
)*/
|
||||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
case Right(_: BindingsMap.ResolvedModuleMethod) =>
|
||||||
errors.Resolution(
|
errors.Resolution(
|
||||||
tpeName,
|
tpeName,
|
||||||
errors.Resolution.UnexpectedMethod(s"type pattern case")
|
errors.Resolution
|
||||||
|
.UnexpectedMethod(s"method type pattern case")
|
||||||
|
)
|
||||||
|
case Right(_: BindingsMap.ResolvedStaticMethod) =>
|
||||||
|
errors.Resolution(
|
||||||
|
tpeName,
|
||||||
|
errors.Resolution.UnexpectedMethod(
|
||||||
|
s"static method inside type pattern case"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case Right(_: BindingsMap.ResolvedConversionMethod) =>
|
||||||
|
errors.Resolution(
|
||||||
|
tpeName,
|
||||||
|
errors.Resolution.UnexpectedMethod(
|
||||||
|
s"conversion method inside type pattern case"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
case Right(_: BindingsMap.ResolvedModule) =>
|
case Right(_: BindingsMap.ResolvedModule) =>
|
||||||
errors.Resolution(
|
errors.Resolution(
|
||||||
tpeName,
|
tpeName,
|
||||||
errors.Resolution.UnexpectedModule(s"type pattern case")
|
errors.Resolution
|
||||||
|
.UnexpectedModule(s"module inside type pattern case")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.getOrElse(tpeName)
|
.getOrElse(tpeName)
|
||||||
|
@ -182,8 +182,10 @@ case object TypeNames extends IRPass {
|
|||||||
bindingsMap.resolveQualifiedName(n.parts.map(_.name))
|
bindingsMap.resolveQualifiedName(n.parts.map(_.name))
|
||||||
)
|
)
|
||||||
case selfRef: Name.SelfType =>
|
case selfRef: Name.SelfType =>
|
||||||
val resolvedSelfType = selfTypeInfo.selfType.toRight {
|
val resolvedSelfType = selfTypeInfo.selfType match {
|
||||||
BindingsMap.SelfTypeOutsideOfTypeDefinition
|
case None => Left(BindingsMap.SelfTypeOutsideOfTypeDefinition)
|
||||||
|
case Some(selfType) =>
|
||||||
|
Right(List(selfType))
|
||||||
}
|
}
|
||||||
processResolvedName(selfRef, resolvedSelfType)
|
processResolvedName(selfRef, resolvedSelfType)
|
||||||
case s: `type`.Set =>
|
case s: `type`.Set =>
|
||||||
@ -192,10 +194,17 @@ case object TypeNames extends IRPass {
|
|||||||
|
|
||||||
private def processResolvedName(
|
private def processResolvedName(
|
||||||
name: Name,
|
name: Name,
|
||||||
resolvedName: Either[BindingsMap.ResolutionError, BindingsMap.ResolvedName]
|
resolvedNamesOpt: Either[BindingsMap.ResolutionError, List[
|
||||||
|
BindingsMap.ResolvedName
|
||||||
|
]]
|
||||||
): Name =
|
): Name =
|
||||||
resolvedName
|
resolvedNamesOpt
|
||||||
.map(res => name.updateMetadata(new MetadataPair(this, Resolution(res))))
|
.map(resolvedNames => {
|
||||||
|
resolvedNames.foreach { resolvedName =>
|
||||||
|
name.updateMetadata(new MetadataPair(this, Resolution(resolvedName)))
|
||||||
|
}
|
||||||
|
name
|
||||||
|
})
|
||||||
.fold(
|
.fold(
|
||||||
error =>
|
error =>
|
||||||
errors.Resolution(name, errors.Resolution.ResolverError(error)),
|
errors.Resolution(name, errors.Resolution.ResolverError(error)),
|
||||||
|
@ -65,7 +65,7 @@ trait IRUtils {
|
|||||||
.flatMap { symbol =>
|
.flatMap { symbol =>
|
||||||
symbol.getMetadata(MethodCalls).flatMap { resolution =>
|
symbol.getMetadata(MethodCalls).flatMap { resolution =>
|
||||||
resolution.target match {
|
resolution.target match {
|
||||||
case BindingsMap.ResolvedMethod(module, _)
|
case BindingsMap.ResolvedModuleMethod(module, _)
|
||||||
if module.getName == moduleName =>
|
if module.getName == moduleName =>
|
||||||
Some(symbol)
|
Some(symbol)
|
||||||
case _ =>
|
case _ =>
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package org.enso.compiler.passes;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
import org.enso.compiler.core.ir.Module;
|
||||||
|
import org.enso.compiler.core.ir.Name;
|
||||||
|
import org.enso.compiler.core.ir.expression.Application;
|
||||||
|
import org.enso.compiler.data.BindingsMap;
|
||||||
|
import org.enso.compiler.pass.resolve.MethodCalls$;
|
||||||
|
import org.enso.test.utils.ContextUtils;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class MethodCallsTest {
|
||||||
|
@Test
|
||||||
|
public void resolveSimpleModuleMethod() {
|
||||||
|
var code =
|
||||||
|
"""
|
||||||
|
module_method x = x
|
||||||
|
main =
|
||||||
|
Test.module_method 42
|
||||||
|
""";
|
||||||
|
var ctx = ContextUtils.createDefaultContext();
|
||||||
|
var ir = ContextUtils.compileModule(ctx, code, "Test");
|
||||||
|
var methodCall = findMethodCall(ir, "module_method");
|
||||||
|
var meta = methodCall.function().passData().get(MethodCalls$.MODULE$);
|
||||||
|
assertThat(meta.isDefined(), is(true));
|
||||||
|
var metaTarget = ((BindingsMap.Resolution) meta.get()).target();
|
||||||
|
assertThat(metaTarget, is(instanceOf(BindingsMap.ResolvedModuleMethod.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Application.Prefix findMethodCall(Module ir, String methodName) {
|
||||||
|
var res =
|
||||||
|
ir.preorder()
|
||||||
|
.find(
|
||||||
|
childIr -> {
|
||||||
|
if (childIr instanceof Application.Prefix app
|
||||||
|
&& app.function() instanceof Name.Literal lit) {
|
||||||
|
return lit.name().equals(methodName);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
assertThat(res.isDefined(), is(true));
|
||||||
|
return (Application.Prefix) res.get();
|
||||||
|
}
|
||||||
|
}
|
@ -36,6 +36,11 @@ public class ExtensionMethodResolutionTest {
|
|||||||
containsString("Method overloads are not supported"),
|
containsString("Method overloads are not supported"),
|
||||||
containsString("defined multiple times"));
|
containsString("defined multiple times"));
|
||||||
|
|
||||||
|
private static final Matcher<String> ambiguousResolutionErrorMessageMatcher =
|
||||||
|
allOf(
|
||||||
|
containsString("resolved ambiguously to"),
|
||||||
|
containsString("The symbol was first resolved to"));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void twoExtensionMethodsWithSameNameInOneModuleShouldFail() throws IOException {
|
public void twoExtensionMethodsWithSameNameInOneModuleShouldFail() throws IOException {
|
||||||
var src = """
|
var src = """
|
||||||
@ -330,7 +335,10 @@ public class ExtensionMethodResolutionTest {
|
|||||||
topScope.compile(true);
|
topScope.compile(true);
|
||||||
fail("Expected compilation error: " + out);
|
fail("Expected compilation error: " + out);
|
||||||
} catch (PolyglotException e) {
|
} catch (PolyglotException e) {
|
||||||
assertThat(e.isSyntaxError(), is(true));
|
assertThat(
|
||||||
|
"Exception should be a syntax error, but instead is " + e.getMessage(),
|
||||||
|
e.isSyntaxError(),
|
||||||
|
is(true));
|
||||||
assertThat(out.toString(), errorMessageMatcher);
|
assertThat(out.toString(), errorMessageMatcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,153 @@
|
|||||||
|
package org.enso.interpreter.test.exports;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
|
import static org.hamcrest.Matchers.hasKey;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.enso.compiler.data.BindingsMap.DefinedEntity;
|
||||||
|
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 ExportExtensionMethodTest {
|
||||||
|
@Rule public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extensionMethodCanBeExportedByName() throws IOException {
|
||||||
|
var tMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("T_Module"),
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
Value x
|
||||||
|
My_Type.extension_method self = self.x
|
||||||
|
""");
|
||||||
|
var aMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("A_Module"),
|
||||||
|
"""
|
||||||
|
from project.T_Module export My_Type, extension_method
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
from project.A_Module import all
|
||||||
|
main =
|
||||||
|
obj = My_Type.Value 42
|
||||||
|
obj.extension_method
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(tMod, aMod, mainMod), projDir);
|
||||||
|
ProjectUtils.testProjectRun(
|
||||||
|
projDir,
|
||||||
|
res -> {
|
||||||
|
assertThat(res.isNumber(), is(true));
|
||||||
|
assertThat(res.asInt(), is(42));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleExtensionMethodsCanBeExportedByName() throws IOException {
|
||||||
|
var tMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("T_Module"),
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
Value x
|
||||||
|
type My_Other_Type
|
||||||
|
Value y
|
||||||
|
My_Type.extension_method self = self.x
|
||||||
|
My_Other_Type.extension_method self = self.y
|
||||||
|
""");
|
||||||
|
var aMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("A_Module"),
|
||||||
|
"""
|
||||||
|
from project.T_Module export My_Type, My_Other_Type, extension_method
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
from project.A_Module import all
|
||||||
|
main =
|
||||||
|
t = My_Type.Value 42
|
||||||
|
ot = My_Other_Type.Value 42
|
||||||
|
t.extension_method == ot.extension_method
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(tMod, aMod, mainMod), projDir);
|
||||||
|
ProjectUtils.testProjectRun(
|
||||||
|
projDir,
|
||||||
|
res -> {
|
||||||
|
assertThat(res.isBoolean(), is(true));
|
||||||
|
assertThat(res.asBoolean(), is(true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extensionMethodIsInBindingMap() throws IOException {
|
||||||
|
var tMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("T_Module"),
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
Value x
|
||||||
|
My_Type.extension_method self = self.x
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
from project.T_Module export My_Type, extension_method
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(tMod, 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, hasKey("extension_method"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extensionMethodIsDefinedEntity() throws IOException {
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
Value x
|
||||||
|
My_Type.extension_method self = self.x
|
||||||
|
""");
|
||||||
|
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);
|
||||||
|
polyCtx.getTopScope().compile(true);
|
||||||
|
var definedEntities = ModuleUtils.getDefinedEntities(ctx, "local.Proj.Main");
|
||||||
|
assertThat(definedEntities.isEmpty(), is(false));
|
||||||
|
var entityNames = definedEntities.stream().map(DefinedEntity::name).toList();
|
||||||
|
assertThat(entityNames, hasItem("extension_method"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
package org.enso.interpreter.test.exports;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
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 ExportStaticMethodTest {
|
||||||
|
@Rule public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void staticMethodCanBeExportedByName() throws IOException {
|
||||||
|
var tMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("T_Module"), """
|
||||||
|
static_method x = x
|
||||||
|
""");
|
||||||
|
var aMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("A_Module"),
|
||||||
|
"""
|
||||||
|
from project.T_Module export static_method
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
from project.A_Module import all
|
||||||
|
main =
|
||||||
|
static_method 42
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(tMod, aMod, mainMod), projDir);
|
||||||
|
|
||||||
|
ProjectUtils.testProjectRun(
|
||||||
|
projDir,
|
||||||
|
res -> {
|
||||||
|
assertThat(res.isNumber(), is(true));
|
||||||
|
assertThat(res.asInt(), is(42));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void staticAndModuleMethodsWithSameNameCanBeImported() throws IOException {
|
||||||
|
var tMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("T_Module"),
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
method x = x
|
||||||
|
method x = x
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
from project.T_Module import My_Type, method
|
||||||
|
main =
|
||||||
|
My_Type.method 42 == method 42
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(tMod, mainMod), projDir);
|
||||||
|
|
||||||
|
ProjectUtils.testProjectRun(
|
||||||
|
projDir,
|
||||||
|
res -> {
|
||||||
|
assertThat(res.isBoolean(), is(true));
|
||||||
|
assertThat(res.asBoolean(), is(true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moduleMethodIsInBindingMap() throws IOException {
|
||||||
|
var tMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("T_Module"), """
|
||||||
|
module_method x = x
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
from project.T_Module export module_method
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(tMod, 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("module_method"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void staticMethodIsInBindingMap() throws IOException {
|
||||||
|
var tMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("T_Module"),
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
My_Type.static_method x = x
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
from project.T_Module export static_method
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(tMod, 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("static_method"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void staticMethodIsDefinedEntity() throws IOException {
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"), """
|
||||||
|
static_method x = x
|
||||||
|
""");
|
||||||
|
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);
|
||||||
|
polyCtx.getTopScope().compile(true);
|
||||||
|
var definedEntities = ModuleUtils.getDefinedEntities(ctx, "local.Proj.Main");
|
||||||
|
assertThat(definedEntities.size(), is(1));
|
||||||
|
assertThat(definedEntities.get(0).name(), containsString("static_method"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package org.enso.interpreter.test.scope;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.enso.common.LanguageInfo;
|
||||||
|
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.ProjectUtils;
|
||||||
|
import org.enso.test.utils.SourceModule;
|
||||||
|
import org.graalvm.polyglot.Source;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
public class ModuleScopeTest {
|
||||||
|
@Rule public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extensionMethodIsRegisteredInModuleScope() throws IOException {
|
||||||
|
var mainSrc =
|
||||||
|
Source.newBuilder(
|
||||||
|
LanguageInfo.ID,
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
Value x
|
||||||
|
My_Type.extension_method self = self.x
|
||||||
|
""",
|
||||||
|
"test.enso")
|
||||||
|
.build();
|
||||||
|
try (var ctx = ContextUtils.createDefaultContext()) {
|
||||||
|
var mainMod = ctx.eval(mainSrc);
|
||||||
|
var mainRuntimeMod = (Module) ContextUtils.unwrapValue(ctx, mainMod);
|
||||||
|
var myType = mainRuntimeMod.getScope().getType("My_Type", true);
|
||||||
|
assertThat(myType, is(notNullValue()));
|
||||||
|
var extensionMethod = mainRuntimeMod.getScope().getMethodForType(myType, "extension_method");
|
||||||
|
assertThat(extensionMethod, is(notNullValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void staticMethodIsInResolvedExports() throws IOException {
|
||||||
|
var aMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("A_module"),
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
My_Type.extension_method self = 42
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
export project.A_Module.My_Type
|
||||||
|
export project.A_Module.extension_method
|
||||||
|
main = 42
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void staticMethodIsRegisteredInModuleScope() throws IOException {
|
||||||
|
var mainSrc =
|
||||||
|
Source.newBuilder(
|
||||||
|
LanguageInfo.ID,
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
static_method _ = 42
|
||||||
|
""",
|
||||||
|
"test.enso")
|
||||||
|
.build();
|
||||||
|
try (var ctx = ContextUtils.createDefaultContext()) {
|
||||||
|
var mainMod = ctx.eval(mainSrc);
|
||||||
|
var mainRuntimeMod = (Module) ContextUtils.unwrapValue(ctx, mainMod);
|
||||||
|
var myType = mainRuntimeMod.getScope().getType("My_Type", true);
|
||||||
|
assertThat(myType, is(notNullValue()));
|
||||||
|
var staticMethod = mainRuntimeMod.getScope().getMethodForType(myType, "static_method");
|
||||||
|
assertThat(staticMethod, is(notNullValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moduleMethodIsRegisteredInModuleScope() throws IOException {
|
||||||
|
var mainSrc =
|
||||||
|
Source.newBuilder(
|
||||||
|
LanguageInfo.ID, """
|
||||||
|
module_method _ = 42
|
||||||
|
""", "test.enso")
|
||||||
|
.build();
|
||||||
|
try (var ctx = ContextUtils.createDefaultContext()) {
|
||||||
|
// ModuleScope is populated in IrToTruffle - at runtime. So we have to evaluate
|
||||||
|
// the main module before we inspect the ModuleScope.
|
||||||
|
var mainMod = ctx.eval(mainSrc);
|
||||||
|
var mainRuntimeMod = (Module) ContextUtils.unwrapValue(ctx, mainMod);
|
||||||
|
var assocType = mainRuntimeMod.getScope().getAssociatedType();
|
||||||
|
assertThat(assocType, is(notNullValue()));
|
||||||
|
var moduleMethod = mainRuntimeMod.getScope().getMethodForType(assocType, "module_method");
|
||||||
|
assertThat(moduleMethod, is(notNullValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void importedStaticMethodIsRegisteredInModuleScope() throws IOException {
|
||||||
|
var mod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Mod"),
|
||||||
|
"""
|
||||||
|
type My_Type
|
||||||
|
static_method _ = 1
|
||||||
|
""");
|
||||||
|
var mainMod =
|
||||||
|
new SourceModule(
|
||||||
|
QualifiedName.fromString("Main"),
|
||||||
|
"""
|
||||||
|
from project.Mod import My_Type
|
||||||
|
main = 2
|
||||||
|
""");
|
||||||
|
var projDir = tempFolder.newFolder().toPath();
|
||||||
|
ProjectUtils.createProject("Proj", Set.of(mod, mainMod), projDir);
|
||||||
|
var mainSrcPath = projDir.resolve("src").resolve("Main.enso");
|
||||||
|
try (var ctx =
|
||||||
|
ContextUtils.defaultContextBuilder()
|
||||||
|
.option(RuntimeOptions.PROJECT_ROOT, projDir.toAbsolutePath().toString())
|
||||||
|
.build()) {
|
||||||
|
var polyCtx = new PolyglotContext(ctx);
|
||||||
|
var mainRuntimeMod = polyCtx.evalModule(mainSrcPath.toFile());
|
||||||
|
var mainMethod = mainRuntimeMod.getMethod(mainRuntimeMod.getAssociatedType(), "main").get();
|
||||||
|
var mainRes = mainMethod.execute();
|
||||||
|
assertThat(mainRes.asInt(), is(2));
|
||||||
|
var ensoCtx = ContextUtils.leakContext(ctx);
|
||||||
|
var runtimeAbstractMod =
|
||||||
|
ensoCtx.getPackageRepository().getLoadedModule("local.Proj.Mod").get();
|
||||||
|
var runtimeConcreteMod = Module.fromCompilerModule(runtimeAbstractMod);
|
||||||
|
var myType = runtimeConcreteMod.getScope().getType("My_Type", true);
|
||||||
|
var staticMethod = runtimeConcreteMod.getScope().getMethodForType(myType, "static_method");
|
||||||
|
assertThat(staticMethod, is(notNullValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,7 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def findUsagesOfStaticMethod(
|
private def findUsagesOfModuleMethod(
|
||||||
moduleName: QualifiedName,
|
moduleName: QualifiedName,
|
||||||
module: Module,
|
module: Module,
|
||||||
ir: IR
|
ir: IR
|
||||||
@ -164,8 +164,8 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
|||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
|
|
||||||
val module = code.preprocessModule(moduleName)
|
val module = code.preprocessModule(moduleName)
|
||||||
val operator1 = IRUtils.findByExternalId(module, uuid1).get
|
val function1 = IRUtils.findByExternalId(module, uuid1).get
|
||||||
val usages = findUsagesOfStaticMethod(moduleName, module, operator1)
|
val usages = findUsagesOfModuleMethod(moduleName, module, function1)
|
||||||
|
|
||||||
usages.value.size shouldEqual 1
|
usages.value.size shouldEqual 1
|
||||||
usages.value.foreach {
|
usages.value.foreach {
|
||||||
@ -192,8 +192,8 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
|||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
|
|
||||||
val module = code.preprocessModule(moduleName)
|
val module = code.preprocessModule(moduleName)
|
||||||
val operator1 = IRUtils.findByExternalId(module, uuid1).get
|
val function1 = IRUtils.findByExternalId(module, uuid1).get
|
||||||
val usages = findUsagesOfStaticMethod(moduleName, module, operator1)
|
val usages = findUsagesOfModuleMethod(moduleName, module, function1)
|
||||||
|
|
||||||
usages.value.size shouldEqual 2
|
usages.value.size shouldEqual 2
|
||||||
usages.value.foreach {
|
usages.value.foreach {
|
||||||
@ -220,8 +220,8 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
|||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
|
|
||||||
val module = code.preprocessModule(moduleName)
|
val module = code.preprocessModule(moduleName)
|
||||||
val operator1 = IRUtils.findByExternalId(module, uuid1).get
|
val function1 = IRUtils.findByExternalId(module, uuid1).get
|
||||||
val usages = findUsagesOfStaticMethod(moduleName, module, operator1)
|
val usages = findUsagesOfModuleMethod(moduleName, module, function1)
|
||||||
|
|
||||||
usages.value.size shouldEqual 1
|
usages.value.size shouldEqual 1
|
||||||
usages.value.foreach {
|
usages.value.foreach {
|
||||||
@ -252,7 +252,7 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
|||||||
|
|
||||||
val module = code.preprocessModule(moduleName)
|
val module = code.preprocessModule(moduleName)
|
||||||
val operator1 = IRUtils.findByExternalId(module, uuid1).get
|
val operator1 = IRUtils.findByExternalId(module, uuid1).get
|
||||||
val usages = findUsagesOfStaticMethod(moduleName, module, operator1)
|
val usages = findUsagesOfModuleMethod(moduleName, module, operator1)
|
||||||
|
|
||||||
usages.value.size shouldEqual 1
|
usages.value.size shouldEqual 1
|
||||||
usages.value.foreach {
|
usages.value.foreach {
|
||||||
|
@ -8,8 +8,11 @@ import org.enso.compiler.data.BindingsMap
|
|||||||
import org.enso.compiler.data.BindingsMap.{
|
import org.enso.compiler.data.BindingsMap.{
|
||||||
Argument,
|
Argument,
|
||||||
Cons,
|
Cons,
|
||||||
|
ConversionMethod,
|
||||||
ModuleMethod,
|
ModuleMethod,
|
||||||
PolyglotSymbol,
|
PolyglotSymbol,
|
||||||
|
ResolvedStaticMethod,
|
||||||
|
StaticMethod,
|
||||||
Type
|
Type
|
||||||
}
|
}
|
||||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||||
@ -54,6 +57,80 @@ class BindingAnalysisTest extends CompilerTest {
|
|||||||
// === The Tests ============================================================
|
// === The Tests ============================================================
|
||||||
|
|
||||||
"Module binding resolution" should {
|
"Module binding resolution" should {
|
||||||
|
"extension method is a defined entity" in {
|
||||||
|
implicit val ctx: ModuleContext = mkModuleContext
|
||||||
|
val ir =
|
||||||
|
"""
|
||||||
|
|type My_Type
|
||||||
|
|My_Type.extension_method = 42
|
||||||
|
|""".stripMargin.preprocessModule.analyse
|
||||||
|
val metadata = ir.unsafeGetMetadata(BindingAnalysis, "Should exist.")
|
||||||
|
|
||||||
|
metadata.definedEntities should contain theSameElementsAs List(
|
||||||
|
Type("My_Type", List(), List(), false),
|
||||||
|
StaticMethod("extension_method", "My_Type")
|
||||||
|
)
|
||||||
|
|
||||||
|
metadata.resolveName("extension_method") shouldEqual Right(
|
||||||
|
List(
|
||||||
|
ResolvedStaticMethod(
|
||||||
|
ctx.moduleReference(),
|
||||||
|
StaticMethod("extension_method", "My_Type")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
"extension methods are defined entities" in {
|
||||||
|
implicit val ctx: ModuleContext = mkModuleContext
|
||||||
|
val ir =
|
||||||
|
"""
|
||||||
|
|type My_Type
|
||||||
|
|type Other_Type
|
||||||
|
|My_Type.extension_method = 42
|
||||||
|
|Other_Type.extension_method = 42
|
||||||
|
|""".stripMargin.preprocessModule.analyse
|
||||||
|
val metadata = ir.unsafeGetMetadata(BindingAnalysis, "Should exist.")
|
||||||
|
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
|
||||||
|
metadata.resolveName("extension_method") shouldBe Right(
|
||||||
|
List(
|
||||||
|
ResolvedStaticMethod(
|
||||||
|
ctx.moduleReference(),
|
||||||
|
StaticMethod("extension_method", "My_Type")
|
||||||
|
),
|
||||||
|
ResolvedStaticMethod(
|
||||||
|
ctx.moduleReference(),
|
||||||
|
StaticMethod("extension_method", "Other_Type")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
"conversion method is a defined entity" in {
|
||||||
|
implicit val ctx: ModuleContext = mkModuleContext
|
||||||
|
val ir =
|
||||||
|
"""
|
||||||
|
|type Source
|
||||||
|
|type Target
|
||||||
|
|Target.from (that:Source) = 42
|
||||||
|
|Source.from (that:Target) = 42
|
||||||
|
|""".stripMargin.preprocessModule.analyse
|
||||||
|
val metadata = ir.unsafeGetMetadata(BindingAnalysis, "Should exist.")
|
||||||
|
metadata.definedEntities should contain theSameElementsAs List(
|
||||||
|
Type("Source", List(), List(), false),
|
||||||
|
Type("Target", List(), List(), false),
|
||||||
|
ConversionMethod("from", "Source", "Target"),
|
||||||
|
ConversionMethod("from", "Target", "Source")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
"discover all atoms, methods, and polyglot symbols in a module" in {
|
"discover all atoms, methods, and polyglot symbols in a module" in {
|
||||||
implicit val ctx: ModuleContext = mkModuleContext
|
implicit val ctx: ModuleContext = mkModuleContext
|
||||||
val ir =
|
val ir =
|
||||||
@ -80,6 +157,8 @@ class BindingAnalysisTest extends CompilerTest {
|
|||||||
val metadata = ir.unsafeGetMetadata(BindingAnalysis, "Should exist.")
|
val metadata = ir.unsafeGetMetadata(BindingAnalysis, "Should exist.")
|
||||||
|
|
||||||
metadata.definedEntities should contain theSameElementsAs List(
|
metadata.definedEntities should contain theSameElementsAs List(
|
||||||
|
PolyglotSymbol("MyClass"),
|
||||||
|
PolyglotSymbol("Renamed_Class"),
|
||||||
Type(
|
Type(
|
||||||
"Foo",
|
"Foo",
|
||||||
List(),
|
List(),
|
||||||
@ -110,8 +189,9 @@ class BindingAnalysisTest extends CompilerTest {
|
|||||||
),
|
),
|
||||||
Type("Bar", List(), List(), builtinType = false),
|
Type("Bar", List(), List(), builtinType = false),
|
||||||
Type("Baz", List("x", "y"), List(), builtinType = false),
|
Type("Baz", List("x", "y"), List(), builtinType = false),
|
||||||
PolyglotSymbol("MyClass"),
|
StaticMethod("foo", "Baz"),
|
||||||
PolyglotSymbol("Renamed_Class"),
|
StaticMethod("baz", "Bar"),
|
||||||
|
ConversionMethod("from", "Bar", "Foo"),
|
||||||
ModuleMethod("foo")
|
ModuleMethod("foo")
|
||||||
)
|
)
|
||||||
metadata.currentModule shouldEqual ctx.moduleReference()
|
metadata.currentModule shouldEqual ctx.moduleReference()
|
||||||
@ -133,8 +213,12 @@ class BindingAnalysisTest extends CompilerTest {
|
|||||||
ir.getMetadata(BindingAnalysis)
|
ir.getMetadata(BindingAnalysis)
|
||||||
.get
|
.get
|
||||||
.definedEntities
|
.definedEntities
|
||||||
.filter(_.isInstanceOf[BindingsMap.ModuleMethod]) shouldEqual List(
|
.filter(
|
||||||
ModuleMethod("bar")
|
_.isInstanceOf[BindingsMap.Method]
|
||||||
|
) should contain theSameElementsAs List(
|
||||||
|
StaticMethod("foo", moduleName),
|
||||||
|
ModuleMethod("bar"),
|
||||||
|
StaticMethod("baz", moduleName)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -102,14 +102,14 @@ class GlobalNamesTest extends CompilerTest {
|
|||||||
.map(expr => expr.asInstanceOf[Expression.Binding].expression)
|
.map(expr => expr.asInstanceOf[Expression.Binding].expression)
|
||||||
|
|
||||||
"not resolve uppercase method names to applications with no arguments" in {
|
"not resolve uppercase method names to applications with no arguments" in {
|
||||||
val expr = bodyExprs(1)
|
val x2Expr = bodyExprs(1)
|
||||||
expr shouldBe an[errors.Resolution]
|
x2Expr shouldBe an[errors.Resolution]
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve method names to applications" in {
|
"resolve method names to applications" in {
|
||||||
val expr = bodyExprs(2)
|
val x3Expr = bodyExprs(2)
|
||||||
expr shouldBe an[Application.Prefix]
|
x3Expr shouldBe an[Application.Prefix]
|
||||||
val app = expr.asInstanceOf[Application.Prefix]
|
val app = x3Expr.asInstanceOf[Application.Prefix]
|
||||||
app.function.asInstanceOf[Name.Literal].name shouldEqual "constant"
|
app.function.asInstanceOf[Name.Literal].name shouldEqual "constant"
|
||||||
app.arguments.length shouldEqual 1
|
app.arguments.length shouldEqual 1
|
||||||
app.arguments(0).value.getMetadata(GlobalNames) shouldEqual Some(
|
app.arguments(0).value.getMetadata(GlobalNames) shouldEqual Some(
|
||||||
@ -118,16 +118,16 @@ class GlobalNamesTest extends CompilerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"not resolve uppercase method names to applications with arguments" in {
|
"not resolve uppercase method names to applications with arguments" in {
|
||||||
val expr = bodyExprs(3)
|
val x4Expr = bodyExprs(3)
|
||||||
expr shouldBe an[Application.Prefix]
|
x4Expr shouldBe an[Application.Prefix]
|
||||||
val app = expr.asInstanceOf[Application.Prefix]
|
val app = x4Expr.asInstanceOf[Application.Prefix]
|
||||||
app.function shouldBe an[errors.Resolution]
|
app.function shouldBe an[errors.Resolution]
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve method names in applications by adding the self argument" in {
|
"resolve method names in applications by adding the self argument" in {
|
||||||
val expr = bodyExprs(4)
|
val x5Expr = bodyExprs(4)
|
||||||
expr shouldBe an[Application.Prefix]
|
x5Expr shouldBe an[Application.Prefix]
|
||||||
val app = expr.asInstanceOf[Application.Prefix]
|
val app = x5Expr.asInstanceOf[Application.Prefix]
|
||||||
app.function.asInstanceOf[Name.Literal].name shouldEqual "add_one"
|
app.function.asInstanceOf[Name.Literal].name shouldEqual "add_one"
|
||||||
app.arguments.length shouldEqual 2
|
app.arguments.length shouldEqual 2
|
||||||
app.arguments(0).value.getMetadata(GlobalNames) shouldEqual Some(
|
app.arguments(0).value.getMetadata(GlobalNames) shouldEqual Some(
|
||||||
@ -136,9 +136,9 @@ class GlobalNamesTest extends CompilerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"resolve method names in partial applications by adding the self argument" in {
|
"resolve method names in partial applications by adding the self argument" in {
|
||||||
val expr = bodyExprs(5)
|
val yExpr = bodyExprs(5)
|
||||||
expr shouldBe an[Application.Prefix]
|
yExpr shouldBe an[Application.Prefix]
|
||||||
val app = expr.asInstanceOf[Application.Prefix]
|
val app = yExpr.asInstanceOf[Application.Prefix]
|
||||||
app.function.asInstanceOf[Name.Literal].name shouldEqual "add_one"
|
app.function.asInstanceOf[Name.Literal].name shouldEqual "add_one"
|
||||||
app.arguments.length shouldEqual 1
|
app.arguments.length shouldEqual 1
|
||||||
app.arguments(0).value.getMetadata(GlobalNames) shouldEqual Some(
|
app.arguments(0).value.getMetadata(GlobalNames) shouldEqual Some(
|
||||||
@ -152,6 +152,43 @@ class GlobalNamesTest extends CompilerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"Global names of static methods" should {
|
||||||
|
"resolve module method name by adding the self argument" in {
|
||||||
|
implicit val ctx: ModuleContext = mkModuleContext._1
|
||||||
|
val ir =
|
||||||
|
"""
|
||||||
|
|method x = x
|
||||||
|
|
|
||||||
|
|type My_Type
|
||||||
|
| method x = x
|
||||||
|
|
|
||||||
|
|main =
|
||||||
|
| method 42
|
||||||
|
| 0
|
||||||
|
|""".stripMargin.preprocessModule.analyse
|
||||||
|
val mainMethodExprs = ir
|
||||||
|
.bindings(3)
|
||||||
|
.asInstanceOf[definition.Method.Explicit]
|
||||||
|
.body
|
||||||
|
.asInstanceOf[Function.Lambda]
|
||||||
|
.body
|
||||||
|
.asInstanceOf[Expression.Block]
|
||||||
|
.expressions
|
||||||
|
val moduleMethodCall = mainMethodExprs(0)
|
||||||
|
.asInstanceOf[Application.Prefix]
|
||||||
|
moduleMethodCall.function
|
||||||
|
.asInstanceOf[Name.Literal]
|
||||||
|
.name shouldEqual "method"
|
||||||
|
moduleMethodCall.arguments.length shouldEqual 2
|
||||||
|
moduleMethodCall
|
||||||
|
.arguments(0)
|
||||||
|
.value
|
||||||
|
.getMetadata(GlobalNames) shouldEqual Some(
|
||||||
|
Resolution(ResolvedModule(ctx.moduleReference()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"Undefined names" should {
|
"Undefined names" should {
|
||||||
"be detected and reported" in {
|
"be detected and reported" in {
|
||||||
implicit val ctx: ModuleContext = mkModuleContext._1
|
implicit val ctx: ModuleContext = mkModuleContext._1
|
||||||
|
@ -265,7 +265,7 @@ class ImportExportTest
|
|||||||
mainBindingMap
|
mainBindingMap
|
||||||
.resolvedImports(1)
|
.resolvedImports(1)
|
||||||
.target
|
.target
|
||||||
.asInstanceOf[BindingsMap.ResolvedMethod]
|
.asInstanceOf[BindingsMap.ResolvedModuleMethod]
|
||||||
.method
|
.method
|
||||||
.name shouldEqual "static_method"
|
.name shouldEqual "static_method"
|
||||||
// In B_Module, we only have ResolvedMethod in the resolvedImports, there is no ResolvedModule
|
// In B_Module, we only have ResolvedMethod in the resolvedImports, there is no ResolvedModule
|
||||||
@ -274,7 +274,7 @@ class ImportExportTest
|
|||||||
bBindingMap
|
bBindingMap
|
||||||
.resolvedImports(0)
|
.resolvedImports(0)
|
||||||
.target
|
.target
|
||||||
.asInstanceOf[BindingsMap.ResolvedMethod]
|
.asInstanceOf[BindingsMap.ResolvedModuleMethod]
|
||||||
.method
|
.method
|
||||||
.name shouldEqual "static_method"
|
.name shouldEqual "static_method"
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ object ImportExport {
|
|||||||
moduleOrTypeName: String
|
moduleOrTypeName: String
|
||||||
) extends Reason {
|
) extends Reason {
|
||||||
override def message(source: (IdentifiedLocation => String)): String =
|
override def message(source: (IdentifiedLocation => String)): String =
|
||||||
s"The symbol $symbolName (module, type, or constructor) does not exist in $moduleOrTypeName."
|
s"The symbol $symbolName (module, type, method, or constructor) does not exist in $moduleOrTypeName."
|
||||||
}
|
}
|
||||||
|
|
||||||
case class NoSuchConstructor(
|
case class NoSuchConstructor(
|
||||||
|
@ -17,7 +17,7 @@ final class ExportsBuilder {
|
|||||||
def build(moduleName: QualifiedName, ir: IR): ModuleExports = {
|
def build(moduleName: QualifiedName, ir: IR): ModuleExports = {
|
||||||
val symbols = getBindings(ir).exportedSymbols.values.flatten
|
val symbols = getBindings(ir).exportedSymbols.values.flatten
|
||||||
.collect {
|
.collect {
|
||||||
case BindingsMap.ResolvedMethod(module, method) =>
|
case BindingsMap.ResolvedModuleMethod(module, method) =>
|
||||||
ExportedSymbol.Method(module.getName.toString, method.name)
|
ExportedSymbol.Method(module.getName.toString, method.name)
|
||||||
case BindingsMap.ResolvedType(module, tp) =>
|
case BindingsMap.ResolvedType(module, tp) =>
|
||||||
ExportedSymbol.Type(module.getName.toString, tp.name)
|
ExportedSymbol.Type(module.getName.toString, tp.name)
|
||||||
|
@ -198,31 +198,35 @@ public final class ImportExportCache
|
|||||||
@Persistable(
|
@Persistable(
|
||||||
clazz = org.enso.compiler.data.BindingsMap$ModuleReference$Abstract.class,
|
clazz = org.enso.compiler.data.BindingsMap$ModuleReference$Abstract.class,
|
||||||
id = 33007)
|
id = 33007)
|
||||||
@Persistable(clazz = BindingsMap.ModuleMethod.class, id = 33008)
|
|
||||||
@Persistable(clazz = BindingsMap.Type.class, id = 33009)
|
@Persistable(clazz = BindingsMap.Type.class, id = 33009)
|
||||||
@Persistable(clazz = BindingsMap.ResolvedImport.class, id = 33010)
|
@Persistable(clazz = BindingsMap.ResolvedImport.class, id = 33010)
|
||||||
@Persistable(clazz = BindingsMap.Cons.class, id = 33011)
|
@Persistable(clazz = BindingsMap.Cons.class, id = 33011)
|
||||||
@Persistable(clazz = BindingsMap.ResolvedModule.class, id = 33012)
|
@Persistable(clazz = BindingsMap.ResolvedModule.class, id = 33012)
|
||||||
@Persistable(clazz = BindingsMap.ResolvedType.class, id = 33013)
|
@Persistable(clazz = BindingsMap.ResolvedType.class, id = 33013)
|
||||||
@Persistable(clazz = BindingsMap.ResolvedMethod.class, id = 33014)
|
@Persistable(clazz = BindingsMap.ResolvedModuleMethod.class, id = 33014)
|
||||||
@Persistable(clazz = BindingsMap.ExportedModule.class, id = 33015)
|
@Persistable(clazz = BindingsMap.ResolvedStaticMethod.class, id = 33015)
|
||||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Only.class, id = 33016)
|
@Persistable(clazz = BindingsMap.ResolvedConversionMethod.class, id = 33016)
|
||||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Union.class, id = 33017)
|
@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(
|
@Persistable(
|
||||||
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Intersect.class,
|
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Intersect.class,
|
||||||
id = 33018)
|
id = 33020)
|
||||||
@Persistable(
|
@Persistable(
|
||||||
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$AllowedResolution.class,
|
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$AllowedResolution.class,
|
||||||
id = 33019)
|
id = 33021)
|
||||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$All$.class, id = 33020)
|
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$All$.class, id = 33022)
|
||||||
@Persistable(
|
@Persistable(
|
||||||
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Hiding.class,
|
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Hiding.class,
|
||||||
id = 33021)
|
id = 33023)
|
||||||
@Persistable(clazz = BindingsMap.Resolution.class, id = 33029)
|
@Persistable(clazz = BindingsMap.Resolution.class, id = 33024)
|
||||||
@Persistable(clazz = BindingsMap.ResolvedConstructor.class, id = 33030)
|
@Persistable(clazz = BindingsMap.ResolvedConstructor.class, id = 33025)
|
||||||
@Persistable(clazz = BindingsMap.ResolvedPolyglotSymbol.class, id = 33031)
|
@Persistable(clazz = BindingsMap.ResolvedPolyglotSymbol.class, id = 33026)
|
||||||
@Persistable(clazz = BindingsMap.ResolvedPolyglotField.class, id = 33032)
|
@Persistable(clazz = BindingsMap.ResolvedPolyglotField.class, id = 33027)
|
||||||
@Persistable(clazz = BindingsMap.Argument.class, id = 33033)
|
@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)
|
||||||
@ServiceProvider(service = Persistance.class)
|
@ServiceProvider(service = Persistance.class)
|
||||||
public static final class PersistBindingsMap extends Persistance<BindingsMap> {
|
public static final class PersistBindingsMap extends Persistance<BindingsMap> {
|
||||||
public PersistBindingsMap() {
|
public PersistBindingsMap() {
|
||||||
|
@ -24,7 +24,13 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
private final Map<String, Object> polyglotSymbols;
|
private final Map<String, Object> polyglotSymbols;
|
||||||
private final Map<String, Type> types;
|
private final Map<String, Type> types;
|
||||||
private final Map<Type, Map<String, Supplier<Function>>> methods;
|
private final Map<Type, Map<String, Supplier<Function>>> methods;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First key is target type, second key is source type. The value is the conversion function from
|
||||||
|
* source to target.
|
||||||
|
*/
|
||||||
private final Map<Type, Map<Type, Function>> conversions;
|
private final Map<Type, Map<Type, Function>> conversions;
|
||||||
|
|
||||||
private final Set<ImportExportScope> imports;
|
private final Set<ImportExportScope> imports;
|
||||||
private final Set<ImportExportScope> exports;
|
private final Set<ImportExportScope> exports;
|
||||||
|
|
||||||
@ -97,24 +103,37 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up a conversion method from source type to target type. The conversion method
|
||||||
|
* implementation looks like this:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Target_Type.from (other : Source_Type) = ...
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The conversion method is first looked up in the scope of the source type, then in the scope of
|
||||||
|
* the target type and finally in all the imported scopes.
|
||||||
|
*
|
||||||
|
* @param source Source type
|
||||||
|
* @param target Target type
|
||||||
|
* @return The conversion method or null if not found.
|
||||||
|
*/
|
||||||
@CompilerDirectives.TruffleBoundary
|
@CompilerDirectives.TruffleBoundary
|
||||||
public Function lookupConversionDefinition(Type original, Type target) {
|
public Function lookupConversionDefinition(Type source, Type target) {
|
||||||
Function definedWithOriginal =
|
Function definedWithSource = source.getDefinitionScope().getConversionsFor(target).get(source);
|
||||||
original.getDefinitionScope().getConversionsFor(target).get(original);
|
if (definedWithSource != null) {
|
||||||
if (definedWithOriginal != null) {
|
return definedWithSource;
|
||||||
return definedWithOriginal;
|
|
||||||
}
|
}
|
||||||
Function definedWithTarget =
|
Function definedWithTarget = target.getDefinitionScope().getConversionsFor(target).get(source);
|
||||||
target.getDefinitionScope().getConversionsFor(target).get(original);
|
|
||||||
if (definedWithTarget != null) {
|
if (definedWithTarget != null) {
|
||||||
return definedWithTarget;
|
return definedWithTarget;
|
||||||
}
|
}
|
||||||
Function definedHere = getConversionsFor(target).get(original);
|
Function definedHere = getConversionsFor(target).get(source);
|
||||||
if (definedHere != null) {
|
if (definedHere != null) {
|
||||||
return definedHere;
|
return definedHere;
|
||||||
}
|
}
|
||||||
return imports.stream()
|
return imports.stream()
|
||||||
.map(scope -> scope.getExportedConversion(original, target))
|
.map(scope -> scope.getExportedConversion(source, target))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
@ -94,8 +94,6 @@ import org.enso.interpreter.node.{
|
|||||||
MethodRootNode,
|
MethodRootNode,
|
||||||
ExpressionNode => RuntimeExpression
|
ExpressionNode => RuntimeExpression
|
||||||
}
|
}
|
||||||
import org.enso.interpreter.runtime.EnsoContext
|
|
||||||
import org.enso.interpreter.runtime.callable
|
|
||||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition
|
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition
|
||||||
import org.enso.interpreter.runtime.data.atom.{Atom, AtomConstructor}
|
import org.enso.interpreter.runtime.data.atom.{Atom, AtomConstructor}
|
||||||
import org.enso.interpreter.runtime.callable.function.{
|
import org.enso.interpreter.runtime.callable.function.{
|
||||||
@ -404,9 +402,17 @@ class IrToTruffle(
|
|||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible polyglot field, should be caught by MethodDefinitions pass."
|
"Impossible polyglot field, should be caught by MethodDefinitions pass."
|
||||||
)
|
)
|
||||||
case _: BindingsMap.ResolvedMethod =>
|
case _: BindingsMap.ResolvedModuleMethod =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible here, should be caught by MethodDefinitions pass."
|
"Impossible module method here, should be caught by MethodDefinitions pass."
|
||||||
|
)
|
||||||
|
case _: BindingsMap.ResolvedStaticMethod =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible static method here, should be caught by MethodDefinitions pass."
|
||||||
|
)
|
||||||
|
case _: BindingsMap.ResolvedConversionMethod =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible conversion method here, should be caught by MethodDefinitions pass."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -871,9 +877,17 @@ class IrToTruffle(
|
|||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible polyglot field, should be caught by MethodDefinitions pass."
|
"Impossible polyglot field, should be caught by MethodDefinitions pass."
|
||||||
)
|
)
|
||||||
case _: BindingsMap.ResolvedMethod =>
|
case _: BindingsMap.ResolvedModuleMethod =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible here, should be caught by MethodDefinitions pass."
|
"Impossible module method here, should be caught by MethodDefinitions pass."
|
||||||
|
)
|
||||||
|
case _: BindingsMap.ResolvedStaticMethod =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible static method here, should be caught by MethodDefinitions pass."
|
||||||
|
)
|
||||||
|
case _: BindingsMap.ResolvedConversionMethod =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible conversion method here, should be caught by MethodDefinitions pass."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -979,7 +993,7 @@ class IrToTruffle(
|
|||||||
name,
|
name,
|
||||||
fun
|
fun
|
||||||
)
|
)
|
||||||
case BindingsMap.ResolvedMethod(module, method) =>
|
case BindingsMap.ResolvedModuleMethod(module, method) =>
|
||||||
val actualModule = module.unsafeAsModule()
|
val actualModule = module.unsafeAsModule()
|
||||||
val fun = asScope(actualModule)
|
val fun = asScope(actualModule)
|
||||||
.getMethodForType(
|
.getMethodForType(
|
||||||
@ -995,6 +1009,100 @@ class IrToTruffle(
|
|||||||
name,
|
name,
|
||||||
fun
|
fun
|
||||||
)
|
)
|
||||||
|
case BindingsMap.ResolvedStaticMethod(module, staticMethod) =>
|
||||||
|
val actualModule = module.unsafeAsModule()
|
||||||
|
val currentScope = asScope(actualModule)
|
||||||
|
actualModule.getBindingsMap.resolveName(
|
||||||
|
staticMethod.tpName
|
||||||
|
) match {
|
||||||
|
case Right(List(BindingsMap.ResolvedType(modWithTp, _))) =>
|
||||||
|
val tpScope = asScope(modWithTp.unsafeAsModule())
|
||||||
|
val tp = tpScope.getType(staticMethod.tpName, true)
|
||||||
|
assert(
|
||||||
|
tp != null,
|
||||||
|
s"Type should be defined in module ${modWithTp.getName}"
|
||||||
|
)
|
||||||
|
// We have to search for the method on eigen type, because it is a static method.
|
||||||
|
// Static methods are always defined on eigen types
|
||||||
|
val eigenTp = tp.getEigentype
|
||||||
|
val fun =
|
||||||
|
currentScope.getMethodForType(
|
||||||
|
eigenTp,
|
||||||
|
staticMethod.methodName
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
fun != null,
|
||||||
|
s"exported symbol (static method) `${staticMethod.name}` needs to be registered first in the module "
|
||||||
|
)
|
||||||
|
scopeBuilder.registerMethod(
|
||||||
|
scopeAssociatedType,
|
||||||
|
name,
|
||||||
|
fun
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
throw new CompilerError(
|
||||||
|
s"Type ${staticMethod.tpName} should be resolvable in module ${module.getName}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case BindingsMap.ResolvedConversionMethod(
|
||||||
|
module,
|
||||||
|
conversionMethod
|
||||||
|
) =>
|
||||||
|
val actualModule = module.unsafeAsModule()
|
||||||
|
val actualScope = asScope(actualModule)
|
||||||
|
actualModule.getBindingsMap.resolveName(
|
||||||
|
conversionMethod.targetTpName
|
||||||
|
) match {
|
||||||
|
case Right(
|
||||||
|
List(BindingsMap.ResolvedType(modWithTargetTp, _))
|
||||||
|
) =>
|
||||||
|
val targetTpScope = asScope(modWithTargetTp.unsafeAsModule())
|
||||||
|
val targetTp =
|
||||||
|
targetTpScope.getType(conversionMethod.targetTpName, true)
|
||||||
|
assert(
|
||||||
|
targetTp != null,
|
||||||
|
s"Target type should be defined in module ${module.getName}"
|
||||||
|
)
|
||||||
|
actualModule.getBindingsMap.resolveName(
|
||||||
|
conversionMethod.sourceTpName
|
||||||
|
) match {
|
||||||
|
case Right(
|
||||||
|
List(BindingsMap.ResolvedType(modWithSourceTp, _))
|
||||||
|
) =>
|
||||||
|
val sourceTpScope =
|
||||||
|
asScope(modWithSourceTp.unsafeAsModule())
|
||||||
|
val sourceTp = sourceTpScope.getType(
|
||||||
|
conversionMethod.sourceTpName,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
sourceTp != null,
|
||||||
|
s"Source type should be defined in module ${module.getName}"
|
||||||
|
)
|
||||||
|
val conversionFun =
|
||||||
|
actualScope.lookupConversionDefinition(
|
||||||
|
sourceTp,
|
||||||
|
targetTp
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
conversionFun != null,
|
||||||
|
s"Conversion method `$conversionMethod` should be defined in module ${module.getName}"
|
||||||
|
)
|
||||||
|
scopeBuilder.registerConversionMethod(
|
||||||
|
targetTp,
|
||||||
|
sourceTp,
|
||||||
|
conversionFun
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
throw new CompilerError(
|
||||||
|
s"Source type ${conversionMethod.sourceTpName} should be resolvable in module ${module.getName}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
throw new CompilerError(
|
||||||
|
s"Target type ${conversionMethod.targetTpName} should be resolvable in module ${module.getName}"
|
||||||
|
)
|
||||||
|
}
|
||||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
|
case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
|
||||||
case BindingsMap.ResolvedPolyglotField(_, _) =>
|
case BindingsMap.ResolvedPolyglotField(_, _) =>
|
||||||
}
|
}
|
||||||
@ -1428,11 +1536,27 @@ class IrToTruffle(
|
|||||||
})
|
})
|
||||||
case Some(
|
case Some(
|
||||||
BindingsMap.Resolution(
|
BindingsMap.Resolution(
|
||||||
BindingsMap.ResolvedMethod(_, _)
|
BindingsMap.ResolvedModuleMethod(_, _)
|
||||||
)
|
)
|
||||||
) =>
|
) =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible method here, should be caught by Patterns resolution pass."
|
"Impossible module method here, should be caught by Patterns resolution pass."
|
||||||
|
)
|
||||||
|
case Some(
|
||||||
|
BindingsMap.Resolution(
|
||||||
|
BindingsMap.ResolvedStaticMethod(_, _)
|
||||||
|
)
|
||||||
|
) =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible static method here, should be caught by Patterns resolution pass."
|
||||||
|
)
|
||||||
|
case Some(
|
||||||
|
BindingsMap.Resolution(
|
||||||
|
BindingsMap.ResolvedConversionMethod(_, _)
|
||||||
|
)
|
||||||
|
) =>
|
||||||
|
throw new CompilerError(
|
||||||
|
"Impossible conversion method here, should be caught by Patterns resolution pass."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1800,9 +1924,17 @@ class IrToTruffle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConstantObjectNode.build(s)
|
ConstantObjectNode.build(s)
|
||||||
case BindingsMap.ResolvedMethod(_, method) =>
|
case BindingsMap.ResolvedModuleMethod(_, method) =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
s"Impossible here, ${method.name} should be caught when translating application"
|
s"Impossible here, module method ${method.name} should be caught when translating application"
|
||||||
|
)
|
||||||
|
case BindingsMap.ResolvedStaticMethod(_, staticMethod) =>
|
||||||
|
throw new CompilerError(
|
||||||
|
s"Impossible here, static method ${staticMethod.name} should be caught when translating application"
|
||||||
|
)
|
||||||
|
case BindingsMap.ResolvedConversionMethod(_, conversionMethod) =>
|
||||||
|
throw new CompilerError(
|
||||||
|
s"Impossible here, conversion method ${conversionMethod.targetTpName}.${conversionMethod.methodName} should be caught when translating application"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,7 @@ import org.graalvm.polyglot.Value;
|
|||||||
import org.graalvm.polyglot.io.IOAccess;
|
import org.graalvm.polyglot.io.IOAccess;
|
||||||
import org.graalvm.polyglot.proxy.ProxyExecutable;
|
import org.graalvm.polyglot.proxy.ProxyExecutable;
|
||||||
|
|
||||||
/**
|
/** A collection of classes and methods useful for testing {@link Context} related stuff. */
|
||||||
* A collection of classes and methods useful for testing {@link Context} related stuff.
|
|
||||||
*/
|
|
||||||
public final class ContextUtils {
|
public final class ContextUtils {
|
||||||
private ContextUtils() {}
|
private ContextUtils() {}
|
||||||
|
|
||||||
@ -90,7 +88,6 @@ public final class ContextUtils {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <E extends Throwable> E raise(Class<E> clazz, Throwable t) throws E {
|
private static <E extends Throwable> E raise(Class<E> clazz, Throwable t) throws E {
|
||||||
throw (E) t;
|
throw (E) t;
|
||||||
@ -150,6 +147,18 @@ public final class ContextUtils {
|
|||||||
return mainMethod.execute();
|
return mainMethod.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static org.enso.compiler.core.ir.Module compileModule(Context ctx, String src) {
|
||||||
|
return compileModule(ctx, src, "Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static org.enso.compiler.core.ir.Module compileModule(
|
||||||
|
Context ctx, String src, String moduleName) {
|
||||||
|
var source = Source.newBuilder(LanguageInfo.ID, src, moduleName + ".enso").buildLiteral();
|
||||||
|
var module = ctx.eval(source);
|
||||||
|
var runtimeMod = (org.enso.interpreter.runtime.Module) unwrapValue(ctx, module);
|
||||||
|
return runtimeMod.getIr();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the given module and returns a method by the given name from the module.
|
* Parses the given module and returns a method by the given name from the module.
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package org.enso.test.utils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
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.ResolvedName;
|
||||||
|
import org.graalvm.polyglot.Context;
|
||||||
|
import scala.jdk.javaapi.CollectionConverters;
|
||||||
|
|
||||||
|
/** Helper utility methods for manipulating with {@link org.enso.interpreter.runtime.Module}. */
|
||||||
|
public class ModuleUtils {
|
||||||
|
private ModuleUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns mapping of symbols to exported resolved names from the given module.
|
||||||
|
*
|
||||||
|
* @param modName FQN of the module
|
||||||
|
* @see {@link BindingsMap#exportedSymbols()}
|
||||||
|
*/
|
||||||
|
public static Map<String, List<ResolvedName>> getExportedSymbolsFromModule(
|
||||||
|
Context ctx, String modName) {
|
||||||
|
var ensoCtx = ContextUtils.leakContext(ctx);
|
||||||
|
var mod = ensoCtx.getPackageRepository().getLoadedModule(modName).get();
|
||||||
|
return getExportedSymbols(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, List<ResolvedName>> getExportedSymbols(Module module) {
|
||||||
|
var bindings = new HashMap<String, List<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;
|
||||||
|
}
|
||||||
|
}
|
@ -13,13 +13,10 @@ import org.graalvm.polyglot.Context;
|
|||||||
import org.graalvm.polyglot.Context.Builder;
|
import org.graalvm.polyglot.Context.Builder;
|
||||||
import org.graalvm.polyglot.Value;
|
import org.graalvm.polyglot.Value;
|
||||||
|
|
||||||
/**
|
/** Utility methods for creating and running Enso projects. */
|
||||||
* Utility methods for creating and running Enso projects.
|
|
||||||
*/
|
|
||||||
public class ProjectUtils {
|
public class ProjectUtils {
|
||||||
private ProjectUtils() {}
|
private ProjectUtils() {}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates temporary project directory structure with a given main source content. No need to
|
* Creates temporary project directory structure with a given main source content. No need to
|
||||||
* clean it up, as it is managed by JUnit TemporaryFolder rule. Note that we need to create a
|
* clean it up, as it is managed by JUnit TemporaryFolder rule. Note that we need to create a
|
||||||
@ -42,13 +39,14 @@ public class ProjectUtils {
|
|||||||
*
|
*
|
||||||
* @param projName Name of the project
|
* @param projName Name of the project
|
||||||
* @param modules Set of modules. Must contain `Main` module.
|
* @param modules Set of modules. Must contain `Main` module.
|
||||||
* @param projDir A directory in which the whole project structure will be created.
|
* @param projDir A directory in which the whole project structure will be created. Must exist and
|
||||||
* Must exist and be a directory.
|
* be a directory.
|
||||||
*/
|
*/
|
||||||
public static void createProject(
|
public static void createProject(String projName, Set<SourceModule> modules, Path projDir)
|
||||||
String projName, Set<SourceModule> modules, Path projDir) throws IOException {
|
throws IOException {
|
||||||
if (!projDir.toFile().exists() || !projDir.toFile().isDirectory()) {
|
if (!projDir.toFile().exists() || !projDir.toFile().isDirectory()) {
|
||||||
throw new IllegalArgumentException("Project directory " + projDir + " must already be created");
|
throw new IllegalArgumentException(
|
||||||
|
"Project directory " + projDir + " must already be created");
|
||||||
}
|
}
|
||||||
var projYaml =
|
var projYaml =
|
||||||
"""
|
"""
|
||||||
|
@ -2,9 +2,5 @@ package org.enso.test.utils;
|
|||||||
|
|
||||||
import org.enso.pkg.QualifiedName;
|
import org.enso.pkg.QualifiedName;
|
||||||
|
|
||||||
/**
|
/** A simple structure corresponding to an Enso module. */
|
||||||
* A simple structure corresponding to an Enso module.
|
public record SourceModule(QualifiedName name, String code) {}
|
||||||
*/
|
|
||||||
public record SourceModule(QualifiedName name, String code) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -8,8 +8,7 @@ import org.enso.interpreter.EnsoLanguage;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An artificial RootNode. Used for tests of nodes that need to be adopted. Just create this root
|
* An artificial RootNode. Used for tests of nodes that need to be adopted. Just create this root
|
||||||
* node inside a context, all the other nodes, and insert them via
|
* node inside a context, all the other nodes, and insert them via {@link #insertChildren(Node...)}.
|
||||||
* {@link #insertChildren(Node...)}.
|
|
||||||
*/
|
*/
|
||||||
public final class TestRootNode extends RootNode {
|
public final class TestRootNode extends RootNode {
|
||||||
|
|
||||||
@ -30,9 +29,7 @@ public final class TestRootNode extends RootNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** In the tests, do not execute this root node, but execute directly the child nodes. */
|
||||||
* In the tests, do not execute this root node, but execute directly the child nodes.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Object execute(VirtualFrame frame) {
|
public Object execute(VirtualFrame frame) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user