Jump to page: 1 2
Thread overview
Stop using the enum as a manifest constant.
Jun 06, 2021
Jack Applegame
Jun 06, 2021
evilrat
Jun 06, 2021
Jack Applegame
Jun 06, 2021
evilrat
Jun 06, 2021
Jack Applegame
Jun 06, 2021
Mathias LANG
Jun 06, 2021
Jack Applegame
Jun 06, 2021
Mathias LANG
Jun 06, 2021
Jack Applegame
Jun 06, 2021
Mathias LANG
Jun 06, 2021
Jack Applegame
Jul 09, 2021
Q. Schroll
Jun 06, 2021
Jack Applegame
Jun 07, 2021
Claude
June 06, 2021

Just don't do it.
Use const or immutable.
If you want to force CTFE, then use static const or static immutable.

First, using an enumerable as a constant is quite strange in itself.

Second, look at this:

pragma(inline, false)
void sideEffect(const int[] arr) @nogc {
    import core.volatile;
    volatileLoad(cast(uint*) arr.ptr);
}

int[] genArray(size_t length) {
    auto result = new int[length];
    foreach(i, ref e; result) e = cast(int) i;
    return result;
}

void useEnum() {
    enum f1 = genArray(3);
    sideEffect(f1);
    sideEffect(f1);
}

void useConst() {
    static const f1 = genArray(3);
    sideEffect(f1);
    sideEffect(f1);
}

Assembler output (ldc2 -O3):

void example.useEnum():
        push    r14
        push    rbx
        push    rax
        mov     r14, qword ptr [rip + TypeInfo_xAi.__init@GOTPCREL]
        mov     esi, 3
        mov     rdi, r14
        call    _d_newarrayU@PLT
        movabs  rbx, 4294967296
        mov     qword ptr [rdx], rbx
        mov     dword ptr [rdx + 8], 2
        mov     edi, 3
        mov     rsi, rdx
        call    @nogc void example.sideEffect(const(int[]))@PLT
        mov     esi, 3
        mov     rdi, r14
        call    _d_newarrayU@PLT
        mov     qword ptr [rdx], rbx
        mov     dword ptr [rdx + 8], 2
        mov     edi, 3
        mov     rsi, rdx
        add     rsp, 8
        pop     rbx
        pop     r14
        jmp     @nogc void example.sideEffect(const(int[]))@PLT

void example.useConst():
        push    rbx
        lea     rbx, [rip + .dynarrayStorage]
        mov     edi, 3
        mov     rsi, rbx
        call    @nogc void example.sideEffect(const(int[]))@PLT
        mov     edi, 3
        mov     rsi, rbx
        pop     rbx
        jmp     @nogc void example.sideEffect(const(int[]))@PLT

.dynarrayStorage:
        .long   0
        .long   1
        .long   2

We should ban it forever.
enum a = 10;
const a = 10;

June 06, 2021

On Sunday, 6 June 2021 at 09:49:11 UTC, Jack Applegame wrote:

>

If you want to force CTFE, then use static const or static immutable.

What did you expected from an enum array? It is known to allocate every time you reference it (except maybe for strings), have you just noticed this?

June 06, 2021

On Sunday, 6 June 2021 at 09:53:09 UTC, evilrat wrote:

>

On Sunday, 6 June 2021 at 09:49:11 UTC, Jack Applegame wrote:

What did you expected from an enum array?

The same output as when using a static const array.

>

It is known to allocate every time you reference it (except maybe for strings), have you just noticed this?

It's a well-known "feature" of enum. And it's one of the reasons why we should ban such use of it.

June 06, 2021

On Sunday, 6 June 2021 at 10:09:03 UTC, Jack Applegame wrote:

>

On Sunday, 6 June 2021 at 09:53:09 UTC, evilrat wrote:

>

On Sunday, 6 June 2021 at 09:49:11 UTC, Jack Applegame wrote:

What did you expected from an enum array?

The same output as when using a static const array.

>

It is known to allocate every time you reference it (except maybe for strings), have you just noticed this?

It's a well-known "feature" of enum. And it's one of the reasons why we should ban such use of it.

Nah, that enum arrays specifically, deprecating this specific case is probably ok though.

June 06, 2021

On Sunday, 6 June 2021 at 10:24:54 UTC, evilrat wrote:

>

