Given »class A { void foo() const {} }; A a = new A;«, »typeof(&a.foo).stringof« yields »void delegate() const«.
However, trying to declare a delegate variable of this type like »void delegate() const dg; dg = &a.foo;« fails with »const/immutable/shared/inout attributes are only valid for non-static member functions« or, depending on the scope one tries to declare the variable in, even more cryptic error messages.
Comment #1 by bearophile_hugs — 2010-09-07T16:33:20Z
Maybe you want to rewrite those code snippets into readable indented little runnable programs.
Comment #2 by code — 2010-09-08T02:13:36Z
Okay, bearophile, here you go:
---
class A {
void foo() const {}
}
void main() {
A a = new A;
pragma( msg, typeof( &a.foo ) ); // Yields »void delegate() const«
void delegate() const dg = &a.foo; // Does not compile.
}
---
The error message for this simple case is »test.d(9): const/immutable/shared/inout attributes are only valid for non-static member functions
«.
Comment #3 by clugdbug — 2010-09-08T02:21:14Z
(In reply to comment #2)
> Okay, bearophile, here you go:
>
> ---
> class A {
> void foo() const {}
> }
>
> void main() {
> A a = new A;
>
> pragma( msg, typeof( &a.foo ) ); // Yields »void delegate() const«
> void delegate() const dg = &a.foo; // Does not compile.
> }
> ---
>
> The error message for this simple case is »test.d(9):
> const/immutable/shared/inout attributes are only valid for non-static member
> functions
> «.
Change that last line to:
const void delegate() dg = &a.foo;
and it works.
It's a parsing issue. I'm sure it's a dup of another bug.
Comment #4 by code — 2010-09-08T02:41:01Z
(In reply to comment #3)
> const void delegate() dg = &a.foo;
Would not that rather create a const(void delegate())?
Comment #5 by schveiguy — 2010-09-08T06:25:16Z
Yes, it does:
import std.stdio;
class A
{
void f() const {}
}
void main()
{
A a = new A;
auto g1 = &a.f;
writefln("g1 = %s", typeof(g1).stringof);
const void delegate() g2 = &a.f;
writefln("g2 = %s", typeof(g2).stringof);
void delegate() g3 = &a.f;
writefln("g3 = %s", typeof(g3).stringof);
g1();
g2();
g3();
}
compiles and outputs:
g1 = void delegate() const
g2 = const(void delegate())
g3 = void delegate()
It appears that you can remove the const decorations at will. This is very not good.
Comment #6 by code — 2010-09-08T06:26:46Z
(In reply to comment #5)
> It appears that you can remove the const decorations at will. This is very not
> good.
See also: bug 1983.
Comment #7 by clugdbug — 2010-09-08T07:11:58Z
General comment: You have to be careful with .stringof, it's not yet reliable (a difference in .stringof doesn't necessarily mean a difference in type). But .mangleof never lies. The conclusion in this case is still correct, though.
A workaround for the original problem is to use auto (or typeof).
pragma( msg, (&a.foo).mangleof );
auto dg = &a.foo; // this works
const void delegate() dg2 = &a.foo;
pragma(msg, dg.mangleof);
pragma(msg, dg2.mangleof);
-------
DxFZv
DxFZv
xDFZv
What I said still applies -- it's a parsing problem.
This bug is very important for const/shared/immutable correctness.
The following code is caused by this issue clearly:
-------------------------
class A {
int x;
void foo() { x = 1; }
}
void bar(void delegate() dg) {
dg();
}
void main() {
immutable a = new immutable(A);
assert(a.x == 0);
bar(&a.foo); // This line should be compilation error.
assert(a.x == 0); // core.exception.AssertError@main(14): Assertion failure
}
-------------------------
Its workaround is the following code:
-------------------------
class A {
int x;
void foo() { x = 1; }
}
alias typeof(&(new class(){void foo() const{}}).foo) void_delegate_const;
void bar(void_delegate_const dg) {
dg();
}
void main() {
immutable a = new immutable(A);
assert(a.x == 0);
bar(&a.foo); // main.d(14): Error: function main.bar (void delegate() const dg) is not callable using argument types (void delegate())
assert(a.x == 0);
}
-------------------------
It is painful to write such correct code.
Fortunately, there is a PullRequest, so I hope to be merged as soon as possible.