Some of these are not like the others:
unittest {
auto fn1(U)() { auto u = U.init; return u; }
alias fn2 = function (t) => t;
alias fn3 = delegate (t) => t;
auto fn4(U)(U u) { return u; }
alias T = const(int[]);
alias T1 = typeof((function (t) {return t;})(T.init));
alias T2 = typeof((delegate (t) {return t;})(T.init));
alias T3 = typeof((t => t)(T.init));
alias T4 = typeof(() { auto t = T.init; return t; }());
alias T5 = typeof(fn1!T());
alias T6 = typeof(fn2(T.init));
alias T7 = typeof(fn3(T.init));
alias T8 = typeof(fn4(T.init));
pragma(msg, T1); // const(int[]) // Const array
pragma(msg, T2); // const(int[])
pragma(msg, T3); // const(int[])
pragma(msg, T4); // const(int[])
pragma(msg, T5); // const(int[])
pragma(msg, T6); // const(int)[] // Mutable array of const elements
pragma(msg, T7); // const(int)[]
pragma(msg, T8); // const(int)[]
}
The exact same behavior can be observed by changing the type of T to const(int*).
It seems to me the behavior in T6-T8 is most useful - consider:
unittest {
import std.range : isInputRange;
auto fn1(U)(U u) { return isInputRange!U; }
alias fn2 = t => isInputRange!(typeof(t));
alias fn3 = function (t) => isInputRange!(typeof(t));
alias fn4 = delegate (t) => isInputRange!(typeof(t));
alias T = const(int[]);
assert(fn1(T.init));
assert(fn2(T.init));
assert(fn3(T.init));
assert(fn4(T.init));
assert(!((t) => isInputRange!(typeof(t)))(T.init));
assert(!(function (t){return isInputRange!(typeof(t));})(T.init));
assert(!(delegate (t){return isInputRange!(typeof(t));})(T.init));
}
It is certainly unexpected that t is an input range only in some cases.
Comment #1 by issues.dlang — 2019-08-04T15:28:20Z
When a template is implicitly instantiated with a dynamic array, the type of the slice of that dynamic array is used (which is tail-const if the dynamic array is const or tail-immutable if it's immutable). It does that so that you don't have to explicitly slice dynamic arrays all over the place when using templated code (though unfortunately, it does mean that dynamic arrays are treated a bit special, since nothing like this happens with other types). Explicitly instantiating a template with a dynamic array uses the exact type, because you're providing the exact type rather than using type inference.
What I don't know is why lambda stuff ends up inferring the exact type of the dynamic array in some cases and the type you get when slicing it in others.
Comment #2 by robert.schadek — 2024-12-13T18:56:12Z