Thread overview
Switch constants
Nov 13, 2010
bearophile
Nov 13, 2010
Dmitry Olshansky
Nov 13, 2010
Daniel Murphy
Nov 14, 2010
bearophile
Nov 14, 2010
Stanislav Blinov
Nov 14, 2010
BCS
November 13, 2010
In a not-ranged cases body, like in the program below (that doesn't compile), the switch variable is a compile-time constant, so why doesn't the compile see x as constant there?


template Foo(uint x) {
    static if (x <= 1)
        enum Foo = 1;
    else
        enum Foo = x * Foo!(x - 1);
}

int bar(uint x) {
    switch (x) {
        case 0: return Foo!x;
        case 1: return Foo!x;
        case 2: return Foo!x;
        case 3: return Foo!x;
        case 4: return Foo!x;
        default: return -1;
    }
}

void main() {
    assert(bar(4) == 24);
}


That code works if I replace lines like:
case 2: return Foo!x;

With:
case 2: return Foo!2;

But when the code isn't DRY bugs may happen...
(There are ten different better ways to write that program, but this is not the point).

Bye,
bearophile
November 13, 2010
On 14.11.2010 1:21, bearophile wrote:
> In a not-ranged cases body, like in the program below (that doesn't compile), the switch variable is a compile-time constant, so why doesn't the compile see x as constant there?
>
Well, there is fall-through ;) And there still could be goto's.
In essence "case x:" is nothing but a glorified local label.
> template Foo(uint x) {
>      static if (x<= 1)
>          enum Foo = 1;
>      else
>          enum Foo = x * Foo!(x - 1);
> }
>
> int bar(uint x) {
>      switch (x) {
>          case 0: return Foo!x;
>          case 1: return Foo!x;
>          case 2: return Foo!x;
>          case 3: return Foo!x;
>          case 4: return Foo!x;
>          default: return -1;
>      }
> }
>
> void main() {
>      assert(bar(4) == 24);
> }
>
>
> That code works if I replace lines like:
> case 2: return Foo!x;
>
> With:
> case 2: return Foo!2;
>
> But when the code isn't DRY bugs may happen...
> (There are ten different better ways to write that program, but this is not the point).
>
> Bye,
> bearophile


-- 
Dmitry Olshansky

November 13, 2010
"bearophile" <bearophileHUGS@lycos.com> wrote in message news:ibn320$2ucs$1@digitalmars.com...
> In a not-ranged cases body, like in the program below (that doesn't compile), the switch variable is a compile-time constant, so why doesn't the compile see x as constant there?

In switch statements, you can do stuff like:

switch(x)
{
case 0:
case 1:
   // what is x here?
  break;
}

switch(x)
{
case 0:
   break; // what is x here?
case 1:
   goto case 0:
}

goto label1;
switch(x)
{
case 0:
label1:
  break; // what is x here?
}

switch(x)
{
case 0:
  x = y;
  break; // what is x here?
}

As far as I know, NONE of the constructs in d allow you to treat a run-time
variable as if it was compile-time constant.
I doubt this would be possible without flow analysis.

You can however do something like this (if you must)

template Foo(uint x) {
    static if (x <= 1)
        enum Foo = 1;
    else
        enum Foo = x * Foo!(x - 1);
}

int getv(int x)
{
   switch(x)
   {
      foreach(i; TypeTuple!(0, 1, 2, 3, 4, 5, 6))
      {
         case i: return Foo!i;
      }
   }
   assert(0);
}

where the switch expands out to

   switch(x)
   {
         case 0: return Foo!0;
         case 1: return Foo!1;
         case 2: return Foo!2;
         case 3: return Foo!3;
         case 4: return Foo!4;
         case 5: return Foo!5;
         case 6: return Foo!6;
   }

Is this DRY enough for you?


November 14, 2010
bearophile wrote:
> In a not-ranged cases body, like in the program below (that doesn't compile), the switch variable is a compile-time constant, so why doesn't the compile see x as constant there?
> 
> 
> template Foo(uint x) {
>     static if (x <= 1)
>         enum Foo = 1;
>     else
>         enum Foo = x * Foo!(x - 1);
> }
> 
> int bar(uint x) {
>     switch (x) {
>         case 0: return Foo!x;
>         case 1: return Foo!x;
>         case 2: return Foo!x;
>         case 3: return Foo!x;
>         case 4: return Foo!x;
>         default: return -1;
>     }
> }
> 
> void main() {
>     assert(bar(4) == 24);
> }
> 
> 
> That code works if I replace lines like:
> case 2: return Foo!x;
> 
> With:
> case 2: return Foo!2;
> 
> But when the code isn't DRY bugs may happen...
> (There are ten different better ways to write that program, but this is not the point).
> 
> Bye,
> bearophile

I would say that while bar may be CTFE'd, it is nevertheless a function that can be called at runtime, in which case x may no longer be a compile-time constant. So there is little compiler can do except for refusing such code.
November 14, 2010
Daniel Murphy:

> switch(x)
> {
> case 0:
>    break; // what is x here?
> case 1:
>    goto case 0:
> }
> etc

You are right. Thank you for all the answers.

Bye,
bearophile
November 14, 2010
Hello bearophile,

> In a not-ranged cases body, like in the program below (that doesn't
> compile), the switch variable is a compile-time constant, so why
> doesn't the compile see x as constant there?
> 
> template Foo(uint x) {
> static if (x <= 1)
> enum Foo = 1;
> else
> enum Foo = x * Foo!(x - 1);
> }
> int bar(uint x) {
> switch (x) {
> case 0: return Foo!x;
> case 1: return Foo!x;
> case 2: return Foo!x;
> case 3: return Foo!x;
> case 4: return Foo!x;
> default: return -1;
> }
> }

If you want exactly that:

switch(x) {
 foreach(X; Tuple!(0,1,2,3,4)) {
   case X: return Foo!X;
 }
}