Properly handle implicit `.call` tearoff on RHS of assignments.

Previously, when inserting an implicit `.call` tearoff on the RHS of
an assignment, both the analyzer and the CFE failed to properly
propagate the type of the tearoff to the static type of the assignment
expression.  This resulted in a soundness bug for compiled programs.

Fixes #48409.
Fixes #48410.

Bug: https://github.com/dart-lang/sdk/issues/48409, https://github.com/dart-lang/sdk/issues/48410
Change-Id: I489f38bd7cac4ebadd3746ec32c1ef0f73e18169
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/233640
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index 1009c21..16e5f20 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -225,27 +225,19 @@
   void _resolveTypes(AssignmentExpressionImpl node,
       {required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
     DartType assignedType;
-    DartType nodeType;
+    DartType? implicitCallContext;
 
     var rightHandSide = node.rightHandSide;
     var operator = node.operator.type;
     if (operator == TokenType.EQ) {
       assignedType = rightHandSide.typeOrThrow;
-      nodeType = assignedType;
+      implicitCallContext = node.writeType;
     } else if (operator == TokenType.QUESTION_QUESTION_EQ) {
-      var leftType = node.readType!;
-
-      // The LHS value will be used only if it is non-null.
-      if (_isNonNullableByDefault) {
-        leftType = _typeSystem.promoteToNonNull(leftType);
-      }
-
       assignedType = rightHandSide.typeOrThrow;
-      nodeType = _typeSystem.getLeastUpperBound(leftType, assignedType);
+      implicitCallContext = node.writeType;
     } else if (operator == TokenType.AMPERSAND_AMPERSAND_EQ ||
         operator == TokenType.BAR_BAR_EQ) {
       assignedType = _typeProvider.boolType;
-      nodeType = assignedType;
     } else {
       var operatorElement = node.staticElement;
       if (operatorElement != null) {
@@ -261,14 +253,27 @@
       } else {
         assignedType = DynamicTypeImpl.instance;
       }
-      nodeType = assignedType;
     }
 
-    _inferenceHelper.recordStaticType(node, nodeType);
-    var callReference = _resolver.insertImplicitCallReference(rightHandSide);
-    if (callReference != rightHandSide) {
-      assignedType = callReference.typeOrThrow;
+    var callInsertion = _resolver.insertImplicitCallReference(rightHandSide,
+        context: implicitCallContext);
+    if (callInsertion != null) {
+      assignedType = callInsertion.staticType;
     }
+    DartType nodeType;
+    if (operator == TokenType.QUESTION_QUESTION_EQ) {
+      var leftType = node.readType!;
+
+      // The LHS value will be used only if it is non-null.
+      if (_isNonNullableByDefault) {
+        leftType = _typeSystem.promoteToNonNull(leftType);
+      }
+
+      nodeType = _typeSystem.getLeastUpperBound(leftType, assignedType);
+    } else {
+      nodeType = assignedType;
+    }
+    _inferenceHelper.recordStaticType(node, nodeType);
 
     // TODO(scheglov) Remove from ErrorVerifier?
     _checkForInvalidAssignment(
diff --git a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
index 3dd455d..0aa6a8f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
@@ -64,7 +64,13 @@
       _resolver.flowAnalysis.flow?.lateInitializer_end();
     }
 
-    initializer = _resolver.insertImplicitCallReference(initializer);
+    var callInsertion = _resolver.insertImplicitCallReference(initializer);
+    if (callInsertion != null) {
+      var insertedExpression = callInsertion.expression;
+      if (insertedExpression != null) {
+        initializer = callInsertion.expression;
+      }
+    }
 
     // Initializers of top-level variables and fields are already included
     // into elements during linking.
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index d07e3e9..d42e16a 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -78,6 +78,24 @@
 /// promoted.
 typedef WhyNotPromotedGetter = Map<DartType, NonPromotionReason> Function();
 
+/// Data structure describing the result of inserting an implicit call reference
+/// into the AST.
+class ImplicitCallInsertionResult {
+  /// The expression that was inserted, or `null`, if no expression was
+  /// inserted.
+  ///
+  /// The only reason this might be `null` is that, at the moment, we only
+  /// insert implicit call reference expressions if the 'constructor-tearoffs'
+  /// feature is enabled (to avoid breaking clients).
+  /// TODO(paulberry): make this non-nullable when we change this behavior.
+  final ImplicitCallReferenceImpl? expression;
+
+  /// The type of the implicit call tear-off.
+  final FunctionType staticType;
+
+  ImplicitCallInsertionResult(this.expression, this.staticType);
+}
+
 /// Maintains and manages contextual type information used for
 /// inferring types.
 class InferenceContext {
@@ -798,32 +816,27 @@
   }
 
   /// If `expression` should be treated as `expression.call`, inserts an
-  /// [ImplicitCallReferece] node which wraps [expression].
+  /// [ImplicitCallReference] node which wraps [expression].
   ///
-  /// If an [ImplicitCallReferece] is inserted, returns it; otherwise, returns
-  /// [expression].
-  ExpressionImpl insertImplicitCallReference(Expression expression) {
+  /// If an [ImplicitCallReference] is inserted, returns an
+  /// [ImplicitCallInsertionResult] describing what was changed; otherwise,
+  /// returns `null`.
+  ImplicitCallInsertionResult? insertImplicitCallReference(
+      Expression expression,
+      {DartType? context}) {
     expression as ExpressionImpl;
-    if (!isConstructorTearoffsEnabled) {
-      // Temporarily, only create [ImplicitCallReference] nodes under the
-      // 'constructor-tearoffs' feature.
-      // TODO(srawlins): When we are ready to make a breaking change release to
-      // the analyzer package, remove this exception.
-      return expression;
-    }
-
     var parent = expression.parent;
     if (parent is CascadeExpression && parent.target == expression) {
       // Do not perform an "implicit tear-off conversion" here. It should only
       // be performed on [parent]. See
       // https://github.com/dart-lang/language/issues/1873.
-      return expression;
+      return null;
     }
-    var context = InferenceContext.getContext(expression);
+    context ??= InferenceContext.getContext(expression);
     var callMethod =
         getImplicitCallMethod(expression.typeOrThrow, context, expression);
     if (callMethod == null || context == null) {
-      return expression;
+      return null;
     }
 
     // `expression` is to be treated as `expression.call`.
@@ -848,6 +861,15 @@
     } else {
       typeArgumentTypes = [];
     }
+
+    if (!isConstructorTearoffsEnabled) {
+      // Temporarily, only create [ImplicitCallReference] nodes under the
+      // 'constructor-tearoffs' feature.
+      // TODO(srawlins, paulberry): When we are ready to make a breaking change
+      // release to the analyzer package, remove this exception.
+      return ImplicitCallInsertionResult(null, callMethodType);
+    }
+
     var callReference = astFactory.implicitCallReference(
       expression: expression,
       staticElement: callMethod,
@@ -858,7 +880,7 @@
 
     callReference.staticType = callMethodType;
 
-    return callReference;
+    return ImplicitCallInsertionResult(callReference, callMethodType);
   }
 
   /// If we reached a null-shorting termination, and the [node] has null
@@ -1440,8 +1462,8 @@
         node as ConstructorFieldInitializerImpl);
     if (fieldElement != null) {
       if (fieldType != null && expression.staticType != null) {
-        var callReference = insertImplicitCallReference(expression);
-        if (expression != callReference) {
+        var callReference = insertImplicitCallReference(expression)?.expression;
+        if (callReference != null) {
           checkForInvalidAssignment(node.fieldName, callReference,
               whyNotPromoted: whyNotPromoted);
         }
diff --git a/pkg/analyzer/test/generated/non_error_resolver_test.dart b/pkg/analyzer/test/generated/non_error_resolver_test.dart
index 73ea37b..66c764c 100644
--- a/pkg/analyzer/test/generated/non_error_resolver_test.dart
+++ b/pkg/analyzer/test/generated/non_error_resolver_test.dart
@@ -105,6 +105,23 @@
 ''');
   }
 
+  test_no_call_tearoff_on_promoted_var() async {
+    await assertNoErrorsInCode('''
+class B {
+  Object call() => '';
+}
+void test(Object x) {
+  x as Object Function();
+  x; // promoted
+  x = B(); // No implicit tearoff of `.call`, demotes x
+  x; // demoted
+}
+''');
+    assertType(findNode.simple('x; // promoted'), 'Object Function()');
+    assertType(findNode.assignment('x = B()'), 'B');
+    assertType(findNode.simple('x; // demoted'), 'Object');
+  }
+
   test_typedef_not_function() async {
     newFile('$testPackageLibPath/a.dart', content: '''
 typedef F = int;
@@ -1482,6 +1499,20 @@
 ''');
   }
 
+  test_implicit_call_tearoff_assignment_rhs() async {
+    await assertNoErrorsInCode('''
+class C {
+  void call() {}
+}
+test() {
+  void Function() f;
+  f = C();
+  return f;
+}
+''');
+    assertType(findNode.assignment('f = C()'), 'void Function()');
+  }
+
   test_importDuplicatedLibraryName() async {
     newFile("$testPackageLibPath/lib.dart", content: "library lib;");
     await assertErrorsInCode(r'''
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 7c41343..cc5d497 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -276,10 +276,7 @@
         typedefType: null);
     ExpressionInferenceResult inferredResult =
         inferrer.instantiateTearOff(resultType, typeContext, node);
-    Expression ensuredResultExpression =
-        inferrer.ensureAssignableResult(typeContext, inferredResult);
-    return new ExpressionInferenceResult(
-        inferredResult.inferredType, ensuredResultExpression);
+    return inferrer.ensureAssignableResult(typeContext, inferredResult);
   }
 
   @override
@@ -505,8 +502,9 @@
         node.condition, expectedType, !inferrer.isTopLevel,
         isVoidAllowed: true);
 
-    Expression condition =
-        inferrer.ensureAssignableResult(expectedType, conditionResult);
+    Expression condition = inferrer
+        .ensureAssignableResult(expectedType, conditionResult)
+        .expression;
     node.condition = condition..parent = node;
     inferrer.flowAnalysis.assert_afterCondition(node.condition);
     if (node.message != null) {
@@ -662,8 +660,9 @@
     ExpressionInferenceResult conditionResult = inferrer.inferExpression(
         node.condition, expectedType, !inferrer.isTopLevel,
         isVoidAllowed: true);
-    Expression condition =
-        inferrer.ensureAssignableResult(expectedType, conditionResult);
+    Expression condition = inferrer
+        .ensureAssignableResult(expectedType, conditionResult)
+        .expression;
     node.condition = condition..parent = node;
     inferrer.flowAnalysis.conditional_thenBegin(node.condition, node);
     bool isThenReachable = inferrer.flowAnalysis.isReachable;
@@ -755,8 +754,9 @@
     DartType receiverType = inferrer.getExtensionReceiverType(
         node.extension, extensionTypeArguments);
 
-    Expression receiver =
-        inferrer.ensureAssignableResult(receiverType, receiverResult);
+    Expression receiver = inferrer
+        .ensureAssignableResult(receiverType, receiverResult)
+        .expression;
 
     ObjectAccessTarget target = new ExtensionAccessTarget(
         node.target, null, ProcedureKind.Setter, extensionTypeArguments);
@@ -767,7 +767,8 @@
     ExpressionInferenceResult valueResult = inferrer.inferExpression(
         node.value, const UnknownType(), true,
         isVoidAllowed: false);
-    Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+    valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+    Expression value = valueResult.expression;
 
     VariableDeclaration? valueVariable;
     if (node.forEffect) {
@@ -823,8 +824,9 @@
     DartType receiverType = inferrer.getExtensionReceiverType(
         node.extension, extensionTypeArguments);
 
-    Expression receiver =
-        inferrer.ensureAssignableResult(receiverType, receiverResult);
+    Expression receiver = inferrer
+        .ensureAssignableResult(receiverType, receiverResult)
+        .expression;
 
     VariableDeclaration? receiverVariable;
     Expression readReceiver;
@@ -876,11 +878,9 @@
         node.rhs,
         null);
 
-    Expression binary = binaryResult.expression;
-    DartType binaryType = binaryResult.inferredType;
-
-    Expression value = inferrer.ensureAssignable(valueType, binaryType, binary,
+    binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
         isVoidAllowed: true);
+    Expression value = binaryResult.expression;
 
     VariableDeclaration? valueVariable;
     if (node.forEffect) {
@@ -952,7 +952,7 @@
         node.condition, boolType, !inferrer.isTopLevel,
         isVoidAllowed: true);
     Expression condition =
-        inferrer.ensureAssignableResult(boolType, conditionResult);
+        inferrer.ensureAssignableResult(boolType, conditionResult).expression;
     node.condition = condition..parent = node;
     inferrer.flowAnalysis.doStatement_end(condition);
     return const StatementInferenceResult();
@@ -1063,9 +1063,10 @@
   InitializerInferenceResult visitFieldInitializer(FieldInitializer node) {
     ExpressionInferenceResult initializerResult =
         inferrer.inferExpression(node.value, node.field.type, true);
-    Expression initializer = inferrer.ensureAssignableResult(
-        node.field.type, initializerResult,
-        fileOffset: node.fileOffset);
+    Expression initializer = inferrer
+        .ensureAssignableResult(node.field.type, initializerResult,
+            fileOffset: node.fileOffset)
+        .expression;
     node.value = initializer..parent = node;
     return const SuccessfulInitializerInferenceResult();
   }
@@ -1345,8 +1346,9 @@
       ExpressionInferenceResult conditionResult = inferrer.inferExpression(
           node.condition!, expectedType, !inferrer.isTopLevel,
           isVoidAllowed: true);
-      Expression condition =
-          inferrer.ensureAssignableResult(expectedType, conditionResult);
+      Expression condition = inferrer
+          .ensureAssignableResult(expectedType, conditionResult)
+          .expression;
       node.condition = condition..parent = node;
     }
 
@@ -1498,8 +1500,9 @@
     ExpressionInferenceResult conditionResult = inferrer.inferExpression(
         node.condition, expectedType, !inferrer.isTopLevel,
         isVoidAllowed: true);
-    Expression condition =
-        inferrer.ensureAssignableResult(expectedType, conditionResult);
+    Expression condition = inferrer
+        .ensureAssignableResult(expectedType, conditionResult)
+        .expression;
     node.condition = condition..parent = node;
     inferrer.flowAnalysis.ifStatement_thenBegin(condition, node);
     StatementInferenceResult thenResult = inferrer.inferStatement(node.then);
@@ -1783,7 +1786,7 @@
           element.condition, boolType, typeChecksNeeded,
           isVoidAllowed: false);
       Expression condition =
-          inferrer.ensureAssignableResult(boolType, conditionResult);
+          inferrer.ensureAssignableResult(boolType, conditionResult).expression;
       element.condition = condition..parent = element;
       inferrer.flowAnalysis.ifStatement_thenBegin(condition, element);
       ExpressionInferenceResult thenResult = inferElement(
@@ -1935,15 +1938,11 @@
       ExpressionInferenceResult result = inferrer.inferExpression(
           element, inferredTypeArgument, inferenceNeeded || typeChecksNeeded,
           isVoidAllowed: true);
-      Expression replacement;
       if (inferredTypeArgument is! UnknownType) {
-        replacement = inferrer.ensureAssignableResult(
-            inferredTypeArgument, result,
+        result = inferrer.ensureAssignableResult(inferredTypeArgument, result,
             isVoidAllowed: inferredTypeArgument is VoidType);
-      } else {
-        replacement = result.expression;
       }
-      return new ExpressionInferenceResult(result.inferredType, replacement);
+      return result;
     }
   }
 
@@ -2101,14 +2100,16 @@
     ExpressionInferenceResult leftResult = inferrer.inferExpression(
         node.left, boolType, !inferrer.isTopLevel,
         isVoidAllowed: false);
-    Expression left = inferrer.ensureAssignableResult(boolType, leftResult);
+    Expression left =
+        inferrer.ensureAssignableResult(boolType, leftResult).expression;
     node.left = left..parent = node;
     inferrer.flowAnalysis.logicalBinaryOp_rightBegin(node.left, node,
         isAnd: node.operatorEnum == LogicalExpressionOperator.AND);
     ExpressionInferenceResult rightResult = inferrer.inferExpression(
         node.right, boolType, !inferrer.isTopLevel,
         isVoidAllowed: false);
-    Expression right = inferrer.ensureAssignableResult(boolType, rightResult);
+    Expression right =
+        inferrer.ensureAssignableResult(boolType, rightResult).expression;
     node.right = right..parent = node;
     inferrer.flowAnalysis.logicalBinaryOp_end(node, node.right,
         isAnd: node.operatorEnum == LogicalExpressionOperator.AND);
@@ -2390,7 +2391,7 @@
           entry.condition, boolType, typeChecksNeeded,
           isVoidAllowed: false);
       Expression condition =
-          inferrer.ensureAssignableResult(boolType, conditionResult);
+          inferrer.ensureAssignableResult(boolType, conditionResult).expression;
       entry.condition = condition..parent = entry;
       inferrer.flowAnalysis.ifStatement_thenBegin(condition, entry);
       // Note that this recursive invocation of inferMapEntry will add two types
@@ -2570,16 +2571,18 @@
       ExpressionInferenceResult keyResult = inferrer.inferExpression(
           entry.key, inferredKeyType, true,
           isVoidAllowed: true);
-      Expression key = inferrer.ensureAssignableResult(
-          inferredKeyType, keyResult,
-          isVoidAllowed: inferredKeyType is VoidType);
+      Expression key = inferrer
+          .ensureAssignableResult(inferredKeyType, keyResult,
+              isVoidAllowed: inferredKeyType is VoidType)
+          .expression;
       entry.key = key..parent = entry;
       ExpressionInferenceResult valueResult = inferrer.inferExpression(
           entry.value, inferredValueType, true,
           isVoidAllowed: true);
-      Expression value = inferrer.ensureAssignableResult(
-          inferredValueType, valueResult,
-          isVoidAllowed: inferredValueType is VoidType);
+      Expression value = inferrer
+          .ensureAssignableResult(inferredValueType, valueResult,
+              isVoidAllowed: inferredValueType is VoidType)
+          .expression;
       entry.value = value..parent = entry;
       actualTypes.add(keyResult.inferredType);
       actualTypes.add(valueResult.inferredType);
@@ -2938,9 +2941,10 @@
         inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
     ExpressionInferenceResult operandResult =
         inferrer.inferExpression(node.operand, boolType, !inferrer.isTopLevel);
-    Expression operand = inferrer.ensureAssignableResult(
-        boolType, operandResult,
-        fileOffset: node.fileOffset);
+    Expression operand = inferrer
+        .ensureAssignableResult(boolType, operandResult,
+            fileOffset: node.fileOffset)
+        .expression;
     node.operand = operand..parent = node;
     inferrer.flowAnalysis.logicalNot_end(node, node.operand);
     return new ExpressionInferenceResult(boolType, node);
@@ -3138,10 +3142,10 @@
         node.binaryName,
         node.rhs,
         null);
