Promote parameter of == to nullable when invoked.

This is required because otherwise

Object() == null

requires the type signature of Object.operator==(Object o) to be
changed to Object.operator==(Object? o). Which I don't think is the
behavior we want.

Confirmation that this CL is correct has been sent to the language
team. I recommend we land, which will unblock my subtyping CL, and
roll back/readdress later if need be.

Change-Id: I498f9870e7128b2cac3012fff0cb1ab50fcc8df7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104344
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 87d1892..3791919 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -420,9 +420,10 @@
       _checkForUseOfVoidResult(node.rightOperand);
       _checkForNullableDereference(node.leftOperand);
       _checkForNullableDereference(node.rightOperand);
-    } else if (type != TokenType.EQ_EQ &&
-        type != TokenType.BANG_EQ &&
-        type != TokenType.QUESTION_QUESTION) {
+    } else if (type == TokenType.EQ_EQ || type == TokenType.BANG_EQ) {
+      _checkForArgumentTypeNotAssignableForArgument(node.rightOperand,
+          promoteParameterToNullable: true);
+    } else if (type != TokenType.QUESTION_QUESTION) {
       _checkForArgumentTypeNotAssignableForArgument(node.rightOperand);
       _checkForNullableDereference(node.leftOperand);
     } else {
@@ -2264,13 +2265,17 @@
    *
    * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
    */
-  void _checkForArgumentTypeNotAssignableForArgument(Expression argument) {
+  void _checkForArgumentTypeNotAssignableForArgument(Expression argument,
+      {bool promoteParameterToNullable = false}) {
     if (argument == null) {
       return;
     }
 
     ParameterElement staticParameterElement = argument.staticParameterElement;
     DartType staticParameterType = staticParameterElement?.type;
+    if (promoteParameterToNullable && staticParameterType != null) {
+      staticParameterType = _typeSystem.makeNullable(staticParameterType);
+    }
     _checkForArgumentTypeNotAssignableWithExpectedTypes(argument,
         staticParameterType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
   }
diff --git a/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart b/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart
new file mode 100644
index 0000000..35e756e
--- /dev/null
+++ b/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2019, 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.
+import 'dart:async';
+
+// SharedOptions=--enable-experiment=non-nullable
+
+// Test that Object.operator==(Object o) is the signature of ==, but that we can
+// still compare nullable values to Object.
+//
+// This is because a == b is essentially sugar for
+//
+// ```
+// identical(a, null) || identical (b, null)
+//   ? identical(a, b)
+//   : a.operator==(b);
+// ```
+//
+// It should not be required to handle `null` inside the implementation of
+// operator==, but it should not be an error to "assign null" to the parameter
+// of the comparison operator.
+main() {
+  Object o;
+  // Valid comparison.
+  o == null;
+
+  // Caveat: it is NOT that the argument is promoted to non-null. Otherwise,
+  // types which we can't cleanly promote, such as FutureOr<int?>, would not be
+  // assignable in comparisons.
+  FutureOr<int?> foInt;
+
+  // Valid comparison.
+  o == foInt;
+}
+
+class C {
+  // Valid override
+  @override
+  bool operator==(Object other) => identical(other, this);
+}