blob: 96ffcd8ebbac06677d6c82f1b98efb6079fc3036 [file] [log] [blame]
// 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;
}