[Issue 18755] std.typecons.Rebindable breaks @safe-ty

d-bugmail at puremagic.com d-bugmail at puremagic.com
Sun Aug 26 08:41:20 UTC 2018


https://issues.dlang.org/show_bug.cgi?id=18755

Johannes Loher <johannes.loher at fg4f.de> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |johannes.loher at fg4f.de

--- Comment #1 from Johannes Loher <johannes.loher at fg4f.de> ---
This also applies to std.typecons.UnqualRef because they use the same
implementation which is responsible for this:

---
class Foo {
    auto opCast(T)() @system immutable pure nothrow {
        *(cast(uint*)0xdeadbeef) = 0xcafebabe;
        return T.init;
    }
}

void main() @safe {
    import std.typecons;
    auto r = UnqualRef!(immutable Foo)(new Foo); // oops
}
---

The reason for the breakage is that they both implement this opAssign, which
gets called by the constructor:

---
void opAssign(T another) @trusted pure nothrow @nogc
{
    stripped = cast(U) another;
}
---

Notice how this performs the cast in an @trusted context.

The problem with this is that we actually need to do this, because casting is
not @safe, but in this case we are only casting away immutability etc. which is
actually verified manually to be safe in this case.

To get around this, we would need to check, if there is a user defined opCast
and if it is provided, check if is safe. If it is not safe, we can either
decide to not instanciate Rebindable / UnqualRef, or we can instanciate
versions of them, which are not @safe (i.e. we omit the @trusted attributes at
the corresponding places).

What do you think would be the correct thing to do in this case?

Here is a basic implementation, which does the necessary checks:

---
template hasElaborateCast(T)
{
    import std.traits : hasMember;
    enum hasElaborateCast = hasMember!(T, "opCast");
}

template hasSafeElaborateCast(T, U)
{
    enum hasSafeElaborateCast = hasElaborateCast!T && __traits(compiles, ()
@safe{ T.init.opCast!U; });
}

private mixin template RebindableCommon(T, U, alias This)
if ((is(T == class) || is(T == interface) || isAssociativeArray!T) &&
    (!hasElaborateCast!T || hasSafeElaborateCast!(T, U)))
/* ... */
---

In this case, I put in as a template constraint, but as mentioned, we could
also put it in static ifs to decide whether the implementation should be
@trusted or not.

--


More information about the Digitalmars-d-bugs mailing list