Any pointer implicitly converts to void*. This is considered safe. Class references are essentially pointers, but converting them to void* requires implicit cast. This makes this conversion illegal in safe code, even though it doesn’t violate memory safety.
class C {}
interface I {}
C c;
I i;
@safe void main()
{
auto cp = cast(void*)c; // Error: cast from `app.C` to `void*` not allowed in safe code
auto ip = cast(void*)i; // Error: cast from `app.I` to `void*` not allowed in safe code
}
Comment #1 by alphaglosined — 2024-09-19T12:40:08Z
The only thing you can do once you cast it is cause program corruption.
Is there a benefit to being able to do this in ``@safe`` that I am not aware of?
Comment #2 by ogion.art — 2024-09-19T16:27:11Z
(In reply to Richard (Rikki) Andrew Cattermole from comment #1)
> The only thing you can do once you cast it is cause program corruption.
interface MyCOMInterface : IUnknown
{
/*...*/
}
MyCOMInterface comObject;
CoCreateInstance(
&clsid,
null,
CLSCTX_INPROC_SERVER,
&iid,
&cast(void*)comObject,
);
Comment #3 by alphaglosined — 2024-09-19T16:29:46Z
That code is unsafe.
You have lost the type system guarantees, calling into unknown code.
Which yes, if you passed the wrong arguments could have led to program corruption.
Comment #4 by ogion.art — 2024-09-19T16:57:49Z
(In reply to Richard (Rikki) Andrew Cattermole from comment #3)
>
> You have lost the type system guarantees, calling into unknown code.
>
> Which yes, if you passed the wrong arguments could have led to program
> corruption.
Irrelevant. The cause of possible memory corruption is function call, not pointer conversion.
Comment #5 by alphaglosined — 2024-09-19T17:00:44Z
It is relevant because I am asking for justification of what you can do with it after casting which would also be @safe.
Right now, we have no examples of this being a positive change, only negative ones.
Comment #6 by tim.dlang — 2024-09-19T17:08:19Z
(In reply to Richard (Rikki) Andrew Cattermole from comment #1)
> The only thing you can do once you cast it is cause program corruption.
>
> Is there a benefit to being able to do this in ``@safe`` that I am not aware
> of?
One use case is printing the address of an object:
```
auto o = new Object();
writeln(cast(void*) o);
```
Addresses could also be compared for equality or used as keys in associative arrays.
Comment #7 by alphaglosined — 2024-09-19T17:11:46Z
That does establish some casting should be valid.
I.e.
```d
import std.stdio;
void main() {
writeln(cast(size_t)cast(void*)new Object);
}
```
Comment #8 by issues.dlang — 2024-09-20T13:53:40Z
As a general rule, if we can guarantee that something is memory safe, it should be @safe. The general issue is being absolutely certain that something cannot result in memory corruption in @safe code. So, we do need to be careful in verifying that.
However, if we are 100% sure that something will not result in violating memory safety in purely @safe code, we arguably should enable it, and I'm not sure if it really matters whether it clearly enables anything.
For this particular case, if the void* is immediately passed to something @system, then the @safety doesn't really matter, because you'll need to slap @trusted on it all anyway. However, if there is a use case where the class is converted to void* in a separate piece of code, then it's arguably just annoying to required @trusted in that part of the code.
But I've certainly run into the case where I have to slap @trusted on code or use debug {} just because I'm trying to print out the pointer value of a class reference.
Comment #9 by dkorpel — 2024-10-02T10:17:26Z
When I made a PR for this, it got closed for a reason which couldn't be specified.
"There was a reason this was marked as unsafe, unfortunately I can't recall at the moment what it was."
https://github.com/dlang/dmd/pull/10672
I personally don't believe it's unsafe however.
Comment #10 by robert.schadek — 2024-12-13T19:37:26Z