Template constraints should introduce identifiers inside their scopes

HuskyNator HuskyNator at protonmail.ch
Wed Sep 21 11:23:57 UTC 2022


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'
}

void main(string[] args) {
	int[2] list = [1, 2];
	foo(list);
	bar(list);
}
```

Although semantically the same, `bar` will fail.
This can of course be solved by changing the template arguments, 
but could lead to issues in more complex scenarios:

```d
alias UnsignedType(T) = mixin(getUnsignedType!(T));

string getUnsignedType(T)() {
	static if (is(T == byte))
		return "ubyte";
	else static if (is(T == short))
		return "ushort";
	else static if (is(T == int))
		return "uint";
	else
		assert(0, "Type not supported: " ~ T.stringof);
}

auto foo(T)(T number) if (is(UnsignedType!T T2)) {
	alias T2 = UnsignedType!T; // still required
	return cast(T2) number;
}

void main(string[] args) {
	foo(1).writeln; // 1
	foo(-1).writeln; // 4294967295
}
```

Apart from these examples, depending on complexity, one might 
even think of a scenario in which a type returned inside the 
template condition should be matched against, in which case the 
body of the function would be required to contain a duplicate 
statement:
`is(aliasMixin!M M2: T[L], T, uint L);` (eg. when getUnsignedType 
returns a more complex type)

---

Given the above consideration, assuming this is not a bug (tested 
on both dmd & ldc), I believe identifiers introduced inside a 
template constraint should be visible inside the scope of the 
template.


More information about the Digitalmars-d mailing list