www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Confusion/trying to understand CTFE keywords

reply gdelazzari <giacky98.mail gmail.com> writes:
Hello everyone, I'm new here on the forum but I've been exploring 
D for quite a while. I'm not an expert programmer by any means, 
so this one may be a really silly question and, in that case, 
please forgive me.

With the premise that I've still not looked a lot into "complex" 
compile time "stuff" (whether it's templates, CTFE, compile-time 
constants, etc...), so that may be the reason I may be missing 
the point... I'm trying to understand why keywords such as 
"static" or "enum" are used to denote compile time "things". What 
I mean is that those keywords are also used for other purposes, 
so I find it a bit confusing. Couldn't a keyword like "ctfe" 
(just making it up right now) exist? So that, when seeing 
something like

ctfe myNumber = 5;

ctfe if (myNumber + 2 == 7)
{
   // ...
}

one could immediately understand that the code is 
executed/evaluated at compile time. True, after someone knows 
that "static" and "enum" mean (in the above example) that some 
compile-time things are happening, it's fine. I just find it a 
bit confusing not having a dedicated keyword but re-using 
existing ones that also serve other purposes...

Note that this is not an attack to the language or anything (I 
actually really love it), I'm just trying to understand the 
reasoning behind this choice.

Thank you very much in advance.
Jun 03 2018
next sibling parent Computermatronic <computermatronic gmail.com> writes:
On Sunday, 3 June 2018 at 21:32:06 UTC, gdelazzari wrote:
I'm trying to understand why
 keywords such as "static" or "enum" are used to denote compile 
 time "things". What I mean is that those keywords are also used 
 for other purposes, so I find it a bit confusing. Couldn't a 
 keyword like "ctfe" (just making it up right now) exist?
I believe the enum was chosen over a dedicated keyword for compile-time constants to prevent adding another keyword, as D already has quite a few.
Jun 03 2018
prev sibling next sibling parent Dave Jones <dave jones.com> writes:
On Sunday, 3 June 2018 at 21:32:06 UTC, gdelazzari wrote:
 Note that this is not an attack to the language or anything (I 
 actually really love it), I'm just trying to understand the 
 reasoning behind this choice.
Because they have a thing about not adding new keywords, apparently it's more important that people dont lose a word they can use for a variable name than it is for expressive non context dependent keywords.
Jun 03 2018
prev sibling next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, June 03, 2018 21:32:06 gdelazzari via Digitalmars-d-learn wrote:
 Hello everyone, I'm new here on the forum but I've been exploring
 D for quite a while. I'm not an expert programmer by any means,
 so this one may be a really silly question and, in that case,
 please forgive me.

 With the premise that I've still not looked a lot into "complex"
 compile time "stuff" (whether it's templates, CTFE, compile-time
 constants, etc...), so that may be the reason I may be missing
 the point... I'm trying to understand why keywords such as
 "static" or "enum" are used to denote compile time "things". What
 I mean is that those keywords are also used for other purposes,
 so I find it a bit confusing. Couldn't a keyword like "ctfe"
 (just making it up right now) exist? So that, when seeing
 something like

 ctfe myNumber = 5;

 ctfe if (myNumber + 2 == 7)
 {
    // ...
 }

 one could immediately understand that the code is
 executed/evaluated at compile time. True, after someone knows
 that "static" and "enum" mean (in the above example) that some
 compile-time things are happening, it's fine. I just find it a
 bit confusing not having a dedicated keyword but re-using
 existing ones that also serve other purposes...

 Note that this is not an attack to the language or anything (I
 actually really love it), I'm just trying to understand the
 reasoning behind this choice.

 Thank you very much in advance.
