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
xinint x;. - D has type variables, e.g. you good old
Tintemplate t(T){..} - D has symbol variables, e.g. template
aliasparameters. - 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.
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.
Permalink
Reply