[Issue 22633] New: Associative array require and update don't work if the value type can't be reassigned or copied.

d-bugmail at puremagic.com d-bugmail at puremagic.com
Tue Dec 28 09:59:18 UTC 2021


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

          Issue ID: 22633
           Summary: Associative array require and update don't work if the
                    value type can't be reassigned or copied.
           Product: D
           Version: D2
          Hardware: x86_64
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P1
         Component: druntime
          Assignee: nobody at puremagic.com
          Reporter: thomas.bockman at gmail.com

The program below should work, but instead fails to compile with various errors
of the form:

Error: cannot modify struct instance `*p` of type `S` because it contains
`const` or `immutable` members

///////////////////////////////////////////////////
module app;

import std.stdio : writeln;

struct S {
    const(int) a;
}

void main() @safe {
    S[string] aa;

    aa.require("x", S(1));
    writeln(aa["x"]);

    aa.update("y", () => S(2), (ref const(S)) { });
    writeln(aa["y"]);
}
///////////////////////////////////////////////////

The cause of the error is that object.require and object.update both use
assignment for initialization, even though it is known at compile time that the
target is new and has not yet been constructed.

The fix is to use core.lifetime.moveEmplace for initialization, instead:

///////////////////////////////////////////////////
ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
{
    bool found;
    // if key is @safe-ly copyable, `require` can infer @safe
    static if (isSafeCopyable!K)
    {
        auto p = () @trusted
        {
            return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof,
&key, found);
        } ();
    }
    else
    {
        auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key,
found);
    }
    if (found)
        return *p;
    else
    {
        import core.internal.traits : hasElaborateMove;
        static if(hasElaborateMove!V)
            () @system { } ();

        (V value) @trusted
        {
            import core.lifetime : moveEmplace;
            moveEmplace(value, *p);
        } (value());
        return *p;  // this might not return a ref to the left-hand side.
    }
}

// ...

void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) ||
is(typeof(update(aa[K.init])) == void)))
{
    bool found;
    // if key is @safe-ly copyable, `update` may infer @safe
    static if (isSafeCopyable!K)
    {
        auto p = () @trusted
        {
            return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof,
&key, found);
        } ();
    }
    else
    {
        auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key,
found);
    }
    if (!found)
    {
        import core.internal.traits : hasElaborateMove;
        static if(hasElaborateMove!V)
            () @system { } ();

        (V value) @trusted
        {
            import core.lifetime : moveEmplace;
            moveEmplace(value, *p);
        } (create());
    }
        else
    {
        static if (is(typeof(update(*p)) == void))
            update(*p);
        else
            *p = update(*p);
    }
}
///////////////////////////////////////////////////

Forum thread: https://forum.dlang.org/post/cmvdguqsiqebppwdvlly@forum.dlang.org

--


More information about the Digitalmars-d-bugs mailing list