Jump to page: 1 2
Thread overview
Determine if CTFE or RT
Jun 24, 2018
Mr.Bingo
Jun 24, 2018
rjframe
Jun 24, 2018
Stefan Koch
Jun 24, 2018
Mr.Bingo
Jun 24, 2018
arturg
Jun 25, 2018
Mr.Bingo
Jun 25, 2018
Jonathan M Davis
Jun 25, 2018
Mr.Bingo
Jun 25, 2018
Jonathan M Davis
Jun 25, 2018
Mr.Bingo
Jun 25, 2018
Simen Kjærås
Jun 25, 2018
Mr.Bingo
Jun 25, 2018
ag0aep6g
June 24, 2018
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.


June 24, 2018
On Sun, 24 Jun 2018 14:43:09 +0000, 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
}

June 24, 2018
On Sunday, 24 June 2018 at 18:21:09 UTC, rjframe wrote:
> On Sun, 24 Jun 2018 14:43:09 +0000, 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.
June 24, 2018
On Sunday, 24 June 2018 at 18:21:09 UTC, rjframe wrote:
> On Sun, 24 Jun 2018 14:43:09 +0000, 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.
June 24, 2018
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 +0000, 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);
June 25, 2018
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 +0000, 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.





June 24, 2018
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

June 25, 2018
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.



June 25, 2018
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

June 25, 2018
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.
« First   ‹ Prev
1 2