@@ -445,16 +445,102 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
445
445
.expression;
446
446
}
447
447
448
- /// Same as [ensureAssignable] , but accepts an [ExpressionInferenceResult]
449
- /// rather than an expression and a type separately. If no change is made,
450
- /// [inferenceResult] is returned unchanged.
451
- ExpressionInferenceResult ensureAssignableResult (
448
+ /// Coerces expression ensuring its assignability to [contextType]
449
+ ///
450
+ /// If the expression is assignable without coercion, [inferenceResult]
451
+ /// is returned unchanged. If no coercion is possible for the given types,
452
+ /// `null` is returned.
453
+ ExpressionInferenceResult ? coerceExpressionForAssignment (
452
454
DartType contextType, ExpressionInferenceResult inferenceResult,
453
455
{int ? fileOffset,
454
456
DartType ? declaredContextType,
455
457
DartType ? runtimeCheckedType,
456
458
bool isVoidAllowed = false ,
457
- bool coerceExpression = true ,
459
+ bool coerceExpression = true }) {
460
+ // ignore: unnecessary_null_comparison
461
+ assert (contextType != null );
462
+
463
+ fileOffset ?? = inferenceResult.expression.fileOffset;
464
+ contextType = computeGreatestClosure (contextType);
465
+
466
+ DartType initialContextType = runtimeCheckedType ?? contextType;
467
+
468
+ Template <Message Function (DartType , DartType , bool )>?
469
+ preciseTypeErrorTemplate =
470
+ _getPreciseTypeErrorTemplate (inferenceResult.expression);
471
+ AssignabilityResult assignabilityResult = _computeAssignabilityKind (
472
+ contextType, inferenceResult.inferredType,
473
+ isNonNullableByDefault: isNonNullableByDefault,
474
+ isVoidAllowed: isVoidAllowed,
475
+ isExpressionTypePrecise: preciseTypeErrorTemplate != null ,
476
+ coerceExpression: coerceExpression);
477
+
478
+ if (assignabilityResult.needsTearOff) {
479
+ TypedTearoff typedTearoff = _tearOffCall (inferenceResult.expression,
480
+ inferenceResult.inferredType as InterfaceType , fileOffset);
481
+ inferenceResult = new ExpressionInferenceResult (
482
+ typedTearoff.tearoffType, typedTearoff.tearoff);
483
+ }
484
+ if (assignabilityResult.implicitInstantiation != null ) {
485
+ inferenceResult = _applyImplicitInstantiation (
486
+ assignabilityResult.implicitInstantiation,
487
+ inferenceResult.inferredType,
488
+ inferenceResult.expression);
489
+ }
490
+
491
+ DartType expressionType = inferenceResult.inferredType;
492
+ Expression expression = inferenceResult.expression;
493
+ switch (assignabilityResult.kind) {
494
+ case AssignabilityKind .assignable:
495
+ return inferenceResult;
496
+ case AssignabilityKind .assignableCast:
497
+ // Insert an implicit downcast.
498
+ Expression asExpression =
499
+ new AsExpression (expression, initialContextType)
500
+ ..isTypeError = true
501
+ ..isForNonNullableByDefault = isNonNullableByDefault
502
+ ..isForDynamic = expressionType is DynamicType
503
+ ..fileOffset = fileOffset;
504
+ flowAnalysis.forwardExpression (asExpression, expression);
505
+ return new ExpressionInferenceResult (expressionType, asExpression,
506
+ postCoercionType: initialContextType);
507
+ case AssignabilityKind .unassignable:
508
+ // Error: not assignable. Perform error recovery.
509
+ return null ;
510
+ case AssignabilityKind .unassignableVoid:
511
+ // Error: not assignable. Perform error recovery.
512
+ return null ;
513
+ case AssignabilityKind .unassignablePrecise:
514
+ // The type of the expression is known precisely, so an implicit
515
+ // downcast is guaranteed to fail. Insert a compile-time error.
516
+ return null ;
517
+ case AssignabilityKind .unassignableCantTearoff:
518
+ return null ;
519
+ case AssignabilityKind .unassignableNullability:
520
+ return null ;
521
+ default :
522
+ return unhandled ("${assignabilityResult }" , "ensureAssignable" ,
523
+ fileOffset, helper.uri);
524
+ }
525
+ }
526
+
527
+ /// Performs assignability checks on an expression
528
+ ///
529
+ /// [inferenceResult.expression] of type [inferenceResult.inferredType] is
530
+ /// checked for assignability to [contextType] . The errors are reported on the
531
+ /// current library and the expression wrapped in an [InvalidExpression] , if
532
+ /// needed. If no change is made, [inferenceResult] is returned unchanged.
533
+ ///
534
+ /// If [isCoercionAllowed] is `true` , the assignability check is made
535
+ /// accounting for a possible coercion that may adjust the type of the
536
+ /// expression.
537
+ ExpressionInferenceResult reportAssignabilityErrors (
538
+ DartType contextType, ExpressionInferenceResult inferenceResult,
539
+ {int ? fileOffset,
540
+ DartType ? declaredContextType,
541
+ DartType ? runtimeCheckedType,
542
+ bool isVoidAllowed = false ,
543
+ bool isCoercionAllowed = true ,
458
544
Template <Message Function (DartType , DartType , bool )>? errorTemplate,
459
545
Template <Message Function (DartType , DartType , bool )>?
460
546
nullabilityErrorTemplate,
@@ -492,8 +578,6 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
492
578
fileOffset ?? = inferenceResult.expression.fileOffset;
493
579
contextType = computeGreatestClosure (contextType);
494
580
495
- DartType initialContextType = runtimeCheckedType ?? contextType;
496
-
497
581
Template <Message Function (DartType , DartType , bool )>?
498
582
preciseTypeErrorTemplate =
499
583
_getPreciseTypeErrorTemplate (inferenceResult.expression);
@@ -502,7 +586,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
502
586
isNonNullableByDefault: isNonNullableByDefault,
503
587
isVoidAllowed: isVoidAllowed,
504
588
isExpressionTypePrecise: preciseTypeErrorTemplate != null ,
505
- coerceExpression: coerceExpression );
589
+ coerceExpression: isCoercionAllowed );
506
590
507
591
if (assignabilityResult.needsTearOff) {
508
592
TypedTearoff typedTearoff = _tearOffCall (inferenceResult.expression,
@@ -519,20 +603,12 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
519
603
520
604
DartType expressionType = inferenceResult.inferredType;
521
605
Expression expression = inferenceResult.expression;
522
- Expression result ;
523
- DartType ? postCoercionType ;
606
+ DartType ? postCoercionType = inferenceResult.postCoercionType ;
607
+ Expression ? result ;
524
608
switch (assignabilityResult.kind) {
525
609
case AssignabilityKind .assignable:
526
- result = expression;
527
610
break ;
528
611
case AssignabilityKind .assignableCast:
529
- // Insert an implicit downcast.
530
- result = new AsExpression (expression, initialContextType)
531
- ..isTypeError = true
532
- ..isForNonNullableByDefault = isNonNullableByDefault
533
- ..isForDynamic = expressionType is DynamicType
534
- ..fileOffset = fileOffset;
535
- postCoercionType = initialContextType;
536
612
break ;
537
613
case AssignabilityKind .unassignable:
538
614
// Error: not assignable. Perform error recovery.
@@ -615,7 +691,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
615
691
fileOffset, helper.uri);
616
692
}
617
693
618
- if (! identical ( result, expression) ) {
694
+ if (result != null ) {
619
695
flowAnalysis.forwardExpression (result, expression);
620
696
return new ExpressionInferenceResult (expressionType, result,
621
697
postCoercionType: postCoercionType);
@@ -624,6 +700,57 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
624
700
}
625
701
}
626
702
703
+ /// Same as [ensureAssignable] , but accepts an [ExpressionInferenceResult]
704
+ /// rather than an expression and a type separately. If no change is made,
705
+ /// [inferenceResult] is returned unchanged.
706
+ ExpressionInferenceResult ensureAssignableResult (
707
+ DartType contextType, ExpressionInferenceResult inferenceResult,
708
+ {int ? fileOffset,
709
+ DartType ? declaredContextType,
710
+ DartType ? runtimeCheckedType,
711
+ bool isVoidAllowed = false ,
712
+ bool coerceExpression = true ,
713
+ Template <Message Function (DartType , DartType , bool )>? errorTemplate,
714
+ Template <Message Function (DartType , DartType , bool )>?
715
+ nullabilityErrorTemplate,
716
+ Template <Message Function (DartType , bool )>? nullabilityNullErrorTemplate,
717
+ Template <Message Function (DartType , DartType , bool )>?
718
+ nullabilityNullTypeErrorTemplate,
719
+ Template <Message Function (DartType , DartType , DartType , DartType , bool )>?
720
+ nullabilityPartErrorTemplate,
721
+ Map <DartType , NonPromotionReason > Function ()? whyNotPromoted}) {
722
+ // ignore: unnecessary_null_comparison
723
+ assert (contextType != null );
724
+
725
+ if (coerceExpression) {
726
+ ExpressionInferenceResult ? coercionResult = coerceExpressionForAssignment (
727
+ contextType, inferenceResult,
728
+ fileOffset: fileOffset,
729
+ declaredContextType: declaredContextType,
730
+ runtimeCheckedType: runtimeCheckedType,
731
+ isVoidAllowed: isVoidAllowed,
732
+ coerceExpression: coerceExpression);
733
+ if (coercionResult != null ) {
734
+ return coercionResult;
735
+ }
736
+ }
737
+
738
+ inferenceResult = reportAssignabilityErrors (contextType, inferenceResult,
739
+ fileOffset: fileOffset,
740
+ declaredContextType: declaredContextType,
741
+ runtimeCheckedType: runtimeCheckedType,
742
+ isVoidAllowed: isVoidAllowed,
743
+ isCoercionAllowed: coerceExpression,
744
+ errorTemplate: errorTemplate,
745
+ nullabilityErrorTemplate: nullabilityErrorTemplate,
746
+ nullabilityNullErrorTemplate: nullabilityNullErrorTemplate,
747
+ nullabilityNullTypeErrorTemplate: nullabilityNullTypeErrorTemplate,
748
+ nullabilityPartErrorTemplate: nullabilityPartErrorTemplate,
749
+ whyNotPromoted: whyNotPromoted);
750
+
751
+ return inferenceResult;
752
+ }
753
+
627
754
Expression _wrapTearoffErrorExpression (Expression expression,
628
755
DartType contextType, Template <Message Function (String )> template) {
629
756
// ignore: unnecessary_null_comparison
0 commit comments