| Index: pkg/kernel/runtime/reify/types.dart
|
| diff --git a/pkg/kernel/runtime/reify/types.dart b/pkg/kernel/runtime/reify/types.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..630ca7da74c9e18a63a9fc35e7934af11124cf83
|
| --- /dev/null
|
| +++ b/pkg/kernel/runtime/reify/types.dart
|
| @@ -0,0 +1,678 @@
|
| +// 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 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;
|
| +}
|
|
|