June 04, 2023
https://issues.dlang.org/show_bug.cgi?id=23960

          Issue ID: 23960
           Summary: opApply and opApplyReverse should work with named
                    mixin templates in aggregates
           Product: D
           Version: D2
          Hardware: x86
                OS: Linux
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dmd
          Assignee: nobody@puremagic.com
          Reporter: witold.baryluk+d@gmail.com

https://godbolt.org/z/9qcGnWz8G

template M(int value) {
  int opApply(scope int delegate(ref int i) dg) {
    int v = value;
    return dg(v);
  }
}

struct S {
  mixin M!5 m1;
  mixin M!6 m2;
}

void main() {
  import std.stdio : writefln;
  S s;
  foreach (i; s.m1) {
    writefln("%d", i);
  }
}

should compile and print 5.

But it does not compile:

dmd 2.094:

<source>(16): Error: expression has no value
Compiler returned: 1


gdc trunk (14.0.0 20230530):

 <source>:16:3: error: invalid 'foreach' aggregate 's.mixin M!5 m1;
   ' of type 'void'
   16 |   foreach (i; s.m1) {
      |   ^
Compiler returned: 1



I discovered this problem when implementing intrusive circular double-linked list, which I have two per Node (each node is a member of two intrusive circular double-linked lists, each with prev and next pointers), where I wanted to use opApply and opApplyReverse to traverse each list independently on demand. I am implementation DLX algorithm (Knuth's Algorithm X brute force depth-first backtracking algorithm for finding solutions to exact cover problem using dancing links technique).

For opApply, it can be worked around using a delegate:

void main() {
  import std.stdio : writefln;
  S s;
  foreach (i; &s.m1.opApply) {
    writefln("%d", i);
  }
}

but one cannot use `foreach_revserse` on delegates, instead one needs to use `foreach` with `&s.m1.opApplyReverse` which is less readable and restricts usage. It also does not work well if there are many opApply and/or opApplyReverse overloads, possibly templated, as then one cannot form delegate without explicit instantiation.

Of course if there is only one unnamed mixin, it should be possible to still use it via class/struct scope:

foreach (i; s),   which will automatically find opApply

and if there are multiple, either ambiguity reported, or aliases uses to provide an overload set. (This is already implemented in current D version).

--