Determine if CTFE or RT

2018-06-24 Thread Mr.Bingo via Digitalmars-d-learn
let is(CTFE == x) mean that x is a compile time constant. CTFE(x) 
converts a x to this compile time constant. Passing any compile 
time constant essentially turns the variable in to a compile time 
constant(effectively turns it in to a template with template 
parameter)


void foo(size_t i)
{
static if (is(CTFE == i))
{
 pragma(msg, CTFE(i));
} else
{
writeln(i);
}
}

which, when called with a compile time constant acts effectively 
like


void foo(size_t i)()
{
pragma(msg, i);
}

so

foo(1), being a CTFE'able, triggers the pragma, while foo(i) for 
volatile i triggers the writeln.





Re: Determine if CTFE or RT

2018-06-24 Thread rjframe via Digitalmars-d-learn
On Sun, 24 Jun 2018 14:43:09 +, Mr.Bingo wrote:

> let is(CTFE == x) mean that x is a compile time constant. CTFE(x)
> converts a x to this compile time constant. Passing any compile time
> constant essentially turns the variable in to a compile time
> constant(effectively turns it in to a template with template parameter)
> 

You can use __ctfe:

static if (__ctfe) {
// compile-time execution
} else {
// run-time execution
}



Re: Determine if CTFE or RT

2018-06-24 Thread Stefan Koch via Digitalmars-d-learn

On Sunday, 24 June 2018 at 18:21:09 UTC, rjframe wrote:

On Sun, 24 Jun 2018 14:43:09 +, Mr.Bingo wrote:

let is(CTFE == x) mean that x is a compile time constant. 
CTFE(x)
converts a x to this compile time constant. Passing any 
compile time

constant essentially turns the variable in to a compile time
constant(effectively turns it in to a template with template 
parameter)




You can use __ctfe:

static if (__ctfe) {
// compile-time execution
} else {
// run-time execution
}


no that will not work.
it cannot be a static if.
it has to be an if.


Re: Determine if CTFE or RT

2018-06-24 Thread Mr.Bingo via Digitalmars-d-learn

On Sunday, 24 June 2018 at 18:21:09 UTC, rjframe wrote:

On Sun, 24 Jun 2018 14:43:09 +, Mr.Bingo wrote:

let is(CTFE == x) mean that x is a compile time constant. 
CTFE(x)
converts a x to this compile time constant. Passing any 
compile time

constant essentially turns the variable in to a compile time
constant(effectively turns it in to a template with template 
parameter)




You can use __ctfe:

static if (__ctfe) {
// compile-time execution
} else {
// run-time execution
}


This does not work:


import std.stdio;

auto foo(int i)
{
if (__ctfe)
{
return 1;
} else {
return 2;
}
}

void main()
{
writeln(foo(3));
}


should print 1 but prints 2.


Re: Determine if CTFE or RT

2018-06-24 Thread arturg via Digitalmars-d-learn

On Sunday, 24 June 2018 at 19:10:36 UTC, Mr.Bingo wrote:

On Sunday, 24 June 2018 at 18:21:09 UTC, rjframe wrote:

On Sun, 24 Jun 2018 14:43:09 +, Mr.Bingo wrote:

let is(CTFE == x) mean that x is a compile time constant. 
CTFE(x)
converts a x to this compile time constant. Passing any 
compile time

constant essentially turns the variable in to a compile time
constant(effectively turns it in to a template with template 
parameter)




You can use __ctfe:

static if (__ctfe) {
// compile-time execution
} else {
// run-time execution
}


This does not work:


import std.stdio;

auto foo(int i)
{
if (__ctfe)
{
return 1;
} else {
return 2;
}
}

void main()
{
writeln(foo(3));
}


should print 1 but prints 2.


you have to call foo with ctfe
enum n = foo(3);
writeln(n);


Re: Determine if CTFE or RT

2018-06-24 Thread Mr.Bingo via Digitalmars-d-learn

On Sunday, 24 June 2018 at 20:03:19 UTC, arturg wrote:

On Sunday, 24 June 2018 at 19:10:36 UTC, Mr.Bingo wrote:

