Comment #0 by witold.baryluk+d — 2009-07-10T07:32:46Z
/** Computes square root at compile time */
real ctfe_sqrt(real x) {
real root = x / 2.0;
for (int ntries = 0; ntries < 10; ntries++) {
if (root * root - x == 0)
break;
root = (root + x / root) / 2.0;
}
return root;
}
/** Square root of 2 */
static const double sqrt2 = ctfe_sqrt(2.0);
import std.metastrings;
pragma(msg, Format!("s=", cast(int)(1000*sqrt2))); // works
pragma(msg, Format!("s=", cast(int)(1000*sqrt2), " ", cast(int)(1000*sqrt2), " ")); // doesn't work
//pragma(msg, Format!("s=", cast(int)(1000*sqrt2))); // also doesn't work
pragma(msg, Format!("s=", cast(int)((1.0+sqrt2/2.0))); // doesn't works
Looks that only first occurence of value derived from constant derived using ctfs can be used.
It was working in some older releases.
Comment #1 by witold.baryluk+d — 2009-07-10T07:34:37Z
s=1414
/home/baryluk/ProjektyZSempa/D/onpd/onp/../onp/utils/misc.d(187): Error: expression cast(int)(1000 * sqrt2) is not a valid template value argument
/home/baryluk/ProjektyZSempa/D/onpd/onp/../onp/utils/misc.d(187): Error: expression cast(int)(1000 * sqrt2) is not a valid template value argument
/home/baryluk/ProjektyZSempa/D/onpd/onp/../onp/utils/misc.d(186): Error: expression cast(int)(1000 * (1 + sqrt2 / 2)) is not a valid template value argument
...
Comment #2 by witold.baryluk+d — 2009-07-10T07:48:34Z
Created attachment 420
Source code which should compile
Test case
Comment #3 by witold.baryluk+d — 2009-07-10T08:07:07Z
Simpler example
/** Square root of 2 */
static const double sqrt2 = ctfe_sqrt(2.0);
template X(E ...) { alias E X; }
//alias X!(sqrt2/1.0, sqrt2/2.0) X2; // doesn't work
//alias X!(sqrt2*sqrt2) X3; // doesn't work
static const double sqrt22 = sqrt2*sqrt2;
//alias X!(sqrt22) X4; // doesn't work
Comment #4 by witold.baryluk+d — 2009-07-10T08:15:21Z
Additionally if i omit "double" in "static const double", it compiles! It is the same type!
I was also testing this ctfe function
T i(T)(T x) {
return x;
}
and only with T=float,double,real i got errors.
Comment #5 by clugdbug — 2009-07-13T00:09:29Z
Reduced test case shows it doesn't require CTFE:
-----
enum float one = 1 * 1;
template X(float E) { int X = 2; }
alias X!(one * one) X3;
----
This is really complicated, and took me ages to track down.
First problem is in declaration.c, near the end of VarDeclaration::semantic().
It calculates the value of the manifest constant, but discards the result unless
it was an int or a string! That's a waste, since it was successfully calculated.
In optimize.c expandVar() copys the exprInitializer and semantically analyzes it, but it doesn't change the exprInitializer. So ei->type remains null the next time expandVar is called for that same variable.
And since expandVar sets v->scope to NULL, it never gets another chance to evaluate it.
So in optimize.c line 92, it bypasses the call to e = ei->copy(), returning NULL from the function.
--> it cannot be evaluated at compile time.
The final patch is tiny: add floating point variables to the list of values which get saved.
// line 1150 declaration.c || e->op==TOKfloat64
e = e->optimize(WANTvalue | WANTinterpret);
else
e = e->optimize(WANTvalue);
- if (e->op == TOKint64 || e->op == TOKstring)
+ if (e->op == TOKint64 || e->op == TOKstring || e->op==TOKfloat64)
{
ei->exp = e; // no errors, keep result
}
However, I think the logic in expandVar() is probably also wrong.
The bug can also be fixed by adding this line in optimize.c, line 89:
e = e->semantic(v->scope);
e = e->implicitCastTo(v->scope, v->type);
+ ei->type = e->type;
v->scope = NULL;