From a4ff66ed004f14bdb45be994010925c238990ceb Mon Sep 17 00:00:00 2001 From: Quirin Schroll Date: Wed, 26 Jun 2024 18:30:34 +0200 Subject: [PATCH 01/10] Fix Bugzilla Issues 23666, 17953, and 24633 --- spec/statement.dd | 250 +++++++++++++++++++++++++++++++++------------- 1 file changed, 182 insertions(+), 68 deletions(-) diff --git a/spec/statement.dd b/spec/statement.dd index 0e2d106a0c..5e1a9a6426 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -698,32 +698,25 @@ foreach (string s, double d; aa) $(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes with `opApply`)) - $(P If the aggregate expression is a struct or class object, - the $(D foreach) is defined by the - special $(LEGACY_LNAME2 opApply, op-apply, $(D opApply)) member function, and the - `foreach_reverse` behavior is defined by the special + $(P If the aggregate expression is a `struct` or `class` object, + iteration with $(D foreach) can be defined by implementing the + $(LEGACY_LNAME2 opApply, op-apply, $(D opApply)) member function, and the + `foreach_reverse` behavior is defined by the $(LEGACY_LNAME2 opApplyReverse, op-apply-reverse, $(D opApplyReverse)) member function. These functions must each have the signature below: ) $(GRAMMAR -$(GNAME OpApplyDeclaration): - `int opApply` `(` `scope` `int delegate` `(` $(I OpApplyParameters) `)` `dg` `)` `;` + `int opApply` `(` `scope` `int delegate` `(` $(I OpApplyParameters) `)` `body` `)` `;` $(GNAME OpApplyParameters): - *OpApplyParameter* - *OpApplyParameter*, *OpApplyParameters* + $(GLINK ParameterDeclaration) + $(GLINK ParameterDeclaration), *OpApplyParameters* $(GNAME OpApplyParameter): - $(GLINK ForeachTypeAttributes)$(OPT) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) + $(GLINK ParameterStorageClass)$(OPT) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) ) - $(P where each $(I OpApplyParameter) of `dg` must match a $(GLINK ForeachType) - in a $(GLINK ForeachStatement), - otherwise the *ForeachStatement* will cause an error.) - - $(P Any *ForeachTypeAttribute* cannot be `enum`.) - $(PANEL To support a `ref` iteration variable, the delegate must take a `ref` parameter: @@ -731,43 +724,62 @@ $(GNAME OpApplyParameter): --- struct S { - int opApply(scope int delegate(ref uint n) dg); + uint n; + int opApply(scope int delegate(ref uint) body) => body(n); } - void f(S s) + void main() { + S s; foreach (ref uint i; s) - i++; + { + i++; // effectively s.n++ + } + foreach (i; s) + { + static assert(is(typeof(i) == uint)); + assert(i == 1); + } } --- ) - Above, `opApply` is still matched when `i` is not `ref`, so by using - a `ref` delegate parameter both forms are supported. - ) + Above, `opApply` is matched both when `i` is and is not `ref`, so by using + a `ref` delegate parameter, both forms are supported. - $(P There can be multiple $(D opApply) and $(D opApplyReverse) functions - - one is selected - by matching each parameter of `dg` to each $(I ForeachType) - declared in the $(I ForeachStatement).) + Stating the type of the variable like in the first loop is optional, + as is showcased in the second loop, where the type is inferred. + ) - $(P The body of the apply - function iterates over the elements it aggregates, passing each one - in successive calls to the `dg` delegate. The delegate return value - determines whether to interrupt iteration:) + $(P The apply + function iterates over the elements it aggregates by passing each one + in successive calls to the `body` delegate. The delegate return value + of the `body` delegate + determines how to proceed iteration:) $(UL - $(LI If the result is nonzero, apply must cease + $(LI If the result is nonzero, the apply function must cease iterating and return that value.) - $(LI If the result is 0, then iteration should continue. + $(LI If the result is `0`, then iteration should continue. If there are no more elements to iterate, - apply must return 0.) + the apply function must return `0`.) ) - $(P The result of calling the delegate will be nonzero if the *ForeachStatement* + $(P The result of the `body` delegate will be nonzero if the *ForeachStatement* body executes a matching $(GLINK BreakStatement), $(GLINK ReturnStatement), or $(GLINK GotoStatement) whose matching label is outside the *ForeachStatement*. ) - $(P For example, consider a class that is a container for two elements:) + $(P The $(D opApply) and $(D opApplyReverse) member functions can be overloaded. + Selection works similar to overload resolution by + comparing the number of `foreach` variables and number of parameters of `body` and, + if more than one overload remains unique overload, + matching the parameter types of `body` and each $(I ForeachType) + declared in the $(I ForeachStatement) when given.) + + $(BEST_PRACTICE Overload apply functions only with delegates differing in number of parameters + to enable type inference for `foreach` variables. + ) + + $(P For example, consider a class that is a container for some elements:) $(SPEC_RUNNABLE_EXAMPLE_RUN -------------- @@ -775,11 +787,11 @@ $(GNAME OpApplyParameter): { uint[] array; - int opApply(scope int delegate(ref uint) dg) + int opApply(scope int delegate(ref uint) body) { foreach (e; array) { - int result = dg(e); + int result = body(e); if (result) return result; } @@ -811,57 +823,159 @@ $(CONSOLE 73 82 ) - $(PANEL - The `scope` storage class on the $(D dg) parameter means that the delegate does - not escape the scope of the $(D opApply) function (an example would be assigning $(D dg) to a - global variable). If it cannot be statically guaranteed that $(D dg) does not escape, a closure may - be allocated for it on the heap instead of the stack. $(BEST_PRACTICE Annotate delegate parameters to `opApply` functions with `scope` when possible.) + + $(PANEL + The `scope` storage class on the `body` parameter means that the delegate does + not escape the scope of the apply function (an example would be assigning`body` to a + global variable). If it cannot be statically guaranteed that `body` does not escape, a closure may + be allocated for it on the heap instead of the stack. ) - $(P $(B Important:) If $(D opApply) catches any exceptions, ensure that those - exceptions did not originate from the delegate passed to $(D opApply). The user would expect + $(P $(B Important:) If apply functions catch any exceptions, ensure that those + exceptions did not originate from the delegate. The user would expect exceptions thrown from a `foreach` body to both terminate the loop, and propagate outside the `foreach` body. ) $(H4 $(LNAME2 template-op-apply, Template `opApply`)) - $(P $(D opApply) can also be a templated function, - which will infer the types of parameters based on the $(I ForeachStatement). - For example:) + $(P `opApply` and `opApplyReverse` can also be a function templates, + which can optionally infer the types of parameters based on the $(I ForeachStatement). + ) + + $(P $(B Note:) An apply function template cannot infer `foreach` variable types.) $(SPEC_RUNNABLE_EXAMPLE_RUN -------------- struct S { - import std.traits : ParameterTypeTuple; // introspection template - import std.stdio; - - int opApply(Dg)(scope Dg dg) - if (ParameterTypeTuple!Dg.length == 2) // foreach with 2 parameters + int opApply(Body)(scope Body body) { - writeln(2); - return 0; - } - - int opApply(Dg)(scope Dg dg) - if (ParameterTypeTuple!Dg.length == 3) // foreach with 3 parameters - { - writeln(3); + pragma(msg, Body); return 0; } } void main() { - foreach (int a, int b; S()) { } // calls first opApply function - foreach (int a, int b, float c; S()) { } // calls second opApply function + foreach (int a, int b; S()) { } // int delegate(ref int, ref int) pure nothrow @nogc @safe + foreach (bool b, string s; S()) { } // int delegate(ref bool, ref string) pure nothrow @nogc @safe + //foreach (x; S()) { } // Error: cannot infer type for `foreach` variable `x`, perhaps set it explicitly } -------------- ) +$(H4 $(LNAME2 template-instance-op-apply, `opApply` as an alias to a explicit function template instance)) + + $(P `opApply` and `opApplyReverse` can be aliases to an appropriate function or function template. + However, special treatment is given to apply functions that are aliases of an appropriate function template instance.) + + $(P In that case, the function template instance is used for overload selection and `foreach` variable type inference, + but the template is instantiated again with the actual delegate type of the `foreach` body. + This way, apply functions can infer attributes depending on the attributes of `body` delegate.) + + $(SPEC_RUNNABLE_EXAMPLE_RUN + -------------- + struct A + { + int opApply(scope int delegate(long) body) => body(42); + } + struct B + { + int opApply(Body)(scope Body body) => body(42); + } + struct C + { + int opApplyImpl(Body)(scope Body body) => body(42); + alias opApply = opApplyImpl!(int delegate(long)); + } + void main() @nogc nothrow pure @safe + { + // Error: `@nogc` function `D main` cannot call non-@nogc function `onlineapp.A.opApply` + // Error: `pure` function `D main` cannot call impure function `onlineapp.A.opApply` + // Error: `@safe` function `D main` cannot call `@system` function `onlineapp.A.opApply` + // Error: function `onlineapp.A.opApply` is not `nothrow` + static assert(!__traits(compiles, () @safe { + foreach (x; A()) { } + })); + + // Error: cannot infer type for `foreach` variable `x`, perhaps set it explicitly + static assert(!__traits(compiles, { + foreach (x; B()) { } + })); + + // Good: + foreach (x; C()) + { + static assert(is(typeof(x) == long)); + assert(x == 42); + } + } + -------------- + ) + + $(P The `opApplyImpl` pattern is generally preferable to + overloading many apply functions with all possible combinations of attributes.) + + $(P Multiple apply function aliases can exist, and selection and `foreach` variable type inference work:) + + -------------- + class Tree(T) + { + private T label; + private Tree[] children; + + this(T label, Tree[] children = null) + { + this.label = label; + this.children = children; + } + + alias opApply = opApplyImpl!(int delegate(ref T label)); + alias opApply = opApplyImpl!(int delegate(size_t depth, ref T label)); + alias opApply = opApplyImpl!(int delegate(size_t depth, bool isLastChild, ref T label)); + + int opApplyImpl(Body)(scope Body body) => opApplyImpl2(0, true, body); + int opApplyImpl2(Body)(size_t depth, bool lastChild, scope Body body) + { + import std.meta : AliasSeq; + static if (is(Body : int delegate(size_t, bool, ref T))) + alias args = AliasSeq!(depth, lastChild, label); + else static if (is(Body : int delegate(size_t, ref T))) + alias args = AliasSeq!(depth, label); + else + alias args = label; + if (auto result = body(args)) return result; + foreach (i, child; children) + { + if (auto result = child.opApplyImpl2!Body(depth + 1, i + 1 == children.length, body)) + return result; + } + return 0; + } + } + + void printTree(Tree)(Tree tree) + { + // Selects the unique one with 2 parameters. + // Infers types: size_t and whatever label type the tree has. + foreach (depth, ref label; tree) + { + import std.stdio; + foreach (_; 0 .. depth) write(" "); + writeln(label); + } + } + + void main() @safe + { + alias T = Tree!int; + printTree(new T(0, [new T(1, [new T(2), new T(3)]), new T(4, [new T(5)])])); + } + -------------- + $(H3 $(LEGACY_LNAME2 foreach_with_ranges, foreach-with-ranges, Foreach over Structs and Classes with Ranges)) $(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)) $(SPEC_RUNNABLE_EXAMPLE_RUN -------------- // Custom loop implementation, that iterates over powers of 2 with - // alternating sign. The foreach loop body is passed in dg. - int myLoop(scope int delegate(int) dg) + // alternating sign. The foreach loop body is passed in `body`. + int myLoop(scope int delegate(int) body) { for (int z = 1; z < 128; z *= -2) { - auto ret = dg(z); + auto result = body(z); - // If the loop body contains a break, ret will be non-zero. - if (ret != 0) - return ret; + // If the loop body contains a break, result will be non-zero. + if (result != 0) + return result; } return 0; } @@ -1583,7 +1697,7 @@ foreach (item; list) $(P Any intervening $(RELATIVE_LINK2 try-statement, `finally`) clauses are executed, and any intervening synchronization objects are released.) - $(P $(D Note:) If a `finally` clause executes a `throw` out of the finally + $(P $(B Note:) If a `finally` clause executes a `throw` out of the finally clause, the continue target is never reached.) $(H2 $(LEGACY_LNAME2 BreakStatement, break-statement, Break Statement)) From 132cad9251c85a31596c2aa96bff3a1a52e35bac Mon Sep 17 00:00:00 2001 From: "Quirin F. Schroll" Date: Thu, 27 Jun 2024 17:58:15 +0200 Subject: [PATCH 02/10] Apply suggestions from code review Co-authored-by: Nick Treleaven --- spec/statement.dd | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/statement.dd b/spec/statement.dd index 5e1a9a6426..db2a1bf1c4 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -745,7 +745,7 @@ $(GNAME OpApplyParameter): Above, `opApply` is matched both when `i` is and is not `ref`, so by using a `ref` delegate parameter, both forms are supported. - Stating the type of the variable like in the first loop is optional, + Stating the type of the variable (as in the first loop) is optional, as is showcased in the second loop, where the type is inferred. ) @@ -770,10 +770,10 @@ $(GNAME OpApplyParameter): $(P The $(D opApply) and $(D opApplyReverse) member functions can be overloaded. Selection works similar to overload resolution by - comparing the number of `foreach` variables and number of parameters of `body` and, - if more than one overload remains unique overload, - matching the parameter types of `body` and each $(I ForeachType) - declared in the $(I ForeachStatement) when given.) + comparing the number of `foreach` variables with the number of parameters of `body` and, + if more than one overload remains, + matching each parameter of `body` against each $(I ForeachType) + declared in the $(I ForeachStatement).) $(BEST_PRACTICE Overload apply functions only with delegates differing in number of parameters to enable type inference for `foreach` variables. @@ -828,12 +828,12 @@ $(CONSOLE $(PANEL The `scope` storage class on the `body` parameter means that the delegate does - not escape the scope of the apply function (an example would be assigning`body` to a + not escape the scope of the apply function (e.g. assigning `body` to a global variable). If it cannot be statically guaranteed that `body` does not escape, a closure may be allocated for it on the heap instead of the stack. ) - $(P $(B Important:) If apply functions catch any exceptions, ensure that those + $(P $(B Important:) If an apply function catches any exceptions, ensure that those exceptions did not originate from the delegate. The user would expect exceptions thrown from a `foreach` body to both terminate the loop, and propagate outside the `foreach` body. @@ -841,7 +841,7 @@ $(CONSOLE $(H4 $(LNAME2 template-op-apply, Template `opApply`)) - $(P `opApply` and `opApplyReverse` can also be a function templates, + $(P `opApply` and `opApplyReverse` can also be function templates, which can optionally infer the types of parameters based on the $(I ForeachStatement). ) From eb2e5786b2b792d3038add46ea3610816a5a9033 Mon Sep 17 00:00:00 2001 From: Quirin Schroll Date: Thu, 27 Jun 2024 19:54:29 +0200 Subject: [PATCH 03/10] Address code review: Remove `OpApplyParameter` --- spec/statement.dd | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/spec/statement.dd b/spec/statement.dd index db2a1bf1c4..4e49b2c123 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -703,22 +703,11 @@ $(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes $(LEGACY_LNAME2 opApply, op-apply, $(D opApply)) member function, and the `foreach_reverse` behavior is defined by the $(LEGACY_LNAME2 opApplyReverse, op-apply-reverse, $(D opApplyReverse)) member function. - These functions must each have the signature below: + These functions must each take exactly one function parameter of delegate type. ) -$(GRAMMAR - `int opApply` `(` `scope` `int delegate` `(` $(I OpApplyParameters) `)` `body` `)` `;` - -$(GNAME OpApplyParameters): - $(GLINK ParameterDeclaration) - $(GLINK ParameterDeclaration), *OpApplyParameters* - -$(GNAME OpApplyParameter): - $(GLINK ParameterStorageClass)$(OPT) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) -) - $(PANEL - To support a `ref` iteration variable, the delegate must take a `ref` parameter: + To support a `ref` iteration, the delegate must take `ref` parameters: $(SPEC_RUNNABLE_EXAMPLE_COMPILE --- @@ -729,7 +718,7 @@ $(GNAME OpApplyParameter): } void main() { - S s; + S s; foreach (ref uint i; s) { i++; // effectively s.n++ From c7bee1387cd2af309cef0332d8152ccc09a51736 Mon Sep 17 00:00:00 2001 From: Quirin Schroll Date: Thu, 27 Jun 2024 22:33:05 +0200 Subject: [PATCH 04/10] Fix phrasing, layout, and highlighting --- spec/statement.dd | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/spec/statement.dd b/spec/statement.dd index 4e49b2c123..8fa634e875 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -740,16 +740,15 @@ $(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes $(P The apply function iterates over the elements it aggregates by passing each one - in successive calls to the `body` delegate. The delegate return value - of the `body` delegate - determines how to proceed iteration:) + in successive calls to the `body` delegate. The return value + of the `body` delegate determines how to proceed:) $(UL - $(LI If the result is nonzero, the apply function must cease + $(LI If the result is nonzero, the apply function $(B must) cease iterating and return that value.) $(LI If the result is `0`, then iteration should continue. If there are no more elements to iterate, - the apply function must return `0`.) + the apply function $(B must) return `0`.) ) $(P The result of the `body` delegate will be nonzero if the *ForeachStatement* @@ -822,9 +821,9 @@ $(CONSOLE be allocated for it on the heap instead of the stack. ) - $(P $(B Important:) If an apply function catches any exceptions, ensure that those + $(NOTE If an apply function catches any exceptions, ensure that those exceptions did not originate from the delegate. The user would expect - exceptions thrown from a `foreach` body to both terminate the loop, and propagate outside + exceptions thrown from a `foreach` body to both terminate the loop and propagate outside the `foreach` body. ) @@ -834,7 +833,7 @@ $(H4 $(LNAME2 template-op-apply, Template `opApply`)) which can optionally infer the types of parameters based on the $(I ForeachStatement). ) - $(P $(B Note:) An apply function template cannot infer `foreach` variable types.) + $(NOTE An apply function template cannot infer `foreach` variable types.) $(SPEC_RUNNABLE_EXAMPLE_RUN -------------- @@ -856,12 +855,12 @@ $(H4 $(LNAME2 template-op-apply, Template `opApply`)) -------------- ) -$(H4 $(LNAME2 template-instance-op-apply, `opApply` as an alias to a explicit function template instance)) +$(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit Function Template Instance)) - $(P `opApply` and `opApplyReverse` can be aliases to an appropriate function or function template. + $(P Like any other function, `opApply` and `opApplyReverse` can be aliases to a function or function template. However, special treatment is given to apply functions that are aliases of an appropriate function template instance.) - $(P In that case, the function template instance is used for overload selection and `foreach` variable type inference, + $(P In that case, the aliased function template instance is used for overload selection and `foreach` variable type inference, but the template is instantiated again with the actual delegate type of the `foreach` body. This way, apply functions can infer attributes depending on the attributes of `body` delegate.) @@ -905,10 +904,11 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an alias to a explicit fu -------------- ) - $(P The `opApplyImpl` pattern is generally preferable to + $(PANEL The `opApplyImpl` pattern is generally preferable to overloading many apply functions with all possible combinations of attributes.) - $(P Multiple apply function aliases can exist, and selection and `foreach` variable type inference work:) + $(P Multiple apply function aliases can exist, and selection and `foreach` variable type inference work. + They can alias instances of the same function template or different function templates.) -------------- class Tree(T) @@ -1686,7 +1686,7 @@ foreach (item; list) $(P Any intervening $(RELATIVE_LINK2 try-statement, `finally`) clauses are executed, and any intervening synchronization objects are released.) - $(P $(B Note:) If a `finally` clause executes a `throw` out of the finally + $(NOTE If a `finally` clause executes a `throw` out of the finally clause, the continue target is never reached.) $(H2 $(LEGACY_LNAME2 BreakStatement, break-statement, Break Statement)) From e61e4777ee513ede3938fc58153ded45618cf0c3 Mon Sep 17 00:00:00 2001 From: Quirin Schroll Date: Thu, 27 Jun 2024 23:05:56 +0200 Subject: [PATCH 05/10] Revise initial example; use Tree as an example more --- spec/statement.dd | 67 +++++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/spec/statement.dd b/spec/statement.dd index 8fa634e875..fea2b829e8 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -718,7 +718,7 @@ $(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes } void main() { - S s; + S s = S(41); foreach (ref uint i; s) { i++; // effectively s.n++ @@ -726,7 +726,7 @@ $(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes foreach (i; s) { static assert(is(typeof(i) == uint)); - assert(i == 1); + assert(i == 42); } } --- @@ -771,35 +771,41 @@ $(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes $(SPEC_RUNNABLE_EXAMPLE_RUN -------------- - class Foo + class Tree { - uint[] array; + int label; + Tree[] children; - int opApply(scope int delegate(ref uint) body) + this(int l) @safe { label = l; } + + int opApply(scope int delegate(ref int) @safe body) @safe { - foreach (e; array) + int result = body(label); + if (result != 0) return result; + + foreach (child; children) { - int result = body(e); - if (result) - return result; + int childResult = child.opApply(body); + if (childResult != 0) return childResult; } return 0; } } - void main() + void main() @safe { import std.stdio; - Foo a = new Foo(); - a.array = [73, 82, 2, 9]; + Tree tree = new Tree(1); + tree.children = [ new Tree(2), new Tree(6) ]; + tree.children[0].children = [ new Tree(3) ]; + tree.children[1].children = [ new Tree(7), new Tree(8) ]; + tree.children[0].children[0].children = [ new Tree(4), new Tree(5) ]; - foreach (uint u; a) + foreach (int label; tree) { - if (u < 5) - break; - - writeln(u); + writeln(label); + if (label == 4) break; } } -------------- @@ -808,8 +814,10 @@ $(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes $(P This would print:) $(CONSOLE -73 -82 +1 +2 +3 +4 ) $(BEST_PRACTICE Annotate delegate parameters to `opApply` functions with `scope` when possible.) @@ -868,16 +876,19 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F -------------- struct A { + // Vanilla int opApply(scope int delegate(long) body) => body(42); } struct B { + // Template int opApply(Body)(scope Body body) => body(42); } struct C { - int opApplyImpl(Body)(scope Body body) => body(42); + // Alias to an explicit function template instance alias opApply = opApplyImpl!(int delegate(long)); + int opApplyImpl(Body)(scope Body body) => body(42); } void main() @nogc nothrow pure @safe { @@ -885,6 +896,7 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F // Error: `pure` function `D main` cannot call impure function `onlineapp.A.opApply` // Error: `@safe` function `D main` cannot call `@system` function `onlineapp.A.opApply` // Error: function `onlineapp.A.opApply` is not `nothrow` + // (You might not encounter all of these errors at once.) static assert(!__traits(compiles, () @safe { foreach (x; A()) { } })); @@ -894,7 +906,7 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F foreach (x; B()) { } })); - // Good: + // Good foreach (x; C()) { static assert(is(typeof(x) == long)); @@ -953,7 +965,7 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F foreach (depth, ref label; tree) { import std.stdio; - foreach (_; 0 .. depth) write(" "); + foreach (_; 0 .. depth) write("--"); writeln(label); } } @@ -965,6 +977,17 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F } -------------- + $(P This would print:) + +$(CONSOLE +0 +--1 +----2 +----3 +--4 +----5 +) + $(H3 $(LEGACY_LNAME2 foreach_with_ranges, foreach-with-ranges, Foreach over Structs and Classes with Ranges)) $(P If the aggregate expression is a struct or class object, but the From 698bbdadb13a5e2fdc908afebff8c688a2ec4de0 Mon Sep 17 00:00:00 2001 From: Quirin Schroll Date: Thu, 27 Jun 2024 23:12:00 +0200 Subject: [PATCH 06/10] Fix Error with --- --- spec/statement.dd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/statement.dd b/spec/statement.dd index fea2b829e8..a3e3e7f4bf 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -965,7 +965,7 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F foreach (depth, ref label; tree) { import std.stdio; - foreach (_; 0 .. depth) write("--"); + foreach (_; 0 .. depth) write("::"); writeln(label); } } @@ -981,11 +981,11 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F $(CONSOLE 0 ---1 -----2 -----3 ---4 -----5 +::1 +::::2 +::::3 +::4 +::::5 ) $(H3 $(LEGACY_LNAME2 foreach_with_ranges, foreach-with-ranges, Foreach over Structs and Classes with Ranges)) From 31976bbbe491e6b5a618bcfa405e4f54aff9247d Mon Sep 17 00:00:00 2001 From: Quirin Schroll Date: Thu, 27 Jun 2024 23:42:12 +0200 Subject: [PATCH 07/10] Fix minor phrasing errors --- spec/statement.dd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/statement.dd b/spec/statement.dd index a3e3e7f4bf..935617d4c4 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -820,7 +820,7 @@ $(CONSOLE 4 ) - $(BEST_PRACTICE Annotate delegate parameters to `opApply` functions with `scope` when possible.) + $(BEST_PRACTICE Annotate delegate parameters to apply functions with `scope` when possible.) $(PANEL The `scope` storage class on the `body` parameter means that the delegate does @@ -870,7 +870,7 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F $(P In that case, the aliased function template instance is used for overload selection and `foreach` variable type inference, but the template is instantiated again with the actual delegate type of the `foreach` body. - This way, apply functions can infer attributes depending on the attributes of `body` delegate.) + This way, apply functions can infer attributes depending on the attributes of the `body` delegate.) $(SPEC_RUNNABLE_EXAMPLE_RUN -------------- From 58e1e5b7cfb740d392de545511998a7d8f9a0c14 Mon Sep 17 00:00:00 2001 From: Quirin Schroll Date: Fri, 28 Jun 2024 15:31:10 +0200 Subject: [PATCH 08/10] Code review: Make example runnable --- spec/statement.dd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/statement.dd b/spec/statement.dd index 935617d4c4..43b93b8105 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -922,6 +922,7 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F $(P Multiple apply function aliases can exist, and selection and `foreach` variable type inference work. They can alias instances of the same function template or different function templates.) + $(SPEC_RUNNABLE_EXAMPLE_RUN -------------- class Tree(T) { @@ -976,6 +977,7 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F printTree(new T(0, [new T(1, [new T(2), new T(3)]), new T(4, [new T(5)])])); } -------------- + ) $(P This would print:) From bc0fe5542233b3e460c5a23e65eb840117b6618d Mon Sep 17 00:00:00 2001 From: Quirin Schroll Date: Fri, 28 Jun 2024 15:32:43 +0200 Subject: [PATCH 09/10] Make some part-sentences non-paragraphs. --- spec/statement.dd | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/spec/statement.dd b/spec/statement.dd index 43b93b8105..72aa2c81f4 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -220,7 +220,7 @@ $(GNAME DeclarationStatement): $(GLINK2 declaration, StorageClasses)$(OPT) $(GLINK2 declaration, Declaration) ) - $(P Some declaration statements:) + $(P Examples of statements:) ---- int a; // declare a as type int and initialize it to 0 @@ -642,8 +642,7 @@ foreach (char c; b) -------------- ) - $(P which would print: - ) + which would print: $(CONSOLE 'a' @@ -811,7 +810,7 @@ $(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes -------------- ) - $(P This would print:) +This would print: $(CONSOLE 1 @@ -979,7 +978,7 @@ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit F -------------- ) - $(P This would print:) +This would print: $(CONSOLE 0 @@ -1017,7 +1016,7 @@ $(H3 $(LEGACY_LNAME2 foreach_with_ranges, foreach-with-ranges, Foreach over Stru foreach (e; range) { ... } --- - $(P translates to:) +translates to: --- for (auto __r = range; !__r.empty; __r.popFront()) @@ -1049,7 +1048,7 @@ $(H3 $(LEGACY_LNAME2 foreach_with_ranges, foreach-with-ranges, Foreach over Stru foreach_reverse (e; range) { ... } --- - $(P translates to:) +translates to: --- for (auto __r = range; !__r.empty; __r.popBack()) @@ -1258,7 +1257,7 @@ void fun() ----- ) - $(P Output:) +prints: $(CONSOLE 7.4 has type double @@ -1544,7 +1543,7 @@ $(GNAME LastExp): case 1: .. case 3: --- - $(P The above is equivalent to:) +The above is equivalent to: --- case 1, 2, 3: @@ -1741,7 +1740,7 @@ foreach (i; 2 .. n) writeln("finished"); --- ) - $(P Output:) +prints: $(CONSOLE Trying: 2 @@ -1924,7 +1923,7 @@ with (expression) } --- - is semantically equivalent to: + is roughly equivalent to: -------------- (auto ref tmp) From 85c520dc9c41308276791a50650ee7bc823fecde Mon Sep 17 00:00:00 2001 From: "Quirin F. Schroll" Date: Fri, 28 Jun 2024 15:52:32 +0200 Subject: [PATCH 10/10] Rephrase subsection heading Co-authored-by: Nick Treleaven --- spec/statement.dd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/statement.dd b/spec/statement.dd index 72aa2c81f4..3dd8530eee 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -862,7 +862,7 @@ $(H4 $(LNAME2 template-op-apply, Template `opApply`)) -------------- ) -$(H4 $(LNAME2 template-instance-op-apply, `opApply` as an Alias to an Explicit Function Template Instance)) +$(H4 $(LNAME2 template-instance-op-apply, `opApply` with Attribute Inference)) $(P Like any other function, `opApply` and `opApplyReverse` can be aliases to a function or function template. However, special treatment is given to apply functions that are aliases of an appropriate function template instance.)