On Sunday, 24 June 2018 at 18:21:09 UTC, rjframe wrote:

On Sun, 24 Jun 2018 14:43:09 +, Mr.Bingo wrote:

let is(CTFE == x) mean that x is a compile time constant. 
CTFE(x)
converts a x to this compile time constant. Passing any 
compile time

constant essentially turns the variable in to a compile time
constant(effectively turns it in to a template with template 
parameter)




You can use __ctfe:

static if (__ctfe) {
// compile-time execution
} else {
// run-time execution
}


This does not work:


import std.stdio;

auto foo(int i)
{
if (__ctfe)
{
return 1;
} else {
return 2;
}
}

void main()
{
writeln(foo(3));
}


should print 1 but prints 2.


you have to call foo with ctfe
enum n = foo(3);
writeln(n);


This defeats the whole purpose. The whole point is for the 
compiler to automatically compute foo(3) since it can be 
computed. Now, with your code, there is no way to simplify code 
like


foo(3) + foo(8);



auto foo(int i)
{
   if (__ctfe && i == 3)
   {
return 1;
   } else {
return 2;
   }
}


Now, this would precompute foo(3), unbeknownst to the caller of 
foo but then this requires, using your method, to write the code 
like


enum x = foo(3);
x + foo(8);

We would have to know which values foo would return as compile 
time values and what not.


foo(x) + foo(y)

could not work simultaneously for both compile time and run time 
variables.



e.g.,
enum x = 4;
enum y = 4;

foo(x) + foo(y)


would not precompute the values even though x and y are enum's, 
we have to do



enum x = 4;
enum y = 4;
enum xx = foo(x);
enum yy = foo(y);
xx + yy;


and if we changed x or y to a run time variable we'd then have to 
rewrite the expressions since your technique will then fail.


int x = 4;
enum y = 4;
enum xx = foo(x); // Invalid
enum yy = foo(y);
xx + yy;


