dmd icon indicating copy to clipboard operation
dmd copied to clipboard

Associative array `byKey` and `byKeyValue` allow writing keys

Open Bolpat opened this issue 2 weeks ago • 2 comments

Using aa.byKey one can write the keys of an associative array and break its invariants. It even works when the AA is typed immutable:

void main() @safe
{
    int[int] aa = [1: 10];
    foreach (ref k; aa.byKey)
    {
        pragma(msg, typeof(k)); // int, but should be const(int) ❌
        k = 2; // should be an error (assignment to const) ❌
    }
    // `aa` is broken now
    assert(aa[1] == 10); // fails ❗
    assert(aa[2] == 10); // fails ❗
}

Problems with immutable associative arrays:

void main() @safe
{
    immutable int[int] aa = [1: 10];
    foreach (ref k; aa.byKey)
    {
        pragma(msg, typeof(k)); // int, but should be immutable(int) ❌
        k = 2; // should be an error (assignment to immutable) ❌
    }
    // For completeness, `byValue` works as expected:
    foreach (ref v; aa.byValue)
    {
        pragma(msg, typeof(v)); // immutable(int) ✔️
        v = 20; // Error: cannot modify `immutable` expression ✔️
    }
}

The same applies to byKeyValue:

void main() @safe
{
    int[int] aa = [1: 10];
    foreach (kv; aa.byKeyValue)
    {
        pragma(msg, typeof(kv.key)); // int, but should be const(int) ❌
        kv.key = 2; // should be an error (assignment to const) ❌
    }
}

and

void main() @safe
{
    immutable int[int] aa = [1: 10];
    foreach (kv; aa.byKeyValue)
    {
        pragma(msg, typeof(kv.key)); // int, but should be immutable(int) ❌
        kv.key = 2; // should be an error (assignment to immutable) ❌
    }
}

Bolpat avatar Dec 04 '25 11:12 Bolpat

Seems to be a duplicate of #17408, though this is interesting as it points out immutable violation too.

ntrel avatar Dec 04 '25 17:12 ntrel

Note: if this gets fixed, the AA properties docs (updated in dlang/dlang.org#4339) need to have the 2 bug notes removed.

ntrel avatar Dec 07 '25 20:12 ntrel