Sdk: Type inference fails to infer generic type arguments of closure default argument values

Created on 28 Jun 2018  路  9Comments  路  Source: dart-lang/sdk

Reproduced in

  • Dart SDK Version 2.0.0-dev.63.0
  • macOS
  • Chrome

Here's a gist you can run to reproduce the error: https://dartpad.dartlang.org/396f0f9b95e4bee49fb13fab97fbe7d3

The default of list in the following example is inferred as a plain List, rather than List<String>.

void Function([List<String>]) foo() {
  return ([list = const []]) {
    /* ... */
  }
}

So long as you pass a list to the closure returned by foo(), everything is fine. You can even pass an untyped list, foo([]) and rely on the inference of the return type to fill in the generic type argument as String.

However, if you omit the argument, the default value is incorrectly instantiated without the correct generic type argument of String and yields a TypeError.

TypeError: Instance of 'JSArray': type 'JSArray' is not a subtype of type 'List'

Here are two workarounds for this bug.

  return ([list = const <String>[]]) {
  return ([List<String> list = const []]) {

Considering that inference works for an untyped list passed into the closure, I'd suspect it's possible for the untyped default argument value to be correctly inferred.

area-front-end

Most helpful comment

It doesn't need to in order to satisfy the return type, but in general it may need to in order to statically type uses of the argument in the body. We don't consider parameter uses when doing inference, so we just go ahead and infer the more specific type.

void Function([List<String>]) foo() {
  return ([list = const []]) {
    if (list.length > 0) {
     print(list[0] + "hello");  // If I don't infer List<String> for the argument, this has to be a dynamic call, and I can't give you an error if you do list[0] + 3
  }
}

All 9 comments

I'm not sure this is a failure/bug versus WAI (though I think the inconsistency hurts in any case).

/cc @jmesserly @leafpetersen thoughts?

This is definitely not WAI. The default value should be inferred using the parameter type as the inference context, even if the parameter type is inferred via downwards inference.

This seems to work correctly on DDC, so I think this is just a CFE bug.

Thanks @leafpetersen!

I'm guessing this is working as intended

([List<dynamic> l]) {} is void Function([List<String]); // true

The inference won't make the argument more specific because it doesn't need to in order to satisfy the return type.

<dynamic>[] is List<String>; // false

Here the inference makes the List type more specific because it needs to in order to satisfy the return type.

I originally expected downwards inference to work here as @leafpetersen suggests, but @natebosch's point seems valid too. If the argument does satisfy the return type, why is it then a runtime error?

It doesn't need to in order to satisfy the return type, but in general it may need to in order to statically type uses of the argument in the body. We don't consider parameter uses when doing inference, so we just go ahead and infer the more specific type.

void Function([List<String>]) foo() {
  return ([list = const []]) {
    if (list.length > 0) {
     print(list[0] + "hello");  // If I don't infer List<String> for the argument, this has to be a dynamic call, and I can't give you an error if you do list[0] + 3
  }
}

in analyzer this happens in two stages:

  1. inferFormalParameterList in static_type_analyzer.dart -> marks the parameter with the inferred type
  2. visitDefaultFormalParameter in resolver.dart -> makes the inferred (or declared) parameter type be the context type for the default value

(both of those stages are actually the same pass, BTW, resolver in analyzer uses static_type_analyzer and element_resolver as helper classes, conceptually they're all the same pass.)

@jmesserly so is step 2 not getting the inferred type, and thus using the declared?

@jmesserly so is step 2 not getting the inferred type, and thus using the declared?

oh no sorry, I was describing the behavior for the Analyzer (used by DDC and IDEs) ... this appears to be working correctly in Analyzer. I wrote that in case it would help the CFE folks track down the bug.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nex3 picture nex3  路  3Comments

xster picture xster  路  3Comments

matanlurey picture matanlurey  路  3Comments

brooth picture brooth  路  3Comments

jmesserly picture jmesserly  路  3Comments