Jump to page: 1 2 3
Thread overview
April 25

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.

April 26
Enum as a keyword has quite different meaning, I suggest ``__ctfe`` instead as it already has a similar meaning.

I looked for discussion to its interaction with string interpolation, and there is none.

Currently I see no benefit from requiring a known at compile time expression to be passed to a parameter.

If however it resulted in that function parameter being treated as a template parameter that would be different. But I don't see anything that suggests this.
April 25

On Thursday, 25 April 2024 at 20:29:27 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

[…]

If however it resulted in that function parameter being treated as a template parameter that would be different. But I don't see anything that suggests this.

But that is exactly what I propose.

Quoting from the draft:

>

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.
The same is true for this in an enum non-static member function body.
The main difference between enum parameters and template value parameters is only in calling syntax:
f!ct_value(args) versus f(ct_value, args).
Another difference is that the type of an enum parameter can be inferred, while a template value parameter must have its type specified.

It would allow to define opIndex as

struct Tpl(Ts...)
{
    Ts[i] opIndex(enum size_t i) { .. }
    Tpl!(Ts[l..u]) opSlice(enum size_t l, enum size_t u) { .. }
}

So that

Tpl!(int, string, Object) t;
t[0] // lowers to t.opIndex(0) of type int
t[1] // lowers to t.opIndex(1) of type string
t[2] // lowers to t.opIndex(2) of type Object
t[3] // lowers to t.opIndex(3), a compile error as (int,string,Object)[3] fails
t[0..2] // is of type Tpl!(int, string)

You could have opBinary that only accepts certain constants to add.
Or opEquals that only allows for comparison with 0.
regex could simply “notice” that the pattern is a compile-time constant and be equivalent to ctRegex.
format can check and/or optimize if the pattern is a compile-time constant.

Maybe with interpolated strings, there’s a lot possible. I don’t know. If you find some application, please let me know.

April 26

On Thursday, 25 April 2024 at 17:56:59 UTC, 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.

I would love to see this happen. This is like a streamlining of template value parameters, akin to how lambdas streamline template type parameters.

I have thoughts on reworking interpolation tuples if this got in...

-Steve

April 26

On Friday, 26 April 2024 at 02:47:18 UTC, Steven Schveighoffer wrote:

>

I have thoughts on reworking interpolation tuples if this got in...

Please, elaborate. I was somewhat absent from the forums while string interpolation took off. I read the docs, but it seems a lot of regulars like you have a lot more intuition about where interpolation tuples are useful. Not only could I use those ideas in the DIP, I’m honestly intrigued because I can’t imagine how they meaningfully interact.

April 28
On 26/04/2024 11:29 AM, Quirin Schroll wrote:
> On Thursday, 25 April 2024 at 20:29:27 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> […]
>>
>> If however it resulted in that function parameter being treated as a template parameter that would be different. But I don't see anything that suggests this.
> 
> But that is exactly what I propose.
> 
> Quoting from the draft:
> 
>> 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.
>> The same is true for `this` in an `enum` non-`static` member function body.
>> The main difference between `enum` parameters and template value parameters is only in calling syntax:
>> `f!ct_value(args)` versus `f(ct_value, args)`.
>> Another difference is that the type of an `enum` parameter can be inferred, while a template value parameter must have its type specified.

It wasn't jumping out at me, so this could do with some reworking.

April 28
On 26/04/2024 9:39 PM, Quirin Schroll wrote:
> On Friday, 26 April 2024 at 02:47:18 UTC, Steven Schveighoffer wrote:
>> I have thoughts on reworking interpolation tuples if this got in...
> 
> Please, elaborate. I was somewhat absent from the forums while string interpolation took off. I read the docs, but it seems a lot of regulars like you have a lot more intuition about where interpolation tuples are useful. Not only could I use those ideas in the DIP, I’m honestly intrigued because I can’t imagine how they meaningfully interact.

String interpolation slices and dices the string up into metadata arguments and the actual expressions specified by the user.

The meta data arguments should be elevated to template parameters, but the expressions must remain runtime.

What makes this tricky is given a sequence of expressions an interleaved sub sequence (that may not be odd/even), needs to be elevated but the others shouldn't be.

If we can elevate the metadata expressions to templates we can validate types and formatting during compilation.
April 29
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?

> 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. Also, is there a way to manually instantiate the template?

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);
```

(Maybe there is better syntax. Mine is inspired from template specializations.)

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.


April 29

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:

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:

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:

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.

April 29
On 4/29/24 18:08, Quirin Schroll wrote:
> 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

The (logistical) issue with this is that e.g., the following is disallowed:

```d
void foo(T...,int x)(){}
```

This gets back to the issue of name mangling, because I think there is no way to distinguish an instantiation where the last template parameter goes to `T...` and an instantiation where the last template parameter goes to `x`.

Of course, this introduces many other issues unrelated to `enum` parameters, e.g., I think you cannot have a (non-nested) template accepting multiple delegate types with different but variadic parameter lists.

So in general, some thought will have to be put into how to support IFTI.
« First   ‹ Prev
1 2 3