mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 19:21:54 +03:00
Add syntax sugar for method/function defs (#765)
This commit is contained in:
parent
42efcdb8c6
commit
a2fe01d399
@ -23,5 +23,7 @@ public class MethodNames {
|
||||
|
||||
public static class Function {
|
||||
public static final String EQUALS = "equals";
|
||||
public static final String GET_SOURCE_START = "get_source_start";
|
||||
public static final String GET_SOURCE_LENGTH = "get_source_length";
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import org.enso.interpreter.runtime.state.Stateful;
|
||||
* determined by the API provided by Truffle.
|
||||
*/
|
||||
@ReportPolymorphism
|
||||
@NodeInfo(shortName = "Closure", description = "A root node for Enso closures")
|
||||
@NodeInfo(shortName = "Closure", description = "A root node for Enso closures.")
|
||||
public class ClosureRootNode extends EnsoRootNode {
|
||||
|
||||
@Child private ExpressionNode body;
|
||||
|
@ -216,9 +216,26 @@ public final class Function implements TruffleObject {
|
||||
@ExportMessage
|
||||
Object invokeMember(String member, Object... args)
|
||||
throws ArityException, UnknownIdentifierException, UnsupportedTypeException {
|
||||
if (member.equals(MethodNames.Function.EQUALS)) {
|
||||
Object that = Types.extractArguments(args, Object.class);
|
||||
return this == that;
|
||||
switch (member) {
|
||||
case MethodNames.Function.EQUALS:
|
||||
Object that = Types.extractArguments(args, Object.class);
|
||||
return this == that;
|
||||
case MethodNames.Function.GET_SOURCE_START:
|
||||
{
|
||||
SourceSection sect = getSourceSection();
|
||||
if (sect == null) {
|
||||
return null;
|
||||
}
|
||||
return sect.getCharIndex();
|
||||
}
|
||||
case MethodNames.Function.GET_SOURCE_LENGTH:
|
||||
{
|
||||
SourceSection sect = getSourceSection();
|
||||
if (sect == null) {
|
||||
return null;
|
||||
}
|
||||
return sect.getCharLength();
|
||||
}
|
||||
}
|
||||
throw UnknownIdentifierException.create(member);
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ class Compiler(
|
||||
* they nevertheless exist.
|
||||
*/
|
||||
val compilerPhaseOrdering: List[IRPass] = List(
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies,
|
||||
SectionsToBinOp,
|
||||
OperatorToFunction,
|
||||
|
@ -4,7 +4,7 @@ import cats.Foldable
|
||||
import cats.implicits._
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR._
|
||||
import org.enso.compiler.exception.UnhandledEntity
|
||||
import org.enso.compiler.exception.{CompilerError, UnhandledEntity}
|
||||
import org.enso.interpreter.Constants
|
||||
import org.enso.syntax.text.AST
|
||||
|
||||
@ -124,7 +124,7 @@ object AstToIr {
|
||||
getIdentifiedLocation(inputAST)
|
||||
)
|
||||
}
|
||||
case AstView.MethodDefinition(targetPath, name, definition) =>
|
||||
case AstView.MethodDefinition(targetPath, name, args, definition) =>
|
||||
val (path, pathLoc) = if (targetPath.nonEmpty) {
|
||||
val pathSegments = targetPath.collect {
|
||||
case AST.Ident.Cons.any(c) => c
|
||||
@ -147,12 +147,15 @@ object AstToIr {
|
||||
}
|
||||
|
||||
val nameStr = name match { case AST.Ident.Var.any(name) => name }
|
||||
Module.Scope.Definition.Method(
|
||||
|
||||
Module.Scope.Definition.Method.Binding(
|
||||
Name.Literal(path, pathLoc.map(IdentifiedLocation(_))),
|
||||
Name.Literal(nameStr.name, getIdentifiedLocation(nameStr)),
|
||||
args.map(translateArgumentDefinition(_)),
|
||||
translateExpression(definition),
|
||||
getIdentifiedLocation(inputAST)
|
||||
)
|
||||
|
||||
case _ =>
|
||||
throw new UnhandledEntity(inputAST, "translateModuleSymbol")
|
||||
}
|
||||
@ -169,6 +172,9 @@ object AstToIr {
|
||||
.getOrElse(maybeParensedInput)
|
||||
|
||||
inputAST match {
|
||||
case AST.Def(consName, _, _) =>
|
||||
IR.Error
|
||||
.Syntax(inputAST, IR.Error.Syntax.TypeDefinedInline(consName.name))
|
||||
case AstView.UnaryMinus(expression) =>
|
||||
expression match {
|
||||
case AST.Literal.Number(base, number) =>
|
||||
@ -191,6 +197,13 @@ object AstToIr {
|
||||
getIdentifiedLocation(inputAST)
|
||||
)
|
||||
}
|
||||
case AstView.FunctionSugar(name, args, body) =>
|
||||
Function.Binding(
|
||||
translateIdent(name).asInstanceOf[IR.Name.Literal],
|
||||
args.map(translateArgumentDefinition(_)),
|
||||
translateExpression(body),
|
||||
getIdentifiedLocation(inputAST)
|
||||
)
|
||||
case AstView
|
||||
.SuspendedBlock(name, block @ AstView.Block(lines, lastLine)) =>
|
||||
Expression.Binding(
|
||||
@ -205,6 +218,11 @@ object AstToIr {
|
||||
)
|
||||
case AstView.Assignment(name, expr) =>
|
||||
translateBinding(getIdentifiedLocation(inputAST), name, expr)
|
||||
case AstView.MethodDefinition(_, name, _, _) =>
|
||||
IR.Error.Syntax(
|
||||
inputAST,
|
||||
IR.Error.Syntax.MethodDefinedInline(name.asInstanceOf[AST.Ident].name)
|
||||
)
|
||||
case AstView.MethodCall(target, name, args) =>
|
||||
val (validArguments, hasDefaultsSuspended) =
|
||||
calculateDefaultsSuspension(args)
|
||||
|
@ -142,8 +142,8 @@ object AstView {
|
||||
|
||||
if (op == lambdaOpSym) {
|
||||
left match {
|
||||
case LambdaParamList(args) => Some((args, right))
|
||||
case _ => None
|
||||
case FunctionParamList(args) => Some((args, right))
|
||||
case _ => None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@ -209,7 +209,7 @@ object AstView {
|
||||
}
|
||||
}
|
||||
|
||||
object LambdaParamList {
|
||||
object FunctionParamList {
|
||||
|
||||
/** Matches on the parameter list of a lambda.
|
||||
*
|
||||
@ -249,6 +249,37 @@ object AstView {
|
||||
}
|
||||
}
|
||||
|
||||
object FunctionSugar {
|
||||
|
||||
/** Matches on terms that represent long-form function definitions.
|
||||
*
|
||||
* @param ast the structure to try and match on
|
||||
* @return the name of the function, the list of function arguments, and
|
||||
* the function body
|
||||
*/
|
||||
def unapply(ast: AST): Option[(AST.Ident, List[AST], AST)] = {
|
||||
ast match {
|
||||
case AstView.Binding(left, body) =>
|
||||
left match {
|
||||
case FunctionParamList(items) =>
|
||||
if (items.length > 1) {
|
||||
val fnName = items.head
|
||||
val args = items.tail
|
||||
|
||||
fnName match {
|
||||
case AST.Ident.Var.any(name) => Some((name, args, body))
|
||||
case _ => None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MaybeParensed {
|
||||
|
||||
/** Matches on terms that _may_ be surrounded by parentheses.
|
||||
@ -428,16 +459,18 @@ object AstView {
|
||||
* arbitrary program expression.
|
||||
*
|
||||
* @param ast the structure to try and match on
|
||||
* @return the path segments of the type reference, the function name, and
|
||||
* the bound expression
|
||||
* @return the path segments of the type reference, the function name, the
|
||||
* arguments to the method, and the bound expression
|
||||
*/
|
||||
def unapply(ast: AST): Option[(List[AST], AST, AST)] = {
|
||||
def unapply(ast: AST): Option[(List[AST], AST, List[AST], AST)] = {
|
||||
ast match {
|
||||
case Binding(lhs, rhs) =>
|
||||
lhs match {
|
||||
case MethodReference(targetPath, name) =>
|
||||
Some((targetPath, name, rhs))
|
||||
case AST.Ident.Var.any(name) => Some((List(), name, rhs))
|
||||
Some((targetPath, name, List(), rhs))
|
||||
case MethodBindingLHS(path, methodName, args) =>
|
||||
Some((path, methodName, args, rhs))
|
||||
case AST.Ident.Var.any(name) => Some((List(), name, List(), rhs))
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
@ -447,6 +480,28 @@ object AstView {
|
||||
}
|
||||
}
|
||||
|
||||
object MethodBindingLHS {
|
||||
|
||||
/** Matches on the left hand side of a sugared method definition.
|
||||
*
|
||||
* @param ast the structure to try and match on
|
||||
* @return the path segments of the type reference, the function name, and
|
||||
* the arguments
|
||||
*/
|
||||
def unapply(ast: AST): Option[(List[AST], AST, List[AST])] = {
|
||||
ast match {
|
||||
case SpacedList(MethodReference(path, methodName) :: args) =>
|
||||
val validArgs = args.forall {
|
||||
case AstView.FunctionParam(_) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
if (validArgs) Some((path, methodName, args)) else None
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ConsOrVar {
|
||||
|
||||
/** Matches any expression that is either the name of a constructor or a
|
||||
|
@ -216,15 +216,12 @@ class IRToTruffle(
|
||||
dataflowInfo
|
||||
)
|
||||
|
||||
val methodFunIsTail = methodDef.body
|
||||
.unsafeGetMetadata(TailCall, "Method body missing tail call info.")
|
||||
|
||||
val funNode = methodDef.body match {
|
||||
case fn: IR.Function =>
|
||||
expressionProcessor.processFunctionBody(
|
||||
fn.arguments,
|
||||
fn.body,
|
||||
fn.location,
|
||||
methodDef.location,
|
||||
Some(methodDef.methodName.name)
|
||||
)
|
||||
case _ =>
|
||||
@ -233,8 +230,6 @@ class IRToTruffle(
|
||||
)
|
||||
}
|
||||
|
||||
funNode.setTail(methodFunIsTail)
|
||||
|
||||
val function = new RuntimeFunction(
|
||||
funNode.getCallTarget,
|
||||
null,
|
||||
@ -606,21 +601,15 @@ class IRToTruffle(
|
||||
case Error.InvalidIR(_, _, _) =>
|
||||
throw new CompilerError("Unexpected Invalid IR during codegen.")
|
||||
case err: Error.Syntax =>
|
||||
context.getBuiltins
|
||||
.syntaxError()
|
||||
.newInstance(err.message)
|
||||
context.getBuiltins.syntaxError().newInstance(err.message)
|
||||
case err: Error.Redefined.Binding =>
|
||||
context.getBuiltins
|
||||
.compileError()
|
||||
.newInstance(err.message)
|
||||
context.getBuiltins.compileError().newInstance(err.message)
|
||||
case err: Error.Redefined.Method =>
|
||||
context.getBuiltins
|
||||
.compileError()
|
||||
.newInstance(err.message)
|
||||
context.getBuiltins.compileError().newInstance(err.message)
|
||||
case err: Error.Redefined.Atom =>
|
||||
context.getBuiltins
|
||||
.compileError()
|
||||
.newInstance(err.message)
|
||||
context.getBuiltins.compileError().newInstance(err.message)
|
||||
case err: Error.Redefined.ThisArg =>
|
||||
context.getBuiltins.compileError().newInstance(err.message)
|
||||
}
|
||||
setLocation(ErrorNode.build(payload), error.location)
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -46,6 +46,7 @@ import scala.reflect.ClassTag
|
||||
*
|
||||
* It must have the following passes run before it:
|
||||
*
|
||||
* - [[org.enso.compiler.pass.desugar.FunctionBinding]]
|
||||
* - [[org.enso.compiler.pass.desugar.GenerateMethodBodies]]
|
||||
* - [[org.enso.compiler.pass.desugar.SectionsToBinOp]]
|
||||
* - [[org.enso.compiler.pass.desugar.OperatorToFunction]]
|
||||
@ -129,7 +130,8 @@ case object AliasAnalysis extends IRPass {
|
||||
val topLevelGraph = new Graph
|
||||
|
||||
ir match {
|
||||
case m @ IR.Module.Scope.Definition.Method(_, _, body, _, _, _) =>
|
||||
case m @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, _, body, _, _, _) =>
|
||||
body match {
|
||||
case _: IR.Function =>
|
||||
m.copy(
|
||||
@ -147,6 +149,10 @@ case object AliasAnalysis extends IRPass {
|
||||
"The body of a method should always be a function."
|
||||
)
|
||||
}
|
||||
case _: IR.Module.Scope.Definition.Method.Binding =>
|
||||
throw new CompilerError(
|
||||
"Method definition sugar should not occur during alias analysis."
|
||||
)
|
||||
case a @ IR.Module.Scope.Definition.Atom(_, args, _, _, _) =>
|
||||
a.copy(
|
||||
arguments =
|
||||
@ -419,6 +425,10 @@ case object AliasAnalysis extends IRPass {
|
||||
)
|
||||
)
|
||||
.updateMetadata(this -->> Info.Scope.Child(graph, currentScope))
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
"Function sugar should not be present during alias analysis."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import scala.collection.mutable
|
||||
*
|
||||
* It must have the following passes run before it:
|
||||
*
|
||||
* - [[org.enso.compiler.pass.desugar.FunctionBinding]]
|
||||
* - [[AliasAnalysis]]
|
||||
* - [[DemandAnalysis]]
|
||||
* - [[TailCall]]
|
||||
@ -97,7 +98,8 @@ case object DataflowAnalysis extends IRPass {
|
||||
arguments = arguments.map(analyseDefinitionArgument(_, info))
|
||||
)
|
||||
.updateMetadata(this -->> info)
|
||||
case method @ IR.Module.Scope.Definition.Method(_, _, body, _, _, _) =>
|
||||
case method @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, _, body, _, _, _) =>
|
||||
info.updateAt(asStatic(body), Set(asStatic(method)))
|
||||
|
||||
method
|
||||
@ -105,6 +107,11 @@ case object DataflowAnalysis extends IRPass {
|
||||
body = analyseExpression(body, info)
|
||||
)
|
||||
.updateMetadata(this -->> info)
|
||||
case _: IR.Module.Scope.Definition.Method.Binding =>
|
||||
throw new CompilerError(
|
||||
"Sugared method definitions should not occur during dataflow " +
|
||||
"analysis."
|
||||
)
|
||||
case err: IR.Error.Redefined => err
|
||||
}
|
||||
}
|
||||
@ -187,6 +194,10 @@ case object DataflowAnalysis extends IRPass {
|
||||
body = analyseExpression(body, info)
|
||||
)
|
||||
.updateMetadata(this -->> info)
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
"Function sugar should not be present during alias analysis."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,10 @@ case object DemandAnalysis extends IRPass {
|
||||
isInsideCallArgument = false
|
||||
)
|
||||
)
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
"Function sugar should not be present during demand analysis."
|
||||
)
|
||||
}
|
||||
|
||||
/** Performs demand analysis for a name.
|
||||
|
@ -18,6 +18,7 @@ import org.enso.compiler.pass.IRPass
|
||||
*
|
||||
* It must have the following passes run before it:
|
||||
*
|
||||
* - [[org.enso.compiler.pass.desugar.FunctionBinding]]
|
||||
* - [[org.enso.compiler.pass.desugar.GenerateMethodBodies]]
|
||||
* - [[org.enso.compiler.pass.desugar.SectionsToBinOp]]
|
||||
* - [[org.enso.compiler.pass.desugar.OperatorToFunction]]
|
||||
@ -76,12 +77,18 @@ case object TailCall extends IRPass {
|
||||
definition: IR.Module.Scope.Definition
|
||||
): IR.Module.Scope.Definition = {
|
||||
definition match {
|
||||
case method @ IR.Module.Scope.Definition.Method(_, _, body, _, _, _) =>
|
||||
case method @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, _, body, _, _, _) =>
|
||||
method
|
||||
.copy(
|
||||
body = analyseExpression(body, isInTailPosition = true)
|
||||
)
|
||||
.updateMetadata(this -->> TailPosition.Tail)
|
||||
case _: IR.Module.Scope.Definition.Method.Binding =>
|
||||
throw new CompilerError(
|
||||
"Sugared method definitions should not occur during tail call " +
|
||||
"analysis."
|
||||
)
|
||||
case atom @ IR.Module.Scope.Definition.Atom(_, args, _, _, _) =>
|
||||
atom
|
||||
.copy(
|
||||
@ -341,6 +348,10 @@ case object TailCall extends IRPass {
|
||||
arguments = args.map(analyseDefArgument),
|
||||
body = analyseExpression(body, isInTailPosition = markAsTail)
|
||||
)
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
"Function sugar should not be present during tail call analysis."
|
||||
)
|
||||
}
|
||||
|
||||
resultFunction.updateMetadata(
|
||||
|
@ -0,0 +1,112 @@
|
||||
package org.enso.compiler.pass.desugar
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Error.Redefined
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
/** This pass handles the desugaring of long-form function and method
|
||||
* definitions into standard bindings using lambdas.
|
||||
*
|
||||
* This works for any definition of the form `f <args> = <body>`.
|
||||
*
|
||||
* This pass has no configuration.
|
||||
*
|
||||
* This pass requires the context to provide:
|
||||
*
|
||||
* - Nothing
|
||||
*
|
||||
* It must have the following passes run before it:
|
||||
*
|
||||
* - None
|
||||
*/
|
||||
//noinspection DuplicatedCode
|
||||
case object FunctionBinding extends IRPass {
|
||||
override type Metadata = IRPass.Metadata.Empty
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
/** Rusn desugaring of sugared method and function bindings on a module.
|
||||
*
|
||||
* @param ir the Enso IR to process
|
||||
* @param moduleContext a context object that contains the information needed
|
||||
* to process a module
|
||||
* @return `ir`, possibly having made transformations or annotations to that
|
||||
* IR.
|
||||
*/
|
||||
override def runModule(
|
||||
ir: IR.Module,
|
||||
@unused moduleContext: ModuleContext
|
||||
): IR.Module = ir.copy(bindings = ir.bindings.map(desugarModuleSymbol))
|
||||
|
||||
/** Runs desugaring of function bindings on an arbitrary expression.
|
||||
*
|
||||
* @param ir the Enso IR to process
|
||||
* @param inlineContext a context object that contains the information needed
|
||||
* for inline evaluation
|
||||
* @return `ir`, possibly having made transformations or annotations to that
|
||||
* IR.
|
||||
*/
|
||||
override def runExpression(
|
||||
ir: IR.Expression,
|
||||
@unused inlineContext: InlineContext
|
||||
): IR.Expression = desugarExpression(ir)
|
||||
|
||||
// === Pass Internals =======================================================
|
||||
|
||||
/** Performs desugaring on an arbitrary Enso expression.
|
||||
*
|
||||
* @param ir the expression to desugar
|
||||
* @return `ir`, with any function definition sugar removed
|
||||
*/
|
||||
def desugarExpression(ir: IR.Expression): IR.Expression = {
|
||||
ir.transformExpressions {
|
||||
case IR.Function.Binding(name, args, body, location, canBeTCO, _, _) =>
|
||||
if (args.isEmpty) {
|
||||
throw new CompilerError("The arguments list should not be empty.")
|
||||
}
|
||||
|
||||
val lambda = args
|
||||
.map(_.mapExpressions(desugarExpression))
|
||||
.foldRight(desugarExpression(body))((arg, body) =>
|
||||
IR.Function.Lambda(List(arg), body, None)
|
||||
)
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.copy(canBeTCO = canBeTCO, location = location)
|
||||
|
||||
IR.Expression.Binding(name, lambda, location)
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs desugaring on a module definition.
|
||||
*
|
||||
* @param definition the module definition to desugar
|
||||
* @return `definition`, with any function definition sugar removed
|
||||
*/
|
||||
def desugarModuleSymbol(
|
||||
definition: IR.Module.Scope.Definition
|
||||
): IR.Module.Scope.Definition = {
|
||||
definition match {
|
||||
case a @ Definition.Atom(_, arguments, _, _, _) =>
|
||||
a.copy(arguments = arguments.map(_.mapExpressions(desugarExpression)))
|
||||
case _: Method.Explicit =>
|
||||
throw new CompilerError(
|
||||
"Explicit method definitions should not exist during function " +
|
||||
"binding desugaring."
|
||||
)
|
||||
case Method.Binding(typeName, methName, args, body, loc, _, _) =>
|
||||
val newBody = args
|
||||
.map(_.mapExpressions(desugarExpression))
|
||||
.foldRight(desugarExpression(body))((arg, body) =>
|
||||
IR.Function.Lambda(List(arg), body, None)
|
||||
)
|
||||
|
||||
Method.Explicit(typeName, methName, newBody, loc)
|
||||
case e: Redefined => e
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ package org.enso.compiler.pass.desugar
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Error.Redefined
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
|
||||
/** This pass is responsible for ensuring that method bodies are in the correct
|
||||
@ -19,7 +21,7 @@ import org.enso.compiler.pass.IRPass
|
||||
*
|
||||
* It must have the following passes run before it:
|
||||
*
|
||||
* - None
|
||||
* - [[FunctionBinding]]
|
||||
*/
|
||||
case object GenerateMethodBodies extends IRPass {
|
||||
|
||||
@ -57,12 +59,20 @@ case object GenerateMethodBodies extends IRPass {
|
||||
def processMethodDef(
|
||||
ir: IR.Module.Scope.Definition.Method
|
||||
): IR.Module.Scope.Definition.Method = {
|
||||
ir.copy(
|
||||
body = ir.body match {
|
||||
case fun: IR.Function => processBodyFunction(fun)
|
||||
case expression => processBodyExpression(expression)
|
||||
}
|
||||
)
|
||||
ir match {
|
||||
case ir: IR.Module.Scope.Definition.Method.Explicit =>
|
||||
ir.copy(
|
||||
body = ir.body match {
|
||||
case fun: IR.Function => processBodyFunction(fun)
|
||||
case expression => processBodyExpression(expression)
|
||||
}
|
||||
)
|
||||
case _: IR.Module.Scope.Definition.Method.Binding =>
|
||||
throw new CompilerError(
|
||||
"Method definition sugar should not be present during method body " +
|
||||
"generation."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Processes the method body if it's a function.
|
||||
@ -73,12 +83,25 @@ case object GenerateMethodBodies extends IRPass {
|
||||
* @param fun the body function
|
||||
* @return the body function with the `this` argument
|
||||
*/
|
||||
def processBodyFunction(fun: IR.Function): IR.Function = {
|
||||
fun match {
|
||||
case lam @ IR.Function.Lambda(args, _, _, _, _, _) =>
|
||||
lam.copy(
|
||||
arguments = genThisArgument :: args
|
||||
)
|
||||
def processBodyFunction(fun: IR.Function): IR.Expression = {
|
||||
val containsThis = collectChainedFunctionArgs(fun).exists(arg =>
|
||||
arg.name == IR.Name.This(arg.name.location)
|
||||
)
|
||||
|
||||
if (!containsThis) {
|
||||
fun match {
|
||||
case lam @ IR.Function.Lambda(args, _, _, _, _, _) =>
|
||||
lam.copy(
|
||||
arguments = genThisArgument :: args
|
||||
)
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
"Function definition sugar should not be present during method " +
|
||||
"body generation."
|
||||
)
|
||||
}
|
||||
} else {
|
||||
IR.Error.Redefined.ThisArg(fun.location)
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,4 +146,20 @@ case object GenerateMethodBodies extends IRPass {
|
||||
ir: IR.Expression,
|
||||
inlineContext: InlineContext
|
||||
): IR.Expression = ir
|
||||
|
||||
/** Collects the argument list of a chain of function definitions.
|
||||
*
|
||||
* @param function the function to collect args for
|
||||
* @return the list of arguments for `function`
|
||||
*/
|
||||
def collectChainedFunctionArgs(
|
||||
function: IR.Function
|
||||
): List[IR.DefinitionArgument] = {
|
||||
val bodyArgs = function.body match {
|
||||
case f: IR.Function => (collectChainedFunctionArgs(f))
|
||||
case _ => List()
|
||||
}
|
||||
|
||||
function.arguments ::: bodyArgs
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.enso.compiler.pass.lint
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis
|
||||
import org.enso.compiler.pass.resolve.IgnoredBindings
|
||||
@ -114,6 +115,10 @@ case object UnusedBindings extends IRPass {
|
||||
arguments = args.map(lintFunctionArgument(_, context)),
|
||||
body = runExpression(body, context)
|
||||
)
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
"Function sugar should not be present during unused bindings linting."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,6 +158,10 @@ case object LambdaConsolidate extends IRPass {
|
||||
canBeTCO = chainedLambdas.last.canBeTCO,
|
||||
passData = MetadataStorage()
|
||||
)
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
"Function sugar should not be present during lambda consolidation."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,11 @@ case object IgnoredBindings extends IRPass {
|
||||
arguments = newArgs,
|
||||
body = desugarExpression(body, supply)
|
||||
)
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
"Function sugar should not be present during ignored " +
|
||||
"bindings desugaring."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ trait CompilerRunner {
|
||||
*/
|
||||
def asMethod: IR.Module.Scope.Definition.Method = {
|
||||
IR.Module.Scope.Definition
|
||||
.Method(
|
||||
.Method.Explicit(
|
||||
IR.Name.Literal("TestType", None),
|
||||
IR.Name.Literal("testMethod", None),
|
||||
ir,
|
||||
|
@ -239,4 +239,93 @@ class AstToIrTest extends CompilerTest {
|
||||
fooArg.value.asInstanceOf[IR.Name.Literal].name shouldEqual "foo"
|
||||
}
|
||||
}
|
||||
|
||||
"AST translation of function sugar" should {
|
||||
"work for function definitions" in {
|
||||
val ir =
|
||||
"""
|
||||
|f a b = a + b
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Function.Binding]
|
||||
}
|
||||
|
||||
"work for method definitions" in {
|
||||
val ir =
|
||||
"""
|
||||
|Foo.bar a b = a + b
|
||||
|""".stripMargin.toIrModule
|
||||
|
||||
ir.bindings.head shouldBe an[IR.Module.Scope.Definition.Method.Binding]
|
||||
}
|
||||
|
||||
"work for method definitions with involved arguments" in {
|
||||
val ir =
|
||||
"""
|
||||
|Foo.bar _ (b = 1) ~c = b + c
|
||||
|""".stripMargin.toIrModule
|
||||
|
||||
ir.bindings.head shouldBe an[IR.Module.Scope.Definition.Method.Binding]
|
||||
}
|
||||
|
||||
"not recognise pattern match bindings" in {
|
||||
val ir =
|
||||
"""
|
||||
|F a b = a + b
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir should not be an[IR.Function.Binding]
|
||||
}
|
||||
|
||||
"work with ignored arguments" in {
|
||||
val ir =
|
||||
"""
|
||||
|f _ b = a + b
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Function.Binding]
|
||||
}
|
||||
|
||||
"work with defaulted arguments" in {
|
||||
val ir =
|
||||
"""
|
||||
|f (a = 1) b = a + b
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Function.Binding]
|
||||
}
|
||||
|
||||
"work with lazy arguments" in {
|
||||
val ir =
|
||||
"""
|
||||
|f ~a b = a + b
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Function.Binding]
|
||||
}
|
||||
}
|
||||
|
||||
"AST translation for the inline flow" should {
|
||||
"disallow method definitions without exploding" in {
|
||||
val ir =
|
||||
"""
|
||||
|Unit.foo a b = a + b
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Error.Syntax]
|
||||
ir.asInstanceOf[IR.Error.Syntax]
|
||||
.reason shouldBe an[IR.Error.Syntax.MethodDefinedInline]
|
||||
}
|
||||
|
||||
"disallow type definitions without explocing" in {
|
||||
val ir =
|
||||
"""
|
||||
|type MyAtom a b
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Error.Syntax]
|
||||
ir.asInstanceOf[IR.Error.Syntax]
|
||||
.reason shouldBe an[IR.Error.Syntax.TypeDefinedInline]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,7 @@ import org.enso.compiler.pass.PassConfiguration._
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{Link, Occurrence}
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis.{Graph, Info}
|
||||
import org.enso.compiler.pass.desugar.{
|
||||
GenerateMethodBodies,
|
||||
LambdaShorthandToLambda,
|
||||
OperatorToFunction,
|
||||
SectionsToBinOp
|
||||
}
|
||||
import org.enso.compiler.pass.desugar._
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
@ -22,6 +17,7 @@ class AliasAnalysisTest extends CompilerTest {
|
||||
|
||||
/** The passes that need to be run before the alias analysis pass. */
|
||||
val precursorPasses: List[IRPass] = List(
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies,
|
||||
SectionsToBinOp,
|
||||
OperatorToFunction,
|
||||
|
@ -5,13 +5,8 @@ import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.PassConfiguration._
|
||||
import org.enso.compiler.pass.analyse.DataflowAnalysis.DependencyInfo
|
||||
import org.enso.compiler.pass.analyse.DataflowAnalysis.DependencyInfo.Type.asStatic
|
||||
import org.enso.compiler.pass.analyse.{
|
||||
AliasAnalysis,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
TailCall
|
||||
}
|
||||
import org.enso.compiler.pass.desugar.{GenerateMethodBodies, OperatorToFunction}
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, DataflowAnalysis, DemandAnalysis, TailCall}
|
||||
import org.enso.compiler.pass.desugar.{FunctionBinding, GenerateMethodBodies, OperatorToFunction}
|
||||
import org.enso.compiler.pass.optimise.LambdaConsolidate
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
@ -25,6 +20,7 @@ class DataflowAnalysisTest extends CompilerTest {
|
||||
|
||||
/** The passes that must be run before the dataflow analysis pass. */
|
||||
val precursorPasses: List[IRPass] = List(
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies,
|
||||
OperatorToFunction,
|
||||
AliasAnalysis,
|
||||
|
@ -72,8 +72,10 @@ class GatherDiagnosticsTest extends CompilerTest {
|
||||
),
|
||||
None
|
||||
),
|
||||
IR.Module.Scope.Definition.Method(typeName, method1Name, lam, None),
|
||||
IR.Module.Scope.Definition.Method(typeName, method2Name, error3, None)
|
||||
IR.Module.Scope.Definition.Method
|
||||
.Explicit(typeName, method1Name, lam, None),
|
||||
IR.Module.Scope.Definition.Method
|
||||
.Explicit(typeName, method2Name, error3, None)
|
||||
),
|
||||
None
|
||||
)
|
||||
|
@ -6,10 +6,7 @@ import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.analyse.TailCall.TailPosition
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, TailCall}
|
||||
import org.enso.compiler.pass.desugar.{
|
||||
GenerateMethodBodies,
|
||||
OperatorToFunction
|
||||
}
|
||||
import org.enso.compiler.pass.desugar.{FunctionBinding, GenerateMethodBodies, OperatorToFunction}
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.interpreter.runtime.scope.LocalScope
|
||||
@ -32,6 +29,7 @@ class TailCallTest extends CompilerTest {
|
||||
)
|
||||
|
||||
val precursorPasses: List[IRPass] = List(
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies,
|
||||
OperatorToFunction,
|
||||
AliasAnalysis
|
||||
|
@ -0,0 +1,196 @@
|
||||
package org.enso.compiler.test.pass.desugar
|
||||
|
||||
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.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class FunctionBindingTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
val precursorPasses: List[IRPass] = List()
|
||||
val passConfig: PassConfiguration = PassConfiguration()
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
new PassManager(precursorPasses, passConfig)
|
||||
|
||||
/** Adds an extension method to run method and function desugaring on an
|
||||
* [[IR.Module]].
|
||||
*
|
||||
* @param ir the module to run desugaring on
|
||||
*/
|
||||
implicit class DesugarModule(ir: IR.Module) {
|
||||
|
||||
/** Runs desugaring on a module.
|
||||
*
|
||||
* @param moduleContext the module context in which desugaring is taking
|
||||
* place
|
||||
* @return [[ir]], with any sugared function and method definitions
|
||||
* desugared
|
||||
*/
|
||||
def desugar(implicit moduleContext: ModuleContext): IR.Module = {
|
||||
FunctionBinding.runModule(ir, moduleContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds an extension method to run function desugaring on an arbitrary
|
||||
* expression.
|
||||
*
|
||||
* @param ir the expression to desugar
|
||||
*/
|
||||
implicit class DesugarExpression(ir: IR.Expression) {
|
||||
|
||||
/** Runs desgaring on an expression.
|
||||
*
|
||||
* @param inlineContext the inline context in which the desugaring is
|
||||
* taking place
|
||||
* @return [[ir]], with any sugared function definitions desugared
|
||||
*/
|
||||
def desugar(implicit inlineContext: InlineContext): IR.Expression = {
|
||||
FunctionBinding.runExpression(ir, inlineContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a defaulted module context.
|
||||
*
|
||||
* @return a defaulted module context
|
||||
*/
|
||||
def mkModuleContext: ModuleContext = {
|
||||
ModuleContext()
|
||||
}
|
||||
|
||||
/** Creates a defaulted inline context.
|
||||
*
|
||||
* @return a defaulted inline context
|
||||
*/
|
||||
def mkInlineContext: InlineContext = {
|
||||
InlineContext()
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Sugared method definitions" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|Unit.foo ~a _ (c = 1) = a + c
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
"desugar to standard method definitions" in {
|
||||
ir.bindings.head shouldBe an[IR.Module.Scope.Definition.Method.Explicit]
|
||||
}
|
||||
|
||||
val explicitMethod =
|
||||
ir.bindings.head.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
|
||||
"have the function arguments in the body functions" in {
|
||||
val lambda1 = explicitMethod.body.asInstanceOf[IR.Function.Lambda]
|
||||
val lambda2 = lambda1.body.asInstanceOf[IR.Function.Lambda]
|
||||
val lambda3 = lambda2.body.asInstanceOf[IR.Function.Lambda]
|
||||
val cArg =
|
||||
lambda3.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
|
||||
lambda1.arguments.head
|
||||
.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
.suspended shouldEqual true
|
||||
lambda1.arguments.head.name.name shouldEqual "a"
|
||||
lambda2.arguments.head
|
||||
.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
.name shouldBe an[IR.Name.Blank]
|
||||
cArg.name.name shouldEqual "c"
|
||||
cArg.defaultValue shouldBe defined
|
||||
}
|
||||
|
||||
"desugar nested sugared functions" in {
|
||||
val ir =
|
||||
"""
|
||||
|Foo.bar a =
|
||||
| f b = b
|
||||
| f 1
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
val body = ir.bindings.head
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
body.expressions.head shouldBe an[IR.Expression.Binding]
|
||||
val binding = body.expressions.head.asInstanceOf[IR.Expression.Binding]
|
||||
|
||||
binding.expression shouldBe an[IR.Function.Lambda]
|
||||
}
|
||||
}
|
||||
|
||||
"Sugared function definitions" should {
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|f ~a _ (c = 1) = a + b * c
|
||||
|""".stripMargin.preprocessExpression.get.desugar
|
||||
|
||||
"desugar to a binding with a lambda" in {
|
||||
ir shouldBe an[IR.Expression.Binding]
|
||||
val binding = ir.asInstanceOf[IR.Expression.Binding]
|
||||
|
||||
binding.name.name shouldEqual "f"
|
||||
binding.expression shouldBe an[IR.Function.Lambda]
|
||||
}
|
||||
|
||||
"work properly for complex argument definition types" in {
|
||||
val lambda1 = ir
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
.expression
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
val lambda2 = lambda1.body.asInstanceOf[IR.Function.Lambda]
|
||||
val lambda3 = lambda2.body.asInstanceOf[IR.Function.Lambda]
|
||||
val cArg =
|
||||
lambda3.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
|
||||
lambda1.arguments.head
|
||||
.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
.suspended shouldEqual true
|
||||
lambda2.arguments.head
|
||||
.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
.name shouldBe an[IR.Name.Blank]
|
||||
cArg.name.name shouldEqual "c"
|
||||
cArg.defaultValue shouldBe defined
|
||||
}
|
||||
|
||||
"work recursively" in {
|
||||
val ir =
|
||||
"""
|
||||
|f (a = (f a = a)) =
|
||||
| g b = b
|
||||
| g 1
|
||||
|""".stripMargin.preprocessExpression.get.desugar
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
|
||||
val aArg = ir.expression
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.arguments
|
||||
.head
|
||||
.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
aArg.name.name shouldEqual "a"
|
||||
aArg.defaultValue.get
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
.name
|
||||
.name shouldEqual "f"
|
||||
|
||||
val body = ir.expression
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
body.expressions.head shouldBe an[IR.Expression.Binding]
|
||||
|
||||
val gBinding = body.expressions.head.asInstanceOf[IR.Expression.Binding]
|
||||
gBinding.name.name shouldEqual "g"
|
||||
gBinding.expression shouldBe an[IR.Function.Lambda]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,41 @@
|
||||
package org.enso.compiler.test.pass.desugar
|
||||
|
||||
import org.enso.compiler.context.ModuleContext
|
||||
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.pass.desugar.GenerateMethodBodies
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.pass.desugar.{FunctionBinding, GenerateMethodBodies}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class GenerateMethodBodiesTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
val ctx = ModuleContext()
|
||||
|
||||
implicit val ctx: ModuleContext = ModuleContext()
|
||||
|
||||
val precursorPasses: List[IRPass] = List(FunctionBinding)
|
||||
val passConfig: PassConfiguration = PassConfiguration()
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
new PassManager(precursorPasses, passConfig)
|
||||
|
||||
/** Adds an extension method to run method and method body generation on an
|
||||
* [[IR.Module]].
|
||||
*
|
||||
* @param ir the module to run desugaring on
|
||||
*/
|
||||
implicit class DesugarModule(ir: IR.Module) {
|
||||
|
||||
/** Runs desugaring on a module.
|
||||
*
|
||||
* @param moduleContext the module context in which desugaring is taking
|
||||
* place
|
||||
* @return [[ir]], with any method bodies desugared
|
||||
*/
|
||||
def desugar(implicit moduleContext: ModuleContext): IR.Module = {
|
||||
GenerateMethodBodies.runModule(ir, moduleContext)
|
||||
}
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
@ -17,10 +43,10 @@ class GenerateMethodBodiesTest extends CompilerTest {
|
||||
val ir =
|
||||
"""
|
||||
|Unit.method = a -> b -> c -> a + b + c
|
||||
|""".stripMargin.toIrModule
|
||||
|""".stripMargin.preprocessModule
|
||||
val irMethod = ir.bindings.head.asInstanceOf[Method]
|
||||
|
||||
val irResult = GenerateMethodBodies.runModule(ir, ctx)
|
||||
val irResult = ir.desugar
|
||||
val irResultMethod = irResult.bindings.head.asInstanceOf[Method]
|
||||
|
||||
"have the `this` argument prepended to the argument list" in {
|
||||
@ -48,10 +74,10 @@ class GenerateMethodBodiesTest extends CompilerTest {
|
||||
val ir =
|
||||
"""
|
||||
|Unit.method = 1
|
||||
|""".stripMargin.toIrModule
|
||||
|""".stripMargin.preprocessModule
|
||||
val irMethod = ir.bindings.head.asInstanceOf[Method]
|
||||
|
||||
val irResult = GenerateMethodBodies.runModule(ir, ctx)
|
||||
val irResult = ir.desugar
|
||||
val irResultMethod = irResult.bindings.head.asInstanceOf[Method]
|
||||
|
||||
"have the expression converted into a function" in {
|
||||
@ -78,4 +104,18 @@ class GenerateMethodBodiesTest extends CompilerTest {
|
||||
irMethod.body.location shouldEqual irResultMethod.body.location
|
||||
}
|
||||
}
|
||||
|
||||
"Methods that redefine `this`" should {
|
||||
val ir =
|
||||
"""
|
||||
|Unit.method = this -> this + 1
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
val method =
|
||||
ir.bindings.head.asInstanceOf[IR.Module.Scope.Definition.Method]
|
||||
|
||||
"have their bodies replaced by an error" in {
|
||||
method.body shouldBe an[IR.Error.Redefined.ThisArg]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
package org.enso.compiler.test.pass.resolve
|
||||
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
import org.enso.compiler.context.ModuleContext
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.desugar.GenerateMethodBodies
|
||||
import org.enso.compiler.pass.desugar.{FunctionBinding, GenerateMethodBodies}
|
||||
import org.enso.compiler.pass.resolve.OverloadsResolution
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
class OverloadsResolutionTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
val passes: List[IRPass] = List(
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies
|
||||
)
|
||||
|
||||
@ -52,15 +51,15 @@ class OverloadsResolutionTest extends CompilerTest {
|
||||
"Method overload resolution" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val atomName = "Unit"
|
||||
val atomName = "Unit"
|
||||
val methodName = "foo"
|
||||
|
||||
val ir =
|
||||
s"""
|
||||
|$atomName.$methodName = x -> x
|
||||
|$atomName.$methodName = x -> y -> x + y
|
||||
|$atomName.$methodName = 10
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|$atomName.$methodName = x -> x
|
||||
|$atomName.$methodName = x -> y -> x + y
|
||||
|$atomName.$methodName = 10
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
"detect overloads within a given module" in {
|
||||
exactly(2, ir.bindings) shouldBe an[IR.Error.Redefined.Method]
|
||||
@ -87,10 +86,10 @@ class OverloadsResolutionTest extends CompilerTest {
|
||||
|
||||
val ir =
|
||||
s"""
|
||||
|type $atomName a b c
|
||||
|type $atomName a b
|
||||
|type $atomName a
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|type $atomName a b c
|
||||
|type $atomName a b
|
||||
|type $atomName a
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
"detect overloads within a given module" in {
|
||||
exactly(2, ir.bindings) shouldBe an[IR.Error.Redefined.Atom]
|
||||
|
@ -1,13 +1,15 @@
|
||||
package org.enso.interpreter.test.semantic
|
||||
import org.enso.interpreter.node.callable.{ApplicationNode, SequenceLiteralNode}
|
||||
import org.enso.interpreter.node.callable.function.CreateFunctionNode
|
||||
import org.enso.interpreter.node.callable.thunk.ForceNode
|
||||
import org.enso.interpreter.node.callable.{ApplicationNode, SequenceLiteralNode}
|
||||
import org.enso.interpreter.node.controlflow.MatchNode
|
||||
import org.enso.interpreter.node.expression.literal.IntegerLiteralNode
|
||||
import org.enso.interpreter.node.scope.{AssignmentNode, ReadLocalVariableNode}
|
||||
import org.enso.interpreter.test.InterpreterTest
|
||||
import org.enso.polyglot.MethodNames
|
||||
|
||||
class CodeLocationsTest extends InterpreterTest {
|
||||
|
||||
def debugPrintCodeLocations(code: String): Unit = {
|
||||
var off = 0
|
||||
code.linesIterator.toList.foreach { line =>
|
||||
@ -203,4 +205,38 @@ class CodeLocationsTest extends InterpreterTest {
|
||||
instrumenter.assertNodeExists(22, 2, classOf[ApplicationNode])
|
||||
eval(code)
|
||||
}
|
||||
|
||||
"Sugared method definitions" should "get the right locations" in
|
||||
withLocationsInstrumenter { instrumenter =>
|
||||
val code =
|
||||
"""
|
||||
|Test.foo a b = a * b - a
|
||||
|
|
||||
|main = Test.foo 2 3
|
||||
|""".stripMargin
|
||||
|
||||
val mod = executionContext.evalModule(code, "Test")
|
||||
val tpe = mod.getAssociatedConstructor
|
||||
val method = mod.getMethod(tpe, "foo")
|
||||
method.value.invokeMember(MethodNames.Function.GET_SOURCE_START) shouldEqual 1
|
||||
method.value.invokeMember(MethodNames.Function.GET_SOURCE_LENGTH) shouldEqual 24
|
||||
|
||||
instrumenter.assertNodeExists(16, 9, classOf[ApplicationNode])
|
||||
|
||||
eval(code)
|
||||
}
|
||||
|
||||
"Sugared function definitions" should "get the right locations" in
|
||||
withLocationsInstrumenter { instrumenter =>
|
||||
val code =
|
||||
"""
|
||||
|main =
|
||||
| f a b = a - b
|
||||
| f 10 20
|
||||
|""".stripMargin
|
||||
|
||||
instrumenter.assertNodeExists(12, 13, classOf[AssignmentNode])
|
||||
instrumenter.assertNodeExists(20, 5, classOf[ApplicationNode])
|
||||
eval(code)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package org.enso.interpreter.test.semantic
|
||||
|
||||
import org.enso.interpreter.test.InterpreterTest
|
||||
|
||||
class FunctionSugarTest extends InterpreterTest {
|
||||
|
||||
"Sugared function definitions" should "work" in {
|
||||
val code =
|
||||
"""
|
||||
|main =
|
||||
| f a b = a - b
|
||||
| f 10 20
|
||||
|""".stripMargin
|
||||
|
||||
eval(code) shouldEqual -10
|
||||
}
|
||||
|
||||
"Sugared method definitions" should "work" in {
|
||||
val code =
|
||||
"""
|
||||
|Unit.foo a b = a * b - a
|
||||
|
|
||||
|main = Unit.foo 2 3
|
||||
|""".stripMargin
|
||||
|
||||
eval(code) shouldEqual 4
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user