Index: lib/compiler/implementation/typechecker.dart |
diff --git a/lib/compiler/implementation/typechecker.dart b/lib/compiler/implementation/typechecker.dart |
index f5a600fa3e08f04b40f497efe977c7630b43dbe0..440c60b7f974d75817b8d7a0a1af6f96da027bef 100644 |
--- a/lib/compiler/implementation/typechecker.dart |
+++ b/lib/compiler/implementation/typechecker.dart |
@@ -27,14 +27,64 @@ class TypeCheckerTask extends CompilerTask { |
interface Type { |
SourceString get name(); |
Element get element(); |
+ |
+ /** |
+ * Performs the substitution [arguments[i]/parameters[i]]this. |
+ * The notation is known from this lambda calculus rule: |
+ * (lambda x.e0)e1 -> [e1/x]e0. |
+ * |
+ * See [TypeVariableType] for a motivation for this method. |
+ */ |
+ Type subst(Link<Type> arguments, List<Type> parameters); |
} |
+/** |
+ * Represents a type variable, that is the type parameters of a class |
+ * or interface type. For example, in class Array<E> { ... }`, |
+ * E is a type variable. |
+ * |
+ * Each class/interface should have its own unique type variables, |
+ * one for each type parameter. A class/interface with type parameters |
+ * is said to be parameterized or generic. |
+ * |
+ * Non-static members, constructors, and factories of generic |
+ * class/interface can refer to type variables of the current class |
+ * (not of supertypes). |
+ * |
+ * When using a generic type, also known as an application or |
+ * instantiation of the type, the actual type arguments should be |
+ * substituted for the type variables in the class declaration. |
+ * |
+ * For example, given a box, `class Box<T> { T value; }`, the |
+ * type of the expression `new Box<String>().value` is |
+ * [String] because we must substitute `String` for the |
+ * the type variable `T`. |
+ */ |
class TypeVariableType implements Type { |
final SourceString name; |
TypeVariableElement element; |
TypeVariableType(this.name, [this.element]); |
- toString() => name.slowToString(); |
+ Type subst(Link<Type> arguments, List<Type> parameters) { |
+ if (arguments.isEmpty()) { |
+ // Return fast on empty substitutions. |
+ return this; |
+ } |
+ Link<Type> argumentLink = arguments; |
+ Iterator<Type> parameterIterator = parameters.iterator(); |
+ while (!argumentLink.isEmpty() && parameterIterator.hasNext()) { |
+ Type parameter = parameterIterator.next(); |
+ Type argument = argumentLink.head; |
+ if (parameter === this) { |
+ return argument; |
+ } |
+ argumentLink = argumentLink.tail; |
+ } |
+ // The type variable was not substituted. |
+ return this; |
+ } |
+ |
+ String toString() => name.slowToString(); |
} |
/** |
@@ -57,6 +107,11 @@ class StatementType implements Type { |
return (this === other) ? this : MAYBE_RETURNING; |
} |
+ Type subst(Link<Type> arguments, List<Type> parameters) { |
+ // Statement types are not substitutable. |
+ return this; |
+ } |
+ |
String toString() => stringName; |
} |
@@ -65,7 +120,12 @@ class VoidType implements Type { |
SourceString get name() => element.name; |
final VoidElement element; |
- toString() => name.slowToString(); |
+ Type subst(Link<Type> arguments, List<Type> parameters) { |
+ // Void cannot be substituted. |
+ return this; |
+ } |
+ |
+ String toString() => name.slowToString(); |
} |
class InterfaceType implements Type { |
@@ -77,7 +137,34 @@ class InterfaceType implements Type { |
SourceString get name() => element.name; |
- toString() { |
+ Type subst(Link<Type> replacements, List<Type> parameters) { |
+ if (arguments.isEmpty()) { |
+ // Return fast on non-generic types. |
+ return this; |
+ } |
+ if (replacements.isEmpty()) { |
+ // Return fast on empty substitutions. |
+ return this; |
+ } |
+ bool changed = false; |
+ var argumentsBuilder = new LinkBuilder<Type>(); |
+ Link<Type> argument = arguments; |
+ while (!argument.isEmpty()) { |
+ var replacement = argument.head.subst(replacements, parameters); |
+ if (!changed && replacement !== argument.head) { |
+ changed = true; |
+ } |
+ argumentsBuilder.addLast(replacement); |
+ argument = argument.tail; |
+ } |
+ if (changed) { |
+ // Create a new type only if necessary. |
+ return new InterfaceType(element, argumentsBuilder.toLink()); |
+ } |
+ return this; |
+ } |
+ |
+ String toString() { |
StringBuffer sb = new StringBuffer(); |
sb.add(name.slowToString()); |
if (!arguments.isEmpty()) { |
@@ -97,7 +184,32 @@ class FunctionType implements Type { |
const FunctionType(Type this.returnType, Link<Type> this.parameterTypes, |
Element this.element); |
- toString() { |
+ Type subst(Link<Type> arguments, List<Type> parameters) { |
+ if (arguments.isEmpty()) { |
+ // Return fast on empty substitutions. |
+ return this; |
+ } |
+ var newReturnType = returnType.subst(arguments, parameters); |
+ bool changed = newReturnType !== returnType; |
+ var parameterBuilder = new LinkBuilder<Type>(); |
+ Link<Type> parameterType = parameterTypes; |
+ while (!parameterType.isEmpty()) { |
+ var replacement = parameterType.head.subst(arguments, parameters); |
+ if (!changed && replacement !== parameterType.head) { |
+ changed = true; |
+ } |
+ parameterBuilder.addLast(replacement); |
+ parameterType = parameterType.tail; |
+ } |
+ if (changed) { |
+ // Create a new type only if necessary. |
+ return new FunctionType(newReturnType, parameterBuilder.toLink(), |
+ element); |
+ } |
+ return this; |
+ } |
+ |
+ String toString() { |
StringBuffer sb = new StringBuffer(); |
bool first = true; |
sb.add('('); |