[cfe] Use only field values in equality of record constants

Previously the static type of the record literals was also used, which
is incorrect interpretation of the equality on records.

Closes https://github.com/dart-lang/sdk/issues/54491

Change-Id: I12fad33271e53279a3d9c8bcfd2a842ac31988a5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/344701
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Chloe Stefantsova 2024-01-11 13:58:47 +00:00 committed by Commit Queue
parent 28e0ae59ec
commit 1eace401d2
14 changed files with 310 additions and 16 deletions

View file

@ -2266,7 +2266,8 @@ class ConstantsTransformer extends RemovingTransformer {
return makeConstantExpression(new UnevaluatedConstant(node), node);
} else {
Constant constant = constantEvaluator.canonicalize(
new RecordConstant(positional, named, node.recordType));
new RecordConstant.fromTypeContext(
positional, named, staticTypeContext));
return makeConstantExpression(constant, node);
}
}
@ -3110,8 +3111,8 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
new NamedExpression(key, _wrap(named[key]!)),
], node.recordType, isConst: true));
}
return canonicalize(new RecordConstant(
positional, named, env.substituteType(node.recordType) as RecordType));
return canonicalize(new RecordConstant.fromTypeContext(
positional, named, staticTypeContext));
}
@override

View file

@ -24,7 +24,7 @@ static method main() → void {
#C2;
#C4;
#C4;
#C6;
#C5;
}
constants {
@ -32,8 +32,7 @@ constants {
#C2 = self::Foo<core::int*> {a:#C1}
#C3 = (#C1, #C1)
#C4 = self::Generic<core::int*> {record:#C3}
#C5 = (#C1, #C1)
#C6 = self::NotGeneric {record:#C5}
#C5 = self::NotGeneric {record:#C3}
}

View file

@ -24,7 +24,7 @@ static method main() → void {
#C2;
#C4;
#C4;
#C6;
#C5;
}
constants {
@ -32,8 +32,7 @@ constants {
#C2 = self::Foo<core::int*> {a:#C1}
#C3 = (#C1, #C1)
#C4 = self::Generic<core::int*> {record:#C3}
#C5 = (#C1, #C1)
#C6 = self::NotGeneric {record:#C5}
#C5 = self::NotGeneric {record:#C3}
}

View file

@ -24,7 +24,7 @@ static method main() → void {
#C2;
#C4;
#C4;
#C6;
#C5;
}
constants {
@ -32,8 +32,7 @@ constants {
#C2 = self::Foo<core::int*> {a:#C1}
#C3 = (#C1, #C1)
#C4 = self::Generic<core::int*> {record:#C3}
#C5 = (#C1, #C1)
#C6 = self::NotGeneric {record:#C5}
#C5 = self::NotGeneric {record:#C3}
}

View file

@ -0,0 +1,22 @@
// Copyright (c) 2024, 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.
void main() {
const Chk((Ex(1),), eq: (1,));
const Chk((1,), eq: (1,));
const Chk((Ex(1),), eq: (Ex(1),));
const Chk((Ex(1),), eq: (1,));
const Chk(((1 as Ex),), eq: (1,));
const Chk((Ex(1) as int,), eq: (1,));
const Chk((Ex(1),) as (int,), eq: (1,));
const Chk((1,) as (Ex,), eq: (1,));
const Chk(Ex((1,)), eq: (1,));
const Chk(Ex((Ex(1),)), eq: (1,));
}
class Chk {
const Chk(Object? v, {required Object? eq}) :
assert(v == eq, "Not equal ${(v, eq: eq)}");
}
extension type const Ex(Object? value) {}

View file

@ -0,0 +1,42 @@
library;
import self as self;
import "dart:core" as core;
class Chk extends core::Object /*hasConstConstructor*/ {
const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
: assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
;
}
extension type Ex(core::Object? value) {
abstract extension-type-member representation-field get value() → core::Object?;
constructor • = self::Ex|constructor#;
constructor tearoff • = self::Ex|constructor#_#new#tearOff;
}
static method main() → void {
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
}
static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex /* = core::Object? */ {
lowered final self::Ex /* = core::Object? */ #this = value;
return #this;
}
static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex /* = core::Object? */
return self::Ex|constructor#(value);
constants {
#C1 = self::Chk {}
}
Constructor coverage from constants:
org-dartlang-testcase:///issue54491.dart:
- Chk. (from org-dartlang-testcase:///issue54491.dart:18:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)

View file

@ -0,0 +1,42 @@
library;
import self as self;
import "dart:core" as core;
class Chk extends core::Object /*hasConstConstructor*/ {
const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
: assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
;
}
extension type Ex(core::Object? value) {
abstract extension-type-member representation-field get value() → core::Object?;
constructor • = self::Ex|constructor#;
constructor tearoff • = self::Ex|constructor#_#new#tearOff;
}
static method main() → void {
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
}
static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex /* = core::Object? */ {
lowered final self::Ex /* = core::Object? */ #this = value;
return #this;
}
static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex /* = core::Object? */
return self::Ex|constructor#(value);
constants {
#C1 = self::Chk {}
}
Constructor coverage from constants:
org-dartlang-testcase:///issue54491.dart:
- Chk. (from org-dartlang-testcase:///issue54491.dart:18:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)

View file

@ -0,0 +1,8 @@
void main() {}
class Chk {
const Chk(Object? v, {required Object? eq})
: assert(v == eq, "Not equal ${(v, eq: eq)}");
}
extension type const Ex(Object? value) {}

View file

@ -0,0 +1,8 @@
class Chk {
const Chk(Object? v, {required Object? eq})
: assert(v == eq, "Not equal ${(v, eq: eq)}");
}
extension type const Ex(Object? value) {}
void main() {}

View file

@ -0,0 +1,42 @@
library;
import self as self;
import "dart:core" as core;
class Chk extends core::Object /*hasConstConstructor*/ {
const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
: assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
;
}
extension type Ex(core::Object? value) {
abstract extension-type-member representation-field get value() → core::Object?;
constructor • = self::Ex|constructor#;
constructor tearoff • = self::Ex|constructor#_#new#tearOff;
}
static method main() → void {
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
}
static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex /* = core::Object? */ {
lowered final self::Ex /* = core::Object? */ #this = value;
return #this;
}
static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex /* = core::Object? */
return self::Ex|constructor#(value);
constants {
#C1 = self::Chk {}
}
Constructor coverage from constants:
org-dartlang-testcase:///issue54491.dart:
- Chk. (from org-dartlang-testcase:///issue54491.dart:18:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)

View file

@ -0,0 +1,42 @@
library;
import self as self;
import "dart:core" as core;
class Chk extends core::Object /*hasConstConstructor*/ {
const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
: assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
;
}
extension type Ex(core::Object? value) {
abstract extension-type-member representation-field get value() → core::Object?;
constructor • = self::Ex|constructor#;
constructor tearoff • = self::Ex|constructor#_#new#tearOff;
}
static method main() → void {
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
}
static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex /* = core::Object? */ {
lowered final self::Ex /* = core::Object? */ #this = value;
return #this;
}
static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex /* = core::Object? */
return self::Ex|constructor#(value);
constants {
#C1 = self::Chk {}
}
Constructor coverage from constants:
org-dartlang-testcase:///issue54491.dart:
- Chk. (from org-dartlang-testcase:///issue54491.dart:18:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)

View file

@ -0,0 +1,22 @@
library;
import self as self;
import "dart:core" as core;
class Chk extends core::Object /*hasConstConstructor*/ {
const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
: assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
;
}
extension type Ex(core::Object? value) {
abstract extension-type-member representation-field get value() → core::Object?;
constructor • = self::Ex|constructor#;
constructor tearoff • = self::Ex|constructor#_#new#tearOff;
}
static method main() → void
;
static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex /* = core::Object? */ {
lowered final self::Ex /* = core::Object? */ #this = value;
return #this;
}
static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex /* = core::Object? */
return self::Ex|constructor#(value);

View file

@ -0,0 +1,42 @@
library;
import self as self;
import "dart:core" as core;
class Chk extends core::Object /*hasConstConstructor*/ {
const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
: assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
;
}
extension type Ex(core::Object? value) {
abstract extension-type-member representation-field get value() → core::Object?;
constructor • = self::Ex|constructor#;
constructor tearoff • = self::Ex|constructor#_#new#tearOff;
}
static method main() → void {
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
#C1;
}
static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex /* = core::Object? */ {
lowered final self::Ex /* = core::Object? */ #this = value;
return #this;
}
static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex /* = core::Object? */
return self::Ex|constructor#(value);
constants {
#C1 = self::Chk {}
}
Constructor coverage from constants:
org-dartlang-testcase:///issue54491.dart:
- Chk. (from org-dartlang-testcase:///issue54491.dart:18:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)

View file

@ -13969,7 +13969,24 @@ class RecordConstant extends Constant {
/// Named field values, sorted by name.
final Map<String, Constant> named;
/// The static type of the constant.
/// The runtime type of the constant.
///
/// [recordType] is computed from the individual types of the record fields
/// and reflects runtime type of the record constant, as opposed to the
/// static type of the expression that defined the constant.
///
/// The following program shows the distinction between the static and the
/// runtime types of the constant. The static type of the first record in the
/// invocation of `identical` is `(E, String)`, the static type of the second
/// `(int, String)`. The runtime type of both constants is `(int, String)`,
/// and the assertion condition should be satisfied.
///
/// extension type const E(Object? it) {}
///
/// main() {
/// const bool check = identical(const (E(1), "foo"), const (1, "foo"));
/// assert(check);
/// }
final RecordType recordType;
RecordConstant(this.positional, this.named, this.recordType)
@ -13993,6 +14010,16 @@ class RecordConstant extends Constant {
"Named fields of a RecordConstant aren't sorted lexicographically: "
"${named.keys.join(", ")}");
RecordConstant.fromTypeContext(
this.positional, this.named, StaticTypeContext staticTypeContext)
: recordType = new RecordType([
for (Constant constant in positional)
constant.getType(staticTypeContext)
], [
for (var MapEntry(key: name, value: constant) in named.entries)
new NamedType(name, constant.getType(staticTypeContext))
], staticTypeContext.nonNullable);
@override
void visitChildren(Visitor v) {
recordType.accept(v);
@ -14048,14 +14075,13 @@ class RecordConstant extends Constant {
String toString() => "RecordConstant(${toStringInternal()})";
@override
late final int hashCode = _Hash.combineFinish(recordType.hashCode,
_Hash.combineMapHashUnordered(named, _Hash.combineListHash(positional)));
late final int hashCode =
_Hash.combineMapHashUnordered(named, _Hash.combineListHash(positional));
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is RecordConstant &&
other.recordType == recordType &&
listEquals(other.positional, positional) &&
mapEquals(other.named, named));