Skip to content

Use bottom-up type inference for invocations of generic functions with non-covariant parameter types #3212

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

Open
eernstg opened this issue Jul 11, 2023 · 0 comments
Labels
small-feature A small feature which is relatively cheap to implement. type-inference Type inference, issues or improvements

Comments

@eernstg
Copy link
Member

eernstg commented Jul 11, 2023

See dart-lang/sdk#52850 for background information, including a detailed analysis from @stereotype441.

Consider the following example:

abstract class A {}

class C<Y> {}

List<U> Function<U>(U Function(U)) f = <U>(c) => <U>[];

List<Object?> g<W>(W Function(W) d) => f/*<W>*/(d);

This library causes a compile-time error to be reported at f(d), but there is no error if we use f<W>(d).

The context type List<Object?> is used during downwards inference to choose the type argument passed to f, so we make it f<Object?>(d) before we even consider whether that choice of type argument makes d a type correct actual argument.

This normally fine: The context type is usually an upper bound (so if we can't use an actual type argument A which is taken from the context type, or a subtype of A, then the entire expression is a type error). Conversely, if we could have used a subtype of the type from the context then it is usually no problem that we use a subtype.

The reason for this is that most parameter types are covariant in the type parameters of the enclosing generic function. In other words, if f<T>(d) is type correct and T <: S then f<S>(d) is also type correct. Based on the context type, we'd choose the most general type which is allowed by the context, which is again the type that allows for the greatest possible set of types for the actual arguments.

However, in this particular case we can't use a supertype, because the type parameter U occurs both covariantly and contravariantly in the parameter type of f. So when we choose Object? as the actual type argument passed to f in the body of g, the corresponding instantiated parameter type of f is Object? Function(Object?), and that's unrelated to the type of d which is W Function(W), and hence we get a type error (or a type inference failure).

I think we might want to use the current approach (based on the context type whenever possible) in all cases where we are inferring actual type arguments for a generic function invocation where the parameter types are covariant in all type parameters, but in the case where some parameter types are non-covariant in some type parameters we could use bottom-up inference.

@eernstg eernstg added inference small-feature A small feature which is relatively cheap to implement. type-inference Type inference, issues or improvements labels Jul 11, 2023
@lrhn lrhn removed the inference label Apr 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
small-feature A small feature which is relatively cheap to implement. type-inference Type inference, issues or improvements
Projects
None yet
Development

No branches or pull requests

2 participants