Trying to use Mir ion, its a perfect example of the failure of D's attribute system

Quirin Schroll qs.il.paperinik at gmail.com
Fri Jan 20 11:49:42 UTC 2023


On Friday, 20 January 2023 at 03:41:02 UTC, Walter Bright wrote:
> On 1/19/2023 7:19 PM, H. S. Teoh wrote:
>> This is a flaw in the language: there is no way to express that
>> `process`'s attributes inherit from the passed-in delegate.  
>> This has
>> been a pain point for years.
>
> void process()(void delegate() userData) {}

Er, no. The template `process` will be inferred `@system thorw 
@gc impure` because the delegate is annotated `@system thorw @gc 
impure` implicitly – unless you don’t actually call it. The 
correct version is this:
```D
void process(DG : void delegate())(DG callback) { callback(); }
```

This has two drawbacks:
1. `process` cannot be virtual.
2. the argument bound to `callback` cannot have its parameter 
types inferred.

By 2. I mean the little more interesting example when the 
delegate takes parameters, say `int`:
```D
/*A*/ void process(void delegate(int) callback) { callback(); }
/*B*/ void process(DG : void delegate(int))(DG callback) { 
callback(); }
```
(The constraint is optional, but useful.)

For version A, when `process` is called, like `process((x){})`, 
the compiler can statically infer that `x` is of type `int`. For 
version B, it cannot, but it does not matter practically for 
functions like `process`, however it does matter for `opApply` 
because programmers using a `foreach` loop really want the type 
of the iteration variable inferred (especially in meta 
programming); this inference only works when `opApply` is not a 
template. `opApply` may still be a template instance (or an alias 
to a template instance), which we can put to use. The shortest I 
could get to:
```d
import std.meta : AliasSeq;
import std.traits : SetFunctionAttributes, functionLinkage, 
functionAttributes, FunctionAttribute;

template WithAnyCombinationOfAttributes(DG)
{
     alias WithAnyCombinationOfAttributes = AliasSeq!();
     static foreach (safety; [ FunctionAttribute.system, 
FunctionAttribute.safe ])
	static foreach (purity; [ FunctionAttribute.none, 
FunctionAttribute.pure_ ])
	static foreach (gcness; [ FunctionAttribute.none, 
FunctionAttribute.nogc ])
	static foreach (exness; [ FunctionAttribute.none, 
FunctionAttribute.nothrow_ ])
     {
         WithAnyCombinationOfAttributes = 
AliasSeq!(WithAnyCombinationOfAttributes, 
SetFunctionAttributes!(DG, functionLinkage!DG, 
(functionAttributes!DG & ~ FunctionAttribute.system) | safety | 
purity | gcness | exness));
     }
}

mixin template opApplyFromImpl(alias impl, protoDG, alias 
_WithAnyCombinationOfAttributes = WithAnyCombinationOfAttributes)
{
     static foreach (DG; _WithAnyCombinationOfAttributes!protoDG)
	alias opApply = impl!DG;
}
```

You can find the code in action 
[here](https://wandbox.org/permlink/rqasKlZTaspJ9gnI). In my 
opinion, something like this should be in Phobos.

The drawbacks are lots of template instantiations and that it 
always generates 16 overloads.


More information about the Digitalmars-d mailing list