I think that part of your problem here comes from the fact that you think of enum or static are "CTFE keywords." That's not what they are at all. Yes, they can trigger CTFE, but they're not the only way. Given how CTFE works in D, it really wouldn't make sense to have a keyword for it. CTFE is simply what kicks in when you have an expression that _must_ be evaluated at compile time. e.g. enum a = 42; enum Foo { a = 42, b = 92 c = 12 } struct S { int i = 9; } all are cases where CTFE is used, because all of them require that the value be known at compile time. In the example, they're just integers, so no functions are called, but any one of them could be initialized with an expression that involved calling a function. e.g. struct S { int i = foo() + 7; } There is no special keyword here, and there is no need for one. By the language's design, if i is directly initialized, its value must be known at compile time, and so any expression that's used to directly initialize it must be evaluated at compile time. The same goes for enums or static variables. How would you expect something like this to even work with a special keyword for CTFE? Now, the fact that enum was used for manifest constants such as enum foo = "hello"; in addition to actual enums such as enum Color { red, green, blue, orange, yellow } is arguably unnecessary and confusing (and as some of the other posts in this thread mention, this was done to avoid adding a new keyword). So, maybe, we should have had something like manifest foo = "hello"; and made enum foo = "hello"; illegal, but even if we had done something like that, it would not have had any effect on how CTFE works. It would have just clarified the difference between manifest constants and proper enums. As for why enum was reused for manifest constants, it was not only to save a keyword but because they bascially act like anonymous enums (and in fact, that's what the spec calls them) in that how they act is exactly like enums except for the fact that they don't declare a new type. So, whether it would have been better to use a new keyword is a matter of debate. As for static, most of what it does is inherited from C/C++ and Java, and while does get used in several contexts, it's actually used quite consistantly, much as it might not seem that way at first. When talking about a static variable, the key difference between a static variable and an enum (be it a manifest constant or an actual enum) is that a static variable is an actual variable with a location in memory, whereas an enum is just a value that you can refer to by name. The value of the enum is basically copy-pasted wherever it it is used. It's not legal to take the address of an enum, and in fact, that means that if you do something like enum arr = [1, 2, 3]; then every time you use arr, you're potentially allocating a new dynamic array, because foo(arr) is the same as foo([1, 2, 3]) except for two things: 1. If you change the value of arr, it changes its value everywhere, wheres if you use [1, 2, 3] directly, you'd have to change every place that you used it if you wanted to change it. 2. The value of arr is copied, not the expression used to initialize it. So, if you had enum arr = [1, 2, bar()]; and bar() resulted in 42, then foo(arr) would be the same as foo([1, 2, 42]) and not foo([1, 2, bar()]). However, static variables are actually variables. The key difference between them and other variables at the same scope is that they're not associated with that particular instance of whatever the non-static variables are associated with. So, in the case of a class or struct - e.g. struct S { int i; static int s; } the static variable is associated with that class or struct, and that variable is shared across all instances of that class or struct within the same thread (for it it also be shared across threads, it would have to be marked as shared). If the static variable is a local variable, then it's shared across all calls to that function rather than being unique to each call of that function. It's scoped to the function, but otherwise, it's basically the same as a module-level variable or static variable in a class or struct. static on module-level variables is a no-op, since there's only one "instance" of the module. So, they're arguably implied to be static. Basically, whenever the keyword static is used, it means that that symbol has no context. In the case of variables, that means that it's not associated with any particular instance of a class or struct or any particular call to a function. In the case of types or functions, it means that it has no context pointer. The place where this is easiest to understand is with static member functions. e.g. in struct S { int foo() {...} static int bar() {...} ... } foo has the implied this pointer/ref to the object that foo is being called on, whereas bar has no impied this pointer/ref. It's scoped to the struct, but it really isn't much different from a free function in the same module. The primary difference is that it must be called using either the type it's in - e.g. S.bar() - or it must be called on an instance of S - e.g. S.init.bar(). But even if it's called on an instance of S, it still doesn't have access to that instance. It has no context pointer (which in this case would have been the this pointer/ref if it had been a non-static member function). For a nested function such as in auto foo(string s) { static int bar(int i) { ... } ... } the static means that it doesn't have a context pointer to the stack of the function that it's in. It can't access any variables that are inside of its outer function, because that would require a context pointer to the stack of the outer function. On the other hand, if it's non-static, e.g. auto foo(string s) { int bar(int i) { ... } ... } then the nested function does have a context pointer to the stack of the outer function, and it can access those variables and manipulate them. Similarly, nested structs and classes have a context pointer to their containing function. e.g. in auto foo(string s) { struct S { int bar(int i) { ... } ... } ... } the struct S has access to the stack of the function that it's in, and bar could access the function paramters of foo. However, if the struct is static auto foo(string s) { static struct S { int bar(int i) { ... } ... } ... } then it doesn't get a context pointer to foo's stack, and if you try to access it, you'll get an error about bar not being able to access the frame of function foo. It should be noted that this context pointer it why you usually need to mark Voldemort types as being static, because otherwise, the compiler ends up with two context pointers for the struct - the function that it's declared in and the context for the predicate that was passed via the alias template parameter, and it unforunately can't handle having multiple context pointers like that. So, if your nested struct or class doesn't actually need access to its outer scope, it really should be marked as static. Nested structs and classes inside of structs and classes follow similar principles but aren't as consistent about it. Essentially, all classes and structs nested inside of structs are implicitly static. They have no context pointer to the struct that contains them and aren't associated with any particular instance of the struct. Structs nested inside classes are the same. However, non-static classes nested inside classes _do_ have access to the class that contains them. They have an implied variable called outer that is the this reference for the class instance that they're associated with (this is something that comes from Java). But if a class nested inside a class is marked with static, then it is not associated with any class instance and does not have an additional context pointer/reference such as outer. Structs and classes at the module level are basically implied to be static, since they have no additional context pointer and are just scoped by the module. Static constructors then are pretty straightforward. They are used to initialize static variables at runtime instead of compile time and have no context pointers of any kind. Module-level static constructors are used to initialize module-level variables (which are implicitly static), and static class/struct constructors initialize static variables inside classes/structs. So, while static _seems_ somewhat inconsistent at first, the way it's used is pretty consistent overall. The main inconsistency is the places where static is essentially implicit rather than explicit (such as module-level variables or structs nested in other structs or classes). Regardless, none fo the extra complexity with regards to the differences between enum and static or the different ways that they're used really has anything to do with CTFE. CTFE is the general mechanism by which constructs which must have their value known at compile time have the expression that gives them their value evaluated. It just so happens that enum and static variables are two of the various cases where a value must be known at compile time and thus are two of the various cases where CTFE is used. So, sorry for the wall of text, but hopefully that helps clarify things. If you haven't yet, I'd suggest reading http://ddili.org/ders/d.en/index.html Even if you understand most of the content already, it will probably help fill in some of the holes you have in your understanding, and depending on how much you've done with D, it may contain quite a bit of new content that will help you out. - Jonathan M Davis
Jun 03 2018
next sibling parent reply gdelazzari <giacky98.mail gmail.com> writes:
On Monday, 4 June 2018 at 03:18:05 UTC, Jonathan M Davis wrote:
 I think that part of your problem here comes from the fact that 
 you think of enum or static are "CTFE keywords." That's not 
 what they are at all. Yes, they can trigger CTFE, but they're 
 not the only way.

 ...
Thank you very much for the extensive explanation, I really appreciate it. I'm slowly getting the point here, also by reading the relevant parts of the book you linked. I was aware of the meaning of the static keyword (coming from other languages where it has similar meaning), but your re-cap was great anyway since I now have a clear view of how exactly works in D with the various features of the language.
 So, while static _seems_ somewhat inconsistent at first, the 
 way it's used is pretty consistent overall. The main 
 inconsistency is the places where static is essentially 
 implicit rather than explicit (such as module-level variables 
 or structs nested in other structs or classes).
I'm not getting this however. I mean, sure, it's totally consistent if you consider the cases you described, but what I meant is that in a piece of code like static if (a == 3) { // stuff... } static else if (a == 2) { // other stuff... } else { // etc... } the "static" keyword has a different meaning, in this case it means that the if gets evaluated at compile time. This is the kind of inconsistency I was talking about. Of course "static if" doesn't sound bad at all, it somehow conveys the idea of what it actually does. My confusion was regarding the fact that "static" is also used for other things (the ones you described) and then I would also expect that, to define a compile-time value, I would use the same keyword again, like static myCompileTimeValue = 5 * 2; instead of enum myCompileTimeValue = 5 * 2; of course this wouldn't be possible with the keyword "static", since `static val = 4;` would have a totally different meaning in the language, and that's why "enum" is used I guess. But then why not `enum if (...) { ... }`? Again, forget about the keywords themselves, I'm just talking about using the same one which is different from keywords that also do other different (or completely different, or slightly different, but still different) things. I get that "enum" has somewhat of a sense, when doing `enum val = 4;`, after reading the related book chapter, but yeah... that's not the problem, as I hope you now understood what I mean.
 If you haven't yet, I'd suggest reading

 http://ddili.org/ders/d.en/index.html

 Even if you understand most of the content already, it will 
 probably help fill in some of the holes you have in your 
 understanding, and depending on how much you've done with D, it 
 may contain quite a bit of new content that will help you out.

 - Jonathan M Davis
Again, thank you very much for the time you took to answer with that amount of quality information, I really appreciate it. And I also appreciate the suggestion about the book, which I'll read as soon as I can. Maybe I'm missing somewhat of a broader view of the language that, once acquired, will make me understand the real meanings/reasons behind those keywords. This argument I started was kind of a first-impression as a newcomer to the language, with the hope of getting to better understand it. Also I thought it's beneficial for the community to have feedback from new users. Thank you very much again, Giacomo
Jun 04 2018
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, June 04, 2018 14:05:28 gdelazzari via Digitalmars-d-learn wrote:
 On Monday, 4 June 2018 at 03:18:05 UTC, Jonathan M Davis wrote:
 So, while static _seems_ somewhat inconsistent at first, the
 way it's used is pretty consistent overall. The main
 inconsistency is the places where static is essentially
 implicit rather than explicit (such as module-level variables
 or structs nested in other structs or classes).
I'm not getting this however. I mean, sure, it's totally consistent if you consider the cases you described, but what I meant is that in a piece of code like static if (a == 3) { // stuff... } static else if (a == 2) { // other stuff... } else { // etc... } the "static" keyword has a different meaning, in this case it means that the if gets evaluated at compile time. This is the kind of inconsistency I was talking about.
Yeah, that would be inconsistent with the others. I'd forgotten about those when I wrote my reply. Basically, in the case of static if, static foreach, and static assert, it just means that it's the compile-time variant of the construct in question. The keyword reuse works well, but it is inconsistent with the other uses. I guess that it could be argued that they don't have a context, because they're done at compile-time, whereas the runtime variants are run within a specific context, but I think that that's stretching things. However, static used with static if, static foreach, and static assert seems to be very easy for people to understand, whereas some of the other ways it's used often cause confusion at first. Certainly, it seems to be far less confusing for folks than using enum for manifest constants has been.
 Of course "static if" doesn't sound bad at all, it somehow
 conveys the idea of what it actually does. My confusion was
 regarding the fact that "static" is also used for other things
 (the ones you described) and then I would also expect that, to
 define a compile-time value, I would use the same keyword again,
 like

 static myCompileTimeValue = 5 * 2;

 instead of

 enum myCompileTimeValue = 5 * 2;

 of course this wouldn't be possible with the keyword "static",
 since `static val = 4;` would have a totally different meaning in
 the language, and that's why "enum" is used I guess. But then why
 not `enum if (...) { ... }`? Again, forget about the keywords
 themselves, I'm just talking about using the same one which is
 different from keywords that also do other different (or
 completely different, or slightly different, but still different)
 things. I get that "enum" has somewhat of a sense, when doing
 `enum val = 4;`, after reading the related book chapter, but
 yeah... that's not the problem, as I hope you now understood what
 I mean.
Using enum instead of static in contexts such as static if would be using enum in a drastically different context from where enum is normally used (the issue of manifest constants already causes a fair bit of confusion), whereas static seems fairly natural there, and folks are used to static meaning multiple things. In both C/C++ and D, static gets used in multiple contexts where most folks would think that they were completely different when in fact it's possible to come up with a definition that covers most or all of the contexts in which it's used. So, regardless of whether static is actually used consistently, it doesn't feel consistent. So, reusing it for static if is likely to seem much more reasonable to many folks, whereas I think that relatively few would think that reusing enum for that would have made sense. But language design is a bit of an art. It's an interesting combination of doing what makes sense from the standpoint of the compiler writer and what makes sense from the standpoint of the programmer using the language.
 This argument I started was kind of a first-impression as a
 newcomer to the language, with the hope of getting to better
 understand it. Also I thought it's beneficial for the community
 to have feedback from new users.
Constructive feedback is always welcome. And even if nothing in the language gets changed because of feedback, it does help us understand where people need help learning the language. - Jonathan M Davis
Jun 04 2018
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 4 June 2018 at 03:18:05 UTC, Jonathan M Davis wrote:
 [snip]

 If you haven't yet, I'd suggest reading
Would make a good blog series?
Jun 06 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, June 06, 2018 18:18:16 jmh530 via Digitalmars-d-learn wrote:
 On Monday, 4 June 2018 at 03:18:05 UTC, Jonathan M Davis wrote:
 [snip]

 If you haven't yet, I'd suggest reading
Would make a good blog series?
What would make a good blog series? Something talking about CTFE? - Jonathan M Davis
Jun 06 2018
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 6 June 2018 at 22:19:58 UTC, Jonathan M Davis wrote:
 On Wednesday, June 06, 2018 18:18:16 jmh530 via 
 Digitalmars-d-learn wrote:
 On Monday, 4 June 2018 at 03:18:05 UTC, Jonathan M Davis wrote:
 [snip]

 If you haven't yet, I'd suggest reading
Would make a good blog series?
What would make a good blog series? Something talking about CTFE? - Jonathan M Davis
I meant that stuff describing the nuances and motivations between static/enum/etc. Maybe more tutorial or something on the wiki than blog series? It's the type of thing that isn't obvious to someone new to D, but you explain very well. Better to save it in some place easy to find than let it get buried in Learn.
Jun 07 2018
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, June 07, 2018 11:31:13 jmh530 via Digitalmars-d-learn wrote:
 On Wednesday, 6 June 2018 at 22:19:58 UTC, Jonathan M Davis wrote:
 On Wednesday, June 06, 2018 18:18:16 jmh530 via

 Digitalmars-d-learn wrote:
 On Monday, 4 June 2018 at 03:18:05 UTC, Jonathan M Davis wrote:
 [snip]

 If you haven't yet, I'd suggest reading
Would make a good blog series?
What would make a good blog series? Something talking about CTFE? - Jonathan M Davis
I meant that stuff describing the nuances and motivations between static/enum/etc. Maybe more tutorial or something on the wiki than blog series? It's the type of thing that isn't obvious to someone new to D, but you explain very well. Better to save it in some place easy to find than let it get buried in Learn.
I probably should do more write-ups of stuff somewhere so that I can point people to them, but for better or worse, I haven't done much of that. I've mostly just answered questions here or at stackoverflow. I did finally get my website set up though (http://jmdavisprog.com) so that I have somewhere to post articles and whatnot, but I haven't done much with it yet. Sadly, the only reason that I finally got around to setting it up was that I was pretty much forced to in order to have a place to put the documentation for dxml. Doing more useful write-ups has been on my todo list for ages but mostly hasn't ended up as a priority with everything else on my plate - which is arguably a bit dumb given how often I ultimately end up spending a bunch of time answering questions on D.Learn even when I theoretically try to not spend a lot of time replying to posts. - Jonathan M Davis
Jun 07 2018
prev sibling parent reply Gopan <gggopan gmail.com> writes:
On Sunday, 3 June 2018 at 21:32:06 UTC, gdelazzari wrote:
 Couldn't a keyword like "ctfe" (just making it up right now) 
 exist? So that, when seeing something like

 ctfe myNumber = 5;

 ctfe if (myNumber + 2 == 7)
 {
   // ...
 }

 one could immediately understand that the code is 
 executed/evaluated at compile time. True, after someone knows 
 that "static" and "enum" mean (in the above example) that some 
 compile-time things are happening, it's fine. I just find it a 
 bit confusing not having a dedicated keyword but re-using 
 existing ones that also serve other purposes...
Hi gdelazzari, While seeing your post, I just recollected my post 4 years ago. https://forum.dlang.org/post/wbfnvwulchrpnrxovqbr forum.dlang.org I recommend you to go through it. Let me put here my thoughts again. void main() { immutable n = __ctfe ? 1 : 2; int[n] a; assert(a.length == n); // fails, wat } Here, the declaration int[n] a forces n to be calculated during compile time. During compile time, __ctfe is true (I dont know if it a variable or macro, etc). Hence the value of n during compile time is 1. So, size of the array a is 1. Anyway, n is calculated during runtime too. At runtime, __ctfe is false. n becomes 2. Finally, at runtime a.length is 1 and n is 2. This shocked me. ie While reading a huge code, you need to check how you got the value of n, which I personally dont want to. While this scared me away, I thought about it. What is the underlying problem? My immediate answer is that the same variable getting computed during both compile time and runtime caused this issue. Why should a variable get a new value at runtime if it has already got a value during compile time, for the same statement? May be I am less imaginative. I still wish some could enlighten me to accept this behavior if everybody else thinks it is right. Regards, Gopan
Jun 05 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 5 June 2018 at 09:36:22 UTC, Gopan wrote:
 void main()
 {
     immutable n = __ctfe ? 1 : 2;
     int[n] a;
     assert(a.length == n); // fails, wat
 }
That's gotta be a bug - that should give a 'variable n cannot be read at compile time' error. The fact that n is immutable shouldn't be enough to use it at compile time. Filed as https://issues.dlang.org/show_bug.cgi?id=18945. -- Simen
Jun 05 2018
next sibling parent reply Gopan <gggopan gmail.com> writes:
On Tuesday, 5 June 2018 at 10:40:20 UTC, Simen Kjærås wrote:
 On Tuesday, 5 June 2018 at 09:36:22 UTC, Gopan wrote:
 void main()
 {
     immutable n = __ctfe ? 1 : 2;
     int[n] a;
     assert(a.length == n); // fails, wat
 }
That's gotta be a bug - that should give a 'variable n cannot be read at compile time' error. The fact that n is immutable shouldn't be enough to use it at compile time. Filed as https://issues.dlang.org/show_bug.cgi?id=18945. -- Simen
Not only immutable. The behavior is same if you declare n as 'const int' also.
Jun 05 2018
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, June 05, 2018 11:18:05 Gopan via Digitalmars-d-learn wrote:
 On Tuesday, 5 June 2018 at 10:40:20 UTC, Simen Kjrs wrote:
 On Tuesday, 5 June 2018 at 09:36:22 UTC, Gopan wrote:
 void main()
 {

     immutable n = __ctfe ? 1 : 2;
     int[n] a;
     assert(a.length == n); // fails, wat

 }
That's gotta be a bug - that should give a 'variable n cannot be read at compile time' error. The fact that n is immutable shouldn't be enough to use it at compile time. Filed as https://issues.dlang.org/show_bug.cgi?id=18945. -- Simen
Not only immutable. The behavior is same if you declare n as 'const int' also.
It's a bug either way. I suspect that someone made it work as an enhancement request at some point on the theory that the variable was guaranteed to always be the same, and they didn't take __ctfe into account. Regardless, initializing a non-static, local variable shouldn't be triggering CTFE. - Jonathan M Davis
Jun 05 2018
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/5/18 6:40 AM, Simen Kjærås wrote:
 On Tuesday, 5 June 2018 at 09:36:22 UTC, Gopan wrote:
 void main()
 {
     immutable n = __ctfe ? 1 : 2;
     int[n] a;
     assert(a.length == n); // fails, wat
 }
That's gotta be a bug - that should give a 'variable n cannot be read at compile time' error. The fact that n is immutable shouldn't be enough to use it at compile time. Filed as https://issues.dlang.org/show_bug.cgi?id=18945.
Indeed it is a bug. Interesting to see what the compiler sees as its AST: import object; void main() { immutable immutable(int) n = __ctfe ? 1 : 2; int[1] a = 0; assert(1LU == cast(ulong)n); return 0; } This is what -vcg-ast spits out. Note the int[1]. -Steve
Jun 05 2018
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 5 June 2018 at 13:27:35 UTC, Steven Schveighoffer 
wrote:
 On 6/5/18 6:40 AM, Simen Kjærås wrote:
 On Tuesday, 5 June 2018 at 09:36:22 UTC, Gopan wrote:
 void main()
 {
     immutable n = __ctfe ? 1 : 2;
     int[n] a;
     assert(a.length == n); // fails, wat
 }
That's gotta be a bug - that should give a 'variable n cannot be read at compile time' error. The fact that n is immutable shouldn't be enough to use it at compile time. Filed as https://issues.dlang.org/show_bug.cgi?id=18945.
Indeed it is a bug. Interesting to see what the compiler sees as its AST: import object; void main() { immutable immutable(int) n = __ctfe ? 1 : 2; int[1] a = 0; assert(1LU == cast(ulong)n); return 0; } This is what -vcg-ast spits out. Note the int[1]. -Steve
This is not bug just not very intuitive. Since you are declaring a static array the value of n needs to known at compiletime. so it'll try to evaluate n at an compile-time context in which n is 1. however when code-generation for the function is done __ctfe will be false. Causing the n variable to be initialized to 2. Therefore n will not be equal to a.length.
Jun 05 2018
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/5/18 12:10 PM, Stefan Koch wrote:
 This is not bug just not very intuitive.
 
 Since you are declaring a static array the value of n needs to known at 
 compiletime.
 so it'll  try to evaluate n at an compile-time context in which n is 1.
 however when code-generation for the function is done __ctfe will be false.
 Causing the n variable to be initialized to 2.
 
 Therefore n will not be equal to a.length.
No, it's definitely a bug. main is not being evaluated at compile time. The real result of this function should be a compile-time error -- __ctfe is a *runtime* value that is always defined based on whether you are __ctfe or not. Therefore, n must be a runtime value, and not usable as a static array dimension. If the posted code is valid, then this should be valid as well: static if(__ctfe) immutable n = 1; else immutable n = 2; But it's not. -Steve
Jun 05 2018
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 5 June 2018 at 18:00:05 UTC, Steven Schveighoffer 
wrote:

 No, it's definitely a bug. main is not being evaluated at 
 compile time. The real result of this function should be a 
 compile-time error -- __ctfe is a *runtime* value that is 
 always defined based on whether you are __ctfe or not. 
 Therefore, n must be a runtime value, and not usable as a 
 static array dimension.

 If the posted code is valid, then this should be valid as well:

 static if(__ctfe)
    immutable n = 1;
 else
    immutable n = 2;

 But it's not.

 -Steve
I see what you mean. I fear fixing this bug will not be easy without breaking arguably valid uses.
Jun 05 2018
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, June 05, 2018 22:08:32 Stefan Koch via Digitalmars-d-learn 
wrote:
 On Tuesday, 5 June 2018 at 18:00:05 UTC, Steven Schveighoffer

 wrote:
 No, it's definitely a bug. main is not being evaluated at
 compile time. The real result of this function should be a
 compile-time error -- __ctfe is a *runtime* value that is
 always defined based on whether you are __ctfe or not.
 Therefore, n must be a runtime value, and not usable as a
 static array dimension.

 If the posted code is valid, then this should be valid as well:

 static if(__ctfe)

    immutable n = 1;

 else

    immutable n = 2;

 But it's not.

 -Steve
I see what you mean. I fear fixing this bug will not be easy without breaking arguably valid uses.
Any such case should work by either using an enum instead or using an enum to have the value during CTFE and then initialize a const or immutable variable with it. We'd likely need to deprecate the old behavior rather than simply fixing it in order to avoid breaking code, but the result would ultimately be cleaner, and it should be easy for any affected code to be updated. As is standsw the fact that immutable i = foo(); int[i] arr; works and uses CTFE whereas immutable i = foo(); doesn't do any CTFE just increases the confusion of how CTFE works. The fact that __ctfe then affects things further just makes matters worse. CTFE really should only kick in when it has to kick in. That way, it's clean and understandable as to when it kicks in and when it doesn't, and anyone who wants to initialize a local variable with CTFE can always just use an intermediary enum. We have too much confusion over when CTFE kicks in even without this quirk with immutable. - Jonathan M Davis
Jun 05 2018
prev sibling parent reply Gopan <gggopan gmail.com> writes:
On Tuesday, 5 June 2018 at 22:08:32 UTC, Stefan Koch wrote:
 On Tuesday, 5 June 2018 at 18:00:05 UTC, Steven Schveighoffer 
 wrote:

 No, it's definitely a bug. main is not being evaluated at 
 compile time. The real result of this function should be a 
 compile-time error -- __ctfe is a *runtime* value that is 
 always defined based on whether you are __ctfe or not. 
 Therefore, n must be a runtime value, and not usable as a 
 static array dimension.

 If the posted code is valid, then this should be valid as well:

 static if(__ctfe)
    immutable n = 1;
 else
    immutable n = 2;

 But it's not.

 -Steve
I see what you mean. I fear fixing this bug will not be easy without breaking arguably valid uses.
Will it be feasible something like int n = CTFE(foo(3)); //dont limit CTFE to enum or immutable, etc. You are calling explicitly. int[n] arr; So that, we can be explicit about when CTFE kicks in. In that case, don't assign value to n at runtime as it has been initialized at compile time. This way, we can get rid of the intermediate enums which are introduced just for the sake of inviting CTFE. Also, this way, it will not silently break existing code; instead a compilation error must be thrown "value of n not known at compile time" for the below code. immutable n = foo(); int[n] arr; Unless called throgh CTFE(....), dont go for CTFE. Will this work? Just putting my thoughts... I am no expert.
Jun 06 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, June 07, 2018 04:41:35 Gopan via Digitalmars-d-learn wrote:
 On Tuesday, 5 June 2018 at 22:08:32 UTC, Stefan Koch wrote:
 On Tuesday, 5 June 2018 at 18:00:05 UTC, Steven Schveighoffer

 wrote:
 No, it's definitely a bug. main is not being evaluated at
 compile time. The real result of this function should be a
 compile-time error -- __ctfe is a *runtime* value that is
 always defined based on whether you are __ctfe or not.
 Therefore, n must be a runtime value, and not usable as a
 static array dimension.

 If the posted code is valid, then this should be valid as well:

 static if(__ctfe)

    immutable n = 1;

 else

    immutable n = 2;

 But it's not.

 -Steve
I see what you mean. I fear fixing this bug will not be easy without breaking arguably valid uses.
Will it be feasible something like int n = CTFE(foo(3)); //dont limit CTFE to enum or immutable, etc. You are calling explicitly. int[n] arr; So that, we can be explicit about when CTFE kicks in. In that case, don't assign value to n at runtime as it has been initialized at compile time. This way, we can get rid of the intermediate enums which are introduced just for the sake of inviting CTFE. Also, this way, it will not silently break existing code; instead a compilation error must be thrown "value of n not known at compile time" for the below code. immutable n = foo(); int[n] arr; Unless called throgh CTFE(....), dont go for CTFE. Will this work? Just putting my thoughts... I am no expert.
The core problem with this is that it's using a runtime variable at compile time, which doesn't normally work. The fact that it works with an immutable variable is a weird quirk based on the fact that it was assumed that the value would be identical at both runtime and compile time, and as already shown in this thread, that results in other bugs. It would be trivial enough to create a wrapper template so that you can do something like immutable n = ctfe!(foo()); e.g. template ctfe(alias value) { enum ctfe = value; } The problem is the fact that a runtime variable is being allowed in a context that requires a compile time value. That violates how CTFE normally works, increasing the confusion about when CTFE kicks in, and as shown in this thread, it can result in subtle bugs. - Jonathan M Davis
Jun 06 2018
parent reply David Bennett <davidbennett bravevision.com> writes:
On Thursday, 7 June 2018 at 04:58:40 UTC, Jonathan M Davis wrote:
 It would be trivial enough to create a wrapper template so that 
 you can do something like

 immutable n = ctfe!(foo());

 e.g.

 template ctfe(alias value)
 {
     enum ctfe = value;
 }
Would this be equivalent to using static immutable? static immutable n = foo(); In this case both the compiletime and runtime values were calculated using cfte. Also back to the OP the way I think of enum, static types is like this: alias and enum create compiletime stuff from compiletime stuff. static creates runtime stuff from compiletime stuff. Is that view valid in most cases?
Jun 07 2018
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
All this talk of CTFE and "compile-time", along with the confusion that
arises from conflating everything done by the compiler into the blanket
term "compile-time" makes me want to scream:

	https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time

</shameless self-plug>

;-)

tl;dr: D's "compile-time" features consist of (at least) two distinct
phases of code transformation: template expansion and CTFE, and having a
proper understanding of exactly what they are and what the distinction
between them is, will go a long way in clearing up a lot of the
confusion commonly experienced by newcomers to D in regard to D's
so-called "compile-time" features.


--T
Jun 07 2018
next sibling parent reply Gopan <gggopan gmail.com> writes:
On Friday, 8 June 2018 at 05:10:16 UTC, H. S. Teoh wrote:
 All this talk of CTFE and "compile-time", along with the 
 confusion that arises from conflating everything done by the 
 compiler into the blanket term "compile-time" makes me want to 
 scream:

 	https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time

 </shameless self-plug>
Hi Teoh, Your article is a killer! A must read for all serious users. Can't we navigate to this article from the DLang site? (I mean, if I don't know the specific url, how can I reach there?) Thanks, Gopan
Jun 08 2018
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jun 08, 2018 at 02:36:01PM +0000, Gopan via Digitalmars-d-learn wrote:
 On Friday, 8 June 2018 at 05:10:16 UTC, H. S. Teoh wrote:
 All this talk of CTFE and "compile-time", along with the confusion
 that arises from conflating everything done by the compiler into the
 blanket term "compile-time" makes me want to scream:
 
 	https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time
 
 </shameless self-plug>
Hi Teoh, Your article is a killer! A must read for all serious users. Can't we navigate to this article from the DLang site? (I mean, if I don't know the specific url, how can I reach there?)
[...] The goal is to eventually put this article among the main articles on the wiki, but there are some loose ends that I need to clean up, and I just haven't had the time to work on it. Perhaps I should just invite others here to edit the article, clean it up, and publish it, instead of letting it linger forever as a userpage. I just don't have enough spare time right now to do it justice. T -- What do you call optometrist jokes? Vitreous humor.
Jun 08 2018
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 8 June 2018 at 05:10:16 UTC, H. S. Teoh wrote:
 All this talk of CTFE and "compile-time", along with the 
 confusion that arises from conflating everything done by the 
 compiler into the blanket term "compile-time" makes me want to 
 scream:

 	https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time

 </shameless self-plug>

 ;-)

 tl;dr: D's "compile-time" features consist of (at least) two 
 distinct phases of code transformation: template expansion and 
 CTFE, and having a proper understanding of exactly what they 
 are and what the distinction between them is, will go a long 
 way in clearing up a lot of the confusion commonly experienced 
 by newcomers to D in regard to D's so-called "compile-time" 
 features.


 --T
