From 22581060b24ee859b818d854a4f5b4e83b95597d Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Tue, 7 Apr 2026 03:55:02 -0700 Subject: [PATCH] [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 Commit-Queue: Johnni Winther --- .../lib/src/source/source_class_builder.dart | 13 +------------ .../nnbd/issue42199.dart.strong.expect | 17 ++++++++++++++++- .../nnbd/issue42199.dart.strong.modular.expect | 17 ++++++++++++++++- .../nnbd/issue42199.dart.strong.outline.expect | 17 ++++++++++++++++- .../issue42199.dart.strong.transformed.expect | 17 ++++++++++++++++- sdk/lib/_internal/vm_shared/lib/date_patch.dart | 2 +- 6 files changed, 66 insertions(+), 17 deletions(-) diff --git a/pkg/front_end/lib/src/source/source_class_builder.dart b/pkg/front_end/lib/src/source/source_class_builder.dart index a52b2b33d42..d5410abf49e 100644 --- a/pkg/front_end/lib/src/source/source_class_builder.dart +++ b/pkg/front_end/lib/src/source/source_class_builder.dart @@ -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, diff --git a/pkg/front_end/testcases/nnbd/issue42199.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue42199.dart.strong.expect index 18b6ccd97c4..302bd7cc7e4 100644 --- a/pkg/front_end/testcases/nnbd/issue42199.dart.strong.expect +++ b/pkg/front_end/testcases/nnbd/issue42199.dart.strong.expect @@ -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 {} diff --git a/pkg/front_end/testcases/nnbd/issue42199.dart.strong.modular.expect b/pkg/front_end/testcases/nnbd/issue42199.dart.strong.modular.expect index 18b6ccd97c4..302bd7cc7e4 100644 --- a/pkg/front_end/testcases/nnbd/issue42199.dart.strong.modular.expect +++ b/pkg/front_end/testcases/nnbd/issue42199.dart.strong.modular.expect @@ -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 {} diff --git a/pkg/front_end/testcases/nnbd/issue42199.dart.strong.outline.expect b/pkg/front_end/testcases/nnbd/issue42199.dart.strong.outline.expect index 51f49e1d9a8..14d9a041786 100644 --- a/pkg/front_end/testcases/nnbd/issue42199.dart.strong.outline.expect +++ b/pkg/front_end/testcases/nnbd/issue42199.dart.strong.outline.expect @@ -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 ; diff --git a/pkg/front_end/testcases/nnbd/issue42199.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue42199.dart.strong.transformed.expect index 18b6ccd97c4..302bd7cc7e4 100644 --- a/pkg/front_end/testcases/nnbd/issue42199.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/nnbd/issue42199.dart.strong.transformed.expect @@ -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 {} diff --git a/sdk/lib/_internal/vm_shared/lib/date_patch.dart b/sdk/lib/_internal/vm_shared/lib/date_patch.dart index f456e02d582..4235a378c5d 100644 --- a/sdk/lib/_internal/vm_shared/lib/date_patch.dart +++ b/sdk/lib/_internal/vm_shared/lib/date_patch.dart @@ -122,7 +122,7 @@ class DateTime { } @patch - bool operator ==(dynamic other) => + bool operator ==(Object other) => other is DateTime && _value == other.microsecondsSinceEpoch && isUtc == other.isUtc;