-    DartType binaryType = binaryResult.inferredType;
 
-    Expression binary =
-        inferrer.ensureAssignableResult(writeType, binaryResult);
+    binaryResult = inferrer.ensureAssignableResult(writeType, binaryResult);
+    DartType binaryType = binaryResult.inferredType;
+    Expression binary = binaryResult.expression;
 
     Expression write = _computePropertySet(node.writeOffset, writeReceiver,
         receiverType, node.propertyName, writeTarget, binary,
@@ -3198,7 +3202,8 @@
         .inferExpression(node.rhs, writeContext, true, isVoidAllowed: true);
     inferrer.flowAnalysis.ifNullExpression_end();
 
-    Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult);
+    rhsResult = inferrer.ensureAssignableResult(writeContext, rhsResult);
+    Expression rhs = rhsResult.expression;
 
     DartType writeType = rhsResult.inferredType;
     Expression write = _computePropertySet(node.writeOffset, writeReceiver,
@@ -3329,7 +3334,8 @@
     ExpressionInferenceResult indexResult = inferrer
         .inferExpression(node.index, indexType, true, isVoidAllowed: true);
 
-    Expression index = inferrer.ensureAssignableResult(indexType, indexResult);
+    Expression index =
+        inferrer.ensureAssignableResult(indexType, indexResult).expression;
 
     ExpressionInferenceResult replacement = _computeIndexGet(
         node.fileOffset,
@@ -3370,7 +3376,8 @@
     ExpressionInferenceResult indexResult = inferrer
         .inferExpression(node.index, indexType, true, isVoidAllowed: true);
 
-    Expression index = inferrer.ensureAssignableResult(indexType, indexResult);
+    Expression index =
+        inferrer.ensureAssignableResult(indexType, indexResult).expression;
 
     VariableDeclaration? indexVariable;
     if (!node.forEffect && !isPureExpression(index)) {
@@ -3380,7 +3387,8 @@
 
     ExpressionInferenceResult valueResult = inferrer
         .inferExpression(node.value, valueType, true, isVoidAllowed: true);
-    Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+    valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+    Expression value = valueResult.expression;
 
     VariableDeclaration? valueVariable;
     Expression? returnedValue;
@@ -3437,7 +3445,8 @@
     ExpressionInferenceResult indexResult = inferrer
         .inferExpression(node.index, indexType, true, isVoidAllowed: true);
 
-    Expression index = inferrer.ensureAssignableResult(indexType, indexResult);
+    Expression index =
+        inferrer.ensureAssignableResult(indexType, indexResult).expression;
 
     VariableDeclaration? indexVariable;
     if (!isPureExpression(index)) {
@@ -3447,7 +3456,8 @@
 
     ExpressionInferenceResult valueResult = inferrer
         .inferExpression(node.value, valueType, true, isVoidAllowed: true);
-    Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+    valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+    Expression value = valueResult.expression;
 
     VariableDeclaration? valueVariable;
     Expression returnedValue;
@@ -3506,8 +3516,9 @@
     DartType receiverType = inferrer.getExtensionReceiverType(
         node.extension, extensionTypeArguments);
 
-    Expression receiver =
-        inferrer.ensureAssignableResult(receiverType, receiverResult);
+    Expression receiver = inferrer
+        .ensureAssignableResult(receiverType, receiverResult)
+        .expression;
 
     VariableDeclaration? receiverVariable;
     if (!isPureExpression(receiver)) {
@@ -3524,11 +3535,13 @@
     ExpressionInferenceResult indexResult = inferrer
         .inferExpression(node.index, indexType, true, isVoidAllowed: true);
 
-    Expression index = inferrer.ensureAssignableResult(indexType, indexResult);
+    Expression index =
+        inferrer.ensureAssignableResult(indexType, indexResult).expression;
 
     ExpressionInferenceResult valueResult = inferrer
         .inferExpression(node.value, valueType, true, isVoidAllowed: true);
-    Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+    valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+    Expression value = valueResult.expression;
 
     VariableDeclaration? valueVariable;
     Expression returnedValue;
@@ -3643,7 +3656,8 @@
 
     ExpressionInferenceResult valueResult = inferrer
         .inferExpression(node.value, valueType, true, isVoidAllowed: true);
-    Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+    valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+    Expression value = valueResult.expression;
     inferrer.flowAnalysis.ifNullExpression_end();
 
     DartType nonNullableReadType = readType.toNonNull();
@@ -3819,7 +3833,8 @@
     inferrer.flowAnalysis.ifNullExpression_rightBegin(read, readType);
     ExpressionInferenceResult valueResult = inferrer
         .inferExpression(node.value, valueType, true, isVoidAllowed: true);
-    Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+    valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+    Expression value = valueResult.expression;
     inferrer.flowAnalysis.ifNullExpression_end();
 
     DartType nonNullableReadType = readType.toNonNull();
@@ -3921,8 +3936,9 @@
     DartType receiverType = inferrer.getExtensionReceiverType(
         node.extension, extensionTypeArguments);
 
-    Expression receiver =
-        inferrer.ensureAssignableResult(receiverType, receiverResult);
+    Expression receiver = inferrer
+        .ensureAssignableResult(receiverType, receiverResult)
+        .expression;
 
     VariableDeclaration? receiverVariable;
     Expression readReceiver;
@@ -3989,7 +4005,8 @@
 
     ExpressionInferenceResult valueResult = inferrer
         .inferExpression(node.value, valueType, true, isVoidAllowed: true);
-    Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+    valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+    Expression value = valueResult.expression;
     inferrer.flowAnalysis.ifNullExpression_end();
 
     DartType nonNullableReadType = readType.toNonNull();
@@ -4135,7 +4152,7 @@
     }
     DartType rightType =
         inferrer.getPositionalParameterTypeForTarget(equalsTarget, leftType, 0);
-    right = inferrer.ensureAssignableResult(
+    rightResult = inferrer.ensureAssignableResult(
         rightType.withDeclaredNullability(inferrer.library.nullable),
         rightResult,
         errorTemplate: templateArgumentTypeNotAssignable,
@@ -4146,6 +4163,7 @@
             templateArgumentTypeNotAssignableNullabilityNull,
         nullabilityNullTypeErrorTemplate:
             templateArgumentTypeNotAssignableNullabilityNullType);
+    right = rightResult.expression;
 
     if (equalsTarget.isInstanceMember || equalsTarget.isObjectMember) {
       FunctionType functionType =
@@ -4239,7 +4257,8 @@
               .createBinary(fileOffset, left, binaryName, right));
     }
 
-    right = inferrer.ensureAssignableResult(rightType, rightResult);
+    rightResult = inferrer.ensureAssignableResult(rightType, rightResult);
+    right = rightResult.expression;
 
     if (isSpecialCasedBinaryOperator) {
       binaryType = inferrer.typeSchemaEnvironment
@@ -5106,15 +5125,15 @@
         node.binaryName,
         node.rhs,
         null);
-    Expression binary = binaryResult.expression;
-    DartType binaryType = binaryResult.inferredType;
 
     writeIndex = inferrer.ensureAssignable(
         writeIndexType, indexResult.inferredType, writeIndex,
         whyNotPromoted: whyNotPromotedIndex);
 
-    binary = inferrer.ensureAssignable(valueType, binaryType, binary,
+    binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
         fileOffset: node.fileOffset);
+    Expression binary = binaryResult.expression;
+    DartType binaryType = binaryResult.inferredType;
 
     VariableDeclaration? valueVariable;
     Expression valueExpression;
@@ -5250,12 +5269,12 @@
         node.binaryName,
         node.rhs,
         null);
+
+    binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
+        fileOffset: node.fileOffset);
     Expression binary = binaryResult.expression;
     DartType binaryType = binaryResult.inferredType;
 
-    binary = inferrer.ensureAssignable(valueType, binaryType, binary,
-        fileOffset: node.fileOffset);
-
     VariableDeclaration? valueVariable;
     Expression valueExpression;
     if (node.forEffect || node.forPostIncDec) {
@@ -5405,20 +5424,15 @@
         node.binaryName,
         node.rhs,
         null);
+
+    binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
+        fileOffset: node.fileOffset);
     Expression binary = binaryResult.expression;
     DartType binaryType = binaryResult.inferredType;
 
     writeIndex = inferrer.ensureAssignable(
         writeIndexType, indexResult.inferredType, writeIndex);
 
-    Expression binaryReplacement = inferrer.ensureAssignable(
-        valueType, binaryType, binary,
-        fileOffset: node.fileOffset);
-    // ignore: unnecessary_null_comparison
-    if (binaryReplacement != null) {
-      binary = binaryReplacement;
-    }
-
     VariableDeclaration? valueVariable;
     Expression valueExpression;
     if (node.forEffect || node.forPostIncDec) {
@@ -5511,8 +5525,9 @@
     DartType receiverType = inferrer.getExtensionReceiverType(
         node.extension, extensionTypeArguments);
 
-    Expression receiver =
-        inferrer.ensureAssignableResult(receiverType, receiverResult);
+    Expression receiver = inferrer
+        .ensureAssignableResult(receiverType, receiverResult)
+        .expression;
 
     VariableDeclaration? receiverVariable;
     Expression readReceiver;
@@ -5587,13 +5602,12 @@
         node.rhs,
         null);
 
-    Expression binary = binaryResult.expression;
-    DartType binaryType = binaryResult.inferredType;
-
     writeIndex = inferrer.ensureAssignable(
         writeIndexType, indexResult.inferredType, writeIndex);
-    binary = inferrer.ensureAssignable(valueType, binaryType, binary,
+    binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
         fileOffset: node.fileOffset);
+    Expression binary = binaryResult.expression;
+    DartType binaryType = binaryResult.inferredType;
 
     VariableDeclaration? valueVariable;
     Expression valueExpression;
@@ -5721,9 +5735,10 @@
     DartType writeContext = inferrer.getSetterType(target, receiverType);
     ExpressionInferenceResult rhsResult = inferrer
         .inferExpression(node.value, writeContext, true, isVoidAllowed: true);
-    DartType rhsType = rhsResult.inferredType;
-    Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult,
+    rhsResult = inferrer.ensureAssignableResult(writeContext, rhsResult,
         fileOffset: node.fileOffset, isVoidAllowed: writeContext is VoidType);
+    Expression rhs = rhsResult.expression;
+    DartType rhsType = rhsResult.inferredType;
 
     Expression replacement = _computePropertySet(
         node.fileOffset, receiver, receiverType, node.name, target, rhs,
@@ -5777,7 +5792,8 @@
 
     ExpressionInferenceResult valueResult = inferrer
         .inferExpression(node.value, valueType, true, isVoidAllowed: true);
-    Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+    valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+    Expression value = valueResult.expression;
 
     Expression write = _computePropertySet(node.writeOffset, writeReceiver,
         nonNullReceiverType, node.name, writeTarget, value,
@@ -6023,8 +6039,9 @@
     TypeInferenceEngine.resolveInferenceNode(writeMember);
     ExpressionInferenceResult rhsResult = inferrer
         .inferExpression(node.value, writeContext, true, isVoidAllowed: true);
-    Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult,
+    rhsResult = inferrer.ensureAssignableResult(writeContext, rhsResult,
         fileOffset: node.fileOffset, isVoidAllowed: writeContext is VoidType);
+    Expression rhs = rhsResult.expression;
     node.value = rhs..parent = node;
     DartType rhsType = rhsResult.inferredType;
     return new ExpressionInferenceResult(rhsType, node);
@@ -6166,8 +6183,9 @@
     }
     ExpressionInferenceResult rhsResult = inferrer
         .inferExpression(node.value, writeContext, true, isVoidAllowed: true);
-    Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult,
+    rhsResult = inferrer.ensureAssignableResult(writeContext, rhsResult,
         fileOffset: node.fileOffset, isVoidAllowed: writeContext is VoidType);
+    Expression rhs = rhsResult.expression;
     node.value = rhs..parent = node;
     return new ExpressionInferenceResult(rhsResult.inferredType, node);
   }
@@ -6447,10 +6465,11 @@
     ExpressionInferenceResult rhsResult = inferrer.inferExpression(
         node.value, promotedType ?? declaredOrInferredType, true,
         isVoidAllowed: true);
-    Expression rhs = inferrer.ensureAssignableResult(
+    rhsResult = inferrer.ensureAssignableResult(
         declaredOrInferredType, rhsResult,
         fileOffset: node.fileOffset,
         isVoidAllowed: declaredOrInferredType is VoidType);
+    Expression rhs = rhsResult.expression;
     inferrer.flowAnalysis
         .write(node, variable, rhsResult.inferredType, rhsResult.expression);
     DartType resultType = rhsResult.inferredType;
@@ -6551,9 +6570,10 @@
             isLate: node.isLate,
             isImplicitlyTyped: node.isImplicitlyTyped);
       }
