[cfe] Enforce override check on Object.==

This enforces the override check for when Object.== implements through inheritance.

Closes #42199

Change-Id: I1be1f7dd1b188834e239d2256ad4c312257f2950
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/493340
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther
2026-04-07 03:55:02 -07:00
committed by Commit Queue
parent b6e0d1ad18
commit 22581060b2
6 changed files with 66 additions and 17 deletions
@@ -7,7 +7,6 @@ import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClassHierarchyMembers;
import 'package:kernel/core_types.dart';
import 'package:kernel/names.dart' show equalsName;
import 'package:kernel/reference_from_index.dart'
show IndexedClass, IndexedLibrary;
import 'package:kernel/src/bounds_checks.dart';
@@ -1744,7 +1743,7 @@ class SourceClassBuilder extends ClassBuilderImpl
],
localMember: localMember,
);
} else if (declaredFunction?.typeParameters != null) {
} else if (declaredFunction?.typeParameters.isNotEmpty ?? false) {
// Since the bound of `interfaceFunction!.parameter[i]` may have changed
// during substitution, it can affect the nullabilities of the types in
// the substitution map. The first parameter to
@@ -2088,16 +2087,6 @@ class SourceClassBuilder extends ClassBuilderImpl
if (interfaceSignatureType != null) {
interfaceParameterType = interfaceSignatureType.positionalParameters[i];
}
if (i == 0 &&
declaredMember.name == equalsName &&
declaredParameterType ==
types.hierarchy.coreTypes.objectNonNullableRawType &&
interfaceParameter.type is DynamicType) {
// TODO(johnniwinther): Add check for opt-in overrides of operator ==.
// `operator ==` methods in opt-out classes have type
// `bool Function(dynamic)`.
continue;
}
_checkTypes(
types,
@@ -1,4 +1,19 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/nnbd/issue42199.dart:5:7: Error: The implementation of '==' in the non-abstract class 'A' does not conform to its interface.
// class A {
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:*: Context: The parameter 'other' of the method 'Object.==' has type 'Object', which does not match the corresponding type, 'dynamic', in the overridden method, 'A.=='.
// - 'Object' is from 'dart:core'.
// Change to a supertype of 'dynamic', or, for a covariant parameter, a subtype.
// external bool operator ==(Object other);
// ^
// pkg/front_end/testcases/nnbd/issue42199.dart:6:12: Context: This is the overridden method ('==').
// operator ==(dynamic other);
// ^
//
import self as self;
import "dart:core" as core;
@@ -6,6 +21,6 @@ class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
abstract operator ==(dynamic other) → core::bool;
abstract erroneous operator ==(dynamic other) → core::bool;
}
static method main() → dynamic {}
@@ -1,4 +1,19 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/nnbd/issue42199.dart:5:7: Error: The implementation of '==' in the non-abstract class 'A' does not conform to its interface.
// class A {
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:*: Context: The parameter 'other' of the method 'Object.==' has type 'Object', which does not match the corresponding type, 'dynamic', in the overridden method, 'A.=='.
// - 'Object' is from 'dart:core'.
// Change to a supertype of 'dynamic', or, for a covariant parameter, a subtype.
// external bool operator ==(Object other);
// ^
// pkg/front_end/testcases/nnbd/issue42199.dart:6:12: Context: This is the overridden method ('==').
// operator ==(dynamic other);
// ^
//
import self as self;
import "dart:core" as core;
@@ -6,6 +21,6 @@ class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
abstract operator ==(dynamic other) → core::bool;
abstract erroneous operator ==(dynamic other) → core::bool;
}
static method main() → dynamic {}
@@ -1,11 +1,26 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/nnbd/issue42199.dart:5:7: Error: The implementation of '==' in the non-abstract class 'A' does not conform to its interface.
// class A {
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:*: Context: The parameter 'other' of the method 'Object.==' has type 'Object', which does not match the corresponding type, 'dynamic', in the overridden method, 'A.=='.
// - 'Object' is from 'dart:core'.
// Change to a supertype of 'dynamic', or, for a covariant parameter, a subtype.
// external bool operator ==(Object other);
// ^
// pkg/front_end/testcases/nnbd/issue42199.dart:6:12: Context: This is the overridden method ('==').
// operator ==(dynamic other);
// ^
//
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
abstract operator ==(dynamic other) → core::bool;
abstract erroneous operator ==(dynamic other) → core::bool;
}
static method main() → dynamic
;
@@ -1,4 +1,19 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/nnbd/issue42199.dart:5:7: Error: The implementation of '==' in the non-abstract class 'A' does not conform to its interface.
// class A {
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:*: Context: The parameter 'other' of the method 'Object.==' has type 'Object', which does not match the corresponding type, 'dynamic', in the overridden method, 'A.=='.
// - 'Object' is from 'dart:core'.
// Change to a supertype of 'dynamic', or, for a covariant parameter, a subtype.
// external bool operator ==(Object other);
// ^
// pkg/front_end/testcases/nnbd/issue42199.dart:6:12: Context: This is the overridden method ('==').
// operator ==(dynamic other);
// ^
//
import self as self;
import "dart:core" as core;
@@ -6,6 +21,6 @@ class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
abstract operator ==(dynamic other) → core::bool;
abstract erroneous operator ==(dynamic other) → core::bool;
}
static method main() → dynamic {}
@@ -122,7 +122,7 @@ class DateTime {
}
@patch
bool operator ==(dynamic other) =>
bool operator ==(Object other) =>
other is DateTime &&
_value == other.microsecondsSinceEpoch &&
isUtc == other.isUtc;