Bug 6705 – Bad codegen when passing fields as template alias params
Status
RESOLVED
Resolution
INVALID
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2011-09-21T02:22:48Z
Last change time
2020-09-15T06:51:27Z
Keywords
accepts-invalid, safe
Assigned to
No Owner
Creator
Vladimir Panteleev
Comments
Comment #0 by dlang-bugzilla — 2011-09-21T02:22:48Z
I'm not sure if this is valid code, so it's either accepts-invalid or wrong-code.
I was trying to implement member field pointers, when I ran into this:
int func(alias F)(int i)
{
return i;
}
class C
{
int a;
}
void main()
{
auto a = &func!(C.a);
assert(a(5) == 5);
//auto i = func!(C.a)(5); // uncomment for a weird compiler error
}
Comment #1 by smjg — 2012-02-12T12:13:22Z
From what I can make out, the code is valid - a class member falls under "any type of D symbol", and func!anything is just an int(int) function.
For me (2.057 Win32), the given code throws an AV at runtime (wrong-code). Reinstating the commented out line gives
C:\Users\Stewart\Documents\Programming\D\Tests\bugs>dmd bz6705.d
bz6705.d(15): Error: need 'this' to access member func
(rejects-valid)
Comment #2 by b2.temp — 2019-11-03T21:44:17Z
this is a case where the global function (and non static) used as template param becomes a (non-static) member.
But when the global func used as alias template parameter is explicitely declared static this just works for example:
static int func(alias F)(int i)
{
return i;
}
class C
{
int a;
}
void main()
{
auto a = &func!(C.a);
assert(a(5) == 5);
auto i = func!(C.a)(5);
}
I once tried to infer "static" but this broke the test suite... so people have to put it by themselves even if it is weird to do so at the global scope.
Comment #3 by bugzilla — 2020-08-09T08:39:51Z
The generated code is:
__Dmain comdat
enter 4,0
mov EAX,offset __D4test__T4funcS_DQq1C1aiZQqMFNaNbNiNfiZi
mov -4[EBP],EAX
mov EAX,5 <=== EAX should be pushed on the stack
call dword ptr -4[EBP]
mov ECX,5
cmp EAX,ECX
je L2A
push 0Eh
mov EDX,offset FLAT:___a6_746573742e64
push EDX
call near ptr __d_assertp
L2A: xor EAX,EAX
leave
ret
__D4test__T4funcS_DQq1C1aiZQqMFNaNbNiNfiZi comdat
mov EAX,4[ESP]
ret 4
Not sure why it isn't pushed on the stack.
Comment #4 by bugzilla — 2020-09-15T06:20:30Z
Here's what's happening:
When C.a is passed as an alias argument to func(), then func becomes (for that instantiation) a member of C. Being a member function, then it expects a 'this' pointer. But when a(5) is called, there is no 'this' pointer, and the '5' is interpreted as the 'this' pointer, and 'i' is garbage.
The same problem appears with this simpler example:
class C
{
int a;
int member(long);
}
void moon()
{
auto f = &C.member; // creates `int function(long)`
f(); // oops, no `this`
}
It does need to be possible to take the address of a member function without the 'this', and if we make that an error, we may break existing legitimate code.
Note the following:
int delegate(long) dg;
pragma(msg, typeof(dg.funcptr));
prints:
int function(long)
so changing the type of &C.member to `void*` isn't going to work, either.
meaning one can construct delegates by using &C.member for dg.funcptr and some void* for dg.ptr.
What I can do is make taking the address of a member without `this` not allowed in @safe code.
Comment #5 by bugzilla — 2020-09-15T06:51:27Z
Well, I'll be. Adding @safe causes the code to give an error on taking the address.
That resolves this issue.