Skip to content

When a generic function instantiation is guided by a type which is demoted away, it shouldn't happen #4243

Open
@eernstg

Description

@eernstg

The language specification says that

Let i be a property extraction expression of the form e?.id,
e.id, or super.id, which is statically resolved to denote an instance
method named id, and let G be the static type of i. Consider the
situation where G is a function type of the form
T0 Function<X1 ◁B1, ..., Xs ◁Bs>(parameters) with s > 0 (that is, G is
a generic function type), and the context type is a non-generic function
type F. In this situation a compile-time error occurs, except when generic
function type instantiation succeeds, that is:

Type inference is applied to G with context type F, and it succeeds,
yielding the actual type argument list T1, ..., Ts.

However, we have a somewhat peculiar behavior in a case where the instantiation fails to produce a function whose type is assignable to the given context type:

List<X> f<X>(X x) => [x];

void main() {
  Object o = (int i) => i;
  if (o is int Function(int)) {
    o = f; // Demotes `o`.
    o.est<E<Object>>(); // Confirms that `o` has static type `Object`.
    print(o.runtimeType); // '(dynamic) => List<dynamic>', i.e., `f` _was_ instantiated.
  }
}

typedef E<X> = X Function(X);

extension<X> on X {
  X est<Y extends E<X>>() => this;
}

It could be claimed that the generic function instantiation that implicitly turns f into f<dynamic> isn't useful (or meaningful), and perhaps it shouldn't have been performed. However, this does match the specified behavior.

We could consider whether we'd want to say that the generic function instantiation should only take place if the resulting function type is assignable to the context type.

This doesn't matter in the typical case because neither the original generic type nor the instantiated non-generic type is assignable to the context type, which means that it is typically a compile-time error, but in the case where the context type is "not mandatory" it does matter, and it will change the behavior of program executions.

We don't have a matching context type and hence the instantiation will most likely use dynamic as the actual type arguments. This may be useful, but most likely it will be less safe, confusing, and not even useful. Hence, I'd recommend that we specify that this generic function instantiation should not be performed after all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions