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 13:08:46 UTC 2023


On Friday, 20 January 2023 at 05:48:26 UTC, Steven Schveighoffer 
wrote:
> On 1/19/23 10:38 PM, Walter Bright wrote:
>> On 1/19/2023 6:18 PM, Adam D Ruppe wrote:
>>> Ideally, you'd be able to say `process` inherits the 
>>> nogc-ness of `userData`.
>> 
>> That's exactly what template attribute inference does.
>> 
> […]
>
> What we want is the effective attributes of the function to be 
> the most restrictive possible of both the code inside the 
> function, and the argument to the function.
>
> The compiler would need a way to specify this for non-inferred 
> functions, but it could be inferred for templates and auto 
> functions.

I wrote a DIP (_Attributes for Higher-Order Functions,_ AfHOF) 
for that, but didn’t make it. I tried to be pragmatic and squeeze 
it into the current syntax via doubling-down on a bug in the type 
system (i.e. making it a ‘feature’). Timon Gehr made me aware of 
the bug. Writing the DIP made me aware that higher-order 
functions are a blocker for making e.g. `@safe` the default: If 
you have `void f(void delegate() dg)`, there is no consistent way 
to add `@safe` to `f` and/or `dg` that won’t break properly 
annotated `@system` code.

Mathias Lang has a work-in-progress DIP called _Argument 
dependent attributes_ (AdA). It introduces syntax to specify that 
the attributes of higher-order function (i.e. a function with 
callbacks) depend on the attributes of the callback arguments. 
It’s a half-way solution that only targets parameters of delegate 
or function pointer type. A full solution would include 
parameters of composed types that include delegate or function 
pointer types somewhere: Slices of delegate or function pointer 
type could be considered reasonably common. Attribute variables 
would do that.

* D has runtime value variables, e.g. your good old `x` in `int 
x;`.
* D has type variables, e.g. you good old `T` in `template 
t(T){..}`
* D has symbol variables, e.g. template `alias` parameters.
* D has 1 type constructor variable: `inout`. (D were more 
consistent if it allowed for two or more independent type 
constructor variables, but that’s rarely needed practically.)
* D has no attribute variables.

If D added 1 fixed-name attribute variable per attribute (cf. 
`inout`), they could be spelled `@safety`, `purity`, `@gcness`, 
and `throwiness`.

```d
void callInSequence(
     void delegate() @safety purity @gcness throwiness[] dgs
) @safety purity @gcness throwiness
{
     foreach (dg; dgs) dg();
}
```
When called, the argument is a slice of delegates that are 
`@safe` or `@system` – `@safety` is replaced by that; that are 
`pure` or not – purity is replaced by that; that are `@nogc` or 
not – `@gcness` is replaced by that; that may throw or not – 
`throwiness` by `throw` or `nothrow`.

This works the same way as when `inout(int)[] f() inout` is 
called on a mutable, `const` or `immutable` object, its return 
type will be `int[]`, `const(int)[]`, or `immutable(int)[]` after 
replacing `inout` with the call-side type constructor applied to 
the object’s type.


More information about the Digitalmars-d mailing list