Bug 18945 – immutable variable is used as if it's an enum

Status
NEW
Severity
normal
Priority
P3
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-06-05T10:39:33Z
Last change time
2024-12-13T18:59:03Z
Keywords
CTFE
Assigned to
No Owner
Creator
Simen Kjaeraas
Moved to GitHub: dmd#19444 →

Comments

Comment #0 by simen.kjaras — 2018-06-05T10:39:33Z
unittest { immutable n = __ctfe ? 1 : 2; int[n] a; assert(a.length == n); } The above assert fails - the value of n is calculated using CTFE for the length of a, and is thus set to 1. Then, at runtime, the value of n is set to 2, and the assert fails. It seems to me the issue is that the variable n is being used as a compile-time constant. The expected behavior is an error message along the lines of 'variable n cannot be read at compile time'.
Comment #1 by issues.dlang — 2018-06-05T11:34:50Z
Yeah. And bizarrely, this code actually results in an error about not being able to call stuff during CTFE: int foo() { import std.datetime; return cast(int)Clock.currTime().stdTime; } void main() { immutable i = foo(); int[i] arr; } Getting rid of the declaration for arr fixes the problem. So, clearly, the compiler is currently deciding whether it should do CTFE on an immutable, local variable based on whether it's then used in a context where its value must be known at compile time instead of forcing enum to be used instead. It also seems to do the same with const. The fact that the static array's size can use a local variable is completely inconsistent with how CTFE normally works and makes the whole situation that much more confusing. CTFE should only be kicking in based on whether the value is actually needed at compile time and not based on whether the variable that it's assigned to is then used at compile time.
Comment #2 by davidbennett — 2018-06-05T11:54:53Z
Disclaimer: Just a D user, I hold no decision making power or insight into the history here. const and immutable are runtime lvalues that have a known way to get "a" value at compiletime. As you have noticed the runtime and compiletime values could be different. Here is another example to show the same effect. --- unittest { immutable n = __ctfe ? 1 : 2; enum j = n; assert(n == j); } --- If you wanted to opt-in to making sure you could only use n at runtime you could make it a `static immutable` but this has the effect of running the assignment expression at compiletime. I believe the current functionality is being used in various projects so I dont see this being changed without at least a deprecation process. For example, I've seen sending const variables as template parameters more than a few times. As for my personal opinion on this issue, I believe using const and immutable at compiletime is useful, it's just that the values could be different that's confusing. So I believe the current reasoning goes like this: immutable values are theoretically known at compile time so why not use them. It's not always possible to ctfe so runtime immutable is assigned at runtime. But the runtime value is not known at compiletime, so when it's used we do the ctfe then and error if it cant.
Comment #3 by issues.dlang — 2018-06-05T12:27:08Z
This probably does need to be removed via deprecation rather than simply making it an error, but allowing it is exactly the sort of thing that increases the confusion about how CTFE works and when it's used. Currently, immutable i = foo(); doesn't do CTFE. But immutable i = foo(); int[i] arr; does, and that muddies the waters considerably. We already have problems due to folks getting really confused about why stuff like auto foo(int i) { return bar!i(); } isn't legal, and allowing a runtime variable to be used just because it's immutable makes that worse - even more so when you consider that auto foo(immutable int i) { return bar!i(); } won't work, and neither will auto foo(immutable int i = 42) { return bar!i(); } And allowing immutable i = foo(); int[i] arr; to work doesn't even buy us anything. You could simply use enum instead of immutable, and it works perfectly fine. My guess is that the current behavior was added because of an enhancement request, and the person who implemented it didn't think of all of the consequences that result from that decision (including the issue with __ctfe). But I definitely think that we'd be better off if you _had_ to use an enum in this case and that the fact that a variable was const or immutable would have zero effect on CTFE.
Comment #4 by davidbennett — 2018-06-05T12:47:49Z
I'm not sure what workaround we would recommend in the deprecation messaged as the current functionality is actually hard to replicate... as enum and static would give a different runtime result (actually an error in this case as __ctfe is not known at statictime... only ctfetime and runtime) The best I could do on short notice was: unittest { immutable n = __ctfe ? 1 : 2; enum l = {return __ctfe ? 1 : 2;}(); int[l] a; assert(a.length == n); }
Comment #5 by dfj1esp02 — 2018-06-05T13:48:59Z
--- int f(in int a) pure { return a; } void g() { immutable int a=0; static assert(f(a)==0); } --- This works. --- int f(in ref int a) pure { return a; } void g() { immutable int a=0; static assert(f(a)==0); } --- This doesn't.
Comment #6 by issues.dlang — 2018-06-05T14:01:55Z
(In reply to David Bennett from comment #4) > I'm not sure what workaround we would recommend in the deprecation messaged > as the current functionality is actually hard to replicate. Just use an enum if you want the value to be known and used at compile time, and use an immutable variable if you want it to be known and used at runtime. Don't try to have a variable with different values at compile time and runtime. And if for some reason, you want the value calculated at compile time but to still have a variable, then use an enum to initialize the immutable variable. That's what you have to do with mutable variables already.
Comment #7 by davidbennett — 2018-06-05T15:34:46Z
(In reply to Jonathan M Davis from comment #6) > > Just use an enum if you want the value to be known and used at compile time, > and use an immutable variable if you want it to be known and used at > runtime. Don't try to have a variable with different values at compile time > and runtime. And if for some reason, you want the value calculated at > compile time but to still have a variable, then use an enum to initialize > the immutable variable. That's what you have to do with mutable variables > already. So the deprecation message shouldn't have a concrete example to opt-in to keep the current functionality. Just something like "Deprecation: Using an immutable variable at compile time will be removed in a future version. Try changing `n` in `int[n] a;` to use an enum variable.". And assume D users will know that they will need to implement the enum separately from the immutable if they want to keep the status quo? Then there are immutable struct members used at compile time, they might not be easy to convert to an enum. https://run.dlang.io/is/cfGfxw I've never seen it used like that... but it does work currently.
Comment #8 by davidbennett — 2018-06-05T15:44:55Z
(In reply to David Bennett from comment #7) > Then there are immutable struct members used at compile time, they might not > be easy to convert to an enum. > https://run.dlang.io/is/cfGfxw > I've never seen it used like that... but it does work currently. Actually, I reduced that last case a bit to much that it would work fine even with this change...
Comment #9 by davidbennett — 2018-06-05T16:09:19Z
(In reply to David Bennett from comment #8) > (In reply to David Bennett from comment #7) > > Then there are immutable struct members used at compile time, they might not > > be easy to convert to an enum. > > https://run.dlang.io/is/cfGfxw > > I've never seen it used like that... but it does work currently. > > Actually, I reduced that last case a bit to much that it would work fine > even with this change... Here is the case I was thinking of: https://run.dlang.io/is/zZWdIQ Only works currently with static immutable, so not sure if it could be a problem as I think this change would only effect immutable (without static). sorry for the noise.
Comment #10 by simen.kjaras — 2018-06-06T07:07:23Z
(In reply to David Bennett from comment #9) > https://run.dlang.io/is/zZWdIQ > > Only works currently with static immutable What do you mean? It works perfectly with enum: https://run.dlang.io/is/3Xy5pI
Comment #11 by davidbennett — 2018-06-06T08:13:44Z
(In reply to Simen Kjaeraas from comment #10) > (In reply to David Bennett from comment #9) > > https://run.dlang.io/is/zZWdIQ > > > > Only works currently with static immutable > > What do you mean? It works perfectly with enum: > https://run.dlang.io/is/3Xy5pI So it does... I'm sure I tried that a while back (years maybe) and it didn't so I had been avoiding it.
Comment #12 by robert.schadek — 2024-12-13T18:59:03Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19444 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB