Jump to page: 1 2 3
Thread overview
Shouldn't casting an object to void* be considered @safe?
Dec 13, 2019
Andrej Mitrovic
Dec 13, 2019
Paulo Pinto
Dec 13, 2019
Andrej Mitrovic
Dec 14, 2019
Dennis
Dec 14, 2019
Dennis
Dec 15, 2019
ag0aep6g
Dec 15, 2019
Dennis
Dec 15, 2019
Dennis
Dec 14, 2019
Dennis
Dec 16, 2019
Andrej Mitrovic
December 13, 2019
I recently got to thinking about a code snippet. The following doesn't compile, because casting to a void* is considered unsafe:

-----
import std.stdio;

class C
{
    void foo() @safe
    {
        writeln("%s: C.foo()", cast(void*)this);
    }
}

void main()
{
}
-----

> test.d(7): Error: cast from `test.C` to `void*` not allowed in safe code

However, I don't see this cast as being unsafe. Casting a class object to a `void*` doesn't break the type system by itself. You cannot assign a `void*` to any other pointer type without an additional cast, and that additional cast would be the unsafe one. Additionally, you cannot reference a `void*`, so as far as I can see it's fairly safe to use in @safe code.

Wouldn't it make sense to allow casting reference types to `void*` in @safe code? Are there edge-cases I haven't considered?

December 13, 2019
On Friday, 13 December 2019 at 08:05:53 UTC, Andrej Mitrovic wrote:
> I recently got to thinking about a code snippet. The following doesn't compile, because casting to a void* is considered unsafe:
>
> -----
> import std.stdio;
>
> class C
> {
>     void foo() @safe
>     {
>         writeln("%s: C.foo()", cast(void*)this);
>     }
> }
>
> void main()
> {
> }
> -----
>
>> test.d(7): Error: cast from `test.C` to `void*` not allowed in safe code
>
> However, I don't see this cast as being unsafe. Casting a class object to a `void*` doesn't break the type system by itself. You cannot assign a `void*` to any other pointer type without an additional cast, and that additional cast would be the unsafe one. Additionally, you cannot reference a `void*`, so as far as I can see it's fairly safe to use in @safe code.
>
> Wouldn't it make sense to allow casting reference types to `void*` in @safe code? Are there edge-cases I haven't considered?

Your example is only safe if writeln happens to be safe.

If it is not implemented in D, or it is available as binary only implementation, everything goes.

Naturally you could argue that those implementations can as well, change the bit representation after getting the safe reference to C, however this is another defense barrier to prevent doing the wrong thing by default.
December 13, 2019
On Friday, 13 December 2019 at 08:24:03 UTC, Paulo Pinto wrote:
> Your example is only safe if writeln happens to be safe.

Well yes, you could only call @safe functions with that void*. That's how transitivity works.
December 14, 2019
On Friday, 13 December 2019 at 08:05:53 UTC, Andrej Mitrovic wrote:
> However, I don't see this cast as being unsafe. Casting a class object to a `void*` doesn't break the type system by itself. You cannot assign a `void*` to any other pointer type without an additional cast, and that additional cast would be the unsafe one. Additionally, you cannot reference a `void*`, so as far as I can see it's fairly safe to use in @safe code.
>
> Wouldn't it make sense to allow casting reference types to `void*` in @safe code? Are there edge-cases I haven't considered?

Surely where code has a cast to `void*` and then later back to some other pointer type, its overall safety is dependent on what happens at both ends.  The safety of the cast from `void*` cannot be validated by the developer without knowing how the cast _to_ `void*` was done.

Making a cast to `void*` unsafe is therefore an important push to the developer to say, "You need to look at this and validate what you're doing against how you use it later."
December 14, 2019
On Saturday, 14 December 2019 at 17:25:25 UTC, Joseph Rushton Wakeling wrote:
> Making a cast to `void*` unsafe is therefore an important push to the developer to say, "You need to look at this and validate what you're doing against how you use it later."

Casting to void should be safe, that is the same as providing a pure object identity.

This is useful for a set of objects.

That is the most restrictive reference-type the type system provides.



December 14, 2019
On Saturday, 14 December 2019 at 17:30:53 UTC, Ola Fosheim Grøstad wrote:
> Casting to void should be safe, that is the same as providing a pure object identity.
>
> This is useful for a set of objects.

