Tuples, CTFE, and Sliding Template Arguments

Timon Gehr timon.gehr at gmx.ch
Fri Jan 12 23:04:58 UTC 2024


On 1/12/24 23:35, Walter Bright wrote:
> Given the interest in CTFE of istrings, I have been thinking about 
> making a general use case out of it instead of one specific to istrings.
> ---------------------
> 
> Sliding Template Arguments
> 
> Consider the following template:
> 
> ```
> void pluto(string s)()
> {
>      pragma(msg, s);
> }
> 
> void test()
> {
>      pluto!"hello"();
> }
> ```
> 
> This compiles because `s` is a compile time argument, and `pragma(msg, s)`
> expects s to be a compile time value.
> 
> ```
> void pluto()(string s)
> {
>      pragma(msg, s);
> }
> 
> void test()
> {
>      pluto("hello");
> }
> ```
> 
> This fails to compile because `s` is a runtime argument that is not 
> accessible
> at compile time, even if it is inlined. (Inlining doesn't help here 
> because inlining is done after CTFE and semantic analysis.)
> 
> Those examples illustrate the difference between a compile time and a 
> runtime argument.
> 
> For background, to generate a tuple of elements:
> 
> ```
> alias AliasSeq(T...) = T;
> ```
> and a function argument list that accepts a tuple:
> ```
> void func(Args...)(Args args)
> {
> }
> ```
> But notice that `args` are runtime arguments. It turns out there is no way
> to use tuples to split an argument tuple into compile time and runtime 
> tuples:
> 
> ```
> void pluto(Args...)(Args args)
> {
>      exec!(args[0])(args[1 .. args.length]);
> }
> ```
> This is the problem that DIP1036e ran into. It's clever solution is to 
> have the compiler (because it cannot be done with metaprogramming) take 
> the first argument, and use it as a compile time argument to a templated 
> dummy value. Then, the type of that argument is a template with the 
> value encoded into the type, which can be programmatically extracted and 
> then processed at compile time.
> 
> The awkwardness of this is it only happens with istrings, it is not a 
> general purpose capability, plus the insertion of dummy arguments into 
> the argument list just so their types can be extracted.
> 
> Hence, the genesis of this proposal which describes a language 
> capability to create a compile time parameter from a tuple of runtime 
> expressions.
> 
> For lack of a better term, I'll call it "Sliding Template Arguments".
> 
> Consider a template function:
> 
> ```
> void pluto(string s, Args...)(Args args)
> {
>      pragma(msg, s);
> }
> 
> void exec()
> {
>      pluto!"hello"(1,2,3);
> }
> ```
> which works now. But this fails to compile:
> ```
> pluto("hello",1,2,3);
> ```
> because there is no argument for `s`.
> 
> So, instead of issuing a compilation error, the compiler can "slide" the 
> arguments to the left, so the first argument is moved into the compile 
> time parameter list. Then, the call will compile.
> 
> The rule would be something like:
> 
> 1. the function is a template taking a variadic runtime argument list
> 2. the compile time parameters are a sequence of N value parameters, 
> followed by
> the variadic type parameter.
> 3. the value parameters do not have default values
> 4. no compile time arguments are provided in the template invocation
> 5. the leftmost N runtime arguments are matched against the compile time 
> parameters,
> and removed from the runtime argument list
> 6. if they match, then the template instantiation is rewritten to 
> reflect this
> 7. compilation then proceeds normally
> 
> Template Sliding thus becomes a general use facility. One interesting 
> consequence of this is it opens up a whole new class of functions that 
> can now do CTFE computations on the leftmost arguments.

Generally I think this is a good idea, but the proposed syntax is a bit 
too specialized, arbitrarily restricted and the behavior may be 
unexpected and should be opt-in instead.

Maybe we can do it like this instead:

```d
void pluto(string s, Args...)(enum string x = s, Args args){

}
```

I.e., you can use `enum` in a function argument list and you have to 
default-initialize them. This means that this parameter always needs to 
have exactly that value.

Then, the arguments matched to an `enum` parameter are evaluated at 
compile time and matched against the initializer.


More information about the Digitalmars-d mailing list