Skip to content

Commit 6efbe7f

Browse files
MichaelRFairhurstcommit-bot@chromium.org
authored andcommitted
[nnbd_migration] Handle downcasting type arguments.
There are no examples of a downcast involving a nullable type in the current set of working packages. This tells me that it's rare, and that this behavior should be good enough. If we're wrong, this unblocks packages and we can revisit this decision later. Change-Id: Ic4e23cc6a848b83b2d1fb6c97ff513c3cc9fdb2e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/128778 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
1 parent 6ed72d3 commit 6efbe7f

File tree

3 files changed

+60
-6
lines changed

3 files changed

+60
-6
lines changed

pkg/nnbd_migration/lib/src/edge_builder.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2528,11 +2528,18 @@ mixin _AssignmentChecker {
25282528
destination: _getTypeParameterTypeBound(destination),
25292529
hard: false);
25302530
} else if (destinationType is InterfaceType) {
2531-
assert(source.typeArguments.isEmpty,
2532-
'downcast from interface type with type args not supported.');
2533-
for (final param in destinationType.element.typeParameters) {
2534-
assert(param.bound == null,
2535-
'downcast to type parameters with bounds not supported');
2531+
if (source.type is InterfaceType) {
2532+
final target = _decoratedClassHierarchy.asInstanceOf(
2533+
destination, source.type.element as ClassElement);
2534+
for (var i = 0; i < source.typeArguments.length; ++i) {
2535+
_checkDowncast(origin,
2536+
source: source.typeArguments[i],
2537+
destination: target.typeArguments[i],
2538+
hard: false);
2539+
}
2540+
} else {
2541+
assert(false,
2542+
'downcasting from ${source.type.runtimeType} to interface type');
25362543
}
25372544
} else if (destinationType is FunctionType) {
25382545
if (source.type.isDartCoreFunction) {

pkg/nnbd_migration/test/api_test.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,26 @@ void f(dynamic a) {
10181018
await _checkSingleFileChanges(content, expected);
10191019
}
10201020

1021+
test_downcast_dynamic_type_argument() async {
1022+
// This pattern is common and seems to have this as a best migration. It is
1023+
// less clear, but plausible, that this holds for other types of type
1024+
// parameter downcasts.
1025+
var content = '''
1026+
List<int> f(List a) => a;
1027+
void main() {
1028+
f(<int>[null]);
1029+
}
1030+
''';
1031+
1032+
var expected = '''
1033+
List<int?> f(List a) => a;
1034+
void main() {
1035+
f(<int?>[null]);
1036+
}
1037+
''';
1038+
await _checkSingleFileChanges(content, expected);
1039+
}
1040+
10211041
@failingTest
10221042
test_downcast_not_widest_type_type_parameters() async {
10231043
// Fails because a hard assignment from List<int/*1*/> to List<int/*2*/>
@@ -1054,6 +1074,26 @@ void f(dynamic a) {
10541074
await _checkSingleFileChanges(content, expected);
10551075
}
10561076

1077+
test_downcast_type_argument_preserve_nullability() async {
1078+
// There are no examples in front of us yet where anyone downcasts a type
1079+
// with a nullable type parameter. This is maybe correct, maybe not, and it
1080+
// unblocks us to find out which at a later point in time.
1081+
var content = '''
1082+
List<int> f(Iterable<num> a) => a;
1083+
void main() {
1084+
f(<num>[null]);
1085+
}
1086+
''';
1087+
1088+
var expected = '''
1089+
List<int?> f(Iterable<num?> a) => a;
1090+
void main() {
1091+
f(<num?>[null]);
1092+
}
1093+
''';
1094+
await _checkSingleFileChanges(content, expected);
1095+
}
1096+
10571097
@failingTest
10581098
test_downcast_widest_type_from_related_type_parameters() async {
10591099
var content = '''

pkg/nnbd_migration/test/edge_builder_test.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ class AssignmentCheckerTest extends Object
195195
assertNoEdge(t.typeArguments[0].node, anyNode);
196196
}
197197

198-
@failingTest
199198
test_generic_to_generic_downcast() {
200199
var t1 = list(list(object()));
201200
var t2 = myListOfList(object());
@@ -212,6 +211,14 @@ class AssignmentCheckerTest extends Object
212211
assertEdge(b, substitutionNode(a, c), hard: false);
213212
}
214213

214+
test_generic_to_generic_downcast_same_element() {
215+
var t1 = list(object());
216+
var t2 = list(int_());
217+
assign(t1, t2, hard: true);
218+
assertEdge(t1.node, t2.node, hard: true);
219+
assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node, hard: false);
220+
}
221+
215222
test_generic_to_generic_same_element() {
216223
var t1 = list(object());
217224
var t2 = list(object());

0 commit comments

Comments
 (0)