This is a copy of a failing TDPL example. First, the compiler doesn't accept the delegate literal syntax unless it includes the keyword "delegate". Fixing that somehow makes the compiler decide the literal is @system and refuses binding it.
module test;// Context begin
#line 1369 "0900-operators.tex"
import std.variant;
alias Variant delegate(Dynamic self, Variant[] args...) DynMethod;
// Context end
// Context begin
#line 1383 "0900-operators.tex"
class Dynamic {
private DynMethod[string] methods;
void addMethod(string name, DynMethod m) {
methods[name] = m;
}
void removeMethod(string name) {
methods.remove(name);
}
// Dispatch dynamically on method
Variant call(string methodName, Variant[] args...) {
return methods[methodName](this, args);
}
// Provide syntactic sugar with opDispatch
Variant opDispatch(string m, Args)(Args args...) {
Variant[] packedArgs = new Variant[args.length];
foreach (i, arg; args) {
packedArgs[i] = Variant(arg);
}
return call(m, args);
}
}
// Context end
#line 1409 "0900-operators.tex"
unittest {
import std.stdio;
auto obj = new Dynamic;
obj.addMethod("sayHello",
delegate Variant(Dynamic, Variant[]) {
writeln("Hello, world!");
return Variant();
});
obj.sayHello(); // Prints "Hello, world!"
}
void main(){}
Comment #1 by k.hara.pg — 2011-12-30T23:28:51Z
This is not the delegate literal type deduction problem.
It is a trivial mismatching of delegate types.
This is the reduced code to explain the problem.
----
struct Variant {}
class Dynamic {}
alias Variant delegate(Dynamic self, Variant[] args...) DynMethod;
void main()
{
DynMethod dg = delegate Variant(Dynamic, Variant[]) {
return Variant();
};
}
Output:
----
test.d(6): Error: cannot implicitly convert expression (__dgliteral1) of type
Variant delegate(Dynamic _param_0, Variant[] _param_1) pure nthrow @safe to
Variant delegate(Dynamic self, Variant[] args...)
DynMethod has typesafe variadic parameter, but literal doesn't have.
So this is a bug of TDPL sample code, not compiler's.
Comment #2 by andrei — 2011-12-31T00:40:00Z
(In reply to comment #1)
> This is not the delegate literal type deduction problem.
> It is a trivial mismatching of delegate types.
>
> This is the reduced code to explain the problem.
> ----
> struct Variant {}
> class Dynamic {}
> alias Variant delegate(Dynamic self, Variant[] args...) DynMethod;
> void main()
> {
> DynMethod dg = delegate Variant(Dynamic, Variant[]) {
> return Variant();
> };
> }
>
> Output:
> ----
> test.d(6): Error: cannot implicitly convert expression (__dgliteral1) of type
> Variant delegate(Dynamic _param_0, Variant[] _param_1) pure nthrow @safe to
> Variant delegate(Dynamic self, Variant[] args...)
>
> DynMethod has typesafe variadic parameter, but literal doesn't have.
> So this is a bug of TDPL sample code, not compiler's.
I think (and perhaps I'm wrong) that the signature with ... and the one without must be equivalent. The ... makes a difference only in the call syntax, but in fact still passes an array. (Note that the variadics ending with "T[] param..." are not unsafe C-style variadics.) Please advise - thanks!
Comment #3 by k.hara.pg — 2011-12-31T01:01:24Z
(In reply to comment #2)
> I think (and perhaps I'm wrong) that the signature with ... and the one without
> must be equivalent. The ... makes a difference only in the call syntax, but in
> fact still passes an array. (Note that the variadics ending with "T[] param..."
> are not unsafe C-style variadics.) Please advise - thanks!
Hmm, current compiler always raise an error against the difference of variadic argument kind (non-variarics vs typesafe-variadics vs C-style variadics).
But, from http://d-programming-language.org/abi.html
> The variadic part is converted to a dynamic array and the rest is the same as for non-variadic functions.
So, in ABI layer, (..., T[]) and (..., T[]...) might be same...
Comment #4 by clugdbug — 2011-12-31T03:12:50Z
(In reply to comment #3)
> (In reply to comment #2)
> > I think (and perhaps I'm wrong) that the signature with ... and the one without
> > must be equivalent. The ... makes a difference only in the call syntax, but in
> > fact still passes an array. (Note that the variadics ending with "T[] param..."
> > are not unsafe C-style variadics.) Please advise - thanks!
>
> Hmm, current compiler always raise an error against the difference of variadic
> argument kind (non-variarics vs typesafe-variadics vs C-style variadics).
>
> But, from http://d-programming-language.org/abi.html
>
> > The variadic part is converted to a dynamic array and the rest is the same as for non-variadic functions.
>
> So, in ABI layer, (..., T[]) and (..., T[]...) might be same...
I don't think so. One difference is visible in declaration.html (there may be other). It says:
"An implementation may construct the object or array instance on the stack. Therefore, it is an error to refer to that instance after the variadic function has returned:
int[] test(int[] a ...) {
return a; // error, array contents invalid after return
}
"
So, it would be legal to call the delegate with the Variant[]... stored on the stack. But it would fail.
It might be OK to assign a (Variant[]...) function to a (Variant[]) delegate, but I'm pretty sure the other direction doesn't work.
Comment #5 by timon.gehr — 2011-12-31T07:32:53Z
(In reply to comment #4)
> (In reply to comment #3)
> > (In reply to comment #2)
> > > I think (and perhaps I'm wrong) that the signature with ... and the one without
> > > must be equivalent. The ... makes a difference only in the call syntax, but in
> > > fact still passes an array. (Note that the variadics ending with "T[] param..."
> > > are not unsafe C-style variadics.) Please advise - thanks!
> >
> > Hmm, current compiler always raise an error against the difference of variadic
> > argument kind (non-variarics vs typesafe-variadics vs C-style variadics).
> >
> > But, from http://d-programming-language.org/abi.html
> >
> > > The variadic part is converted to a dynamic array and the rest is the same as for non-variadic functions.
> >
> > So, in ABI layer, (..., T[]) and (..., T[]...) might be same...
>
> I don't think so. One difference is visible in declaration.html (there may be
> other). It says:
>
> "An implementation may construct the object or array instance on the stack.
> Therefore, it is an error to refer to that instance after the variadic function
> has returned:
>
> int[] test(int[] a ...) {
> return a; // error, array contents invalid after return
> }
> "
> So, it would be legal to call the delegate with the Variant[]... stored on the
> stack. But it would fail.
> It might be OK to assign a (Variant[]...) function to a (Variant[]) delegate,
> but I'm pretty sure the other direction doesn't work.
It should be OK to assign (scope Variant[]) to (Variant[]...).
Comment #6 by andrei — 2011-12-31T08:43:40Z
OK, it looks like this is safe to close now. I compiled a slightly modified version. There was a bug elsewhere, the line
Variant opDispatch(string m, Args)(Args args...) {
should be
Variant opDispatch(string m, Args...)(Args args) {
Also, the signature of the delegate should contain "...", i.e. replace
delegate Variant(Dynamic, Variant[]) {
with
delegate Variant(Dynamic, Variant[]...) {
The working code is:
module test;// Context begin
#line 1369 "0900-operators.tex"
import std.variant;
alias Variant delegate(Dynamic self, Variant[] args...) DynMethod;
// Context end
// Context begin
#line 1383 "0900-operators.tex"
class Dynamic {
private DynMethod[string] methods;
void addMethod(string name, DynMethod m) {
methods[name] = m;
}
void removeMethod(string name) {
methods.remove(name);
}
// Dispatch dynamically on method
Variant call(string methodName, Variant[] args...) {
return methods[methodName](this, args);
}
// Provide syntactic sugar with opDispatch
Variant opDispatch(string m, Args...)(Args args) {
Variant[] packedArgs = new Variant[args.length];
foreach (i, arg; args) {
packedArgs[i] = Variant(arg);
}
return call(m, args);
}
}
// Context end
#line 1409 "0900-operators.tex"
unittest {
import std.stdio;
auto obj = new Dynamic;
obj.addMethod("sayHello",
delegate Variant(Dynamic, Variant[]...) {
writeln("Hello, world!");
return Variant();
});
obj.sayHello(); // Prints "Hello, world!"
}
void main(){}
Comment #7 by andrei — 2011-12-31T10:32:43Z
One more thing - the example in the book reads:
obj.addMethod("sayHello",
Variant(Dynamic, Variant[]...) {
writeln("Hello, world!");
return Variant();
});
That doesn't work. The compilable version reads:
obj.addMethod("sayHello",
delegate Variant(Dynamic, Variant[]...) {
writeln("Hello, world!");
return Variant();
});
Can we arrange things such that the "delegate" or "function" keywords aren't require in the literal definition?
Comment #8 by k.hara.pg — 2012-01-01T05:56:47Z
(In reply to comment #7)
> One more thing - the example in the book reads:
>
> obj.addMethod("sayHello",
> Variant(Dynamic, Variant[]...) {
> writeln("Hello, world!");
> return Variant();
> });
>
> That doesn't work. The compilable version reads:
>
> obj.addMethod("sayHello",
> delegate Variant(Dynamic, Variant[]...) {
> writeln("Hello, world!");
> return Variant();
> });
>
> Can we arrange things such that the "delegate" or "function" keywords aren't
> require in the literal definition?
From past, omission of delegate keyword + specification of return type has been wrong syntax. So Variant(Dynamic, Varinat[]...) { ... } couldn't compile.
And recent commit
https://github.com/D-Programming-Language/dmd/commit/c50eb5f5726a65efa1224ff0f0fd60acbb6e3027
has been disallowed optional parameter names, because of parameter type inference.
From these matters, current TDPL code never become valid.
----
Today, following literal notations has been allowed with current dmd.
delegate Variant(Dynamic self, Variant[] args...) {...}
delegate (Dynamic self, Variant[] args...) {...}
(Dynamic self, Variant[] args...) {...}
----
Note: Unfortunately, current dmd does not support parameter type inference + type-safe variadisc like follows.
//delegate (self, args...) {...}
//(self, args...) {...}
Because args could have three kind of infer targets.
T[N] args... // T is the element type of args
T[] args... // T is the element type of args
T args... // T is class type, and args is translated to new T(args)
Comment #9 by k.hara.pg — 2012-03-13T20:50:21Z
(In reply to comment #8)
> Note: Unfortunately, current dmd does not support parameter type inference +
> type-safe variadisc like follows.
>
> //delegate (self, args...) {...}
> //(self, args...) {...}
I've filed bug 7705 to allow such lambda syntax, and posted a pull request to fix it.