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.
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.
Most helpful comment
Analyzer infers correct static type, I added a new test.