-
-
Notifications
You must be signed in to change notification settings - Fork 608
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Issue 3720 - Taking address of member functions possible without an instance #14688
Conversation
|
Thanks for your pull request and interest in making D better, @RazvanN7! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla references
|
| assert(callMember!(C1.f2)(c1, 20, 3) == 123); | ||
| assert(callMember!(C1.f3)(c1, 20, 3) == 123); | ||
| assert(callMember!(C1.f4)(c1, 20, 3, 0) == 123); | ||
| //assert(callMember!(c1.f0)(c1) == 100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to test the same thing as the above delegate test. What am I missing @ibuclaw ?
|
Why? What value does this inconsistency bring to the language? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. This is very clearly a hack. Changing the typeof so the pointer cannot be called is wrong.
Not to mention, we need more typing on delegate, not less.
|
Yeah, this is definitely wrong. What I'd like to do is formalize the delegate abi a little so we can actually define it. So it might return (well since it is a delegate and the type of S is erased in there, it'd probably have to be |
|
Most people will agree that taking the address of Taking the address of the delegate is like going to live into the wild, the type system does not protect you anymore and you need to specify everything yourself (via casts). I don't see how making this ugly pattern legal by typing it more is going to help. We need to discourage people to use this kind of code. Typing it even more is the opposite of simplifying the language. I understand that this may not be the best solution, but @Geod24 @adamdruppe your comments are not constructive at all. Comments like "it's wrong", "it should be like this" without providing a rationale are not helpful at all. Could you maybe present your use cases so that we reach a consensus? I think that your use cases may have other solutions that do not require making the language more complex. |
I disagree. Because maintaining interior pointers is unreliable for structs, storing delegates isn't feasible. That leaves us with function pointers, which are currently unusable due to being underdefined, or hacks like this. This should be more defined, not less. Furthermore, taking the address of S.fun is only allowed in I would also like to point out that casting Taking the address of S.fun should return something useful, or simply be an error. |
Where's your evidence for this? Of course, even if you did prove most people would agree, declaring that makes it right is still an appeal to popularity fallacy, so where is your actual rationale for it? The existence of tests that had to be commented out (one of which btw, you merged - just last year! - to close another bug report) ought to show that there is another use case to this. How do you justify this regression? My proposal is to match the actual ABI and make the function both type-safe and actually callable with a runtime function pointer without as many unreliable casts. (Note that with a template alias, you can call |
I think you should split this into two parts:
Point 1 needs to be fixed asap, because it leads to hard to find bugs. Point 2 is a discussion that should not be done inside this PR. |
|
@JohanEngelen Typing it as I think that what could be done is add a deprecation for taking the address of |
My viewpoint is that this is an "accepts invalid" bug. Hence breakage is desired. |
My comment was solely about the solution you provide, not the bug you are tackling. I can express it in more words, if you'd like: The compiler should have as much static information as possible / convenient. Because with more static information, we can take better decisions: detect errors earlier, generate more efficient code / diagnostic, etc... |
|
@Geod24 I have implemented typing |
|
With this change, I think that |
Wait what? Context pointer is the first parameter. |
|
@ibuclaw That's how I implemented it at first, but then I noticed that I'm getting segfaults for functions that have more than 0 parameters. It was surprising for me also. |
|
Initially, I put the context pointer in the type as being the first parameter, but then this example issued a segfault: struct S
{
int a;
void fun(int _)
{
this.a = 1;
}
}
void main()
{
auto fp = &S.fun;
S s;
fp(&s, 2);
import std.stdio;
writeln(s.a);
}When I put the ctx pointer at the end, and called |
|
@RazvanN7 looks like you're trying to do X86 ABI hacks in the front-end AST then. What you're doing won't work - see for example #13590 and the decades of grief that caused us in order to get opCmp and opEquals working uniformly across all architectures. Remember, X86 ABI pushes arguments RTL, contrary to every single other platform which pushes LTR. |
|
@ibuclaw Thanks for shedding some light on this. It seems that the frontend doesn't really have a way to express a function type for which the context pointer is provided by the user (other than delegates.funcptr). |
|
My two cents: The first thing to consider is that Assuming
The syntax I’d imagine the module-level function implementation as follows: For every aggregate type |
The type of
S.funis set tovoid*. That way, you cannot call fp directly.Next step would be to make
delegate.funcptrreturn avoid*also.I had to modify some dmd tests because now
typeof(S.fun)is going to returnvoid*. I expect such cases to be rare, but this will definitely require a changelog entry and a spec PR. An alternative would be to trigger a deprecation whentypeof(&S.fun)is encountered. What do you think?