On Monday, 28 July 2025 at 04:39:00 UTC, Mike Shah wrote:
**So really my one concrete question is** -- can I see main.main()__foreachbody_L21_C3(ref int) anywhere?

I think that's where the confusion comes from, that misleading `-vcg-ast` output for the loop-body-lambda, apparently printed as `… => 0` regardless of the actual loop body.

Based on your example:
```d
import core.stdc.stdio;

struct Array(T) {
  T[] array;
  int opApply(scope int delegate(ref T) dg){
    foreach (ref i; array){
      const result = dg(i);
      if (result)
        return result;
    }
    return 0;
  }
}

void main() {
  Array!int ints;
  ints.array = [4,6,8,10,12];

  foreach (item; ints) {
    if (item == 1)
      continue;
    if (item == 2)
      break;
    printf("%d\n", item);
  }
}
```

The loop is actually rewritten by the compiler to:
```d
ints.opApply((ref int item) {
  if (item == 1)
    return 0;  // continue => abort this iteration
  if (item == 2)
    return 1;  // break => abort this and all future iterations
  printf("%d\n", item);
  return 0;    // continue with next iteration
});
```

So the main thing here is that the body is promoted to a lambda, and the control-flow statements inside the body (`break` and `continue` in the example above) are transformed to specific return codes for the opApply delegate protocol.

If we add a `return` statement to the body:
```d
int main() {
  Array!int ints;
  ints.array = [4,6,8,10,12];

  foreach (item; ints) {
    if (item == 0)
      return item;
    if (item == 1)
      continue;
    if (item == 2)
      break;
    printf("%d\n", item);
  }
  return 0;
}
```

then the rewrite becomes a bit more complex:
```d
int __result; // magic variable inserted by the compiler, for the main() return value
const __opApplyResult = ints.opApply((ref int item) {
  if (item == 0) {
    __result = item;  // set return value for parent function
return 2; // return => abort the loop and exit from parent function
  }
  if (item == 1)
    return 0;  // continue => abort this iteration
  if (item == 2)
    return 1;  // break => abort this and all future iterations
  printf("%d\n", item);
  return 0;    // continue with next iteration
});
switch (__opApplyResult) {
  default:
    break;
  case 2:
    return __result;
}
return __result = 0;
```

**A third question** Will this call to a delegate provide more hidden allocations I wonder?

As with any regular lambda, captured outer variables (like the `__result` in the 2nd example) will cause a closure, but as long as the `opApply` takes the delegate as `scope`, the closure will be on the stack, so no harm.

Reply via email to