From 96a0c92c8b74d6d327e5584d2d69a83d79438e7c Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Wed, 27 Apr 2022 19:57:00 +0200 Subject: [PATCH] Automatically force all-defaulted functions (#3414) This changes the interpreter to treat functions with all-defaulted args as thunks. Seems to have no performance impact in compiled code. --- CHANGELOG.md | 2 + .../org/enso/interpreter/node/BaseNode.java | 8 +- .../node/callable/InvokeCallableNode.java | 2 - .../argument/IndirectArgumentSorterNode.java | 5 +- .../callable/argument/ReadArgumentNode.java | 8 +- .../node/callable/thunk/CreateThunkNode.java | 4 +- .../node/callable/thunk/ForceNode.java | 1 - .../callable/thunk/ThunkExecutorNode.java | 83 +++++++++++--- .../builtin/bool/IfThenElseNode.java | 1 - .../builtin/function/ApplicationOperator.java | 1 - .../function/ExplicitCallFunctionNode.java | 32 ------ .../builtin/meta/NewAtomInstanceNode.java | 1 - .../builtin/runtime/NoInlineNode.java | 1 - .../thread/WithInterruptHandlerNode.java | 1 - .../node/expression/debug/EvalNode.java | 6 +- .../interpreter/runtime/builtin/Builtins.java | 3 - .../callable/argument/ArgumentDefinition.java | 2 +- .../callable/argument/CallArgument.java | 2 +- .../callable/argument/CallArgumentInfo.java | 12 +- .../runtime/callable/argument/Thunk.java | 40 ------- .../runtime/callable/function/Function.java | 15 ++- .../callable/function/FunctionSchema.java | 11 +- .../enso/interpreter/runtime/type/Types.java | 5 - .../runtime/src/main/resources/Builtins.enso | 17 --- .../enso/compiler/codegen/IrToTruffle.scala | 10 +- .../scala/org/enso/compiler/core/IR.scala | 27 +---- .../compiler/pass/analyse/AliasAnalysis.scala | 2 +- .../pass/analyse/DataflowAnalysis.scala | 2 +- .../pass/analyse/DemandAnalysis.scala | 107 ++++-------------- .../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/DataflowAnalysisTest.scala | 99 ++-------------- .../pass/analyse/DemandAnalysisTest.scala | 94 +-------------- .../test/semantic/CurryingTest.scala | 61 +++++++++- .../semantic/LambdaShorthandArgsTest.scala | 2 +- 38 files changed, 236 insertions(+), 461 deletions(-) delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ExplicitCallFunctionNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/Thunk.java diff --git a/CHANGELOG.md b/CHANGELOG.md index dd7575a3bc3..c5965c127fb 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) 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 35c6cbc47f5..afa17792d2b 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 abstract class BaseNode extends Node { /** 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 static final 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/InvokeCallableNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java index 34974bc3c5c..a83071f5cb0 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.Fallback; 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; @@ -17,7 +16,6 @@ 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.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 0e4fb06e22a..311951b0ea9 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 @@ public abstract class IndirectArgumentSorterNode extends Node { 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/argument/ReadArgumentNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentNode.java index aab241c1b0d..2514412f2d0 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 class ReadArgumentNode extends ExpressionNode { */ @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/CreateThunkNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/CreateThunkNode.java index 0ef1b7681e6..2971231b3ae 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.RootCallTarget; 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 class CreateThunkNode extends ExpressionNode { */ @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 cb4772a7e7f..f669ffe7563 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.FrameUtil; 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 8669e5dd144..0881ef7d6a8 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,12 +7,14 @@ import com.oracle.truffle.api.nodes.IndirectCallNode; 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.Thunk; +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 @@ -40,30 +42,25 @@ public abstract class ThunkExecutorNode extends Node { */ 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 = {"function.isThunk()", "sameCallTarget(callNode, function)"}, limit = Constants.CacheSizes.THUNK_EXECUTOR_NODE) Stateful doCached( - Thunk thunk, + 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) { - 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()); @@ -71,9 +68,9 @@ public abstract class ThunkExecutorNode extends Node { } } - @Specialization(replaces = "doCached") + @Specialization(replaces = "doCached", guards = "function.isThunk()") Stateful doUncached( - Thunk thunk, + Function function, Object state, BaseNode.TailStatus isTail, @Cached IndirectCallNode callNode, @@ -81,16 +78,66 @@ public abstract class ThunkExecutorNode extends Node { 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()); } } } + + static InvokeFunctionNode buildInvokeFunctionNode(BaseNode.TailStatus tailStatus) { + var node = + InvokeFunctionNode.build( + new CallArgumentInfo[0], + InvokeCallableNode.DefaultsExecutionMode.EXECUTE, + InvokeCallableNode.ArgumentsExecutionMode.EXECUTE); + node.setTailStatus(tailStatus); + return node; + } + + static int numberOfTailStatuses() { + return BaseNode.TailStatus.numberOfValues(); + } + + @Specialization( + guards = {"!fn.isThunk()", "fn.isFullyApplied()", "isTail == cachedIsTail"}, + limit = "numberOfTailStatuses()") + 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/node/expression/builtin/bool/IfThenElseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java index f4d860a8a09..8cf4a7a0e82 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.MonadicState; 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 12d2a5bc711..6b13615141d 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.dsl.Suspend; 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/function/ExplicitCallFunctionNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ExplicitCallFunctionNode.java deleted file mode 100644 index fd8a9652dbd..00000000000 --- 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/node/expression/builtin/meta/NewAtomInstanceNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/NewAtomInstanceNode.java index b1d42bd2a21..9c1ae5585a2 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.dsl.BuiltinMethod; 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 07d8f384a76..582e813cc15 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.MonadicState; 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 5040f68c645..437f1678433 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.MonadicState; 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 9e426bf59fa..5ea378ef6a1 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.callable.thunk.ThunkExecutorNode; 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 @@ public abstract class EvalNode extends BaseNode { "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 @@ public abstract class EvalNode extends BaseNode { 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/builtin/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java index 9d7a170e69a..82d9567eb81 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.CatchPanicMethodGen; 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; @@ -191,8 +190,6 @@ public class Builtins { 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 5ad4f90900e..356d0708c60 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 java.util.Optional; 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 74c2c259720..42e48c6dbd1 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 @@ package org.enso.interpreter.runtime.callable.argument; 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 7b5bb47457b..904055d1e22 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 @@ import java.util.stream.IntStream; * 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 class CallArgumentInfo { * 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 class CallArgumentInfo { * 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/argument/Thunk.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/argument/Thunk.java deleted file mode 100644 index 1aeddcb5fbc..00000000000 --- 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 67442963a59..ab7d28de428 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.CallerInfo; 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 final class Function implements TruffleObject { 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 final class Function implements TruffleObject { * @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,12 @@ public final class Function implements TruffleObject { return function; } } + + 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 770e467470d..94362227ba3 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 @@ import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; * 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. */ @@ -29,6 +29,8 @@ public class FunctionSchema { } } + 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; @@ -36,6 +38,8 @@ public class FunctionSchema { private final boolean hasOversaturatedArguments; private final CallerFrameAccess callerFrameAccess; + private final boolean isFullyApplied; + /** * Creates an {@link FunctionSchema} instance. * @@ -66,6 +70,7 @@ public class FunctionSchema { this.hasAnyPreApplied = hasAnyPreApplied; this.hasOversaturatedArguments = this.oversaturatedArguments.length > 0; + this.isFullyApplied = isFullyApplied(InvokeCallableNode.DefaultsExecutionMode.EXECUTE); } /** @@ -211,4 +216,8 @@ public class FunctionSchema { } return functionIsFullyApplied; } + + public boolean isFullyApplied() { + return isFullyApplied; + } } 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 9e6bc5d78a4..ab8f33db880 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 @@ package org.enso.interpreter.runtime.type; 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 @@ import org.enso.polyglot.data.TypeGraph; Function.class, Atom.class, AtomConstructor.class, - Thunk.class, DataflowError.class, UnresolvedConversion.class, UnresolvedSymbol.class, @@ -124,8 +121,6 @@ public class Types { 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)) { diff --git a/engine/runtime/src/main/resources/Builtins.enso b/engine/runtime/src/main/resources/Builtins.enso index 189f0e47fc8..4fd14541226 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 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 b24901abfbc..d72e8cb312d 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,6 +1316,8 @@ 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, _, _) => val callArgFactory = new CallArgumentProcessor(scope, scopeName) @@ -1407,7 +1409,6 @@ class IrToTruffle( name, value, _, - shouldBeSuspended, _, _ ) => @@ -1422,12 +1423,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 270cacea613..4268258785d 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,21 +5331,18 @@ 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() + override val passData: MetadataStorage = MetadataStorage(), + override val diagnostics: DiagnosticStorage = DiagnosticStorage() ) extends CallArgument with IRKind.Primitive { override protected var id: Identifier = randomId @@ -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 d80b4f31fc4..23b4dc96a7a 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 32bd34890a3..cd520e4178a 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 09f1cc1453b..83403bc0f32 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 = isUsageOfSuspendedTerm(name) - 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 @@ -227,11 +207,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, _, _, _) => @@ -261,43 +242,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 @@ -309,13 +253,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 6442279f1bc..df4b6cc6d70 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 af56e456ac7..9ccfe3f69cd 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 1a528f9d039..4f6e96567d8 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 f5ea8683291..72b37f41d55 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 b2947adbb26..776554374a9 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/DataflowAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala index 4ef8507bd9a..c10b7a034ca 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,9 +936,8 @@ 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 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] @@ -947,16 +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 fnArgYDefaultId = mkStaticDep(fnArgYDefault.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("+") @@ -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 30fd710b971..3a70bf6fb07 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,79 +157,9 @@ 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] } - - "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 { - 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] - } - - "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 +210,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 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 5c369ee00ab..deaa966d067 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 @@ -45,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 @@ -74,11 +73,63 @@ 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 + } + + "allow to pass suspended functions in arguments with `...`" in { + val code = + """main = + | foo f = f 2 + | bar x=1 = x + 1 + | foo (bar ...) + |""".stripMargin + eval(code) shouldEqual 3 + } + + "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" + } + } } 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 018d391b72c..45c32a4303f 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