mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 21:12:44 +03:00
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.
This commit is contained in:
parent
bb6a5bac02
commit
96a0c92c8b
@ -185,6 +185,7 @@
|
|||||||
- [Fixed compiler issue related to module cache.][3367]
|
- [Fixed compiler issue related to module cache.][3367]
|
||||||
- [Fixed execution of defaulted arguments of Atom Constructors][3358]
|
- [Fixed execution of defaulted arguments of Atom Constructors][3358]
|
||||||
- [Converting Enso Date to java.time.LocalDate and back][3374]
|
- [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
|
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||||
@ -195,6 +196,7 @@
|
|||||||
[3367]: https://github.com/enso-org/enso/pull/3367
|
[3367]: https://github.com/enso-org/enso/pull/3367
|
||||||
[3374]: https://github.com/enso-org/enso/pull/3374
|
[3374]: https://github.com/enso-org/enso/pull/3374
|
||||||
[3412]: https://github.com/enso-org/enso/pull/3412
|
[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
|
[3417]: https://github.com/enso-org/enso/pull/3417
|
||||||
|
|
||||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||||
|
@ -18,7 +18,13 @@ public abstract class BaseNode extends Node {
|
|||||||
/** Node is in a tail position and marked as a tail call. */
|
/** Node is in a tail position and marked as a tail call. */
|
||||||
TAIL_LOOP,
|
TAIL_LOOP,
|
||||||
/** Node is not in a tail position. */
|
/** 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;
|
private @CompilationFinal TailStatus tailStatus = TailStatus.NOT_TAIL;
|
||||||
|
@ -5,7 +5,6 @@ import com.oracle.truffle.api.dsl.Fallback;
|
|||||||
import com.oracle.truffle.api.dsl.Specialization;
|
import com.oracle.truffle.api.dsl.Specialization;
|
||||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
|
||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.locks.Lock;
|
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.UnresolvedConversion;
|
||||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
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.Atom;
|
||||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||||
import org.enso.interpreter.runtime.callable.function.Function;
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
|
@ -41,10 +41,9 @@ public abstract class IndirectArgumentSorterNode extends Node {
|
|||||||
Object state,
|
Object state,
|
||||||
ThunkExecutorNode thunkExecutorNode) {
|
ThunkExecutorNode thunkExecutorNode) {
|
||||||
for (int i = 0; i < mapping.getArgumentShouldExecute().length; i++) {
|
for (int i = 0; i < mapping.getArgumentShouldExecute().length; i++) {
|
||||||
if (TypesGen.isThunk(arguments[i]) && mapping.getArgumentShouldExecute()[i]) {
|
if (mapping.getArgumentShouldExecute()[i]) {
|
||||||
Stateful result =
|
Stateful result =
|
||||||
thunkExecutorNode.executeThunk(
|
thunkExecutorNode.executeThunk(arguments[i], state, BaseNode.TailStatus.NOT_TAIL);
|
||||||
TypesGen.asThunk(arguments[i]), state, BaseNode.TailStatus.NOT_TAIL);
|
|
||||||
arguments[i] = result.getValue();
|
arguments[i] = result.getValue();
|
||||||
state = result.getState();
|
state = result.getState();
|
||||||
}
|
}
|
||||||
|
@ -45,17 +45,17 @@ public class ReadArgumentNode extends ExpressionNode {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object executeGeneric(VirtualFrame frame) {
|
public Object executeGeneric(VirtualFrame frame) {
|
||||||
Object argument = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[index];
|
Object arguments[] = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments());
|
||||||
|
|
||||||
if (defaultValue == null) {
|
if (defaultValue == null) {
|
||||||
return argument;
|
return arguments[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note [Handling Argument Defaults]
|
// Note [Handling Argument Defaults]
|
||||||
if (defaultingProfile.profile(argument == null)) {
|
if (defaultingProfile.profile(arguments.length <= index || arguments[index] == null)) {
|
||||||
return defaultValue.executeGeneric(frame);
|
return defaultValue.executeGeneric(frame);
|
||||||
} else {
|
} else {
|
||||||
return argument;
|
return arguments[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import com.oracle.truffle.api.RootCallTarget;
|
|||||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
import org.enso.interpreter.node.ExpressionNode;
|
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. */
|
/** 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")
|
@NodeInfo(shortName = "CreateThunk", description = "Wraps a call target in a thunk at runtime")
|
||||||
@ -34,6 +34,6 @@ public class CreateThunkNode extends ExpressionNode {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Object executeGeneric(VirtualFrame frame) {
|
public Object executeGeneric(VirtualFrame frame) {
|
||||||
return new Thunk(this.callTarget, frame.materialize());
|
return Function.thunk(this.callTarget, frame.materialize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import com.oracle.truffle.api.frame.FrameUtil;
|
|||||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
import org.enso.interpreter.node.ExpressionNode;
|
import org.enso.interpreter.node.ExpressionNode;
|
||||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
|
||||||
import org.enso.interpreter.runtime.state.Stateful;
|
import org.enso.interpreter.runtime.state.Stateful;
|
||||||
|
|
||||||
/** Node responsible for handling user-requested thunks forcing. */
|
/** Node responsible for handling user-requested thunks forcing. */
|
||||||
|
@ -7,12 +7,14 @@ import com.oracle.truffle.api.nodes.IndirectCallNode;
|
|||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
import org.enso.interpreter.Constants;
|
import org.enso.interpreter.Constants;
|
||||||
import org.enso.interpreter.node.BaseNode;
|
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.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.callable.function.Function;
|
||||||
import org.enso.interpreter.runtime.control.TailCallException;
|
import org.enso.interpreter.runtime.control.TailCallException;
|
||||||
import org.enso.interpreter.runtime.state.Stateful;
|
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. */
|
/** Node responsible for executing (forcing) thunks passed to it as runtime values. */
|
||||||
@GenerateUncached
|
@GenerateUncached
|
||||||
@ -40,30 +42,25 @@ public abstract class ThunkExecutorNode extends Node {
|
|||||||
*/
|
*/
|
||||||
public abstract Stateful executeThunk(Object thunk, Object state, BaseNode.TailStatus isTail);
|
public abstract Stateful executeThunk(Object thunk, Object state, BaseNode.TailStatus isTail);
|
||||||
|
|
||||||
static boolean isThunk(Object th) {
|
boolean sameCallTarget(DirectCallNode callNode, Function function) {
|
||||||
return TypesGen.isThunk(th);
|
return function.getCallTarget() == callNode.getCallTarget();
|
||||||
}
|
|
||||||
|
|
||||||
@Specialization(guards = "!isThunk(thunk)")
|
|
||||||
Stateful doOther(Object thunk, Object state, BaseNode.TailStatus isTail) {
|
|
||||||
return new Stateful(state, thunk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(
|
@Specialization(
|
||||||
guards = "callNode.getCallTarget() == thunk.getCallTarget()",
|
guards = {"function.isThunk()", "sameCallTarget(callNode, function)"},
|
||||||
limit = Constants.CacheSizes.THUNK_EXECUTOR_NODE)
|
limit = Constants.CacheSizes.THUNK_EXECUTOR_NODE)
|
||||||
Stateful doCached(
|
Stateful doCached(
|
||||||
Thunk thunk,
|
Function function,
|
||||||
Object state,
|
Object state,
|
||||||
BaseNode.TailStatus isTail,
|
BaseNode.TailStatus isTail,
|
||||||
@Cached("create(thunk.getCallTarget())") DirectCallNode callNode,
|
@Cached("create(function.getCallTarget())") DirectCallNode callNode,
|
||||||
@Cached LoopingCallOptimiserNode loopingCallOptimiserNode) {
|
@Cached LoopingCallOptimiserNode loopingCallOptimiserNode) {
|
||||||
CompilerAsserts.partialEvaluationConstant(isTail);
|
CompilerAsserts.partialEvaluationConstant(isTail);
|
||||||
if (isTail != BaseNode.TailStatus.NOT_TAIL) {
|
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 {
|
} else {
|
||||||
try {
|
try {
|
||||||
return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
|
return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(function, state));
|
||||||
} catch (TailCallException e) {
|
} catch (TailCallException e) {
|
||||||
return loopingCallOptimiserNode.executeDispatch(
|
return loopingCallOptimiserNode.executeDispatch(
|
||||||
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
|
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(
|
Stateful doUncached(
|
||||||
Thunk thunk,
|
Function function,
|
||||||
Object state,
|
Object state,
|
||||||
BaseNode.TailStatus isTail,
|
BaseNode.TailStatus isTail,
|
||||||
@Cached IndirectCallNode callNode,
|
@Cached IndirectCallNode callNode,
|
||||||
@ -81,16 +78,66 @@ public abstract class ThunkExecutorNode extends Node {
|
|||||||
if (isTail != BaseNode.TailStatus.NOT_TAIL) {
|
if (isTail != BaseNode.TailStatus.NOT_TAIL) {
|
||||||
return (Stateful)
|
return (Stateful)
|
||||||
callNode.call(
|
callNode.call(
|
||||||
thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state));
|
function.getCallTarget(), Function.ArgumentsHelper.buildArguments(function, state));
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return (Stateful)
|
return (Stateful)
|
||||||
callNode.call(
|
callNode.call(
|
||||||
thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state));
|
function.getCallTarget(), Function.ArgumentsHelper.buildArguments(function, state));
|
||||||
} catch (TailCallException e) {
|
} catch (TailCallException e) {
|
||||||
return loopingCallOptimiserNode.executeDispatch(
|
return loopingCallOptimiserNode.executeDispatch(
|
||||||
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import org.enso.interpreter.dsl.MonadicState;
|
|||||||
import org.enso.interpreter.dsl.Suspend;
|
import org.enso.interpreter.dsl.Suspend;
|
||||||
import org.enso.interpreter.node.BaseNode;
|
import org.enso.interpreter.node.BaseNode;
|
||||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
|
||||||
import org.enso.interpreter.runtime.state.Stateful;
|
import org.enso.interpreter.runtime.state.Stateful;
|
||||||
|
|
||||||
@BuiltinMethod(
|
@BuiltinMethod(
|
||||||
|
@ -8,7 +8,6 @@ import org.enso.interpreter.dsl.Suspend;
|
|||||||
import org.enso.interpreter.node.BaseNode;
|
import org.enso.interpreter.node.BaseNode;
|
||||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
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.callable.function.Function;
|
||||||
import org.enso.interpreter.runtime.state.Stateful;
|
import org.enso.interpreter.runtime.state.Stateful;
|
||||||
|
|
||||||
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.Atom;
|
||||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||||
import org.enso.interpreter.runtime.data.Array;
|
import org.enso.interpreter.runtime.data.Array;
|
||||||
import org.enso.interpreter.runtime.type.TypesGen;
|
|
||||||
|
|
||||||
@BuiltinMethod(
|
@BuiltinMethod(
|
||||||
type = "Meta",
|
type = "Meta",
|
||||||
|
@ -7,7 +7,6 @@ import org.enso.interpreter.dsl.MonadicState;
|
|||||||
import org.enso.interpreter.dsl.Suspend;
|
import org.enso.interpreter.dsl.Suspend;
|
||||||
import org.enso.interpreter.node.BaseNode;
|
import org.enso.interpreter.node.BaseNode;
|
||||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
|
||||||
import org.enso.interpreter.runtime.state.Stateful;
|
import org.enso.interpreter.runtime.state.Stateful;
|
||||||
|
|
||||||
@BuiltinMethod(
|
@BuiltinMethod(
|
||||||
|
@ -6,7 +6,6 @@ import org.enso.interpreter.dsl.MonadicState;
|
|||||||
import org.enso.interpreter.dsl.Suspend;
|
import org.enso.interpreter.dsl.Suspend;
|
||||||
import org.enso.interpreter.node.BaseNode;
|
import org.enso.interpreter.node.BaseNode;
|
||||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
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.control.ThreadInterruptedException;
|
||||||
import org.enso.interpreter.runtime.state.Stateful;
|
import org.enso.interpreter.runtime.state.Stateful;
|
||||||
|
|
||||||
|
@ -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.node.expression.builtin.text.util.ToJavaStringNode;
|
||||||
import org.enso.interpreter.runtime.Context;
|
import org.enso.interpreter.runtime.Context;
|
||||||
import org.enso.interpreter.runtime.callable.CallerInfo;
|
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.data.text.Text;
|
||||||
import org.enso.interpreter.runtime.scope.LocalScope;
|
import org.enso.interpreter.runtime.scope.LocalScope;
|
||||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||||
@ -98,7 +98,7 @@ public abstract class EvalNode extends BaseNode {
|
|||||||
"parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expressionStr)")
|
"parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expressionStr)")
|
||||||
RootCallTarget cachedCallTarget,
|
RootCallTarget cachedCallTarget,
|
||||||
@Cached("build()") ThunkExecutorNode thunkExecutorNode) {
|
@Cached("build()") ThunkExecutorNode thunkExecutorNode) {
|
||||||
Thunk thunk = new Thunk(cachedCallTarget, callerInfo.getFrame());
|
Function thunk = Function.thunk(cachedCallTarget, callerInfo.getFrame());
|
||||||
return thunkExecutorNode.executeThunk(thunk, state, getTailStatus());
|
return thunkExecutorNode.executeThunk(thunk, state, getTailStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ public abstract class EvalNode extends BaseNode {
|
|||||||
callerInfo.getLocalScope(),
|
callerInfo.getLocalScope(),
|
||||||
callerInfo.getModuleScope(),
|
callerInfo.getModuleScope(),
|
||||||
toJavaStringNode.execute(expression));
|
toJavaStringNode.execute(expression));
|
||||||
Thunk thunk = new Thunk(callTarget, callerInfo.getFrame());
|
Function thunk = Function.thunk(callTarget, callerInfo.getFrame());
|
||||||
return thunkExecutorNode.executeThunk(thunk, state, getTailStatus());
|
return thunkExecutorNode.executeThunk(thunk, state, getTailStatus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.CaughtPanicConvertToDataflowErrorMethodGen;
|
||||||
import org.enso.interpreter.node.expression.builtin.error.GetAttachedStackTraceMethodGen;
|
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.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.AddToClassPathMethodGen;
|
||||||
import org.enso.interpreter.node.expression.builtin.interop.java.LookupClassMethodGen;
|
import org.enso.interpreter.node.expression.builtin.interop.java.LookupClassMethodGen;
|
||||||
import org.enso.interpreter.node.expression.builtin.io.GetCwdMethodGen;
|
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, MethodNames.Debug.EVAL, DebugEvalMethodGen.makeFunction(language));
|
||||||
scope.registerMethod(debug, "breakpoint", DebugBreakpointMethodGen.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_text", AnyToTextMethodGen.makeFunction(language));
|
||||||
scope.registerMethod(any, "to_display_text", AnyToDisplayTextMethodGen.makeFunction(language));
|
scope.registerMethod(any, "to_display_text", AnyToDisplayTextMethodGen.makeFunction(language));
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import java.util.Optional;
|
|||||||
import org.enso.interpreter.node.ExpressionNode;
|
import org.enso.interpreter.node.ExpressionNode;
|
||||||
|
|
||||||
/** Tracks the specifics about how arguments are defined at the callable definition site. */
|
/** 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. */
|
/** Represents the mode of passing this argument to the function. */
|
||||||
public enum ExecutionMode {
|
public enum ExecutionMode {
|
||||||
|
@ -3,7 +3,7 @@ package org.enso.interpreter.runtime.callable.argument;
|
|||||||
import org.enso.interpreter.node.ExpressionNode;
|
import org.enso.interpreter.node.ExpressionNode;
|
||||||
|
|
||||||
/** Tracks the specifics about how arguments are specified at a call site. */
|
/** 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 String name;
|
||||||
private final ExpressionNode expression;
|
private final ExpressionNode expression;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import java.util.stream.IntStream;
|
|||||||
* Tracks simple information about call-site arguments, used to make processing of caller argument
|
* Tracks simple information about call-site arguments, used to make processing of caller argument
|
||||||
* lists much more simple.
|
* lists much more simple.
|
||||||
*/
|
*/
|
||||||
public class CallArgumentInfo {
|
public final class CallArgumentInfo {
|
||||||
private final String name;
|
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.
|
* A class that represents the partitioned mapping of the arguments applied to a given callable.
|
||||||
*/
|
*/
|
||||||
public static class ArgumentMapping {
|
public static class ArgumentMapping {
|
||||||
private @CompilationFinal(dimensions = 1) int[] appliedArgumentMapping;
|
private final @CompilationFinal(dimensions = 1) int[] appliedArgumentMapping;
|
||||||
private @CompilationFinal(dimensions = 1) int[] oversaturatedArgumentMapping;
|
private final @CompilationFinal(dimensions = 1) int[] oversaturatedArgumentMapping;
|
||||||
private @CompilationFinal(dimensions = 1) boolean[] isValidAppliedArg;
|
private final @CompilationFinal(dimensions = 1) boolean[] isValidAppliedArg;
|
||||||
private @CompilationFinal(dimensions = 1) boolean[] argumentShouldExecute;
|
private final @CompilationFinal(dimensions = 1) boolean[] argumentShouldExecute;
|
||||||
private final FunctionSchema postApplicationSchema;
|
private final FunctionSchema postApplicationSchema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,7 +245,7 @@ public class CallArgumentInfo {
|
|||||||
* the callable
|
* the callable
|
||||||
* @param postApplicationSchema the schema resulting from applying this mapping
|
* @param postApplicationSchema the schema resulting from applying this mapping
|
||||||
*/
|
*/
|
||||||
public ArgumentMapping(
|
private ArgumentMapping(
|
||||||
int[] appliedArgumentMapping,
|
int[] appliedArgumentMapping,
|
||||||
int[] oversaturatedArgumentMapping,
|
int[] oversaturatedArgumentMapping,
|
||||||
boolean[] isAppliedFlags,
|
boolean[] isAppliedFlags,
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,6 @@ import org.enso.interpreter.runtime.callable.CallerInfo;
|
|||||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
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.callable.atom.AtomConstructor;
|
||||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||||
import org.enso.interpreter.runtime.state.data.EmptyMap;
|
import org.enso.interpreter.runtime.state.data.EmptyMap;
|
||||||
@ -79,6 +78,10 @@ public final class Function implements TruffleObject {
|
|||||||
this(callTarget, scope, schema, null, null);
|
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.
|
* 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
|
* @param state the state to execute the thunk with
|
||||||
* @return an array containing the necessary information to call an Enso thunk
|
* @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]};
|
return new Object[] {thunk.getScope(), null, state, new Object[0]};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,4 +459,12 @@ public final class Function implements TruffleObject {
|
|||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isThunk() {
|
||||||
|
return schema == FunctionSchema.THUNK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFullyApplied() {
|
||||||
|
return schema.isFullyApplied();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
* Holds the definition site argument information together with information on the partially applied
|
||||||
* arguments positions.
|
* arguments positions.
|
||||||
*/
|
*/
|
||||||
public class FunctionSchema {
|
public final class FunctionSchema {
|
||||||
/** Denotes the caller frame access functions with this schema require to run properly. */
|
/** Denotes the caller frame access functions with this schema require to run properly. */
|
||||||
public enum CallerFrameAccess {
|
public enum CallerFrameAccess {
|
||||||
/** Requires full access to the (materialized) caller frame. */
|
/** 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) ArgumentDefinition[] argumentInfos;
|
||||||
private final @CompilationFinal(dimensions = 1) boolean[] hasPreApplied;
|
private final @CompilationFinal(dimensions = 1) boolean[] hasPreApplied;
|
||||||
private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments;
|
private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments;
|
||||||
@ -36,6 +38,8 @@ public class FunctionSchema {
|
|||||||
private final boolean hasOversaturatedArguments;
|
private final boolean hasOversaturatedArguments;
|
||||||
private final CallerFrameAccess callerFrameAccess;
|
private final CallerFrameAccess callerFrameAccess;
|
||||||
|
|
||||||
|
private final boolean isFullyApplied;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link FunctionSchema} instance.
|
* Creates an {@link FunctionSchema} instance.
|
||||||
*
|
*
|
||||||
@ -66,6 +70,7 @@ public class FunctionSchema {
|
|||||||
|
|
||||||
this.hasAnyPreApplied = hasAnyPreApplied;
|
this.hasAnyPreApplied = hasAnyPreApplied;
|
||||||
this.hasOversaturatedArguments = this.oversaturatedArguments.length > 0;
|
this.hasOversaturatedArguments = this.oversaturatedArguments.length > 0;
|
||||||
|
this.isFullyApplied = isFullyApplied(InvokeCallableNode.DefaultsExecutionMode.EXECUTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -211,4 +216,8 @@ public class FunctionSchema {
|
|||||||
}
|
}
|
||||||
return functionIsFullyApplied;
|
return functionIsFullyApplied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFullyApplied() {
|
||||||
|
return isFullyApplied;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,8 @@ package org.enso.interpreter.runtime.type;
|
|||||||
import com.oracle.truffle.api.dsl.TypeSystem;
|
import com.oracle.truffle.api.dsl.TypeSystem;
|
||||||
import com.oracle.truffle.api.interop.ArityException;
|
import com.oracle.truffle.api.interop.ArityException;
|
||||||
import com.oracle.truffle.api.interop.UnsupportedTypeException;
|
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.UnresolvedConversion;
|
||||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
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.Atom;
|
||||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||||
import org.enso.interpreter.runtime.callable.function.Function;
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
@ -40,7 +38,6 @@ import org.enso.polyglot.data.TypeGraph;
|
|||||||
Function.class,
|
Function.class,
|
||||||
Atom.class,
|
Atom.class,
|
||||||
AtomConstructor.class,
|
AtomConstructor.class,
|
||||||
Thunk.class,
|
|
||||||
DataflowError.class,
|
DataflowError.class,
|
||||||
UnresolvedConversion.class,
|
UnresolvedConversion.class,
|
||||||
UnresolvedSymbol.class,
|
UnresolvedSymbol.class,
|
||||||
@ -124,8 +121,6 @@ public class Types {
|
|||||||
return TypesGen.asAtom(value).getConstructor().getQualifiedName().toString();
|
return TypesGen.asAtom(value).getConstructor().getQualifiedName().toString();
|
||||||
} else if (TypesGen.isAtomConstructor(value)) {
|
} else if (TypesGen.isAtomConstructor(value)) {
|
||||||
return TypesGen.asAtomConstructor(value).getQualifiedName().toString();
|
return TypesGen.asAtomConstructor(value).getQualifiedName().toString();
|
||||||
} else if (TypesGen.isThunk(value)) {
|
|
||||||
return Constants.THUNK;
|
|
||||||
} else if (TypesGen.isDataflowError(value)) {
|
} else if (TypesGen.isDataflowError(value)) {
|
||||||
return Constants.ERROR;
|
return Constants.ERROR;
|
||||||
} else if (TypesGen.isUnresolvedSymbol(value) || TypesGen.isUnresolvedConversion(value)) {
|
} else if (TypesGen.isUnresolvedSymbol(value) || TypesGen.isUnresolvedConversion(value)) {
|
||||||
|
@ -470,23 +470,6 @@ type Function
|
|||||||
@Builtin_Type
|
@Builtin_Type
|
||||||
type Function
|
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.
|
## Generic utilities for interacting with other languages.
|
||||||
type Polyglot
|
type Polyglot
|
||||||
|
|
||||||
|
@ -1316,6 +1316,8 @@ class IrToTruffle(
|
|||||||
*/
|
*/
|
||||||
def processApplication(application: IR.Application): RuntimeExpression =
|
def processApplication(application: IR.Application): RuntimeExpression =
|
||||||
application match {
|
application match {
|
||||||
|
case IR.Application.Prefix(fn, Nil, true, _, _, _) =>
|
||||||
|
run(fn)
|
||||||
case IR.Application.Prefix(fn, args, hasDefaultsSuspended, loc, _, _) =>
|
case IR.Application.Prefix(fn, args, hasDefaultsSuspended, loc, _, _) =>
|
||||||
val callArgFactory = new CallArgumentProcessor(scope, scopeName)
|
val callArgFactory = new CallArgumentProcessor(scope, scopeName)
|
||||||
|
|
||||||
@ -1407,7 +1409,6 @@ class IrToTruffle(
|
|||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
_,
|
_,
|
||||||
shouldBeSuspended,
|
|
||||||
_,
|
_,
|
||||||
_
|
_
|
||||||
) =>
|
) =>
|
||||||
@ -1422,12 +1423,7 @@ class IrToTruffle(
|
|||||||
case _: IR.Name => false
|
case _: IR.Name => false
|
||||||
case _: IR.Literal.Text => false
|
case _: IR.Literal.Text => false
|
||||||
case _: IR.Literal.Number => false
|
case _: IR.Literal.Number => false
|
||||||
case _ =>
|
case _ => true
|
||||||
shouldBeSuspended.getOrElse(
|
|
||||||
throw new CompilerError(
|
|
||||||
"Demand analysis information missing from call argument."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val childScope = if (shouldSuspend) {
|
val childScope = if (shouldSuspend) {
|
||||||
|
@ -5310,15 +5310,6 @@ object IR {
|
|||||||
/** The expression of the argument, if present. */
|
/** The expression of the argument, if present. */
|
||||||
val value: Expression
|
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 */
|
/** @inheritdoc */
|
||||||
override def mapExpressions(fn: Expression => Expression): CallArgument
|
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
|
* A [[CallArgument]] where the `value` is an [[IR.Name.Blank]] is a
|
||||||
* representation of a lambda shorthand argument.
|
* representation of a lambda shorthand argument.
|
||||||
*
|
*
|
||||||
* @param name the name of the argument being called, if present
|
* @param name the name of the argument being called, if present
|
||||||
* @param value the expression being passed as the argument's value
|
* @param value the expression being passed as the argument's value
|
||||||
* @param location the source location that the node corresponds to
|
* @param location the source location that the node corresponds to
|
||||||
* @param shouldBeSuspended whether or not the argument should be passed
|
* @param passData the pass metadata associated with this node
|
||||||
* suspended
|
|
||||||
* @param passData the pass metadata associated with this node
|
|
||||||
* @param diagnostics compiler diagnostics for this node
|
* @param diagnostics compiler diagnostics for this node
|
||||||
*/
|
*/
|
||||||
sealed case class Specified(
|
sealed case class Specified(
|
||||||
override val name: Option[IR.Name],
|
override val name: Option[IR.Name],
|
||||||
override val value: Expression,
|
override val value: Expression,
|
||||||
override val location: Option[IdentifiedLocation],
|
override val location: Option[IdentifiedLocation],
|
||||||
override val shouldBeSuspended: Option[Boolean] = None,
|
override val passData: MetadataStorage = MetadataStorage(),
|
||||||
override val passData: MetadataStorage = MetadataStorage(),
|
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
||||||
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
|
||||||
) extends CallArgument
|
) extends CallArgument
|
||||||
with IRKind.Primitive {
|
with IRKind.Primitive {
|
||||||
override protected var id: Identifier = randomId
|
override protected var id: Identifier = randomId
|
||||||
@ -5375,7 +5363,6 @@ object IR {
|
|||||||
name: Option[IR.Name] = name,
|
name: Option[IR.Name] = name,
|
||||||
value: Expression = value,
|
value: Expression = value,
|
||||||
location: Option[IdentifiedLocation] = location,
|
location: Option[IdentifiedLocation] = location,
|
||||||
shouldBeSuspended: Option[Boolean] = shouldBeSuspended,
|
|
||||||
passData: MetadataStorage = passData,
|
passData: MetadataStorage = passData,
|
||||||
diagnostics: DiagnosticStorage = diagnostics,
|
diagnostics: DiagnosticStorage = diagnostics,
|
||||||
id: Identifier = id
|
id: Identifier = id
|
||||||
@ -5384,7 +5371,6 @@ object IR {
|
|||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
location,
|
location,
|
||||||
shouldBeSuspended,
|
|
||||||
passData,
|
passData,
|
||||||
diagnostics
|
diagnostics
|
||||||
)
|
)
|
||||||
@ -5439,7 +5425,6 @@ object IR {
|
|||||||
|name = $name,
|
|name = $name,
|
||||||
|value = $value,
|
|value = $value,
|
||||||
|location = $location,
|
|location = $location,
|
||||||
|shouldBeSuspended = $shouldBeSuspended,
|
|
||||||
|passData = ${this.showPassData},
|
|passData = ${this.showPassData},
|
||||||
|diagnostics = $diagnostics,
|
|diagnostics = $diagnostics,
|
||||||
|id = $id
|
|id = $id
|
||||||
|
@ -493,7 +493,7 @@ case object AliasAnalysis extends IRPass {
|
|||||||
graph: AliasAnalysis.Graph,
|
graph: AliasAnalysis.Graph,
|
||||||
parentScope: AliasAnalysis.Graph.Scope
|
parentScope: AliasAnalysis.Graph.Scope
|
||||||
): List[IR.CallArgument] = {
|
): List[IR.CallArgument] = {
|
||||||
args.map { case arg @ IR.CallArgument.Specified(_, expr, _, _, _, _) =>
|
args.map { case arg @ IR.CallArgument.Specified(_, expr, _, _, _) =>
|
||||||
val currentScope = expr match {
|
val currentScope = expr match {
|
||||||
case _: IR.Literal => parentScope
|
case _: IR.Literal => parentScope
|
||||||
case _ => parentScope.addChild()
|
case _ => parentScope.addChild()
|
||||||
|
@ -692,7 +692,7 @@ case object DataflowAnalysis extends IRPass {
|
|||||||
info: DependencyInfo
|
info: DependencyInfo
|
||||||
): IR.CallArgument = {
|
): IR.CallArgument = {
|
||||||
argument match {
|
argument match {
|
||||||
case spec @ IR.CallArgument.Specified(name, value, _, _, _, _) =>
|
case spec @ IR.CallArgument.Specified(name, value, _, _, _) =>
|
||||||
val specDep = asStatic(spec)
|
val specDep = asStatic(spec)
|
||||||
val valueDep = asStatic(value)
|
val valueDep = asStatic(value)
|
||||||
info.dependents.updateAt(valueDep, Set(specDep))
|
info.dependents.updateAt(valueDep, Set(specDep))
|
||||||
|
@ -169,51 +169,31 @@ case object DemandAnalysis extends IRPass {
|
|||||||
name: IR.Name,
|
name: IR.Name,
|
||||||
isInsideCallArgument: Boolean
|
isInsideCallArgument: Boolean
|
||||||
): IR.Expression = {
|
): IR.Expression = {
|
||||||
val usesLazyTerm = isUsageOfSuspendedTerm(name)
|
|
||||||
|
|
||||||
if (isInsideCallArgument) {
|
if (isInsideCallArgument) {
|
||||||
name
|
name
|
||||||
} else {
|
} else {
|
||||||
if (usesLazyTerm) {
|
name match {
|
||||||
val forceLocation = name.location
|
case lit: IR.Name.Literal if isDefined(lit) =>
|
||||||
val newNameLocation = name.location.map(l => l.copy(id = None))
|
val forceLocation = name.location
|
||||||
|
val newNameLocation = name.location.map(l => l.copy(id = None))
|
||||||
val newName = name match {
|
val newName = lit.copy(location = newNameLocation)
|
||||||
case lit: IR.Name.Literal => lit.copy(location = newNameLocation)
|
IR.Application.Force(newName, forceLocation)
|
||||||
case ths: IR.Name.This => ths.copy(location = newNameLocation)
|
case _ => name
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
/** Performs demand analysis on an application.
|
||||||
*
|
*
|
||||||
* @param application the function application to perform demand analysis on
|
* @param application the function application to perform demand analysis on
|
||||||
@ -227,11 +207,12 @@ case object DemandAnalysis extends IRPass {
|
|||||||
): IR.Application =
|
): IR.Application =
|
||||||
application match {
|
application match {
|
||||||
case pref @ IR.Application.Prefix(fn, args, _, _, _, _) =>
|
case pref @ IR.Application.Prefix(fn, args, _, _, _, _) =>
|
||||||
|
val newFun = fn match {
|
||||||
|
case n: IR.Name => n
|
||||||
|
case e => analyseExpression(e, isInsideCallArgument = false)
|
||||||
|
}
|
||||||
pref.copy(
|
pref.copy(
|
||||||
function = analyseExpression(
|
function = newFun,
|
||||||
fn,
|
|
||||||
isInsideCallArgument = false
|
|
||||||
),
|
|
||||||
arguments = args.map(analyseCallArgument)
|
arguments = args.map(analyseCallArgument)
|
||||||
)
|
)
|
||||||
case force @ IR.Application.Force(target, _, _, _) =>
|
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.
|
/** Performs demand analysis on a function call argument.
|
||||||
*
|
*
|
||||||
* In keeping with the requirement by the runtime to pass all function
|
* 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 = {
|
def analyseCallArgument(arg: IR.CallArgument): IR.CallArgument = {
|
||||||
arg match {
|
arg match {
|
||||||
case spec @ IR.CallArgument.Specified(_, expr, _, _, _, _) =>
|
case spec @ IR.CallArgument.Specified(_, expr, _, _, _) =>
|
||||||
spec.copy(
|
spec.copy(
|
||||||
value = analyseExpression(
|
value = analyseExpression(
|
||||||
expr,
|
expr,
|
||||||
isInsideCallArgument = true
|
isInsideCallArgument = true
|
||||||
),
|
)
|
||||||
shouldBeSuspended = Some(!isUsageOfSuspendedTerm(expr))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,7 @@ case object TailCall extends IRPass {
|
|||||||
*/
|
*/
|
||||||
def analyseCallArg(argument: IR.CallArgument): IR.CallArgument = {
|
def analyseCallArg(argument: IR.CallArgument): IR.CallArgument = {
|
||||||
argument match {
|
argument match {
|
||||||
case arg @ IR.CallArgument.Specified(_, expr, _, _, _, _) =>
|
case arg @ IR.CallArgument.Specified(_, expr, _, _, _) =>
|
||||||
arg
|
arg
|
||||||
.copy(
|
.copy(
|
||||||
// Note [Call Argument Tail Position]
|
// Note [Call Argument Tail Position]
|
||||||
|
@ -186,7 +186,7 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
args
|
args
|
||||||
.zip(argIsUnderscore)
|
.zip(argIsUnderscore)
|
||||||
.map(updateShorthandArg(_, freshNameSupply))
|
.map(updateShorthandArg(_, freshNameSupply))
|
||||||
.map { case s @ IR.CallArgument.Specified(_, value, _, _, _, _) =>
|
.map { case s @ IR.CallArgument.Specified(_, value, _, _, _) =>
|
||||||
s.copy(value = desugarExpression(value, freshNameSupply))
|
s.copy(value = desugarExpression(value, freshNameSupply))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +301,7 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
* position is lambda shorthand, otherwise `false`
|
* position is lambda shorthand, otherwise `false`
|
||||||
*/
|
*/
|
||||||
def determineLambdaShorthand(args: List[IR.CallArgument]): List[Boolean] = {
|
def determineLambdaShorthand(args: List[IR.CallArgument]): List[Boolean] = {
|
||||||
args.map { case IR.CallArgument.Specified(_, value, _, _, _, _) =>
|
args.map { case IR.CallArgument.Specified(_, value, _, _, _) =>
|
||||||
value match {
|
value match {
|
||||||
case _: IR.Name.Blank => true
|
case _: IR.Name.Blank => true
|
||||||
case _ => false
|
case _ => false
|
||||||
@ -325,7 +325,7 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
val isShorthand = argAndIsShorthand._2
|
val isShorthand = argAndIsShorthand._2
|
||||||
|
|
||||||
arg match {
|
arg match {
|
||||||
case s @ IR.CallArgument.Specified(_, value, _, _, _, _) =>
|
case s @ IR.CallArgument.Specified(_, value, _, _, _) =>
|
||||||
if (isShorthand) {
|
if (isShorthand) {
|
||||||
val newName = freshNameSupply
|
val newName = freshNameSupply
|
||||||
.newName()
|
.newName()
|
||||||
@ -354,7 +354,7 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
): Option[IR.DefinitionArgument] = {
|
): Option[IR.DefinitionArgument] = {
|
||||||
if (isShorthand) {
|
if (isShorthand) {
|
||||||
arg match {
|
arg match {
|
||||||
case IR.CallArgument.Specified(_, value, _, _, passData, diagnostics) =>
|
case IR.CallArgument.Specified(_, value, _, passData, diagnostics) =>
|
||||||
// Note [Safe Casting to IR.Name.Literal]
|
// Note [Safe Casting to IR.Name.Literal]
|
||||||
val defArgName =
|
val defArgName =
|
||||||
IR.Name.Literal(
|
IR.Name.Literal(
|
||||||
|
@ -105,7 +105,7 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
case Section.Left(arg, op, loc, passData, diagnostics) =>
|
case Section.Left(arg, op, loc, passData, diagnostics) =>
|
||||||
val rightArgName = freshNameSupply.newName()
|
val rightArgName = freshNameSupply.newName()
|
||||||
val rightCallArg =
|
val rightCallArg =
|
||||||
IR.CallArgument.Specified(None, rightArgName, None, None)
|
IR.CallArgument.Specified(None, rightArgName, None)
|
||||||
val rightDefArg = IR.DefinitionArgument.Specified(
|
val rightDefArg = IR.DefinitionArgument.Specified(
|
||||||
rightArgName.duplicate(),
|
rightArgName.duplicate(),
|
||||||
None,
|
None,
|
||||||
@ -117,7 +117,7 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
if (arg.value.isInstanceOf[IR.Name.Blank]) {
|
if (arg.value.isInstanceOf[IR.Name.Blank]) {
|
||||||
val leftArgName = freshNameSupply.newName()
|
val leftArgName = freshNameSupply.newName()
|
||||||
val leftCallArg =
|
val leftCallArg =
|
||||||
IR.CallArgument.Specified(None, leftArgName, None, None)
|
IR.CallArgument.Specified(None, leftArgName, None)
|
||||||
val leftDefArg = IR.DefinitionArgument.Specified(
|
val leftDefArg = IR.DefinitionArgument.Specified(
|
||||||
leftArgName.duplicate(),
|
leftArgName.duplicate(),
|
||||||
None,
|
None,
|
||||||
@ -165,7 +165,7 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
case Section.Sides(op, loc, passData, diagnostics) =>
|
case Section.Sides(op, loc, passData, diagnostics) =>
|
||||||
val leftArgName = freshNameSupply.newName()
|
val leftArgName = freshNameSupply.newName()
|
||||||
val leftCallArg =
|
val leftCallArg =
|
||||||
IR.CallArgument.Specified(None, leftArgName, None, None)
|
IR.CallArgument.Specified(None, leftArgName, None)
|
||||||
val leftDefArg = IR.DefinitionArgument.Specified(
|
val leftDefArg = IR.DefinitionArgument.Specified(
|
||||||
leftArgName.duplicate(),
|
leftArgName.duplicate(),
|
||||||
None,
|
None,
|
||||||
@ -176,7 +176,7 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
|
|
||||||
val rightArgName = freshNameSupply.newName()
|
val rightArgName = freshNameSupply.newName()
|
||||||
val rightCallArg =
|
val rightCallArg =
|
||||||
IR.CallArgument.Specified(None, rightArgName, None, None)
|
IR.CallArgument.Specified(None, rightArgName, None)
|
||||||
val rightDefArg = IR.DefinitionArgument.Specified(
|
val rightDefArg = IR.DefinitionArgument.Specified(
|
||||||
rightArgName.duplicate(),
|
rightArgName.duplicate(),
|
||||||
None,
|
None,
|
||||||
@ -228,7 +228,7 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
case Section.Right(op, arg, loc, passData, diagnostics) =>
|
case Section.Right(op, arg, loc, passData, diagnostics) =>
|
||||||
val leftArgName = freshNameSupply.newName()
|
val leftArgName = freshNameSupply.newName()
|
||||||
val leftCallArg =
|
val leftCallArg =
|
||||||
IR.CallArgument.Specified(None, leftArgName, None, None)
|
IR.CallArgument.Specified(None, leftArgName, None)
|
||||||
val leftDefArg =
|
val leftDefArg =
|
||||||
IR.DefinitionArgument.Specified(
|
IR.DefinitionArgument.Specified(
|
||||||
leftArgName.duplicate(),
|
leftArgName.duplicate(),
|
||||||
@ -242,7 +242,7 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
// Note [Blanks in Sections]
|
// Note [Blanks in Sections]
|
||||||
val rightArgName = freshNameSupply.newName()
|
val rightArgName = freshNameSupply.newName()
|
||||||
val rightCallArg =
|
val rightCallArg =
|
||||||
IR.CallArgument.Specified(None, rightArgName, None, None)
|
IR.CallArgument.Specified(None, rightArgName, None)
|
||||||
val rightDefArg = IR.DefinitionArgument.Specified(
|
val rightDefArg = IR.DefinitionArgument.Specified(
|
||||||
rightArgName.duplicate(),
|
rightArgName.duplicate(),
|
||||||
None,
|
None,
|
||||||
|
@ -196,7 +196,7 @@ case object TypeFunctions extends IRPass {
|
|||||||
*/
|
*/
|
||||||
def resolveCallArgument(arg: IR.CallArgument): IR.CallArgument = {
|
def resolveCallArgument(arg: IR.CallArgument): IR.CallArgument = {
|
||||||
arg match {
|
arg match {
|
||||||
case spec @ IR.CallArgument.Specified(_, value, _, _, _, _) =>
|
case spec @ IR.CallArgument.Specified(_, value, _, _, _) =>
|
||||||
spec.copy(
|
spec.copy(
|
||||||
value = resolveExpression(value)
|
value = resolveExpression(value)
|
||||||
)
|
)
|
||||||
@ -218,8 +218,8 @@ case object TypeFunctions extends IRPass {
|
|||||||
*/
|
*/
|
||||||
def isValidCallArg(arg: IR.CallArgument): Boolean = {
|
def isValidCallArg(arg: IR.CallArgument): Boolean = {
|
||||||
arg match {
|
arg match {
|
||||||
case IR.CallArgument.Specified(name, _, _, susp, _, _) =>
|
case IR.CallArgument.Specified(name, _, _, _, _) =>
|
||||||
name.isEmpty && (susp.isEmpty || susp.get)
|
name.isEmpty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ case object VectorLiterals extends IRPass {
|
|||||||
vec.duplicate(),
|
vec.duplicate(),
|
||||||
List(
|
List(
|
||||||
IR.CallArgument
|
IR.CallArgument
|
||||||
.Specified(None, trans.copy(location = None), None, None)
|
.Specified(None, trans.copy(location = None), None)
|
||||||
),
|
),
|
||||||
false,
|
false,
|
||||||
trans.location
|
trans.location
|
||||||
|
@ -936,9 +936,8 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
fn.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified]
|
fn.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||||
val fnArgY =
|
val fnArgY =
|
||||||
fn.arguments(1).asInstanceOf[IR.DefinitionArgument.Specified]
|
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 fnBody = fn.body.asInstanceOf[IR.Application.Prefix]
|
val plusFn = fnBody.function.asInstanceOf[IR.Name.Literal]
|
||||||
val plusFn = fnBody.function.asInstanceOf[IR.Name.Literal]
|
|
||||||
val plusArgX =
|
val plusArgX =
|
||||||
fnBody.arguments.head.asInstanceOf[IR.CallArgument.Specified]
|
fnBody.arguments.head.asInstanceOf[IR.CallArgument.Specified]
|
||||||
val plusArgXExpr = plusArgX.value.asInstanceOf[IR.Name.Literal]
|
val plusArgXExpr = plusArgX.value.asInstanceOf[IR.Name.Literal]
|
||||||
@ -947,16 +946,15 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
val plusArgYExpr = plusArgY.value.asInstanceOf[IR.Name.Literal]
|
val plusArgYExpr = plusArgY.value.asInstanceOf[IR.Name.Literal]
|
||||||
|
|
||||||
// Identifiers
|
// Identifiers
|
||||||
val fnId = mkStaticDep(fn.getId)
|
val fnId = mkStaticDep(fn.getId)
|
||||||
val fnArgXId = mkStaticDep(fnArgX.getId)
|
val fnArgXId = mkStaticDep(fnArgX.getId)
|
||||||
val fnArgYId = mkStaticDep(fnArgY.getId)
|
val fnArgYId = mkStaticDep(fnArgY.getId)
|
||||||
val fnArgYDefaultId = mkStaticDep(fnArgYDefault.getId)
|
val fnBodyId = mkStaticDep(fnBody.getId)
|
||||||
val fnBodyId = mkStaticDep(fnBody.getId)
|
val plusFnId = mkStaticDep(plusFn.getId)
|
||||||
val plusFnId = mkStaticDep(plusFn.getId)
|
val plusArgXId = mkStaticDep(plusArgX.getId)
|
||||||
val plusArgXId = mkStaticDep(plusArgX.getId)
|
val plusArgXExprId = mkStaticDep(plusArgXExpr.getId)
|
||||||
val plusArgXExprId = mkStaticDep(plusArgXExpr.getId)
|
val plusArgYId = mkStaticDep(plusArgY.getId)
|
||||||
val plusArgYId = mkStaticDep(plusArgY.getId)
|
val plusArgYExprId = mkStaticDep(plusArgYExpr.getId)
|
||||||
val plusArgYExprId = mkStaticDep(plusArgYExpr.getId)
|
|
||||||
|
|
||||||
// Dynamic Symbols
|
// Dynamic Symbols
|
||||||
val plusSym = mkDynamicDep("+")
|
val plusSym = mkDynamicDep("+")
|
||||||
@ -967,11 +965,7 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
|
|
||||||
// The Tests for dependents
|
// The Tests for dependents
|
||||||
dependents.getDirect(fnId) should not be defined
|
dependents.getDirect(fnId) should not be defined
|
||||||
dependents.getDirect(fnArgXId) shouldEqual Some(
|
|
||||||
Set(plusArgXExprId, fnArgYDefaultId)
|
|
||||||
)
|
|
||||||
dependents.getDirect(fnArgYId) shouldEqual Some(Set(plusArgYExprId))
|
dependents.getDirect(fnArgYId) shouldEqual Some(Set(plusArgYExprId))
|
||||||
dependents.getDirect(fnArgYDefaultId) shouldEqual Some(Set(fnArgYId))
|
|
||||||
dependents.getDirect(fnBodyId) shouldEqual Some(Set(fnId))
|
dependents.getDirect(fnBodyId) shouldEqual Some(Set(fnId))
|
||||||
dependents.getDirect(plusSym) shouldEqual Some(Set(plusFnId))
|
dependents.getDirect(plusSym) shouldEqual Some(Set(plusFnId))
|
||||||
dependents.getDirect(plusArgXId) shouldEqual Some(Set(fnBodyId))
|
dependents.getDirect(plusArgXId) shouldEqual Some(Set(fnBodyId))
|
||||||
@ -982,8 +976,6 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
// The Tests for dependencies
|
// The Tests for dependencies
|
||||||
dependencies.getDirect(fnId) shouldEqual Some(Set(fnBodyId))
|
dependencies.getDirect(fnId) shouldEqual Some(Set(fnBodyId))
|
||||||
dependencies.getDirect(fnArgXId) shouldEqual None
|
dependencies.getDirect(fnArgXId) shouldEqual None
|
||||||
dependencies.getDirect(fnArgYId) shouldEqual Some(Set(fnArgYDefaultId))
|
|
||||||
dependencies.getDirect(fnArgYDefaultId) shouldEqual Some(Set(fnArgXId))
|
|
||||||
dependencies.getDirect(fnBodyId) shouldEqual Some(
|
dependencies.getDirect(fnBodyId) shouldEqual Some(
|
||||||
Set(plusFnId, plusArgXId, plusArgYId)
|
Set(plusFnId, plusArgXId, plusArgYId)
|
||||||
)
|
)
|
||||||
@ -1100,25 +1092,15 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
val lam = ir.asInstanceOf[IR.Function.Lambda]
|
val lam = ir.asInstanceOf[IR.Function.Lambda]
|
||||||
val argX =
|
val argX =
|
||||||
lam.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified]
|
lam.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||||
val xUse = lam.body.asInstanceOf[IR.Name.Literal]
|
|
||||||
|
|
||||||
// The IDs
|
// The IDs
|
||||||
val lamId = mkStaticDep(lam.getId)
|
|
||||||
val argXId = mkStaticDep(argX.getId)
|
val argXId = mkStaticDep(argX.getId)
|
||||||
val xUseId = mkStaticDep(xUse.getId)
|
|
||||||
|
|
||||||
// The info
|
// The info
|
||||||
val dependents = depInfo.dependents
|
|
||||||
val dependencies = depInfo.dependencies
|
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
|
// The test for dependencies
|
||||||
dependencies.getDirect(argXId) shouldEqual None
|
dependencies.getDirect(argXId) shouldEqual None
|
||||||
dependencies.getDirect(xUseId) shouldEqual Some(Set(argXId))
|
|
||||||
dependencies.getDirect(lamId) shouldEqual Some(Set(xUseId))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"work properly for blocks" in {
|
"work properly for blocks" in {
|
||||||
@ -1136,14 +1118,12 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
val xBind = block.expressions.head.asInstanceOf[IR.Expression.Binding]
|
val xBind = block.expressions.head.asInstanceOf[IR.Expression.Binding]
|
||||||
val xBindName = xBind.name.asInstanceOf[IR.Name.Literal]
|
val xBindName = xBind.name.asInstanceOf[IR.Name.Literal]
|
||||||
val xBindExpr = xBind.expression.asInstanceOf[IR.Literal.Number]
|
val xBindExpr = xBind.expression.asInstanceOf[IR.Literal.Number]
|
||||||
val xUse = block.returnValue.asInstanceOf[IR.Name.Literal]
|
|
||||||
|
|
||||||
// The IDs
|
// The IDs
|
||||||
val blockId = mkStaticDep(block.getId)
|
val blockId = mkStaticDep(block.getId)
|
||||||
val xBindId = mkStaticDep(xBind.getId)
|
val xBindId = mkStaticDep(xBind.getId)
|
||||||
val xBindNameId = mkStaticDep(xBindName.getId)
|
val xBindNameId = mkStaticDep(xBindName.getId)
|
||||||
val xBindExprId = mkStaticDep(xBindExpr.getId)
|
val xBindExprId = mkStaticDep(xBindExpr.getId)
|
||||||
val xUseId = mkStaticDep(xUse.getId)
|
|
||||||
|
|
||||||
// The info
|
// The info
|
||||||
val dependents = depInfo.dependents
|
val dependents = depInfo.dependents
|
||||||
@ -1151,14 +1131,10 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
|
|
||||||
// The test for dependents
|
// The test for dependents
|
||||||
dependents.getDirect(blockId) should not be defined
|
dependents.getDirect(blockId) should not be defined
|
||||||
dependents.getDirect(xBindId) shouldEqual Some(Set(xUseId))
|
|
||||||
dependents.getDirect(xBindNameId) shouldEqual Some(Set(xBindId))
|
dependents.getDirect(xBindNameId) shouldEqual Some(Set(xBindId))
|
||||||
dependents.getDirect(xBindExprId) shouldEqual Some(Set(xBindId))
|
dependents.getDirect(xBindExprId) shouldEqual Some(Set(xBindId))
|
||||||
dependents.getDirect(xUseId) shouldEqual Some(Set(blockId))
|
|
||||||
|
|
||||||
// The test for dependencies
|
// The test for dependencies
|
||||||
dependencies.getDirect(blockId) shouldEqual Some(Set(xUseId))
|
|
||||||
dependencies.getDirect(xUseId) shouldEqual Some(Set(xBindId))
|
|
||||||
dependencies.getDirect(xBindId) shouldEqual Some(
|
dependencies.getDirect(xBindId) shouldEqual Some(
|
||||||
Set(xBindNameId, xBindExprId)
|
Set(xBindNameId, xBindExprId)
|
||||||
)
|
)
|
||||||
@ -1202,47 +1178,6 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
dependencies.getDirect(bindingExprId) shouldEqual None
|
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 {
|
"work properly for undefined variables in expressions" in {
|
||||||
implicit val inlineContext: InlineContext = mkInlineContext
|
implicit val inlineContext: InlineContext = mkInlineContext
|
||||||
|
|
||||||
@ -1331,7 +1266,6 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
val vector = callArg.value
|
val vector = callArg.value
|
||||||
.asInstanceOf[IR.Application.Literal.Sequence]
|
.asInstanceOf[IR.Application.Literal.Sequence]
|
||||||
|
|
||||||
val xDefId = mkStaticDep(ir.arguments(0).getId)
|
|
||||||
val xUseId = mkStaticDep(vector.items(0).getId)
|
val xUseId = mkStaticDep(vector.items(0).getId)
|
||||||
val yId = mkStaticDep(vector.items(1).getId)
|
val yId = mkStaticDep(vector.items(1).getId)
|
||||||
val litId = mkStaticDep(vector.items(2).getId)
|
val litId = mkStaticDep(vector.items(2).getId)
|
||||||
@ -1345,7 +1279,6 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
val dependencies = depInfo.dependencies
|
val dependencies = depInfo.dependencies
|
||||||
|
|
||||||
// Tests for dependents
|
// Tests for dependents
|
||||||
dependents.getDirect(xDefId) shouldEqual Some(Set(xUseId))
|
|
||||||
dependents.getDirect(xUseId) shouldEqual Some(Set(vecId))
|
dependents.getDirect(xUseId) shouldEqual Some(Set(vecId))
|
||||||
dependents.getDirect(yId) shouldEqual Some(Set(vecId))
|
dependents.getDirect(yId) shouldEqual Some(Set(vecId))
|
||||||
dependents.getDirect(litId) 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]
|
caseBinding.expression.asInstanceOf[IR.Application.Prefix]
|
||||||
val caseBindingName = caseBinding.name.asInstanceOf[IR.Name.Literal]
|
val caseBindingName = caseBinding.name.asInstanceOf[IR.Name.Literal]
|
||||||
val caseExpr = caseBlock.returnValue.asInstanceOf[IR.Case.Expr]
|
val caseExpr = caseBlock.returnValue.asInstanceOf[IR.Case.Expr]
|
||||||
val scrutinee = caseExpr.scrutinee.asInstanceOf[IR.Name.Literal]
|
|
||||||
val consBranch = caseExpr.branches.head
|
val consBranch = caseExpr.branches.head
|
||||||
val catchAllbranch = caseExpr.branches(1)
|
|
||||||
|
|
||||||
val consBranchPattern =
|
val consBranchPattern =
|
||||||
consBranch.pattern.asInstanceOf[Pattern.Constructor]
|
consBranch.pattern.asInstanceOf[Pattern.Constructor]
|
||||||
@ -1432,9 +1363,7 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
val caseBindingExprId = mkStaticDep(caseBindingExpr.getId)
|
val caseBindingExprId = mkStaticDep(caseBindingExpr.getId)
|
||||||
val caseBindingNameId = mkStaticDep(caseBindingName.getId)
|
val caseBindingNameId = mkStaticDep(caseBindingName.getId)
|
||||||
val caseExprId = mkStaticDep(caseExpr.getId)
|
val caseExprId = mkStaticDep(caseExpr.getId)
|
||||||
val scrutineeId = mkStaticDep(scrutinee.getId)
|
|
||||||
val consBranchId = mkStaticDep(consBranch.getId)
|
val consBranchId = mkStaticDep(consBranch.getId)
|
||||||
val catchAllBranchId = mkStaticDep(catchAllbranch.getId)
|
|
||||||
|
|
||||||
val consBranchPatternId = mkStaticDep(consBranchPattern.getId)
|
val consBranchPatternId = mkStaticDep(consBranchPattern.getId)
|
||||||
val consBranchPatternConsId = mkStaticDep(consBranchPatternCons.getId)
|
val consBranchPatternConsId = mkStaticDep(consBranchPatternCons.getId)
|
||||||
@ -1457,8 +1386,6 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
// Tests for dependents
|
// Tests for dependents
|
||||||
dependents.getDirect(caseBlockId) should not be defined
|
dependents.getDirect(caseBlockId) should not be defined
|
||||||
dependents.getDirect(caseExprId) shouldEqual Some(Set(caseBlockId))
|
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(
|
dependents.getDirect(caseBindingExprId) shouldEqual Some(
|
||||||
Set(caseBindingId)
|
Set(caseBindingId)
|
||||||
)
|
)
|
||||||
@ -1499,10 +1426,6 @@ class DataflowAnalysisTest extends CompilerTest {
|
|||||||
dependencies.getDirect(caseBindingId) shouldEqual Some(
|
dependencies.getDirect(caseBindingId) shouldEqual Some(
|
||||||
Set(caseBindingNameId, caseBindingExprId)
|
Set(caseBindingNameId, caseBindingExprId)
|
||||||
)
|
)
|
||||||
dependencies.getDirect(caseExprId) shouldEqual Some(
|
|
||||||
Set(scrutineeId, consBranchId, catchAllBranchId)
|
|
||||||
)
|
|
||||||
dependencies.getDirect(scrutineeId) shouldEqual Some(Set(caseBindingId))
|
|
||||||
dependencies.getDirect(consBranchId) shouldEqual Some(
|
dependencies.getDirect(consBranchId) shouldEqual Some(
|
||||||
Set(consBranchPatternId, consBranchExpressionId)
|
Set(consBranchPatternId, consBranchExpressionId)
|
||||||
)
|
)
|
||||||
|
@ -157,79 +157,9 @@ class DemandAnalysisTest extends CompilerTest {
|
|||||||
|
|
||||||
vec.items(0) shouldBe an[IR.Application.Force]
|
vec.items(0) shouldBe an[IR.Application.Force]
|
||||||
vec.items(1) 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 {
|
"Suspended blocks" should {
|
||||||
@ -280,28 +210,6 @@ class DemandAnalysisTest extends CompilerTest {
|
|||||||
.value shouldBe an[IR.Name]
|
.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 {
|
"force terms in blocks passed directly as arguments" in {
|
||||||
implicit val ctx: ModuleContext = mkModuleContext
|
implicit val ctx: ModuleContext = mkModuleContext
|
||||||
|
|
||||||
|
@ -32,8 +32,7 @@ class CurryingTest extends InterpreterTest {
|
|||||||
| fn1 = fn ...
|
| fn1 = fn ...
|
||||||
| fn2 = fn1 1 2 ...
|
| fn2 = fn1 1 2 ...
|
||||||
| fn3 = fn2 3 ...
|
| fn3 = fn2 3 ...
|
||||||
|
|
| fn3
|
||||||
| fn3.call
|
|
||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
|
|
||||||
eval(code) shouldEqual 26
|
eval(code) shouldEqual 26
|
||||||
@ -45,7 +44,7 @@ class CurryingTest extends InterpreterTest {
|
|||||||
|main =
|
|main =
|
||||||
| fn = w -> x -> (y = 10) -> (z = 20) -> w + x + y + z
|
| fn = w -> x -> (y = 10) -> (z = 20) -> w + x + y + z
|
||||||
|
|
|
|
||||||
| fn.call 1 2 (z = 10)
|
| fn 1 2 (z = 10)
|
||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
|
|
||||||
eval(code) shouldEqual 23
|
eval(code) shouldEqual 23
|
||||||
@ -74,11 +73,63 @@ class CurryingTest extends InterpreterTest {
|
|||||||
| fn1 = Nothing.fn ...
|
| fn1 = Nothing.fn ...
|
||||||
| fn2 = fn1 1 2 ...
|
| fn2 = fn1 1 2 ...
|
||||||
| fn3 = fn2 3 ...
|
| fn3 = fn2 3 ...
|
||||||
|
|
| fn3
|
||||||
| fn3.call
|
|
||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
|
|
||||||
eval(code) shouldEqual 26
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ class LambdaShorthandArgsTest extends InterpreterTest {
|
|||||||
"""
|
"""
|
||||||
|main =
|
|main =
|
||||||
| f = (x = _) -> x
|
| f = (x = _) -> x
|
||||||
| g = f.call
|
| g = f
|
||||||
| h = _
|
| h = _
|
||||||
| res1 = g 10
|
| res1 = g 10
|
||||||
| res2 = h 10
|
| res2 = h 10
|
||||||
|
Loading…
Reference in New Issue
Block a user