Frist Draft (in this forum): Enum Parameters
Quirin Schroll
qs.il.paperinik at gmail.com
Mon Apr 29 16:08:03 UTC 2024
On Monday, 29 April 2024 at 13:39:57 UTC, Timon Gehr wrote:
> On 4/25/24 19:56, Quirin Schroll wrote:
>> https://github.com/Bolpat/DIPs/blob/2bfef0b05e99c5448b416078da2e743482e3193d/DIPs/1NNN-QFS.md
>>
>> This supersedes my idea of static indexing. The static
>> indexing DIP draft even said that if some way to pass values
>> as compile-time constants to functions, that would supersede
>> it.
>>
>> **Abstract**
>> On function templates, allow `enum` to be used as a function
>> parameter storage class and a member function attribute.
>> Arguments binding to `enum` parameters must be compile-time
>> constants, as if template value parameters. With `auto enum`,
>> “compile-time-ness” is determined from argument (cf. `auto
>> ref`) and queried via a trait.
>>
>
> Nice, thanks! However, I think the technical part will need
> some more elaboration.
>
>> if candidates contain enum parameters, constant folding must
>> be attempted for them, i.e. a candidate can only be excluded
>> when an enum parameter is bound to an argument for which
>> constant folding failed.
>
> I am not sure what you mean by constant folding. Do you mean
> CTFE has to be attempted? What are possible reasons for it to
> fail? E.g., if it throws an exception, will it match a runtime
> parameter instead?
TL;DR: I thought constant-folding was the general term of
evaluating stuff at compile-time and CTFE means executing a
function at compile-time (as part of the constant-folding
process).
Constant folding is (technically, AFAIK) optional when binding to
a run-time parameter: In the call `f(sqrt(2))`, the compiler may
choose to CTFE `sqrt(2)` as part of optimization and call `f` on
the literal value; or it issue a call to `sqrt` at runtime.
An example for what my sentence means:
```d
void f(enum int x) { } // A
void f(long y) { } // B
```
The call `f(userInput)` requires overload resolution; that
overload resolution can exclude overload `A` because it requires
a compile-time value, but `userInput` is a run-time value, so
only `B` remains. On the other hand, `f(10L + ctfeMystery())`
will – due to D’s shenanigans – possibly `A` because `10L +
ctfeMystery()` can be evaluated at compile-time and the value,
albeit being typed `long` might fit into an `int` and therefore
an `int` overload is preferred.
>> In the function body (including contracts and constraints), an
>> enum parameter’s value is a compile-time constant as if it
>> were template value parameter.
>
> But it is not, so probably you have to specify some sort of
> lowering and/or name mangling strategy.
I haven’t thought about mangling much as I thought that it will
be possible somehow as the DIP adds a brand new concept. I know
next to nothing about mangling, basically only what it means:
Encoding the type of a function into a plain name.
> Also, is there a way to manually instantiate the template?
In my idea, the function is required to be a template for similar
reasons a function is required to be a template for `auto ref`:
It may generate different implementations under the same name. A
plain function can’t do that. As with a function template with
empty parameters (think `f()(auto ref T value)`), even if through
the single choice in template parameters, not all instantiation
are the same. So, as with `auto ref`, you could instantiate it,
but values for `enum` parameters are passed by function call
syntax.
In the background, they could be lowered and mangled like
template parameters.
> The idea I had (that I am not yet fully satisfied with) to
> bypass all of this was to require specific values for `enum`
> parameters. Then you'd do:
>
> ```d
> auto opSlice(size_t l, size_t u)(enum : l, enum : u) =>
> slice!(l, u);
> ```
>
> This way, you can manually instantiate the templates if you
> need to, and you also do not add a new category of symbol that
> requires updating the way D mangles names.
The way I’d view/frame your idea:
An enum parameter allows you to infer a template value parameter
form the value you pass as a function argument.
Probably spelling out the template parameter should be optional,
so that you _can_ control where in the template parameter list it
appears if you need to (probably so that manually passing
template parameters is possible), but if you just want to have
enum parameters, the language adds the needed template value
parameters at the end of the template parameter list. There’s one
caveat, though: For an `auto enum`, you can’t spell it out in a
template value parameter on the template – the parameter simply
need not be there.
I don’t know what to do about the call syntax, though. Probably
`enum` parameters should be skipped in the function parameter
part. Should one be allowed to partially pass template and enum
parameters? Probably not.
Consider:
```d
struct X
{
auto f()(auto enum size_t i)
{
static if (__traits(isEnum, i) { ... } else { ... }
}
}
X x;
size_t i;
```
Then, `x.f(0)` lowers to `x.f!0()`, but `x.f(i)` lowers to
`x.f!()(i)`, and the latter does not have any template value
parameters.
I’m considering this, it solves a problem that I intentionally
didn’t address.
More information about the dip.development
mailing list