May I suggest that you add a flow chart that gives a very high level understanding of the compiler steps. Like how Rust's introduction to MIR has: https://blog.rust-lang.org/2016/04/19/MIR.html
Jun 08 2018
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jun 08, 2018 at 02:57:26PM +0000, jmh530 via Digitalmars-d-learn wrote:
[...]
 May I suggest that you add a flow chart that gives a very high level
 understanding of the compiler steps. Like how Rust's introduction to
 MIR has:
 https://blog.rust-lang.org/2016/04/19/MIR.html
I would, if I had the time to spare to make such a chart. Perhaps you (or somebody here) could do it? It *is* a wiki, after all. Y'all don't have to wait for me to get around to it, the edit button is right there. T -- Give a man a fish, and he eats once. Teach a man to fish, and he will sit forever.
Jun 08 2018
parent jmh530 <john.michael.hall gmail.com> writes:
On Friday, 8 June 2018 at 17:09:54 UTC, H. S. Teoh wrote:
 I would, if I had the time to spare to make such a chart.  
 Perhaps you (or somebody here) could do it?  It *is* a wiki, 
 after all.  Y'all don't have to wait for me to get around to 
 it, the edit button is right there.


 T
Fair point.
Jun 08 2018
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, June 08, 2018 03:51:11 David Bennett via Digitalmars-d-learn 
wrote:
 On Thursday, 7 June 2018 at 04:58:40 UTC, Jonathan M Davis wrote:
 It would be trivial enough to create a wrapper template so that
 you can do something like

 immutable n = ctfe!(foo());

 e.g.

 template ctfe(alias value)
 {

     enum ctfe = value;

 }