The compiler should be easily able to figure out that foo(3) can 
be precomputed(ctfe'ed) and do so. It can already do this, as you 
say, by forcing enum on it. Why can't the compiler figure it out 
directly?


The problem here, if you didn't understand, is that one can't get 
foo to be "precomputed" IF it can be done, but IF NOT then it 
will just get the full computation. Because one does not 
necessarily know when and where and how foo will be 
precomputed(or even have completely different behavior for ctfe 
vs rtfe), one can't use two different methods that have the same 
syntax.








Re: Determine if CTFE or RT

2018-06-24 Thread Jonathan M Davis via Digitalmars-d-learn
On Monday, June 25, 2018 05:03:26 Mr.Bingo via Digitalmars-d-learn wrote:
> The compiler should be easily able to figure out that foo(3) can
> be precomputed(ctfe'ed) and do so. It can already do this, as you
> say, by forcing enum on it. Why can't the compiler figure it out
> directly?

The big problem with that is that determining whether the calculation can be
done at compile time or not means solving the halting problem. In general,
the only feasible way to do it would be to attempt it for every function
call and then give up when something didn't work during CTFE, which would
balloon compilation times and likely cause the compiler to run out of memory
on a regular basis given how it currently manages memory and how CTFE tends
to use a lot of memory.

It was decided ages ago that the best approach to the problem was to use
CTFE only when CTFE was actually required. If an expression is used in a
context where its value must be known at compile time, then it's evaluated
at compile time; otherwise, it isn't. The compiler never attempts CTFE as an
optimization or because it thinks that it might be possible to evaluate the
value at compile time. As things stand, it should be pretty trivial to be
able to look at an expression and determine whether it's evaluated at
compile time or not based on how it's used.

If you're looking to have the compiler figure out when to do CTFE based on
the fact that an expression could theoretically be evaluated at compile
time, or because you want the compiler to optimize using CTFE, then you're
going to be disappointed, because that's never how CTFE has worked, and I'd
be _very_ surprised if it ever worked any differently.

- Jonathan M Davis



Re: Determine if CTFE or RT

2018-06-24 Thread Mr.Bingo via Digitalmars-d-learn

On Monday, 25 June 2018 at 05:14:31 UTC, Jonathan M Davis wrote:
On Monday, June 25, 2018 05:03:26 Mr.Bingo via 
Digitalmars-d-learn wrote:
The compiler should be easily able to figure out that foo(3) 
can be precomputed(ctfe'ed) and do so. It can already do this, 
as you say, by forcing enum on it. Why can't the compiler 
figure it out directly?


The big problem with that is that determining whether the 
calculation can be done at compile time or not means solving 
the halting problem. In general, the only feasible way to do it 
would be to attempt it for every function call and then give up 
when something didn't work during CTFE, which would balloon 
compilation times and likely cause the compiler to run out of 
memory on a regular basis given how it currently manages memory 
and how CTFE tends to use a lot of memory.


It was decided ages ago that the best approach to the problem 
was to use CTFE only when CTFE was actually required. If an 
expression is used in a context where its value must be known 
at compile time, then it's evaluated at compile time; 
otherwise, it isn't. The compiler never attempts CTFE as an 
optimization or because it thinks that it might be possible to 
evaluate the value at compile time. As things stand, it should 
be pretty trivial to be able to look at an expression and 
determine whether it's evaluated at compile time or not based 
on how it's used.


If you're looking to have the compiler figure out when to do 
CTFE based on the fact that an expression could theoretically 
be evaluated at compile time, or because you want the compiler 
to optimize using CTFE, then you're going to be disappointed, 
because that's never how CTFE has worked, and I'd be _very_ 
surprised if it ever worked any differently.


- Jonathan M Davis



The docs say that CTFE is used only when explicit, I was under 
the impression that it would attempt to optimize functions if 
they could be computed at compile time. The halting problem has 
nothing to do with this. The ctfe engine already complains when 
one recurses to deep, it is not difficult to have a time out 
function that cancels the computation within some user definable 
time limit... and since fail can simply fall through and use the 
rtfe, it is not a big deal.


The problem then, if D can't arbitrarily use ctfe, means that 
there should be a way to force ctfe optionally!


This means that the compiler will force ctfe if the input values 
are known, just like normal but if they are not known then it 
just treats the call as non-ctfe.


so, instead of

enum x = foo(y); // invalid or valid depending on if y is known 
at CT


we have

cast(enum)foo(y)

which, hypothetically of course, would attempt to precompute 
foo(y) as in the first case but if it is not precomputable then 
just calls it at compile time.


So, the above code becomes

static if (ctfeable(y))
enum x = foo(y);
return x; // compile time version ("precomputed")
else
return foo(y) // runtime version


The semantic above is defined for something like cast(enum)(might 
be confusing but anything could be used that works better).


hence
auto x = cast(enum)foo(y);

will result in x being an enum if y is an enum(since chaining can 
then occur), else a runtime variable with the return of foo 
calculated at runtime.



So, this has nothing to do with the halting problem. I'm not 
asking for the compiler to do the impossible, I'm asking for a 
notation that combines to different syntaxes in to a composite 
pattern so one can benefit from the other. Don't make it harder 
than it seems, it's a pretty easy concept.


It might even be possible to do this in a template:

import std.stdio;


auto IsCTFE()
{
if (__ctfe) return true;
return false;
}

template AutoEnum(alias s)
{
static if (IsCTFE)
{
pragma(msg, "CTFE!");
alias AutoEnum = s;
}
else
{
pragma(msg, "RTFE!");
alias AutoEnum = s;
}
}


double foo(int x) { return 3.42322*x; }

void main()
{
int x = 3;
writeln(AutoEnum!(foo(x)));
}

So, the problem is that if we replace x with 3 the above code is 
executed at compile time(a precomputation).


But the way it is it won't allow it to be computed even at 
runtime! There is no reason that the compiler can't just replace 
the code with `writeln(foo(x));` for RTFE... yet it errors 
*RATHER THAN* default to RTFE!




The simple fix is to allow for the alternative to not try to ctfe 
and just compute the value at runtime.






Re: Determine if CTFE or RT

2018-06-25 Thread Jonathan M Davis via Digitalmars-d-learn
On Monday, June 25, 2018 05:47:30 Mr.Bingo via Digitalmars-d-learn wrote:
> The problem then, if D can't arbitrarily use ctfe, means that
> there should be a way to force ctfe optionally!

If you want to use CTFE, then give an enum the value of the expression you
want calculated. If you want to do it in place, then use a template such as

template ctfe(alias exp)
{
enum ctfe = exp;
}

so that you get stuff like func(ctfe!(foo(42))). I would be extremely
surprised if the compiler is ever changed to just try CTFE just in case it
will work as an optimization. That would make it harder for the programmer
to understand what's going on, and it would balloon compilation times. If
you want to write up a DIP on the topic and argue for rules on how CTFE
could and should function with the compiler deciding to try CTFE on some
basis rather than it only being done when it must be done, then you're free
to do so.

https://github.com/dlang/DIPs

But I expect that you will be sorely disappointed if you ever expect the
compiler to start doing CTFE as an optimization. It's trivial to trigger it
explicitly on your own, and compilation time is valued far too much to waste
it on attempting CTFE when in the vast majority of cases, it's going to
fail. And it's worked quite well thus far to have it work only cases when
it's actually needed - especially with how easy it is to make arbitrary code
run during CTFE simply by doing something like using an enum.

- Jonathan M Davis



Re: Determine if CTFE or RT

2018-06-25 Thread Mr.Bingo via Digitalmars-d-learn

On Monday, 25 June 2018 at 07:02:24 UTC, Jonathan M Davis wrote:
On Monday, June 25, 2018 05:47:30 Mr.Bingo via 
Digitalmars-d-learn wrote:
The problem then, if D can't arbitrarily use ctfe, means that 
there should be a way to force ctfe optionally!


If you want to use CTFE, then give an enum the value of the 
expression you want calculated. If you want to do it in place, 
then use a template such as


template ctfe(alias exp)
{
enum ctfe = exp;
}

so that you get stuff like func(ctfe!(foo(42))). I would be 
extremely surprised if the compiler is ever changed to just try 
CTFE just in case it will work as an optimization. That would 
make it harder for the programmer to understand what's going 
on, and it would balloon compilation times. If you want to 
write up a DIP on the topic and argue for rules on how CTFE 
could and should function with the compiler deciding to try 
CTFE on some basis rather than it only being done when it must 
be done, then you're free to do so.


https://github.com/dlang/DIPs

But I expect that you will be sorely disappointed if you ever 
expect the compiler to start doing CTFE as an optimization. 
It's trivial to trigger it explicitly on your own, and 
compilation time is valued far too much to waste it on 
attempting CTFE when in the vast majority of cases, it's going 
to fail. And it's worked quite well thus far to have it work 
only cases when it's actually needed - especially with how easy 
it is to make arbitrary code run during CTFE simply by doing 
something like using an enum.


- Jonathan M Davis


You still don't get it!

It is not trivial! It is impossible to trigger it! You are 
focused far too much on the optimization side when it is only an 
application that takes advantage of the ability for rtfe to 
become ctfe when told, if it is possible.


I don't know how to make this any simpler, sorry... I guess we'll 
end it here.


Re: Determine if CTFE or RT

2018-06-25 Thread Martin Tschierschke via Digitalmars-d-learn

On Monday, 25 June 2018 at 08:05:53 UTC, Mr.Bingo wrote:

On Monday, 25 June 2018 at 07:02:24 UTC, Jonathan M Davis wrote:
On Monday, June 25, 2018 05:47:30 Mr.Bingo via 
Digitalmars-d-learn wrote:
The problem then, if D can't arbitrarily use ctfe, means that 
there should be a way to force ctfe optionally!


If you want to use CTFE, then give an enum the value of the 
expression you want calculated. If you want to do it in place, 
then use a template such as


template ctfe(alias exp)
{
enum ctfe = exp;
}

so that you get stuff like func(ctfe!(foo(42))). I would be 
extremely surprised if the compiler is ever changed to just 
try CTFE just in case it will work as an optimization. That 
would make it harder for the programmer to understand what's 
going on, and it would balloon compilation times. If you want 
to write up a DIP on the topic and argue for rules on how CTFE 
could and should function with the compiler deciding to try 
CTFE on some basis rather than it only being done when it must 
be done, then you're free to do so.


https://github.com/dlang/DIPs

But I expect that you will be sorely disappointed if you ever 
expect the compiler to start doing CTFE as an optimization. 
It's trivial to trigger it explicitly on your own, and 
compilation time is valued far too much to waste it on 
attempting CTFE when in the vast majority of cases, it's going 
to fail. And it's worked quite well thus far to have it work 
only cases when it's actually needed - especially with how 
easy it is to make arbitrary code run during CTFE simply by 
doing something like using an enum.


- Jonathan M Davis


You still don't get it!

It is not trivial! It is impossible to trigger it! You are 
focused far too much on the optimization side when it is only 
an application that takes advantage of the ability for rtfe to 
become ctfe when told, if it is possible.


I don't know how to make this any simpler, sorry... I guess 
we'll end it here.


I am not sure that I understood it right, but there is a way to 
detect the status of a parameter:


My question was different, but I wished to get a ctRegex! or 
regex used depending on the expression:


 import std.regex:replaceAll,ctRegex,regex;

 auto reg(alias var)(){
   static if (__traits(compiles, {enum ctfeFmt = var;}) ){
// "Promotion" to compile time value
enum ctfeReg =  var ;
pragma(msg, "ctRegex used");
return(ctRegex!ctfeReg);

   }else{
return(regex(var));
pragma(msg,"regex used");
}
   }
}
So now I can always use reg!("") and let the compiler decide.

To speed up compilation I made an additional switch, that when 
using DMD (for development)

alway the runtime version is used.

The trick is to use the alias var in the declaration and check if 
it can be assigned to enum.
The only thing is now, that you now always use the !() compile 
time parameter to call the function. Even, when in the end is 
translated to an runtime call.


reg!("") and not reg("...").







Re: Determine if CTFE or RT

2018-06-25 Thread ag0aep6g via Digitalmars-d-learn

On 06/25/2018 07:47 AM, Mr.Bingo wrote:
The docs say that CTFE is used only when explicit, I was under the 
impression that it would attempt to optimize functions if they could be 
computed at compile time. The halting problem has nothing to do with 
this. The ctfe engine already complains when one recurses to deep, it is 
not difficult to have a time out function that cancels the computation 
within some user definable time limit... and since fail can simply fall 
through and use the rtfe, it is not a big deal.


The problem then, if D can't arbitrarily use ctfe, means that there 
should be a way to force ctfe optionally!


A D compiler is free to precompute whatever it sees fit, as an 
optimization. It's just not called "CTFE" then, and `__ctfe` will be 
false during that kind of precomputation.


For example, let's try compiling this code based on an earlier example 
of yours:



int main()
{
return foo(3) + foo(8);
}
int foo(int i)
{
   return __ctfe && i == 3 ? 1 : 2;
}


`dmd -O -inline` compiles that to:


 <_Dmain>:
   0:   55  push   rbp
   1:   48 8b ecmovrbp,rsp
   4:   b8 04 00 00 00  moveax,0x4
   9:   5d  poprbp
   a:   c3  ret


As expected, `ldc2 -O` is even smarter:


 <_Dmain>:
   0:   b8 04 00 00 00  moveax,0x4
   5:   c3  ret


Both compilers manage to eliminate the calls to `foo`. They have been 
precomputed. `__ctfe` was false, though, because the term "CTFE" only 
covers the forced/guaranteed kind of precomputation, not the optimization.


Re: Determine if CTFE or RT

2018-06-25 Thread Simen Kjærås via Digitalmars-d-learn
On Monday, 25 June 2018 at 09:36:45 UTC, Martin Tschierschke 
wrote:
I am not sure that I understood it right, but there is a way to 
detect the status of a parameter:


My question was different, but I wished to get a ctRegex! or 
regex used depending on the expression:


 import std.regex:replaceAll,ctRegex,regex;

 auto reg(alias var)(){
   static if (__traits(compiles, {enum ctfeFmt = var;}) ){
// "Promotion" to compile time value
enum ctfeReg =  var ;
pragma(msg, "ctRegex used");
return(ctRegex!ctfeReg);

   }else{
return(regex(var));
pragma(msg,"regex used");
}
   }
}
So now I can always use reg!("") and let the compiler 
decide.


To speed up compilation I made an additional switch, that when 
using DMD (for development)

alway the runtime version is used.

The trick is to use the alias var in the declaration and check 
if it can be assigned to enum.
The only thing is now, that you now always use the !() compile 
time parameter to call the function. Even, when in the end is 
translated to an runtime call.


reg!("") and not reg("...").


Now try reg!("prefix" ~ var) or reg!(func(var)). This works in 
some limited cases, but falls apart when you try something more 
involved. It can sorta be coerced into working by passing lambdas:



template ctfe(T...) if (T.length == 1) {
import std.traits : isCallable;
static if (isCallable!(T[0])) {
static if (is(typeof({enum a = T[0]();}))) {
enum ctfe = T[0]();
} else {
alias ctfe = T[0];
}
} else {
static if (is(typeof({enum a = T[0];}))) {
enum ctfe = T[0];
} else {
alias ctfe = T[0];
}
}
}

string fun(string s) { return s; }

unittest {
auto a = ctfe!"a";
string b = "a";
auto c = ctfe!"b";
auto d = ctfe!("a" ~ b); // Error: variable b cannot be read 
at compile time

auto e = ctfe!(() => "a" ~ b);
auto f = ctfe!(fun(b)); // Error: variable b cannot be read 
at compile time

auto g = ctfe!(() => fun(b));
}

--
  Simen


Re: Determine if CTFE or RT

2018-06-25 Thread Mr.Bingo via Digitalmars-d-learn

On Monday, 25 June 2018 at 10:49:26 UTC, Simen Kjærås wrote:
On Monday, 25 June 2018 at 09:36:45 UTC, Martin Tschierschke 
wrote:
I am not sure that I understood it right, but there is a way 
to detect the status of a parameter:


My question was different, but I wished to get a ctRegex! or 
regex used depending on the expression:


 import std.regex:replaceAll,ctRegex,regex;

 auto reg(alias var)(){
   static if (__traits(compiles, {enum ctfeFmt = var;}) ){
// "Promotion" to compile time value
enum ctfeReg =  var ;
pragma(msg, "ctRegex used");
return(ctRegex!ctfeReg);

   }else{
return(regex(var));
pragma(msg,"regex used");
}
   }
}
So now I can always use reg!("") and let the compiler 
decide.


