I need to get the product of an arbitrary number of variables. The actual number of variables and their values will be known at compile-time, however I cannot hardcode these because they come from reflection done on types at compile-time, using templates.
I can get the product of these into a BigInt
at runtime just fine, however if I try to do so at compile-time using templates and immutable variables, I can only get the product for a small number of variables before I get a compiler error.
Here is a condensed example that doesn't use type-traits, but suffers from the same issue:
import std.bigint; // BigInt
import std.stdio; // writeln
template Product(ulong value) {
immutable BigInt Product = value;
}
template Product(ulong value, values...) {
immutable BigInt Product = Product!value * Product!values;
}
immutable BigInt NO_PROBLEM = cast(BigInt)ulong.max * ulong.max * ulong.max;
immutable BigInt ERROR = Product!(ulong.max, ulong.max, ulong.max);
void main() {
writeln(NO_PROBLEM, " ", ERROR);
}
Trying to compile this with dmd
compiler gives the error message:
/opt/compiler-explorer/dmd2-nightly/dmd2/linux/bin64/../../src/druntime/import/core/cpuid.d(121): Error: static variable `_dataCaches` cannot be read at compile time
/opt/compiler-explorer/dmd2-nightly/dmd2/linux/bin64/../../src/phobos/std/internal/math/biguintcore.d(200): called from here: `dataCaches()`
/opt/compiler-explorer/dmd2-nightly/dmd2/linux/bin64/../../src/phobos/std/internal/math/biguintcore.d(1547): called from here: `getCacheLimit()`
/opt/compiler-explorer/dmd2-nightly/dmd2/linux/bin64/../../src/phobos/std/internal/math/biguintcore.d(758): called from here: `mulInternal(result, cast(const(uint)[])y.data, cast(const(uint)[])x.data)`
/opt/compiler-explorer/dmd2-nightly/dmd2/linux/bin64/../../src/phobos/std/bigint.d(380): called from here: `mul(this.data, y.data)`
/opt/compiler-explorer/dmd2-nightly/dmd2/linux/bin64/../../src/phobos/std/bigint.d(380): called from here: `this.data.opAssign(mul(this.data, y.data))`
/opt/compiler-explorer/dmd2-nightly/dmd2/linux/bin64/../../src/phobos/std/bigint.d(430): called from here: `r.opOpAssign(y)`
<source>(9): called from here: `Product.opBinary(Product)`
<source>(13): Error: template instance `example.Product!(18446744073709551615LU, 18446744073709551615LU, 18446744073709551615LU)` error instantiating
ASM generation compiler returned: 1
I'm quite puzzled by this. At initial glance, it would appear that too much memory is being requested at compile-time (I would understand if there were less heap available during compile-time execution than at runtime), however I'm not sure this is actually the problem, as I can generate the result at compile-time, just not through the recursive template.
Could it be a bug in the Phobos runtime, or an undocumented limitation?
std.bigint
appears to be designed to be able to produce huge values at compile-time, with lines such as this compiling and executing fine (and bloating the size of the executable!):
immutable BigInt VERY_BIG = BigInt(2) ^^ 10000000;
The error happens on the last line of this function:
It looks like std.bigint is currently not written to work in CTFE in this circumstance. Perhaps simply making the GC.free
call conditional on __ctfe
will fix the problem.
As to why it happens with 10 iterations but not 11, the function has a branch which allows performing the calculation for small numbers without dynamic memory allocation.