Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
eernstg opened this issue Jan 31, 2025 · 2 comments
Labels
question Further information is requested specification

Comments

@eernstg
Copy link
Member

eernstg commented Jan 31, 2025

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.

@eernstg eernstg added question Further information is requested specification labels Jan 31, 2025
@munificent
Copy link
Member

Wow, what a weird corner of the language.

I'm a little confused. I wouldn't expect there to be an implicit generic function instantiation because shouldn't inference fail on the instantiation?

@eernstg
Copy link
Member Author

eernstg commented Feb 7, 2025

Fun stuff indeed! 😄

The new rule is specifically about the case where the inferred generic function instantiation didn't yield an expression whose type is assignable to the context type, but this is actually not an error in the situation where the context type is optional (in particular, in an assignment to a promoted variable).

So we're performing this generic function instantiation (turning f into f<dynamic> in the example), and this transformation doesn't help (because neither the type of f nor the type of f<dynamic> is assignable to the context type). I'm just thinking that we're better off if we simply don't perform a transformation like this, because it is subtle and doesn't help anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested specification
Projects
None yet
Development

No branches or pull requests

2 participants