mirror of
https://github.com/enso-org/enso.git
synced 2024-12-24 08:12:06 +03:00
Add compiler support for conversion methods (#1834)
This commit is contained in:
parent
520cd70d55
commit
b71d01b507
@ -409,9 +409,14 @@ object AstView {
|
||||
varName match {
|
||||
case AST.Ident.Var.any(v) =>
|
||||
Some((v, expr, Some(exprVal), false))
|
||||
case AST.Ident.Blank.any(v) =>
|
||||
Some((v, expr, Some(exprVal), false))
|
||||
case AST.App.Section
|
||||
.Right(AST.Ident.Opr("~"), AST.Ident.Var.any(v)) =>
|
||||
Some((v, expr, Some(exprVal), true))
|
||||
case AST.App.Section
|
||||
.Right(AST.Ident.Opr("~"), AST.Ident.Blank.any(v)) =>
|
||||
Some((v, expr, Some(exprVal), true))
|
||||
case _ => None
|
||||
}
|
||||
case _ => None
|
||||
@ -420,9 +425,14 @@ object AstView {
|
||||
varName match {
|
||||
case AST.Ident.Var.any(v) =>
|
||||
Some((v, expr, None, false))
|
||||
case AST.Ident.Blank.any(v) =>
|
||||
Some((v, expr, None, false))
|
||||
case AST.App.Section
|
||||
.Right(AST.Ident.Opr("~"), AST.Ident.Var.any(v)) =>
|
||||
Some((v, expr, None, true))
|
||||
case AST.App.Section
|
||||
.Right(AST.Ident.Opr("~"), AST.Ident.Blank.any(v)) =>
|
||||
Some((v, expr, None, true))
|
||||
case _ => None
|
||||
}
|
||||
case _ => None
|
||||
|
@ -988,6 +988,11 @@ class IrToTruffle(
|
||||
.error()
|
||||
.compileError()
|
||||
.newInstance(Text.create(err.message))
|
||||
case err: Error.Redefined.Conversion =>
|
||||
context.getBuiltins
|
||||
.error()
|
||||
.compileError()
|
||||
.newInstance(Text.create(err.message))
|
||||
case err: Error.Redefined.Atom =>
|
||||
context.getBuiltins
|
||||
.error()
|
||||
|
@ -5620,6 +5620,11 @@ object IR {
|
||||
"A conversion definition must have at least one argument."
|
||||
}
|
||||
|
||||
case object UnsupportedSourceType extends Reason {
|
||||
override def explain: String =
|
||||
"Arbitrary expressions are not yet supported as source types."
|
||||
}
|
||||
|
||||
case class MissingSourceType(argName: String) extends Reason {
|
||||
override def explain: String =
|
||||
s"The argument `$argName` does not define a source type."
|
||||
@ -5631,6 +5636,11 @@ object IR {
|
||||
s"`$argName` does not."
|
||||
}
|
||||
|
||||
case class SuspendedSourceArgument(argName: String) extends Reason {
|
||||
override def explain: String =
|
||||
s"The source type argument in a conversion (here $argName) cannot " +
|
||||
s"be suspended."
|
||||
}
|
||||
}
|
||||
|
||||
/** A representation of an error resulting from name resolution.
|
||||
@ -6260,6 +6270,103 @@ object IR {
|
||||
override def showCode(indent: Int): String = "(Redefined This_Arg)"
|
||||
}
|
||||
|
||||
/** An error representing the redefinition of a conversion in a given
|
||||
* module. This is also known as a method overload.
|
||||
*
|
||||
* @param targetType the name of the atom the conversion was being
|
||||
* redefined on
|
||||
* @param sourceType the source type for the conversion
|
||||
* @param location the location in the source to which this error
|
||||
* corresponds
|
||||
* @param passData the pass metadata for the error
|
||||
* @param diagnostics any diagnostics associated with this error.
|
||||
*/
|
||||
sealed case class Conversion(
|
||||
targetType: IR.Name,
|
||||
sourceType: IR.Name,
|
||||
override val location: Option[IdentifiedLocation],
|
||||
override val passData: MetadataStorage = MetadataStorage(),
|
||||
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
||||
) extends Redefined
|
||||
with Diagnostic.Kind.Interactive
|
||||
with Module.Scope.Definition
|
||||
with IRKind.Primitive {
|
||||
override protected var id: Identifier = randomId
|
||||
|
||||
/** Creates a copy of `this`.
|
||||
*
|
||||
* @param targetType the name of the atom the conversion was being
|
||||
* redefined on
|
||||
* @param sourceType the source type for the conversion
|
||||
* @param location the location in the source to which this error
|
||||
* corresponds
|
||||
* @param passData the pass metadata for the error
|
||||
* @param diagnostics any diagnostics associated with this error.
|
||||
* @param id the identifier for the node
|
||||
* @return a copy of `this`, updated with the specified values
|
||||
*/
|
||||
def copy(
|
||||
targetType: IR.Name = targetType,
|
||||
sourceType: IR.Name = sourceType,
|
||||
location: Option[IdentifiedLocation] = location,
|
||||
passData: MetadataStorage = passData,
|
||||
diagnostics: DiagnosticStorage = diagnostics,
|
||||
id: Identifier = id
|
||||
): Conversion = {
|
||||
val res =
|
||||
Conversion(targetType, sourceType, location, passData, diagnostics)
|
||||
res.id = id
|
||||
res
|
||||
}
|
||||
|
||||
override def duplicate(
|
||||
keepLocations: Boolean = true,
|
||||
keepMetadata: Boolean = true,
|
||||
keepDiagnostics: Boolean = true
|
||||
): Conversion =
|
||||
copy(
|
||||
targetType = targetType
|
||||
.duplicate(keepLocations, keepMetadata, keepDiagnostics),
|
||||
sourceType = sourceType
|
||||
.duplicate(keepLocations, keepMetadata, keepDiagnostics),
|
||||
location = if (keepLocations) location else None,
|
||||
passData =
|
||||
if (keepMetadata) passData.duplicate else MetadataStorage(),
|
||||
diagnostics =
|
||||
if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(),
|
||||
id = randomId
|
||||
)
|
||||
|
||||
override def setLocation(
|
||||
location: Option[IdentifiedLocation]
|
||||
): Conversion =
|
||||
copy(location = location)
|
||||
|
||||
override def message: String =
|
||||
s"Method overloads are not supported: ${targetType.name}.from " +
|
||||
s"${sourceType.showCode()} is defined multiple times in this module."
|
||||
|
||||
override def mapExpressions(fn: Expression => Expression): Conversion =
|
||||
this
|
||||
|
||||
override def toString: String =
|
||||
s"""
|
||||
|IR.Error.Redefined.Method(
|
||||
|targetType = $targetType,
|
||||
|sourceType = $sourceType,
|
||||
|location = $location,
|
||||
|passData = ${this.showPassData},
|
||||
|diagnostics = $diagnostics,
|
||||
|id = $id
|
||||
|)
|
||||
|""".stripMargin
|
||||
|
||||
override def children: List[IR] = List(targetType, sourceType)
|
||||
|
||||
override def showCode(indent: Int): String =
|
||||
s"(Redefined (Conversion $targetType.from $sourceType))"
|
||||
}
|
||||
|
||||
/** An error representing the redefinition of a method in a given module.
|
||||
* This is also known as a method overload.
|
||||
*
|
||||
|
@ -2,7 +2,6 @@ package org.enso.compiler.pass.analyse
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.core.IR.Pattern
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
@ -136,10 +135,23 @@ case object AliasAnalysis extends IRPass {
|
||||
val topLevelGraph = new Graph
|
||||
|
||||
ir match {
|
||||
case _: Method.Conversion =>
|
||||
throw new CompilerError("Conversion methods are not yet supported.")
|
||||
case m @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, body, _, _, _) =>
|
||||
case m: IR.Module.Scope.Definition.Method.Conversion =>
|
||||
m.body match {
|
||||
case _: IR.Function =>
|
||||
m.copy(
|
||||
body = analyseExpression(
|
||||
m.body,
|
||||
topLevelGraph,
|
||||
topLevelGraph.rootScope,
|
||||
lambdaReuseScope = true
|
||||
)
|
||||
).updateMetadata(this -->> Info.Scope.Root(topLevelGraph))
|
||||
case _ =>
|
||||
throw new CompilerError(
|
||||
"The body of a method should always be a function."
|
||||
)
|
||||
}
|
||||
case m @ IR.Module.Scope.Definition.Method.Explicit(_, body, _, _, _) =>
|
||||
body match {
|
||||
case _: IR.Function =>
|
||||
m.copy(
|
||||
@ -181,7 +193,7 @@ case object AliasAnalysis extends IRPass {
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of alias " +
|
||||
"analysis."
|
||||
"analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
@ -342,7 +354,15 @@ case object AliasAnalysis extends IRPass {
|
||||
scope: Scope
|
||||
): List[IR.DefinitionArgument] = {
|
||||
args.map {
|
||||
case arg @ IR.DefinitionArgument.Specified(name, _, value, susp, _, _, _) =>
|
||||
case arg @ IR.DefinitionArgument.Specified(
|
||||
name,
|
||||
_,
|
||||
value,
|
||||
susp,
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) =>
|
||||
val nameOccursInScope =
|
||||
scope.hasSymbolOccurrenceAs[Occurrence.Def](name.name)
|
||||
if (!nameOccursInScope) {
|
||||
|
@ -7,7 +7,14 @@ import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.desugar.{ComplexType, FunctionBinding, GenerateMethodBodies, LambdaShorthandToLambda, OperatorToFunction, SectionsToBinOp}
|
||||
import org.enso.compiler.pass.desugar.{
|
||||
ComplexType,
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies,
|
||||
LambdaShorthandToLambda,
|
||||
OperatorToFunction,
|
||||
SectionsToBinOp
|
||||
}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.jdk.CollectionConverters._
|
||||
@ -85,8 +92,10 @@ case object CachePreferenceAnalysis extends IRPass {
|
||||
arguments.map(analyseDefinitionArgument(_, weights))
|
||||
)
|
||||
.updateMetadata(this -->> weights)
|
||||
case _: Method.Conversion =>
|
||||
throw new CompilerError("Conversion methods are not yet supported.")
|
||||
case method: Method.Conversion =>
|
||||
method
|
||||
.copy(body = analyseExpression(method.body, weights))
|
||||
.updateMetadata(this -->> weights)
|
||||
case method @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, body, _, _, _) =>
|
||||
method
|
||||
|
@ -103,8 +103,18 @@ case object DataflowAnalysis extends IRPass {
|
||||
arguments = arguments.map(analyseDefinitionArgument(_, info))
|
||||
)
|
||||
.updateMetadata(this -->> info)
|
||||
case _: Method.Conversion =>
|
||||
throw new CompilerError("Conversion methods are not yet supported.")
|
||||
case m: Method.Conversion =>
|
||||
val bodyDep = asStatic(m.body)
|
||||
val methodDep = asStatic(m)
|
||||
val sourceTypeDep = asStatic(m.sourceTypeName)
|
||||
info.dependents.updateAt(sourceTypeDep, Set(methodDep))
|
||||
info.dependents.updateAt(bodyDep, Set(methodDep))
|
||||
info.dependencies.updateAt(methodDep, Set(bodyDep, sourceTypeDep))
|
||||
|
||||
m.copy(
|
||||
body = analyseExpression(m.body, info),
|
||||
sourceTypeName = m.sourceTypeName.updateMetadata(this -->> info)
|
||||
).updateMetadata(this -->> info)
|
||||
case method @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, body, _, _, _) =>
|
||||
val bodyDep = asStatic(body)
|
||||
|
@ -2,7 +2,6 @@ package org.enso.compiler.pass.analyse
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.core.IR.Pattern
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
@ -83,8 +82,12 @@ case object TailCall extends IRPass {
|
||||
definition: IR.Module.Scope.Definition
|
||||
): IR.Module.Scope.Definition = {
|
||||
definition match {
|
||||
case _: Method.Conversion =>
|
||||
throw new CompilerError("Conversion methods are not yet supported.")
|
||||
case method: IR.Module.Scope.Definition.Method.Conversion =>
|
||||
method
|
||||
.copy(
|
||||
body = analyseExpression(method.body, isInTailPosition = true)
|
||||
)
|
||||
.updateMetadata(this -->> TailPosition.Tail)
|
||||
case method @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, body, _, _, _) =>
|
||||
method
|
||||
|
@ -3,7 +3,6 @@ package org.enso.compiler.pass.resolve
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Case.Branch
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
@ -132,8 +131,11 @@ case object DocumentationComments extends IRPass {
|
||||
ir: IR.Module.Scope.Definition
|
||||
): IR.Module.Scope.Definition =
|
||||
ir match {
|
||||
case _: Method.Conversion =>
|
||||
throw new CompilerError("Conversion methods are not yet supported.")
|
||||
case _: IR.Module.Scope.Definition.Method.Conversion =>
|
||||
throw new CompilerError(
|
||||
"Conversion methods should not yet be present in the compiler " +
|
||||
"pipeline."
|
||||
)
|
||||
case method: IR.Module.Scope.Definition.Method.Binding =>
|
||||
method.copy(body = resolveExpression(method.body))
|
||||
case method: IR.Module.Scope.Definition.Method.Explicit =>
|
||||
|
@ -13,8 +13,8 @@ import org.enso.compiler.pass.desugar.{
|
||||
GenerateMethodBodies
|
||||
}
|
||||
|
||||
/** Resolves the correct `this` argument type for methods definitions
|
||||
* and stores the resolution in the method's metadata.
|
||||
/** Resolves the correct `this` argument type for method definitions and stores
|
||||
* the resolution in the method's metadata.
|
||||
*/
|
||||
case object MethodDefinitions extends IRPass {
|
||||
|
||||
@ -45,58 +45,101 @@ case object MethodDefinitions extends IRPass {
|
||||
"MethodDefinitionResolution is being run before BindingResolution"
|
||||
)
|
||||
val newDefs = ir.bindings.map {
|
||||
case method: IR.Module.Scope.Definition.Method.Explicit =>
|
||||
case method: IR.Module.Scope.Definition.Method =>
|
||||
val methodRef = method.methodReference
|
||||
val resolvedTypeRef = methodRef.typePointer match {
|
||||
case tp: IR.Name.Here =>
|
||||
tp.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(availableSymbolsMap.currentModule)
|
||||
)
|
||||
)
|
||||
case tp @ IR.Name.Qualified(names, _, _, _) =>
|
||||
val items = names.map(_.name)
|
||||
availableSymbolsMap.resolveQualifiedName(items) match {
|
||||
case Left(err) =>
|
||||
IR.Error.Resolution(tp, IR.Error.Resolution.ResolverError(err))
|
||||
case Right(value: BindingsMap.ResolvedConstructor) =>
|
||||
tp.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedModule) =>
|
||||
tp.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedPolyglotSymbol) =>
|
||||
IR.Error.Resolution(
|
||||
tp,
|
||||
IR.Error.Resolution.UnexpectedPolyglot(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
IR.Error.Resolution(
|
||||
tp,
|
||||
IR.Error.Resolution.UnexpectedMethod(
|
||||
"a method definition target"
|
||||
)
|
||||
val resolvedTypeRef =
|
||||
resolveType(methodRef.typePointer, availableSymbolsMap)
|
||||
val resolvedMethodRef = methodRef.copy(typePointer = resolvedTypeRef)
|
||||
|
||||
method match {
|
||||
case method: IR.Module.Scope.Definition.Method.Explicit =>
|
||||
val resolvedMethod =
|
||||
method.copy(methodReference = resolvedMethodRef)
|
||||
resolvedMethod
|
||||
case method: IR.Module.Scope.Definition.Method.Conversion =>
|
||||
val sourceTypeExpr = method.sourceTypeName
|
||||
|
||||
val resolvedName: IR.Name = sourceTypeExpr match {
|
||||
case name: IR.Name => resolveType(name, availableSymbolsMap)
|
||||
case _ =>
|
||||
IR.Error.Conversion(
|
||||
sourceTypeExpr,
|
||||
IR.Error.Conversion.UnsupportedSourceType
|
||||
)
|
||||
}
|
||||
case tp: IR.Error.Resolution => tp
|
||||
|
||||
val resolvedMethod = method.copy(
|
||||
methodReference = resolvedMethodRef,
|
||||
sourceTypeName = resolvedName
|
||||
)
|
||||
resolvedMethod
|
||||
|
||||
case _ =>
|
||||
throw new CompilerError(
|
||||
"Unexpected kind of name for method reference"
|
||||
"Unexpected method type in MethodDefinitions pass."
|
||||
)
|
||||
}
|
||||
val resolvedMethodRef = methodRef.copy(typePointer = resolvedTypeRef)
|
||||
val resolvedMethod = method.copy(methodReference = resolvedMethodRef)
|
||||
resolvedMethod
|
||||
case other => other
|
||||
}
|
||||
|
||||
ir.copy(bindings = newDefs)
|
||||
}
|
||||
|
||||
private def resolveType(
|
||||
typePointer: IR.Name,
|
||||
availableSymbolsMap: BindingsMap
|
||||
): IR.Name = {
|
||||
typePointer match {
|
||||
case tp: IR.Name.Here =>
|
||||
tp.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(availableSymbolsMap.currentModule)
|
||||
)
|
||||
)
|
||||
case _: IR.Name.Qualified | _: IR.Name.Literal =>
|
||||
val items = typePointer match {
|
||||
case IR.Name.Qualified(names, _, _, _) => names.map(_.name)
|
||||
case IR.Name.Literal(name, _, _, _, _, _) => List(name)
|
||||
case _ =>
|
||||
throw new CompilerError("Impossible to reach.")
|
||||
}
|
||||
availableSymbolsMap.resolveQualifiedName(items) match {
|
||||
case Left(err) =>
|
||||
IR.Error.Resolution(
|
||||
typePointer,
|
||||
IR.Error.Resolution.ResolverError(err)
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedConstructor) =>
|
||||
typePointer.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedModule) =>
|
||||
typePointer.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedPolyglotSymbol) =>
|
||||
IR.Error.Resolution(
|
||||
typePointer,
|
||||
IR.Error.Resolution.UnexpectedPolyglot(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
IR.Error.Resolution(
|
||||
typePointer,
|
||||
IR.Error.Resolution.UnexpectedMethod(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
}
|
||||
case tp: IR.Error.Resolution => tp
|
||||
case _ =>
|
||||
throw new CompilerError(
|
||||
"Unexpected kind of name for method reference"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Executes the pass on the provided `ir`, and returns a possibly transformed
|
||||
* or annotated version of `ir` in an inline context.
|
||||
*
|
||||
|
@ -4,6 +4,7 @@ import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.data.BindingsMap.ResolvedModule
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis
|
||||
|
||||
@ -48,15 +49,26 @@ case object ModuleThisToHere extends IRPass {
|
||||
val localResolution =
|
||||
BindingsMap.Resolution(ResolvedModule(moduleContext.module))
|
||||
val newBindings = ir.bindings.map {
|
||||
case m: IR.Module.Scope.Definition.Method.Explicit =>
|
||||
case m: IR.Module.Scope.Definition.Method =>
|
||||
if (
|
||||
m.methodReference.typePointer
|
||||
.getMetadata(MethodDefinitions)
|
||||
.contains(localResolution)
|
||||
) {
|
||||
m.copy(body = m.body.transformExpressions {
|
||||
val result = m.body.transformExpressions {
|
||||
case IR.Name.This(loc, _, _) => IR.Name.Here(loc)
|
||||
})
|
||||
}
|
||||
|
||||
m match {
|
||||
case m: IR.Module.Scope.Definition.Method.Explicit =>
|
||||
m.copy(body = result)
|
||||
case m: IR.Module.Scope.Definition.Method.Conversion =>
|
||||
m.copy(body = result)
|
||||
case _ =>
|
||||
throw new CompilerError(
|
||||
"Impossible method type during `ModuleThisToHere`."
|
||||
)
|
||||
}
|
||||
} else m
|
||||
case other => other
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.desugar.{ComplexType, GenerateMethodBodies}
|
||||
|
||||
import scala.annotation.unused
|
||||
import scala.collection.mutable
|
||||
|
||||
/** This pass performs static detection of method overloads and emits errors
|
||||
* at the overload definition site if they are detected. It also checks for
|
||||
@ -63,7 +64,7 @@ case object OverloadsResolution extends IRPass {
|
||||
})
|
||||
|
||||
val methods = ir.bindings.collect {
|
||||
case meth: IR.Module.Scope.Definition.Method =>
|
||||
case meth: IR.Module.Scope.Definition.Method.Explicit =>
|
||||
seenMethods = seenMethods + (meth.typeName.name -> Set())
|
||||
meth
|
||||
}
|
||||
@ -81,8 +82,30 @@ case object OverloadsResolution extends IRPass {
|
||||
}
|
||||
})
|
||||
|
||||
val conversionsForType: mutable.Map[String, Set[String]] = mutable.Map()
|
||||
|
||||
val conversions: List[IR.Module.Scope.Definition] = ir.bindings.collect {
|
||||
case m: IR.Module.Scope.Definition.Method.Conversion =>
|
||||
val fromName = m.sourceTypeName.asInstanceOf[IR.Name]
|
||||
conversionsForType.get(m.typeName.name) match {
|
||||
case Some(elems) =>
|
||||
if (elems.contains(fromName.name)) {
|
||||
IR.Error.Redefined.Conversion(m.typeName, fromName, m.location)
|
||||
} else {
|
||||
conversionsForType.update(
|
||||
m.typeName.name,
|
||||
conversionsForType(m.typeName.name) + fromName.name
|
||||
)
|
||||
m
|
||||
}
|
||||
case None =>
|
||||
conversionsForType.put(m.typeName.name, Set(fromName.name))
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
ir.copy(
|
||||
bindings = newAtoms ::: newMethods
|
||||
bindings = newAtoms ::: newMethods ::: conversions
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -102,8 +102,46 @@ case object SuspendedArguments extends IRPass {
|
||||
binding: IR.Module.Scope.Definition
|
||||
): IR.Module.Scope.Definition = {
|
||||
binding match {
|
||||
case _: Method.Conversion =>
|
||||
throw new CompilerError("Conversion methods are not yet supported.")
|
||||
case method: Method.Conversion =>
|
||||
method.body match {
|
||||
case lam @ IR.Function.Lambda(args, body, _, _, _, _) =>
|
||||
method.getMetadata(TypeSignatures) match {
|
||||
case Some(Signature(signature)) =>
|
||||
val newArgs = computeSuspensions(args.drop(1), signature)
|
||||
if (newArgs.head.suspended) {
|
||||
IR.Error.Conversion(
|
||||
method,
|
||||
IR.Error.Conversion.SuspendedSourceArgument(
|
||||
newArgs.head.name.name
|
||||
)
|
||||
)
|
||||
} else {
|
||||
method.copy(body =
|
||||
lam.copy(
|
||||
arguments = args.head :: newArgs,
|
||||
body = resolveExpression(body)
|
||||
)
|
||||
)
|
||||
}
|
||||
case None =>
|
||||
if (args(1).suspended) {
|
||||
IR.Error.Conversion(
|
||||
method,
|
||||
IR.Error.Conversion.SuspendedSourceArgument(
|
||||
args(1).name.name
|
||||
)
|
||||
)
|
||||
} else {
|
||||
method.copy(
|
||||
body = lam.copy(body = resolveExpression(body))
|
||||
)
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
throw new CompilerError(
|
||||
"Method bodies must be lambdas at this point."
|
||||
)
|
||||
}
|
||||
case explicit @ Method.Explicit(_, body, _, _, _) =>
|
||||
body match {
|
||||
case lam @ IR.Function.Lambda(args, lamBody, _, _, _, _) =>
|
||||
|
@ -12,6 +12,8 @@ import org.enso.compiler.pass.analyse.AliasAnalysis.{Graph, Info}
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
class AliasAnalysisTest extends CompilerTest {
|
||||
|
||||
// === Utilities ============================================================
|
||||
@ -833,6 +835,122 @@ class AliasAnalysisTest extends CompilerTest {
|
||||
}
|
||||
}
|
||||
|
||||
"Alias analysis on conversion methods" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val conversionMethod =
|
||||
"""Bar.from (value : Foo) =
|
||||
| Bar value.get_thing here
|
||||
|""".stripMargin.preprocessModule.analyse.bindings.head
|
||||
.asInstanceOf[Method.Conversion]
|
||||
|
||||
val graph = conversionMethod
|
||||
.unsafeGetMetadata(AliasAnalysis, "Missing aliasing info")
|
||||
.unsafeAs[Info.Scope.Root]
|
||||
.graph
|
||||
val graphLinks = graph.links
|
||||
|
||||
val lambda = conversionMethod.body.asInstanceOf[IR.Function.Lambda]
|
||||
val lambdaBody = lambda.body.asInstanceOf[IR.Expression.Block]
|
||||
val app = lambdaBody.returnValue.asInstanceOf[IR.Application.Prefix]
|
||||
|
||||
"assign Info.Scope.Root metatata to the method" in {
|
||||
val meta = conversionMethod.getMetadata(AliasAnalysis)
|
||||
|
||||
meta shouldBe defined
|
||||
meta.get shouldBe an[Info.Scope.Root]
|
||||
}
|
||||
|
||||
"assign Info.Scope.Child to all child scopes" in {
|
||||
lambda.getMetadata(AliasAnalysis).get shouldBe an[Info.Scope.Child]
|
||||
lambdaBody.getMetadata(AliasAnalysis).get shouldBe an[Info.Scope.Child]
|
||||
}
|
||||
|
||||
"not allocate additional scopes unnecessarily" in {
|
||||
graph.nesting shouldEqual 3
|
||||
graph.numScopes shouldEqual 4
|
||||
|
||||
val topScope = graph.rootScope
|
||||
val lambdaScope = lambda
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Scope.Child]
|
||||
.scope
|
||||
val blockScope = lambdaBody
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Scope.Child]
|
||||
.scope
|
||||
|
||||
topScope shouldEqual lambdaScope
|
||||
lambdaScope shouldEqual blockScope
|
||||
}
|
||||
|
||||
"allocate new scopes where necessary" in {
|
||||
val topScope = graph.rootScope
|
||||
val arg1Scope = app.arguments.head
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Scope.Child]
|
||||
.scope
|
||||
@unused val arg2Scope = app
|
||||
.arguments(1)
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Scope.Child]
|
||||
.scope
|
||||
|
||||
topScope.childScopes should contain(arg1Scope)
|
||||
topScope.childScopes should contain(arg2Scope)
|
||||
}
|
||||
|
||||
"assign Info.Occurrence to definitions and usages of symbols" in {
|
||||
lambda.arguments.foreach(arg =>
|
||||
arg.getMetadata(AliasAnalysis).get.as[Info.Occurrence] shouldBe defined
|
||||
)
|
||||
val firstAppArg =
|
||||
app.arguments.head.value.asInstanceOf[IR.Application.Prefix]
|
||||
val innerAppArg = firstAppArg.arguments.head.value
|
||||
innerAppArg
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.as[Info.Occurrence] shouldBe defined
|
||||
}
|
||||
|
||||
"create the correct usage links for resolvable entities" in {
|
||||
val valueDefId = lambda
|
||||
.arguments(1)
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Occurrence]
|
||||
.id
|
||||
val valueUseId = app.arguments.head.value
|
||||
.asInstanceOf[IR.Application.Prefix]
|
||||
.arguments
|
||||
.head
|
||||
.value
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Occurrence]
|
||||
.id
|
||||
|
||||
graphLinks should contain(Link(valueUseId, 2, valueDefId))
|
||||
}
|
||||
|
||||
"not resolve links for unknown symbols" in {
|
||||
val unknownHereId = app
|
||||
.arguments(1)
|
||||
.value
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Occurrence]
|
||||
.id
|
||||
|
||||
graph.linksFor(unknownHereId) shouldBe empty
|
||||
graph.getOccurrence(unknownHereId).get shouldBe an[Occurrence.Use]
|
||||
}
|
||||
}
|
||||
|
||||
"Alias analysis on case expressions" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
|
@ -3,7 +3,6 @@ package org.enso.compiler.test.pass.analyse
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.data.BindingsMap.{Cons, ModuleMethod, PolyglotSymbol}
|
||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
@ -60,17 +59,30 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
|Baz.foo = 123
|
||||
|Bar.baz = Baz 1 2 . foo
|
||||
|
|
||||
|from (_ : Bar) = Foo 0 0 0
|
||||
|from (value : Baz) = Foo value.x value.x value.y
|
||||
|
|
||||
|Foo.from (_ : Bar) = undefined
|
||||
|
|
||||
|foo = 123
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
ir.getMetadata(BindingAnalysis) shouldEqual Some(
|
||||
BindingsMap(
|
||||
List(Cons("Foo", 3), Cons("Bar", 0), Cons("Baz", 2)),
|
||||
List(PolyglotSymbol("MyClass"), PolyglotSymbol("Renamed_Class")),
|
||||
List(ModuleMethod("enso_project"), ModuleMethod("foo")),
|
||||
ctx.module
|
||||
)
|
||||
val metadata = ir.unsafeGetMetadata(BindingAnalysis, "Should exist.")
|
||||
|
||||
metadata.types shouldEqual List(
|
||||
Cons("Foo", 3),
|
||||
Cons("Bar", 0),
|
||||
Cons("Baz", 2)
|
||||
)
|
||||
metadata.polyglotSymbols shouldEqual List(
|
||||
PolyglotSymbol("MyClass"),
|
||||
PolyglotSymbol("Renamed_Class")
|
||||
)
|
||||
metadata.moduleMethods shouldEqual List(
|
||||
ModuleMethod("enso_project"),
|
||||
ModuleMethod("foo")
|
||||
)
|
||||
metadata.currentModule shouldEqual ctx.module
|
||||
}
|
||||
|
||||
"properly assign module-level methods when a type with the same name as module is defined" in {
|
||||
|
@ -3,14 +3,15 @@ package org.enso.compiler.test.pass.analyse
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.core.IR.Pattern
|
||||
import org.enso.compiler.data.CompilerConfig
|
||||
import org.enso.compiler.pass.PassConfiguration._
|
||||
import org.enso.compiler.pass.analyse.DataflowAnalysis.DependencyInfo.Type.asStatic
|
||||
import org.enso.compiler.pass.analyse.DataflowAnalysis.{
|
||||
DependencyInfo,
|
||||
DependencyMapping
|
||||
}
|
||||
import org.enso.compiler.pass.analyse.DataflowAnalysis.DependencyInfo.Type.asStatic
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, DataflowAnalysis}
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
@ -1587,4 +1588,110 @@ class DataflowAnalysisTest extends CompilerTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"Dataflow analysis of conversions" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|Foo.from (value : Bar) =
|
||||
| Foo value 1
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
val depInfo = ir.getMetadata(DataflowAnalysis).get
|
||||
|
||||
// The method and its body
|
||||
val conversion = ir.bindings.head.asInstanceOf[Method.Conversion]
|
||||
val sourceType = conversion.sourceTypeName.asInstanceOf[IR.Name]
|
||||
val lambda = conversion.body.asInstanceOf[IR.Function.Lambda]
|
||||
val fnArgThis =
|
||||
lambda.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
val fnArgValue =
|
||||
lambda.arguments(1).asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
val fnBody = lambda.body.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
// The `Foo` application
|
||||
val fooExpr = fnBody.returnValue.asInstanceOf[IR.Application.Prefix]
|
||||
val fooFunction = fooExpr.function.asInstanceOf[IR.Name]
|
||||
val fooArg1 = fooExpr.arguments.head.asInstanceOf[IR.CallArgument.Specified]
|
||||
val fooArg1Expr = fooArg1.value.asInstanceOf[IR.Name]
|
||||
val fooArg2 = fooExpr.arguments(1).asInstanceOf[IR.CallArgument.Specified]
|
||||
val fooArg2Expr = fooArg2.value.asInstanceOf[IR.Literal.Number]
|
||||
|
||||
// The global symbols
|
||||
val fooSymbol = mkDynamicDep("Foo")
|
||||
|
||||
// The identifiers
|
||||
val conversionId = mkStaticDep(conversion.getId)
|
||||
val sourceTypeId = mkStaticDep(sourceType.getId)
|
||||
val lambdaId = mkStaticDep(lambda.getId)
|
||||
val fnArgThisId = mkStaticDep(fnArgThis.getId)
|
||||
val fnArgValueId = mkStaticDep(fnArgValue.getId)
|
||||
val fnBodyId = mkStaticDep(fnBody.getId)
|
||||
val fooExprId = mkStaticDep(fooExpr.getId)
|
||||
val fooFunctionId = mkStaticDep(fooFunction.getId)
|
||||
val fooArg1Id = mkStaticDep(fooArg1.getId)
|
||||
val fooArg1ExprId = mkStaticDep(fooArg1Expr.getId)
|
||||
val fooArg2Id = mkStaticDep(fooArg2.getId)
|
||||
val fooArg2ExprId = mkStaticDep(fooArg2Expr.getId)
|
||||
|
||||
// The info
|
||||
val dependents = depInfo.dependents
|
||||
val dependencies = depInfo.dependencies
|
||||
|
||||
"correctly identify global symbol direct dependents" in {
|
||||
dependents.getDirect(fooSymbol) shouldEqual Some(Set(fooFunctionId))
|
||||
}
|
||||
|
||||
"correctly identify global symbol direct dependencies" in {
|
||||
dependencies.getDirect(fooSymbol) shouldBe empty
|
||||
}
|
||||
|
||||
"correctly identify local direct dependents" in {
|
||||
dependents.getDirect(conversionId) shouldBe empty
|
||||
dependents.getDirect(sourceTypeId) shouldEqual Some(Set(conversionId))
|
||||
dependents.getDirect(lambdaId) shouldEqual Some(Set(conversionId))
|
||||
dependents.getDirect(fnArgThisId) shouldBe empty
|
||||
dependents.getDirect(fnArgValueId) shouldBe Some(Set(fooArg1ExprId))
|
||||
dependents.getDirect(fnBodyId) shouldBe Some(Set(lambdaId))
|
||||
dependents.getDirect(fooExprId) shouldBe Some(Set(fnBodyId))
|
||||
dependents.getDirect(fooFunctionId) shouldBe Some(Set(fooExprId))
|
||||
dependents.getDirect(fooArg1Id) shouldBe Some(Set(fooExprId))
|
||||
dependents.getDirect(fooArg1ExprId) shouldBe Some(Set(fooArg1Id))
|
||||
dependents.getDirect(fooArg2Id) shouldBe Some(Set(fooExprId))
|
||||
dependents.getDirect(fooArg2ExprId) shouldBe Some(Set(fooArg2Id))
|
||||
}
|
||||
|
||||
"correctly identify local direct dependencies" in {
|
||||
dependencies.getDirect(conversionId) shouldEqual Some(
|
||||
Set(sourceTypeId, lambdaId)
|
||||
)
|
||||
dependencies.getDirect(sourceTypeId) shouldBe empty
|
||||
dependencies.getDirect(lambdaId) shouldEqual Some(Set(fnBodyId))
|
||||
dependencies.getDirect(fnBodyId) shouldEqual Some(Set(fooExprId))
|
||||
dependencies.getDirect(fooExprId) shouldEqual Some(
|
||||
Set(fooFunctionId, fooArg1Id, fooArg2Id)
|
||||
)
|
||||
dependencies.getDirect(fooFunctionId) shouldEqual Some(Set(fooSymbol))
|
||||
dependencies.getDirect(fooArg1Id) shouldEqual Some(Set(fooArg1ExprId))
|
||||
dependencies.getDirect(fooArg2Id) shouldEqual Some(Set(fooArg2ExprId))
|
||||
dependencies.getDirect(fooArg1ExprId) shouldEqual Some(Set(fnArgValueId))
|
||||
dependencies.getDirect(fooArg2ExprId) shouldBe empty
|
||||
}
|
||||
|
||||
"associate the dependency info with every node in the IR" in {
|
||||
conversion.hasDependencyInfo
|
||||
sourceType.hasDependencyInfo
|
||||
lambda.hasDependencyInfo
|
||||
fnArgThis.hasDependencyInfo
|
||||
fnArgValue.hasDependencyInfo
|
||||
fnBody.hasDependencyInfo
|
||||
fooExpr.hasDependencyInfo
|
||||
fooFunction.hasDependencyInfo
|
||||
fooArg1.hasDependencyInfo
|
||||
fooArg1Expr.hasDependencyInfo
|
||||
fooArg2.hasDependencyInfo
|
||||
fooArg2Expr.hasDependencyInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,16 +95,20 @@ class TailCallTest extends CompilerTest {
|
||||
| _ -> d
|
||||
|
|
||||
|type MyAtom a b c
|
||||
|
|
||||
|Foo.from (value : Bar) = undefined
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"mark methods as tail" in {
|
||||
ir.bindings.head
|
||||
.getMetadata(TailCall) shouldEqual Some(TailPosition.Tail)
|
||||
ir.bindings.head.getMetadata(TailCall) shouldEqual Some(TailPosition.Tail)
|
||||
}
|
||||
|
||||
"mark atoms as tail" in {
|
||||
ir.bindings(1)
|
||||
.getMetadata(TailCall) shouldEqual Some(TailPosition.Tail)
|
||||
ir.bindings(1).getMetadata(TailCall) shouldEqual Some(TailPosition.Tail)
|
||||
}
|
||||
|
||||
"mark conversions as tail" in {
|
||||
ir.bindings(2).getMetadata(TailCall) shouldEqual Some(TailPosition.Tail)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.desugar.FunctionBinding
|
||||
import org.enso.compiler.pass.resolve.{DocumentationComments, ModuleAnnotations}
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
@ -172,6 +173,30 @@ class FunctionBindingTest extends CompilerTest {
|
||||
subArguments.head.suspended shouldBe true
|
||||
}
|
||||
|
||||
"retain documentation comments and annotations associated with them" in {
|
||||
val ir =
|
||||
s"""
|
||||
|## My documentation for this conversion.
|
||||
|@My_Annotation
|
||||
|My_Type.$from (that : Value) = that
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
ir.bindings.head shouldBe an[IR.Module.Scope.Definition.Method.Conversion]
|
||||
val conversion = ir.bindings.head
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Conversion]
|
||||
|
||||
val annotations =
|
||||
conversion.unsafeGetMetadata(ModuleAnnotations, "Should be present.")
|
||||
annotations.annotations.length shouldEqual 1
|
||||
annotations.annotations.head.name shouldEqual "@My_Annotation"
|
||||
|
||||
val doc = conversion.unsafeGetMetadata(
|
||||
DocumentationComments,
|
||||
"Should be present."
|
||||
)
|
||||
doc.documentation shouldEqual " My documentation for this conversion."
|
||||
}
|
||||
|
||||
"return an error if the conversion has no arguments" in {
|
||||
val ir =
|
||||
s"""My_Type.$from = a + b
|
||||
|
@ -39,7 +39,7 @@ class MethodDefinitionsTest extends CompilerTest {
|
||||
* @param context the module context in which analysis takes place
|
||||
* @return [[ir]], with tail call analysis metadata attached
|
||||
*/
|
||||
def analyse(implicit context: ModuleContext) = {
|
||||
def analyse(implicit context: ModuleContext): IR.Module = {
|
||||
MethodDefinitions.runModule(ir, context)
|
||||
}
|
||||
}
|
||||
@ -52,6 +52,7 @@ class MethodDefinitionsTest extends CompilerTest {
|
||||
val ir =
|
||||
"""
|
||||
|type Foo a b c
|
||||
|type Bar
|
||||
|
|
||||
|Foo.my_method a b c = a + b + c
|
||||
|
|
||||
@ -60,10 +61,16 @@ class MethodDefinitionsTest extends CompilerTest {
|
||||
|Test_Module.other_method = 11
|
||||
|
|
||||
|Does_Not_Exist.method = 32
|
||||
|
|
||||
|Foo.from (value : Bar) = undefined
|
||||
|
|
||||
|Bar.from (value : Does_Not_Exist) = undefined
|
||||
|
|
||||
|Does_Not_Exist.from (value : Foo) = undefined
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"attach resolved atoms to the method definitions" in {
|
||||
ir.bindings(1)
|
||||
ir.bindings(2)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.methodReference
|
||||
.typePointer
|
||||
@ -75,15 +82,6 @@ class MethodDefinitionsTest extends CompilerTest {
|
||||
)
|
||||
)
|
||||
)
|
||||
ir.bindings(2)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.methodReference
|
||||
.typePointer
|
||||
.getMetadata(MethodDefinitions) shouldEqual Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(ctx.module)
|
||||
)
|
||||
)
|
||||
ir.bindings(3)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.methodReference
|
||||
@ -94,9 +92,56 @@ class MethodDefinitionsTest extends CompilerTest {
|
||||
)
|
||||
)
|
||||
ir.bindings(4)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.methodReference
|
||||
.typePointer
|
||||
.getMetadata(MethodDefinitions) shouldEqual Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(ctx.module)
|
||||
)
|
||||
)
|
||||
ir.bindings(5)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.methodReference
|
||||
.typePointer shouldBe a[IR.Error.Resolution]
|
||||
|
||||
val conv1 = ir
|
||||
.bindings(6)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Conversion]
|
||||
conv1.methodReference.typePointer.getMetadata(
|
||||
MethodDefinitions
|
||||
) shouldEqual Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedConstructor(ctx.module, Cons("Foo", 3))
|
||||
)
|
||||
)
|
||||
conv1.sourceTypeName.getMetadata(MethodDefinitions) shouldEqual Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedConstructor(ctx.module, Cons("Bar", 0))
|
||||
)
|
||||
)
|
||||
|
||||
val conv2 = ir
|
||||
.bindings(7)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Conversion]
|
||||
conv2.methodReference.typePointer.getMetadata(
|
||||
MethodDefinitions
|
||||
) shouldEqual Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedConstructor(ctx.module, Cons("Bar", 0))
|
||||
)
|
||||
)
|
||||
conv2.sourceTypeName shouldBe an[IR.Error.Resolution]
|
||||
|
||||
val conv3 = ir
|
||||
.bindings(8)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Conversion]
|
||||
conv3.methodReference.typePointer shouldBe an[IR.Error.Resolution]
|
||||
conv3.sourceTypeName.getMetadata(MethodDefinitions) shouldEqual Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedConstructor(ctx.module, Cons("Foo", 3))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,18 @@ class ModuleThisToHereTest extends CompilerTest {
|
||||
| y = case this of
|
||||
| A -> this * here
|
||||
| z = y -> this + y
|
||||
|
|
||||
|from (other : Foo) =
|
||||
| x = this * this + this
|
||||
| y = case this of
|
||||
| A -> this * here
|
||||
| z = y -> this + y
|
||||
|
|
||||
|Foo.from (other : Foo) =
|
||||
| x = this * this + this
|
||||
| y = case this of
|
||||
| A -> this * here
|
||||
| z = y -> this + y
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"desugar this to here in module methods" in {
|
||||
@ -78,13 +90,13 @@ class ModuleThisToHereTest extends CompilerTest {
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
val children = method2.preorder
|
||||
val thisOccurences = children.collect { case n: IR.Name.This => n }
|
||||
val hereOccurences = children.collect { case n: IR.Name.Here => n }
|
||||
thisOccurences.length shouldEqual 0
|
||||
hereOccurences.length shouldEqual 7
|
||||
val thisOccurrences = children.collect { case n: IR.Name.This => n }
|
||||
val hereOccurrences = children.collect { case n: IR.Name.Here => n }
|
||||
thisOccurrences.length shouldEqual 0
|
||||
hereOccurrences.length shouldEqual 7
|
||||
}
|
||||
|
||||
"leave occurences of this and here untouched in non-module methods" in {
|
||||
"leave occurrences of this and here untouched in non-module methods" in {
|
||||
val method1 = ir
|
||||
.bindings(1)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
@ -92,11 +104,38 @@ class ModuleThisToHereTest extends CompilerTest {
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
val children = method1.preorder
|
||||
val thisOccurences = children.collect { case n: IR.Name.This => n }
|
||||
val hereOccurences = children.collect { case n: IR.Name.Here => n }
|
||||
thisOccurences.length shouldEqual 6
|
||||
hereOccurences.length shouldEqual 1
|
||||
val thisOccurrences = children.collect { case n: IR.Name.This => n }
|
||||
val hereOccurrences = children.collect { case n: IR.Name.Here => n }
|
||||
thisOccurrences.length shouldEqual 6
|
||||
hereOccurrences.length shouldEqual 1
|
||||
}
|
||||
|
||||
"desugar this to here in module conversions" in {
|
||||
val conv1 = ir
|
||||
.bindings(3)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Conversion]
|
||||
.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
val children = conv1.preorder
|
||||
val thisOccurrences = children.collect { case n: IR.Name.This => n}
|
||||
val hereOccurrences = children.collect { case n: IR.Name.Here => n}
|
||||
thisOccurrences.length shouldEqual 0
|
||||
hereOccurrences.length shouldEqual 7
|
||||
}
|
||||
|
||||
"leave occurrences of this and here untouched in non-module conversions" in {
|
||||
val conv2 = ir
|
||||
.bindings(4)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Conversion]
|
||||
.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
val children = conv2.preorder
|
||||
val thisOccurrences = children.collect { case n: IR.Name.This => n}
|
||||
val hereOccurrences = children.collect { case n: IR.Name.Here => n}
|
||||
thisOccurrences.length shouldEqual 6
|
||||
hereOccurrences.length shouldEqual 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package org.enso.compiler.test.pass.resolve
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.pass.resolve.OverloadsResolution
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
@ -79,6 +80,34 @@ class OverloadsResolutionTest extends CompilerTest {
|
||||
}
|
||||
}
|
||||
|
||||
"Conversion overload resolution" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
"allow overloads on the source type" in {
|
||||
val ir =
|
||||
"""Unit.from (value : Integer) = undefined
|
||||
|Unit.from (value : Boolean) = undefined
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 2
|
||||
ir.bindings.head shouldBe a[Method.Conversion]
|
||||
ir.bindings(1) shouldBe a[Method.Conversion]
|
||||
}
|
||||
|
||||
"raise an error if there are multiple definitions with the same source type" in {
|
||||
val ir =
|
||||
"""Unit.from (value : Integer) = undefined
|
||||
|Unit.from (value : Boolean) = undefined
|
||||
|Unit.from (value : Boolean) = undefined
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 3
|
||||
ir.bindings.head shouldBe a[Method.Conversion]
|
||||
ir.bindings(1) shouldBe a[Method.Conversion]
|
||||
ir.bindings(2) shouldBe an[IR.Error.Redefined.Conversion]
|
||||
}
|
||||
}
|
||||
|
||||
"Atom overload resolution" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
|
@ -140,7 +140,8 @@ class SuspendedArgumentsTest extends CompilerTest {
|
||||
"""
|
||||
|File.with_output_stream : Vector.Vector -> (Output_Stream -> Any ! File_Error) -> Any ! File_Error
|
||||
|File.with_output_stream open_options action = undefined
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head.asInstanceOf[Method.Explicit]
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head
|
||||
.asInstanceOf[Method.Explicit]
|
||||
|
||||
val bodyLam = ir.body.asInstanceOf[IR.Function.Lambda]
|
||||
|
||||
@ -149,6 +150,35 @@ class SuspendedArgumentsTest extends CompilerTest {
|
||||
assert(!bodyLam.arguments(1).suspended, "open_options was suspended")
|
||||
assert(!bodyLam.arguments(2).suspended, "action was suspended")
|
||||
}
|
||||
|
||||
"work for conversion methods" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""File.from : Text -> Suspended -> Any
|
||||
|File.from (value : Text) config=Nothing = undefined
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head
|
||||
.asInstanceOf[Method.Conversion]
|
||||
|
||||
val bodyLam = ir.body.asInstanceOf[IR.Function.Lambda]
|
||||
val args = bodyLam.arguments
|
||||
|
||||
args.length shouldEqual 3
|
||||
assert(!args(1).suspended, "the source argument was suspended")
|
||||
assert(args(2).suspended, "the config argument was not suspended")
|
||||
}
|
||||
|
||||
"raise an error if a conversion method marks its source argument as suspended" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""File.from (~value : Text) = undefined
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head
|
||||
|
||||
ir shouldBe an[IR.Error.Conversion]
|
||||
ir.asInstanceOf[IR.Error.Conversion]
|
||||
.reason shouldBe an[IR.Error.Conversion.SuspendedSourceArgument]
|
||||
}
|
||||
}
|
||||
|
||||
"Suspended arguments resolution in expressions" should {
|
||||
|
Loading…
Reference in New Issue
Block a user