From 0e71c3962d5d29aa81795da4de91de4248b2876f Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Thu, 7 Apr 2022 21:39:22 +0200 Subject: [PATCH 01/13] unify function and thunk --- .../node/callable/InvokeCallableNode.java | 1 - .../argument/IndirectArgumentSorterNode.java | 5 +-- .../node/callable/thunk/CreateThunkNode.java | 4 +- .../node/callable/thunk/ForceNode.java | 1 - .../callable/thunk/ThunkExecutorNode.java | 23 +++++------ .../builtin/bool/IfThenElseNode.java | 1 - .../builtin/function/ApplicationOperator.java | 1 - .../builtin/meta/NewAtomInstanceNode.java | 1 - .../builtin/runtime/NoInlineNode.java | 1 - .../thread/WithInterruptHandlerNode.java | 1 - .../node/expression/debug/EvalNode.java | 6 +-- .../runtime/callable/argument/Thunk.java | 40 ------------------- .../runtime/callable/function/Function.java | 11 ++++- .../callable/function/FunctionSchema.java | 2 + .../enso/interpreter/runtime/type/Types.java | 5 --- 15 files changed, 29 insertions(+), 74 deletions(-) delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/Thunk.java diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java index 34974bc3c5c7..12cf5cedfc6c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java @@ -17,7 +17,6 @@ import org.enso.interpreter.runtime.callable.UnresolvedConversion; import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.function.Function; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/IndirectArgumentSorterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/IndirectArgumentSorterNode.java index 0e4fb06e22aa..311951b0ea90 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/IndirectArgumentSorterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/IndirectArgumentSorterNode.java @@ -41,10 +41,9 @@ private Object executeArguments( Object state, ThunkExecutorNode thunkExecutorNode) { for (int i = 0; i < mapping.getArgumentShouldExecute().length; i++) { - if (TypesGen.isThunk(arguments[i]) && mapping.getArgumentShouldExecute()[i]) { + if (mapping.getArgumentShouldExecute()[i]) { Stateful result = - thunkExecutorNode.executeThunk( - TypesGen.asThunk(arguments[i]), state, BaseNode.TailStatus.NOT_TAIL); + thunkExecutorNode.executeThunk(arguments[i], state, BaseNode.TailStatus.NOT_TAIL); arguments[i] = result.getValue(); state = result.getState(); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/CreateThunkNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/CreateThunkNode.java index 0ef1b7681e6e..2971231b3ae9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/CreateThunkNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/CreateThunkNode.java @@ -4,7 +4,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.NodeInfo; import org.enso.interpreter.node.ExpressionNode; -import org.enso.interpreter.runtime.callable.argument.Thunk; +import org.enso.interpreter.runtime.callable.function.Function; /** This node is responsible for wrapping a call target in a {@link Thunk} at execution time. */ @NodeInfo(shortName = "CreateThunk", description = "Wraps a call target in a thunk at runtime") @@ -34,6 +34,6 @@ public static CreateThunkNode build(RootCallTarget callTarget) { */ @Override public Object executeGeneric(VirtualFrame frame) { - return new Thunk(this.callTarget, frame.materialize()); + return Function.thunk(this.callTarget, frame.materialize()); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ForceNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ForceNode.java index cb4772a7e7f4..f669ffe75630 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ForceNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ForceNode.java @@ -7,7 +7,6 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.NodeInfo; import org.enso.interpreter.node.ExpressionNode; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.state.Stateful; /** Node responsible for handling user-requested thunks forcing. */ diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java index 8669e5dd1441..d236bcbcb2f2 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java @@ -8,7 +8,6 @@ import org.enso.interpreter.Constants; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.dispatch.LoopingCallOptimiserNode; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.state.Stateful; @@ -40,20 +39,15 @@ public static ThunkExecutorNode build() { */ public abstract Stateful executeThunk(Object thunk, Object state, BaseNode.TailStatus isTail); - static boolean isThunk(Object th) { - return TypesGen.isThunk(th); - } - - @Specialization(guards = "!isThunk(thunk)") - Stateful doOther(Object thunk, Object state, BaseNode.TailStatus isTail) { - return new Stateful(state, thunk); + boolean sameCallTarget(DirectCallNode callNode, Function function) { + return function.getCallTarget() == callNode.getCallTarget(); } @Specialization( - guards = "callNode.getCallTarget() == thunk.getCallTarget()", + guards = {"thunk.isThunk()", "sameCallTarget(callNode, thunk)"}, limit = Constants.CacheSizes.THUNK_EXECUTOR_NODE) Stateful doCached( - Thunk thunk, + Function thunk, Object state, BaseNode.TailStatus isTail, @Cached("create(thunk.getCallTarget())") DirectCallNode callNode, @@ -71,9 +65,9 @@ Stateful doCached( } } - @Specialization(replaces = "doCached") + @Specialization(replaces = "doCached", guards = "thunk.isThunk()") Stateful doUncached( - Thunk thunk, + Function thunk, Object state, BaseNode.TailStatus isTail, @Cached IndirectCallNode callNode, @@ -93,4 +87,9 @@ Stateful doUncached( } } } + + @Fallback + Stateful doOther(Object thunk, Object state, BaseNode.TailStatus isTail) { + return new Stateful(state, thunk); + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java index f4d860a8a095..8cf4a7a0e823 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java @@ -7,7 +7,6 @@ import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.state.Stateful; @BuiltinMethod( diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java index 12d2a5bc711b..6b13615141d6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java @@ -8,7 +8,6 @@ import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.state.Stateful; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/NewAtomInstanceNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/NewAtomInstanceNode.java index b1d42bd2a213..9c1ae5585a2e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/NewAtomInstanceNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/NewAtomInstanceNode.java @@ -5,7 +5,6 @@ import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.data.Array; -import org.enso.interpreter.runtime.type.TypesGen; @BuiltinMethod( type = "Meta", diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/NoInlineNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/NoInlineNode.java index 07d8f384a762..582e813cc153 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/NoInlineNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/NoInlineNode.java @@ -7,7 +7,6 @@ import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.state.Stateful; @BuiltinMethod( diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/thread/WithInterruptHandlerNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/thread/WithInterruptHandlerNode.java index 5040f68c6452..437f16784334 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/thread/WithInterruptHandlerNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/thread/WithInterruptHandlerNode.java @@ -6,7 +6,6 @@ import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.control.ThreadInterruptedException; import org.enso.interpreter.runtime.state.Stateful; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java index 9e426bf59fac..5ea378ef6a13 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java @@ -15,7 +15,7 @@ import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode; import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.callable.CallerInfo; -import org.enso.interpreter.runtime.callable.argument.Thunk; +import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.scope.LocalScope; import org.enso.interpreter.runtime.scope.ModuleScope; @@ -98,7 +98,7 @@ Stateful doCached( "parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expressionStr)") RootCallTarget cachedCallTarget, @Cached("build()") ThunkExecutorNode thunkExecutorNode) { - Thunk thunk = new Thunk(cachedCallTarget, callerInfo.getFrame()); + Function thunk = Function.thunk(cachedCallTarget, callerInfo.getFrame()); return thunkExecutorNode.executeThunk(thunk, state, getTailStatus()); } @@ -114,7 +114,7 @@ Stateful doUncached( callerInfo.getLocalScope(), callerInfo.getModuleScope(), toJavaStringNode.execute(expression)); - Thunk thunk = new Thunk(callTarget, callerInfo.getFrame()); + Function thunk = Function.thunk(callTarget, callerInfo.getFrame()); return thunkExecutorNode.executeThunk(thunk, state, getTailStatus()); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/Thunk.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/Thunk.java deleted file mode 100644 index 1aeddcb5fbce..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/Thunk.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.enso.interpreter.runtime.callable.argument; - -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.RootCallTarget; -import com.oracle.truffle.api.frame.MaterializedFrame; - -/** Runtime representation of a suspended function argument. */ -public class Thunk { - private final RootCallTarget callTarget; - private final MaterializedFrame scope; - - /** - * Creates a runtime thunk. - * - * @param callTarget the {@link CallTarget} representing the argument's expression - * @param scope the caller scope used for evaluating the {@code callTarget} - */ - public Thunk(RootCallTarget callTarget, MaterializedFrame scope) { - this.callTarget = callTarget; - this.scope = scope; - } - - /** - * Returns the call target representing the argument's expression. - * - * @return the call target representing the argument's expression. - */ - public CallTarget getCallTarget() { - return callTarget; - } - - /** - * Returns the caller scope. - * - * @return the caller scope used for evaluating this thunk. - */ - public MaterializedFrame getScope() { - return scope; - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java index 67442963a593..c42c1f3b4f19 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java @@ -24,7 +24,6 @@ import org.enso.interpreter.runtime.callable.UnresolvedConversion; import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary; import org.enso.interpreter.runtime.state.data.EmptyMap; @@ -79,6 +78,10 @@ public Function(RootCallTarget callTarget, MaterializedFrame scope, FunctionSche this(callTarget, scope, schema, null, null); } + public static Function thunk(RootCallTarget callTarget, MaterializedFrame scope) { + return new Function(callTarget, scope, FunctionSchema.THUNK); + } + /** * Creates a Function object from a {@link BuiltinRootNode} and argument definitions. * @@ -308,7 +311,7 @@ public static Object[] buildArguments( * @param state the state to execute the thunk with * @return an array containing the necessary information to call an Enso thunk */ - public static Object[] buildArguments(Thunk thunk, Object state) { + public static Object[] buildArguments(Function thunk, Object state) { return new Object[] {thunk.getScope(), null, state, new Object[0]}; } @@ -456,4 +459,8 @@ static Function resolve(Function _this, AtomConstructor target, UnresolvedConver return function; } } + + public boolean isThunk() { + return schema == FunctionSchema.THUNK; + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java index 770e467470d3..9d97b7064cad 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java @@ -29,6 +29,8 @@ public boolean shouldFrameBePassed() { } } + public static final FunctionSchema THUNK = new FunctionSchema(); + private final @CompilationFinal(dimensions = 1) ArgumentDefinition[] argumentInfos; private final @CompilationFinal(dimensions = 1) boolean[] hasPreApplied; private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java index 9e6bc5d78a4e..ab8f33db8804 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java @@ -3,10 +3,8 @@ import com.oracle.truffle.api.dsl.TypeSystem; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.UnsupportedTypeException; -import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.callable.UnresolvedConversion; import org.enso.interpreter.runtime.callable.UnresolvedSymbol; -import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.function.Function; @@ -40,7 +38,6 @@ Function.class, Atom.class, AtomConstructor.class, - Thunk.class, DataflowError.class, UnresolvedConversion.class, UnresolvedSymbol.class, @@ -124,8 +121,6 @@ public static String getName(Object value) { return TypesGen.asAtom(value).getConstructor().getQualifiedName().toString(); } else if (TypesGen.isAtomConstructor(value)) { return TypesGen.asAtomConstructor(value).getQualifiedName().toString(); - } else if (TypesGen.isThunk(value)) { - return Constants.THUNK; } else if (TypesGen.isDataflowError(value)) { return Constants.ERROR; } else if (TypesGen.isUnresolvedSymbol(value) || TypesGen.isUnresolvedConversion(value)) { From 973b52990d3d562d7355eedf72c6a92333eafc30 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Fri, 15 Apr 2022 21:57:30 +0200 Subject: [PATCH 02/13] Holy cow, I can't believe this actually works --- .../callable/argument/ReadArgumentNode.java | 8 ++-- .../callable/thunk/ThunkExecutorNode.java | 46 ++++++++++++++++++- .../runtime/callable/function/Function.java | 4 ++ .../callable/function/FunctionSchema.java | 7 +++ .../pass/analyse/DemandAnalysis.scala | 11 +++-- .../test/semantic/CurryingTest.scala | 15 ++++-- 6 files changed, 77 insertions(+), 14 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentNode.java index aab241c1b0dc..2514412f2d07 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentNode.java @@ -45,17 +45,17 @@ public static ReadArgumentNode build(int position, ExpressionNode defaultValue) */ @Override public Object executeGeneric(VirtualFrame frame) { - Object argument = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[index]; + Object arguments[] = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); if (defaultValue == null) { - return argument; + return arguments[index]; } // Note [Handling Argument Defaults] - if (defaultingProfile.profile(argument == null)) { + if (defaultingProfile.profile(arguments.length <= index || arguments[index] == null)) { return defaultValue.executeGeneric(frame); } else { - return argument; + return arguments[index]; } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java index d236bcbcb2f2..c7b5cb06c0bc 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java @@ -7,11 +7,14 @@ import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.Constants; import org.enso.interpreter.node.BaseNode; +import org.enso.interpreter.node.callable.InvokeCallableNode; +import org.enso.interpreter.node.callable.dispatch.IndirectInvokeFunctionNode; +import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode; import org.enso.interpreter.node.callable.dispatch.LoopingCallOptimiserNode; +import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.state.Stateful; -import org.enso.interpreter.runtime.type.TypesGen; /** Node responsible for executing (forcing) thunks passed to it as runtime values. */ @GenerateUncached @@ -88,6 +91,47 @@ Stateful doUncached( } } + static InvokeFunctionNode buildInvokeFunctionNode(BaseNode.TailStatus tailStatus) { + var node = + InvokeFunctionNode.build( + new CallArgumentInfo[0], + InvokeCallableNode.DefaultsExecutionMode.EXECUTE, + InvokeCallableNode.ArgumentsExecutionMode.EXECUTE); + node.setTailStatus(tailStatus); + return node; + } + + @Specialization( + guards = {"!fn.isThunk()", "fn.isFullyApplied()", "isTail == cachedIsTail"}, + limit = "3" /* limit is 3 because that's the number of different values for isTail */) + Stateful doCachedFn( + Function fn, + Object state, + BaseNode.TailStatus isTail, + @Cached("isTail") BaseNode.TailStatus cachedIsTail, + @Cached("buildInvokeFunctionNode(cachedIsTail)") InvokeFunctionNode invokeFunctionNode) { + return invokeFunctionNode.execute(fn, null, state, new Object[0]); + } + + @Specialization( + guards = {"!fn.isThunk()", "fn.isFullyApplied()"}, + replaces = {"doCachedFn"}) + Stateful doUncachedFn( + Function fn, + Object state, + BaseNode.TailStatus isTail, + @Cached IndirectInvokeFunctionNode invokeFunctionNode) { + return invokeFunctionNode.execute( + fn, + null, + state, + new Object[0], + new CallArgumentInfo[0], + InvokeCallableNode.DefaultsExecutionMode.EXECUTE, + InvokeCallableNode.ArgumentsExecutionMode.EXECUTE, + isTail); + } + @Fallback Stateful doOther(Object thunk, Object state, BaseNode.TailStatus isTail) { return new Stateful(state, thunk); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java index c42c1f3b4f19..ab7d28de428a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java @@ -463,4 +463,8 @@ static Function resolve(Function _this, AtomConstructor target, UnresolvedConver public boolean isThunk() { return schema == FunctionSchema.THUNK; } + + public boolean isFullyApplied() { + return schema.isFullyApplied(); + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java index 9d97b7064cad..066601f1443c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java @@ -38,6 +38,8 @@ public boolean shouldFrameBePassed() { private final boolean hasOversaturatedArguments; private final CallerFrameAccess callerFrameAccess; + public final boolean isFullyApplied; + /** * Creates an {@link FunctionSchema} instance. * @@ -68,6 +70,7 @@ public FunctionSchema( this.hasAnyPreApplied = hasAnyPreApplied; this.hasOversaturatedArguments = this.oversaturatedArguments.length > 0; + this.isFullyApplied = isFullyApplied(InvokeCallableNode.DefaultsExecutionMode.EXECUTE); } /** @@ -213,4 +216,8 @@ public boolean isFullyApplied(InvokeCallableNode.DefaultsExecutionMode defaultsE } return functionIsFullyApplied; } + + public boolean isFullyApplied() { + return isFullyApplied; + } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala index 09f1cc1453bc..99a6419e66e1 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala @@ -169,7 +169,7 @@ case object DemandAnalysis extends IRPass { name: IR.Name, isInsideCallArgument: Boolean ): IR.Expression = { - val usesLazyTerm = isUsageOfSuspendedTerm(name) + val usesLazyTerm = true if (isInsideCallArgument) { name @@ -227,11 +227,12 @@ case object DemandAnalysis extends IRPass { ): IR.Application = application match { case pref @ IR.Application.Prefix(fn, args, _, _, _, _) => + val newFun = fn match { + case n: IR.Name => n + case e => analyseExpression(e, isInsideCallArgument = false) + } pref.copy( - function = analyseExpression( - fn, - isInsideCallArgument = false - ), + function = newFun, arguments = args.map(analyseCallArgument) ) case force @ IR.Application.Force(target, _, _, _) => diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala index 5c369ee00abb..17572465f0b0 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala @@ -32,8 +32,7 @@ class CurryingTest extends InterpreterTest { | fn1 = fn ... | fn2 = fn1 1 2 ... | fn3 = fn2 3 ... - | - | fn3.call + | fn3 |""".stripMargin eval(code) shouldEqual 26 @@ -74,11 +73,19 @@ class CurryingTest extends InterpreterTest { | fn1 = Nothing.fn ... | fn2 = fn1 1 2 ... | fn3 = fn2 3 ... - | - | fn3.call + | fn3 |""".stripMargin eval(code) shouldEqual 26 } + + "automatically force functions with all-defaulted arguments" in { + val code = + """main = + | foo (x=1) = x + | foo + 1 + |""".stripMargin + eval(code) shouldEqual 2 + } } } From b6ce2c2b39dfe0fff4199eca13a5ccec8add5623 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Thu, 21 Apr 2022 16:16:12 +0200 Subject: [PATCH 03/13] it works! --- .../node/callable/InvokeCallableNode.java | 1 - .../enso/compiler/codegen/IrToTruffle.scala | 3 ++ .../test/semantic/CurryingTest.scala | 44 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java index 12cf5cedfc6c..a83071f5cb05 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java @@ -5,7 +5,6 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.source.SourceSection; import java.util.UUID; import java.util.concurrent.locks.Lock; diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index b24901abfbcc..526c08ece6f1 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -1317,6 +1317,9 @@ class IrToTruffle( def processApplication(application: IR.Application): RuntimeExpression = application match { case IR.Application.Prefix(fn, args, hasDefaultsSuspended, loc, _, _) => + if (args.isEmpty && hasDefaultsSuspended) { + return run(fn) + } val callArgFactory = new CallArgumentProcessor(scope, scopeName) val arguments = args diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala index 17572465f0b0..135d57e95d77 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala @@ -87,5 +87,49 @@ class CurryingTest extends InterpreterTest { |""".stripMargin eval(code) shouldEqual 2 } + + "allow to pass suspended functions in arguments with `...`" in { + val code = + """main = + | foo f = f 1 + | bar x=1 = x + 1 + | foo (bar ...) + |""".stripMargin + eval(code) shouldEqual 2 + } + + "allow to pass suspended functions in arguments with `...` but still auto-execute them" in { + val code = + """main = + | foo f = f + | bar x=1 = x + 1 + | foo (bar ...) + |""".stripMargin + eval(code) shouldEqual 2 + } + + "should handle a defaulted-suspended combo" in { + val code = + """main = + | foo ~f = f + | bar x=1 = x + 1 + | foo (bar ...) + |""".stripMargin + eval(code) shouldEqual 2 + } + + "should make `...` an identity on Atom Constructors" in { + val code = + """ + |type My_Atom x=1 + | + |My_Atom.my_static = "hello" + | + |main = + | (My_Atom ...).my_static + |""".stripMargin + eval(code) shouldEqual "hello" + } + } } From a11dd65df75b4e5c384edf5f611738f03ec534b4 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Fri, 22 Apr 2022 12:17:41 +0200 Subject: [PATCH 04/13] get rid of badly coded metadata --- .../enso/compiler/codegen/IrToTruffle.scala | 8 +-- .../scala/org/enso/compiler/core/IR.scala | 23 ++----- .../compiler/pass/analyse/AliasAnalysis.scala | 2 +- .../pass/analyse/DataflowAnalysis.scala | 2 +- .../pass/analyse/DemandAnalysis.scala | 40 +----------- .../enso/compiler/pass/analyse/TailCall.scala | 2 +- .../desugar/LambdaShorthandToLambda.scala | 8 +-- .../pass/desugar/SectionsToBinOp.scala | 12 ++-- .../compiler/pass/resolve/TypeFunctions.scala | 6 +- .../pass/resolve/VectorLiterals.scala | 2 +- .../pass/analyse/DemandAnalysisTest.scala | 64 ------------------- 11 files changed, 23 insertions(+), 146 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 526c08ece6f1..d67a634652d5 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -1410,7 +1410,6 @@ class IrToTruffle( name, value, _, - shouldBeSuspended, _, _ ) => @@ -1425,12 +1424,7 @@ class IrToTruffle( case _: IR.Name => false case _: IR.Literal.Text => false case _: IR.Literal.Number => false - case _ => - shouldBeSuspended.getOrElse( - throw new CompilerError( - "Demand analysis information missing from call argument." - ) - ) + case _ => true } val childScope = if (shouldSuspend) { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala index 270cacea6138..63d0b6253bb0 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala @@ -5310,15 +5310,6 @@ object IR { /** The expression of the argument, if present. */ val value: Expression - /** Whether or not the argument should be suspended at code generation time. - * - * A value of `Some(true)` implies that code generation should suspend the - * argument. A value of `Some(false)` implies that code generation should - * _not_ suspend the argument. A value of [[None]] implies that the - * information is missing. - */ - val shouldBeSuspended: Option[Boolean] - /** @inheritdoc */ override def mapExpressions(fn: Expression => Expression): CallArgument @@ -5340,19 +5331,16 @@ object IR { * A [[CallArgument]] where the `value` is an [[IR.Name.Blank]] is a * representation of a lambda shorthand argument. * - * @param name the name of the argument being called, if present - * @param value the expression being passed as the argument's value - * @param location the source location that the node corresponds to - * @param shouldBeSuspended whether or not the argument should be passed - * suspended - * @param passData the pass metadata associated with this node + * @param name the name of the argument being called, if present + * @param value the expression being passed as the argument's value + * @param location the source location that the node corresponds to + * @param passData the pass metadata associated with this node * @param diagnostics compiler diagnostics for this node */ sealed case class Specified( override val name: Option[IR.Name], override val value: Expression, override val location: Option[IdentifiedLocation], - override val shouldBeSuspended: Option[Boolean] = None, override val passData: MetadataStorage = MetadataStorage(), override val diagnostics: DiagnosticStorage = DiagnosticStorage() ) extends CallArgument @@ -5375,7 +5363,6 @@ object IR { name: Option[IR.Name] = name, value: Expression = value, location: Option[IdentifiedLocation] = location, - shouldBeSuspended: Option[Boolean] = shouldBeSuspended, passData: MetadataStorage = passData, diagnostics: DiagnosticStorage = diagnostics, id: Identifier = id @@ -5384,7 +5371,6 @@ object IR { name, value, location, - shouldBeSuspended, passData, diagnostics ) @@ -5439,7 +5425,6 @@ object IR { |name = $name, |value = $value, |location = $location, - |shouldBeSuspended = $shouldBeSuspended, |passData = ${this.showPassData}, |diagnostics = $diagnostics, |id = $id diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala index d80b4f31fc4e..23b4dc96a7a6 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala @@ -493,7 +493,7 @@ case object AliasAnalysis extends IRPass { graph: AliasAnalysis.Graph, parentScope: AliasAnalysis.Graph.Scope ): List[IR.CallArgument] = { - args.map { case arg @ IR.CallArgument.Specified(_, expr, _, _, _, _) => + args.map { case arg @ IR.CallArgument.Specified(_, expr, _, _, _) => val currentScope = expr match { case _: IR.Literal => parentScope case _ => parentScope.addChild() diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DataflowAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DataflowAnalysis.scala index 32bd34890a34..cd520e4178a2 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DataflowAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DataflowAnalysis.scala @@ -692,7 +692,7 @@ case object DataflowAnalysis extends IRPass { info: DependencyInfo ): IR.CallArgument = { argument match { - case spec @ IR.CallArgument.Specified(name, value, _, _, _, _) => + case spec @ IR.CallArgument.Specified(name, value, _, _, _) => val specDep = asStatic(spec) val valueDep = asStatic(value) info.dependents.updateAt(valueDep, Set(specDep)) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala index 99a6419e66e1..8e0733b2606b 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala @@ -262,43 +262,6 @@ case object DemandAnalysis extends IRPass { ) } - /** Determines whether a particular piece of IR represents the usage of a - * suspended term (and hence requires forcing). - * - * @param expr the expression to check - * @return `true` if `expr` represents the usage of a suspended term, `false` - * otherwise - */ - def isUsageOfSuspendedTerm(expr: IR.Expression): Boolean = { - expr match { - case name: IR.Name => - val aliasInfo = name - .unsafeGetMetadata( - AliasAnalysis, - "Missing alias occurrence information for a name usage" - ) - .unsafeAs[AliasAnalysis.Info.Occurrence] - - aliasInfo.graph - .defLinkFor(aliasInfo.id) - .flatMap(link => { - aliasInfo.graph - .getOccurrence(link.target) - .getOrElse( - throw new CompilerError( - s"Malformed aliasing link with target ${link.target}" - ) - ) match { - case AliasAnalysis.Graph.Occurrence.Def(_, _, _, _, isLazy) => - if (isLazy) Some(true) else None - case _ => None - } - }) - .isDefined - case _ => false - } - } - /** Performs demand analysis on a function call argument. * * In keeping with the requirement by the runtime to pass all function @@ -310,13 +273,12 @@ case object DemandAnalysis extends IRPass { */ def analyseCallArgument(arg: IR.CallArgument): IR.CallArgument = { arg match { - case spec @ IR.CallArgument.Specified(_, expr, _, _, _, _) => + case spec @ IR.CallArgument.Specified(_, expr, _, _, _) => spec.copy( value = analyseExpression( expr, isInsideCallArgument = true ), - shouldBeSuspended = Some(!isUsageOfSuspendedTerm(expr)) ) } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailCall.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailCall.scala index 6442279f1bc5..df4b6cc6d707 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailCall.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailCall.scala @@ -271,7 +271,7 @@ case object TailCall extends IRPass { */ def analyseCallArg(argument: IR.CallArgument): IR.CallArgument = { argument match { - case arg @ IR.CallArgument.Specified(_, expr, _, _, _, _) => + case arg @ IR.CallArgument.Specified(_, expr, _, _, _) => arg .copy( // Note [Call Argument Tail Position] diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/LambdaShorthandToLambda.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/LambdaShorthandToLambda.scala index af56e456ac7e..9ccfe3f69cd9 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/LambdaShorthandToLambda.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/LambdaShorthandToLambda.scala @@ -186,7 +186,7 @@ case object LambdaShorthandToLambda extends IRPass { args .zip(argIsUnderscore) .map(updateShorthandArg(_, freshNameSupply)) - .map { case s @ IR.CallArgument.Specified(_, value, _, _, _, _) => + .map { case s @ IR.CallArgument.Specified(_, value, _, _, _) => s.copy(value = desugarExpression(value, freshNameSupply)) } @@ -301,7 +301,7 @@ case object LambdaShorthandToLambda extends IRPass { * position is lambda shorthand, otherwise `false` */ def determineLambdaShorthand(args: List[IR.CallArgument]): List[Boolean] = { - args.map { case IR.CallArgument.Specified(_, value, _, _, _, _) => + args.map { case IR.CallArgument.Specified(_, value, _, _, _) => value match { case _: IR.Name.Blank => true case _ => false @@ -325,7 +325,7 @@ case object LambdaShorthandToLambda extends IRPass { val isShorthand = argAndIsShorthand._2 arg match { - case s @ IR.CallArgument.Specified(_, value, _, _, _, _) => + case s @ IR.CallArgument.Specified(_, value, _, _, _) => if (isShorthand) { val newName = freshNameSupply .newName() @@ -354,7 +354,7 @@ case object LambdaShorthandToLambda extends IRPass { ): Option[IR.DefinitionArgument] = { if (isShorthand) { arg match { - case IR.CallArgument.Specified(_, value, _, _, passData, diagnostics) => + case IR.CallArgument.Specified(_, value, _, passData, diagnostics) => // Note [Safe Casting to IR.Name.Literal] val defArgName = IR.Name.Literal( diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/SectionsToBinOp.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/SectionsToBinOp.scala index 1a528f9d0390..4f6e96567d8a 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/SectionsToBinOp.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/SectionsToBinOp.scala @@ -105,7 +105,7 @@ case object SectionsToBinOp extends IRPass { case Section.Left(arg, op, loc, passData, diagnostics) => val rightArgName = freshNameSupply.newName() val rightCallArg = - IR.CallArgument.Specified(None, rightArgName, None, None) + IR.CallArgument.Specified(None, rightArgName, None) val rightDefArg = IR.DefinitionArgument.Specified( rightArgName.duplicate(), None, @@ -117,7 +117,7 @@ case object SectionsToBinOp extends IRPass { if (arg.value.isInstanceOf[IR.Name.Blank]) { val leftArgName = freshNameSupply.newName() val leftCallArg = - IR.CallArgument.Specified(None, leftArgName, None, None) + IR.CallArgument.Specified(None, leftArgName, None) val leftDefArg = IR.DefinitionArgument.Specified( leftArgName.duplicate(), None, @@ -165,7 +165,7 @@ case object SectionsToBinOp extends IRPass { case Section.Sides(op, loc, passData, diagnostics) => val leftArgName = freshNameSupply.newName() val leftCallArg = - IR.CallArgument.Specified(None, leftArgName, None, None) + IR.CallArgument.Specified(None, leftArgName, None) val leftDefArg = IR.DefinitionArgument.Specified( leftArgName.duplicate(), None, @@ -176,7 +176,7 @@ case object SectionsToBinOp extends IRPass { val rightArgName = freshNameSupply.newName() val rightCallArg = - IR.CallArgument.Specified(None, rightArgName, None, None) + IR.CallArgument.Specified(None, rightArgName, None) val rightDefArg = IR.DefinitionArgument.Specified( rightArgName.duplicate(), None, @@ -228,7 +228,7 @@ case object SectionsToBinOp extends IRPass { case Section.Right(op, arg, loc, passData, diagnostics) => val leftArgName = freshNameSupply.newName() val leftCallArg = - IR.CallArgument.Specified(None, leftArgName, None, None) + IR.CallArgument.Specified(None, leftArgName, None) val leftDefArg = IR.DefinitionArgument.Specified( leftArgName.duplicate(), @@ -242,7 +242,7 @@ case object SectionsToBinOp extends IRPass { // Note [Blanks in Sections] val rightArgName = freshNameSupply.newName() val rightCallArg = - IR.CallArgument.Specified(None, rightArgName, None, None) + IR.CallArgument.Specified(None, rightArgName, None) val rightDefArg = IR.DefinitionArgument.Specified( rightArgName.duplicate(), None, diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/TypeFunctions.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/TypeFunctions.scala index f5ea86832912..72b37f41d55c 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/TypeFunctions.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/TypeFunctions.scala @@ -196,7 +196,7 @@ case object TypeFunctions extends IRPass { */ def resolveCallArgument(arg: IR.CallArgument): IR.CallArgument = { arg match { - case spec @ IR.CallArgument.Specified(_, value, _, _, _, _) => + case spec @ IR.CallArgument.Specified(_, value, _, _, _) => spec.copy( value = resolveExpression(value) ) @@ -218,8 +218,8 @@ case object TypeFunctions extends IRPass { */ def isValidCallArg(arg: IR.CallArgument): Boolean = { arg match { - case IR.CallArgument.Specified(name, _, _, susp, _, _) => - name.isEmpty && (susp.isEmpty || susp.get) + case IR.CallArgument.Specified(name, _, _, _, _) => + name.isEmpty } } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/VectorLiterals.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/VectorLiterals.scala index b2947adbb263..776554374a94 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/VectorLiterals.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/VectorLiterals.scala @@ -122,7 +122,7 @@ case object VectorLiterals extends IRPass { vec.duplicate(), List( IR.CallArgument - .Specified(None, trans.copy(location = None), None, None) + .Specified(None, trans.copy(location = None), None) ), false, trans.location diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala index 30fd710b9716..b702fed8d320 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala @@ -160,29 +160,6 @@ class DemandAnalysisTest extends CompilerTest { vec.items(2) shouldBe an[IR.Name] } - - "be marked as not to suspend during codegen when passed to a function" in { - implicit val ctx: InlineContext = mkInlineContext - - val ir = - """ - |~x -> ~y -> z -> foo x y z - |""".stripMargin.preprocessExpression.get.analyse - - val app = ir - .asInstanceOf[IR.Function.Lambda] - .body - .asInstanceOf[IR.Application.Prefix] - - app.arguments.head - .asInstanceOf[IR.CallArgument.Specified] - .shouldBeSuspended shouldEqual Some(false) - - app - .arguments(1) - .asInstanceOf[IR.CallArgument.Specified] - .shouldBeSuspended shouldEqual Some(false) - } } "Non-suspended arguments" should { @@ -211,25 +188,6 @@ class DemandAnalysisTest extends CompilerTest { .asInstanceOf[IR.CallArgument.Specified] .value shouldBe an[IR.Name] } - - "be marked for suspension during codegen when passed to a function" in { - val xArg = body.returnValue - .asInstanceOf[IR.Application.Prefix] - .arguments - .head - .asInstanceOf[IR.CallArgument.Specified] - - val aArg = body.returnValue - .asInstanceOf[IR.Application.Prefix] - .arguments(1) - .asInstanceOf[IR.CallArgument.Specified] - - xArg.value shouldBe an[IR.Name] - xArg.shouldBeSuspended shouldEqual Some(true) - - aArg.value shouldBe an[IR.Name] - aArg.shouldBeSuspended shouldEqual Some(true) - } } "Suspended blocks" should { @@ -280,28 +238,6 @@ class DemandAnalysisTest extends CompilerTest { .value shouldBe an[IR.Name] } - "be marked as not to suspend during codegen when passed to a function" in { - implicit val ctx: InlineContext = mkInlineContext - - val ir = - """ - |x -> - | blck = - | foo a b - | bar blck - |""".stripMargin.preprocessExpression.get.analyse - - ir.asInstanceOf[IR.Function.Lambda] - .body - .asInstanceOf[IR.Expression.Block] - .returnValue - .asInstanceOf[IR.Application.Prefix] - .arguments - .head - .asInstanceOf[IR.CallArgument.Specified] - .shouldBeSuspended shouldEqual Some(false) - } - "force terms in blocks passed directly as arguments" in { implicit val ctx: ModuleContext = mkModuleContext From 1187847ea840b1d16b9085ca5b2cc6b13c3a7275 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Fri, 22 Apr 2022 14:25:13 +0200 Subject: [PATCH 05/13] fix tests --- .../pass/analyse/DemandAnalysis.scala | 62 +++++---------- .../pass/analyse/DataflowAnalysisTest.scala | 77 ------------------- .../pass/analyse/DemandAnalysisTest.scala | 30 +------- 3 files changed, 22 insertions(+), 147 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala index 8e0733b2606b..83403bc0f32c 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DemandAnalysis.scala @@ -169,51 +169,31 @@ case object DemandAnalysis extends IRPass { name: IR.Name, isInsideCallArgument: Boolean ): IR.Expression = { - val usesLazyTerm = true - if (isInsideCallArgument) { name } else { - if (usesLazyTerm) { - val forceLocation = name.location - val newNameLocation = name.location.map(l => l.copy(id = None)) - - val newName = name match { - case lit: IR.Name.Literal => lit.copy(location = newNameLocation) - case ths: IR.Name.This => ths.copy(location = newNameLocation) - case here: IR.Name.Here => here.copy(location = newNameLocation) - case special: IR.Name.Special => - special.copy(location = newNameLocation) - case _: IR.Name.Annotation => - throw new CompilerError( - "Annotations should not be present by the time demand analysis" + - " runs." - ) - case _: IR.Name.MethodReference => - throw new CompilerError( - "Method references should not be present by the time demand " + - "analysis runs." - ) - case _: IR.Name.Qualified => - throw new CompilerError( - "Qualified names should not be present by the time demand " + - "analysis runs." - ) - case err: IR.Error.Resolution => err - case err: IR.Error.Conversion => err - case _: IR.Name.Blank => - throw new CompilerError( - "Blanks should not be present by the time demand analysis runs." - ) - } - - IR.Application.Force(newName, forceLocation) - } else { - name + name match { + case lit: IR.Name.Literal if isDefined(lit) => + val forceLocation = name.location + val newNameLocation = name.location.map(l => l.copy(id = None)) + val newName = lit.copy(location = newNameLocation) + IR.Application.Force(newName, forceLocation) + case _ => name } } } + private def isDefined(name: IR.Name): Boolean = { + val aliasInfo = name + .unsafeGetMetadata( + AliasAnalysis, + "Missing alias occurrence information for a name usage" + ) + .unsafeAs[AliasAnalysis.Info.Occurrence] + + aliasInfo.graph.defLinkFor(aliasInfo.id).isDefined + } + /** Performs demand analysis on an application. * * @param application the function application to perform demand analysis on @@ -229,10 +209,10 @@ case object DemandAnalysis extends IRPass { case pref @ IR.Application.Prefix(fn, args, _, _, _, _) => val newFun = fn match { case n: IR.Name => n - case e => analyseExpression(e, isInsideCallArgument = false) + case e => analyseExpression(e, isInsideCallArgument = false) } pref.copy( - function = newFun, + function = newFun, arguments = args.map(analyseCallArgument) ) case force @ IR.Application.Force(target, _, _, _) => @@ -278,7 +258,7 @@ case object DemandAnalysis extends IRPass { value = analyseExpression( expr, isInsideCallArgument = true - ), + ) ) } } diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala index 4ef8507bd9ae..59f04c988a90 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala @@ -936,7 +936,6 @@ class DataflowAnalysisTest extends CompilerTest { fn.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified] val fnArgY = fn.arguments(1).asInstanceOf[IR.DefinitionArgument.Specified] - val fnArgYDefault = fnArgY.defaultValue.get.asInstanceOf[IR.Name.Literal] val fnBody = fn.body.asInstanceOf[IR.Application.Prefix] val plusFn = fnBody.function.asInstanceOf[IR.Name.Literal] val plusArgX = @@ -950,7 +949,6 @@ class DataflowAnalysisTest extends CompilerTest { val fnId = mkStaticDep(fn.getId) val fnArgXId = mkStaticDep(fnArgX.getId) val fnArgYId = mkStaticDep(fnArgY.getId) - val fnArgYDefaultId = mkStaticDep(fnArgYDefault.getId) val fnBodyId = mkStaticDep(fnBody.getId) val plusFnId = mkStaticDep(plusFn.getId) val plusArgXId = mkStaticDep(plusArgX.getId) @@ -967,11 +965,7 @@ class DataflowAnalysisTest extends CompilerTest { // The Tests for dependents dependents.getDirect(fnId) should not be defined - dependents.getDirect(fnArgXId) shouldEqual Some( - Set(plusArgXExprId, fnArgYDefaultId) - ) dependents.getDirect(fnArgYId) shouldEqual Some(Set(plusArgYExprId)) - dependents.getDirect(fnArgYDefaultId) shouldEqual Some(Set(fnArgYId)) dependents.getDirect(fnBodyId) shouldEqual Some(Set(fnId)) dependents.getDirect(plusSym) shouldEqual Some(Set(plusFnId)) dependents.getDirect(plusArgXId) shouldEqual Some(Set(fnBodyId)) @@ -982,8 +976,6 @@ class DataflowAnalysisTest extends CompilerTest { // The Tests for dependencies dependencies.getDirect(fnId) shouldEqual Some(Set(fnBodyId)) dependencies.getDirect(fnArgXId) shouldEqual None - dependencies.getDirect(fnArgYId) shouldEqual Some(Set(fnArgYDefaultId)) - dependencies.getDirect(fnArgYDefaultId) shouldEqual Some(Set(fnArgXId)) dependencies.getDirect(fnBodyId) shouldEqual Some( Set(plusFnId, plusArgXId, plusArgYId) ) @@ -1100,25 +1092,15 @@ class DataflowAnalysisTest extends CompilerTest { val lam = ir.asInstanceOf[IR.Function.Lambda] val argX = lam.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified] - val xUse = lam.body.asInstanceOf[IR.Name.Literal] // The IDs - val lamId = mkStaticDep(lam.getId) val argXId = mkStaticDep(argX.getId) - val xUseId = mkStaticDep(xUse.getId) // The info - val dependents = depInfo.dependents val dependencies = depInfo.dependencies - // The test for dependents - dependents.getDirect(argXId) shouldEqual Some(Set(xUseId)) - dependents.getDirect(xUseId) shouldEqual Some(Set(lamId)) - // The test for dependencies dependencies.getDirect(argXId) shouldEqual None - dependencies.getDirect(xUseId) shouldEqual Some(Set(argXId)) - dependencies.getDirect(lamId) shouldEqual Some(Set(xUseId)) } "work properly for blocks" in { @@ -1136,14 +1118,12 @@ class DataflowAnalysisTest extends CompilerTest { val xBind = block.expressions.head.asInstanceOf[IR.Expression.Binding] val xBindName = xBind.name.asInstanceOf[IR.Name.Literal] val xBindExpr = xBind.expression.asInstanceOf[IR.Literal.Number] - val xUse = block.returnValue.asInstanceOf[IR.Name.Literal] // The IDs val blockId = mkStaticDep(block.getId) val xBindId = mkStaticDep(xBind.getId) val xBindNameId = mkStaticDep(xBindName.getId) val xBindExprId = mkStaticDep(xBindExpr.getId) - val xUseId = mkStaticDep(xUse.getId) // The info val dependents = depInfo.dependents @@ -1151,14 +1131,10 @@ class DataflowAnalysisTest extends CompilerTest { // The test for dependents dependents.getDirect(blockId) should not be defined - dependents.getDirect(xBindId) shouldEqual Some(Set(xUseId)) dependents.getDirect(xBindNameId) shouldEqual Some(Set(xBindId)) dependents.getDirect(xBindExprId) shouldEqual Some(Set(xBindId)) - dependents.getDirect(xUseId) shouldEqual Some(Set(blockId)) // The test for dependencies - dependencies.getDirect(blockId) shouldEqual Some(Set(xUseId)) - dependencies.getDirect(xUseId) shouldEqual Some(Set(xBindId)) dependencies.getDirect(xBindId) shouldEqual Some( Set(xBindNameId, xBindExprId) ) @@ -1202,47 +1178,6 @@ class DataflowAnalysisTest extends CompilerTest { dependencies.getDirect(bindingExprId) shouldEqual None } - "work properly for undefined variables" in { - implicit val inlineContext: InlineContext = mkInlineContext - - val ir = - """ - |x = undefined - |""".stripMargin.preprocessExpression.get.analyse - - val depInfo = ir.getMetadata(DataflowAnalysis).get - - val binding = ir.asInstanceOf[IR.Expression.Binding] - val bindingName = binding.name.asInstanceOf[IR.Name.Literal] - val bindingExpr = binding.expression.asInstanceOf[IR.Error.Resolution] - val undefinedName = bindingExpr.originalName - - // The IDs - val bindingId = mkStaticDep(binding.getId) - val bindingNameId = mkStaticDep(bindingName.getId) - val bindingExprId = mkStaticDep(bindingExpr.getId) - val undefinedNameId = mkDynamicDep(undefinedName.name) - - // The info - val dependents = depInfo.dependents - val dependencies = depInfo.dependencies - - // The tests for dependents - dependents.getDirect(bindingId) should not be defined - dependents.getDirect(bindingNameId) shouldEqual Some(Set(bindingId)) - dependents.getDirect(bindingExprId) shouldEqual Some(Set(bindingId)) - dependents.getDirect(undefinedNameId) shouldEqual Some(Set(bindingExprId)) - - // The tests for dependencies - dependencies.getDirect(bindingId) shouldEqual Some( - Set(bindingNameId, bindingExprId) - ) - dependencies.getDirect(bindingNameId) shouldEqual None - dependencies.getDirect(bindingExprId) shouldEqual Some( - Set(undefinedNameId) - ) - } - "work properly for undefined variables in expressions" in { implicit val inlineContext: InlineContext = mkInlineContext @@ -1331,7 +1266,6 @@ class DataflowAnalysisTest extends CompilerTest { val vector = callArg.value .asInstanceOf[IR.Application.Literal.Sequence] - val xDefId = mkStaticDep(ir.arguments(0).getId) val xUseId = mkStaticDep(vector.items(0).getId) val yId = mkStaticDep(vector.items(1).getId) val litId = mkStaticDep(vector.items(2).getId) @@ -1345,7 +1279,6 @@ class DataflowAnalysisTest extends CompilerTest { val dependencies = depInfo.dependencies // Tests for dependents - dependents.getDirect(xDefId) shouldEqual Some(Set(xUseId)) dependents.getDirect(xUseId) shouldEqual Some(Set(vecId)) dependents.getDirect(yId) shouldEqual Some(Set(vecId)) dependents.getDirect(litId) shouldEqual Some(Set(vecId)) @@ -1397,9 +1330,7 @@ class DataflowAnalysisTest extends CompilerTest { caseBinding.expression.asInstanceOf[IR.Application.Prefix] val caseBindingName = caseBinding.name.asInstanceOf[IR.Name.Literal] val caseExpr = caseBlock.returnValue.asInstanceOf[IR.Case.Expr] - val scrutinee = caseExpr.scrutinee.asInstanceOf[IR.Name.Literal] val consBranch = caseExpr.branches.head - val catchAllbranch = caseExpr.branches(1) val consBranchPattern = consBranch.pattern.asInstanceOf[Pattern.Constructor] @@ -1432,9 +1363,7 @@ class DataflowAnalysisTest extends CompilerTest { val caseBindingExprId = mkStaticDep(caseBindingExpr.getId) val caseBindingNameId = mkStaticDep(caseBindingName.getId) val caseExprId = mkStaticDep(caseExpr.getId) - val scrutineeId = mkStaticDep(scrutinee.getId) val consBranchId = mkStaticDep(consBranch.getId) - val catchAllBranchId = mkStaticDep(catchAllbranch.getId) val consBranchPatternId = mkStaticDep(consBranchPattern.getId) val consBranchPatternConsId = mkStaticDep(consBranchPatternCons.getId) @@ -1457,8 +1386,6 @@ class DataflowAnalysisTest extends CompilerTest { // Tests for dependents dependents.getDirect(caseBlockId) should not be defined dependents.getDirect(caseExprId) shouldEqual Some(Set(caseBlockId)) - dependents.getDirect(scrutineeId) shouldEqual Some(Set(caseExprId)) - dependents.getDirect(caseBindingId) shouldEqual Some(Set(scrutineeId)) dependents.getDirect(caseBindingExprId) shouldEqual Some( Set(caseBindingId) ) @@ -1499,10 +1426,6 @@ class DataflowAnalysisTest extends CompilerTest { dependencies.getDirect(caseBindingId) shouldEqual Some( Set(caseBindingNameId, caseBindingExprId) ) - dependencies.getDirect(caseExprId) shouldEqual Some( - Set(scrutineeId, consBranchId, catchAllBranchId) - ) - dependencies.getDirect(scrutineeId) shouldEqual Some(Set(caseBindingId)) dependencies.getDirect(consBranchId) shouldEqual Some( Set(consBranchPatternId, consBranchExpressionId) ) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala index b702fed8d320..3a70bf6fb07f 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala @@ -157,39 +157,11 @@ class DemandAnalysisTest extends CompilerTest { vec.items(0) shouldBe an[IR.Application.Force] vec.items(1) shouldBe an[IR.Application.Force] - vec.items(2) shouldBe an[IR.Name] + vec.items(2) shouldBe an[IR.Application.Force] } } - "Non-suspended arguments" should { - implicit val ctx: InlineContext = mkInlineContext - - val ir = - """x -> y -> - | a = x - | foo x a - |""".stripMargin.preprocessExpression.get.analyse - - val body = ir - .asInstanceOf[IR.Function.Lambda] - .body - .asInstanceOf[IR.Expression.Block] - - "be left alone by demand analysis" in { - body.expressions.head - .asInstanceOf[IR.Expression.Binding] - .expression shouldBe an[IR.Name] - - body.returnValue - .asInstanceOf[IR.Application.Prefix] - .arguments - .head - .asInstanceOf[IR.CallArgument.Specified] - .value shouldBe an[IR.Name] - } - } - "Suspended blocks" should { "be forced when used" in { implicit val ctx: InlineContext = mkInlineContext From 4b8aea8fa020a7bd428efa43ed9e720192285b59 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 13:32:20 +0200 Subject: [PATCH 06/13] Update engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala Co-authored-by: Hubert Plociniczak --- .../main/scala/org/enso/compiler/codegen/IrToTruffle.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index d67a634652d5..692821e9a6d9 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -1316,10 +1316,9 @@ class IrToTruffle( */ def processApplication(application: IR.Application): RuntimeExpression = application match { + case IR.Application.Prefix(fn, Nil, true, _, _, _) => + run(fn) case IR.Application.Prefix(fn, args, hasDefaultsSuspended, loc, _, _) => - if (args.isEmpty && hasDefaultsSuspended) { - return run(fn) - } val callArgFactory = new CallArgumentProcessor(scope, scopeName) val arguments = args From 0b6cd84dee2757db9bdb4ff8b8de7e05417da24b Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 13:34:47 +0200 Subject: [PATCH 07/13] CR feedback --- .../org/enso/interpreter/node/BaseNode.java | 8 ++++- .../callable/thunk/ThunkExecutorNode.java | 22 +++++++------ .../function/ExplicitCallFunctionNode.java | 32 ------------------- .../interpreter/runtime/builtin/Builtins.java | 4 +-- .../callable/argument/ArgumentDefinition.java | 2 +- .../callable/argument/CallArgument.java | 2 +- .../callable/argument/CallArgumentInfo.java | 12 +++---- .../callable/function/FunctionSchema.java | 4 +-- .../test/semantic/CurryingTest.scala | 4 +-- 9 files changed, 33 insertions(+), 57 deletions(-) delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ExplicitCallFunctionNode.java diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/BaseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/BaseNode.java index 35c6cbc47f5f..b88c9f68b338 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/BaseNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/BaseNode.java @@ -18,7 +18,13 @@ public enum TailStatus { /** Node is in a tail position and marked as a tail call. */ TAIL_LOOP, /** Node is not in a tail position. */ - NOT_TAIL + NOT_TAIL; + + private final static int NUMBER_OF_VALUES = values().length; + + public static int numberOfValues() { + return NUMBER_OF_VALUES; + } } private @CompilationFinal TailStatus tailStatus = TailStatus.NOT_TAIL; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java index c7b5cb06c0bc..ae5cfef4665b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java @@ -47,20 +47,20 @@ boolean sameCallTarget(DirectCallNode callNode, Function function) { } @Specialization( - guards = {"thunk.isThunk()", "sameCallTarget(callNode, thunk)"}, + guards = {"function.isThunk()", "sameCallTarget(callNode, thunk)"}, limit = Constants.CacheSizes.THUNK_EXECUTOR_NODE) Stateful doCached( - Function thunk, + Function function, Object state, BaseNode.TailStatus isTail, @Cached("create(thunk.getCallTarget())") DirectCallNode callNode, @Cached LoopingCallOptimiserNode loopingCallOptimiserNode) { CompilerAsserts.partialEvaluationConstant(isTail); if (isTail != BaseNode.TailStatus.NOT_TAIL) { - return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state)); + return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(function, state)); } else { try { - return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state)); + return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(function, state)); } catch (TailCallException e) { return loopingCallOptimiserNode.executeDispatch( e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments()); @@ -68,9 +68,9 @@ Stateful doCached( } } - @Specialization(replaces = "doCached", guards = "thunk.isThunk()") + @Specialization(replaces = "doCached", guards = "function.isThunk()") Stateful doUncached( - Function thunk, + Function function, Object state, BaseNode.TailStatus isTail, @Cached IndirectCallNode callNode, @@ -78,12 +78,12 @@ Stateful doUncached( if (isTail != BaseNode.TailStatus.NOT_TAIL) { return (Stateful) callNode.call( - thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state)); + function.getCallTarget(), Function.ArgumentsHelper.buildArguments(function, state)); } else { try { return (Stateful) callNode.call( - thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state)); + function.getCallTarget(), Function.ArgumentsHelper.buildArguments(function, state)); } catch (TailCallException e) { return loopingCallOptimiserNode.executeDispatch( e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments()); @@ -101,9 +101,13 @@ static InvokeFunctionNode buildInvokeFunctionNode(BaseNode.TailStatus tailStatus return node; } + static int numberOfTailStatuses() { + return BaseNode.TailStatus.numberOfValues(); + } + @Specialization( guards = {"!fn.isThunk()", "fn.isFullyApplied()", "isTail == cachedIsTail"}, - limit = "3" /* limit is 3 because that's the number of different values for isTail */) + limit = "numberOfTailStatuses()") Stateful doCachedFn( Function fn, Object state, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ExplicitCallFunctionNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ExplicitCallFunctionNode.java deleted file mode 100644 index fd8a9652dbd0..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ExplicitCallFunctionNode.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.function; - -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.dsl.MonadicState; -import org.enso.interpreter.node.BaseNode; -import org.enso.interpreter.node.callable.InvokeCallableNode; -import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; -import org.enso.interpreter.runtime.callable.function.Function; -import org.enso.interpreter.runtime.state.Stateful; - -@BuiltinMethod( - type = "Function", - name = "call", - description = "Allows function calls to be made explicitly") -public class ExplicitCallFunctionNode extends Node { - private @Child InvokeCallableNode invokeCallableNode; - - ExplicitCallFunctionNode() { - invokeCallableNode = - InvokeCallableNode.build( - new CallArgumentInfo[0], - InvokeCallableNode.DefaultsExecutionMode.EXECUTE, - InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED); - invokeCallableNode.setTailStatus(BaseNode.TailStatus.TAIL_DIRECT); - } - - Stateful execute(VirtualFrame frame, @MonadicState Object state, Function _this) { - return invokeCallableNode.execute(_this, frame, state, new Object[0]); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java index 9d7a170e69a7..62dd7fa90aaa 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java @@ -190,9 +190,7 @@ public Builtins(Context context) { scope.registerMethod(debug, MethodNames.Debug.EVAL, DebugEvalMethodGen.makeFunction(language)); scope.registerMethod(debug, "breakpoint", DebugBreakpointMethodGen.makeFunction(language)); - - scope.registerMethod(function, "call", ExplicitCallFunctionMethodGen.makeFunction(language)); - + scope.registerMethod(any, "to_text", AnyToTextMethodGen.makeFunction(language)); scope.registerMethod(any, "to_display_text", AnyToDisplayTextMethodGen.makeFunction(language)); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/ArgumentDefinition.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/ArgumentDefinition.java index 5ad4f90900e8..356d0708c603 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/ArgumentDefinition.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/ArgumentDefinition.java @@ -4,7 +4,7 @@ import org.enso.interpreter.node.ExpressionNode; /** Tracks the specifics about how arguments are defined at the callable definition site. */ -public class ArgumentDefinition { +public final class ArgumentDefinition { /** Represents the mode of passing this argument to the function. */ public enum ExecutionMode { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgument.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgument.java index 74c2c259720a..42e48c6dbd15 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgument.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgument.java @@ -3,7 +3,7 @@ import org.enso.interpreter.node.ExpressionNode; /** Tracks the specifics about how arguments are specified at a call site. */ -public class CallArgument { +public final class CallArgument { private final String name; private final ExpressionNode expression; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgumentInfo.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgumentInfo.java index 7b5bb47457b5..9d1a174e0fe0 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgumentInfo.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgumentInfo.java @@ -13,7 +13,7 @@ * Tracks simple information about call-site arguments, used to make processing of caller argument * lists much more simple. */ -public class CallArgumentInfo { +public final class CallArgumentInfo { private final String name; /** @@ -228,10 +228,10 @@ public FunctionSchema getPostApplicationSchema() { * A class that represents the partitioned mapping of the arguments applied to a given callable. */ public static class ArgumentMapping { - private @CompilationFinal(dimensions = 1) int[] appliedArgumentMapping; - private @CompilationFinal(dimensions = 1) int[] oversaturatedArgumentMapping; - private @CompilationFinal(dimensions = 1) boolean[] isValidAppliedArg; - private @CompilationFinal(dimensions = 1) boolean[] argumentShouldExecute; + private final @CompilationFinal(dimensions = 1) int[] appliedArgumentMapping; + private final @CompilationFinal(dimensions = 1) int[] oversaturatedArgumentMapping; + private final @CompilationFinal(dimensions = 1) boolean[] isValidAppliedArg; + private final @CompilationFinal(dimensions = 1) boolean[] argumentShouldExecute; private final FunctionSchema postApplicationSchema; /** @@ -245,7 +245,7 @@ public static class ArgumentMapping { * the callable * @param postApplicationSchema the schema resulting from applying this mapping */ - public ArgumentMapping( + private ArgumentMapping( int[] appliedArgumentMapping, int[] oversaturatedArgumentMapping, boolean[] isAppliedFlags, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java index 066601f1443c..94362227ba34 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/FunctionSchema.java @@ -10,7 +10,7 @@ * Holds the definition site argument information together with information on the partially applied * arguments positions. */ -public class FunctionSchema { +public final class FunctionSchema { /** Denotes the caller frame access functions with this schema require to run properly. */ public enum CallerFrameAccess { /** Requires full access to the (materialized) caller frame. */ @@ -38,7 +38,7 @@ public boolean shouldFrameBePassed() { private final boolean hasOversaturatedArguments; private final CallerFrameAccess callerFrameAccess; - public final boolean isFullyApplied; + private final boolean isFullyApplied; /** * Creates an {@link FunctionSchema} instance. diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala index 135d57e95d77..40984dab858a 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala @@ -91,11 +91,11 @@ class CurryingTest extends InterpreterTest { "allow to pass suspended functions in arguments with `...`" in { val code = """main = - | foo f = f 1 + | foo f = f 2 | bar x=1 = x + 1 | foo (bar ...) |""".stripMargin - eval(code) shouldEqual 2 + eval(code) shouldEqual 3 } "allow to pass suspended functions in arguments with `...` but still auto-execute them" in { From 49b15759b3ba17ca2a953a2b43b7ca2a7537cde3 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 13:37:13 +0200 Subject: [PATCH 08/13] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd7575a3bc30..c5965c127fbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -185,6 +185,7 @@ - [Fixed compiler issue related to module cache.][3367] - [Fixed execution of defaulted arguments of Atom Constructors][3358] - [Converting Enso Date to java.time.LocalDate and back][3374] +- [Functions with all-defaulted arguments now execute automatically][3414] [3227]: https://github.com/enso-org/enso/pull/3227 [3248]: https://github.com/enso-org/enso/pull/3248 @@ -195,6 +196,7 @@ [3367]: https://github.com/enso-org/enso/pull/3367 [3374]: https://github.com/enso-org/enso/pull/3374 [3412]: https://github.com/enso-org/enso/pull/3412 +[3414]: https://github.com/enso-org/enso/pull/3414 [3417]: https://github.com/enso-org/enso/pull/3417 # Enso 2.0.0-alpha.18 (2021-10-12) From 85c09748deb83b4274dcb48b18c1dd0822a09ea2 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 13:43:28 +0200 Subject: [PATCH 09/13] formatting --- .../enso/compiler/codegen/IrToTruffle.scala | 4 ++-- .../scala/org/enso/compiler/core/IR.scala | 4 ++-- .../pass/analyse/DataflowAnalysisTest.scala | 22 +++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 692821e9a6d9..d72e8cb312de 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -1317,7 +1317,7 @@ class IrToTruffle( def processApplication(application: IR.Application): RuntimeExpression = application match { case IR.Application.Prefix(fn, Nil, true, _, _, _) => - run(fn) + run(fn) case IR.Application.Prefix(fn, args, hasDefaultsSuspended, loc, _, _) => val callArgFactory = new CallArgumentProcessor(scope, scopeName) @@ -1423,7 +1423,7 @@ class IrToTruffle( case _: IR.Name => false case _: IR.Literal.Text => false case _: IR.Literal.Number => false - case _ => true + case _ => true } val childScope = if (shouldSuspend) { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala index 63d0b6253bb0..4268258785dd 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala @@ -5341,8 +5341,8 @@ object IR { override val name: Option[IR.Name], override val value: Expression, override val location: Option[IdentifiedLocation], - override val passData: MetadataStorage = MetadataStorage(), - override val diagnostics: DiagnosticStorage = DiagnosticStorage() + override val passData: MetadataStorage = MetadataStorage(), + override val diagnostics: DiagnosticStorage = DiagnosticStorage() ) extends CallArgument with IRKind.Primitive { override protected var id: Identifier = randomId diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala index 59f04c988a90..c10b7a034cad 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala @@ -936,8 +936,8 @@ class DataflowAnalysisTest extends CompilerTest { fn.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified] val fnArgY = fn.arguments(1).asInstanceOf[IR.DefinitionArgument.Specified] - val fnBody = fn.body.asInstanceOf[IR.Application.Prefix] - val plusFn = fnBody.function.asInstanceOf[IR.Name.Literal] + val fnBody = fn.body.asInstanceOf[IR.Application.Prefix] + val plusFn = fnBody.function.asInstanceOf[IR.Name.Literal] val plusArgX = fnBody.arguments.head.asInstanceOf[IR.CallArgument.Specified] val plusArgXExpr = plusArgX.value.asInstanceOf[IR.Name.Literal] @@ -946,15 +946,15 @@ class DataflowAnalysisTest extends CompilerTest { val plusArgYExpr = plusArgY.value.asInstanceOf[IR.Name.Literal] // Identifiers - val fnId = mkStaticDep(fn.getId) - val fnArgXId = mkStaticDep(fnArgX.getId) - val fnArgYId = mkStaticDep(fnArgY.getId) - val fnBodyId = mkStaticDep(fnBody.getId) - val plusFnId = mkStaticDep(plusFn.getId) - val plusArgXId = mkStaticDep(plusArgX.getId) - val plusArgXExprId = mkStaticDep(plusArgXExpr.getId) - val plusArgYId = mkStaticDep(plusArgY.getId) - val plusArgYExprId = mkStaticDep(plusArgYExpr.getId) + val fnId = mkStaticDep(fn.getId) + val fnArgXId = mkStaticDep(fnArgX.getId) + val fnArgYId = mkStaticDep(fnArgY.getId) + val fnBodyId = mkStaticDep(fnBody.getId) + val plusFnId = mkStaticDep(plusFn.getId) + val plusArgXId = mkStaticDep(plusArgX.getId) + val plusArgXExprId = mkStaticDep(plusArgXExpr.getId) + val plusArgYId = mkStaticDep(plusArgY.getId) + val plusArgYExprId = mkStaticDep(plusArgYExpr.getId) // Dynamic Symbols val plusSym = mkDynamicDep("+") From 6a49839a007df7f7ffdcd77eb2eb3da32a450ed2 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 13:44:44 +0200 Subject: [PATCH 10/13] remove old builtin --- engine/runtime/src/main/resources/Builtins.enso | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/engine/runtime/src/main/resources/Builtins.enso b/engine/runtime/src/main/resources/Builtins.enso index 189f0e47fc8a..4fd145412268 100644 --- a/engine/runtime/src/main/resources/Builtins.enso +++ b/engine/runtime/src/main/resources/Builtins.enso @@ -470,23 +470,6 @@ type Function @Builtin_Type type Function - ## Forces evaluation of a fully-applied but not evaluated function. - - This is particularly useful when you want to call a function that is - fully applied with default arguments. - - This is usually _not_ the correct solution to a problem, and so should be - used sparingly. - - > Example - Evaluate a fully applied function to get 4. - - example_call = - f (a = 2) = a * a - f.call - call : Any - call = @Builtin_Method "Function.call" - ## Generic utilities for interacting with other languages. type Polyglot From 5f083b588d0567e6e050a112d37df1ac30e03e6d Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 13:58:00 +0200 Subject: [PATCH 11/13] format --- .../src/main/java/org/enso/interpreter/node/BaseNode.java | 2 +- .../java/org/enso/interpreter/runtime/builtin/Builtins.java | 2 +- .../interpreter/runtime/callable/argument/CallArgumentInfo.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/BaseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/BaseNode.java index b88c9f68b338..afa17792d2b8 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/BaseNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/BaseNode.java @@ -20,7 +20,7 @@ public enum TailStatus { /** Node is not in a tail position. */ NOT_TAIL; - private final static int NUMBER_OF_VALUES = values().length; + private static final int NUMBER_OF_VALUES = values().length; public static int numberOfValues() { return NUMBER_OF_VALUES; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java index 62dd7fa90aaa..30ae6a62f59b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java @@ -190,7 +190,7 @@ public Builtins(Context context) { scope.registerMethod(debug, MethodNames.Debug.EVAL, DebugEvalMethodGen.makeFunction(language)); scope.registerMethod(debug, "breakpoint", DebugBreakpointMethodGen.makeFunction(language)); - + scope.registerMethod(any, "to_text", AnyToTextMethodGen.makeFunction(language)); scope.registerMethod(any, "to_display_text", AnyToDisplayTextMethodGen.makeFunction(language)); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgumentInfo.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgumentInfo.java index 9d1a174e0fe0..904055d1e22a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgumentInfo.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/CallArgumentInfo.java @@ -245,7 +245,7 @@ public static class ArgumentMapping { * the callable * @param postApplicationSchema the schema resulting from applying this mapping */ - private ArgumentMapping( + private ArgumentMapping( int[] appliedArgumentMapping, int[] oversaturatedArgumentMapping, boolean[] isAppliedFlags, From 8e13b03d80987d0f43a8286c1a7edff2784decf4 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 14:20:33 +0200 Subject: [PATCH 12/13] TIL: non-compiling code is frowned upon in this repository. --- .../interpreter/node/callable/thunk/ThunkExecutorNode.java | 4 ++-- .../java/org/enso/interpreter/runtime/builtin/Builtins.java | 1 - .../org/enso/interpreter/test/semantic/CurryingTest.scala | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java index ae5cfef4665b..0881ef7d6a8b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java @@ -47,13 +47,13 @@ boolean sameCallTarget(DirectCallNode callNode, Function function) { } @Specialization( - guards = {"function.isThunk()", "sameCallTarget(callNode, thunk)"}, + guards = {"function.isThunk()", "sameCallTarget(callNode, function)"}, limit = Constants.CacheSizes.THUNK_EXECUTOR_NODE) Stateful doCached( Function function, Object state, BaseNode.TailStatus isTail, - @Cached("create(thunk.getCallTarget())") DirectCallNode callNode, + @Cached("create(function.getCallTarget())") DirectCallNode callNode, @Cached LoopingCallOptimiserNode loopingCallOptimiserNode) { CompilerAsserts.partialEvaluationConstant(isTail); if (isTail != BaseNode.TailStatus.NOT_TAIL) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java index 30ae6a62f59b..82d9567eb814 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java @@ -16,7 +16,6 @@ import org.enso.interpreter.node.expression.builtin.error.CaughtPanicConvertToDataflowErrorMethodGen; import org.enso.interpreter.node.expression.builtin.error.GetAttachedStackTraceMethodGen; import org.enso.interpreter.node.expression.builtin.error.ThrowPanicMethodGen; -import org.enso.interpreter.node.expression.builtin.function.ExplicitCallFunctionMethodGen; import org.enso.interpreter.node.expression.builtin.interop.java.AddToClassPathMethodGen; import org.enso.interpreter.node.expression.builtin.interop.java.LookupClassMethodGen; import org.enso.interpreter.node.expression.builtin.io.GetCwdMethodGen; diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala index 40984dab858a..deaa966d0672 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala @@ -44,7 +44,7 @@ class CurryingTest extends InterpreterTest { |main = | fn = w -> x -> (y = 10) -> (z = 20) -> w + x + y + z | - | fn.call 1 2 (z = 10) + | fn 1 2 (z = 10) |""".stripMargin eval(code) shouldEqual 23 From c65d38408fde4adf4768896887b86103f79d8468 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 15:16:14 +0200 Subject: [PATCH 13/13] TIL: failing tests are frowned upon in this repo. --- .../interpreter/test/semantic/LambdaShorthandArgsTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/LambdaShorthandArgsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/LambdaShorthandArgsTest.scala index 018d391b72cb..45c32a4303f7 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/LambdaShorthandArgsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/LambdaShorthandArgsTest.scala @@ -85,7 +85,7 @@ class LambdaShorthandArgsTest extends InterpreterTest { """ |main = | f = (x = _) -> x - | g = f.call + | g = f | h = _ | res1 = g 10 | res2 = h 10