Files
sdk/pkg/kernel/lib/src/norm.dart
T
Chloe Stefantsova de302d7f3b [cfe] Introduce StructuralParameters
This CL introduces StructuralParameter and StructuralParameterType
classes. They are intended to replace TypeParameter and
TypeParameterType respectively where those were used as type
parameters defined by FunctionTypes. Previously, type parameters of
FunctionTypes were represented by TypeParameter objects with the
‘parent’ field set to null. By introducing StructuralParameter and
StructuralParameterType this CL unambiguously separates the two
notions of type parameters.

TEST=existing

Change-Id: Ida3feb7ad96a7b2acef55840eacba9e36bf2a3e7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/312264
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
2023-09-25 10:44:41 +00:00

164 lines
6.3 KiB
Dart

// 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 file.
import '../ast.dart';
import '../core_types.dart';
import '../type_algebra.dart';
import 'replacement_visitor.dart';
/// Returns normalization of [type].
DartType norm(CoreTypes coreTypes, DartType type) {
return type.accept1(new _Norm(coreTypes), Variance.covariant) ?? type;
}
/// Returns normalization of [supertype].
Supertype normSupertype(CoreTypes coreTypes, Supertype supertype) {
if (supertype.typeArguments.isEmpty) return supertype;
_Norm normVisitor = new _Norm(coreTypes);
List<DartType>? typeArguments = null;
for (int i = 0; i < supertype.typeArguments.length; ++i) {
DartType? typeArgument =
supertype.typeArguments[i].accept1(normVisitor, Variance.covariant);
if (typeArgument != null) {
typeArguments ??= supertype.typeArguments.toList();
typeArguments[i] = typeArgument;
}
}
if (typeArguments == null) return supertype;
return new Supertype(supertype.classNode, typeArguments);
}
/// 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, int variance) {
return super
.visitInterfaceType(node, variance)
?.withDeclaredNullability(node.nullability);
}
@override
DartType visitFutureOrType(FutureOrType node, int variance) {
DartType typeArgument = node.typeArgument;
typeArgument = typeArgument.accept1(this, variance) ?? typeArgument;
if (coreTypes.isTop(typeArgument)) {
assert(typeArgument.nullability == Nullability.nullable ||
typeArgument.nullability == Nullability.legacy);
// [typeArgument] is nullable because it's a top type. No need to unite
// the nullabilities of [node] and [typeArgument].
return typeArgument;
} 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.withDeclaredNullability(node.nullability);
} 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, node.nullability, <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, node.nullability),
<DartType>[typeArgument]);
}
assert(!coreTypes.isTop(typeArgument));
assert(!coreTypes.isObject(typeArgument));
assert(!coreTypes.isBottom(typeArgument));
assert(!coreTypes.isNull(typeArgument));
// TODO(johnniwinther): We should return `null` if [typeArgument] is
// the same as `node.typeArgument`.
return new FutureOrType(typeArgument, node.nullability);
}
@override
DartType? visitTypeParameterType(TypeParameterType node, int variance) {
DartType bound = node.parameter.bound;
if (normalizesToNever(bound)) {
DartType result = NeverType.fromNullability(node.nullability);
return result.accept1(this, variance) ?? result;
}
assert(!coreTypes.isBottom(bound));
// If the bound isn't Never, the type is already normalized.
return null;
}
@override
DartType? visitStructuralParameterType(
StructuralParameterType node, int variance) {
DartType bound = node.parameter.bound;
if (normalizesToNever(bound)) {
DartType result = NeverType.fromNullability(node.nullability);
return result.accept1(this, variance) ?? result;
}
assert(!coreTypes.isBottom(bound));
// If the bound isn't Never, the type is already normalized.
return null;
}
@override
DartType? visitIntersectionType(IntersectionType node, int variance) {
DartType right = node.right;
right = right.accept1(this, variance) ?? right;
if (right is NeverType && right.nullability == Nullability.nonNullable) {
return right;
} else if (coreTypes.isTop(right)) {
assert(!coreTypes.isBottom(right));
assert(right.nullability == Nullability.nullable);
return node.left;
} else if (right is TypeParameterType &&
right.parameter == node.left.parameter &&
right.declaredNullability == node.left.declaredNullability) {
assert(!coreTypes.isBottom(right));
assert(!coreTypes.isTop(right));
return node.left;
} else if (right == coreTypes.objectNonNullableRawType &&
norm(coreTypes, node.left.parameter.bound) ==
coreTypes.objectNonNullableRawType) {
return node.left;
} else if (identical(right, node.right)) {
// If [bound] is identical to [node.right], 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 IntersectionType(node.left, right);
}
@override
DartType? visitNeverType(NeverType node, int variance) {
if (node.nullability == Nullability.nullable) return const NullType();
return null;
}
bool normalizesToNever(DartType type) {
if (type is NeverType && type.nullability == Nullability.nonNullable) {
return true;
} else if (type is TypeParameterType) {
return normalizesToNever(type.parameter.bound);
} else if (type is IntersectionType) {
return normalizesToNever(type.right);
}
return false;
}
}