Skip to content

Dart and analyzer work with old-style function type alias with generic type parameter in a strange way. #45718

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

Closed
iarkh opened this issue Apr 15, 2021 · 1 comment

Comments

@iarkh
Copy link
Contributor

iarkh commented Apr 15, 2021

Dart SDK version: 2.14.0-3.0.dev (dev) (Tue Apr 13 22:57:19 2021 -0700) on "windows_x64"

Please try to run the following code:

typedef void TEST<T extends void Function<TT>()>();
void testme<T extends void Function<TT>()>() { print(T); }

main() {
  TEST t = testme;

  dynamic t1 = t;
  dynamic t2 = testme;

  print(testme is TEST);
  print(t is TEST);
  print(t1 is TEST);
  print(t2 is TEST);

  t = t1;
  t = t2;
}

It's expected that a runtime type of t, t1 and t2 variables is the same (they points to the same Function object). However, this is not so - dart sample output is:

$> dart --enable-experiment=nonfunction-type-aliases,generic-metadata test.dart
false
true
true
false
Unhandled exception:
type '<T extends <X1>() => void>() => void' is not a subtype of type '() => void'
#0      main (file:///D:/DART/sdk/tests/co19/src/LanguageFeatures/Generic-functions-as-type-args/test.dart:16:3)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
#2      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

Analyzer silently passes with the example above.

See also Issue 45313, Issue 45322.

@eernstg
Copy link
Member

eernstg commented Apr 15, 2021

That's not quite true. TEST<T> means void Function() for any T so the raw TEST also means void Function().

This implies that TEST t = testme; initializes t to the generic function instantiation of testme which has type void Function(), and when executed that instantiation of testme will print something that means void Function<TT>() (it probably prints something like '<TT>() => void').

So this means that t and t1 is the same function object, which is different from the result of evaluating testme with a context type that doesn't cause the generic function instantiation to take place (e.g., var t2 = testme; or dynamic t2 = testme;).

This explains why we get 'false', 'true', 'true', 'false', and also why t = t2 throws.

So it's all working as intended.

@eernstg eernstg closed this as completed Apr 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants