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

Object is accepted as FutureOr<T> which results in runtime TypeError #42340

Closed
cubuspl42 opened this issue Jun 15, 2020 · 2 comments
Closed

Object is accepted as FutureOr<T> which results in runtime TypeError #42340

cubuspl42 opened this issue Jun 15, 2020 · 2 comments
Labels
closed-as-intended Closed as the reported issue is expected behavior type-question A question about expected behavior or functionality

Comments

@cubuspl42
Copy link

import 'dart:async';

void complete_<T>(Completer<T> completer, T value) {
  print(T); // prints "Object"
  completer.complete(value);
}

void main(List<String> arguments) {
  final completer = Completer<int>();

  // completer.complete("foo");
  // Compilation error:
  // error: The argument type 'String' can't be assigned to the parameter type 'FutureOr<int>'. (argument_type_not_assignable at [HelloWorldDart] bin/main.dart:11)

  complete_(completer, 'foo');
  // Compilation success (no issues)
  // Runtime error:
  // type 'String' is not a subtype of type 'FutureOr<int>' of 'value'
}
▶ dart --version
Dart VM version: 2.8.4 (stable) (Wed Jun 3 12:26:04 2020 +0200) on "macos_x64"

REPL

@cubuspl42
Copy link
Author

cubuspl42 commented Jun 15, 2020

While I believed that this issue is related with incorrect handling of FutureOr, I was pointed out that this is same for other (more "typical") generics like List:

void add_<T>(List<T> list, T value) {
  list.add(value);
}

void main(List<String> arguments) {
  final List<int> list = [];

  // list.add("foo");
  // Compilation error:
  // error: The argument type 'String' can't be assigned to the parameter type 'int'. (argument_type_not_assignable at [HelloWorldDart] bin/main.dart:9)

  add_(list, 'foo');
  // Compilation success (no issues)
  // Runtime error:
  //  type 'String' is not a subtype of type 'int' of 'value'
}

@eernstg
Copy link
Member

eernstg commented Jun 15, 2020

This is actually working as specified, and the dynamic error arises because completer is given the type Completer<Object> when the (inferred or specified) type argument to complete_ is Object, and then the invocation of completer.complete(value) fails at run-time: The statically known argument type is Object, but that parameter is "covariant by class" so it will be subject to a dynamic check, which fails.

So the reason why there is a dynamic error and not a compile-time error is the unsound covariance that is used for the type arguments of generic classes. Dart used to be much more dynamic in nature than it is today, and it is still developing in the direction of replacing dynamic checks by static checks.

In particular, this kind of issue can be eliminated (such that the check will be at compile-time) using sound variance: dart-lang/language#753, dart-lang/language#524.

It might be possible to change the Completer class itself (and use declaration-site variance), in which case the type inference on the invocation complete_ would fail.

Otherwise, we could use use-site variance/invariance and make the invocation of complete_ fail when unsafe, even though the Completer class would still use unsound covariance:

void complete_<T>(Completer<exactly T> completer, T value) {
  print(T); // prints "Object"
  completer.complete(value);
}

void main(List<String> arguments) {
  final completer = Completer<int>();
  complete_(completer, 'foo'); // Type inference fails.
  complete_<int>(completer, 'foo'); // Error on passing 'foo'.
  complete_<String>(completer, 'foo'); // Error on passing 'completer'.
  complete_<Object>(completer, 'foo'); // Error on passing 'completer', too.
}

@mraleph mraleph added type-question A question about expected behavior or functionality closed-as-intended Closed as the reported issue is expected behavior labels Jun 16, 2020
@mraleph mraleph closed this as completed Jun 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-as-intended Closed as the reported issue is expected behavior type-question A question about expected behavior or functionality
Projects
None yet
Development

No branches or pull requests

3 participants