Comment #0 by qs.il.paperinik — 2023-02-02T17:15:13Z
Normally, when `opApply` is a template, `foreach` variable types cannot be inferred and must be stated explicitly. I propose an exception to this rule to make implementing `opApply` with respect to attributes (safe, nogc, pure, nothrow) easier[1, 2].
Basic idea: Make `opApply` a template to infer attributes based on the delegate argument’s type. If `opApply` is a template with a single template type parameter that is used as the type of the single function parameter and is additioanlly constrained to a delegate type, we can use that type to infer `foreach` variable types.
A minimal example:
```d
struct S
{
int opApply(DG : int delegate(int))(DG dg)
{
return 0;
}
}
void main() @safe
{
foreach (x; S()) // infer `int` for x
{ }
}
```
Details:
When all `opApply` function templates in an aggregate have as their first template parameter a type parameter that is “aptly” constrained[5] (“aptly” defined blow), the respective instantiations with the constraint type are valid (i.e. instantiate `opApply(DG : Constraint)` as `opApply!Constraint`), and the resulting template instance (a member function) can be called with 1 argument of the type of the constraint (i.e. `opApply!Constraint(Constraint.init)` compiles), these member function templates are added to non-template `opApply` member functions used to determine `foreach` types.
To be “aptly constrained” means that the constraint is a type of the following form:
```d
int delegate ParameterList MemberFunctionAttributes`
```
where `ParameterList` and `MemberFunctionAttributes` [3, 4] are defined in the D grammar.
The parameters of the `ParameterList` in an “apt” constraint are considered together with `ParameterList`s of non-template `opApply(int delegate ParameterList MemberFunctionAttributes)` (if any) to find the best match in terms of number and `ref`-ness. If the best match for a given `foreach` statement is such a template instance, it is instantiated again with the type of the `foreach` lambda, where the types of the `foreach` body lambda’s parameters are set to the types determied by the constraint in the `opApply` template.
A template `opApply` may have more than one template parameter and more than one function parameter when the additional parameters have defaults.
[1] Relevant thread: https://forum.dlang.org/thread/[email protected]
[2] Relevant post: https://forum.dlang.org/post/[email protected]
[3] https://dlang.org/spec/function.html#Parameters
[4] https://dlang.org/spec/function.html#MemberFunctionAttributes
[5] https://dlang.org/spec/template.html#Constraint
Comment #1 by qs.il.paperinik — 2023-02-06T18:30:37Z
Generalization:
To be “aptly constrained” means that the constraint is a type of the following form:
```d
Int delegate ParameterList MemberFunctionAttributes`
```
where `ParameterList` and `MemberFunctionAttributes` [3, 4] are defined in the D grammar, and Int is `int` with any type constructors (`const`, `inout`, `shared`) applied to it (they don’t matter anyways), but it shouldn’t fail because some type inference made it e.g. `const`.
Comment #2 by nick — 2023-12-12T17:41:14Z
You can use `auto opApply` to infer attributes instead of a template.
Comment #3 by qs.il.paperinik — 2024-05-29T10:15:49Z
(In reply to Nick Treleaven from comment #2)
> You can use `auto opApply` to infer attributes instead of a template.
```d
struct S
{
auto opApply(int delegate(ref int) callback)
{
int x;
return callback(x);
}
}
void main() @safe
{
foreach (ref x; S()) {} // Error: `@safe` function `D main` cannot call `@system` function `S.opApply`
}
```
However, this does (for reasons beyond my understanding):
```d
struct S
{
int opApplyImpl(DG : int delegate(ref int))(scope DG callback)
{
int x;
return callback(x);
}
alias opApply = opApplyImpl!(int delegate(ref int));
}
void main() @safe
{
foreach (ref x; S()) {}
}
```
Comment #4 by nick — 2024-05-31T09:56:45Z
Sorry, yes. This proposal seems workable.
Comment #5 by dlang-bot — 2024-06-26T16:49:29Z
@Bolpat created dlang/dlang.org pull request #3859 "Specify `opApply` as an alias to a function template instance" fixing this issue:
- Fix Bugzilla Issues 23666, 17953, 23116, and 24633
https://github.com/dlang/dlang.org/pull/3859
Comment #6 by robert.schadek — 2024-12-13T19:27:04Z