Consider these semantically identical functions:
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:
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.