Description
The constructor tear-off specifiction defines the behavior of a tear-off as "equivalent to" tearing off a corresponding static function, which has the same type parameters as the class, the same parameter list as the constructor, and a return type which is the class-type instantiated with those type arguments.
That definition doesn't work for all redirecting factory constructors with optional parameters, because those cannot have default values, which makes the corresponding static function also not have default values, even if the optional parameter's type is non-nullable. That is, the "corresponding static function" is not a valid Dart function.
(And this is, again, why we shouldn't define things in terms of desugaring.)
The current approach cannot be saved by saying that we should use the default value of the (transitive) redirect target's parameter, which must also be optional and theregore surely have one. That parameter can have a wider type, and a default value which isn't valid for the redirecting factory's parameter.
Example:
class C {
factory C([int x]) = D;
}
class D implements C {
C([int? x]) { assert(x == null); }
}
void main() {
C Function([int]) f = C.new;
assert(f.runtimeType == typeof<C Function([int])>);
f();
}
typedef typeof<T> = T;
There is no valid desugaring which can introduce a Dart function for C.new
which behaves like calling C.new
should
(that is, calling that function works just like calling C.new
with the same arguments would).
I suggest we rewrite the places in the specification where we introduce a wrapper which copies default values, into forwarding argument lists.
Constructor tear-off:
The result of tearing off a constructor D.C of a class declaration D with type parameters TP and parameter list P
is a function value with signatureD<TP> Function(P)
.
When that function is invoked with type arguments TS and argument list A, it returns the result of invoking the constructor D.C on the instantiated type D<TS> with argument list A.The result of tearing off the same constructor from an instantiated type, tearing off D<TS>.C, is a function with
signatureD<TS> Function(P')
where P' is P with type arguments TS substituted for type parameters TP.
Invoking that function with argument list A returns the result of invoking D<TS>.C with argument list A.
(or something to that effect.)
Basically accept that not all function signatures have default values, even where user-declared functions must have them.
The same principle should be applied to generic function instantiation, implicit or explicit. It introduces a new function with a signature which has no type arguments and where the parameter and return types replaces the type parameters with the instantiating type arguments. Calling that function invokes the original function with those type arguments and the same argument list. Again, without any attempt to copy default values, because there are functions which do not have default values - even if it's only redirecting factory constructor tear-offs, for now.