DIPs
                                
                                 DIPs copied to clipboard
                                
                                    DIPs copied to clipboard
                            
                            
                            
                        DIP4242 (?): Argument dependent attributes (ADAs)
This DIP aims to solve the common problem that arise when mixing delegates and attributes. It describes a language addition which the author feels will be natural to seasoned users that allow describing the relationship between a function's attribute and its callable(s) parameters.
Thanks for the feedback @MoonlightSentinel, comments addressed.
I had a similar idea. Your DIP proposes a pure addition to the language which avoids breakage. My approach contains breakage (in theory), but immediately works for all templates that are written already. You may want to have a look at it: #199
As it stands, using @attribute(param) is illegal if param is not a function pointer or delegate. First note that slices and arrays of delegates are encouraged by the language. You should mention and handle them. Similar how unreachable code is an error in non-template code, but commonly found in templates, also for the sake of generic code, it's generally advisable to allow @attribute(param) as long as param exists. If it is not a function pointer or delegate, just ignore it. Generic code might expect param to be function pointer or delegate or a callable object. As it stands, you force generic code to make a needless distinction.
As it stands, using
@attribute(param)is illegal ifparamis not a function pointer or delegate. [...] As it stands, you force generic code to make a needless distinction.
To address this whole paragraph, my expectation is that generic code with use @attribute(*) which uses all matching parameter, and is explicitly described as supporting the case of no parameter being a delegate / function for this exact reason. The only case where users would want to use @attribute(param) with generic code would be if one of the param is called but not the other, which I suspect will almost never happen. TL;DR generic code should use @attribute(*).
But if such a case happens, generic code is encouraged to use the index variant, which is less error prone.
This is of course my intuition / expectation as designer of this feature and first user, and I suspect the best way to convince people is to provide a working PoC, which I'm workin on.
I'll also check your DIP shortly.
This is going to be very nice to have but two things:
- opApply needs more examples. I have a mixin template to generate opApply's to a templated implementation method to accept any combination of attributes on both the delegate and method.
- This isn't specific to delegates, it's specific to the pointer to a function regardless of how fat it is. It may be better to write it as a function pointer and only upfront and where required say it also applies to delegates.
It might be a late addition, but @tgehr convinced me in several private and public conversations that it’s much more orthogonal and valuable to add “attribute variables”, i.e. a something that (for it to be relevant) occurs two or more times in place of an attribute. As in mathematics, is the same everywhere. D already has something like this with inout, which is a hard-coded “type qualifier variable”, i.e. when a function is called that includes inout in its declaration, it is replaced by immutable, const or nothing (mutable). By hard-coded I mean that you cannot change its name and that there is no way to have multiple “type qualifier variables” in one declaration: You cannot express:
inout₁(int) f(inout₂(bool)[] input, out inout₂(bool)* output) inout₁;
I fear something like this is possibly happening with ADAs. Instead of referring to parameter names (or indices), maybe let e.g. @safe(id) be a general attribute variable, a @safe-ty if you like, that can take the values @safe and @system (@trusted is the same as @safe from the perspective of the caller). What ADAs cannot express, but attribute variables can, would be something like this:
void delegate() @safe(a) hof(void delegate() @safe(a) callback) @safe;
It says that hof is a @safe function that returns a @safe function if and only if its parameter is @safe. Here, a is just an identifier to distinguish it from others.
Closing to ADAs, as syntactic sugar, if a parameter p has a single function pointer or delegate type in its type expression – its type could be void function()[] which is a slice of function pointers, not just a function pointer –, then this function pointer or delegate type implicitly carries @safe(p), pure(p) etc. – meaning that
void hof(void function()[] callbacks) @safe(callbacks);
is equivalent to
void hof(void function() @safe(a)[] callbacks) @safe(a);
Apart from the sugaring, using an attribute variable only once should be an error. If you need the parameter names of packs or unnamed ones, there is __traits(parameters)[i] right there for you to get the i-th parameter name. Today, technically, __traits(parameters) cannot be used in a function’s signature or function template’s contract, but it’s not a big change allowing it.
Just was talking about this on discord, we need a solution for something like:
void foo(T)(T val, void delegate() dg) ???
{
   val.member();
   dg();
}
Basically, foo is a template, you want to infer attributes based on the usage of T, but you also want to color the attributes from the call site based on the non-template dg. How does one specify this?
An alternative that I have been thinking about, what if you just use @called as a delegate/function pointer attribute? e.g. for your first example:
// you wrote
void toString (scope void delegate(in char[]) sink) const
    @safe(sink) pure(sink) nothrow(sink) @nogc(sink)
// I'm thinking:
void toString (scope void delegate(in char[]) @called sink) const
    @safe pure nothrow @nogc;
I think this works, without double-underscores, because nothing custom can go in that position.
you want to infer attributes based on the usage of T
And you could infer @nogc(sink) et al just the same, couldn't you ?
what if you just use
@called
I'll have to think about it, but I think it's not granular enough. Definitely need to add a section in the DIP about that idea, though.
Shouldn’t the @called attribute be a parameter attribute (along the lines of scope)? A function attribute feels wrong. It’s not really part of the function type.
Maybe, but also, you are doing funky things to connect the attributes of the argument to the attributes of the function itself. i.e. it's like a placeholder for the argument that you pass in.
If you put it as a parameter attribute, it probably has to be @__called. That also works.