It was always just a placeholder, but not a particularly good one: the
expression certainly has a value in most cases, and 'void' can also not
serve as an indicator for an undeduced type because of void{}.
Instead I think the dependent type is a good fit: an initializer list
is an expression that can instantiate to expressions of arbitrary types:
One could say that {...} is a map
T : Type → T, T ↦ T{...}
The type parameter can be explicitly specified or be deduced from an
implicit conversion target type. So one could for example view {1} as
struct InitList1 { template<typename T> operator T() { return T{1}; } };
That in itself isn't type-dependent, but note that we let InitListExprs
have the actual type they initialize in the end, so in this case 'T'
instead of 'InitList1'.
A test failure in CXX/expr/expr.prim/expr.prim.lambda/p4.cpp revealed
that we were emitting errors "return type ... must match previous
return type ... when lambda expression has unspecified explicit return
type" even after diagnosing InitListExprs with "cannot deduce lambda
return type from initializer list", which in this case didn't show up
because it accidentally matched with the actual 'void' of the other
return statements.
If the warning is looking at this in the original template, we're initializing a gsl::owner<T *> which is type-dependent. So we can't carry out the initialization yet.
Got the impression earlier that the lifetime checks want to “understand” templates which can of course not work because templates can be specialized, overload resolution is only performed on the instantiation, and so on.