Sorry in advance for the long answer.
On Saturday, 24 September 2022 at 01:25:17 UTC, Nicholas Wilson wrote:
> Regarding Prior Art, this reminds me a lot of C++'s auto
function parameters that make the function a template function without the need of the syntax overhead that comes with C++.
The key difference here is that C++ template syntax is verbose and auto
is a syntax sugar.
I thought about having functions with enum
parameters be templates implicitly. I decided against that. For one, it could be allowed later if deemed useful, for second, making a function a function template in D requires adding ()
between the name and the parameter list. I.e.
void f(enum int i) { } // error, f is not a template
void g()(enum int i) { } // this is how it’s done
> It also reminds me of SPIR-V specialisation constants, which serve a different purpose of late binding values for the optimiser.
I know nothing about SPIR-V. If you don’t mind, you can explain it to me or send me a link to a description.
> Those are two incompatible meanings, and it is not entirely clear which one the DIP wants to be. I think the best option would be to have a static opSlice be something else and have enum parameters be values that must resolve at compile time.
Do you mean that that they must resolve at compile-time and then be run-time values? That would throw away valuable information with virtually no gain.
> This DIP makes no mention of what one can do with an enum parameter.
It does, three examples are provided: format, regex, static indexing. You can do with them whatever you can do with template value parameters.
> In particular it would be very useful to be able to use CTFE with it, and do static assertions (e.g. check signatures of format strings). and (not that I'm sure there is any easy way to do it) being able to differentiate (and let the compiler know, probably more of a problem for DMD) if the intention is to reduce template bloat (in the case of format) or use the value as a specialisation constant.
That is exactly the specified semantics. I thought about defining the feature in terms or rewrites. Those would make the proposal more understandable, but also constrain the implementation. Also, it would define things to be a certain way that could be depended upon.
Potential rewrite implementation examples for
void f(T)(enum T x, T y) { /+impl+/ }
(A) Append the enum
parameters to the template argument list (cf. C++ auto
parameters).
void f(T, T x)(T y) { /+impl+/ }
// which in turn is actually
template f(T, T x)
{
void f(T y) { /+impl+/ }
}
f(10, 20) // equivalently, 10.f(20)
// is actually
f!(int, 10)(20)
(B) Nested template
template f(T)
{
template f(T x)
{
void f(T y) { /+impl+/ }
}
}
f(10, 20)
// is actually
(f!int)!10(20) // note: syntactically invalid,
// i.e. in code, it must be:
Instantiate!(f!int, 10)(20)
I guess both have their upsides and downsides. I did not investigate this further. Again, it need not be a rewrite at all, and I don’t think it will be, unless someone has a great idea that has no weird semantics.
> > cf. a ref parameter only binds to lvalues
This is not the case, for C++ interop reasons (among others) const ref
can bind rvalues.
I tried on run.dlang.io/nightly, this statement is wrong; const ref
parameters cannot bind rvalues.
You can, however, use in
instead of const ref
. With -preview=in
, in
on parameters of an extern(C++)
function means what const&
means in C++.
Example:
extern(C++) void f(const ref int x) { }
extern(C++) void g(in int x) { }
void main()
{
f(1); // error
g(1); // good
}
DIP 1016 wanted ref
to be able to bind rvalues. It was rejected.