@@ -1610,6 +1610,7 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
1610
1610
destinationType = destinationExpression.accept (this );
1611
1611
}
1612
1612
}
1613
+
1613
1614
if (questionAssignNode != null ) {
1614
1615
_guards.add (destinationType.node);
1615
1616
}
@@ -2153,6 +2154,21 @@ mixin _AssignmentChecker {
2153
2154
{@required DecoratedType source,
2154
2155
@required DecoratedType destination,
2155
2156
@required bool hard}) {
2157
+ var sourceType = source.type;
2158
+ var destinationType = destination.type;
2159
+ if (! _typeSystem.isSubtypeOf (sourceType, destinationType)) {
2160
+ // Not a proper upcast assignment.
2161
+ if (_typeSystem.isSubtypeOf (destinationType, sourceType)) {
2162
+ // But rather a downcast.
2163
+ _checkDowncast (origin,
2164
+ source: source, destination: destination, hard: hard);
2165
+ return ;
2166
+ }
2167
+ // Neither a proper upcast assignment nor an implicit downcast (some
2168
+ // illegal code, or we did something wrong to get here).
2169
+ assert (false , 'side cast not supported: $sourceType to $destinationType ' );
2170
+ return ;
2171
+ }
2156
2172
_connect (source.node, destination.node, origin, hard: hard);
2157
2173
_checkAssignment_recursion (origin,
2158
2174
source: source, destination: destination);
@@ -2165,35 +2181,7 @@ mixin _AssignmentChecker {
2165
2181
{@required DecoratedType source, @required DecoratedType destination}) {
2166
2182
var sourceType = source.type;
2167
2183
var destinationType = destination.type;
2168
- if (! _typeSystem.isSubtypeOf (sourceType, destinationType)) {
2169
- // Not a proper upcast assignment. It is either an implicit downcast or
2170
- // some illegal code. It's handled on a "best effort" basis.
2171
- if (destinationType is TypeParameterType &&
2172
- sourceType is ! TypeParameterType ) {
2173
- // Assume an assignment to the type parameter's bound.
2174
- _checkAssignment (origin,
2175
- source: source,
2176
- destination:
2177
- _getTypeParameterTypeBound (destination).withNode (_graph.always),
2178
- hard: false );
2179
- return ;
2180
- }
2181
- if (sourceType is InterfaceType && destinationType is InterfaceType ) {
2182
- if (_typeSystem.isSubtypeOf (destinationType, sourceType)) {
2183
- var rewrittenDestination = _decoratedClassHierarchy.asInstanceOf (
2184
- destination, sourceType.element);
2185
- assert (rewrittenDestination.typeArguments.length ==
2186
- source.typeArguments.length);
2187
- for (int i = 0 ; i < rewrittenDestination.typeArguments.length; i++ ) {
2188
- _checkAssignment (origin,
2189
- source: source.typeArguments[i],
2190
- destination: rewrittenDestination.typeArguments[i],
2191
- hard: false );
2192
- }
2193
- }
2194
- }
2195
- return ;
2196
- }
2184
+ assert (_typeSystem.isSubtypeOf (sourceType, destinationType));
2197
2185
if (destinationType.isDartAsyncFutureOr) {
2198
2186
var s1 = destination.typeArguments[0 ];
2199
2187
if (sourceType.isDartAsyncFutureOr) {
@@ -2327,6 +2315,63 @@ mixin _AssignmentChecker {
2327
2315
}
2328
2316
}
2329
2317
2318
+ void _checkDowncast (EdgeOrigin origin,
2319
+ {@required DecoratedType source,
2320
+ @required DecoratedType destination,
2321
+ @required bool hard}) {
2322
+ assert (_typeSystem.isSubtypeOf (destination.type, source.type));
2323
+ // Nullability should narrow to maintain subtype relationship.
2324
+ _connect (source.node, destination.node, origin, hard: hard);
2325
+ if (source.type.isDynamic) {
2326
+ assert (destination.typeFormals? .isEmpty ?? true ,
2327
+ 'downcast to something with type parameters not yet supported.' );
2328
+ assert (destination is ! FunctionType ,
2329
+ 'downcast to function type not yet supported.' );
2330
+ if (destination.type is ParameterizedType ) {
2331
+ for (final param
2332
+ in (destination.type as ParameterizedType ).typeParameters) {
2333
+ assert (param.type.bound.isDynamic,
2334
+ 'downcast to type parameters with bounds not supported' );
2335
+ }
2336
+ }
2337
+
2338
+ for (final arg in destination.typeArguments) {
2339
+ // We cannot assume we're downcasting to C<T!>. Downcast to C<T?>.
2340
+ _checkDowncast (origin, source: source, destination: arg, hard: false );
2341
+ }
2342
+ } else if (destination.type is TypeParameterType &&
2343
+ source.type is ! TypeParameterType ) {
2344
+ // Assume an assignment to the type parameter's bound.
2345
+ _checkAssignment (origin,
2346
+ source: source,
2347
+ destination:
2348
+ _getTypeParameterTypeBound (destination).withNode (_graph.always),
2349
+ hard: false );
2350
+ } else if (destination.type is InterfaceTypeImpl ) {
2351
+ assert (source.typeArguments.isEmpty,
2352
+ 'downcast from interface type with type args not supported.' );
2353
+ if (destination.type is ParameterizedType ) {
2354
+ for (final param
2355
+ in (destination.type as ParameterizedType ).typeParameters) {
2356
+ assert (param.type.bound.isDynamic,
2357
+ 'downcast to type parameters with bounds not supported' );
2358
+ }
2359
+ }
2360
+ for (final arg in destination.typeArguments) {
2361
+ // We cannot assume we're downcasting to C<T!>. Downcast to C<T?>.
2362
+ _checkDowncast (origin,
2363
+ source: DecoratedType (_typeProvider.dynamicType, _graph.always),
2364
+ destination: arg,
2365
+ hard: false );
2366
+ }
2367
+ } else {
2368
+ assert (
2369
+ false ,
2370
+ 'downcasting from ${source .type .runtimeType } to '
2371
+ '${destination .type .runtimeType } not supported.' );
2372
+ }
2373
+ }
2374
+
2330
2375
void _connect (
2331
2376
NullabilityNode source, NullabilityNode destination, EdgeOrigin origin,
2332
2377
{bool hard = false });
0 commit comments