# Operations corresponding to function calls.

# A call with no arguments and no return value.
internal-op Call0
op1-read
class X
side-effects
num-call-args 0

# A call with no arguments and a return value.
internal-assignment-op Call0
class V
side-effects OP_CALL0_X OP_X
assign-val v
num-call-args 0

# Calls with 1 argument and no return value.
internal-op Call1
op1-read
classes V C
side-effects
num-call-args 1

# Same but with a return value.
internal-assignment-op Call1
class VV
side-effects OP_CALL1_V OP_V
assign-val v
num-call-args 1

internal-assignment-op Call1
class VC
side-effects OP_CALL1_C OP_C
assign-val v
num-call-args 1

# Calls with 2-5 arguments and no return value.
internal-op Call2
class X
side-effects
num-call-args 2

# Same with a return value.
internal-assignment-op Call2
class V
side-effects OP_CALL2_X OP_X
assign-val v
num-call-args 2

internal-op Call3
class X
side-effects
num-call-args 3

# Same with a return value.
internal-assignment-op Call3
class V
side-effects OP_CALL3_X OP_X
assign-val v
num-call-args 3

internal-op Call4
class X
side-effects
num-call-args 4

# Same with a return value.
internal-assignment-op Call4
class V
side-effects OP_CALL4_X OP_X
assign-val v
num-call-args 4

internal-op Call5
class X
side-effects
num-call-args 5

# Same with a return value.
internal-assignment-op Call5
class V
side-effects OP_CALL5_X OP_X
assign-val v
num-call-args 5

# ... and with an arbitrary number of arguments.

internal-op CallN
class X
side-effects
num-call-args n

# Same with a return value.
internal-assignment-op CallN
class V
side-effects OP_CALLN_X OP_X
assign-val v
num-call-args n

# Same, but for indirect calls via a global variable.
internal-op IndCallN
class X
side-effects
indirect-call
num-call-args n

# Same with a return value.
internal-assignment-op IndCallN
class V
side-effects OP_INDCALLN_X OP_X
assign-val v
indirect-call
num-call-args n

# And versions with a local variable rather than a global.
internal-op Local-IndCallN
op1-read
class V
side-effects
indirect-local-call
num-call-args n

internal-assignment-op Local-IndCallN
class VV
side-effects OP_LOCAL_INDCALLN_V OP_V
assign-val v
indirect-local-call
num-call-args n

# A call made in a "when" context.  These always have assignment targets.
# To keep things simple, we just use one generic flavor (for N arguments,
# doing a less-streamlined-but-simpler Val-based assignment).
macro WhenCall(lhs, func)
	if ( ! func )
		throw ZAMDelayedCallException();
	auto trigger = Z_FRAME->GetTrigger();
	Val* v = trigger ? trigger->Lookup(Z_AUX->call_expr.get()) : nullptr;
	ValPtr vp;
	if ( v )
		vp = {NewRef{}, v};
	else
		{
		auto aux = Z_AUX;
		auto current_assoc = Z_FRAME->GetTriggerAssoc();
		auto n = aux->n;
		std::vector<ValPtr> args;
		for ( auto i = 0; i < n; ++i )
			args.push_back(aux->ToVal(frame, i));
		Z_FRAME->SetCall(Z_AUX->call_expr.get());
		/* It's possible that this function will call another that
		 * itself returns null because *it* is the actual blocker.
		 * That will set ZAM_error, which we need to ignore.
		 */
		auto hold_ZAM_error = ZAM_error;
		vp = func->Invoke(&args, Z_FRAME);
		ZAM_error = hold_ZAM_error;
		Z_FRAME->SetTriggerAssoc(current_assoc);
		if ( ! vp )
			throw ZAMDelayedCallException();
		}
	if ( Z_IS_MANAGED )
		ZVal::DeleteManagedType(lhs);
	lhs = ZVal(vp, Z_TYPE);

internal-op WhenCallN
class V
side-effects
eval	WhenCall($$, Z_AUX_FUNC)

internal-op WhenIndCallN
class VV
op-types X F
side-effects
eval	WhenCall($$, $1)

# Form for when we need to look up the function value at run-time.
internal-op When-ID-IndCallN
class V
side-effects
eval	WhenCall($$, Z_AUX_ID->GetVal()->AsFunc())
