@@ -445,16 +445,102 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
445445 .expression;
446446 }
447447
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 (
452454 DartType contextType, ExpressionInferenceResult inferenceResult,
453455 {int ? fileOffset,
454456 DartType ? declaredContextType,
455457 DartType ? runtimeCheckedType,
456458 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 ,
458544 Template <Message Function (DartType , DartType , bool )>? errorTemplate,
459545 Template <Message Function (DartType , DartType , bool )>?
460546 nullabilityErrorTemplate,
@@ -492,8 +578,6 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
492578 fileOffset ?? = inferenceResult.expression.fileOffset;
493579 contextType = computeGreatestClosure (contextType);
494580
495- DartType initialContextType = runtimeCheckedType ?? contextType;
496-
497581 Template <Message Function (DartType , DartType , bool )>?
498582 preciseTypeErrorTemplate =
499583 _getPreciseTypeErrorTemplate (inferenceResult.expression);
@@ -502,7 +586,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
502586 isNonNullableByDefault: isNonNullableByDefault,
503587 isVoidAllowed: isVoidAllowed,
504588 isExpressionTypePrecise: preciseTypeErrorTemplate != null ,
505- coerceExpression: coerceExpression );
589+ coerceExpression: isCoercionAllowed );
506590
507591 if (assignabilityResult.needsTearOff) {
508592 TypedTearoff typedTearoff = _tearOffCall (inferenceResult.expression,
@@ -519,20 +603,12 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
519603
520604 DartType expressionType = inferenceResult.inferredType;
521605 Expression expression = inferenceResult.expression;
522- Expression result ;
523- DartType ? postCoercionType ;
606+ DartType ? postCoercionType = inferenceResult.postCoercionType ;
607+ Expression ? result ;
524608 switch (assignabilityResult.kind) {
525609 case AssignabilityKind .assignable:
526- result = expression;
527610 break ;
528611 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;
536612 break ;
537613 case AssignabilityKind .unassignable:
538614 // Error: not assignable. Perform error recovery.
@@ -615,7 +691,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
615691 fileOffset, helper.uri);
616692 }
617693
618- if (! identical ( result, expression) ) {
694+ if (result != null ) {
619695 flowAnalysis.forwardExpression (result, expression);
620696 return new ExpressionInferenceResult (expressionType, result,
621697 postCoercionType: postCoercionType);
@@ -624,6 +700,57 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
624700 }
625701 }
626702
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+
627754 Expression _wrapTearoffErrorExpression (Expression expression,
628755 DartType contextType, Template <Message Function (String )> template) {
629756 // ignore: unnecessary_null_comparison
0 commit comments