mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:17:14 +00:00
6617737ff5
R=kmillikin@google.com BUG= Review-Url: https://codereview.chromium.org/2830353002 .
679 lines
20 KiB
Dart
679 lines
20 KiB
Dart
// Copyright (c) 2016, 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.
|
|
|
|
/// Notation:
|
|
///
|
|
/// * `[[T]]` is the runtime representation of type `T` (that is, `T` reified).
|
|
library kernel.transformations.reify.runtime.types;
|
|
|
|
import 'declarations.dart' show Class;
|
|
|
|
export 'declarations.dart';
|
|
|
|
export 'interceptors.dart';
|
|
|
|
// The public interface of this library are static functions to access parts of
|
|
// reified type objects and the constructors on the ReifiedType subclasses.
|
|
|
|
bool isSubtypeOf(ReifiedType a, ReifiedType b) => a._isSubtypeOf(b);
|
|
|
|
bool isMoreSpecificThan(ReifiedType a, ReifiedType b) {
|
|
return a._isMoreSpecificThan(b);
|
|
}
|
|
|
|
Kind getKind(ReifiedType type) => type._kind;
|
|
|
|
ReifiedType asInstanceOf(Interface type, Class declaration) {
|
|
return type.asInstanceOf(declaration);
|
|
}
|
|
|
|
List<ReifiedType> getTypeArguments(Interface type) => type._typeArguments;
|
|
|
|
bool isDynamic(ReifiedType type) => type._isDynamic;
|
|
|
|
bool isFunction(ReifiedType type) => type._isFunction;
|
|
|
|
bool isInterface(ReifiedType type) => type._isInterface;
|
|
|
|
bool isIntersection(ReifiedType type) => type._isIntersection;
|
|
|
|
bool isVariable(ReifiedType type) => type._isVariable;
|
|
|
|
bool isVoid(ReifiedType type) => type._isVoid;
|
|
|
|
bool isObject(ReifiedType type) => false;
|
|
|
|
ReifiedType getSupertype(var type) => type._supertype;
|
|
|
|
Iterable<ReifiedType> getInterfaces(Interface type) => type._interfaces;
|
|
|
|
ReifiedType subst(ReifiedType type, List<ReifiedType> arguments,
|
|
List<ReifiedType> parameters) {
|
|
return type._subst(arguments, parameters);
|
|
}
|
|
|
|
// TODO(ahe): Do we need ReifiedNullType?
|
|
|
|
ReifiedType _intersection(ReifiedType a, ReifiedType b) {
|
|
if (a == null) return b;
|
|
if (b == null) return a;
|
|
if (a == b) return a;
|
|
return new Intersection(a, b);
|
|
}
|
|
|
|
enum Kind {
|
|
Bottom,
|
|
Dynamic,
|
|
Function,
|
|
Interface,
|
|
Intersection,
|
|
Variable,
|
|
Void,
|
|
}
|
|
|
|
abstract class ReifiedType {
|
|
// TODO(ahe): Should this be a getter to save memory? Which is faster?
|
|
final Kind _kind;
|
|
|
|
const ReifiedType(this._kind);
|
|
|
|
bool get _isDynamic => _kind == Kind.Dynamic;
|
|
|
|
bool get _isFunction => _kind == Kind.Function;
|
|
|
|
bool get _isInterface => _kind == Kind.Interface;
|
|
|
|
bool get _isIntersection => _kind == Kind.Intersection;
|
|
|
|
bool get _isVariable => _kind == Kind.Variable;
|
|
|
|
bool get _isVoid => _kind == Kind.Void;
|
|
|
|
bool get _isObject => false;
|
|
|
|
/// Returns true if [this] is more specific than [type].
|
|
bool _isMoreSpecificThan(ReifiedType type);
|
|
|
|
/// Performs the substitution `[arguments[i]/parameters[i]]this`.
|
|
///
|
|
/// The notation is known from this lambda calculus rule:
|
|
///
|
|
/// (lambda x.e0)e1 -> [e1/x]e0.
|
|
///
|
|
/// Invariant: There must be the same number of [arguments] and [parameters].
|
|
ReifiedType _subst(List<ReifiedType> arguments, List<ReifiedType> parameters);
|
|
|
|
/// Returns true if [this] is a subtype of [type].
|
|
bool _isSubtypeOf(ReifiedType type) {
|
|
return _subst(const <ReifiedType>[const Bottom()],
|
|
const <ReifiedType>[const Dynamic()])._isMoreSpecificThan(type);
|
|
}
|
|
|
|
bool _isAssignableTo(ReifiedType type) {
|
|
if (type._isDynamic) return true;
|
|
return this._isSubtypeOf(type) || type._isSubtypeOf(this);
|
|
}
|
|
}
|
|
|
|
/// Represents the type `dynamic`.
|
|
class Dynamic extends ReifiedType {
|
|
const Dynamic() : super(Kind.Dynamic);
|
|
|
|
bool _isMoreSpecificThan(ReifiedType type) => type._isDynamic;
|
|
|
|
ReifiedType _subst(
|
|
List<ReifiedType> arguments, List<ReifiedType> parameters) {
|
|
int index = 0;
|
|
for (ReifiedType parameter in parameters) {
|
|
if (parameter._isDynamic) return arguments[index];
|
|
index++;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
String toString() => "dynamic";
|
|
}
|
|
|
|
/// Represents the bottom type.
|
|
class Bottom extends ReifiedType {
|
|
const Bottom() : super(Kind.Bottom);
|
|
|
|
bool _isMoreSpecificThan(ReifiedType type) => true;
|
|
|
|
ReifiedType _subst(
|
|
List<ReifiedType> arguments, List<ReifiedType> parameters) {
|
|
return this;
|
|
}
|
|
|
|
String toString() => "<bottom>";
|
|
}
|
|
|
|
/// Represents the type `void`.
|
|
class Void extends ReifiedType {
|
|
const Void() : super(Kind.Void);
|
|
|
|
bool _isMoreSpecificThan(ReifiedType type) {
|
|
// `void` isn't more specific than anything but itself.
|
|
return type._isVoid;
|
|
}
|
|
|
|
bool _isSubtypeOf(ReifiedType type) {
|
|
// `void` isn't the subtype of anything besides `dynamic` and itself.
|
|
return type._isVoid || type._isDynamic;
|
|
}
|
|
|
|
ReifiedType _subst(
|
|
List<ReifiedType> arguments, List<ReifiedType> parameters) {
|
|
return this;
|
|
}
|
|
|
|
String toString() => "void";
|
|
}
|
|
|
|
/// Represents an interface type. That is, the type of any class.
|
|
///
|
|
/// For example, the type
|
|
///
|
|
/// String
|
|
///
|
|
/// Would be represented as:
|
|
///
|
|
/// new Interface(stringDeclaration);
|
|
///
|
|
/// Where `stringDeclaration` is an instance of [Class] which contains
|
|
/// information about [String]'s supertype and implemented interfaces.
|
|
///
|
|
/// A parameterized type, for example:
|
|
///
|
|
/// Box<int>
|
|
///
|
|
/// Would be represented as:
|
|
///
|
|
/// new Interface(boxDeclaration,
|
|
/// [new Interface(intDeclaration)]);
|
|
///
|
|
/// Implementation notes and considerations:
|
|
///
|
|
/// * It's possible that we want to split this class in two to save memory: one
|
|
/// for non-generic classes and one for generic classes. Only the latter
|
|
/// would need [_typeArguments]. However, this must be weighed against the
|
|
/// additional polymorphism.
|
|
/// * Generally, we don't canonicalize types. However, simple types like `new
|
|
/// Interface(intDeclaration)` should be canonicalized to save
|
|
/// memory. Precisely how this canonicalization will happen is TBD, but it
|
|
/// may simply be by using compile-time constants.
|
|
class Interface extends ReifiedType implements Type {
|
|
final Class _declaration;
|
|
|
|
final List<ReifiedType> _typeArguments;
|
|
|
|
const Interface(this._declaration,
|
|
[this._typeArguments = const <ReifiedType>[]])
|
|
: super(Kind.Interface);
|
|
|
|
bool get _isObject => _declaration.supertype == null;
|
|
|
|
Interface get _supertype {
|
|
return _declaration.supertype
|
|
?._subst(_typeArguments, _declaration.variables);
|
|
}
|
|
|
|
Iterable<Interface> get _interfaces {
|
|
return _declaration.interfaces.map((Interface type) {
|
|
return type._subst(_typeArguments, _declaration.variables);
|
|
});
|
|
}
|
|
|
|
FunctionType get _callableType {
|
|
return _declaration.callableType
|
|
?._subst(_typeArguments, _declaration.variables);
|
|
}
|
|
|
|
bool _isMoreSpecificThan(ReifiedType type) {
|
|
if (type._isDynamic) return true;
|
|
// Intersection types can only occur as the result of calling
|
|
// [asInstanceOf], they should never be passed in to this method.
|
|
assert(!type._isIntersection);
|
|
if (type._isFunction) {
|
|
return _callableType?._isMoreSpecificThan(type) ?? false;
|
|
}
|
|
if (!type._isInterface) return false;
|
|
if (this == type) return true;
|
|
ReifiedType supertype = asInstanceOfType(type);
|
|
if (supertype == null) {
|
|
// Special case: A callable class is a subtype of [Function], regardless
|
|
// if it implements [Function]. It isn't more specific than
|
|
// [Function]. The type representing [Function] is the supertype of
|
|
// `declaration.callableType`.
|
|
return _declaration.callableType?._supertype?._isSubtypeOf(type) ?? false;
|
|
}
|
|
if (type == supertype) return true;
|
|
switch (supertype._kind) {
|
|
case Kind.Dynamic:
|
|
case Kind.Variable:
|
|
// Shouldn't happen. See switch in [asInstanceOf].
|
|
throw "internal error: $supertype";
|
|
|
|
case Kind.Interface:
|
|
Interface s = supertype;
|
|
Interface t = type;
|
|
for (int i = 0; i < s._typeArguments.length; i++) {
|
|
if (!s._typeArguments[i]._isMoreSpecificThan(t._typeArguments[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
|
|
case Kind.Intersection:
|
|
return supertype._isMoreSpecificThan(type);
|
|
|
|
default:
|
|
throw "Internal error: unhandled kind '${type._kind}'";
|
|
}
|
|
}
|
|
|
|
bool _isSubtypeOf(ReifiedType type) {
|
|
if (type._isInterface) {
|
|
Interface interface = type;
|
|
if (interface._declaration != this._declaration) {
|
|
// This addition to the specified rules allows us to handle cases like
|
|
// class D extends A<dynamic> {}
|
|
// new D() is A<A>
|
|
// where directly going to `isMoreSpecific` would leave `dynamic` in the
|
|
// result of `asInstanceOf(A)` instead of bottom.
|
|
ReifiedType that = asInstanceOf(interface._declaration);
|
|
if (that != null) {
|
|
return that._isSubtypeOf(type);
|
|
}
|
|
}
|
|
}
|
|
return super._isSubtypeOf(type) ||
|
|
(_callableType?._isSubtypeOf(type) ?? false);
|
|
}
|
|
|
|
/// Returns [this] translated to [type] if [type] is a supertype of
|
|
/// [this]. Otherwise null.
|
|
///
|
|
/// For example, given:
|
|
///
|
|
/// class Box<T> {}
|
|
/// class BeatBox extends Box<Beat> {}
|
|
/// class Beat {}
|
|
///
|
|
/// We have:
|
|
///
|
|
/// [[BeatBox]].asInstanceOf([[Box]]) -> [[Box<Beat>]].
|
|
ReifiedType asInstanceOf(Class other) {
|
|
if (_declaration == other) return this;
|
|
ReifiedType result = _declaration.supertype
|
|
?._subst(_typeArguments, _declaration.variables)
|
|
?.asInstanceOf(other);
|
|
for (Interface interface in _declaration.interfaces) {
|
|
result = _intersection(
|
|
result,
|
|
interface
|
|
._subst(_typeArguments, _declaration.variables)
|
|
.asInstanceOf(other));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ReifiedType asInstanceOfType(Interface type) {
|
|
return asInstanceOf(type._declaration);
|
|
}
|
|
|
|
Interface _subst(List<ReifiedType> arguments, List<ReifiedType> parameters) {
|
|
List<ReifiedType> copy;
|
|
int index = 0;
|
|
for (ReifiedType typeArgument in _typeArguments) {
|
|
ReifiedType substitution = typeArgument._subst(arguments, parameters);
|
|
if (substitution != typeArgument) {
|
|
if (copy == null) {
|
|
copy = new List<ReifiedType>.from(_typeArguments);
|
|
}
|
|
copy[index] = substitution;
|
|
}
|
|
index++;
|
|
}
|
|
return copy == null ? this : new Interface(_declaration, copy);
|
|
}
|
|
|
|
String toString() {
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.write(_declaration.name);
|
|
if (_typeArguments.isNotEmpty) {
|
|
sb.write("<");
|
|
sb.writeAll(_typeArguments, ", ");
|
|
sb.write(">");
|
|
}
|
|
return "$sb";
|
|
}
|
|
|
|
int get hashCode {
|
|
int code = 23;
|
|
code = (71 * code + _declaration.hashCode) & 0x3fffffff;
|
|
for (ReifiedType typeArgument in _typeArguments) {
|
|
code = (71 * code + typeArgument.hashCode) & 0x3fffffff;
|
|
}
|
|
return code;
|
|
}
|
|
|
|
bool operator ==(other) {
|
|
if (other is Interface) {
|
|
if (_declaration != other._declaration) return false;
|
|
if (identical(_typeArguments, other._typeArguments)) return true;
|
|
assert(_typeArguments.length == other._typeArguments.length);
|
|
for (int i = 0; i < _typeArguments.length; i++) {
|
|
if (_typeArguments[i] != other._typeArguments[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Represents the intersection type of [typeA] and [typeB]. The intersection
|
|
/// type represents a type that is a subtype of both [typeA] and [typeB].
|
|
///
|
|
/// This type is produced when a class implements the same interface twice with
|
|
/// different type arguments. For example:
|
|
///
|
|
/// abstract class MyNumberList implements List<int>, List<double> {}
|
|
///
|
|
/// Can lead to this intersection type:
|
|
///
|
|
/// new Intersection([[List<int>]], [[List<double>]])
|
|
///
|
|
/// For example,
|
|
///
|
|
/// [[MyNumberList]].asInstanceOf([[List]]) ->
|
|
/// new Intersection([[List<int>]], [[List<double>]])
|
|
///
|
|
/// Note: sometimes, people confuse this with union types. However the union
|
|
/// type of `A` and `B` would be anything that is a subtype of either `A` or
|
|
/// `B`.
|
|
///
|
|
/// See [Intersection types]
|
|
/// (https://en.wikipedia.org/wiki/Type_system#Intersection_types).
|
|
class Intersection extends ReifiedType {
|
|
final ReifiedType typeA;
|
|
final ReifiedType typeB;
|
|
|
|
const Intersection(this.typeA, this.typeB) : super(Kind.Intersection);
|
|
|
|
bool _isMoreSpecificThan(ReifiedType type) {
|
|
// In the above example, `MyNumberList` is a subtype of List<int> *or*
|
|
// List<double>.
|
|
return typeA._isMoreSpecificThan(type) || typeB._isMoreSpecificThan(type);
|
|
}
|
|
|
|
ReifiedType _subst(
|
|
List<ReifiedType> arguments, List<ReifiedType> parameters) {
|
|
ReifiedType aSubstitution = typeA._subst(arguments, parameters);
|
|
ReifiedType bSubstitution = typeB._subst(arguments, parameters);
|
|
return (aSubstitution == typeA && bSubstitution == typeB)
|
|
? this
|
|
: _intersection(aSubstitution, bSubstitution);
|
|
}
|
|
|
|
String toString() => "{ $typeA, $typeB }";
|
|
}
|
|
|
|
/// Represents a type variable aka type parameter.
|
|
///
|
|
/// For example, this class:
|
|
///
|
|
/// class Box<T> {}
|
|
///
|
|
/// Defines one type variable. In the type `Box<int>`, there are no type
|
|
/// variables. However, `int` is a type argument to the type
|
|
/// parameter/variable `T`.
|
|
class TypeVariable extends ReifiedType {
|
|
final int _id;
|
|
|
|
// TODO(ahe): Do we need to reify bounds?
|
|
ReifiedType bound;
|
|
|
|
TypeVariable(this._id) : super(Kind.Variable);
|
|
|
|
bool _isMoreSpecificThan(ReifiedType type) {
|
|
if (type == this || type._isDynamic || type._isObject) return true;
|
|
// The bounds of a type variable may contain a cycle, such as:
|
|
//
|
|
// class C<T extends S, S extends T> {}
|
|
//
|
|
// We use the [tortoise and hare algorithm]
|
|
// (https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare) to
|
|
// handle cycles.
|
|
ReifiedType tortoise = bound;
|
|
if (tortoise == type) return true;
|
|
ReifiedType hare = getBoundOrNull(bound);
|
|
while (tortoise != hare) {
|
|
tortoise = getBoundOrNull(tortoise);
|
|
if (tortoise == type) return true;
|
|
hare = getBoundOrNull(getBoundOrNull(hare));
|
|
}
|
|
// Here we know that `tortoise == hare`. Either they're both `null` or we
|
|
// detected a cycle.
|
|
if (tortoise != null) {
|
|
// There was a cycle of type variables in the bounds, but it didn't
|
|
// involve [type]. The variable [tortoise] visited all the type variables
|
|
// in the cycle (at least once), and each time we compared it to [type].
|
|
return false;
|
|
}
|
|
// There are no cycles and it's safe to recurse on [bound].
|
|
return bound._isMoreSpecificThan(type);
|
|
}
|
|
|
|
ReifiedType _subst(
|
|
List<ReifiedType> arguments, List<ReifiedType> parameters) {
|
|
int index = 0;
|
|
for (ReifiedType parameter in parameters) {
|
|
if (this == parameter) return arguments[index];
|
|
index++;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
String toString() => "#$_id";
|
|
}
|
|
|
|
/// Represents a function type.
|
|
class FunctionType extends ReifiedType {
|
|
/// Normally, the [Interface] representing [Function]. But an
|
|
/// implementation-specific subtype of [Function] may also be used.
|
|
final ReifiedType _supertype;
|
|
|
|
final ReifiedType _returnType;
|
|
|
|
/// Encodes number of optional parameters and if the optional parameters are
|
|
/// named.
|
|
final int _data;
|
|
|
|
/// Encodes the argument types. Positional parameters use one element, the
|
|
/// type; named parameters use two, the name [String] and type. Named
|
|
/// parameters must be sorted by name.
|
|
final List _encodedParameters;
|
|
|
|
static const FunctionTypeRelation subtypeRelation =
|
|
const FunctionSubtypeRelation();
|
|
|
|
static const FunctionTypeRelation moreSpecificRelation =
|
|
const FunctionMoreSpecificRelation();
|
|
|
|
const FunctionType(
|
|
this._supertype, this._returnType, this._data, this._encodedParameters)
|
|
: super(Kind.Function);
|
|
|
|
bool get hasNamedParameters => (_data & 1) == 1;
|
|
|
|
int get optionalParameters => _data >> 1;
|
|
|
|
int get parameters {
|
|
return hasNamedParameters
|
|
? _encodedParameters.length - optionalParameters
|
|
: _encodedParameters.length;
|
|
}
|
|
|
|
int get requiredParameters {
|
|
return hasNamedParameters
|
|
? _encodedParameters.length - optionalParameters * 2
|
|
: _encodedParameters.length - optionalParameters;
|
|
}
|
|
|
|
bool _isSubtypeOf(ReifiedType type) => subtypeRelation.areRelated(this, type);
|
|
|
|
bool _isMoreSpecificThan(ReifiedType type) {
|
|
return moreSpecificRelation.areRelated(this, type);
|
|
}
|
|
|
|
FunctionType _subst(
|
|
List<ReifiedType> arguments, List<ReifiedType> parameters) {
|
|
List substitutedParameters;
|
|
int positionalParameters =
|
|
hasNamedParameters ? requiredParameters : this.parameters;
|
|
for (int i = 0; i < _encodedParameters.length; i++) {
|
|
if (i >= positionalParameters) {
|
|
// Skip the name of a named parameter.
|
|
i++;
|
|
}
|
|
ReifiedType type = _encodedParameters[i];
|
|
ReifiedType substitution = type._subst(arguments, parameters);
|
|
if (substitution != type) {
|
|
if (substitutedParameters == null) {
|
|
substitutedParameters = new List.from(_encodedParameters);
|
|
}
|
|
substitutedParameters[i] = substitution;
|
|
}
|
|
}
|
|
ReifiedType substitutedReturnType =
|
|
_returnType._subst(arguments, parameters);
|
|
if (substitutedParameters == null) {
|
|
if (_returnType == substitutedReturnType) return this;
|
|
substitutedParameters = _encodedParameters;
|
|
}
|
|
return new FunctionType(
|
|
_supertype, substitutedReturnType, _data, substitutedParameters);
|
|
}
|
|
|
|
String toString() {
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.write("(");
|
|
bool first = true;
|
|
for (int i = 0; i < requiredParameters; i++) {
|
|
if (!first) {
|
|
sb.write(", ");
|
|
}
|
|
sb.write(_encodedParameters[i]);
|
|
first = false;
|
|
}
|
|
if (requiredParameters != parameters) {
|
|
if (!first) {
|
|
sb.write(", ");
|
|
}
|
|
if (hasNamedParameters) {
|
|
sb.write("{");
|
|
first = true;
|
|
for (int i = requiredParameters;
|
|
i < _encodedParameters.length;
|
|
i += 2) {
|
|
if (!first) {
|
|
sb.write(", ");
|
|
}
|
|
sb.write(_encodedParameters[i + 1]);
|
|
sb.write(" ");
|
|
sb.write(_encodedParameters[i]);
|
|
first = false;
|
|
}
|
|
sb.write("}");
|
|
} else {
|
|
sb.write("[");
|
|
first = true;
|
|
for (int i = requiredParameters; i < _encodedParameters.length; i++) {
|
|
if (!first) {
|
|
sb.write(", ");
|
|
}
|
|
sb.write(_encodedParameters[i]);
|
|
first = false;
|
|
}
|
|
sb.write("]");
|
|
}
|
|
}
|
|
sb.write(") -> ");
|
|
sb.write(_returnType);
|
|
return "$sb";
|
|
}
|
|
}
|
|
|
|
abstract class FunctionTypeRelation {
|
|
const FunctionTypeRelation();
|
|
|
|
bool areRelated(FunctionType self, ReifiedType type, {bool isMoreSpecific}) {
|
|
if (!type._isFunction) {
|
|
return arePartsRelated(self._supertype, type);
|
|
}
|
|
FunctionType other = type;
|
|
if (!other._returnType._isVoid) {
|
|
if (!arePartsRelated(self._returnType, other._returnType)) return false;
|
|
}
|
|
int positionalParameters =
|
|
self.hasNamedParameters ? self.requiredParameters : self.parameters;
|
|
int otherPositionalParameters =
|
|
other.hasNamedParameters ? other.requiredParameters : other.parameters;
|
|
if (self.requiredParameters > other.requiredParameters) return false;
|
|
if (positionalParameters < otherPositionalParameters) return false;
|
|
|
|
for (int i = 0; i < otherPositionalParameters; i++) {
|
|
if (!arePartsRelated(
|
|
self._encodedParameters[i], other._encodedParameters[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!other.hasNamedParameters) true;
|
|
|
|
int j = positionalParameters;
|
|
for (int i = otherPositionalParameters;
|
|
i < other._encodedParameters.length;
|
|
i += 2) {
|
|
String name = other._encodedParameters[i];
|
|
for (; j < self._encodedParameters.length; j += 2) {
|
|
if (self._encodedParameters[j] == name) break;
|
|
}
|
|
if (j == self._encodedParameters.length) return false;
|
|
if (!arePartsRelated(
|
|
self._encodedParameters[j + 1], other._encodedParameters[i + 1])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool arePartsRelated(ReifiedType a, ReifiedType b);
|
|
}
|
|
|
|
class FunctionSubtypeRelation extends FunctionTypeRelation {
|
|
const FunctionSubtypeRelation();
|
|
|
|
bool arePartsRelated(ReifiedType a, ReifiedType b) => a._isAssignableTo(b);
|
|
}
|
|
|
|
class FunctionMoreSpecificRelation extends FunctionTypeRelation {
|
|
const FunctionMoreSpecificRelation();
|
|
|
|
bool arePartsRelated(ReifiedType a, ReifiedType b) =>
|
|
a._isMoreSpecificThan(b);
|
|
}
|
|
|
|
/// If [type] is a type variable, return its bound. Otherwise `null`.
|
|
ReifiedType getBoundOrNull(ReifiedType type) {
|
|
if (type == null) return null;
|
|
if (!type._isVariable) return null;
|
|
TypeVariable tv = type;
|
|
return tv.bound;
|
|
}
|