Template constraints should introduce identifiers inside their scopes

Paul Backus snarwin at gmail.com
Wed Sep 21 12:32:38 UTC 2022


On Wednesday, 21 September 2022 at 12:18:19 UTC, Quirin Schroll 
wrote:
> On Wednesday, 21 September 2022 at 11:23:57 UTC, HuskyNator 
> wrote:
>> Consider these semantically identical functions:
>>
>> ```d
>> void foo(M)(M list) {
>> 	static if (is(M : T[L], T, uint L)) {
>> 		pragma(msg, "Length: " ~ L.to!string); // Length: 2
>> 	}
>> }
>>
>> void bar(M)(M list) if (is(M : T[L], T, uint L)) {
>> 	pragma(msg, "Length: " ~ L.to!string); // undefined 
>> identifier 'L'
>> }
>> ```
>> [...]
>>
>> Although semantically the same,
>
> They are not semantically the same. The first can be 
> instantiated with any type and conditionally makes an output 
> (it is empty otherwise), the other says it cannot be 
> instantiated unless the arguments have specific properties.

If you want the semantics to match exactly you can replace the 
`static if` with a `static assert` (or add `else static 
assert(0);`).

> Your suggestion is very much independent of the DbI vs 
> constraints. You want identifiers defined in a constraint to be 
> visible in the body of the template. This is possible in simple 
> cases like yours, but what if the `is` check is nested or 
> negated?

`static if` already handles these cases. I don't know exactly 
what the rules are (needs better documentation), but presumably 
they would work the same way for constraints.

> In your case,
> ```d
> void foo(M : T[L], T, uint L)(M list) { }
> ```
> does the trick.

As I'm sure you're aware, there are cases where the desired 
constraint cannot be expressed using template specializations. 
For example:

```d
auto fun(R)(R r)
     if (isInputRange!R && is(ElementType!R == T[], T))
```


More information about the Digitalmars-d mailing list