To speed up compilation I made an additional switch, that when 
using DMD (for development)

alway the runtime version is used.

The trick is to use the alias var in the declaration and check 
if it can be assigned to enum.
The only thing is now, that you now always use the !() compile 
time parameter to call the function. Even, when in the end is 
translated to an runtime call.


reg!("") and not reg("...").


Now try reg!("prefix" ~ var) or reg!(func(var)). This works in 
some limited cases, but falls apart when you try something more 
involved. It can sorta be coerced into working by passing 
lambdas:



template ctfe(T...) if (T.length == 1) {
import std.traits : isCallable;
static if (isCallable!(T[0])) {
static if (is(typeof({enum a = T[0]();}))) {
enum ctfe = T[0]();
} else {
alias ctfe = T[0];
}
} else {
static if (is(typeof({enum a = T[0];}))) {
enum ctfe = T[0];
} else {
alias ctfe = T[0];
}
}
}

string fun(string s) { return s; }

unittest {
auto a = ctfe!"a";
string b = "a";
auto c = ctfe!"b";
auto d = ctfe!("a" ~ b); // Error: variable b cannot be 
read at compile time

auto e = ctfe!(() => "a" ~ b);
auto f = ctfe!(fun(b)); // Error: variable b cannot be read 
at compile time

auto g = ctfe!(() => fun(b));
}

--
  Simen


This doesn't work, the delegate only hides the error until you 
call it.


auto also does not detect enum. Ideally it should be a manifest 
constant if precomputed... this allows chaining of optimizations.


auto x = 3;
auto y = foo(x);


the compiler realizes x is an enum int and then it can also 
precompute foo(x).


Since it converts to a runtime type immediately it prevents any 
optimizations and template tricks.