blob: 8927d8584eff3a0903c040a1596ca2c06f3d3814 [file] [log] [blame]
// Copyright (c) 2017, 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 'package:kernel/ast.dart';
import 'package:kernel/src/replacement_visitor.dart';
import 'type_schema.dart' show UnknownType;
/// Returns the greatest closure of the given type [schema] with respect to `?`.
///
/// The greatest closure of a type schema `P` with respect to `?` is defined as
/// `P` with every covariant occurrence of `?` replaced with `Null`, and every
/// contravariant occurrence of `?` replaced with `Object`.
///
/// If the schema contains no instances of `?`, the original schema object is
/// returned to avoid unnecessary allocation.
///
/// Note that the closure of a type schema is a proper type.
///
/// Note that the greatest closure of a type schema is always a supertype of any
/// type which matches the schema.
DartType greatestClosure(
DartType schema, DartType topType, DartType bottomType) {
return _TypeSchemaEliminationVisitor.run(false, schema, topType, bottomType);
}
/// Returns the least closure of the given type [schema] with respect to `?`.
///
/// The least closure of a type schema `P` with respect to `?` is defined as
/// `P` with every covariant occurrence of `?` replaced with `Object`, and every
/// contravariant occurrence of `?` replaced with `Null`.
///
/// If the schema contains no instances of `?`, the original schema object is
/// returned to avoid unnecessary allocation.
///
/// Note that the closure of a type schema is a proper type.
///
/// Note that the least closure of a type schema is always a subtype of any type
/// which matches the schema.
DartType leastClosure(DartType schema, DartType topType, DartType bottomType) {
return _TypeSchemaEliminationVisitor.run(true, schema, topType, bottomType);
}
/// Visitor that computes least and greatest closures of a type schema.
///
/// Each visitor method returns `null` if there are no `?`s contained in the
/// type, otherwise it returns the result of substituting `?` with `Null` or
/// `Object`, as appropriate.
class _TypeSchemaEliminationVisitor extends ReplacementVisitor {
final DartType topType;
final DartType bottomType;
_TypeSchemaEliminationVisitor(this.topType, this.bottomType);
@override
DartType? defaultDartType(DartType node, int variance) {
bool isLeastClosure = variance == Variance.covariant;
if (node is UnknownType) {
return isLeastClosure ? bottomType : topType;
}
return null;
}
/// Runs an instance of the visitor on the given [schema] and returns the
/// resulting type. If the schema contains no instances of `?`, the original
/// schema object is returned to avoid unnecessary allocation.
static DartType run(bool isLeastClosure, DartType schema, DartType topType,
DartType bottomType) {
assert(topType == const DynamicType() ||
topType is InterfaceType &&
topType.nullability == Nullability.nullable &&
topType.classNode.enclosingLibrary.importUri.scheme == "dart" &&
topType.classNode.enclosingLibrary.importUri.path == "core" &&
topType.classNode.name == "Object");
assert(
bottomType == const NeverType.nonNullable() || bottomType is NullType);
_TypeSchemaEliminationVisitor visitor =
new _TypeSchemaEliminationVisitor(topType, bottomType);
DartType? result = schema.accept1(
visitor, isLeastClosure ? Variance.covariant : Variance.contravariant);
return result ?? schema;
}
}