Thread overview
[Issue 24793] Allow implicit conversion of const pointers to void*
1 day ago
Dennis
18 hours ago
Dennis
12 hours ago
Nick Treleaven
October 04
https://issues.dlang.org/show_bug.cgi?id=24793

--- Comment #1 from anonymous4 <dfj1esp02@sneakemail.com> ---
Any pimpl pattern is affected by this.
---
struct Console
{
    GtkButton* btn;

    void write(string s) const
    {
        g_signal_connect_data(cast(GtkButton*)btn, "clicked", &onClick);
    }
}
---

--
1 day ago
https://issues.dlang.org/show_bug.cgi?id=24793

Jonathan M Davis <issues.dlang@jmdavisProg.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |issues.dlang@jmdavisProg.co
                   |                            |m

--- Comment #2 from Jonathan M Davis <issues.dlang@jmdavisProg.com> ---
The const still tells you whether the data can be mutated, and depending on how the void* is used, that could actually matter. For instance, there's the memmove function which this question on stackoverflow which is discussing void* vs const void* discusses: https://stackoverflow.com/questions/5547131/c-question-const-void-vs-void

An implicit conversion to void* would render the const on the parameters of such a function pointless.

And while usually void* is going to be used with C code, it's quite possible that D code could overload on constness with a void*, and it could behave differently depending on whether it's given a const or mutable pointer. So, having const implicitly removed somewhere could then cause issues even if the rest of the type information is gone. Having the const implicitly disappear would make it so that there would be one more thing that the programmer would have to be extremely careful about when dealing with void* when they're already going to have to be keeping track of what the actual type is somehow in order to deal with it properly - if it's just being treated as raw memory, then const matters that much more, because those kinds of functions are typically used for stuff like blitting memory, and you wouldn't want to accidentally blit onto const data and thus violate the type system.

So, while yes, a lot of the type safety has been thrown away with void*, the mutability of the type has not been thrown away, and given how strict D's const is, I think that throwing it away would be a very bad idea without the programmer explicitly stating that that's what they want.

Also, at present, the implicit conversion from a mutable pointer to void* is considered @safe - e.g.

---
void main() @safe
{
    int* i;
    void* v = i;
}
---

whereas it would be completely inappropriate to consider it memory safe if const is implicitly removed in the process. So, even if we did add such an implicit conversion, it would need to be @system. But in general, I think that _any_ kind of implicit removal of const where an independent copy has not been made is just a terrible idea. Sure, it might make some code nicer, because fewer casts would be required, but it makes casting away const _much_ easier to miss when it's inherently the kind of cast where you need to be extremely careful about it.

The programmer also has to be absolutely certain that the function they're calling is not actually going to mutate the data, and if the cast is implicit, it's going to be very easy to not even notice that it's a potential problem.

--
1 day ago
https://issues.dlang.org/show_bug.cgi?id=24793

