Issue 24772 - Casting class references to void* should be @safe
Summary: Casting class references to void* should be @safe
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P1 enhancement
Assignee: No Owner
URL:
Keywords: safe
Depends on:
Blocks:
 
Reported: 2024-09-19 12:36 UTC by Georgy Markov
Modified: 2024-10-02 10:17 UTC (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Georgy Markov 2024-09-19 12:36:52 UTC
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 Richard (Rikki) Andrew Cattermole 2024-09-19 12:40:08 UTC
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 Georgy Markov 2024-09-19 16:27:11 UTC
(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 Richard (Rikki) Andrew Cattermole 2024-09-19 16:29:46 UTC
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 Georgy Markov 2024-09-19 16:57:49 UTC
(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 Richard (Rikki) Andrew Cattermole 2024-09-19 17:00:44 UTC
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 Tim 2024-09-19 17:08:19 UTC
(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 Richard (Rikki) Andrew Cattermole 2024-09-19 17:11:46 UTC
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 Jonathan M Davis 2024-09-20 13:53:40 UTC
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 Dennis 2024-10-02 10:17:26 UTC
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.