New blog post on the cost of compile time
Steven Schveighoffer
schveiguy at gmail.com
Sun Feb 19 18:08:28 UTC 2023
On 2/18/23 2:25 PM, Nick Treleaven wrote:
> On Saturday, 18 February 2023 at 17:16:18 UTC, Steven Schveighoffer wrote:
>> This is how isInputRange used to look. The reason it was changed is
>> because the compiler now "sees" the different clauses separated by &&,
>> and will tell you which one failed. When you wrap it like this, it
>> just sees one big constraint.
>
> Makes sense, although I don't get that on my machine with dmd v2.101.0:
> ```d
> import std.range;
> int f(R)() if (isInputRange!R) => 2;
> int i = f!int;
> ```
> ```
> isinputrange.d(68): Error: template instance `isinputrange.f!int` does
> not match template declaration `f(R)()`
> with `R = int`
> must satisfy the following constraint:
> ` isInputRange!R`
> ```
> That's it, nothing about why isInputRange failed. Maybe I'm doing
> something wrong.
uhh... I think I am wrong on the reasoning then. I thought it would keep
going down into the constraint, but it doesn't? Maybe it was planned? I
do know that && in the constraint itself does do that. e.g.:
```d
void foo(R)(R r) if (isInputRange!R && !is(R == int[])) {}
void main()
{
foo(1);
foo([1]);
}
```
```
onlineapp.d(8): Error: none of the overloads of template `onlineapp.foo`
are callable using argument types `!()(int)`
onlineapp.d(2): Candidate is: `foo(R)(R r)`
with `R = int`
must satisfy the following constraint:
` isInputRange!R`
onlineapp.d(9): Error: none of the overloads of template `onlineapp.foo`
are callable using argument types `!()(int[])`
onlineapp.d(2): Candidate is: `foo(R)(R r)`
with `R = int[]`
must satisfy the following constraint:
` !is(R == int[])`
```
and I also do know that the original `isInputRange` code was like you
proposed -- one lambda with all the things.
> Anyway, assuming dmd can use that I wonder if this would work (aside
> from the inout issue):
> ```d
> template isInputRange(R) {
> extern R r; // dummy
> enum isInputRange =
> is(typeof(R.init) == R) &&
> is(typeof({ return r.empty; }()) == bool) &&
> (is(typeof(() return => r.front)) ||
> is(typeof(ref () return => r.front))) &&
> !is(typeof({ return r.front; }()) == void) &&
> is(typeof({ r.popFront; }));
> }
> ```
> dmd could still see through the eponymous template to the expression, in
> theory.
I don't know if that works, would it fail to link? It's an interesting idea.
>
>> I actually did get a PR merged. It wasn't as simple as I had written
>> in that blog post, due to the stupid `inout` requirement that `inout`
>> data can only be used inside a function with an `inout` parameter.
>
> OK, so `typeof((R r) { return r.empty; } (R.init))` works with inout.
Yes, that's what I found that worked. You need the lambda parameter to
mimic the mutability of the type. Which kind of sucks, but it's what we
have right now.
`lvalueOf!R` would work too, I just figured it isn't needed.
I wonder what the cost of using `T.init` is vs. a cached lookup of a
template. It's probably pretty small, but these are the kinds of things
that are unintuitive.
>
> Thanks for the blog post BTW.
You're welcome! I write blog posts too infrequently, but I do have a
bunch of half-started ones. I'm working on another right now...
>> I did start with using `lvalueOf!R`, but then realized that since
>> `isInputRange` validates that `typeof(R.init) == R`, I just used
>> `R.init` as the parameter.
>
> Is that validation to detect types with redefined `init`? I didn't
> realize Phobos needs to care about that.
I believe so. I'm not 100% sure why we are checking for redefined init
here, but apparently it's needed somewhere inside phobos.
-Steve
More information about the Digitalmars-d
mailing list