What's the point in generating a pure object identity, or a set of objects, unless those objects are going to be used?  And how do you validate that that usage is safe without knowing something about the circumstances in which those pure identities were generated?
December 14, 2019
On Saturday, 14 December 2019 at 17:54:20 UTC, Joseph Rushton Wakeling wrote:
> What's the point in generating a pure object identity, or a set of objects, unless those objects are going to be used?  And how do you validate that that usage is safe without knowing something about the circumstances in which those pure identities were generated?

Sometimes you just want a set of all object identities with a specific property so that you can query for that property.

It is the same a the "tag"  reference type in Pony lang. It is safe for multi-threading too.

Say, if a thread is waiting for a set of futures to complete then it can remove object identities from the set based on events.

December 14, 2019
On Saturday, 14 December 2019 at 18:10:20 UTC, Ola Fosheim Grøstad wrote:
> Sometimes you just want a set of all object identities with a specific property so that you can query for that property.
>
> It is the same a the "tag"  reference type in Pony lang. It is safe for multi-threading too.
>
> Say, if a thread is waiting for a set of futures to complete then it can remove object identities from the set based on events.

Fair enough, but note that the safety of the `void*` in those use-cases is still down to the specifics of how they are used.

The obligation to put a `@trusted` on the cast to `void*` for use-cases like this seems minor compared to the value of explicitly nudging the developer to verify that the use-case is safe.

IOW, having casts to `void*` be unsafe seems a tactic, rather than a principle.
December 14, 2019
On Saturday, 14 December 2019 at 17:54:20 UTC, Joseph Rushton Wakeling wrote:
> What's the point in generating a pure object identity, or a set of objects, unless those objects are going to be used?

Whether something is useful or a good practice is not relevant for @safe, it is only concerned with whether it makes memory corruption possible.

> And how do you validate that that usage is safe without knowing
> something about the circumstances in which those pure identities were generated?

Any function that takes a void* and casts it to a different pointer that gets dereferenced is not @safe, whether class casting to void* is allowed or not. You simply can't assume anything about a void* even in @safe code.
Note that casting pointer types to void is already allowed in @safe:

```
class C {}
void main() @safe {
    void* v0 = cast(void*) new int; // allowed, implicit conversion
    void* v1 = cast(void*) 0xDEAFBEEF; // not allowed, not a pointer type
    void* v2 = cast(void*) new C(); // not allowed, not a pointer type
}
```

The spec says:

> - No casting from a pointer type to any type other than void*.
> - No casting from any non-pointer type to a pointer type.

https://dlang.org/spec/function.html#safe-functions

The second line could be changed to "No casting from any non-pointer type to a pointer type other than void*" however.
December 14, 2019
On Saturday, 14 December 2019 at 18:46:38 UTC, Dennis wrote:
> Whether something is useful or a good practice is not relevant for @safe, it is only concerned with whether it makes memory corruption possible.

Indeed.  Has anything I said suggested otherwise?

>> And how do you validate that that usage is safe without knowing
>> something about the circumstances in which those pure identities were generated?
>
> Any function that takes a void* and casts it to a different pointer that gets dereferenced is not @safe, whether class casting to void* is allowed or not. You simply can't assume anything about a void* even in @safe code.

Note that I said safe, not @safe.  The difference matters: code can be memory-safe, but that memory-safety may not be verifiable by the compiler.

That's relevant to this discussion, because when asking "Should the cast to `void*` be @safe?" we are really asking, "Is it possible for the compiler to decide this without needing the developer to verify?"

> Note that casting pointer types to void is already allowed in @safe:

You're right, I had a memory lapse on that one.  Let's return the focus to casting objects to `void*`.

I would suggest that the fact that there's a pointer under the hood of an object is an implementation detail.  An unavoidable one, sure, but the point is that by casting it to a pointer type of any kind (`void*` or otherwise) we are creating opportunities for unsafe memory access that didn't exist previously.

The question of whether that cast is safe -- not @safe! -- is down to the use-case.  So it's reasonable to ask the developer to validate both ends (i.e. how the cast to a pointer is done, and how that `void*` is used afterwards).

> The second line could be changed to "No casting from any non-pointer type to a pointer type other than void*" however.

No, that won't do.  What if you cast from a `ulong` to a `void*`?

The spec is correct to ban casting from non-pointer types to pointer types.  Andrej has raised a reasonable point about whether we might reconsider where objects are concerned, but I think the spec is correct as is (for reasons already given, and probably others too).
« First   ‹ Prev
1 2 3