mirror of
https://github.com/enso-org/enso.git
synced 2024-10-05 17:17:50 +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-server`,
|
||||
testkit,
|
||||
`test-utils`,
|
||||
`common-polyglot-core-utils`,
|
||||
`std-base`,
|
||||
`std-database`,
|
||||
|
@ -100,15 +100,17 @@ binds the function name. This means that:
|
||||
## Methods
|
||||
|
||||
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
|
||||
`this` argument, while functions are not.
|
||||
`self` argument, while functions are not.
|
||||
|
||||
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`
|
||||
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
|
||||
type Maybe a
|
||||
@ -120,9 +122,9 @@ type Maybe a
|
||||
Maybe.Just _ -> True
|
||||
```
|
||||
|
||||
2. **As an Extension Method:** A function defined _explicitly_ on an atom counts
|
||||
as an extension method on that atom. It can be defined on a typeset to apply
|
||||
to all the atoms within that typeset.
|
||||
2. **As an Extension Method:** A function defined _explicitly_ on a type counts
|
||||
as an extension method on that type. An _extension_ method can be _static_ or
|
||||
_instance_, depending on whether the `self` argument is present or not.
|
||||
|
||||
```ruby
|
||||
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
|
||||
the type of the `this` argument specified to be a type.
|
||||
3. **As a Function with an Explicit `self` Argument:** A function defined with
|
||||
the type of the `self` argument specified to be a type.
|
||||
|
||||
```ruby
|
||||
floor (this : Number) = case this of
|
||||
floor (self : Number) = case self of
|
||||
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 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
|
||||
|
||||
|
@ -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.data.BindingsMap;
|
||||
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.ResolvedPolyglotSymbol;
|
||||
import org.enso.compiler.data.BindingsMap.ResolvedType;
|
||||
@ -513,9 +513,9 @@ public class IRDumper {
|
||||
bldr.addLabelLine(
|
||||
"target: ResolvedConstructor(" + resolvedConstructor.cons().name() + ")");
|
||||
}
|
||||
case ResolvedMethod resolvedMethod -> {
|
||||
case ResolvedModuleMethod resolvedModuleMethod -> {
|
||||
bldr.addLabelLine(
|
||||
"target: ResolvedMethod(" + resolvedMethod.method().name() + ")");
|
||||
"target: ResolvedMethod(" + resolvedModuleMethod.method().name() + ")");
|
||||
}
|
||||
case ResolvedPolyglotField resolvedPolyglotField -> {
|
||||
bldr.addLabelLine(
|
||||
@ -563,7 +563,7 @@ public class IRDumper {
|
||||
switch (entity) {
|
||||
case BindingsMap.Type tp -> bldr.addLabelLine(" - Type(" + tp.name() + ")");
|
||||
case BindingsMap.ModuleMethod method -> bldr.addLabelLine(
|
||||
" - Method(" + method.name() + ")");
|
||||
" - ModuleMethod(" + method.name() + ")");
|
||||
case BindingsMap.PolyglotSymbol polySym -> bldr.addLabelLine(
|
||||
" - PolyglotSymbol(" + polySym.name() + ")");
|
||||
default -> throw unimpl(entity);
|
||||
|
@ -125,16 +125,18 @@ public class PrivateSymbolsAnalysis implements IRPass {
|
||||
private Pattern processCasePattern(Pattern pattern, BindingsMap bindingsMap) {
|
||||
if (pattern instanceof Pattern.Constructor cons) {
|
||||
var consName = cons.constructor();
|
||||
var resolvedCons = tryResolveName(consName, bindingsMap);
|
||||
if (resolvedCons != null && isProjectPrivate(resolvedCons)) {
|
||||
var curProjName = getProjName(bindingsMap.currentModule().getName());
|
||||
var resolvedProjName = getProjName(resolvedCons.module().getName());
|
||||
if (!curProjName.equals(resolvedProjName)) {
|
||||
var reason =
|
||||
new org.enso.compiler.core.ir.expression.errors.Pattern.PrivateConstructor(
|
||||
consName.name(), curProjName, resolvedProjName);
|
||||
return new org.enso.compiler.core.ir.expression.errors.Pattern(
|
||||
cons, reason, cons.passData(), cons.diagnostics());
|
||||
var resolvedNames = tryResolveName(consName, bindingsMap);
|
||||
for (var resolvedName : resolvedNames) {
|
||||
if (isProjectPrivate(resolvedName)) {
|
||||
var curProjName = getProjName(bindingsMap.currentModule().getName());
|
||||
var resolvedProjName = getProjName(resolvedName.module().getName());
|
||||
if (!curProjName.equals(resolvedProjName)) {
|
||||
var reason =
|
||||
new org.enso.compiler.core.ir.expression.errors.Pattern.PrivateConstructor(
|
||||
consName.name(), curProjName, resolvedProjName);
|
||||
return new org.enso.compiler.core.ir.expression.errors.Pattern(
|
||||
cons, reason, cons.passData(), cons.diagnostics());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,16 +144,18 @@ public class PrivateSymbolsAnalysis implements IRPass {
|
||||
}
|
||||
|
||||
private Expression processName(Name name, BindingsMap bindingsMap) {
|
||||
var resolvedName = tryResolveName(name, bindingsMap);
|
||||
if (resolvedName != null && isProjectPrivate(resolvedName)) {
|
||||
var curProjName = getProjName(bindingsMap.currentModule().getName());
|
||||
var resolvedProjName = getProjName(resolvedName.module().getName());
|
||||
if (!curProjName.equals(resolvedProjName)) {
|
||||
var reason =
|
||||
new org.enso.compiler.core.ir.expression.errors.Resolution.PrivateEntity(
|
||||
curProjName, resolvedProjName);
|
||||
return new org.enso.compiler.core.ir.expression.errors.Resolution(
|
||||
name, reason, name.passData(), name.diagnostics());
|
||||
var resolvedNames = tryResolveName(name, bindingsMap);
|
||||
for (var resolvedName : resolvedNames) {
|
||||
if (isProjectPrivate(resolvedName)) {
|
||||
var curProjName = getProjName(bindingsMap.currentModule().getName());
|
||||
var resolvedProjName = getProjName(resolvedName.module().getName());
|
||||
if (!curProjName.equals(resolvedProjName)) {
|
||||
var reason =
|
||||
new org.enso.compiler.core.ir.expression.errors.Resolution.PrivateEntity(
|
||||
curProjName, resolvedProjName);
|
||||
return new org.enso.compiler.core.ir.expression.errors.Resolution(
|
||||
name, reason, name.passData(), name.diagnostics());
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
case Name.Literal lit -> {
|
||||
var resolved = bindingsMap.resolveName(lit.name());
|
||||
if (resolved.isRight()) {
|
||||
yield (ResolvedName) resolved.getOrElse(() -> null);
|
||||
var resolvedNames = resolved.toOption().get();
|
||||
yield CollectionConverters.asJava(resolvedNames);
|
||||
} else {
|
||||
yield null;
|
||||
yield List.of();
|
||||
}
|
||||
}
|
||||
case Name.Qualified qual -> {
|
||||
var nameParts = qual.parts().map(Name::name);
|
||||
var resolved = bindingsMap.resolveQualifiedName(nameParts);
|
||||
if (resolved.isRight()) {
|
||||
yield (ResolvedName) resolved.getOrElse(() -> null);
|
||||
var resolvedNames = resolved.toOption().get();
|
||||
yield CollectionConverters.asJava(resolvedNames);
|
||||
} 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 scala.annotation.unused
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
/** A utility structure for resolving symbols in a given module.
|
||||
*
|
||||
@ -174,15 +175,18 @@ case class BindingsMap(
|
||||
}).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.
|
||||
* @return a resolution for `name` or an error, if the name could not be
|
||||
* resolved.
|
||||
* @param name (Unqualified) name of the symbol to resolve
|
||||
* @return A list of all resolutions for the given name or an error if no resolution
|
||||
* was found
|
||||
*/
|
||||
def resolveName(
|
||||
name: String
|
||||
): Either[ResolutionError, ResolvedName] = {
|
||||
): Either[ResolutionError, List[ResolvedName]] = {
|
||||
val local = findLocalCandidates(name)
|
||||
if (local.nonEmpty) {
|
||||
return BindingsMap.handleAmbiguity(local)
|
||||
@ -200,14 +204,14 @@ case class BindingsMap(
|
||||
scope: ResolvedName,
|
||||
submoduleNames: List[String],
|
||||
finalItem: String
|
||||
): Either[ResolutionError, ResolvedName] = scope match {
|
||||
): Either[ResolutionError, List[ResolvedName]] = scope match {
|
||||
case scoped: ImportTarget =>
|
||||
var currentScope = scoped
|
||||
for (modName <- submoduleNames) {
|
||||
val resolution = currentScope.resolveExportedSymbol(modName)
|
||||
resolution match {
|
||||
val resolutions = currentScope.resolveExportedSymbol(modName)
|
||||
resolutions match {
|
||||
case Left(err) => return Left(err)
|
||||
case Right(t: ImportTarget) =>
|
||||
case Right(List(t: ImportTarget)) =>
|
||||
currentScope = t
|
||||
case _ => return Left(ResolutionNotFound)
|
||||
}
|
||||
@ -215,7 +219,7 @@ case class BindingsMap(
|
||||
currentScope.resolveExportedSymbol(finalItem)
|
||||
case s @ ResolvedPolyglotSymbol(_, _) =>
|
||||
val found = s.findExportedSymbolFor(finalItem)
|
||||
Right(found)
|
||||
Right(List(found))
|
||||
case _ => Left(ResolutionNotFound)
|
||||
}
|
||||
|
||||
@ -223,37 +227,64 @@ case class BindingsMap(
|
||||
*
|
||||
* @param name the name to resolve
|
||||
* @return a resolution for `name`
|
||||
* @see [[resolveName]]
|
||||
*/
|
||||
def resolveQualifiedName(
|
||||
name: List[String]
|
||||
): Either[ResolutionError, ResolvedName] =
|
||||
): Either[ResolutionError, List[ResolvedName]] =
|
||||
name match {
|
||||
case List() => Left(ResolutionNotFound)
|
||||
case List(item) => resolveName(item)
|
||||
case firstModuleName :: rest =>
|
||||
resolveName(firstModuleName).flatMap { firstModule =>
|
||||
// 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 =
|
||||
firstModule == ResolvedModule(currentModule)
|
||||
if (isQualifiedLocalImport) {
|
||||
resolveLocalName(rest)
|
||||
} else {
|
||||
val consName = rest.last
|
||||
val modNames = rest.init
|
||||
resolveQualifiedNameIn(firstModule, modNames, consName)
|
||||
}
|
||||
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.
|
||||
val isQualifiedLocalImport =
|
||||
firstResolvedNames == List(ResolvedModule(currentModule))
|
||||
if (isQualifiedLocalImport) {
|
||||
resolveLocalName(rest)
|
||||
} else {
|
||||
val consName = rest.last
|
||||
val modNames = rest.init
|
||||
|
||||
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(
|
||||
name: List[String]
|
||||
): Either[ResolutionError, ResolvedName] = name match {
|
||||
): Either[ResolutionError, List[ResolvedName]] = name match {
|
||||
case List() => Left(ResolutionNotFound)
|
||||
case List(singleItem) =>
|
||||
handleAmbiguity(findLocalCandidates(singleItem))
|
||||
case firstName :: rest =>
|
||||
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(
|
||||
@ -269,7 +300,7 @@ case class BindingsMap(
|
||||
*/
|
||||
def resolveExportedName(
|
||||
name: String
|
||||
): Either[ResolutionError, ResolvedName] = {
|
||||
): Either[ResolutionError, List[ResolvedName]] = {
|
||||
handleAmbiguity(findExportedSymbolsFor(name))
|
||||
}
|
||||
|
||||
@ -323,11 +354,25 @@ object BindingsMap {
|
||||
|
||||
private def handleAmbiguity(
|
||||
candidates: List[ResolvedName]
|
||||
): Either[ResolutionError, ResolvedName] = {
|
||||
): Either[ResolutionError, List[ResolvedName]] = {
|
||||
candidates.distinct match {
|
||||
case List() => Left(ResolutionNotFound)
|
||||
case List(it) => Right(it)
|
||||
case items => Left(ResolutionAmbiguous(items))
|
||||
case List(it) => Right(List(it))
|
||||
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 toConcrete(moduleMap: ModuleMap): Option[ImportTarget]
|
||||
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(
|
||||
name: String
|
||||
): Either[ResolutionError, ResolvedName] =
|
||||
): Either[ResolutionError, List[ResolvedName]] =
|
||||
BindingsMap.handleAmbiguity(findExportedSymbolsFor(name))
|
||||
|
||||
def exportedSymbols: Map[String, List[ResolvedName]]
|
||||
}
|
||||
|
||||
@ -720,14 +775,21 @@ object BindingsMap {
|
||||
|
||||
sealed trait DefinedEntity {
|
||||
def name: String
|
||||
|
||||
def resolvedIn(module: ModuleReference): ResolvedName = this match {
|
||||
case t: Type => ResolvedType(module, t)
|
||||
case m: ModuleMethod => ResolvedMethod(module, m)
|
||||
case t: Type => ResolvedType(module, t)
|
||||
case staticMethod: StaticMethod =>
|
||||
ResolvedStaticMethod(module, staticMethod)
|
||||
case conversionMethod: ConversionMethod =>
|
||||
ResolvedConversionMethod(module, conversionMethod)
|
||||
case m: ModuleMethod => ResolvedModuleMethod(module, m)
|
||||
case p: PolyglotSymbol => ResolvedPolyglotSymbol(module, p)
|
||||
}
|
||||
|
||||
def resolvedIn(module: Module): ResolvedName = resolvedIn(
|
||||
ModuleReference.Concrete(module)
|
||||
)
|
||||
|
||||
// Determines if this entity can be exported during export resolution pass
|
||||
def canExport: Boolean
|
||||
}
|
||||
@ -811,12 +873,43 @@ object BindingsMap {
|
||||
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.
|
||||
*/
|
||||
case class ModuleMethod(override val name: String) extends DefinedEntity {
|
||||
override def canExport: Boolean = true
|
||||
case class ModuleMethod(override val name: String) extends Method {}
|
||||
|
||||
/** 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.
|
||||
@ -938,23 +1031,27 @@ object BindingsMap {
|
||||
.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 method the method representation.
|
||||
*/
|
||||
case class ResolvedMethod(module: ModuleReference, method: ModuleMethod)
|
||||
extends ResolvedName {
|
||||
case class ResolvedModuleMethod(module: ModuleReference, method: ModuleMethod)
|
||||
extends ResolvedMethod {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toAbstract: ResolvedMethod = {
|
||||
override def toAbstract: ResolvedModuleMethod = {
|
||||
this.copy(module = module.toAbstract)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toConcrete(
|
||||
moduleMap: ModuleMap
|
||||
): Option[ResolvedMethod] = {
|
||||
): Option[ResolvedModuleMethod] = {
|
||||
module.toConcrete(moduleMap).map(module => this.copy(module = module))
|
||||
}
|
||||
|
||||
@ -979,6 +1076,58 @@ object BindingsMap {
|
||||
|
||||
override def qualifiedName: QualifiedName =
|
||||
module.getName.createChild(method.name)
|
||||
|
||||
override def methodName: String = method.name
|
||||
}
|
||||
|
||||
/** Method resolved on a type - either static method or extension method.
|
||||
*/
|
||||
case class ResolvedStaticMethod(
|
||||
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.
|
||||
@ -1046,8 +1195,12 @@ object BindingsMap {
|
||||
s" The imported polyglot symbol ${symbol.name};"
|
||||
case BindingsMap.ResolvedPolyglotField(_, 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}"
|
||||
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) =>
|
||||
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.data.BindingsMap
|
||||
import org.enso.compiler.core.CompilerError
|
||||
import org.enso.compiler.data.BindingsMap.ResolvedName
|
||||
import org.enso.compiler.pass.IRPass
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -106,33 +107,34 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
case Some(importTarget) =>
|
||||
val encounteredErrors: ListBuffer[errors.ImportExport] =
|
||||
ListBuffer()
|
||||
val imp =
|
||||
onlyNames.foldLeft(moduleImport: Import) { case (imp, symbol) =>
|
||||
val symbolName = symbol.name
|
||||
importTarget.resolveExportedSymbol(symbolName) match {
|
||||
case Right(resolvedName) =>
|
||||
onlyNames.foreach { symbol =>
|
||||
val symbolName = symbol.name
|
||||
importTarget.resolveExportedSymbol(symbolName) match {
|
||||
case Right(resolvedNames) =>
|
||||
resolvedNames.foreach { resolvedName =>
|
||||
val symbolPath = resolvedName.qualifiedName.toString
|
||||
tryAddEncounteredSymbol(
|
||||
encounteredSymbols,
|
||||
imp,
|
||||
moduleImport,
|
||||
symbolName,
|
||||
symbolPath
|
||||
symbolPath,
|
||||
Some(resolvedName)
|
||||
) match {
|
||||
case Left(error) =>
|
||||
encounteredErrors += error
|
||||
imp
|
||||
case Right(imp) => imp
|
||||
case Right(_) => ()
|
||||
}
|
||||
case Left(resolutionError) =>
|
||||
throw new CompilerError(
|
||||
s"Unreachable: (should have been resolved in previous passes) $resolutionError"
|
||||
)
|
||||
}
|
||||
}
|
||||
case Left(resolutionError) =>
|
||||
throw new CompilerError(
|
||||
s"Unreachable: (should have been resolved in previous passes) $resolutionError"
|
||||
)
|
||||
}
|
||||
}
|
||||
if (encounteredErrors.nonEmpty) {
|
||||
Left(encounteredErrors.toList)
|
||||
} else {
|
||||
Right(imp)
|
||||
Right(moduleImport)
|
||||
}
|
||||
|
||||
case None =>
|
||||
@ -164,32 +166,32 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
}
|
||||
val encounteredErrors: ListBuffer[errors.ImportExport] =
|
||||
ListBuffer()
|
||||
val imp =
|
||||
symbolsToIterate.foldLeft(moduleImport: Import) {
|
||||
case (imp, symbolName) =>
|
||||
importTarget.resolveExportedSymbol(symbolName) match {
|
||||
case Left(resolutionError) =>
|
||||
throw new CompilerError(
|
||||
s"Unreachable: (should have been resolved in previous passes) $resolutionError"
|
||||
)
|
||||
case Right(resolvedName) =>
|
||||
tryAddEncounteredSymbol(
|
||||
encounteredSymbols,
|
||||
imp,
|
||||
symbolName,
|
||||
resolvedName.qualifiedName.toString
|
||||
) match {
|
||||
case Left(error) =>
|
||||
encounteredErrors += error
|
||||
imp
|
||||
case Right(imp) => imp
|
||||
}
|
||||
symbolsToIterate.foreach { symbolName =>
|
||||
importTarget.resolveExportedSymbol(symbolName) match {
|
||||
case Left(resolutionError) =>
|
||||
throw new CompilerError(
|
||||
s"Unreachable: (should have been resolved in previous passes) $resolutionError"
|
||||
)
|
||||
case Right(List(resolvedName)) =>
|
||||
tryAddEncounteredSymbol(
|
||||
encounteredSymbols,
|
||||
moduleImport,
|
||||
symbolName,
|
||||
resolvedName.qualifiedName.toString,
|
||||
Some(resolvedName)
|
||||
) match {
|
||||
case Left(error) =>
|
||||
encounteredErrors += error
|
||||
case Right(_) => ()
|
||||
}
|
||||
// If the symbolName is resolved to multiple objects, we ignore it.
|
||||
case Right(_) => ()
|
||||
}
|
||||
}
|
||||
if (encounteredErrors.nonEmpty) {
|
||||
Left(encounteredErrors.toList)
|
||||
} else {
|
||||
Right(imp)
|
||||
Right(moduleImport)
|
||||
}
|
||||
|
||||
case None =>
|
||||
@ -213,7 +215,8 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
encounteredSymbols,
|
||||
moduleImport,
|
||||
rename.name,
|
||||
symbolPath
|
||||
symbolPath,
|
||||
None
|
||||
) match {
|
||||
case Left(error) => Left(List(error))
|
||||
case Right(imp) => Right(imp)
|
||||
@ -235,7 +238,8 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
encounteredSymbols,
|
||||
moduleImport,
|
||||
importPath.parts.last.name,
|
||||
importPath.name
|
||||
importPath.name,
|
||||
None
|
||||
) match {
|
||||
case Left(err) => Left(List(err))
|
||||
case Right(imp) => Right(imp)
|
||||
@ -252,7 +256,8 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
encounteredSymbols,
|
||||
polyImport,
|
||||
symbolName,
|
||||
symbolPath
|
||||
symbolPath,
|
||||
None
|
||||
) match {
|
||||
case Left(err) => Left(List(err))
|
||||
case Right(imp) => Right(imp)
|
||||
@ -288,7 +293,8 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
encounteredSymbols: EncounteredSymbols,
|
||||
currentImport: Import,
|
||||
symbolName: String,
|
||||
symbolPath: String
|
||||
symbolPath: String,
|
||||
resolvedName: Option[ResolvedName]
|
||||
): Either[errors.ImportExport, Import] = {
|
||||
if (encounteredSymbols.containsSymbol(symbolName)) {
|
||||
val encounteredFullName =
|
||||
@ -304,18 +310,31 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
)
|
||||
Right(currentImport.addDiagnostic(warn))
|
||||
} else {
|
||||
Left(
|
||||
createErrorForAmbiguousImport(
|
||||
originalImport,
|
||||
encounteredFullName,
|
||||
currentImport,
|
||||
symbolName,
|
||||
symbolPath
|
||||
)
|
||||
// The symbol was encountered before and the physical path is different.
|
||||
val ambiguousImpErr = createErrorForAmbiguousImport(
|
||||
originalImport,
|
||||
encounteredFullName,
|
||||
currentImport,
|
||||
symbolName,
|
||||
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 {
|
||||
encounteredSymbols.addSymbol(currentImport, symbolName, symbolPath)
|
||||
encounteredSymbols.addSymbol(
|
||||
currentImport,
|
||||
symbolName,
|
||||
symbolPath,
|
||||
resolvedName
|
||||
)
|
||||
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,
|
||||
* along with the entity path. The entity path is vital to decide whether an imported symbol is duplicated
|
||||
* or ambiguous.
|
||||
* Note that there are some exceptions that are allowed to be ambiguous, like extension methods.
|
||||
*/
|
||||
private class EncounteredSymbols(
|
||||
private val encounteredSymbols: mutable.Map[
|
||||
String,
|
||||
(Import, String)
|
||||
SymbolTarget
|
||||
] = mutable.HashMap.empty
|
||||
) {
|
||||
|
||||
@ -370,9 +401,13 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
def addSymbol(
|
||||
imp: Import,
|
||||
symbol: String,
|
||||
symbolPath: String
|
||||
symbolPath: String,
|
||||
resolvedName: Option[ResolvedName]
|
||||
): Unit = {
|
||||
encounteredSymbols.put(symbol, (imp, symbolPath))
|
||||
encounteredSymbols.put(
|
||||
symbol,
|
||||
SymbolTarget(symbolPath, resolvedName, imp)
|
||||
)
|
||||
}
|
||||
|
||||
/** Returns the entity path for the symbol.
|
||||
@ -381,8 +416,19 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
symbol: String
|
||||
): String = {
|
||||
encounteredSymbols.get(symbol) match {
|
||||
case Some((_, fullName)) =>
|
||||
fullName
|
||||
case Some(symbolTarget) =>
|
||||
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 =>
|
||||
throw new IllegalStateException("unreachable")
|
||||
}
|
||||
@ -393,7 +439,7 @@ case object AmbiguousImportsAnalysis extends IRPass {
|
||||
def getOriginalImportForSymbol(
|
||||
symbol: String
|
||||
): Option[Import] = {
|
||||
encounteredSymbols.get(symbol).map(_._1)
|
||||
encounteredSymbols.get(symbol).map(_.originalImport)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import org.enso.compiler.core.ir.{
|
||||
Name
|
||||
}
|
||||
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.ir.expression.{Application, Operator}
|
||||
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
|
||||
// arguments.
|
||||
app.function.getMetadata(MethodCalls) match {
|
||||
case Some(Resolution(method: ResolvedMethod)) =>
|
||||
case Some(Resolution(method: ResolvedModuleMethod)) =>
|
||||
val methodIr = method.unsafeGetIr("Invalid method call resolution.")
|
||||
val isParallelize = methodIr
|
||||
.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.MetadataStorage.MetadataPair
|
||||
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.desugar.{
|
||||
ComplexType,
|
||||
@ -60,33 +65,61 @@ case object BindingAnalysis extends IRPass {
|
||||
val importedPolyglot = ir.imports.collect { case poly: imports.Polyglot =>
|
||||
BindingsMap.PolyglotSymbol(poly.getVisibleName)
|
||||
}
|
||||
val moduleMethods = ir.bindings
|
||||
.collect { case method: definition.Method.Explicit =>
|
||||
val staticMethods: List[BindingsMap.Method] = ir.bindings.collect {
|
||||
case method: definition.Method.Explicit =>
|
||||
val ref = method.methodReference
|
||||
ref.typePointer match {
|
||||
case Some(Name.Qualified(List(), _, _, _)) =>
|
||||
Some(ref.methodName.name)
|
||||
Some(ModuleMethod(ref.methodName.name))
|
||||
case Some(Name.Qualified(List(n), _, _, _)) =>
|
||||
val shadowed = definedSumTypes.exists(_.name == n.name)
|
||||
if (!shadowed && n.name == moduleContext.getName().item)
|
||||
Some(ref.methodName.name)
|
||||
else None
|
||||
Some(ModuleMethod(ref.methodName.name))
|
||||
else {
|
||||
Some(
|
||||
StaticMethod(ref.methodName.name, n.name)
|
||||
)
|
||||
}
|
||||
case Some(literal: Name.Literal) =>
|
||||
val shadowed = definedSumTypes.exists(_.name == literal.name)
|
||||
if (!shadowed && literal.name == moduleContext.getName().item)
|
||||
Some(ref.methodName.name)
|
||||
else None
|
||||
case None => Some(ref.methodName.name)
|
||||
Some(ModuleMethod(ref.methodName.name))
|
||||
else {
|
||||
Some(
|
||||
StaticMethod(ref.methodName.name, literal.name)
|
||||
)
|
||||
}
|
||||
case None => Some(ModuleMethod(ref.methodName.name))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
.flatten
|
||||
.map(BindingsMap.ModuleMethod)
|
||||
case conversion: definition.Method.Conversion =>
|
||||
val targetTpNameOpt = conversion.typeName match {
|
||||
case Some(targetTpName) =>
|
||||
Some(targetTpName.name)
|
||||
case None => None
|
||||
}
|
||||
val sourceTpNameOpt = conversion.sourceTypeName match {
|
||||
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(
|
||||
new MetadataPair(
|
||||
this,
|
||||
BindingsMap(
|
||||
definedSumTypes ++ importedPolyglot ++ moduleMethods,
|
||||
definedSumTypes ++ importedPolyglot ++ staticMethods,
|
||||
moduleContext.moduleReference()
|
||||
)
|
||||
)
|
||||
|
@ -199,16 +199,22 @@ case object FullyQualifiedNames extends IRPass {
|
||||
case tp: Definition.Type =>
|
||||
tp.copy(members =
|
||||
tp.members.map(
|
||||
_.mapExpressions(
|
||||
_.mapExpressions(expr => {
|
||||
val selfTypeResolution =
|
||||
bindings.resolveName(tp.name.name) match {
|
||||
case Right(List(resolvedName)) =>
|
||||
Some(Resolution(resolvedName))
|
||||
case _ => None
|
||||
}
|
||||
processExpression(
|
||||
_,
|
||||
expr,
|
||||
bindings,
|
||||
tp.params.map(_.name),
|
||||
freshNameSupply,
|
||||
bindings.resolveName(tp.name.name).toOption.map(Resolution),
|
||||
selfTypeResolution,
|
||||
pkgRepo
|
||||
)
|
||||
)
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -19,8 +19,8 @@ import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.data.BindingsMap.{
|
||||
Resolution,
|
||||
ResolutionNotFound,
|
||||
ResolvedMethod,
|
||||
ResolvedModule
|
||||
ResolvedModule,
|
||||
ResolvedModuleMethod
|
||||
}
|
||||
import org.enso.compiler.core.CompilerError
|
||||
import org.enso.compiler.core.ConstantsNames
|
||||
@ -122,15 +122,21 @@ case object GlobalNames extends IRPass {
|
||||
case tp: Definition.Type =>
|
||||
tp.copy(members =
|
||||
tp.members.map(
|
||||
_.mapExpressions(
|
||||
_.mapExpressions { expr =>
|
||||
val selfTypeResolution =
|
||||
bindings.resolveName(tp.name.name) match {
|
||||
case Right(List(resolvedName)) =>
|
||||
Some(Resolution(resolvedName))
|
||||
case _ => None
|
||||
}
|
||||
processExpression(
|
||||
_,
|
||||
expr,
|
||||
bindings,
|
||||
tp.params,
|
||||
freshNameSupply,
|
||||
bindings.resolveName(tp.name.name).toOption.map(Resolution)
|
||||
selfTypeResolution
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@ -181,10 +187,17 @@ case object GlobalNames extends IRPass {
|
||||
lit,
|
||||
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) {
|
||||
lit.updateMetadata(
|
||||
new MetadataPair(this, BindingsMap.Resolution(r))
|
||||
new MetadataPair(
|
||||
this,
|
||||
BindingsMap.Resolution(resolvedModuleMethod)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val self = freshNameSupply
|
||||
@ -193,14 +206,15 @@ case object GlobalNames extends IRPass {
|
||||
new MetadataPair(
|
||||
this,
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(mod)
|
||||
BindingsMap
|
||||
.ResolvedModule(resolvedModuleMethod.module)
|
||||
)
|
||||
)
|
||||
)
|
||||
// The synthetic applications gets the location so that instrumentation
|
||||
// identifies the node correctly
|
||||
val fun = lit.copy(
|
||||
name = method.name,
|
||||
name = resolvedModuleMethod.method.name,
|
||||
location = None
|
||||
)
|
||||
val app = Application.Prefix(
|
||||
@ -222,9 +236,11 @@ case object GlobalNames extends IRPass {
|
||||
fun.passData.remove(ExpressionAnnotations)
|
||||
app
|
||||
}
|
||||
case Right(value) =>
|
||||
lit.updateMetadata(
|
||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||
case Right(values) =>
|
||||
values.foldLeft(lit)((lit, value) =>
|
||||
lit.updateMetadata(
|
||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -297,7 +313,7 @@ case object GlobalNames extends IRPass {
|
||||
)
|
||||
)
|
||||
processedFun.getMetadata(this) match {
|
||||
case Some(Resolution(ResolvedMethod(mod, _))) if !isLocalVar(fun) =>
|
||||
case Some(Resolution(ResolvedModuleMethod(mod, _))) if !isLocalVar(fun) =>
|
||||
val self = freshNameSupply
|
||||
.newName()
|
||||
.updateMetadata(
|
||||
@ -408,8 +424,8 @@ case object GlobalNames extends IRPass {
|
||||
)
|
||||
.resolveExportedName(consName.name)
|
||||
resolution match {
|
||||
case Right(res) => Some(res)
|
||||
case _ => None
|
||||
case Right(List(res)) => Some(res)
|
||||
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.expression.Application
|
||||
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.analyse.BindingAnalysis
|
||||
|
||||
@ -63,9 +67,9 @@ object MethodCalls extends IRPass {
|
||||
app.function match {
|
||||
case name: Name if name.isMethod =>
|
||||
app.arguments match {
|
||||
case first :: _ =>
|
||||
case selfArgument :: _ =>
|
||||
val targetBindings =
|
||||
first.value.getMetadata(GlobalNames) match {
|
||||
selfArgument.value.getMetadata(GlobalNames) match {
|
||||
case Some(Resolution(ResolvedModule(module))) =>
|
||||
val moduleIr = module.unsafeAsModule().getIr
|
||||
Option
|
||||
@ -77,13 +81,23 @@ object MethodCalls extends IRPass {
|
||||
}
|
||||
targetBindings match {
|
||||
case Some(bindings) =>
|
||||
val resolution =
|
||||
val resolutionsOpt =
|
||||
bindings.exportedSymbols.get(name.name)
|
||||
resolution match {
|
||||
case Some(List(resolution)) =>
|
||||
val resolvedModuleMethodOpt = resolutionsOpt match {
|
||||
case Some(resolutions) =>
|
||||
resolutions.collectFirst { case x: ResolvedModuleMethod =>
|
||||
x
|
||||
}
|
||||
case None => None
|
||||
}
|
||||
resolvedModuleMethodOpt match {
|
||||
case Some(resolvedModuleMethod) =>
|
||||
val newName =
|
||||
name.updateMetadata(
|
||||
new MetadataPair(this, Resolution(resolution))
|
||||
new MetadataPair(
|
||||
this,
|
||||
Resolution(resolvedModuleMethod)
|
||||
)
|
||||
)
|
||||
val newArgs =
|
||||
app.arguments.map(
|
||||
|
@ -190,43 +190,60 @@ case object MethodDefinitions extends IRPass {
|
||||
typePointer,
|
||||
errors.Resolution.ResolverError(err)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedConstructor) =>
|
||||
errors.Resolution(
|
||||
typePointer,
|
||||
errors.Resolution.UnexpectedConstructor(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedModule) =>
|
||||
typePointer.updateMetadata(
|
||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedType) =>
|
||||
typePointer.updateMetadata(
|
||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedPolyglotSymbol) =>
|
||||
errors.Resolution(
|
||||
typePointer,
|
||||
errors.Resolution.UnexpectedPolyglot(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedPolyglotField) =>
|
||||
errors.Resolution(
|
||||
typePointer,
|
||||
errors.Resolution.UnexpectedPolyglot(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
errors.Resolution(
|
||||
typePointer,
|
||||
errors.Resolution.UnexpectedMethod(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
|
||||
case Right(resolvedItems) =>
|
||||
assert(resolvedItems.size == 1, "Expected a single resolution")
|
||||
resolvedItems.head match {
|
||||
case _: BindingsMap.ResolvedConstructor =>
|
||||
errors.Resolution(
|
||||
typePointer,
|
||||
errors.Resolution.UnexpectedConstructor(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case value: BindingsMap.ResolvedModule =>
|
||||
typePointer.updateMetadata(
|
||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||
)
|
||||
case value: BindingsMap.ResolvedType =>
|
||||
typePointer.updateMetadata(
|
||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||
)
|
||||
case _: BindingsMap.ResolvedPolyglotSymbol =>
|
||||
errors.Resolution(
|
||||
typePointer,
|
||||
errors.Resolution.UnexpectedPolyglot(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case _: BindingsMap.ResolvedPolyglotField =>
|
||||
errors.Resolution(
|
||||
typePointer,
|
||||
errors.Resolution.UnexpectedPolyglot(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case _: BindingsMap.ResolvedModuleMethod =>
|
||||
errors.Resolution(
|
||||
typePointer,
|
||||
errors.Resolution.UnexpectedMethod(
|
||||
"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 _ =>
|
||||
|
@ -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(
|
||||
expr: Expression,
|
||||
bindings: BindingsMap,
|
||||
@ -93,7 +138,8 @@ object Patterns extends IRPass {
|
||||
qual.parts match {
|
||||
case (_: Name.SelfType) :: (others :+ item) =>
|
||||
selfTypeResolution.map(
|
||||
bindings.resolveQualifiedNameIn(
|
||||
resolveSingleQualifiedNameIn(
|
||||
bindings,
|
||||
_,
|
||||
others.map(_.name),
|
||||
item.name
|
||||
@ -102,11 +148,11 @@ object Patterns extends IRPass {
|
||||
case _ =>
|
||||
val parts = qual.parts.map(_.name)
|
||||
Some(
|
||||
bindings.resolveQualifiedName(parts)
|
||||
resolveSingleQualifiedName(bindings, parts)
|
||||
)
|
||||
}
|
||||
case lit: Name.Literal =>
|
||||
Some(bindings.resolveName(lit.name))
|
||||
Some(resolveSingleName(bindings, lit.name))
|
||||
case _: Name.SelfType =>
|
||||
selfTypeResolution.map(Right(_))
|
||||
case _ => None
|
||||
@ -140,14 +186,34 @@ object Patterns extends IRPass {
|
||||
new MetadataPair(this, BindingsMap.Resolution(value))
|
||||
)
|
||||
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
case Right(_: BindingsMap.ResolvedModuleMethod) =>
|
||||
val r = errors.Resolution(
|
||||
consName,
|
||||
errors.Resolution.UnexpectedMethod(
|
||||
"a pattern match"
|
||||
"method inside pattern match"
|
||||
)
|
||||
)
|
||||
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)
|
||||
|
||||
@ -158,7 +224,15 @@ object Patterns extends IRPass {
|
||||
case BindingsMap.ResolvedModule(_) => 0
|
||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) => 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(
|
||||
"Impossible, should be transformed into an error before."
|
||||
)
|
||||
@ -186,10 +260,10 @@ object Patterns extends IRPass {
|
||||
case qual: Name.Qualified =>
|
||||
val parts = qual.parts.map(_.name)
|
||||
Some(
|
||||
bindings.resolveQualifiedName(parts)
|
||||
resolveSingleQualifiedName(bindings, parts)
|
||||
)
|
||||
case lit: Name.Literal =>
|
||||
Some(bindings.resolveName(lit.name))
|
||||
Some(resolveSingleName(bindings, lit.name))
|
||||
case _: Name.SelfType =>
|
||||
selfTypeResolution.map(Right(_))
|
||||
case _ => None
|
||||
@ -223,15 +297,31 @@ object Patterns extends IRPass {
|
||||
tpeName,
|
||||
errors.Resolution.UnexpectedPolyglot(s"type pattern case")
|
||||
)*/
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
case Right(_: BindingsMap.ResolvedModuleMethod) =>
|
||||
errors.Resolution(
|
||||
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) =>
|
||||
errors.Resolution(
|
||||
tpeName,
|
||||
errors.Resolution.UnexpectedModule(s"type pattern case")
|
||||
errors.Resolution
|
||||
.UnexpectedModule(s"module inside type pattern case")
|
||||
)
|
||||
}
|
||||
.getOrElse(tpeName)
|
||||
|
@ -182,8 +182,10 @@ case object TypeNames extends IRPass {
|
||||
bindingsMap.resolveQualifiedName(n.parts.map(_.name))
|
||||
)
|
||||
case selfRef: Name.SelfType =>
|
||||
val resolvedSelfType = selfTypeInfo.selfType.toRight {
|
||||
BindingsMap.SelfTypeOutsideOfTypeDefinition
|
||||
val resolvedSelfType = selfTypeInfo.selfType match {
|
||||
case None => Left(BindingsMap.SelfTypeOutsideOfTypeDefinition)
|
||||
case Some(selfType) =>
|
||||
Right(List(selfType))
|
||||
}
|
||||
processResolvedName(selfRef, resolvedSelfType)
|
||||
case s: `type`.Set =>
|
||||
@ -192,10 +194,17 @@ case object TypeNames extends IRPass {
|
||||
|
||||
private def processResolvedName(
|
||||
name: Name,
|
||||
resolvedName: Either[BindingsMap.ResolutionError, BindingsMap.ResolvedName]
|
||||
resolvedNamesOpt: Either[BindingsMap.ResolutionError, List[
|
||||
BindingsMap.ResolvedName
|
||||
]]
|
||||
): Name =
|
||||
resolvedName
|
||||
.map(res => name.updateMetadata(new MetadataPair(this, Resolution(res))))
|
||||
resolvedNamesOpt
|
||||
.map(resolvedNames => {
|
||||
resolvedNames.foreach { resolvedName =>
|
||||
name.updateMetadata(new MetadataPair(this, Resolution(resolvedName)))
|
||||
}
|
||||
name
|
||||
})
|
||||
.fold(
|
||||
error =>
|
||||
errors.Resolution(name, errors.Resolution.ResolverError(error)),
|
||||
|
@ -65,7 +65,7 @@ trait IRUtils {
|
||||
.flatMap { symbol =>
|
||||
symbol.getMetadata(MethodCalls).flatMap { resolution =>
|
||||
resolution.target match {
|
||||
case BindingsMap.ResolvedMethod(module, _)
|
||||
case BindingsMap.ResolvedModuleMethod(module, _)
|
||||
if module.getName == moduleName =>
|
||||
Some(symbol)
|
||||
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("defined multiple times"));
|
||||
|
||||
private static final Matcher<String> ambiguousResolutionErrorMessageMatcher =
|
||||
allOf(
|
||||
containsString("resolved ambiguously to"),
|
||||
containsString("The symbol was first resolved to"));
|
||||
|
||||
@Test
|
||||
public void twoExtensionMethodsWithSameNameInOneModuleShouldFail() throws IOException {
|
||||
var src = """
|
||||
@ -330,7 +335,10 @@ public class ExtensionMethodResolutionTest {
|
||||
topScope.compile(true);
|
||||
fail("Expected compilation error: " + out);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
module: Module,
|
||||
ir: IR
|
||||
@ -164,8 +164,8 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
||||
|""".stripMargin
|
||||
|
||||
val module = code.preprocessModule(moduleName)
|
||||
val operator1 = IRUtils.findByExternalId(module, uuid1).get
|
||||
val usages = findUsagesOfStaticMethod(moduleName, module, operator1)
|
||||
val function1 = IRUtils.findByExternalId(module, uuid1).get
|
||||
val usages = findUsagesOfModuleMethod(moduleName, module, function1)
|
||||
|
||||
usages.value.size shouldEqual 1
|
||||
usages.value.foreach {
|
||||
@ -192,8 +192,8 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
||||
|""".stripMargin
|
||||
|
||||
val module = code.preprocessModule(moduleName)
|
||||
val operator1 = IRUtils.findByExternalId(module, uuid1).get
|
||||
val usages = findUsagesOfStaticMethod(moduleName, module, operator1)
|
||||
val function1 = IRUtils.findByExternalId(module, uuid1).get
|
||||
val usages = findUsagesOfModuleMethod(moduleName, module, function1)
|
||||
|
||||
usages.value.size shouldEqual 2
|
||||
usages.value.foreach {
|
||||
@ -220,8 +220,8 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
||||
|""".stripMargin
|
||||
|
||||
val module = code.preprocessModule(moduleName)
|
||||
val operator1 = IRUtils.findByExternalId(module, uuid1).get
|
||||
val usages = findUsagesOfStaticMethod(moduleName, module, operator1)
|
||||
val function1 = IRUtils.findByExternalId(module, uuid1).get
|
||||
val usages = findUsagesOfModuleMethod(moduleName, module, function1)
|
||||
|
||||
usages.value.size shouldEqual 1
|
||||
usages.value.foreach {
|
||||
@ -252,7 +252,7 @@ class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
|
||||
|
||||
val module = code.preprocessModule(moduleName)
|
||||
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.foreach {
|
||||
|
@ -8,8 +8,11 @@ import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.data.BindingsMap.{
|
||||
Argument,
|
||||
Cons,
|
||||
ConversionMethod,
|
||||
ModuleMethod,
|
||||
PolyglotSymbol,
|
||||
ResolvedStaticMethod,
|
||||
StaticMethod,
|
||||
Type
|
||||
}
|
||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||
@ -54,6 +57,80 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
// === The Tests ============================================================
|
||||
|
||||
"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 {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
val ir =
|
||||
@ -80,6 +157,8 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
val metadata = ir.unsafeGetMetadata(BindingAnalysis, "Should exist.")
|
||||
|
||||
metadata.definedEntities should contain theSameElementsAs List(
|
||||
PolyglotSymbol("MyClass"),
|
||||
PolyglotSymbol("Renamed_Class"),
|
||||
Type(
|
||||
"Foo",
|
||||
List(),
|
||||
@ -110,8 +189,9 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
),
|
||||
Type("Bar", List(), List(), builtinType = false),
|
||||
Type("Baz", List("x", "y"), List(), builtinType = false),
|
||||
PolyglotSymbol("MyClass"),
|
||||
PolyglotSymbol("Renamed_Class"),
|
||||
StaticMethod("foo", "Baz"),
|
||||
StaticMethod("baz", "Bar"),
|
||||
ConversionMethod("from", "Bar", "Foo"),
|
||||
ModuleMethod("foo")
|
||||
)
|
||||
metadata.currentModule shouldEqual ctx.moduleReference()
|
||||
@ -133,8 +213,12 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
ir.getMetadata(BindingAnalysis)
|
||||
.get
|
||||
.definedEntities
|
||||
.filter(_.isInstanceOf[BindingsMap.ModuleMethod]) shouldEqual List(
|
||||
ModuleMethod("bar")
|
||||
.filter(
|
||||
_.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)
|
||||
|
||||
"not resolve uppercase method names to applications with no arguments" in {
|
||||
val expr = bodyExprs(1)
|
||||
expr shouldBe an[errors.Resolution]
|
||||
val x2Expr = bodyExprs(1)
|
||||
x2Expr shouldBe an[errors.Resolution]
|
||||
}
|
||||
|
||||
"resolve method names to applications" in {
|
||||
val expr = bodyExprs(2)
|
||||
expr shouldBe an[Application.Prefix]
|
||||
val app = expr.asInstanceOf[Application.Prefix]
|
||||
val x3Expr = bodyExprs(2)
|
||||
x3Expr shouldBe an[Application.Prefix]
|
||||
val app = x3Expr.asInstanceOf[Application.Prefix]
|
||||
app.function.asInstanceOf[Name.Literal].name shouldEqual "constant"
|
||||
app.arguments.length shouldEqual 1
|
||||
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 {
|
||||
val expr = bodyExprs(3)
|
||||
expr shouldBe an[Application.Prefix]
|
||||
val app = expr.asInstanceOf[Application.Prefix]
|
||||
val x4Expr = bodyExprs(3)
|
||||
x4Expr shouldBe an[Application.Prefix]
|
||||
val app = x4Expr.asInstanceOf[Application.Prefix]
|
||||
app.function shouldBe an[errors.Resolution]
|
||||
}
|
||||
|
||||
"resolve method names in applications by adding the self argument" in {
|
||||
val expr = bodyExprs(4)
|
||||
expr shouldBe an[Application.Prefix]
|
||||
val app = expr.asInstanceOf[Application.Prefix]
|
||||
val x5Expr = bodyExprs(4)
|
||||
x5Expr shouldBe an[Application.Prefix]
|
||||
val app = x5Expr.asInstanceOf[Application.Prefix]
|
||||
app.function.asInstanceOf[Name.Literal].name shouldEqual "add_one"
|
||||
app.arguments.length shouldEqual 2
|
||||
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 {
|
||||
val expr = bodyExprs(5)
|
||||
expr shouldBe an[Application.Prefix]
|
||||
val app = expr.asInstanceOf[Application.Prefix]
|
||||
val yExpr = bodyExprs(5)
|
||||
yExpr shouldBe an[Application.Prefix]
|
||||
val app = yExpr.asInstanceOf[Application.Prefix]
|
||||
app.function.asInstanceOf[Name.Literal].name shouldEqual "add_one"
|
||||
app.arguments.length shouldEqual 1
|
||||
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 {
|
||||
"be detected and reported" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext._1
|
||||
|
@ -265,7 +265,7 @@ class ImportExportTest
|
||||
mainBindingMap
|
||||
.resolvedImports(1)
|
||||
.target
|
||||
.asInstanceOf[BindingsMap.ResolvedMethod]
|
||||
.asInstanceOf[BindingsMap.ResolvedModuleMethod]
|
||||
.method
|
||||
.name shouldEqual "static_method"
|
||||
// In B_Module, we only have ResolvedMethod in the resolvedImports, there is no ResolvedModule
|
||||
@ -274,7 +274,7 @@ class ImportExportTest
|
||||
bBindingMap
|
||||
.resolvedImports(0)
|
||||
.target
|
||||
.asInstanceOf[BindingsMap.ResolvedMethod]
|
||||
.asInstanceOf[BindingsMap.ResolvedModuleMethod]
|
||||
.method
|
||||
.name shouldEqual "static_method"
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ object ImportExport {
|
||||
moduleOrTypeName: String
|
||||
) extends Reason {
|
||||
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(
|
||||
|
@ -17,7 +17,7 @@ final class ExportsBuilder {
|
||||
def build(moduleName: QualifiedName, ir: IR): ModuleExports = {
|
||||
val symbols = getBindings(ir).exportedSymbols.values.flatten
|
||||
.collect {
|
||||
case BindingsMap.ResolvedMethod(module, method) =>
|
||||
case BindingsMap.ResolvedModuleMethod(module, method) =>
|
||||
ExportedSymbol.Method(module.getName.toString, method.name)
|
||||
case BindingsMap.ResolvedType(module, tp) =>
|
||||
ExportedSymbol.Type(module.getName.toString, tp.name)
|
||||
|
@ -198,31 +198,35 @@ public final class ImportExportCache
|
||||
@Persistable(
|
||||
clazz = org.enso.compiler.data.BindingsMap$ModuleReference$Abstract.class,
|
||||
id = 33007)
|
||||
@Persistable(clazz = BindingsMap.ModuleMethod.class, id = 33008)
|
||||
@Persistable(clazz = BindingsMap.Type.class, id = 33009)
|
||||
@Persistable(clazz = BindingsMap.ResolvedImport.class, id = 33010)
|
||||
@Persistable(clazz = BindingsMap.Cons.class, id = 33011)
|
||||
@Persistable(clazz = BindingsMap.ResolvedModule.class, id = 33012)
|
||||
@Persistable(clazz = BindingsMap.ResolvedType.class, id = 33013)
|
||||
@Persistable(clazz = BindingsMap.ResolvedMethod.class, id = 33014)
|
||||
@Persistable(clazz = BindingsMap.ExportedModule.class, id = 33015)
|
||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Only.class, id = 33016)
|
||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Union.class, id = 33017)
|
||||
@Persistable(clazz = BindingsMap.ResolvedModuleMethod.class, id = 33014)
|
||||
@Persistable(clazz = BindingsMap.ResolvedStaticMethod.class, id = 33015)
|
||||
@Persistable(clazz = BindingsMap.ResolvedConversionMethod.class, id = 33016)
|
||||
@Persistable(clazz = BindingsMap.ExportedModule.class, id = 33017)
|
||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Only.class, id = 33018)
|
||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Union.class, id = 33019)
|
||||
@Persistable(
|
||||
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Intersect.class,
|
||||
id = 33018)
|
||||
id = 33020)
|
||||
@Persistable(
|
||||
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$AllowedResolution.class,
|
||||
id = 33019)
|
||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$All$.class, id = 33020)
|
||||
id = 33021)
|
||||
@Persistable(clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$All$.class, id = 33022)
|
||||
@Persistable(
|
||||
clazz = org.enso.compiler.data.BindingsMap$SymbolRestriction$Hiding.class,
|
||||
id = 33021)
|
||||
@Persistable(clazz = BindingsMap.Resolution.class, id = 33029)
|
||||
@Persistable(clazz = BindingsMap.ResolvedConstructor.class, id = 33030)
|
||||
@Persistable(clazz = BindingsMap.ResolvedPolyglotSymbol.class, id = 33031)
|
||||
@Persistable(clazz = BindingsMap.ResolvedPolyglotField.class, id = 33032)
|
||||
@Persistable(clazz = BindingsMap.Argument.class, id = 33033)
|
||||
id = 33023)
|
||||
@Persistable(clazz = BindingsMap.Resolution.class, id = 33024)
|
||||
@Persistable(clazz = BindingsMap.ResolvedConstructor.class, id = 33025)
|
||||
@Persistable(clazz = BindingsMap.ResolvedPolyglotSymbol.class, id = 33026)
|
||||
@Persistable(clazz = BindingsMap.ResolvedPolyglotField.class, id = 33027)
|
||||
@Persistable(clazz = BindingsMap.ModuleMethod.class, id = 33028)
|
||||
@Persistable(clazz = BindingsMap.StaticMethod.class, id = 33029)
|
||||
@Persistable(clazz = BindingsMap.ConversionMethod.class, id = 33030)
|
||||
@Persistable(clazz = BindingsMap.Argument.class, id = 33031)
|
||||
@ServiceProvider(service = Persistance.class)
|
||||
public static final class PersistBindingsMap extends Persistance<BindingsMap> {
|
||||
public PersistBindingsMap() {
|
||||
|
@ -24,7 +24,13 @@ public final class ModuleScope implements EnsoObject {
|
||||
private final Map<String, Object> polyglotSymbols;
|
||||
private final Map<String, Type> types;
|
||||
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 Set<ImportExportScope> imports;
|
||||
private final Set<ImportExportScope> exports;
|
||||
|
||||
@ -97,24 +103,37 @@ public final class ModuleScope implements EnsoObject {
|
||||
.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
|
||||
public Function lookupConversionDefinition(Type original, Type target) {
|
||||
Function definedWithOriginal =
|
||||
original.getDefinitionScope().getConversionsFor(target).get(original);
|
||||
if (definedWithOriginal != null) {
|
||||
return definedWithOriginal;
|
||||
public Function lookupConversionDefinition(Type source, Type target) {
|
||||
Function definedWithSource = source.getDefinitionScope().getConversionsFor(target).get(source);
|
||||
if (definedWithSource != null) {
|
||||
return definedWithSource;
|
||||
}
|
||||
Function definedWithTarget =
|
||||
target.getDefinitionScope().getConversionsFor(target).get(original);
|
||||
Function definedWithTarget = target.getDefinitionScope().getConversionsFor(target).get(source);
|
||||
if (definedWithTarget != null) {
|
||||
return definedWithTarget;
|
||||
}
|
||||
Function definedHere = getConversionsFor(target).get(original);
|
||||
Function definedHere = getConversionsFor(target).get(source);
|
||||
if (definedHere != null) {
|
||||
return definedHere;
|
||||
}
|
||||
return imports.stream()
|
||||
.map(scope -> scope.getExportedConversion(original, target))
|
||||
.map(scope -> scope.getExportedConversion(source, target))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
@ -94,8 +94,6 @@ import org.enso.interpreter.node.{
|
||||
MethodRootNode,
|
||||
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.data.atom.{Atom, AtomConstructor}
|
||||
import org.enso.interpreter.runtime.callable.function.{
|
||||
@ -404,9 +402,17 @@ class IrToTruffle(
|
||||
throw new CompilerError(
|
||||
"Impossible polyglot field, should be caught by MethodDefinitions pass."
|
||||
)
|
||||
case _: BindingsMap.ResolvedMethod =>
|
||||
case _: BindingsMap.ResolvedModuleMethod =>
|
||||
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(
|
||||
"Impossible polyglot field, should be caught by MethodDefinitions pass."
|
||||
)
|
||||
case _: BindingsMap.ResolvedMethod =>
|
||||
case _: BindingsMap.ResolvedModuleMethod =>
|
||||
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,
|
||||
fun
|
||||
)
|
||||
case BindingsMap.ResolvedMethod(module, method) =>
|
||||
case BindingsMap.ResolvedModuleMethod(module, method) =>
|
||||
val actualModule = module.unsafeAsModule()
|
||||
val fun = asScope(actualModule)
|
||||
.getMethodForType(
|
||||
@ -995,6 +1009,100 @@ class IrToTruffle(
|
||||
name,
|
||||
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.ResolvedPolyglotField(_, _) =>
|
||||
}
|
||||
@ -1428,11 +1536,27 @@ class IrToTruffle(
|
||||
})
|
||||
case Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedMethod(_, _)
|
||||
BindingsMap.ResolvedModuleMethod(_, _)
|
||||
)
|
||||
) =>
|
||||
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)
|
||||
case BindingsMap.ResolvedMethod(_, method) =>
|
||||
case BindingsMap.ResolvedModuleMethod(_, method) =>
|
||||
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.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 {
|
||||
private ContextUtils() {}
|
||||
|
||||
@ -90,7 +88,6 @@ public final class ContextUtils {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable> E raise(Class<E> clazz, Throwable t) throws E {
|
||||
throw (E) t;
|
||||
@ -150,6 +147,18 @@ public final class ContextUtils {
|
||||
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.
|
||||
*
|
||||
|
@ -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.Value;
|
||||
|
||||
/**
|
||||
* Utility methods for creating and running Enso projects.
|
||||
*/
|
||||
/** Utility methods for creating and running Enso projects. */
|
||||
public class ProjectUtils {
|
||||
private ProjectUtils() {}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -42,13 +39,14 @@ public class ProjectUtils {
|
||||
*
|
||||
* @param projName Name of the project
|
||||
* @param modules Set of modules. Must contain `Main` module.
|
||||
* @param projDir A directory in which the whole project structure will be created.
|
||||
* Must exist and be a directory.
|
||||
* @param projDir A directory in which the whole project structure will be created. Must exist and
|
||||
* be a directory.
|
||||
*/
|
||||
public static void createProject(
|
||||
String projName, Set<SourceModule> modules, Path projDir) throws IOException {
|
||||
public static void createProject(String projName, Set<SourceModule> modules, Path projDir)
|
||||
throws IOException {
|
||||
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 =
|
||||
"""
|
||||
|
@ -2,9 +2,5 @@ package org.enso.test.utils;
|
||||
|
||||
import org.enso.pkg.QualifiedName;
|
||||
|
||||
/**
|
||||
* A simple structure corresponding to an Enso module.
|
||||
*/
|
||||
public record SourceModule(QualifiedName name, String code) {
|
||||
|
||||
}
|
||||
/** A simple structure corresponding to an Enso module. */
|
||||
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
|
||||
* node inside a context, all the other nodes, and insert them via
|
||||
* {@link #insertChildren(Node...)}.
|
||||
* node inside a context, all the other nodes, and insert them via {@link #insertChildren(Node...)}.
|
||||
*/
|
||||
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
|
||||
public Object execute(VirtualFrame frame) {
|
||||
if (callback == null) {
|
||||
|
Loading…
Reference in New Issue
Block a user