Issue 23375 - enum is not considered global mutable state
Summary: enum is not considered global mutable state
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P2 critical
Assignee: No Owner
URL:
Keywords: accepts-invalid
Depends on:
Blocks:
 
Reported: 2022-09-26 09:42 UTC by Bolpat
Modified: 2022-12-17 10:40 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Bolpat 2022-09-26 09:42:03 UTC
An enum of slice of class can be mutated and it can be mutated even in a `pure` function.

Example:

    class C
    {
        string s;
        this(string a) pure @safe nothrow  { s = a; }
    }

    enum C[] cs = [ new C("a"), new C("b") ];

    void f() pure @safe @nogc nothrow
    {
        cs[0].s = "c";
    }
Comment 1 Dennis 2022-09-26 09:59:57 UTC
I thought it would allocate a new array literal in f(), but it really modifies global state:

```
class C
{
    string s;
    this(string a) pure @safe nothrow  { s = a; }
    override string toString() {return s;}
}

enum C[] cs = [ new C("a"), new C("b") ];

void f() pure @safe @nogc nothrow
{
    cs[0].s = "c";
}

import std.writeln;
void main()
{
    writeln(cs); // [a, b]
    f();
    writeln(cs); // [c, b]
}
```

I think the could should be accepted without @nogc, and it would modify a copy of the array.
Comment 2 Bolpat 2022-09-26 12:28:42 UTC
As enum was intended to be a replacement of C’s `#define` constants, the compiler should thoroughly treat `enum` as a named literal; it can support “mutable” indirections, but a lot more has to be taken. By “mutable” I mean typed non-const, not that actual mutation is valid.

An easy way would be to specify that enums are deep-copied at usage site. This might be possible because circular definitions are rejected:

    class C
    {
        C bestie;
        this() { }
        this(C friend) { bestie = friend; }
    }

    enum C me = new C(you);
    enum C you = new C(me); // error
    enum C[] us = [ me, you ];

But you can cheese it:

    enum C[] us = [ new C, new C ];

    shared static this()
    {
        us[0].bestie = us[1];
        us[1].bestie = us[0];
    }

    void main()
    {
        assert(us[0].bestie is us[1]);
        assert(us[1].bestie is us[0]);
        assert(us[0].bestie.bestie is us[0]);
        assert(us[1].bestie.bestie is us[1]);
    }