[cfe] Adjust type schema closures in UP and DOWN as per spec
See breaking change request https://github.com/dart-lang/sdk/issues/56466 Closes https://github.com/dart-lang/sdk/issues/56466 Change-Id: I7b04821da92b8b41cb8610e701fa7e7905ff2487 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/375121 Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
committed by
Commit Queue
parent
fe88cdb5aa
commit
b6b375b2cb
@@ -58,6 +58,17 @@ number, like `.`, `x`, or the `e` in scientific notation.
|
||||
|
||||
### Tools
|
||||
|
||||
#### CFE
|
||||
|
||||
- **Breaking Change** [#56065][]: The implementation of the UP and
|
||||
DOWN algorithms in the CFE are changed to match the specification
|
||||
and the corresponding implementations in the Analyzer. The upper and
|
||||
lower closures of type schemas are now computed just before they are
|
||||
passed into the subtype testing procedure instead of at the very
|
||||
beginning of the UP and DOWN algorithms.
|
||||
|
||||
[#56065]: https://github.com/dart-lang/sdk/issues/56065
|
||||
|
||||
#### Wasm compiler (dart2wasm)
|
||||
|
||||
- The condition `dart.library.js` is now false on conditional imports in
|
||||
|
||||
@@ -9,18 +9,28 @@ import 'type_schema.dart' show UnknownType;
|
||||
import 'type_schema_elimination.dart';
|
||||
|
||||
mixin TypeSchemaStandardBounds on StandardBounds {
|
||||
@override
|
||||
DartType greatestClosureForLowerBound(DartType typeSchema) {
|
||||
// - We replace all uses of `T1 <: T2` in the `DOWN` algorithm by `S1 <:
|
||||
// S2` where `Si` is the greatest closure of `Ti` with respect to `_`.
|
||||
return greatestClosure(typeSchema, coreTypes.objectNullableRawType,
|
||||
const NeverType.nonNullable());
|
||||
}
|
||||
|
||||
@override
|
||||
DartType leastClosureForUpperBound(DartType typeSchema) {
|
||||
// - We replace all uses of `T1 <: T2` in the `UP` algorithm by `S1 <: S2`
|
||||
// where `Si` is the least closure of `Ti` with respect to `_`.
|
||||
return leastClosure(typeSchema, coreTypes.objectNullableRawType,
|
||||
const NeverType.nonNullable());
|
||||
}
|
||||
|
||||
@override
|
||||
DartType getNullabilityAwareStandardLowerBoundInternal(
|
||||
DartType type1, DartType type2) {
|
||||
// - We add the axiom that `DOWN(T, _) == T` and the symmetric version.
|
||||
// - We replace all uses of `T1 <: T2` in the `DOWN` algorithm by `S1 <:
|
||||
// S2` where `Si` is the greatest closure of `Ti` with respect to `_`.
|
||||
if (type1 is UnknownType) return type2;
|
||||
if (type2 is UnknownType) return type1;
|
||||
type1 = greatestClosure(
|
||||
type1, coreTypes.objectNullableRawType, const NeverType.nonNullable());
|
||||
type2 = greatestClosure(
|
||||
type2, coreTypes.objectNullableRawType, const NeverType.nonNullable());
|
||||
|
||||
return super.getNullabilityAwareStandardLowerBoundInternal(type1, type2);
|
||||
}
|
||||
@@ -43,14 +53,9 @@ mixin TypeSchemaStandardBounds on StandardBounds {
|
||||
DartType getNullabilityAwareStandardUpperBoundInternal(
|
||||
DartType type1, DartType type2) {
|
||||
// - We add the axiom that `UP(T, _) == T` and the symmetric version.
|
||||
// - We replace all uses of `T1 <: T2` in the `UP` algorithm by `S1 <: S2`
|
||||
// where `Si` is the least closure of `Ti` with respect to `_`.
|
||||
if (type1 is UnknownType) return type2;
|
||||
if (type2 is UnknownType) return type1;
|
||||
type1 = leastClosure(
|
||||
type1, coreTypes.objectNullableRawType, const NeverType.nonNullable());
|
||||
type2 = leastClosure(
|
||||
type2, coreTypes.objectNullableRawType, const NeverType.nonNullable());
|
||||
|
||||
return super.getNullabilityAwareStandardUpperBoundInternal(type1, type2);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// 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.
|
||||
|
||||
class A<X extends A<X>> {
|
||||
A(X x);
|
||||
}
|
||||
|
||||
test<Y extends A<Y>>(Y y) {
|
||||
A<Object?> a = new A(y);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A<X extends self::A<self::A::X> = self::A<dynamic>> extends core::Object {
|
||||
constructor •(self::A::X x) → self::A<self::A::X>
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
static method test<Y extends self::A<self::test::Y> = self::A<dynamic>>(self::test::Y y) → dynamic {
|
||||
self::A<core::Object?> a = new self::A::•<self::test::Y>(y);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A<X extends self::A<self::A::X> = self::A<dynamic>> extends core::Object {
|
||||
constructor •(self::A::X x) → self::A<self::A::X>
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
static method test<Y extends self::A<self::test::Y> = self::A<dynamic>>(self::test::Y y) → dynamic {
|
||||
self::A<core::Object?> a = new self::A::•<self::test::Y>(y);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A<X extends self::A<self::A::X> = self::A<dynamic>> extends core::Object {
|
||||
constructor •(self::A::X x) → self::A<self::A::X>
|
||||
;
|
||||
}
|
||||
static method test<Y extends self::A<self::test::Y> = self::A<dynamic>>(self::test::Y y) → dynamic
|
||||
;
|
||||
@@ -0,0 +1,12 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A<X extends self::A<self::A::X> = self::A<dynamic>> extends core::Object {
|
||||
constructor •(self::A::X x) → self::A<self::A::X>
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
static method test<Y extends self::A<self::test::Y> = self::A<dynamic>>(self::test::Y y) → dynamic {
|
||||
self::A<core::Object?> a = new self::A::•<self::test::Y>(y);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class A<X extends A<X>> {
|
||||
A(X x);
|
||||
}
|
||||
|
||||
test<Y extends A<Y>>(Y y) {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A<X extends A<X>> {
|
||||
A(X x);
|
||||
}
|
||||
|
||||
test<Y extends A<Y>>(Y y) {}
|
||||
@@ -569,15 +569,15 @@ mixin StandardBounds {
|
||||
// subtype relation is established.
|
||||
case (_, _)
|
||||
when isSubtypeOf(
|
||||
type1WithoutNullabilityMarker,
|
||||
type2WithoutNullabilityMarker,
|
||||
greatestClosureForLowerBound(type1WithoutNullabilityMarker),
|
||||
greatestClosureForLowerBound(type2WithoutNullabilityMarker),
|
||||
SubtypeCheckMode.withNullabilities):
|
||||
return type1.withDeclaredNullability(intersectNullabilities(
|
||||
type1.declaredNullability, type2.declaredNullability));
|
||||
case (_, _)
|
||||
when isSubtypeOf(
|
||||
type2WithoutNullabilityMarker,
|
||||
type1WithoutNullabilityMarker,
|
||||
greatestClosureForLowerBound(type2WithoutNullabilityMarker),
|
||||
greatestClosureForLowerBound(type1WithoutNullabilityMarker),
|
||||
SubtypeCheckMode.withNullabilities):
|
||||
return type2.withDeclaredNullability(intersectNullabilities(
|
||||
type2.declaredNullability, type1.declaredNullability));
|
||||
@@ -1052,8 +1052,8 @@ mixin StandardBounds {
|
||||
// type if the subtype relation is established.
|
||||
case (_, _)
|
||||
when isSubtypeOf(
|
||||
typeWithoutNullabilityMarker1,
|
||||
typeWithoutNullabilityMarker2,
|
||||
leastClosureForUpperBound(typeWithoutNullabilityMarker1),
|
||||
leastClosureForUpperBound(typeWithoutNullabilityMarker2),
|
||||
SubtypeCheckMode.withNullabilities):
|
||||
// UP(T1, T2) = T2 if T1 <: T2
|
||||
// Note that both types must be interface or extension types at this
|
||||
@@ -1062,8 +1062,8 @@ mixin StandardBounds {
|
||||
uniteNullabilities(type1.nullability, type2.nullability));
|
||||
case (_, _)
|
||||
when isSubtypeOf(
|
||||
typeWithoutNullabilityMarker2,
|
||||
typeWithoutNullabilityMarker1,
|
||||
leastClosureForUpperBound(typeWithoutNullabilityMarker2),
|
||||
leastClosureForUpperBound(typeWithoutNullabilityMarker1),
|
||||
SubtypeCheckMode.withNullabilities):
|
||||
// UP(T1, T2) = T1 if T2 <: T1
|
||||
// Note that both types must be interface or extension types at this
|
||||
@@ -1699,13 +1699,15 @@ mixin StandardBounds {
|
||||
// where B1a is the greatest closure of B1 with respect to X1,
|
||||
// as defined in [inference.md].
|
||||
|
||||
if (isSubtypeOf(type1, type2, SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(leastClosureForUpperBound(type1),
|
||||
leastClosureForUpperBound(type2), SubtypeCheckMode.withNullabilities)) {
|
||||
return type2.withDeclaredNullability(combineNullabilitiesForSubstitution(
|
||||
inner: type2.nullability,
|
||||
outer: uniteNullabilities(
|
||||
type1.declaredNullability, type2.nullability)));
|
||||
}
|
||||
if (isSubtypeOf(type2, type1, SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(leastClosureForUpperBound(type2),
|
||||
leastClosureForUpperBound(type1), SubtypeCheckMode.withNullabilities)) {
|
||||
return type1.withDeclaredNullability(combineNullabilitiesForSubstitution(
|
||||
inner: type1.declaredNullability,
|
||||
outer: uniteNullabilities(
|
||||
@@ -1740,11 +1742,15 @@ mixin StandardBounds {
|
||||
// where B1a is the greatest closure of B1 with respect to X1,
|
||||
// as defined in [inference.md].
|
||||
DartType demoted = type1.left;
|
||||
if (isSubtypeOf(demoted, type2, SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(leastClosureForUpperBound(demoted),
|
||||
leastClosureForUpperBound(type2), SubtypeCheckMode.withNullabilities)) {
|
||||
return type2.withDeclaredNullability(uniteNullabilities(
|
||||
type1.declaredNullability, type2.declaredNullability));
|
||||
}
|
||||
if (isSubtypeOf(type2, demoted, SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(
|
||||
leastClosureForUpperBound(type2),
|
||||
leastClosureForUpperBound(demoted),
|
||||
SubtypeCheckMode.withNullabilities)) {
|
||||
return demoted.withDeclaredNullability(uniteNullabilities(
|
||||
demoted.declaredNullability, type2.declaredNullability));
|
||||
}
|
||||
@@ -2065,10 +2071,12 @@ mixin StandardBounds {
|
||||
// 3. Otherwise return the spec-defined standard upper bound. This will
|
||||
// be an upper bound, might (or might not) be least, and might
|
||||
// (or might not) be a well-formed type.
|
||||
if (isSubtypeOf(type1, type2, SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(leastClosureForUpperBound(type1),
|
||||
leastClosureForUpperBound(type2), SubtypeCheckMode.withNullabilities)) {
|
||||
return type2;
|
||||
}
|
||||
if (isSubtypeOf(type2, type1, SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(leastClosureForUpperBound(type2),
|
||||
leastClosureForUpperBound(type1), SubtypeCheckMode.withNullabilities)) {
|
||||
return type1;
|
||||
}
|
||||
if (identical(type1.classNode, type2.classNode)) {
|
||||
@@ -2163,4 +2171,26 @@ mixin StandardBounds {
|
||||
return const DynamicType();
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the greatest closure of [typeSchema] for subtyping in DOWN.
|
||||
///
|
||||
/// > We add the axiom that DOWN(T, _) == T and the symmetric version.
|
||||
/// > We replace all uses of T1 <: T2 in the DOWN algorithm by S1 <: S2 where
|
||||
/// > Si is the greatest closure of Ti with respect to _.
|
||||
///
|
||||
/// The specification of using the greatest closure in DOWN can be found at
|
||||
/// https://github.com/dart-lang/language/blob/main/resources/type-system/inference.md#upper-bound
|
||||
DartType greatestClosureForLowerBound(DartType typeSchema) => typeSchema;
|
||||
|
||||
/// Compute the least closure of [typeSchema] for subtyping in UP.
|
||||
///
|
||||
/// Taking closures of type schemas in UP is specified as follows:
|
||||
///
|
||||
/// > We add the axiom that UP(T, _) == T and the symmetric version.
|
||||
/// > We replace all uses of T1 <: T2 in the UP algorithm by S1 <: S2 where Si
|
||||
/// > is the least closure of Ti with respect to _.
|
||||
///
|
||||
/// The specification of using the least closure in UP can be found at
|
||||
/// https://github.com/dart-lang/language/blob/main/resources/type-system/inference.md#upper-bound
|
||||
DartType leastClosureForUpperBound(DartType typeSchema) => typeSchema;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user