[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:
Dmitry Stefantsov 2020-02-07 15:00:24 +00:00
parent 3433d8cfc3
commit 535ee87c21
11 changed files with 404 additions and 79 deletions

2
DEPS
View file

@ -469,7 +469,7 @@ deps = {
"packages": [
{
"package": "dart/cfe/benchmark_data",
"version": "sha1sum:4640fa0bff40726392748d1ad3147e5dd0324ea2",
"version": "sha1sum:5b6e6dfa33b85c733cab4e042bf46378984d1544",
}
],
"dep_type": "cipd",

View file

@ -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() {

View file

@ -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.

View file

@ -638,6 +638,7 @@ nk
nnbd
node's
nonimplementation
norm
normalization
notifier
notifying

View file

@ -3151,6 +3151,7 @@ visit
visited
visiting
visitor
visitor's
visual
vm
void

View file

@ -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

View 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;
}
}

View file

@ -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})";

View file

@ -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*',

View file

@ -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*',

View 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();