@@ -698,88 +698,100 @@ foreach (string s, double d; aa)
698698
699699$(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes with `opApply`))
700700
701- $(P If the aggregate expression is a struct or class object,
702- the $(D foreach) is defined by the
703- special $(LEGACY_LNAME2 opApply, op-apply, $(D opApply)) member function, and the
704- `foreach_reverse` behavior is defined by the special
701+ $(P If the aggregate expression is a ` struct` or ` class` object,
702+ iteration with $(D foreach) can be defined by implementing the
703+ $(LEGACY_LNAME2 opApply, op-apply, $(D opApply)) member function, and the
704+ `foreach_reverse` behavior is defined by the
705705 $(LEGACY_LNAME2 opApplyReverse, op-apply-reverse, $(D opApplyReverse)) member function.
706706 These functions must each have the signature below:
707707 )
708708
709709$(GRAMMAR
710- $(GNAME OpApplyDeclaration):
711- `int opApply` `(` `scope` `int delegate` `(` $(I OpApplyParameters) `)` `dg` `)` `;`
710+ `int opApply` `(` `scope` `int delegate` `(` $(I OpApplyParameters) `)` `body` `)` `;`
712711
713712$(GNAME OpApplyParameters):
714- *OpApplyParameter*
715- *OpApplyParameter* , *OpApplyParameters*
713+ $(GLINK ParameterDeclaration)
714+ $(GLINK ParameterDeclaration) , *OpApplyParameters*
716715
717716$(GNAME OpApplyParameter):
718- $(GLINK ForeachTypeAttributes )$(OPT) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator)
717+ $(GLINK ParameterStorageClass )$(OPT) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator)
719718)
720719
721- $(P where each $(I OpApplyParameter) of `dg` must match a $(GLINK ForeachType)
722- in a $(GLINK ForeachStatement),
723- otherwise the *ForeachStatement* will cause an error.)
724-
725- $(P Any *ForeachTypeAttribute* cannot be `enum`.)
726-
727720 $(PANEL
728721 To support a `ref` iteration variable, the delegate must take a `ref` parameter:
729722
730723 $(SPEC_RUNNABLE_EXAMPLE_COMPILE
731724 ---
732725 struct S
733726 {
734- int opApply(scope int delegate(ref uint n) dg);
727+ uint n;
728+ int opApply(scope int delegate(ref uint) body) => body(n);
735729 }
736- void f(S s )
730+ void main( )
737731 {
732+ S s;
738733 foreach (ref uint i; s)
739- i++;
734+ {
735+ i++; // effectively s.n++
736+ }
737+ foreach (i; s)
738+ {
739+ static assert(is(typeof(i) == uint));
740+ assert(i == 1);
741+ }
740742 }
741743 ---
742744 )
743- Above, `opApply` is still matched when `i` is not `ref`, so by using
744- a `ref` delegate parameter both forms are supported.
745- )
745+ Above, `opApply` is matched both when `i` is and is not `ref`, so by using
746+ a `ref` delegate parameter, both forms are supported.
746747
747- $(P There can be multiple $(D opApply) and $(D opApplyReverse) functions -
748- one is selected
749- by matching each parameter of `dg` to each $(I ForeachType)
750- declared in the $(I ForeachStatement).)
748+ Stating the type of the variable like in the first loop is optional,
749+ as is showcased in the second loop, where the type is inferred.
750+ )
751751
752- $(P The body of the apply
753- function iterates over the elements it aggregates, passing each one
754- in successive calls to the `dg` delegate. The delegate return value
755- determines whether to interrupt iteration:)
752+ $(P The apply
753+ function iterates over the elements it aggregates by passing each one
754+ in successive calls to the `body` delegate. The delegate return value
755+ of the `body` delegate
756+ determines how to proceed iteration:)
756757
757758 $(UL
758- $(LI If the result is nonzero, apply must cease
759+ $(LI If the result is nonzero, the apply function must cease
759760 iterating and return that value.)
760- $(LI If the result is 0 , then iteration should continue.
761+ $(LI If the result is `0` , then iteration should continue.
761762 If there are no more elements to iterate,
762- apply must return 0 .)
763+ the apply function must return `0` .)
763764 )
764765
765- $(P The result of calling the delegate will be nonzero if the *ForeachStatement*
766+ $(P The result of the `body` delegate will be nonzero if the *ForeachStatement*
766767 body executes a matching $(GLINK BreakStatement), $(GLINK ReturnStatement), or
767768 $(GLINK GotoStatement) whose matching label is outside the *ForeachStatement*.
768769 )
769770
770- $(P For example, consider a class that is a container for two elements:)
771+ $(P The $(D opApply) and $(D opApplyReverse) member functions can be overloaded.
772+ Selection works similar to overload resolution by
773+ comparing the number of `foreach` variables and number of parameters of `body` and,
774+ if more than one overload remains unique overload,
775+ matching the parameter types of `body` and each $(I ForeachType)
776+ declared in the $(I ForeachStatement) when given.)
777+
778+ $(BEST_PRACTICE Overload apply functions only with delegates differing in number of parameters
779+ to enable type inference for `foreach` variables.
780+ )
781+
782+ $(P For example, consider a class that is a container for some elements:)
771783
772784 $(SPEC_RUNNABLE_EXAMPLE_RUN
773785 --------------
774786 class Foo
775787 {
776788 uint[] array;
777789
778- int opApply(scope int delegate(ref uint) dg )
790+ int opApply(scope int delegate(ref uint) body )
779791 {
780792 foreach (e; array)
781793 {
782- int result = dg (e);
794+ int result = body (e);
783795 if (result)
784796 return result;
785797 }
@@ -811,57 +823,159 @@ $(CONSOLE
81182373
81282482
813825)
814- $(PANEL
815- The `scope` storage class on the $(D dg) parameter means that the delegate does
816- not escape the scope of the $(D opApply) function (an example would be assigning $(D dg) to a
817- global variable). If it cannot be statically guaranteed that $(D dg) does not escape, a closure may
818- be allocated for it on the heap instead of the stack.
819826
820827 $(BEST_PRACTICE Annotate delegate parameters to `opApply` functions with `scope` when possible.)
828+
829+ $(PANEL
830+ The `scope` storage class on the `body` parameter means that the delegate does
831+ not escape the scope of the apply function (an example would be assigning`body` to a
832+ global variable). If it cannot be statically guaranteed that `body` does not escape, a closure may
833+ be allocated for it on the heap instead of the stack.
821834 )
822835
823- $(P $(B Important:) If $(D opApply) catches any exceptions, ensure that those
824- exceptions did not originate from the delegate passed to $(D opApply) . The user would expect
836+ $(P $(B Important:) If apply functions catch any exceptions, ensure that those
837+ exceptions did not originate from the delegate. The user would expect
825838 exceptions thrown from a `foreach` body to both terminate the loop, and propagate outside
826839 the `foreach` body.
827840 )
828841
829842$(H4 $(LNAME2 template-op-apply, Template `opApply`))
830843
831- $(P $(D opApply) can also be a templated function,
832- which will infer the types of parameters based on the $(I ForeachStatement).
833- For example:)
844+ $(P `opApply` and `opApplyReverse` can also be a function templates,
845+ which can optionally infer the types of parameters based on the $(I ForeachStatement).
846+ )
847+
848+ $(P $(B Note:) An apply function template cannot infer `foreach` variable types.)
834849
835850 $(SPEC_RUNNABLE_EXAMPLE_RUN
836851 --------------
837852 struct S
838853 {
839- import std.traits : ParameterTypeTuple; // introspection template
840- import std.stdio;
841-
842- int opApply(Dg)(scope Dg dg)
843- if (ParameterTypeTuple!Dg.length == 2) // foreach with 2 parameters
854+ int opApply(Body)(scope Body body)
844855 {
845- writeln(2);
846- return 0;
847- }
848-
849- int opApply(Dg)(scope Dg dg)
850- if (ParameterTypeTuple!Dg.length == 3) // foreach with 3 parameters
851- {
852- writeln(3);
856+ pragma(msg, Body);
853857 return 0;
854858 }
855859 }
856860
857861 void main()
858862 {
859- foreach (int a, int b; S()) { } // calls first opApply function
860- foreach (int a, int b, float c; S()) { } // calls second opApply function
863+ foreach (int a, int b; S()) { } // int delegate(ref int, ref int) pure nothrow @nogc @safe
864+ foreach (bool b, string s; S()) { } // int delegate(ref bool, ref string) pure nothrow @nogc @safe
865+ foreach (x; S()) { } // Error: cannot infer type for `foreach` variable `x`, perhaps set it explicitly
861866 }
862867 --------------
863868 )
864869
870+ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an alias to a explicit function template instance))
871+
872+ $(P `opApply` and `opApplyReverse` can be aliases to an appropriate function or function template.
873+ However, special treatment is given to apply functions that are aliases of an appropriate function template instance.)
874+
875+ $(P In that case, the function template instance is used for overload selection and `foreach` variable type inference,
876+ but the template is instantiated again with the actual delegate type of the `foreach` body.
877+ This way, apply functions can infer attributes depending on the attributes of `body` delegate.)
878+
879+ $(SPEC_RUNNABLE_EXAMPLE_RUN
880+ --------------
881+ struct A
882+ {
883+ int opApply(scope int delegate(long) body) => body(42);
884+ }
885+ struct B
886+ {
887+ int opApply(Body)(scope Body body) => body(42);
888+ }
889+ struct C
890+ {
891+ int opApplyImpl(Body)(scope Body body) => body(42);
892+ alias opApply = opApplyImpl!(int delegate(long));
893+ }
894+ void main() @nogc nothrow pure @safe
895+ {
896+ // Error: `@nogc` function `D main` cannot call non-@nogc function `onlineapp.A.opApply`
897+ // Error: `pure` function `D main` cannot call impure function `onlineapp.A.opApply`
898+ // Error: `@safe` function `D main` cannot call `@system` function `onlineapp.A.opApply`
899+ // Error: function `onlineapp.A.opApply` is not `nothrow`
900+ static assert(!__traits(compiles, () @safe {
901+ foreach (x; A()) { }
902+ }));
903+
904+ // Error: cannot infer type for `foreach` variable `x`, perhaps set it explicitly
905+ static assert(!__traits(compiles, {
906+ foreach (x; B()) { }
907+ }));
908+
909+ // Good:
910+ foreach (x; C())
911+ {
912+ static assert(is(typeof(x) == long));
913+ assert(x == 42);
914+ }
915+ }
916+ --------------
917+ )
918+
919+ $(P The `opApplyImpl` pattern is generally preferable to
920+ overloading many apply functions with all possible combinations of attributes.)
921+
922+ $(P Multiple apply function aliases can exist, and selection and `foreach` variable type inference work:)
923+
924+ --------------
925+ class Tree(T)
926+ {
927+ private T label;
928+ private Tree[] children;
929+
930+ this(T label, Tree[] children = null)
931+ {
932+ this.label = label;
933+ this.children = children;
934+ }
935+
936+ alias opApply = opApplyImpl!(int delegate(ref T label));
937+ alias opApply = opApplyImpl!(int delegate(size_t depth, ref T label));
938+ alias opApply = opApplyImpl!(int delegate(size_t depth, bool isLastChild, ref T label));
939+
940+ int opApplyImpl(Body)(scope Body body) => opApplyImpl2(0, true, body);
941+ int opApplyImpl2(Body)(size_t depth, bool lastChild, scope Body body)
942+ {
943+ import std.meta : AliasSeq;
944+ static if (is(Body : int delegate(size_t, bool, ref T)))
945+ alias args = AliasSeq!(depth, lastChild, label);
946+ else static if (is(Body : int delegate(size_t, ref T)))
947+ alias args = AliasSeq!(depth, label);
948+ else
949+ alias args = label;
950+ if (auto result = body(args)) return result;
951+ foreach (i, child; children)
952+ {
953+ if (auto result = child.opApplyImpl2!Body(depth + 1, i + 1 == children.length, body))
954+ return result;
955+ }
956+ return 0;
957+ }
958+ }
959+
960+ void printTree(Tree)(Tree tree)
961+ {
962+ // Selects the unique one with 2 parameters.
963+ // Infers types: size_t and whatever label type the tree has.
964+ foreach (depth, ref label; tree)
965+ {
966+ import std.stdio;
967+ foreach (_; 0 .. depth) write(" ");
968+ writeln(label);
969+ }
970+ }
971+
972+ void main() @safe
973+ {
974+ alias T = Tree!int;
975+ printTree(new T(0, [new T(1, [new T(2), new T(3)]), new T(4, [new T(5)])]));
976+ }
977+ --------------
978+
865979$(H3 $(LEGACY_LNAME2 foreach_with_ranges, foreach-with-ranges, Foreach over Structs and Classes with Ranges))
866980
867981 $(P If the aggregate expression is a struct or class object, but the
@@ -1028,16 +1142,16 @@ $(H3 $(LNAME2 foreach_over_delegates, Foreach over Delegates))
10281142$(SPEC_RUNNABLE_EXAMPLE_RUN
10291143 --------------
10301144 // Custom loop implementation, that iterates over powers of 2 with
1031- // alternating sign. The foreach loop body is passed in dg .
1032- int myLoop(scope int delegate(int) dg )
1145+ // alternating sign. The foreach loop body is passed in `body` .
1146+ int myLoop(scope int delegate(int) body )
10331147 {
10341148 for (int z = 1; z < 128; z *= -2)
10351149 {
1036- auto ret = dg (z);
1150+ auto result = body (z);
10371151
1038- // If the loop body contains a break, ret will be non-zero.
1039- if (ret != 0)
1040- return ret ;
1152+ // If the loop body contains a break, result will be non-zero.
1153+ if (result != 0)
1154+ return result ;
10411155 }
10421156 return 0;
10431157 }
@@ -1583,7 +1697,7 @@ foreach (item; list)
15831697 $(P Any intervening $(RELATIVE_LINK2 try-statement, `finally`) clauses are executed,
15841698 and any intervening synchronization objects are released.)
15851699
1586- $(P $(D Note:) If a `finally` clause executes a `throw` out of the finally
1700+ $(P $(B Note:) If a `finally` clause executes a `throw` out of the finally
15871701 clause, the continue target is never reached.)
15881702
15891703$(H2 $(LEGACY_LNAME2 BreakStatement, break-statement, Break Statement))
0 commit comments