Thread overview
Parameterized delegate attributes
Sep 28, 2016
Eyal Lotem
Sep 28, 2016
Lodovico Giaretta
Sep 28, 2016
pineapple
Sep 28, 2016
Lodovico Giaretta
September 28, 2016
struct Example {
    int opApply(int delegate(int x) dlg) const {
        return dlg(0);
    }
}

Example.opApply now cannot be used in a @nogc context, even though it is fully @nogc whenever dlg is.

Ditto with nothrow, pure, @safe and other attributes.

For a large code-base containing higher-order functions, this makes use of these attributes nearly impractical.

What I would like to say is something like:

struct Example {
    int opApply(int delegate(int x) dlg G?@nogc T?nothrow P?pure) const @G @T @P {
        return dlg(0);
    }
}

The above would assign G to @nogc or nothing, according to whether the delegate is.
Ditto with T and nothrow, P and pure.

Then they can be used freely in other parts of the type signature.

Unlike templates, these would not require any generated code duplication (they're just for type-checking, after all).

Fixing this along-side the more minor issue of many standard libraries lacking proper annotations -- would make @nogc, pure and nothrow much more practical.
September 28, 2016
On Wednesday, 28 September 2016 at 16:20:18 UTC, Eyal Lotem wrote:
> [...]

I would just make opApply a templated function, for the following reasons:

1) You are saying that having this feature in the language would spare some template instantiations and code generations; but a good compiler should be able to recognize functions with the same code and fold them; for example, if a function takes a generic pointer, chances are it doesn't need to be duplicated for every pointer type.

2) It is actually not true that attributes are only for type checking; the compiler can use them to generate different code: a nothrow function does not need machinery to forward exceptions and clean the stack, for example; so templates are the correct way here, because they can generate different code when needed and also be folded to a single codegen if possible.
September 28, 2016
On Wednesday, 28 September 2016 at 17:00:49 UTC, Lodovico Giaretta wrote:
> but a good compiler should be able to recognize functions with the same code and fold them; for example, if a function takes a generic pointer, chances are it doesn't need to be duplicated for every pointer type.

But using a templated opApply currently breaks type inference in `foreach`, right?

It'd be really nice if that were fixed.
September 28, 2016
On Wednesday, 28 September 2016 at 20:23:10 UTC, pineapple wrote:
> But using a templated opApply currently breaks type inference in `foreach`, right?
>
> It'd be really nice if that were fixed.

Yeah, it would be nice, but I don't think it's technically possible to fix it. The compiler cannot "guess" the correct opApply instantiation if you don't specify the variable type.

<SEMI-OT> This is due to opApply "inversion of control", so that instead of your code calling other code, other code calls yours. For this reason, I think that ranges are way more straightforward and should be preferred whenever possible </SEMI-OT>

By the way, just to show how opApply can be templated successfully to drive attribute inference:

=======================
struct Foo
{
    uint[2] array;

    int opApply(T : int delegate(ref uint))(scope T dg)
    {
	pragma(msg, "Instantiated with " ~ T.stringof);
		
        int result = 0;
		
        for (int i = 0; i < array.length; i++)
        {
            result = dg(array[i]);
            if (result)
                break;
        }
        return result;
    }
}

void main()
{
    Foo a;

    a.array[0] = 73;
    a.array[1] = 82;

    foreach (ref uint u; a)
    {
	(() @system => u++)();
    }

    foreach (uint u; a)
    {
	import std.stdio;
        writeln(u);
    }
}
=======================
September 29, 2016
On 9/28/16 5:35 PM, Lodovico Giaretta wrote:
> On Wednesday, 28 September 2016 at 20:23:10 UTC, pineapple wrote:
>> But using a templated opApply currently breaks type inference in
>> `foreach`, right?
>>
>> It'd be really nice if that were fixed.
>
> Yeah, it would be nice, but I don't think it's technically possible to
> fix it. The compiler cannot "guess" the correct opApply instantiation if
> you don't specify the variable type.

opApply suffers from a few problems. IMO, it should almost always be inlined, so the compiler can "cheat" with attribute inference.

It's an interesting idea to apply inferred attributes to a function based on the given delegate, especially for pure and @nogc. As I understand it, those attributes don't modify generated code at all, they just restrict what you can call (and in the case of strong pure, allow call-site optimizations). If everything you call aside from the delegate is pure and @nogc, then the attributes can be inferred at the call site to be whatever the delegate is, and no separate template generation needs to happen.

nothrow, I think, modifies how the code is generated. But it's possible we could do the same here in a way that allows nothrow to be inferred at call site, but generate the stack unwinding code just in case (so only one function is ultimately emitted).

Note, we may not need to actually add any syntax for this. If you return auto from opApply, you guarantee the code is available, so the compiler can do the inference.

-Steve