Would this be equivalent to using static immutable? static immutable n = foo(); In this case both the compiletime and runtime values were calculated using cfte.
No, they're not equivalent. That template definition just forces the value to be calculated at compile time. It doesn't change anything about the variable itself. Making the variable static means that there is only one instance of the variable across all calls to the function. So, its address is not within the stack of the function. Given that the variable is immutable and thus cannot be changed means that that distinction doesn't matter a lot in this case, but there's still a difference. And if you make the variables mutable, it makes a huge difference. e.g auto n = ctfe!(foo()); vs static n = foo(); In both cases, the value is calculated at compile time, but in the first case, you get a unique copy for each function call that does not exist beyond that function call, whereas in the second case, it's reused for each function call, so if you mutate it, subsequent calls will see the mutated value (which would not be true if the variable weren't static).
 Also back to the OP the way I think of enum, static types is like
 this:

 alias and enum create compiletime stuff from compiletime stuff.
 static creates runtime stuff from compiletime stuff.

 Is that view valid in most cases?
Sort of but not really. enums are just names for compile-time values. As such, they have to be calculated at compile time. static variables on the other hand really have nothing to do with compile time per se. It's just that they have a single location in memory for the entire thread of the program. So, they get initialized when the thread is created and not when the function is called and as such have to have their initial value known at compile time unless a static constructor is used to initialize them. They're either intialized with the init value for their type or the value that they're given when declared. The exact same thing happens with member variables. e.g. struct S { int i = foo(); } makes it so that S.init.i is whatever value was the result of foo. If the struct has a constructor, then that's the value of i before the constructor is called. The init value for the struct must be known at compile time, so any direct initialization is done at compile time (whereas if none is done, then the init value for the type of the member variable is used). If the member variable is const or immutable, then if it's directly initialized, then it can't be assigned in the constructor, and if it's not directly initialized, its init value is overwritten by the constructor and isn't really used. But regardless, the init value must be known at compile time, which means that any direct initialization must be calculated at compile time. So, the fact that static variables get their intial values at compile time is just because of how they're intialized, not because of static specifically being designed with compile-time anything in mind. The result is that you then have a variable whose initial value was determined at compile time and then can be manipulated at runtime (assuming that it's mutable), but the key thing about static variables is their lifetime, not that they're initialized at compile time. If what you're really looking for is to calculate the value of a local variable at compile-time, you're better off using an enum and then initializing the local variable with the enum than using a static variable. The only reason to use a static variable is if you specifically want a variable whose lifetime is for the duration of the thread rather than the duration of the function call. If you're using static just to get the value calculated at compile time, you're increasing how much memory your thread takes up when there really isn't a good reason to do so. - Jonathan M Davis
Jun 08 2018