docs/infix/05-il-emission-algorithm.md
inner: target MethodBase of the call. prefixes, postfixes: sorted for this call occurrence. callPrefixes: any immediate IL prefixes before the original call that apply to the call (e.g., constrained., tail.).
Same stack effect as the original call. No change to prior argument‑loading code. Only the original call is replaced. The immediate call‑only prefixes are absorbed.
At the position of the original call:
for operand in [argN .. arg1, instance?]:
stloc <cap_local_operand> // exact stack type, including managed pointers
Init per‑site locals bool __runOriginal = true; TResult __result = default; (only if non‑void)
Inner prefixes in order: If any prefix can skip, guard subsequent prefixes on __runOriginal. Emit call to prefix with bound parameters (see binding doc). If prefix returns bool, store to __runOriginal.
if (canSkipAny)
if (!__runOriginal) goto AfterPrefixes;
call InnerPrefix(...); // may return bool
if (returnsBool) stloc __runOriginal
AfterPrefixes:
if (!__runOriginal) goto AfterCall
[constrained.|tail.]? // only if originally present
ldloc instance?
ldloc arg1 .. ldloc argN
call/callvirt inner
if (hasResult) stloc __result
AfterCall:
Inner postfixes in order: Call with bound parameters. Optional passthrough: if postfix returns TResult, take it as new __result.
Write‑backs For any captured by‑ref argument that you converted to a value local, store back to the original address now. Prefer not to convert by‑ref args to values in v1: capture their managed pointer and pass it through, so no write‑back is needed. Restore original stack effect If inner is non‑void: ldloc __result to leave the result on the stack. If void: push nothing. Done.