mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 12:57:42 +00:00
[kernel] Add NORM implementation
Change-Id: I6292a4247ed0c418f2d3d6fa841ed086894c31c9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134335 Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
parent
3433d8cfc3
commit
535ee87c21
2
DEPS
2
DEPS
|
@ -469,7 +469,7 @@ deps = {
|
|||
"packages": [
|
||||
{
|
||||
"package": "dart/cfe/benchmark_data",
|
||||
"version": "sha1sum:4640fa0bff40726392748d1ad3147e5dd0324ea2",
|
||||
"version": "sha1sum:5b6e6dfa33b85c733cab4e042bf46378984d1544",
|
||||
}
|
||||
],
|
||||
"dep_type": "cipd",
|
||||
|
|
|
@ -257,7 +257,7 @@ class TypeSchemaEnvironmentTest {
|
|||
"Wn*": "Wn extends Tn, Tn extends Never",
|
||||
|
||||
// Null.
|
||||
"Null?": null,
|
||||
"Null": null,
|
||||
};
|
||||
|
||||
static String joinTypeParameters(
|
||||
|
@ -425,8 +425,8 @@ class TypeSchemaEnvironmentTest {
|
|||
|
||||
testLower("<X extends dynamic>() -> void", "<Y extends Object?>() -> void",
|
||||
"<Z extends dynamic>() -> void");
|
||||
testLower("<X extends Null?>() -> void", "<Y extends Never?>() -> void",
|
||||
"<Z extends Null?>() -> void");
|
||||
testLower("<X extends Null>() -> void", "<Y extends Never?>() -> void",
|
||||
"<Z extends Null>() -> void");
|
||||
testLower(
|
||||
"<X extends FutureOr<dynamic>?>() -> void",
|
||||
"<Y extends FutureOr<Object?>>() -> void",
|
||||
|
@ -786,8 +786,8 @@ class TypeSchemaEnvironmentTest {
|
|||
|
||||
testUpper("<X extends dynamic>() -> void", "<Y extends Object?>() -> void",
|
||||
"<Z extends dynamic>() -> void");
|
||||
testUpper("<X extends Null?>() -> void", "<Y extends Never?>() -> void",
|
||||
"<Z extends Null?>() -> void");
|
||||
testUpper("<X extends Null>() -> void", "<Y extends Never?>() -> void",
|
||||
"<Z extends Null>() -> void");
|
||||
testUpper(
|
||||
"<X extends FutureOr<dynamic>?>() -> void",
|
||||
"<Y extends FutureOr<Object?>>() -> void",
|
||||
|
@ -1077,7 +1077,7 @@ class TypeSchemaEnvironmentTest {
|
|||
expect(
|
||||
env.solveTypeConstraint(_makeConstraint(lower: toType("A<unknown>*")),
|
||||
grounded: true),
|
||||
toType("A<Null?>*"));
|
||||
toType("A<Null>*"));
|
||||
|
||||
// Solve(? <: T <: A*) => A*
|
||||
expect(
|
||||
|
@ -1155,7 +1155,7 @@ class TypeSchemaEnvironmentTest {
|
|||
_makeConstraint(
|
||||
lower: toType("B<unknown>*"), upper: toType("A<unknown>*")),
|
||||
grounded: true),
|
||||
toType("B<Null?>*"));
|
||||
toType("B<Null>*"));
|
||||
}
|
||||
|
||||
void test_typeConstraint_default() {
|
||||
|
|
|
@ -61,8 +61,8 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('double*', 'num*');
|
||||
isSubtype('num', 'Object');
|
||||
isSubtype('num*', 'Object');
|
||||
isSubtype('Null?', 'num*');
|
||||
isSubtype('Null?', 'num?');
|
||||
isSubtype('Null', 'num*');
|
||||
isSubtype('Null', 'num?');
|
||||
isSubtype('Never', 'num');
|
||||
isSubtype('Never', 'num*');
|
||||
isSubtype('Never', 'num?');
|
||||
|
@ -75,7 +75,7 @@ abstract class SubtypeTest<T, E> {
|
|||
isNotSubtype('int*', 'Iterable<int*>*');
|
||||
isNotSubtype('Comparable<int*>*', 'Iterable<int*>*');
|
||||
isObliviousSubtype('num?', 'Object');
|
||||
isObliviousSubtype('Null?', 'num');
|
||||
isObliviousSubtype('Null', 'num');
|
||||
isNotSubtype('num', 'Never');
|
||||
|
||||
// Tests for subtypes and supertypes of List.
|
||||
|
@ -91,8 +91,8 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('List<int*>*', 'List<Comparable<Comparable<num*>*>*>*');
|
||||
isSubtype('List<int*>', 'Object');
|
||||
isSubtype('List<int*>*', 'Object');
|
||||
isSubtype('Null?', 'List<int*>*');
|
||||
isSubtype('Null?', 'List<int*>?');
|
||||
isSubtype('Null', 'List<int*>*');
|
||||
isSubtype('Null', 'List<int*>?');
|
||||
isSubtype('Never', 'List<int*>');
|
||||
isSubtype('Never', 'List<int*>*');
|
||||
isSubtype('Never', 'List<int*>?');
|
||||
|
@ -130,7 +130,7 @@ abstract class SubtypeTest<T, E> {
|
|||
isNotSubtype('List<int*>*', 'List<Comparable<int*>*>*');
|
||||
isNotSubtype('List<int*>*', 'List<Comparable<Comparable<int*>*>*>*');
|
||||
isObliviousSubtype('List<int*>?', 'Object');
|
||||
isObliviousSubtype('Null?', 'List<int*>');
|
||||
isObliviousSubtype('Null', 'List<int*>');
|
||||
isNotSubtype('List<int*>', 'Never');
|
||||
|
||||
isObliviousSubtype('T?', 'List<int>',
|
||||
|
@ -141,8 +141,8 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('(num*) ->* int*', '(num*) ->* num*');
|
||||
isSubtype('(num*) ->* int*', '(int*) ->* num*');
|
||||
isNotSubtype('(int*) ->* int*', '(num*) ->* num*');
|
||||
isSubtype('Null?', '(int*) ->* num*');
|
||||
isSubtype('Null?', '(int*) ->? num*');
|
||||
isSubtype('Null', '(int*) ->* num*');
|
||||
isSubtype('Null', '(int*) ->? num*');
|
||||
isSubtype('Never', '(int*) -> num*');
|
||||
isSubtype('Never', '(int*) ->* num*');
|
||||
isSubtype('Never', '(int*) ->? num*');
|
||||
|
@ -152,7 +152,7 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('(num*) ->* num*', 'Object');
|
||||
isSubtype('(num*) -> num*', 'Object');
|
||||
isObliviousSubtype('(num*) ->? num*', 'Object');
|
||||
isObliviousSubtype('Null?', '(int*) -> num*');
|
||||
isObliviousSubtype('Null', '(int*) -> num*');
|
||||
isNotSubtype('(int*) -> num*', 'Never');
|
||||
isNotSubtype('num', '(num) -> num');
|
||||
isNotSubtype('Object', '(num) -> num');
|
||||
|
@ -248,8 +248,8 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('FutureOr<int*>*', 'FutureOr<int*>*');
|
||||
isSubtype('FutureOr<int*>*', 'FutureOr<num*>*');
|
||||
isSubtype('FutureOr<int*>*', 'Object*');
|
||||
isSubtype('Null?', 'FutureOr<num?>');
|
||||
isSubtype('Null?', 'FutureOr<num>?');
|
||||
isSubtype('Null', 'FutureOr<num?>');
|
||||
isSubtype('Null', 'FutureOr<num>?');
|
||||
isSubtype('num?', 'FutureOr<num?>');
|
||||
isSubtype('num?', 'FutureOr<num>?');
|
||||
isSubtype('Future<num>', 'FutureOr<num?>');
|
||||
|
@ -317,8 +317,8 @@ abstract class SubtypeTest<T, E> {
|
|||
isNotSubtype('FutureOr<double*>*', 'int*');
|
||||
isNotSubtype('FutureOr<int*>*', 'Future<num*>*');
|
||||
isNotSubtype('FutureOr<int*>*', 'num*');
|
||||
isSubtype('Null?', 'FutureOr<int*>*');
|
||||
isSubtype('Null?', 'Future<int*>*');
|
||||
isSubtype('Null', 'FutureOr<int*>*');
|
||||
isSubtype('Null', 'Future<int*>*');
|
||||
isSubtype('dynamic', 'FutureOr<dynamic>*');
|
||||
isNotSubtype('dynamic', 'FutureOr<String*>*');
|
||||
isSubtype('void', 'FutureOr<void>*');
|
||||
|
@ -450,13 +450,12 @@ abstract class SubtypeTest<T, E> {
|
|||
isNotSubtype('FutureOr<num>', 'Never');
|
||||
|
||||
// Testing bottom types against an intersection type.
|
||||
isSubtype('Null?', 'T* & num*', typeParameters: 'T extends Object*');
|
||||
isSubtype('Null', 'T* & num*', typeParameters: 'T extends Object*');
|
||||
isSubtype('Never', 'T* & num*', typeParameters: 'T extends Object*');
|
||||
isObliviousSubtype('Null?', 'T & num', typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Null?', 'T & num?',
|
||||
typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Null?', 'T & num', typeParameters: 'T extends Object');
|
||||
isObliviousSubtype('Null?', 'T & S',
|
||||
isObliviousSubtype('Null', 'T & num', typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Null', 'T & num?', typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Null', 'T & num', typeParameters: 'T extends Object');
|
||||
isObliviousSubtype('Null', 'T & S',
|
||||
typeParameters: 'T extends Object?, S extends T');
|
||||
isNotSubtype('T* & num*', 'Never', typeParameters: 'T extends Object*');
|
||||
isSubtype('T', 'Never', typeParameters: 'T extends Never');
|
||||
|
@ -464,19 +463,19 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('bottom', 'T & int', typeParameters: 'T extends Object');
|
||||
|
||||
// Testing bottom types against type-parameter types.
|
||||
isSubtype('Null?', 'T?', typeParameters: 'T extends Object');
|
||||
isSubtype('Null?', 'T?', typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Null?', 'T', typeParameters: 'T extends Object');
|
||||
isObliviousSubtype('Null?', 'T', typeParameters: 'T extends Object?');
|
||||
isSubtype('Null', 'T?', typeParameters: 'T extends Object');
|
||||
isSubtype('Null', 'T?', typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Null', 'T', typeParameters: 'T extends Object');
|
||||
isObliviousSubtype('Null', 'T', typeParameters: 'T extends Object?');
|
||||
isSubtype('Never', 'T?', typeParameters: 'T extends Object');
|
||||
isSubtype('Never', 'T?', typeParameters: 'T extends Object?');
|
||||
isSubtype('Never', 'T', typeParameters: 'T extends Object');
|
||||
isSubtype('Never', 'T', typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Never?', 'T', typeParameters: 'T extends Object?');
|
||||
isSubtype('T', 'Null?', typeParameters: 'T extends Null?');
|
||||
isSubtype('T?', 'Null?', typeParameters: 'T extends Null?');
|
||||
isNotSubtype('T', 'Null?', typeParameters: 'T extends Object');
|
||||
isNotSubtype('T', 'Null?', typeParameters: 'T extends Object?');
|
||||
isSubtype('T', 'Null', typeParameters: 'T extends Null');
|
||||
isSubtype('T?', 'Null', typeParameters: 'T extends Null');
|
||||
isNotSubtype('T', 'Null', typeParameters: 'T extends Object');
|
||||
isNotSubtype('T', 'Null', typeParameters: 'T extends Object?');
|
||||
isSubtype('T', 'Never', typeParameters: 'T extends Never');
|
||||
isObliviousSubtype('T', 'Never', typeParameters: 'T extends Never?');
|
||||
isObliviousSubtype('T?', 'Never', typeParameters: 'T extends Never');
|
||||
|
@ -653,31 +652,31 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('() ->? int*', 'Object*');
|
||||
isSubtype('() ->? int*', 'Object?');
|
||||
|
||||
// Tests for "Null?".
|
||||
isSubtype('Null?', 'double*');
|
||||
isSubtype('Null?', 'Comparable<Object*>*');
|
||||
isSubtype('Null?', 'Comparable<Object*>?');
|
||||
isSubtype('Null?', 'Typedef<Object*>*');
|
||||
isSubtype('Null?', 'Typedef<Object*>?');
|
||||
isSubtype('Null?', 'T', typeParameters: 'T extends Object*');
|
||||
isSubtype('Null?', 'T?', typeParameters: 'T extends Object');
|
||||
isObliviousSubtype('Null?', 'T', typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Null?', 'T', typeParameters: 'T extends Object');
|
||||
isObliviousSubtype('Null?', 'Object');
|
||||
// Tests for "Null".
|
||||
isSubtype('Null', 'double*');
|
||||
isSubtype('Null', 'Comparable<Object*>*');
|
||||
isSubtype('Null', 'Comparable<Object*>?');
|
||||
isSubtype('Null', 'Typedef<Object*>*');
|
||||
isSubtype('Null', 'Typedef<Object*>?');
|
||||
isSubtype('Null', 'T', typeParameters: 'T extends Object*');
|
||||
isSubtype('Null', 'T?', typeParameters: 'T extends Object');
|
||||
isObliviousSubtype('Null', 'T', typeParameters: 'T extends Object?');
|
||||
isObliviousSubtype('Null', 'T', typeParameters: 'T extends Object');
|
||||
isObliviousSubtype('Null', 'Object');
|
||||
|
||||
// Tests for bottom and top types.
|
||||
isSubtype('Null?', 'Null?');
|
||||
isNotSubtype('Null?', 'bottom');
|
||||
isNotSubtype('Null?', 'Never');
|
||||
isSubtype('bottom', 'Null?');
|
||||
isSubtype('Null', 'Null');
|
||||
isNotSubtype('Null', 'bottom');
|
||||
isNotSubtype('Null', 'Never');
|
||||
isSubtype('bottom', 'Null');
|
||||
isSubtype('bottom', 'bottom');
|
||||
isSubtype('bottom', 'Never');
|
||||
isSubtype('Never', 'Null?');
|
||||
isSubtype('Never', 'Null');
|
||||
isNotSubtype('Never', 'bottom');
|
||||
isSubtype('Never', 'Never');
|
||||
|
||||
isSubtype('Null?', 'Never?');
|
||||
isSubtype('Never?', 'Null?');
|
||||
isSubtype('Null', 'Never?');
|
||||
isSubtype('Never?', 'Null');
|
||||
isSubtype('Never', 'Never?');
|
||||
isObliviousSubtype('Never?', 'Never');
|
||||
|
||||
|
@ -706,23 +705,23 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('bottom', 'Object*');
|
||||
isSubtype('bottom', 'dynamic');
|
||||
isSubtype('bottom', 'void');
|
||||
isSubtype('Null?', 'Object?');
|
||||
isSubtype('Null?', 'Object*');
|
||||
isSubtype('Null?', 'dynamic');
|
||||
isSubtype('Null?', 'void');
|
||||
isSubtype('Null', 'Object?');
|
||||
isSubtype('Null', 'Object*');
|
||||
isSubtype('Null', 'dynamic');
|
||||
isSubtype('Null', 'void');
|
||||
|
||||
isNotSubtype('Object?', 'Never');
|
||||
isNotSubtype('Object?', 'bottom');
|
||||
isNotSubtype('Object?', 'Null?');
|
||||
isNotSubtype('Object?', 'Null');
|
||||
isNotSubtype('Object*', 'Never');
|
||||
isNotSubtype('Object*', 'bottom');
|
||||
isNotSubtype('Object*', 'Null?');
|
||||
isNotSubtype('Object*', 'Null');
|
||||
isNotSubtype('dynamic', 'Never');
|
||||
isNotSubtype('dynamic', 'bottom');
|
||||
isNotSubtype('dynamic', 'Null?');
|
||||
isNotSubtype('dynamic', 'Null');
|
||||
isNotSubtype('void', 'Never');
|
||||
isNotSubtype('void', 'bottom');
|
||||
isNotSubtype('void', 'Null?');
|
||||
isNotSubtype('void', 'Null');
|
||||
|
||||
// Tests for Object against the top and the bottom types.
|
||||
isSubtype('bottom', 'Object');
|
||||
|
@ -734,7 +733,7 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('Object*', 'Object');
|
||||
|
||||
isNotSubtype('Object', 'bottom');
|
||||
isNotSubtype('Object', 'Null?');
|
||||
isNotSubtype('Object', 'Null');
|
||||
isNotSubtype('Object', 'Never');
|
||||
isObliviousSubtype('dynamic', 'Object');
|
||||
isObliviousSubtype('void', 'Object');
|
||||
|
@ -857,12 +856,12 @@ abstract class SubtypeTest<T, E> {
|
|||
isSubtype('Id<Object>', 'dynamic');
|
||||
isObliviousSubtype('void', 'Id<Object>');
|
||||
isSubtype('Id<Object>', 'void');
|
||||
isObliviousSubtype('Null?', 'Id<Object>');
|
||||
isObliviousSubtype('Null', 'Id<Object>');
|
||||
isSubtype('Never', 'Id<Object>');
|
||||
isSubtype('Never', 'Id<Never>');
|
||||
isSubtype('Id<Never>', 'Never');
|
||||
isNotSubtype('Null?', 'Id<Never>');
|
||||
isSubtype('Id<Never>', 'Null?');
|
||||
isNotSubtype('Null', 'Id<Never>');
|
||||
isSubtype('Id<Never>', 'Null');
|
||||
isNotSubtype('Id<Object>', 'Never');
|
||||
isSubtype('Id<int>', 'num');
|
||||
isObliviousSubtype('Id<int?>', 'num');
|
||||
|
@ -952,7 +951,7 @@ abstract class SubtypeTest<T, E> {
|
|||
isNotSubtype('Id<() ->* Object*>*', 'Id<() ->* int*>*');
|
||||
|
||||
isSubtype('void', 'Id<void>*');
|
||||
isNotSubtype('void', 'Id<Null?>*');
|
||||
isNotSubtype('void', 'Id<Null>*');
|
||||
|
||||
// The following function type tests are derived from
|
||||
// ../../../../../tests/compiler/dart2js/model/subtype_test.dart.
|
||||
|
|
|
@ -638,6 +638,7 @@ nk
|
|||
nnbd
|
||||
node's
|
||||
nonimplementation
|
||||
norm
|
||||
normalization
|
||||
notifier
|
||||
notifying
|
||||
|
|
|
@ -3151,6 +3151,7 @@ visit
|
|||
visited
|
||||
visiting
|
||||
visitor
|
||||
visitor's
|
||||
visual
|
||||
vm
|
||||
void
|
||||
|
|
|
@ -254,7 +254,8 @@ class BenchMaker implements DartTypeVisitor1<void, StringBuffer> {
|
|||
|
||||
@override
|
||||
void visitInterfaceType(InterfaceType node, StringBuffer sb) {
|
||||
sb.write(computeName(node.classNode));
|
||||
Class cls = node.classNode;
|
||||
sb.write(computeName(cls));
|
||||
if (node.typeArguments.isNotEmpty) {
|
||||
sb.write("<");
|
||||
bool first = true;
|
||||
|
@ -265,7 +266,13 @@ class BenchMaker implements DartTypeVisitor1<void, StringBuffer> {
|
|||
}
|
||||
sb.write(">");
|
||||
}
|
||||
writeNullability(node.nullability, sb);
|
||||
Uri clsImportUri = cls.enclosingLibrary.importUri;
|
||||
bool isNull = cls.name == "Null" &&
|
||||
clsImportUri.scheme == "dart" &&
|
||||
clsImportUri.path == "core";
|
||||
if (!isNull) {
|
||||
writeNullability(node.nullability, sb);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
146
pkg/kernel/lib/src/norm.dart
Normal file
146
pkg/kernel/lib/src/norm.dart
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) 2019, 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.md file.
|
||||
|
||||
import '../ast.dart' hide MapEntry;
|
||||
import '../core_types.dart';
|
||||
|
||||
import 'future_or.dart';
|
||||
import 'replacement_visitor.dart';
|
||||
|
||||
/// Returns normalization of [type].
|
||||
DartType norm(CoreTypes coreTypes, DartType type) {
|
||||
return type.accept(new _Norm(coreTypes)) ?? type;
|
||||
}
|
||||
|
||||
/// Visitor implementing the NORM algorithm.
|
||||
///
|
||||
/// Visitor's methods return null if the type is unchanged by the NORM
|
||||
/// algorithm. The algorithm is specified at
|
||||
/// https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md
|
||||
class _Norm extends ReplacementVisitor {
|
||||
final CoreTypes coreTypes;
|
||||
|
||||
_Norm(this.coreTypes);
|
||||
|
||||
@override
|
||||
DartType visitInterfaceType(InterfaceType node) {
|
||||
if (node.classNode == coreTypes.futureOrClass) {
|
||||
DartType typeArgument = node.typeArguments.single;
|
||||
typeArgument = typeArgument.accept(this) ?? typeArgument;
|
||||
if (coreTypes.isTop(typeArgument)) {
|
||||
Nullability nullabilityAsProperty =
|
||||
computeNullability(typeArgument, coreTypes.futureOrClass);
|
||||
assert(nullabilityAsProperty == Nullability.nullable ||
|
||||
nullabilityAsProperty == Nullability.legacy);
|
||||
// [typeArgument] is nullable because it's a top type. No need to unite
|
||||
// the nullabilities of [node] and [typeArgument].
|
||||
return typeArgument.withNullability(nullabilityAsProperty);
|
||||
} else if (typeArgument is InterfaceType &&
|
||||
typeArgument.classNode == coreTypes.objectClass &&
|
||||
typeArgument.nullability == Nullability.nonNullable) {
|
||||
assert(!coreTypes.isTop(typeArgument));
|
||||
// [typeArgument] is non-nullable, so the union of that and the
|
||||
// nullability of [node] is the nullability of [node].
|
||||
return typeArgument
|
||||
.withNullability(computeNullability(node, coreTypes.futureOrClass));
|
||||
} else if (typeArgument is NeverType &&
|
||||
typeArgument.nullability == Nullability.nonNullable) {
|
||||
assert(!coreTypes.isTop(typeArgument));
|
||||
assert(!coreTypes.isObject(typeArgument));
|
||||
// [typeArgument] is non-nullable, so the union of that and the
|
||||
// nullability of [node] is the nullability of [node].
|
||||
return new InterfaceType(
|
||||
coreTypes.futureClass,
|
||||
computeNullability(node, coreTypes.futureOrClass),
|
||||
<DartType>[typeArgument]);
|
||||
} else if (coreTypes.isNull(typeArgument)) {
|
||||
assert(!coreTypes.isTop(typeArgument));
|
||||
assert(!coreTypes.isObject(typeArgument));
|
||||
assert(!coreTypes.isBottom(typeArgument));
|
||||
return new InterfaceType(
|
||||
coreTypes.futureClass,
|
||||
uniteNullabilities(typeArgument.nullability,
|
||||
computeNullability(node, coreTypes.futureOrClass)),
|
||||
<DartType>[typeArgument]);
|
||||
}
|
||||
assert(!coreTypes.isTop(typeArgument));
|
||||
assert(!coreTypes.isObject(typeArgument));
|
||||
assert(!coreTypes.isBottom(typeArgument));
|
||||
assert(!coreTypes.isNull(typeArgument));
|
||||
return new InterfaceType(
|
||||
coreTypes.futureOrClass,
|
||||
computeNullability(node, coreTypes.futureOrClass),
|
||||
<DartType>[typeArgument]);
|
||||
}
|
||||
return super
|
||||
.visitInterfaceType(node)
|
||||
?.withNullability(computeNullability(node, coreTypes.futureOrClass));
|
||||
}
|
||||
|
||||
@override
|
||||
DartType visitTypeParameterType(TypeParameterType node) {
|
||||
if (node.promotedBound == null) {
|
||||
DartType bound = node.parameter.bound;
|
||||
if (normalizesToNever(bound)) {
|
||||
DartType result = new NeverType(Nullability.nonNullable)
|
||||
.withNullability(node.nullability);
|
||||
return result.accept(this) ?? result;
|
||||
}
|
||||
assert(!coreTypes.isBottom(bound));
|
||||
// If the bound isn't Never, the type is already normalized.
|
||||
return null;
|
||||
} else {
|
||||
DartType bound = node.promotedBound;
|
||||
bound = bound.accept(this) ?? bound;
|
||||
if (bound is NeverType && bound.nullability == Nullability.nonNullable) {
|
||||
return bound;
|
||||
} else if (coreTypes.isTop(bound)) {
|
||||
assert(!coreTypes.isBottom(bound));
|
||||
assert(bound.nullability == Nullability.nullable);
|
||||
return new TypeParameterType(
|
||||
node.parameter, node.typeParameterTypeNullability);
|
||||
} else if (bound is TypeParameterType &&
|
||||
bound.parameter == node.parameter &&
|
||||
bound.typeParameterTypeNullability ==
|
||||
node.typeParameterTypeNullability &&
|
||||
bound.promotedBound == null) {
|
||||
assert(!coreTypes.isBottom(bound));
|
||||
assert(!coreTypes.isTop(bound));
|
||||
return new TypeParameterType(
|
||||
node.parameter, node.typeParameterTypeNullability);
|
||||
} else if (bound == coreTypes.objectNonNullableRawType &&
|
||||
norm(coreTypes, node.parameter.bound) ==
|
||||
coreTypes.objectNonNullableRawType) {
|
||||
return new TypeParameterType(
|
||||
node.parameter, node.typeParameterTypeNullability);
|
||||
} else if (identical(bound, node.promotedBound)) {
|
||||
// If [bound] is identical to [node.promotedBound], then the NORM
|
||||
// algorithms didn't change the promoted bound, so the [node] is
|
||||
// unchanged as well, and we return null to indicate that.
|
||||
return null;
|
||||
}
|
||||
return new TypeParameterType(
|
||||
node.parameter, node.typeParameterTypeNullability, bound);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
DartType visitNeverType(NeverType node) {
|
||||
if (node.nullability == Nullability.nullable) return coreTypes.nullType;
|
||||
return null;
|
||||
}
|
||||
|
||||
bool normalizesToNever(DartType type) {
|
||||
if (type is NeverType && type.nullability == Nullability.nonNullable) {
|
||||
return true;
|
||||
} else if (type is TypeParameterType) {
|
||||
if (type.promotedBound == null) {
|
||||
return normalizesToNever(type.parameter.bound);
|
||||
} else {
|
||||
return normalizesToNever(type.promotedBound);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -115,6 +115,11 @@ class Env {
|
|||
DartType parseType(String text) {
|
||||
return _libraryEnvironment.parseType(text);
|
||||
}
|
||||
|
||||
void extendWithTypeParameters(String typeParameters) {
|
||||
_libraryEnvironment =
|
||||
_libraryEnvironment.extendWithTypeParameters(typeParameters);
|
||||
}
|
||||
}
|
||||
|
||||
class TypeParserEnvironment {
|
||||
|
@ -229,11 +234,15 @@ class _KernelFromParsedType implements Visitor<Node, TypeParserEnvironment> {
|
|||
arguments[i].accept<Node, TypeParserEnvironment>(this, environment);
|
||||
}
|
||||
if (declaration is Class) {
|
||||
Nullability nullability =
|
||||
interpretParsedNullability(node.parsedNullability);
|
||||
if (declaration.name == 'Null' &&
|
||||
declaration.enclosingLibrary.importUri.scheme == 'dart' &&
|
||||
declaration.enclosingLibrary.importUri.path == 'core' &&
|
||||
node.parsedNullability != ParsedNullability.nullable) {
|
||||
throw "Null type must be written as 'Null?'";
|
||||
declaration.enclosingLibrary.importUri.path == 'core') {
|
||||
if (node.parsedNullability != ParsedNullability.omitted) {
|
||||
throw "Null type must be written without explicit nullability";
|
||||
}
|
||||
nullability = Nullability.nullable;
|
||||
}
|
||||
List<TypeParameter> typeVariables = declaration.typeParameters;
|
||||
if (kernelArguments.isEmpty && typeVariables.isNotEmpty) {
|
||||
|
@ -244,8 +253,7 @@ class _KernelFromParsedType implements Visitor<Node, TypeParserEnvironment> {
|
|||
} else if (kernelArguments.length != typeVariables.length) {
|
||||
throw "Expected ${typeVariables.length} type arguments: $node";
|
||||
}
|
||||
return new InterfaceType(declaration,
|
||||
interpretParsedNullability(node.parsedNullability), kernelArguments);
|
||||
return new InterfaceType(declaration, nullability, kernelArguments);
|
||||
} else if (declaration is TypeParameter) {
|
||||
if (arguments.isNotEmpty) {
|
||||
throw "Type variable can't have arguments (${node.name})";
|
||||
|
|
|
@ -9,9 +9,9 @@ import 'package:kernel/src/legacy_erasure.dart';
|
|||
import 'package:kernel/testing/type_parser_environment.dart';
|
||||
|
||||
const Map<String, String> data = {
|
||||
'Null?': 'Null?',
|
||||
'Never': 'Null?',
|
||||
'Never?': 'Null?',
|
||||
'Null': 'Null',
|
||||
'Never': 'Null',
|
||||
'Never?': 'Null',
|
||||
'void': 'void',
|
||||
'dynamic': 'dynamic',
|
||||
'bool': 'bool*',
|
||||
|
|
|
@ -23,9 +23,9 @@ const Map<String, dynamic> data = {
|
|||
'Object? vs dynamic': 'Object?',
|
||||
'Object* vs dynamic': null,
|
||||
'Object vs dynamic': null,
|
||||
'Never? vs Null?': null,
|
||||
'Never* vs Null?': 'Null?',
|
||||
'Never vs Null?': null,
|
||||
'Never? vs Null': null,
|
||||
'Never* vs Null': 'Null',
|
||||
'Never vs Null': null,
|
||||
'int? vs int?': 'int?',
|
||||
'int? vs int*': 'int?',
|
||||
'int* vs int*': 'int*',
|
||||
|
|
163
pkg/kernel/test/norm_test.dart
Normal file
163
pkg/kernel/test/norm_test.dart
Normal file
|
@ -0,0 +1,163 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
import "package:expect/expect.dart" show Expect;
|
||||
|
||||
import 'package:kernel/ast.dart' hide MapEntry;
|
||||
import 'package:kernel/src/norm.dart';
|
||||
import 'package:kernel/testing/type_parser_environment.dart';
|
||||
|
||||
run() {
|
||||
checkNormToSame('Null');
|
||||
checkNormToSame('Never');
|
||||
check('Never?', 'Null');
|
||||
checkNormToSame('Never*');
|
||||
checkNormToSame('void');
|
||||
checkNormToSame('dynamic');
|
||||
checkNormToSame('Object?');
|
||||
check('FutureOr<dynamic>', 'dynamic');
|
||||
check('FutureOr<void>', 'void');
|
||||
check('FutureOr<Object>', 'Object');
|
||||
check('FutureOr<Object?>', 'Object?');
|
||||
check('FutureOr<Object*>', 'Object*');
|
||||
check('FutureOr<Never>', 'Future<Never>');
|
||||
check('FutureOr<Never?>', 'Future<Null>?');
|
||||
|
||||
// TODO(dmitryas): Use the following test case instead when FutureOr can
|
||||
// distinguish between the declared nullability and the nullability as a
|
||||
// property.
|
||||
//
|
||||
//check('FutureOr<Never*>', 'Future<Never*>');
|
||||
check('FutureOr<Never*>', 'Future<Never*>*');
|
||||
|
||||
check('FutureOr<Null>', 'Future<Null>?');
|
||||
check('FutureOr<FutureOr<dynamic>>', 'dynamic');
|
||||
check('FutureOr<FutureOr<void>>', 'void');
|
||||
check('FutureOr<FutureOr<Object>>', 'Object');
|
||||
check('FutureOr<FutureOr<Object?>>', 'Object?');
|
||||
check('FutureOr<FutureOr<Object*>>', 'Object*');
|
||||
check('FutureOr<FutureOr<Never>>', 'FutureOr<Future<Never>>');
|
||||
|
||||
// TODO(dmitryas): Use the following test case instead when FutureOr can
|
||||
// distinguish between the declared nullability and the nullability as a
|
||||
// property.
|
||||
//
|
||||
//check('FutureOr<FutureOr<Never?>>', 'FutureOr<Future<Null>?>');
|
||||
check('FutureOr<FutureOr<Never?>>', 'FutureOr<Future<Null>?>?');
|
||||
|
||||
// TODO(dmitryas): Use the following test case instead when FutureOr can
|
||||
// distinguish between the declared nullability and the nullability as a
|
||||
// property.
|
||||
//
|
||||
//check('FutureOr<FutureOr<Never*>>', 'FutureOr<Future<Never*>>');
|
||||
check('FutureOr<FutureOr<Never*>>', 'FutureOr<Future<Never*>*>*');
|
||||
|
||||
// TODO(dmitryas): Use the following test case instead when FutureOr can
|
||||
// distinguish between the declared nullability and the nullability as a
|
||||
// property.
|
||||
//
|
||||
//check('FutureOr<FutureOr<Null>>', 'FutureOr<Future<Null>?>');
|
||||
check('FutureOr<FutureOr<Null>>', 'FutureOr<Future<Null>?>?');
|
||||
|
||||
checkNormToSame('bool');
|
||||
checkNormToSame('bool?');
|
||||
checkNormToSame('bool*');
|
||||
|
||||
checkNormToSame('List<bool>');
|
||||
checkNormToSame('List<bool?>');
|
||||
checkNormToSame('List<bool*>');
|
||||
checkNormToSame('List<bool>?');
|
||||
checkNormToSame('List<bool?>?');
|
||||
checkNormToSame('List<bool*>?');
|
||||
checkNormToSame('List<bool>*');
|
||||
checkNormToSame('List<bool?>*');
|
||||
checkNormToSame('List<bool*>*');
|
||||
check('List<FutureOr<Object?>>', 'List<Object?>');
|
||||
check('List<T>', 'List<Never>', 'T extends Never');
|
||||
check('List<T?>', 'List<Null>', 'T extends Never');
|
||||
|
||||
checkNormToSame('() ->* bool*');
|
||||
checkNormToSame('() ->? bool*');
|
||||
checkNormToSame('() -> bool*');
|
||||
checkNormToSame('() ->* bool?');
|
||||
checkNormToSame('() ->? bool?');
|
||||
checkNormToSame('() -> bool?');
|
||||
checkNormToSame('() ->* bool');
|
||||
checkNormToSame('() ->? bool');
|
||||
checkNormToSame('() -> bool');
|
||||
check('() ->? List<FutureOr<Object?>>', '() ->? List<Object?>');
|
||||
check('() ->? List<T>', '() ->? List<Never>', 'T extends Never');
|
||||
check('() ->? List<T?>', '() ->? List<Null>',
|
||||
'T extends S, S extends U, U extends Never');
|
||||
|
||||
checkNormToSame('(int*) -> void');
|
||||
checkNormToSame('(int?) -> void');
|
||||
checkNormToSame('(int) -> void');
|
||||
checkNormToSame('([int*]) -> void');
|
||||
checkNormToSame('([int?]) -> void');
|
||||
checkNormToSame('([int]) -> void');
|
||||
checkNormToSame('({int* a}) -> void');
|
||||
checkNormToSame('({int? a}) -> void');
|
||||
checkNormToSame('({int a}) -> void');
|
||||
checkNormToSame('({required int* a}) -> void');
|
||||
checkNormToSame('({required int? a}) -> void');
|
||||
checkNormToSame('({required int a}) -> void');
|
||||
check('(List<FutureOr<Object?>>) -> void', '(List<Object?>) -> void');
|
||||
check('([List<FutureOr<Object?>>]) -> void', '([List<Object?>]) -> void');
|
||||
check('({List<FutureOr<Object?>> x}) -> void', '({List<Object?> x}) -> void');
|
||||
check('({required List<FutureOr<Object?>> x}) -> void',
|
||||
'({required List<Object?> x}) -> void');
|
||||
|
||||
checkNormToSame('<T>(T) -> void');
|
||||
checkNormToSame('<T>(T?) -> void');
|
||||
checkNormToSame('<T extends bool>(T) -> void');
|
||||
checkNormToSame('<T extends List<T>>(T) -> void');
|
||||
check('<T extends List<FutureOr<Object?>>>(T) -> void',
|
||||
'<T extends List<Object?>>(T) -> void');
|
||||
|
||||
checkNormToSame('T', 'T extends Object?');
|
||||
checkNormToSame('T?', 'T extends Object?');
|
||||
checkNormToSame('T', 'T extends FutureOr<Never>');
|
||||
check('T', 'Never', 'T extends Never');
|
||||
check('T & Never', 'Never', 'T extends Object?');
|
||||
check('T', 'Never', 'T extends S, S extends Never');
|
||||
check('List<T?>', 'List<Null>', 'T extends S, S extends Never');
|
||||
check('FutureOr<T?>', 'Future<Null>?', 'T extends S, S extends Never');
|
||||
|
||||
check('FutureOr<FutureOr<dynamic>>', 'dynamic');
|
||||
check('FutureOr<FutureOr<void>>', 'void');
|
||||
check('FutureOr<FutureOr<Object?>>', 'Object?');
|
||||
check('FutureOr<FutureOr<dynamic>?>?', 'dynamic');
|
||||
check('FutureOr<FutureOr<void>?>?', 'void');
|
||||
check('FutureOr<FutureOr<Object?>?>?', 'Object?');
|
||||
|
||||
// TODO(dmitryas): The following test cases should be removed when FutureOr
|
||||
// can distinguish between the declared nullability and the nullability as a
|
||||
// property.
|
||||
check('FutureOr<int?>', 'FutureOr<int?>?');
|
||||
check('FutureOr<FutureOr<int?>>', 'FutureOr<FutureOr<int?>?>?');
|
||||
check('FutureOr<FutureOr<int>?>', 'FutureOr<FutureOr<int>?>?');
|
||||
check('FutureOr<FutureOr<FutureOr<int>>?>',
|
||||
'FutureOr<FutureOr<FutureOr<int>>?>?');
|
||||
}
|
||||
|
||||
check(String input, String output, [String typeParameters = '']) {
|
||||
Env env = new Env('')..extendWithTypeParameters(typeParameters);
|
||||
DartType inputType = env.parseType(input);
|
||||
DartType expectedOutputType = env.parseType(output);
|
||||
DartType actualOutputType = norm(env.coreTypes, inputType);
|
||||
print('norm($inputType) = $actualOutputType, expected $expectedOutputType');
|
||||
Expect.equals(
|
||||
expectedOutputType,
|
||||
actualOutputType,
|
||||
"Unexpected norm of $inputType ('$input'):\n"
|
||||
"Expected: ${expectedOutputType} ('$output')\n"
|
||||
"Actual: ${actualOutputType}");
|
||||
}
|
||||
|
||||
checkNormToSame(String type, [String typeParameters = '']) {
|
||||
return check(type, type, typeParameters);
|
||||
}
|
||||
|
||||
main() => run();
|
Loading…
Reference in a new issue