Bug 7265 – Function Literals where a keyword was omitted should be delegate even if inference.
Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P2
Component
dlang.org
Product
D
Version
D2
Platform
All
OS
All
Creation time
2012-01-10T09:30:00Z
Last change time
2012-01-17T10:11:05Z
Assigned to
nobody
Creator
zan77137
Comments
Comment #0 by zan77137 — 2012-01-10T09:30:54Z
This code should be work (on git head):
-------
import std.stdio;
void main()
{
auto dg = { writeln("delegate"); };
static assert(is(typeof(dg) == delegate));
}
-------
main.d(6): Error: static assert (is(void function() == delegate)) is false
-------
This is being documented as follows:
> If the keywords function or delegate are omitted, it defaults to being a delegate.
Comment #1 by timon.gehr — 2012-01-15T11:56:18Z
This is a case of outdated documentation.
From TDPL, p150:
"If both function and delegate are absent from the literal, the compiler automatically detects which one is necessary".
Comment #2 by zan77137 — 2012-01-16T03:48:12Z
(In reply to comment #1)
> This is a case of outdated documentation.
> From TDPL, p150:
> "If both function and delegate are absent from the literal, the compiler
> automatically detects which one is necessary".
It is an example unlike the specifications(TDPL, p150).
The case is the following situation:
-------
import std.stdio, std.traits;
void main()
{
void function() fn = { writeln("function"); };
static assert(isFunctionPointer!(typeof(fn)));
void delegate() dg = { writeln("delegate"); };
static assert(isDelegate!(typeof(dg)));
}
-------
As noted above, the specifications are applied only when there is a necessary type.
When there is not a required type, in the case like this time, it should infer the type of the default.
This resembles the following examples:
-------
import std.traits;
void main()
{
byte a = 1; // typeof(a) is byte
auto b = 1; // typeof(b) is int
// int cannot implicitly cast to byte.
static assert (isImplicitlyConvertible!(typeof(b), typeof(a))); // false.
}
-------
Comment #3 by timon.gehr — 2012-01-16T08:44:22Z
Everything behaves as specified in TDPL. This is not a compiler bug.
Comment #4 by andrei — 2012-01-16T09:25:15Z
(In reply to comment #3)
> Everything behaves as specified in TDPL. This is not a compiler bug.
Yah, I just tested this:
import std.stdio, std.traits;
void main()
{
auto fn = { writeln("function"); };
static assert(isFunctionPointer!(typeof(fn)));
int x;
auto dg = { writeln("delegate because it prints ", x); };
static assert(isDelegate!(typeof(dg)));
}
So the literal is properly classified as a function or delegate depending on it needing a frame pointer or not.
There's one remaining question I have. I rewrote the example like this:
import std.stdio, std.traits;
void main()
{
auto fn = { writeln("function"); };
static assert(is(typeof(fn) == function));
int x;
auto dg = { writeln("delegate because it prints ", x); };
static assert(is(typeof(dg) == delegate));
}
The second static assert works, but the first doesn't. Why?
Comment #5 by zan77137 — 2012-01-16T10:07:04Z
(In reply to comment #4)
> (In reply to comment #3)
> > Everything behaves as specified in TDPL. This is not a compiler bug.
>
> Yah, I just tested this:
>
> import std.stdio, std.traits;
> void main()
> {
> auto fn = { writeln("function"); };
> static assert(isFunctionPointer!(typeof(fn)));
> int x;
> auto dg = { writeln("delegate because it prints ", x); };
> static assert(isDelegate!(typeof(dg)));
> }
>
> So the literal is properly classified as a function or delegate depending on it
> needing a frame pointer or not.
>
Hmm...
Because it included a clear breaking change, I thought that a sample code was wrong.
> There's one remaining question I have. I rewrote the example like this:
>
>
> import std.stdio, std.traits;
>
> void main()
> {
> auto fn = { writeln("function"); };
> static assert(is(typeof(fn) == function));
>
> int x;
> auto dg = { writeln("delegate because it prints ", x); };
> static assert(is(typeof(dg) == delegate));
> }
>
> The second static assert works, but the first doesn't. Why?
"fn" of the first case is a function pointer and is not a function. The code outputs a compilation error definitely.
-----
import std.stdio, std.traits;
void main()
{
static assert(is(typeof(main) == function)); // true, typeof(main) is function
static assert(is(typeof(&main) == function)); // false, typeof(&main) is function pointer
}
-----
Comment #6 by andrei — 2012-01-16T10:15:03Z
(In reply to comment #5)
> -----
> import std.stdio, std.traits;
>
> void main()
> {
> static assert(is(typeof(main) == function)); // true, typeof(main) is
> function
> static assert(is(typeof(&main) == function)); // false, typeof(&main) is
> function pointer
> }
> -----
I think fn in
auto fn = { ... };
should be the same thing - a name for a function. I guess that's not easy to implement. Kenji, Walter?
Comment #7 by k.hara.pg — 2012-01-16T16:11:52Z
> I think fn in
>
> auto fn = { ... };
>
> should be the same thing - a name for a function. I guess that's not easy to
> implement. Kenji, Walter?
Rewriting it to true function declaration is easy, but
fn is looks like a variable so peaple will try to
rebind it with assignment:
fn = { writeln("another function literal"); };
and will get an error. It is inconsistent.
Comment #8 by andrei — 2012-01-16T16:36:05Z
OK, thanks.
Comment #9 by zan77137 — 2012-01-17T06:37:31Z
By the way, the reason why I set this Issue with regression is that I cause a difficult problem with the following cases:
------
import std.stdio, std.functional;
// Library code
struct Control {
alias void delegate(Control o) Handler;
Handler[] _handlers;
void addHandler(H)(H hnd) {
_handlers ~= cast(Handler)hnd;
}
void addHandler2(H)(H hnd) if (is(H == delegate)) {
_handlers ~= cast(Handler)hnd;
}
// Workaround. It is settled if can handle either by toDelegate.
void addHandler3(H)(H hnd) {
_handlers ~= cast(Handler)toDelegate(hnd);
}
void call() {
foreach (h; _handlers) {
h(this);
}
}
}
// User code
void main() {
int i;
auto c = new Control;
// OK. This literal is inferred delegate.
c.addHandler( (Object o){ writeln(i); } );
// Error. This literal is inferred function pointer.
// c.addHandler( (Object o){ writeln("test"); } );
// Error. This literal is inferred function pointer, too.
// The constraint-if does not influence the type inference.
// c.addHandler2( (Object o){ writeln("test2"); } );
// Workaround.
c.addHandler3( (Object o){ writeln("test3"); } );
c.call();
}
------
When a library code has a definition such as Control, many breaking changes befall user codes.
Actually, this way is used by DFL.
One of simple workaround is to use toDelegate with library codes.
I think that another solution is to let implicit conversion perform a role as toDelegate.
I remember it has entered ML agenda several times. But I've forgotten the conclusion.
Is there a person knowing the details?
Comment #10 by timon.gehr — 2012-01-17T10:11:05Z
(In reply to comment #9)
> One of simple workaround is to use toDelegate with library codes.
>
> I think that another solution is to let implicit conversion perform a role as
> toDelegate.
>
> I remember it has entered ML agenda several times. But I've forgotten the
> conclusion.
> Is there a person knowing the details?
I think no conclusion has been reached yet. Andrei (and me too) wants implicit conversions from function pointer to delegate to work, but Walter disagrees with it.