Implement NORM for RecordType.
Also adds `RecordType.==` as equality of the shape and field types.
We intentionally don't check that elements are the same.
Change-Id: I4f5ef66a838a410843696f4f569ce3ed36bb7053
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255147
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/normalize.dart b/pkg/analyzer/lib/src/dart/element/normalize.dart
index 136dd8c..5ae6067 100644
--- a/pkg/analyzer/lib/src/dart/element/normalize.dart
+++ b/pkg/analyzer/lib/src/dart/element/normalize.dart
@@ -145,6 +145,15 @@
);
}
+ // NORM(Record(T0, ..., Tn)) = Record(R0, ..., Rn) where Ri is NORM(Ti)
+ if (T is RecordTypeImpl) {
+ return RecordTypeImpl(
+ element2: T.element2,
+ fieldTypes: T.fieldTypes.map(_normalize).toList(),
+ nullabilitySuffix: NullabilitySuffix.none,
+ );
+ }
+
// NORM(R Function<X extends B>(S)) = R1 Function(X extends B1>(S1)
return _functionType(T as FunctionType);
}
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 6361b9a..7a3f98b 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -1012,6 +1012,14 @@
@override
RecordElementImpl get element => element2;
+ @override
+ int get hashCode {
+ return Object.hash(
+ element2.positionalFields,
+ element2.namedFieldsSorted.length,
+ );
+ }
+
@Deprecated('Check element, or use getDisplayString()')
@override
String? get name => null;
@@ -1039,6 +1047,42 @@
}
@override
+ bool operator ==(Object other) {
+ if (identical(other, this)) {
+ return true;
+ }
+
+ if (other is! RecordTypeImpl) {
+ return false;
+ }
+
+ if (other.nullabilitySuffix != nullabilitySuffix) {
+ return false;
+ }
+
+ final thisPositional = positionalFields;
+ final otherPositional = other.positionalFields;
+ if (thisPositional.length != otherPositional.length) {
+ return false;
+ }
+
+ final thisNamed = namedFields;
+ final otherNamed = other.namedFields;
+ if (thisNamed.length != otherNamed.length) {
+ return false;
+ }
+ for (var i = 0; i < thisNamed.length; i++) {
+ final thisField = thisNamed[i];
+ final otherField = otherNamed[i];
+ if (thisField.name != otherField.name) {
+ return false;
+ }
+ }
+
+ return TypeImpl.equalArrays(other.fieldTypes, fieldTypes);
+ }
+
+ @override
R accept<R>(TypeVisitor<R> visitor) {
return visitor.visitRecordType(this);
}
diff --git a/pkg/analyzer/test/src/dart/element/normalize_type_test.dart b/pkg/analyzer/test/src/dart/element/normalize_type_test.dart
index e3c53d6..aa6ea5d 100644
--- a/pkg/analyzer/test/src/dart/element/normalize_type_test.dart
+++ b/pkg/analyzer/test/src/dart/element/normalize_type_test.dart
@@ -338,6 +338,59 @@
check(futureOrQuestion(objectStar), objectStar);
}
+ test_recordType() {
+ _check(
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ ],
+ ),
+ ),
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ ],
+ ),
+ ),
+ );
+
+ _check(
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: futureOrNone(objectNone)),
+ ],
+ ),
+ ),
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: objectNone),
+ ],
+ ),
+ ),
+ );
+
+ _check(
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'foo', type: futureOrNone(objectNone)),
+ ],
+ ),
+ ),
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'foo', type: objectNone),
+ ],
+ ),
+ ),
+ );
+ }
+
/// NORM(T*)
/// * let S be NORM(T)
test_star() {
diff --git a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
index 9a1b70f..4a3a6d0 100644
--- a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
+++ b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
@@ -3501,7 +3501,10 @@
var result = typeSystem.getLeastUpperBound(T1, T2);
var resultStr = _typeString(result);
- expect(resultStr, expectedStr);
+ expect(result, expected, reason: '''
+expected: $expectedStr
+actual: $resultStr
+''');
// Check that the result is an upper bound.
expect(typeSystem.isSubtypeOf(T1, result), true);
@@ -3510,7 +3513,10 @@
// Check for symmetry.
result = typeSystem.getLeastUpperBound(T2, T1);
resultStr = _typeString(result);
- expect(resultStr, expectedStr);
+ expect(result, expected, reason: '''
+expected: $expectedStr
+actual: $resultStr
+''');
}
void _checkLeastUpperBound2(String T1, String T2, String expected) {