strange CFTE issue

ag0aep6g via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Mar 14 20:40:42 PDT 2017


On 03/15/2017 03:01 AM, Inquie wrote:
>
> If I do something like
>
> enum X = Methods!(C);
>
> foreach(x; X)
> {
>     mixin(x);
> }
>
> I get an error about x not being a compile time variable. (code above is
> simplified, the error has nothing to do with the form but of the
> foreach(x )

"Compile time variable" may be misleading here. The compiler does not 
try to figure out what values are actually constant at compile time. 
Rather, the language defines some specific cases where it's guaranteed 
that a value is a compile-time constant. Only then can you use it in 
static contexts such as mixins. Other values are rejected, even if they 
would turn out be constant on a closer look.

`foreach` is mostly a run-time feature. There is a special case when you 
iterate over a "compile-time list" [1] (aka AliasSeq, formerly 
TypeTuple). In that case, the loop variable is recognized as a constant. 
That variant of `foreach` is also dubbed a "static foreach" (even though 
the `static` keyword is not used).

Note that it's not a "static foreach" when you iterate over an array, no 
matter if that array is constant at compile time or not.

So this works:

     import std.meta: AliasSeq;
     foreach (x; AliasSeq!("int foo;", "double bar;")) mixin(x);

But this doesn't:

     foreach (x; ["int foo;", "double bar;"]) mixin(x);

This is expected and works as intended. Without the definition of your 
`Methods` template, I can't say for sure if you're hitting this or if 
something else is going on. But if your `X` is an array, this is it.

> but if I wrap it in a function it works
>
> string foo()
> {
> enum X = Methods!(C);
> string y = "";
> foreach(x; X)
> {
>    y ~= x;
> }
>  return y;
> }
>
> mixin(y);

(I'm assuming that last line should be `mixin(foo());`.)

The return value of `foo` is recognized as a compile-time constant 
there, because you call it in a context that forces it. This is called CTFE.

Note that you cannot assign the result of `foo()` to a variable first:

    string y = foo(); /* no CTFE */
    mixin(y); /* no go */

That's because `y` is a normal variable, which is not a recognized 
compile-time constant. The value could of course be evaluated at 
compile-time, but the compiler doesn't attempt CTFE opportunistically.

> The only diff, of course, is the foreach in the first case mixes in on
> each iteration, while in the second it doesn't... but it shouldn't
> matter. in both cases x is the same.. and it definitely is a compile
> time constant in both.

To the compiler it's not a "compile-time constant" in either of them (in 
a rather specific sense of the term "compile-time"). During CTFE, 
run-time rules apply. So in `foo`, `x` is a normal variable. The same 
rules apply as for actual run-time variables. Only the return value of 
`foo` is seen as a compile-time value by the compiler.

You're not the first one who stumbles over this meaning of "compile 
time". CTFE happens at compile time, and has "compile time" in the name, 
but during CTFE you're actually dealing with "run time" values that 
might never see the actual run time. Maybe these things could use some 
better names.


[1] https://dlang.org/ctarguments.html


More information about the Digitalmars-d-learn mailing list