Sdk: NNBD SDK broke existing code - inference of `orElse` argument to Iterable.singleWhere

Created on 24 Mar 2020  路  4Comments  路  Source: dart-lang/sdk

Here is a repro:

void main() {
  findKey(x, 'bar');
}

const x = {'foo': 'bar'};

void findKey(Map<String, dynamic> m, dynamic search) {
  print(m.entries
      .singleWhere((entry) => entry.value == search, orElse: () => null)
      ?.key);
}

This prints foo on all published SDKs, and the current SDK when it's not built with --nnbd. When I build the SDK with --nnbd and run this code I instead get:

Unhandled exception:
type '() => MapEntry<String, dynamic>' is not a subtype of type '(() => MapEntry<String, String>)?' of 'orElse'
#0      Iterable.singleWhere (dart:core/iterable.dart)
#1      findKey (file:///usr/local/google/home/nbosch/projects/dart_repro/foo.dart:9:8)
#2      main (file:///usr/local/google/home/nbosch/projects/dart_repro/foo.dart:2:3)
#3      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:300:19)
#4      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:167:12)

So when the runtime is Map<String, String>, but statically we have Map<String, dynamic> the orElse argument gets the wrong time.

This does _not_ require enabling the experiment or turning on strong mode.

NNBD area-front-end type-bug

Most helpful comment

Analyzer infers correct static type, I added a new test.

All 4 comments

FYI @kevmoo this surfaces in json_serializable inside _$enumDecode

Smaller reproduction:

test5.dart

void foo(Map<String, String> Function() f) {
  print(f.runtimeType);
}
// @dart = 2.5

import 'test5.dart';

void main() {
  foo(() => null);
}

When compiled without the experiment, this prints "() => Null". With the experiment, it prints () => Map<String, String>.

@stefantsov @johnniwinther what I think is probably happening is that in the last section of the function literal inference here we have the following:

Let T be the actual returned type of a function literal as computed above. Let R be the greatest
closure of the typing context K as computed above. If T <: R then let S be T

Since we are in a legacy library, I believe that T <: R should be interpreted as LEGACY_SUBTYPE(T, R), and I suspect it may not be?

cc @scheglov not sure if this is observable in the analyzer or not, since it mostly affects the reified type.

Analyzer infers correct static type, I added a new test.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nex3 picture nex3  路  3Comments

sgrekhov picture sgrekhov  路  3Comments

DartBot picture DartBot  路  3Comments

ranquild picture ranquild  路  3Comments

DartBot picture DartBot  路  3Comments