Bug 21862 – Taking address of non-static method without "this" should not be allowed
Status
RESOLVED
Resolution
DUPLICATE
Severity
major
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2021-04-25T14:07:40Z
Last change time
2022-12-09T12:16:09Z
Assigned to
No Owner
Creator
Eyal
Comments
Comment #0 by eyal — 2021-04-25T14:07:40Z
This should *not* compile, &S.m needs 'this' which is not available there:
struct S {
int m(int x) {return x;}
}
void main() {
int function(int) func = &S.m;
writeln(func(5)); // does not print 5!
}
Comment #1 by razvan.nitu1305 — 2022-12-05T16:30:57Z
I have been looking into this and it seems that, surprisingly, this is intended behavior. I first tried to fix it to give an error, but then realized that actually this is correct code.
Right now, when we are talking about functions we have 2 categories: functions or delegates. A function is represented by a function pointer, whereas a delegate is represented by a function pointer and a context pointer.
When you take the address of a member function, as you are doing in the bug report, the compiler simply sets the function pointer to the address of the function code. However, member functions (unless they are static) also have a context pointer that is passed behind the scenes as the first parameter. So in the bug report code, we end up with code that looks like this:
int m(void* ctx, int x)
{
return x; // mov rax, rsi - returning second parameter
}
void main()
{
int function(int) func = &S.m; // mov rax, m
func(5) ; // mov rdi, 5;
// jmp rax
}
That's why we get garbage. You take the address of the function and then trick the compiler to call m like it has a single parameter. Note that if the type of func is delegate, then you correctly get 5, because now the compiler knows to pass the ctx (null in this case) to m.
It is useful to be able to take the actual address of a function that is a member function without requiring a context. The test suite is full of such cases and if we disable this we are left with no alternative. On the flip side, if main is marked as @safe, the code does not compile ("Error: `this` reference necessary to take address of member `myFunc` in `@safe` function `main`"). Therefore, in safe code this is not allowed, but advanced users that understand what happens may use this feature.
So this is actually an invalid bug report. Marking it as such, but please reopen if I am missing something.
Comment #2 by eyal — 2022-12-06T07:45:12Z
Why would you take the function ptr of a delegate without the ctx, except as a "void*" value to compare against?
A delegate's function ptr is *not* callable without the context, so it should not have the type of callable things.
If the test suite assumes the delegate is implicitly convertible to a callable function without the context -- the test suite should be fixed, as this is non-sensical.
Comment #3 by razvan.nitu1305 — 2022-12-06T07:53:17Z
(In reply to Eyal from comment #2)
> Why would you take the function ptr of a delegate without the ctx, except as
> a "void*" value to compare against?
>
> A delegate's function ptr is *not* callable without the context, so it
> should not have the type of callable things.
>
> If the test suite assumes the delegate is implicitly convertible to a
> callable function without the context -- the test suite should be fixed, as
> this is non-sensical.
You are correct that the function ptr of a delegate should not be callable without it's context however, you can manually set the context ptr and the function ptr of delegates.
```d
struct S {
int m(int x) {return x;}
}
void main() {
import std.stdio;
int function(int) func = &S.m;
int delegate(int) func2;
S s;
func2.funcptr = func;
func2.ptr = &s;
writeln(func2(5));
}
```
&S.m returns a function pointer so technically func is callable. It is up to the user to make sure that he is using them properly. So right now we cannot disallow taking the adddress of S.m. The only way this could be fixed would be to change the type of &S.m something else, but I think that this will cause more problems.
Comment #4 by eyal — 2022-12-06T07:57:23Z
The type of a delegate's "funcptr" should either be a "void*" or a new kind of non-callable "ret delegatefuncptr(args)" type.
That will allow everything correct to happen while making the incorrectly typed programs at least take some effort.
If changing the funcptr type to void* causes problems - those are better problems to have/solve than this problem.
Comment #5 by razvan.nitu1305 — 2022-12-09T12:16:09Z
It turns out this is actually a duplicate of an error that has been persistently reported over the years.
*** This issue has been marked as a duplicate of issue 3720 ***