Nah, that enum arrays specifically,

Except for character arrays. Just change int[] to char[]/wchar[]/dchar[].

void example.useEnum():
        push    rbx
        lea     rbx, [rip + .L.str]
        mov     edi, 3
        mov     rsi, rbx
        call    @nogc void example.sideEffect(const(char[]))@PLT
        mov     edi, 3
        mov     rsi, rbx
        pop     rbx
        jmp     @nogc void example.sideEffect(const(char[]))@PLT

void example.useConst():
        push    rbx
        lea     rbx, [rip + .L.str]
        mov     edi, 3
        mov     rsi, rbx
        call    @nogc void example.sideEffect(const(char[]))@PLT
        mov     edi, 3
        mov     rsi, rbx
        pop     rbx
        jmp     @nogc void example.sideEffect(const(char[]))@PLT
>

deprecating this specific case is probably ok though.

That's not enough. Let's deprecate any use of enum as a manifest constant.

June 06, 2021

On Sunday, 6 June 2021 at 10:40:13 UTC, Jack Applegame wrote:

>

That's not enough. Let's deprecate any use of enum as a manifest constant.

I wouldn't go that far. enum are like #define, and there are cases where you want them. E.g. when using them as define arguments:

void foo (char[] fmt = DEFAULT_VALUE) { /* ... */ }

If DEFAULT_VALUE is immutable, this will never compile, but with enum, it might. You'd think that's an odd use case, but we actually had a few instances at Sociomantic which triggered when we tried to change some internal tools to use static immutable instead of enum.

I agree with the wider point though. People expect manifest constant to have an address and to be eagerly evaluated. That's a [static] immutable. enum is just a copy-pasted literal.

June 06, 2021

On Sunday, 6 June 2021 at 10:47:54 UTC, Mathias LANG wrote:

>

I wouldn't go that far. enum are like #define, and there are cases where you want them. E.g. when using them as define arguments:

void foo (char[] fmt = DEFAULT_VALUE) { /* ... */ }

If DEFAULT_VALUE is immutable, this will never compile, but with enum, it might.

Really?

enum DEFAULT_VALUE = "aaaa";
void foo (char[] fmt = DEFAULT_VALUE) {} // Error: cannot implicitly convert expression `"aaaa"` of type `string` to `char[]`
June 06, 2021

On Sunday, 6 June 2021 at 10:47:54 UTC, Mathias LANG wrote:

>

I wouldn't go that far. enum are like #define, and there are cases where you want them. E.g. when using them as define arguments:

void foo (char[] fmt = DEFAULT_VALUE) { /* ... */ }

If DEFAULT_VALUE is immutable, this will never compile, but with enum, it might.

Both compiles perfectly:

const DEFAULT_VALUE = "aaaa";
void foo (const char[] fmt = DEFAULT_VALUE) {}

const DEFAULT_VALUE = "aaaa";
void foo (char[] fmt = DEFAULT_VALUE.dup) {}
June 06, 2021

On Sunday, 6 June 2021 at 11:16:29 UTC, Jack Applegame wrote:

>

On Sunday, 6 June 2021 at 10:47:54 UTC, Mathias LANG wrote:

>

I wouldn't go that far. enum are like #define, and there are cases where you want them. E.g. when using them as define arguments:

void foo (char[] fmt = DEFAULT_VALUE) { /* ... */ }

If DEFAULT_VALUE is immutable, this will never compile, but with enum, it might.

Really?

enum DEFAULT_VALUE = "aaaa";
void foo (char[] fmt = DEFAULT_VALUE) {} // Error: cannot implicitly convert expression `"aaaa"` of type `string` to `char[]`

It might, e.g. the following:

enum DEFAULT = foo();
char[] foo () { return null; }
void bar (char[] arg = DEFAULT);
June 06, 2021

On Sunday, 6 June 2021 at 13:21:35 UTC, Mathias LANG wrote:

>

It might, e.g. the following:

enum DEFAULT = foo();
char[] foo () { return null; }
void bar (char[] arg = DEFAULT);

Mutable enum??? WAT?
It's absolutely unacceptable.

char[] foo() @safe { return "hello".dup; }
enum WAT = foo();
void main() @safe {
    WAT[0] = 'w';
    writeln(WAT);
}
Error: program killed by signal 11
« First   ‹ Prev
1 2