--- Comment #3 from Dennis <dkorpel@live.nl> ---
(In reply to Jonathan M Davis from comment #2)
> Also, at present, the implicit conversion from a mutable pointer to void* is considered @safe - e.g.
> 
> ---
> void main() @safe
> {
>     int* i;
>     void* v = i;
> }
> ---
> 
> whereas it would be completely inappropriate to consider it memory safe if const is implicitly removed in the process. So, even if we did add such an implicit conversion, it would need to be @system.

No it wouldn't, unless you can actually demonstrate how it would result in memory corruption.

--
1 day ago
https://issues.dlang.org/show_bug.cgi?id=24793

--- Comment #4 from Jonathan M Davis <issues.dlang@jmdavisProg.com> ---
(In reply to Dennis from comment #3)
> (In reply to Jonathan M Davis from comment #2)
> > Also, at present, the implicit conversion from a mutable pointer to void* is considered @safe - e.g.
> > 
> > ---
> > void main() @safe
> > {
> >     int* i;
> >     void* v = i;
> > }
> > ---
> > 
> > whereas it would be completely inappropriate to consider it memory safe if const is implicitly removed in the process. So, even if we did add such an implicit conversion, it would need to be @system.
> 
> No it wouldn't, unless you can actually demonstrate how it would result in memory corruption.

Casting away const in general is supposed to be @system, because it's making it so that it's in the programmer's hands to not violate the type system, and it should need @trusted to indicate that the programmer is claiming that they've verified that the code doesn't violate the type system.

While void* is so limited to the point that it's essentially useless on its own, and any function you call with it that could actually do anything interesting with it would need to be @system anyway, all it takes is one layer of function calls in between, and you have a situation where the void* code has been validated for correctness under the assumption that the data is mutable but which is completely divorced from the code where the conversion to void* occurs. And right now, the assumption that the void* pointed to mutable data would be correct unless the programmer had cast away const elsewhere using a cast that was @system. But with your proposed change here, it would become possible to pass a non-const pointer to an @trusted function - or to an @safe function which calls @trusted function or however many layers happen to be in between - which operates on void* under the assumption that the data is mutable. So, you have an @safe implicit cast resulting in memory corruption in @trusted code that had no reason to expect that it would ever have to worry about operating on const data.

IMHO, we should never be implicitly removing const when any indirections are involved - _especially_ when the code being given the data has to be @trusted and be careful with the assumptions that it makes. It's just a recipe for disaster. And even if the code being called doesn't happen to mutate its data today, it could quite legally be changed to do so tomorrow with the full expectation that it's dealing with data that's mutable, because that's what the type system is telling it what it has.

In addition, we should be very conservative about deciding that something is @safe, especially since all it takes is one assumption about the current situation to change, and it might cease being memory-safe - and that's especially likely to happen when we keep trying to widen what's considered @safe. It's just too easy to accidentally introduce a corner case - or outright miss a corner case in the initial analysis - where the code isn't actually memory safe. We already occasionally find holes in @safe that we missed, and as Rikki points out in https://issues.dlang.org/show_bug.cgi?id=24866, we're risking memory safety issues with stuff like this with the introduction of language features such as placement new.

--
18 hours ago
https://issues.dlang.org/show_bug.cgi?id=24793

--- Comment #5 from Dennis <dkorpel@live.nl> ---
(In reply to Jonathan M Davis from comment #4)
> And right now, the assumption that the void*
> pointed to mutable data would be correct

It seems your whole argument is based on the premise that a function like this could exist:

```
void somehowMutateP(void* p) @trusted;
```

Unless that function checks the validity of `p` at runtime, that's not a safe interface. You cannot statically assume a void* points to something mutable, or that it even is a pointer at all: It can also be an integer or handle, like `PTHREAD_CANCELED = cast(void*) -1` or the result of `CreateSemaphoreW(...)`.

--
12 hours ago
https://issues.dlang.org/show_bug.cgi?id=24793

Nick Treleaven <nick@geany.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |nick@geany.org

--- Comment #6 from Nick Treleaven <nick@geany.org> ---
comment 4:
> we're risking memory safety issues with stuff like this with the introduction of language features such as placement new.

Surely passing a `void*` to placement new is not going to be @safe anyway.

comment 5:
> Unless that function checks the validity of `p` at runtime, that's not a safe interface

True. But there is value in @system code of const(void*) not converting to void* - that is useful with some C APIs. GLib uses const void* parameters (aliased as gconstpointer) here: https://docs.gtk.org/glib/callback.CompareFunc.html.

So perhaps explicit casting of a const T* to a void* should be @safe, because safe D can't mutate the pointee byte(s). But I don't think it should be implicitly allowed.

--
9 hours ago
https://issues.dlang.org/show_bug.cgi?id=24793

Richard (Rikki) Andrew Cattermole <alphaglosined@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |alphaglosined@gmail.com

--- Comment #7 from Richard (Rikki) Andrew Cattermole <alphaglosined@gmail.com> ---
(In reply to Nick Treleaven from comment #6)
> comment 4:
> > we're risking memory safety issues with stuff like this with the introduction of language features such as placement new.
> 
> Surely passing a `void*` to placement new is not going to be @safe anyway.

When I made my comment in the other ticket, it was not mentioned in the DIP.

It was important to note because we have been relying on language features such as it, not existing.

> comment 5:
> > Unless that function checks the validity of `p` at runtime, that's not a safe interface
> 
> True. But there is value in @system code of const(void*) not converting to void* - that is useful with some C APIs. GLib uses const void* parameters (aliased as gconstpointer) here: https://docs.gtk.org/glib/callback.CompareFunc.html.
> 
> So perhaps explicit casting of a const T* to a void* should be @safe, because safe D can't mutate the pointee byte(s). But I don't think it should be implicitly allowed.

I'm on the side of void* shouldn't be able to cast to in @safe at all (unless going straight to size_t). The entry point of memory unsafetyness should not be in a @safe function. @trusted sure, but not @safe.

--