-      Expression initializer = inferrer.ensureAssignableResult(
+      initializerResult = inferrer.ensureAssignableResult(
           node.type, initializerResult,
           fileOffset: node.fileOffset, isVoidAllowed: node.type is VoidType);
+      Expression initializer = initializerResult.expression;
       node.initializer = initializer..parent = node;
     }
     if (!inferrer.isTopLevel) {
@@ -6821,8 +6841,9 @@
     ExpressionInferenceResult conditionResult = inferrer.inferExpression(
         node.condition, expectedType, !inferrer.isTopLevel,
         isVoidAllowed: false);
-    Expression condition =
-        inferrer.ensureAssignableResult(expectedType, conditionResult);
+    Expression condition = inferrer
+        .ensureAssignableResult(expectedType, conditionResult)
+        .expression;
     node.condition = condition..parent = node;
     inferrer.flowAnalysis.whileStatement_bodyBegin(node, node.condition);
     StatementInferenceResult bodyResult = inferrer.inferStatement(node.body);
@@ -6992,11 +7013,8 @@
 
   ExpressionInferenceResult visitParenthesized(
       ParenthesizedExpression node, DartType typeContext) {
-    ExpressionInferenceResult result = inferrer.inferExpression(
-        node.expression, typeContext, true,
+    return inferrer.inferExpression(node.expression, typeContext, true,
         isVoidAllowed: true);
-    return new ExpressionInferenceResult(
-        result.inferredType, result.expression);
   }
 
   void reportNonNullableInNullAwareWarningIfNeeded(
diff --git a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
index 6904eb1..5ccc48d 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
@@ -925,9 +925,10 @@
         ? inferrer.wrapType(_yieldElementContext,
             inferrer.coreTypes.iterableClass, inferrer.library.nonNullable)
         : _yieldElementContext;
-    Expression expression = inferrer.ensureAssignableResult(
-        expectedType, expressionResult,
-        fileOffset: node.fileOffset);
+    Expression expression = inferrer
+        .ensureAssignableResult(expectedType, expressionResult,
+            fileOffset: node.fileOffset)
+        .expression;
     node.expression = expression..parent = node;
     DartType type = expressionResult.inferredType;
     if (!identical(expressionResult.expression, expression)) {
@@ -1057,9 +1058,10 @@
             inferrer.coreTypes.streamClass, inferrer.library.nonNullable)
         : _yieldElementContext;
 
-    Expression expression = inferrer.ensureAssignableResult(
-        expectedType, expressionResult,
-        fileOffset: node.fileOffset);
+    Expression expression = inferrer
+        .ensureAssignableResult(expectedType, expressionResult,
+            fileOffset: node.fileOffset)
+        .expression;
     node.expression = expression..parent = node;
     DartType type = expressionResult.inferredType;
     if (!identical(expressionResult.expression, expression)) {
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 14e18d1..e5b0ec9 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -464,29 +464,6 @@
         .isSubtypeWhenIgnoringNullabilities();
   }
 
-  Expression ensureAssignableResult(
-      DartType expectedType, ExpressionInferenceResult result,
-      {int? fileOffset,
-      bool isVoidAllowed: false,
-      Template<Message Function(DartType, DartType, bool)>? errorTemplate,
-      Template<Message Function(DartType, DartType, bool)>?
-          nullabilityErrorTemplate,
-      Template<Message Function(DartType, bool)>? nullabilityNullErrorTemplate,
-      Template<Message Function(DartType, DartType, bool)>?
-          nullabilityNullTypeErrorTemplate,
-      Template<Message Function(DartType, DartType, DartType, DartType, bool)>?
-          nullabilityPartErrorTemplate}) {
-    return ensureAssignable(
-        expectedType, result.inferredType, result.expression,
-        fileOffset: fileOffset,
-        isVoidAllowed: isVoidAllowed,
-        errorTemplate: errorTemplate,
-        nullabilityErrorTemplate: nullabilityErrorTemplate,
-        nullabilityNullErrorTemplate: nullabilityNullErrorTemplate,
-        nullabilityNullTypeErrorTemplate: nullabilityNullTypeErrorTemplate,
-        nullabilityPartErrorTemplate: nullabilityPartErrorTemplate);
-  }
-
   /// Ensures that [expressionType] is assignable to [contextType].
   ///
   /// Checks whether [expressionType] can be assigned to the greatest closure of
@@ -506,7 +483,40 @@
   /// before returned and therefore shouldn't be checked to be a `Future`
   /// directly.
   Expression ensureAssignable(
-      DartType contextType, DartType expressionType, Expression expression,
+      DartType expectedType, DartType expressionType, Expression expression,
+      {int? fileOffset,
+      DartType? declaredContextType,
+      DartType? runtimeCheckedType,
+      bool isVoidAllowed: false,
+      Template<Message Function(DartType, DartType, bool)>? errorTemplate,
+      Template<Message Function(DartType, DartType, bool)>?
+          nullabilityErrorTemplate,
+      Template<Message Function(DartType, bool)>? nullabilityNullErrorTemplate,
+      Template<Message Function(DartType, DartType, bool)>?
+          nullabilityNullTypeErrorTemplate,
+      Template<Message Function(DartType, DartType, DartType, DartType, bool)>?
+          nullabilityPartErrorTemplate,
+      Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
+    return ensureAssignableResult(expectedType,
+            new ExpressionInferenceResult(expressionType, expression),
+            fileOffset: fileOffset,
+            declaredContextType: declaredContextType,
+            runtimeCheckedType: runtimeCheckedType,
+            isVoidAllowed: isVoidAllowed,
+            errorTemplate: errorTemplate,
+            nullabilityErrorTemplate: nullabilityErrorTemplate,
+            nullabilityNullErrorTemplate: nullabilityNullErrorTemplate,
+            nullabilityNullTypeErrorTemplate: nullabilityNullTypeErrorTemplate,
+            nullabilityPartErrorTemplate: nullabilityPartErrorTemplate,
+            whyNotPromoted: whyNotPromoted)
+        .expression;
+  }
+
+  /// Same as [ensureAssignable], but accepts an [ExpressionInferenceResult]
+  /// rather than an expression and a type separately.  If no change is made,
+  /// [inferenceResult] is returned unchanged.
+  ExpressionInferenceResult ensureAssignableResult(
+      DartType contextType, ExpressionInferenceResult inferenceResult,
       {int? fileOffset,
       DartType? declaredContextType,
       DartType? runtimeCheckedType,
@@ -548,35 +558,37 @@
     // We don't need to insert assignability checks when doing top level type
     // inference since top level type inference only cares about the type that
     // is inferred (the kernel code is discarded).
-    if (isTopLevel) return expression;
+    if (isTopLevel) return inferenceResult;
 
-    fileOffset ??= expression.fileOffset;
+    fileOffset ??= inferenceResult.expression.fileOffset;
     contextType = computeGreatestClosure(contextType);
 
     DartType initialContextType = runtimeCheckedType ?? contextType;
 
     Template<Message Function(DartType, DartType, bool)>?
-        preciseTypeErrorTemplate = _getPreciseTypeErrorTemplate(expression);
+        preciseTypeErrorTemplate =
+        _getPreciseTypeErrorTemplate(inferenceResult.expression);
     AssignabilityResult assignabilityResult = _computeAssignabilityKind(
-        contextType, expressionType,
+        contextType, inferenceResult.inferredType,
         isNonNullableByDefault: isNonNullableByDefault,
         isVoidAllowed: isVoidAllowed,
         isExpressionTypePrecise: preciseTypeErrorTemplate != null);
 
     if (assignabilityResult.needsTearOff) {
-      TypedTearoff typedTearoff =
-          _tearOffCall(expression, expressionType as InterfaceType, fileOffset);
-      expression = typedTearoff.tearoff;
-      expressionType = typedTearoff.tearoffType;
+      TypedTearoff typedTearoff = _tearOffCall(inferenceResult.expression,
+          inferenceResult.inferredType as InterfaceType, fileOffset);
+      inferenceResult = new ExpressionInferenceResult(
+          typedTearoff.tearoffType, typedTearoff.tearoff);
     }
     if (assignabilityResult.implicitInstantiation != null) {
-      ExpressionInferenceResult instantiationResult =
-          _applyImplicitInstantiation(assignabilityResult.implicitInstantiation,
-              expressionType, expression);
-      expression = instantiationResult.expression;
-      expressionType = instantiationResult.inferredType;
+      inferenceResult = _applyImplicitInstantiation(
+          assignabilityResult.implicitInstantiation,
+          inferenceResult.inferredType,
+          inferenceResult.expression);
     }
 
+    DartType expressionType = inferenceResult.inferredType;
+    Expression expression = inferenceResult.expression;
     Expression result;
     switch (assignabilityResult.kind) {
       case AssignabilityKind.assignable:
@@ -673,8 +685,10 @@
 
     if (!identical(result, expression)) {
       flowAnalysis.forwardExpression(result, expression);
+      return new ExpressionInferenceResult(expressionType, result);
+    } else {
+      return inferenceResult;
     }
-    return result;
   }
 
   Expression _wrapTearoffErrorExpression(Expression expression,
@@ -2058,11 +2072,10 @@
     this.helper = helper;
     ExpressionInferenceResult initializerResult =
         inferExpression(initializer, declaredType, true, isVoidAllowed: true);
-    initializer = ensureAssignableResult(declaredType, initializerResult,
+    initializerResult = ensureAssignableResult(declaredType, initializerResult,
         isVoidAllowed: declaredType is VoidType);
     this.helper = null;
-    return new ExpressionInferenceResult(
-        initializerResult.inferredType, initializer);
+    return initializerResult;
   }
 
   @override
@@ -4003,7 +4016,7 @@
     ExpressionInferenceResult result =
         inferExpression(initializer, declaredType, true);
     if (hasDeclaredInitializer) {
-      initializer = ensureAssignableResult(declaredType, result);
+      initializer = ensureAssignableResult(declaredType, result).expression;
     }
     this.helper = null;
     return initializer;
diff --git a/tests/language/call/implicit_tearoff_assignment_test.dart b/tests/language/call/implicit_tearoff_assignment_test.dart
new file mode 100644
index 0000000..c3d4d58
--- /dev/null
+++ b/tests/language/call/implicit_tearoff_assignment_test.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This test ensures that when a `.call` tearoff occurs on the right hand side
+// of an assignment, the resulting expression has an appropriate type.
+
+import "package:expect/expect.dart";
+
+import '../static_type_helper.dart';
+
+dynamic _topLevelPropertySetValue;
+
+set topLevelProperty(void Function() value) {
+  Expect.isNull(_topLevelPropertySetValue);
+  _topLevelPropertySetValue = value;
+}
+
+class C {
+  static dynamic _staticPropertySetValue;
+
+  void call() {}
+
+  static set staticProperty(void Function() value) {
+    Expect.isNull(_staticPropertySetValue);
+    _staticPropertySetValue = value;
+  }
+}
+
+class Base {
+  late final dynamic _basePropertySetValue;
+
+  set baseProperty(void Function() value) {
+    _basePropertySetValue = value;
+  }
+}
+
+class Derived extends Base {
+  late final dynamic _indexSetValue;
+  late final dynamic _instanceSetValue;
+
+  operator []=(int index, void Function() value) {
+    _indexSetValue = value;
+  }
+
+  set instanceProperty(void Function() value) {
+    _instanceSetValue = value;
+  }
+
+  void testSuperPropertySet() {
+    Expect.type<void Function()>((super.baseProperty = C())
+      ..expectStaticType<Exactly<void Function()>>());
+    Expect.type<void Function()>(super._basePropertySetValue);
+  }
+}
+
+class Extended {
+  late final dynamic _extensionIndexSetValue;
+  late final dynamic _extensionPropertySetValue;
+}
+
+extension on Extended {
+  operator []=(int index, void Function() value) {
+    _extensionIndexSetValue = value;
+  }
+
+  set extensionProperty(void Function() value) {
+    _extensionPropertySetValue = value;
+  }
+}
+
+void testExtensionIndexSet() {
+  Extended e = Extended();
+  Expect.type<void Function()>(
+      (e[0] = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(e._extensionIndexSetValue);
+}
+
+void testExtensionSet() {
+  Extended e = Extended();
+  Expect.type<void Function()>((e.extensionProperty = C())
+    ..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(e._extensionPropertySetValue);
+}
+
+void testIndexSet() {
+  Derived d = Derived();
+  Expect.type<void Function()>(
+      (d[0] = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(d._indexSetValue);
+}
+
+void testInstanceSet() {
+  Derived d = Derived();
+  Expect.type<void Function()>(
+      (d.instanceProperty = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(d._instanceSetValue);
+}
+
+void testNullAwarePropertySet() {
+  Derived? d = Derived() as Derived?; // ignore: unnecessary_cast
+  Expect.type<void Function()>((d?.instanceProperty = C())
+    ..expectStaticType<Exactly<void Function()?>>());
+  Expect.type<void Function()>(d!._instanceSetValue);
+}
+
+void testStaticSet() {
+  C._staticPropertySetValue = null;
+  Expect.type<void Function()>(
+      (C.staticProperty = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(C._staticPropertySetValue);
+}
+
+void testTopLevelSet() {
+  _topLevelPropertySetValue = null;
+  Expect.type<void Function()>(
+      (topLevelProperty = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(_topLevelPropertySetValue);
+}
+
+void testVariableSet() {
+  void Function() f;
+  Expect.type<void Function()>(
+      (f = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(f);
+}
+
+main() {
+  testExtensionIndexSet();
+  testExtensionSet();
+  testIndexSet();
+  testInstanceSet();
+  testStaticSet();
+  Derived().testSuperPropertySet();
+  testTopLevelSet();
+  testVariableSet();
+}
diff --git a/tests/language_2/call/implicit_tearoff_assignment_test.dart b/tests/language_2/call/implicit_tearoff_assignment_test.dart
new file mode 100644
index 0000000..87ccd15
--- /dev/null
+++ b/tests/language_2/call/implicit_tearoff_assignment_test.dart
@@ -0,0 +1,144 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This test ensures that when a `.call` tearoff occurs on the right hand side
+// of an assignment, the resulting expression has an appropriate type.
+
+// @dart = 2.9
+
+import "package:expect/expect.dart";
+
+import '../static_type_helper.dart';
+
+dynamic _topLevelPropertySetValue;
+
+set topLevelProperty(void Function() value) {
+  Expect.isNull(_topLevelPropertySetValue);
+  _topLevelPropertySetValue = value;
+}
+
+class C {
+  static dynamic _staticPropertySetValue;
+
+  void call() {}
+
+  static set staticProperty(void Function() value) {
+    Expect.isNull(_staticPropertySetValue);
+    _staticPropertySetValue = value;
+  }
+}
+
+class Base {
+  dynamic _basePropertySetValue;
+
+  set baseProperty(void Function() value) {
+    Expect.isNull(_basePropertySetValue);
+    _basePropertySetValue = value;
+  }
+}
+
+class Derived extends Base {
+  dynamic _indexSetValue;
+  dynamic _instanceSetValue;
+
+  operator []=(int index, void Function() value) {
+    Expect.isNull(_indexSetValue);
+    _indexSetValue = value;
+  }
+
+  set instanceProperty(void Function() value) {
+    Expect.isNull(_instanceSetValue);
+    _instanceSetValue = value;
+  }
+
+  void testSuperPropertySet() {
+    Expect.type<void Function()>((super.baseProperty = C())
+      ..expectStaticType<Exactly<void Function()>>());
+    Expect.type<void Function()>(super._basePropertySetValue);
+  }
+}
+
+class Extended {
+  dynamic _extensionIndexSetValue;
+  dynamic _extensionPropertySetValue;
+}
+
+extension on Extended {
+  operator []=(int index, void Function() value) {
+    Expect.isNull(_extensionIndexSetValue);
+    _extensionIndexSetValue = value;
+  }
+
+  set extensionProperty(void Function() value) {
+    Expect.isNull(_extensionPropertySetValue);
+    _extensionPropertySetValue = value;
+  }
+}
+
+void testExtensionIndexSet() {
+  Extended e = Extended();
+  Expect.type<void Function()>(
+      (e[0] = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(e._extensionIndexSetValue);
+}
+
+void testExtensionSet() {
+  Extended e = Extended();
+  Expect.type<void Function()>((e.extensionProperty = C())
+    ..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(e._extensionPropertySetValue);
+}
+
+void testIndexSet() {
+  Derived d = Derived();
+  Expect.type<void Function()>(
+      (d[0] = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(d._indexSetValue);
+}
+
+void testInstanceSet() {
+  Derived d = Derived();
+  Expect.type<void Function()>(
+      (d.instanceProperty = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(d._instanceSetValue);
+}
+
+void testNullAwarePropertySet() {
+  Derived d = Derived();
+  Expect.type<void Function()>((d?.instanceProperty = C())
+    ..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(d._instanceSetValue);
+}
+
+void testStaticSet() {
+  C._staticPropertySetValue = null;
+  Expect.type<void Function()>(
+      (C.staticProperty = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(C._staticPropertySetValue);
+}
+
+void testTopLevelSet() {
+  _topLevelPropertySetValue = null;
+  Expect.type<void Function()>(
+      (topLevelProperty = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(_topLevelPropertySetValue);
+}
+
+void testVariableSet() {
+  void Function() f;
+  Expect.type<void Function()>(
+      (f = C())..expectStaticType<Exactly<void Function()>>());
+  Expect.type<void Function()>(f);
+}
+
+main() {
+  testExtensionIndexSet();
+  testExtensionSet();
+  testIndexSet();
+  testInstanceSet();
+  testStaticSet();
+  Derived().testSuperPropertySet();
+  testTopLevelSet();
+  testVariableSet();
+}