OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 class TypeCheckerTask extends CompilerTask { | 5 class TypeCheckerTask extends CompilerTask { |
6 TypeCheckerTask(Compiler compiler) : super(compiler); | 6 TypeCheckerTask(Compiler compiler) : super(compiler); |
7 String get name() => "Type checker"; | 7 String get name() => "Type checker"; |
8 | 8 |
9 static final bool LOG_FAILURES = false; | 9 static final bool LOG_FAILURES = false; |
10 | 10 |
(...skipping 11 matching lines...) Expand all Loading... |
22 } | 22 } |
23 }); | 23 }); |
24 } | 24 } |
25 } | 25 } |
26 | 26 |
27 interface Type { | 27 interface Type { |
28 SourceString get name(); | 28 SourceString get name(); |
29 Element get element(); | 29 Element get element(); |
30 | 30 |
31 /** | 31 /** |
32 * Performs the substitution [arguments[i]/parameters[i]]this. | 32 * Performs the substitution `[arguments[i]/parameters[i]]this`, where |
| 33 * `parameters[i]` denotes the i'th type variable in [typeParameters]. |
33 * The notation is known from this lambda calculus rule: | 34 * The notation is known from this lambda calculus rule: |
34 * (lambda x.e0)e1 -> [e1/x]e0. | 35 * |
| 36 * (lambda x.e0)e1 -> [e1/x]e0. |
35 * | 37 * |
36 * See [TypeVariableType] for a motivation for this method. | 38 * See [TypeVariableType] for a motivation for this method. |
37 */ | 39 */ |
38 Type subst(Link<Type> arguments, List<Type> parameters); | 40 Type subst(Compiler compiler, Link<Type> arguments, |
| 41 LinkedHashMap<SourceString, TypeVariableElement> typeParameters); |
39 } | 42 } |
40 | 43 |
41 /** | 44 /** |
42 * Represents a type variable, that is the type parameters of a class | 45 * Represents a type variable, that is the type parameters of a class |
43 * or interface type. For example, in class Array<E> { ... }`, | 46 * or interface type. For example, in class Array<E> { ... }`, |
44 * E is a type variable. | 47 * E is a type variable. |
45 * | 48 * |
46 * Each class/interface should have its own unique type variables, | 49 * Each class/interface should have its own unique type variables, |
47 * one for each type parameter. A class/interface with type parameters | 50 * one for each type parameter. A class/interface with type parameters |
48 * is said to be parameterized or generic. | 51 * is said to be parameterized or generic. |
49 * | 52 * |
50 * Non-static members, constructors, and factories of generic | 53 * Non-static members, constructors, and factories of generic |
51 * class/interface can refer to type variables of the current class | 54 * class/interface can refer to type variables of the current class |
52 * (not of supertypes). | 55 * (not of supertypes). |
53 * | 56 * |
54 * When using a generic type, also known as an application or | 57 * When using a generic type, also known as an application or |
55 * instantiation of the type, the actual type arguments should be | 58 * instantiation of the type, the actual type arguments should be |
56 * substituted for the type variables in the class declaration. | 59 * substituted for the type variables in the class declaration. |
57 * | 60 * |
58 * For example, given a box, `class Box<T> { T value; }`, the | 61 * For example, given a box, `class Box<T> { T value; }`, the |
59 * type of the expression `new Box<String>().value` is | 62 * type of the expression `new Box<String>().value` is |
60 * [String] because we must substitute `String` for the | 63 * [String] because we must substitute `String` for the |
61 * the type variable `T`. | 64 * the type variable `T`. |
62 */ | 65 */ |
63 class TypeVariableType implements Type { | 66 class TypeVariableType implements Type { |
64 final SourceString name; | 67 final SourceString name; |
65 TypeVariableElement element; | 68 TypeVariableElement element; |
66 TypeVariableType(this.name, [this.element]); | 69 TypeVariableType(this.name, [this.element]); |
67 | 70 |
68 Type subst(Link<Type> arguments, List<Type> parameters) { | 71 Type subst(Compiler compiler, Link<Type> arguments, |
69 if (arguments.isEmpty()) { | 72 LinkedHashMap<SourceString, TypeVariableElement> typeParameters) { |
| 73 if (typeParameters.isEmpty()) { |
70 // Return fast on empty substitutions. | 74 // Return fast on empty substitutions. |
71 return this; | 75 return this; |
72 } | 76 } |
73 Link<Type> argumentLink = arguments; | 77 Iterator<TypeVariableElement> parameterIterator = |
74 Iterator<Type> parameterIterator = parameters.iterator(); | 78 typeParameters.getValues().iterator(); |
75 while (!argumentLink.isEmpty() && parameterIterator.hasNext()) { | 79 if (arguments.isEmpty()) { |
76 Type parameter = parameterIterator.next(); | 80 // Raw substitution: Dynamic replaces parameters. |
77 Type argument = argumentLink.head; | 81 while (parameterIterator.hasNext()) { |
78 if (parameter === this) { | 82 TypeVariableType parameter = parameterIterator.next().type; |
79 return argument; | 83 if (parameter == this) { |
| 84 return compiler.types.dynamicType; |
| 85 } |
80 } | 86 } |
81 argumentLink = argumentLink.tail; | 87 } else { |
| 88 // Normal substitution. |
| 89 Link<Type> argumentLink = arguments; |
| 90 while (!argumentLink.isEmpty() && parameterIterator.hasNext()) { |
| 91 TypeVariableType parameter = parameterIterator.next().type; |
| 92 Type argument = argumentLink.head; |
| 93 if (parameter == this) { |
| 94 return argument; |
| 95 } |
| 96 argumentLink = argumentLink.tail; |
| 97 } |
82 } | 98 } |
83 // The type variable was not substituted. | 99 // The type variable was not substituted. |
84 return this; | 100 return this; |
85 } | 101 } |
86 | 102 |
87 String toString() => name.slowToString(); | 103 String toString() => name.slowToString(); |
88 } | 104 } |
89 | 105 |
90 /** | 106 /** |
91 * A statement type tracks whether a statement returns or may return. | 107 * A statement type tracks whether a statement returns or may return. |
92 */ | 108 */ |
93 class StatementType implements Type { | 109 class StatementType implements Type { |
94 final String stringName; | 110 final String stringName; |
95 Element get element() => null; | 111 Element get element() => null; |
96 | 112 |
97 SourceString get name() => new SourceString(stringName); | 113 SourceString get name() => new SourceString(stringName); |
98 | 114 |
99 const StatementType(this.stringName); | 115 const StatementType(this.stringName); |
100 | 116 |
101 static final RETURNING = const StatementType('<returning>'); | 117 static final RETURNING = const StatementType('<returning>'); |
102 static final NOT_RETURNING = const StatementType('<not returning>'); | 118 static final NOT_RETURNING = const StatementType('<not returning>'); |
103 static final MAYBE_RETURNING = const StatementType('<maybe returning>'); | 119 static final MAYBE_RETURNING = const StatementType('<maybe returning>'); |
104 | 120 |
105 /** Combine the information about two control-flow edges that are joined. */ | 121 /** Combine the information about two control-flow edges that are joined. */ |
106 StatementType join(StatementType other) { | 122 StatementType join(StatementType other) { |
107 return (this === other) ? this : MAYBE_RETURNING; | 123 return (this === other) ? this : MAYBE_RETURNING; |
108 } | 124 } |
109 | 125 |
110 Type subst(Link<Type> arguments, List<Type> parameters) { | 126 Type subst(Compiler compiler, Link<Type> arguments, |
| 127 LinkedHashMap<SourceString, TypeVariableElement> typeParameters) { |
111 // Statement types are not substitutable. | 128 // Statement types are not substitutable. |
112 return this; | 129 return this; |
113 } | 130 } |
114 | 131 |
115 String toString() => stringName; | 132 String toString() => stringName; |
116 } | 133 } |
117 | 134 |
118 class VoidType implements Type { | 135 class VoidType implements Type { |
119 const VoidType(this.element); | 136 const VoidType(this.element); |
120 SourceString get name() => element.name; | 137 SourceString get name() => element.name; |
121 final VoidElement element; | 138 final VoidElement element; |
122 | 139 |
123 Type subst(Link<Type> arguments, List<Type> parameters) { | 140 Type subst(Compiler compiler, Link<Type> arguments, |
| 141 LinkedHashMap<SourceString, TypeVariableElement> typeParameters) { |
124 // Void cannot be substituted. | 142 // Void cannot be substituted. |
125 return this; | 143 return this; |
126 } | 144 } |
127 | 145 |
128 String toString() => name.slowToString(); | 146 String toString() => name.slowToString(); |
129 } | 147 } |
130 | 148 |
131 class InterfaceType implements Type { | 149 class InterfaceType implements Type { |
132 final Element element; | 150 final ClassElement element; |
133 final Link<Type> arguments; | 151 final Link<Type> typeArguments; |
134 | 152 |
135 const InterfaceType(this.element, | 153 const InterfaceType(this.element, |
136 [this.arguments = const EmptyLink<Type>()]); | 154 [this.typeArguments = const EmptyLink<Type>()]); |
137 | 155 |
138 SourceString get name() => element.name; | 156 SourceString get name() => element.name; |
139 | 157 |
140 Type subst(Link<Type> replacements, List<Type> parameters) { | 158 Type subst(Compiler compiler, Link<Type> arguments, |
141 if (arguments.isEmpty()) { | 159 LinkedHashMap<SourceString, TypeVariableElement> typeParameters) { |
| 160 if (typeArguments.isEmpty()) { |
142 // Return fast on non-generic types. | 161 // Return fast on non-generic types. |
143 return this; | 162 return this; |
144 } | 163 } |
145 if (replacements.isEmpty()) { | 164 if (typeParameters.isEmpty()) { |
146 // Return fast on empty substitutions. | 165 // Return fast on empty substitutions. |
147 return this; | 166 return this; |
148 } | 167 } |
| 168 if (arguments.isEmpty()) { |
| 169 // Return the 'raw' type on raw substitutions. |
| 170 return new InterfaceType(element); |
| 171 } |
149 bool changed = false; | 172 bool changed = false; |
150 var argumentsBuilder = new LinkBuilder<Type>(); | 173 var argumentsBuilder = new LinkBuilder<Type>(); |
151 Link<Type> argument = arguments; | 174 Link<Type> typeArgument = typeArguments; |
152 while (!argument.isEmpty()) { | 175 while (!typeArgument.isEmpty()) { |
153 var replacement = argument.head.subst(replacements, parameters); | 176 var argument = |
154 if (!changed && replacement !== argument.head) { | 177 typeArgument.head.subst(compiler, arguments, typeParameters); |
| 178 if (!changed && argument !== typeArgument.head) { |
155 changed = true; | 179 changed = true; |
156 } | 180 } |
157 argumentsBuilder.addLast(replacement); | 181 argumentsBuilder.addLast(argument); |
158 argument = argument.tail; | 182 typeArgument = typeArgument.tail; |
159 } | 183 } |
160 if (changed) { | 184 if (changed) { |
161 // Create a new type only if necessary. | 185 // Create a new type only if necessary. |
162 return new InterfaceType(element, argumentsBuilder.toLink()); | 186 return new InterfaceType(element, argumentsBuilder.toLink()); |
163 } | 187 } |
164 return this; | 188 return this; |
165 } | 189 } |
166 | 190 |
| 191 /** |
| 192 * Finds the method, field or property on this interface type named [name]. |
| 193 */ |
| 194 Member lookupMember(Compiler compiler, SourceString name) { |
| 195 ClassElement classElement = element; |
| 196 InterfaceType receiver = this; |
| 197 InterfaceType declarer = receiver; |
| 198 Element member = classElement.lookupLocalMember(name); |
| 199 if (member === null) { |
| 200 classElement.ensureResolved(compiler); |
| 201 for (Link<InterfaceType> supertypes = classElement.allSupertypes; |
| 202 !supertypes.isEmpty() && member === null; |
| 203 supertypes = supertypes.tail) { |
| 204 declarer = supertypes.head; |
| 205 ClassElement lookupTarget = declarer.element; |
| 206 member = lookupTarget.lookupLocalMember(name); |
| 207 } |
| 208 } |
| 209 if (member == null) { |
| 210 return null; |
| 211 } |
| 212 if (member.kind == ElementKind.FUNCTION || |
| 213 member.kind == ElementKind.ABSTRACT_FIELD || |
| 214 member.kind == ElementKind.FIELD) { |
| 215 return new Member(receiver, declarer, member); |
| 216 } |
| 217 return null; |
| 218 } |
| 219 |
167 String toString() { | 220 String toString() { |
168 StringBuffer sb = new StringBuffer(); | 221 StringBuffer sb = new StringBuffer(); |
169 sb.add(name.slowToString()); | 222 sb.add(name.slowToString()); |
170 if (!arguments.isEmpty()) { | 223 if (!typeArguments.isEmpty()) { |
171 sb.add('<'); | 224 sb.add('<'); |
172 arguments.printOn(sb, ', '); | 225 typeArguments.printOn(sb, ', '); |
173 sb.add('>'); | 226 sb.add('>'); |
174 } | 227 } |
175 return sb.toString(); | 228 return sb.toString(); |
176 } | 229 } |
177 } | 230 } |
178 | 231 |
179 class FunctionType implements Type { | 232 class FunctionType implements Type { |
180 final Element element; | 233 final Element element; |
181 final Type returnType; | 234 final Type returnType; |
182 final Link<Type> parameterTypes; | 235 final Link<Type> parameterTypes; |
183 | 236 |
184 const FunctionType(Type this.returnType, Link<Type> this.parameterTypes, | 237 const FunctionType(Type this.returnType, Link<Type> this.parameterTypes, |
185 Element this.element); | 238 Element this.element); |
186 | 239 |
187 Type subst(Link<Type> arguments, List<Type> parameters) { | 240 Type subst(Compiler compiler, Link<Type> arguments, |
188 if (arguments.isEmpty()) { | 241 LinkedHashMap<SourceString, TypeVariableElement> typeParameters) { |
| 242 if (typeParameters.isEmpty()) { |
189 // Return fast on empty substitutions. | 243 // Return fast on empty substitutions. |
190 return this; | 244 return this; |
191 } | 245 } |
192 var newReturnType = returnType.subst(arguments, parameters); | 246 var newReturnType = returnType.subst(compiler, arguments, typeParameters); |
193 bool changed = newReturnType !== returnType; | 247 bool changed = newReturnType !== returnType; |
194 var parameterBuilder = new LinkBuilder<Type>(); | 248 var newParameterTypesBuilder = new LinkBuilder<Type>(); |
195 Link<Type> parameterType = parameterTypes; | 249 Link<Type> parameterType = parameterTypes; |
196 while (!parameterType.isEmpty()) { | 250 while (!parameterType.isEmpty()) { |
197 var replacement = parameterType.head.subst(arguments, parameters); | 251 var newParameterType = |
198 if (!changed && replacement !== parameterType.head) { | 252 parameterType.head.subst(compiler, arguments, typeParameters); |
| 253 if (!changed && newParameterType !== parameterType.head) { |
199 changed = true; | 254 changed = true; |
200 } | 255 } |
201 parameterBuilder.addLast(replacement); | 256 newParameterTypesBuilder.addLast(newParameterType); |
202 parameterType = parameterType.tail; | 257 parameterType = parameterType.tail; |
203 } | 258 } |
204 if (changed) { | 259 if (changed) { |
205 // Create a new type only if necessary. | 260 // Create a new type only if necessary. |
206 return new FunctionType(newReturnType, parameterBuilder.toLink(), | 261 return new FunctionType(newReturnType, newParameterTypesBuilder.toLink(), |
207 element); | 262 element); |
208 } | 263 } |
209 return this; | 264 return this; |
210 } | 265 } |
211 | 266 |
212 String toString() { | 267 String toString() { |
213 StringBuffer sb = new StringBuffer(); | 268 StringBuffer sb = new StringBuffer(); |
214 bool first = true; | 269 bool first = true; |
215 sb.add('('); | 270 sb.add('('); |
216 parameterTypes.printOn(sb, ', '); | 271 parameterTypes.printOn(sb, ', '); |
217 sb.add(') -> ${returnType}'); | 272 sb.add(') -> ${returnType}'); |
218 return sb.toString(); | 273 return sb.toString(); |
219 } | 274 } |
220 | 275 |
221 SourceString get name() => const SourceString('Function'); | 276 SourceString get name() => const SourceString('Function'); |
222 | 277 |
223 int computeArity() { | 278 int computeArity() { |
224 int arity = 0; | 279 int arity = 0; |
225 parameterTypes.forEach((_) { arity++; }); | 280 parameterTypes.forEach((_) { arity++; }); |
226 return arity; | 281 return arity; |
227 } | 282 } |
228 } | 283 } |
229 | 284 |
| 285 /** |
| 286 * Member encapsulates a member (constructor, method, field, property, getter, |
| 287 * or setter) with the types of the declarer and receiver in order to do |
| 288 * substitution on the member type. |
| 289 * |
| 290 * Consider for instance these classes and the variable `B<String> b`: |
| 291 * |
| 292 * class A<E> { |
| 293 * E field; |
| 294 * } |
| 295 * class B<F> extends A<F> {} |
| 296 * |
| 297 * In a [Member] for `b.field` the [receiver] is `B<String>` and the declarer |
| 298 * is `A<F>`, which is the supertype of `B<F>` from which `field` has been |
| 299 * inherited. To compute the type of `b.field` we must first substitute `E` |
| 300 * by `F` using the relation between `A<E>` and `A<F>`, and then `F` by `String` |
| 301 * using the relation between `B<F>` and `B<String>`. |
| 302 */ |
| 303 class Member { |
| 304 final InterfaceType receiver; |
| 305 final InterfaceType declarer; |
| 306 final Element element; |
| 307 |
| 308 Member(this.receiver, this.declarer, this.element); |
| 309 |
| 310 Type computeType(Compiler compiler) { |
| 311 Type type; |
| 312 if (element.kind == ElementKind.ABSTRACT_FIELD) { |
| 313 AbstractFieldElement abstractFieldElement = element; |
| 314 if (abstractFieldElement.getter != null) { |
| 315 type = abstractFieldElement.getter.computeType(compiler).returnType; |
| 316 } else { |
| 317 type = abstractFieldElement.setter.computeType( |
| 318 compiler).parameterTypes.head; |
| 319 if (type == null) { |
| 320 type = compiler.types.dynamicType; |
| 321 } |
| 322 } |
| 323 } else { |
| 324 type = element.computeType(compiler); |
| 325 } |
| 326 if (!declarer.element.typeParameters.isEmpty()) { |
| 327 type = type.subst(compiler, |
| 328 declarer.typeArguments, declarer.element.typeParameters); |
| 329 type = type.subst(compiler, |
| 330 receiver.typeArguments, receiver.element.typeParameters); |
| 331 } |
| 332 return type; |
| 333 } |
| 334 |
| 335 String toString() { |
| 336 return '$receiver.$element'; |
| 337 } |
| 338 } |
| 339 |
230 class Types { | 340 class Types { |
231 final VoidType voidType; | 341 final VoidType voidType; |
232 final InterfaceType dynamicType; | 342 final InterfaceType dynamicType; |
| 343 final Element functionElement; |
233 | 344 |
234 Types(Element dynamicElement) | 345 Types(Element dynamicElement, Element functionElement) |
235 : this.with(dynamicElement, new LibraryElement(new Script(null, null))); | 346 : this.with(dynamicElement, functionElement, |
| 347 new LibraryElement(new Script(null, null))); |
236 | 348 |
237 // TODO(karlklose): should we have a class Void? | 349 // TODO(karlklose): should we have a class Void? |
238 Types.with(Element dynamicElement, LibraryElement library) | 350 Types.with(Element dynamicElement, |
| 351 Element this.functionElement, |
| 352 LibraryElement library) |
239 : voidType = new VoidType(new VoidElement(library)), | 353 : voidType = new VoidType(new VoidElement(library)), |
240 dynamicType = new InterfaceType(dynamicElement); | 354 dynamicType = new InterfaceType(dynamicElement); |
241 | 355 |
242 /** Returns true if t is a subtype of s */ | 356 /** Returns true if t is a subtype of s */ |
243 bool isSubtype(Type t, Type s) { | 357 bool isSubtype(Type t, Type s) { |
244 if (t === s || t === dynamicType || s === dynamicType || | 358 if (t === s || t === dynamicType || s === dynamicType || |
245 // TODO(karlklose): Test for s.element === compiler.objectClass. | 359 // TODO(karlklose): Test for s.element === compiler.objectClass. |
246 s.name == const SourceString('Object')) return true; | 360 s.name == const SourceString('Object')) return true; |
247 if (t is VoidType) { | 361 if (t is VoidType) { |
248 return false; | 362 return false; |
249 } else if (t is InterfaceType) { | 363 } else if (t is InterfaceType) { |
250 if (s is !InterfaceType) return false; | 364 if (s is !InterfaceType) return false; |
251 ClassElement tc = t.element; | 365 ClassElement tc = t.element; |
252 if (tc === s.element) return true; | 366 if (tc === s.element) return true; |
253 for (Link<Type> supertypes = tc.allSupertypes; | 367 for (Link<Type> supertypes = tc.allSupertypes; |
254 supertypes != null && !supertypes.isEmpty(); | 368 supertypes != null && !supertypes.isEmpty(); |
255 supertypes = supertypes.tail) { | 369 supertypes = supertypes.tail) { |
256 Type supertype = supertypes.head; | 370 Type supertype = supertypes.head; |
257 if (supertype.element === s.element) return true; | 371 if (supertype.element === s.element) return true; |
258 } | 372 } |
259 return false; | 373 return false; |
260 } else if (t is FunctionType) { | 374 } else if (t is FunctionType) { |
| 375 if (s.element == functionElement) return true; |
261 if (s is !FunctionType) return false; | 376 if (s is !FunctionType) return false; |
262 FunctionType tf = t; | 377 FunctionType tf = t; |
263 FunctionType sf = s; | 378 FunctionType sf = s; |
264 Link<Type> tps = tf.parameterTypes; | 379 Link<Type> tps = tf.parameterTypes; |
265 Link<Type> sps = sf.parameterTypes; | 380 Link<Type> sps = sf.parameterTypes; |
266 while (!tps.isEmpty() && !sps.isEmpty()) { | 381 while (!tps.isEmpty() && !sps.isEmpty()) { |
267 if (!isAssignable(tps.head, sps.head)) return false; | 382 if (!isAssignable(tps.head, sps.head)) return false; |
268 tps = tps.tail; | 383 tps = tps.tail; |
269 sps = sps.tail; | 384 sps = sps.tail; |
270 } | 385 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 | 426 |
312 TypeCheckerVisitor(this.compiler, this.elements, this.types) { | 427 TypeCheckerVisitor(this.compiler, this.elements, this.types) { |
313 intType = compiler.intClass.computeType(compiler); | 428 intType = compiler.intClass.computeType(compiler); |
314 doubleType = compiler.doubleClass.computeType(compiler); | 429 doubleType = compiler.doubleClass.computeType(compiler); |
315 boolType = compiler.boolClass.computeType(compiler); | 430 boolType = compiler.boolClass.computeType(compiler); |
316 stringType = compiler.stringClass.computeType(compiler); | 431 stringType = compiler.stringClass.computeType(compiler); |
317 objectType = compiler.objectClass.computeType(compiler); | 432 objectType = compiler.objectClass.computeType(compiler); |
318 listType = compiler.listClass.computeType(compiler); | 433 listType = compiler.listClass.computeType(compiler); |
319 } | 434 } |
320 | 435 |
321 Type fail(node, [reason]) { | 436 Type fail(Node node, [reason]) { |
322 String message = 'cannot type-check'; | 437 String message = 'cannot type-check'; |
323 if (reason !== null) { | 438 if (node != null) { |
| 439 message = '$message ${node.getObjectDescription()} `$node`'; |
| 440 } |
| 441 if (reason != null) { |
324 message = '$message: $reason'; | 442 message = '$message: $reason'; |
325 } | 443 } |
326 throw new CancelTypeCheckException(node, message); | 444 throw new CancelTypeCheckException(node, message); |
327 } | 445 } |
328 | 446 |
329 reportTypeWarning(Node node, MessageKind kind, [List arguments = const []]) { | 447 reportTypeWarning(Node node, MessageKind kind, [List arguments = const []]) { |
330 compiler.reportWarning(node, new TypeWarning(kind, arguments)); | 448 compiler.reportWarning(node, new TypeWarning(kind, arguments)); |
331 } | 449 } |
332 | 450 |
333 // TODO(karlklose): remove these functions. | 451 // TODO(karlklose): remove these functions. |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
499 } | 617 } |
500 } | 618 } |
501 if (member !== null && member.kind == ElementKind.FUNCTION) { | 619 if (member !== null && member.kind == ElementKind.FUNCTION) { |
502 return computeType(member); | 620 return computeType(member); |
503 } | 621 } |
504 reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND, | 622 reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND, |
505 [classElement.name, name]); | 623 [classElement.name, name]); |
506 return types.dynamicType; | 624 return types.dynamicType; |
507 } | 625 } |
508 | 626 |
| 627 /** |
| 628 * Returns the interface type on which to lookup members. If [type] is the |
| 629 * function of a getter, the return type is returned. If [type] is a type |
| 630 * variable the bound is returned. |
| 631 */ |
| 632 InterfaceType findReceiverType(Node receiver, Type receiverType) { |
| 633 if (receiverType === null) { |
| 634 fail(receiver, 'receivertype is null'); |
| 635 } |
| 636 if (receiverType.element == compiler.dynamicClass) { |
| 637 return receiverType; |
| 638 } |
| 639 ElementKind receiverKind = receiverType.element.kind; |
| 640 if (receiverKind === ElementKind.GETTER) { |
| 641 FunctionType getterType = receiverType; |
| 642 return findReceiverType(receiver, getterType.returnType); |
| 643 } |
| 644 if (receiverKind === ElementKind.TYPEDEF) { |
| 645 // TODO(karlklose): handle typedefs. |
| 646 return null; |
| 647 } |
| 648 if (receiverKind === ElementKind.TYPE_VARIABLE) { |
| 649 TypeVariableElement typeVariableElement = receiverType.element; |
| 650 return findReceiverType(receiver, typeVariableElement.bound); |
| 651 } |
| 652 if (receiverKind !== ElementKind.CLASS) { |
| 653 fail(receiver, 'unexpected receiver kind: ${receiverKind}'); |
| 654 } |
| 655 return receiverType; |
| 656 } |
| 657 |
509 void analyzeArguments(Send send, FunctionType funType) { | 658 void analyzeArguments(Send send, FunctionType funType) { |
510 Link<Node> arguments = send.arguments; | 659 Link<Node> arguments = send.arguments; |
511 if (funType === null || funType === types.dynamicType) { | 660 if (funType === null || funType === types.dynamicType) { |
512 while(!arguments.isEmpty()) { | 661 while(!arguments.isEmpty()) { |
513 analyze(arguments.head); | 662 analyze(arguments.head); |
514 arguments = arguments.tail; | 663 arguments = arguments.tail; |
515 } | 664 } |
516 } else { | 665 } else { |
517 Link<Type> parameterTypes = funType.parameterTypes; | 666 Link<Type> parameterTypes = funType.parameterTypes; |
518 while (!arguments.isEmpty() && !parameterTypes.isEmpty()) { | 667 while (!arguments.isEmpty() && !parameterTypes.isEmpty()) { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
564 if (!arguments.isEmpty()) { | 713 if (!arguments.isEmpty()) { |
565 // TODO(karlklose): check number of arguments in validator. | 714 // TODO(karlklose): check number of arguments in validator. |
566 checkAssignable(secondArgument, boolType, secondArgumentType); | 715 checkAssignable(secondArgument, boolType, secondArgumentType); |
567 } | 716 } |
568 return boolType; | 717 return boolType; |
569 } | 718 } |
570 fail(selector, 'unexpected operator ${name}'); | 719 fail(selector, 'unexpected operator ${name}'); |
571 | 720 |
572 } else if (node.isPropertyAccess) { | 721 } else if (node.isPropertyAccess) { |
573 if (node.receiver !== null) { | 722 if (node.receiver !== null) { |
574 // TODO(karlklose): we cannot handle fields. | 723 InterfaceType receiverType = |
575 return unhandledExpression(); | 724 findReceiverType(node.receiver, analyze(node.receiver)); |
| 725 if (receiverType === null) return null; |
| 726 if (receiverType.element === compiler.dynamicClass) { |
| 727 return types.dynamicType; |
| 728 } |
| 729 |
| 730 Member member = receiverType.lookupMember(compiler, selector.source); |
| 731 if (member === null || |
| 732 !(member.element.kind === ElementKind.FIELD || |
| 733 member.element.kind === ElementKind.ABSTRACT_FIELD)) { |
| 734 reportTypeWarning(selector, MessageKind.PROPERTY_NOT_FOUND, |
| 735 [receiverType, selector.source]); |
| 736 } else { |
| 737 return member.computeType(compiler); |
| 738 } |
| 739 return types.dynamicType; |
| 740 } else { |
| 741 Element element = elements[node]; |
| 742 if (element === null) { |
| 743 return types.dynamicType; |
| 744 } |
| 745 if ((element.kind == ElementKind.FIELD || |
| 746 element.kind == ElementKind.GETTER) && |
| 747 element.isInstanceMember()) { |
| 748 // Ensure substitution of declared type variables with inherited type |
| 749 // variables. |
| 750 InterfaceType thisType = currentClass.computeType(compiler); |
| 751 Member member = thisType.lookupMember(compiler, selector.source); |
| 752 if (member != null) { |
| 753 // Should always work! |
| 754 return member.computeType(compiler); |
| 755 } |
| 756 compiler.internalError('Could not lookup resolved member ' |
| 757 '${selector.source} on ${thisType}.'); |
| 758 } |
| 759 return computeType(element); |
576 } | 760 } |
577 Element element = elements[node]; | |
578 if (element === null) return types.dynamicType; | |
579 return computeType(element); | |
580 | |
581 } else if (node.isFunctionObjectInvocation) { | 761 } else if (node.isFunctionObjectInvocation) { |
582 fail(node.receiver, 'function object invocation unimplemented'); | 762 fail(node.receiver, 'function object invocation unimplemented'); |
583 | 763 |
584 } else { | 764 } else { |
585 FunctionType computeFunType() { | 765 FunctionType computeFunType() { |
586 if (node.receiver !== null) { | 766 if (node.receiver !== null) { |
587 Type receiverType = analyze(node.receiver); | 767 InterfaceType receiverType = |
588 if (receiverType.element == compiler.dynamicClass) return null; | 768 findReceiverType(node.receiver, analyze(node.receiver)); |
589 if (receiverType === null) { | 769 if (receiverType === null) return null; |
590 fail(node.receiver, 'receivertype is null'); | 770 if (receiverType.element === compiler.dynamicClass) return null; |
591 } | 771 |
592 if (receiverType.element.kind === ElementKind.GETTER) { | 772 Member member = receiverType.lookupMember(compiler, selector.source); |
593 FunctionType getterType = receiverType; | 773 if (member === null || member.element.kind !== ElementKind.FUNCTION) { |
594 receiverType = getterType.returnType; | 774 reportTypeWarning(selector, MessageKind.METHOD_NOT_FOUND, |
595 } | 775 [receiverType, name]); |
596 ElementKind receiverKind = receiverType.element.kind; | |
597 if (receiverKind === ElementKind.TYPEDEF) { | |
598 // TODO(karlklose): handle typedefs. | |
599 return null; | 776 return null; |
600 } | 777 } |
601 if (receiverKind === ElementKind.TYPE_VARIABLE) { | 778 Type memberType = member.computeType(compiler); |
602 // TODO(karlklose): handle type variables. | 779 if (memberType.element == compiler.dynamicClass) return null; |
603 return null; | 780 if (memberType.element == compiler.functionClass) return null; |
604 } | |
605 if (receiverKind !== ElementKind.CLASS) { | |
606 fail(node.receiver, 'unexpected receiver kind: ${receiverKind}'); | |
607 } | |
608 ClassElement classElement = receiverType.element; | |
609 // TODO(karlklose): substitute type arguments. | |
610 Type memberType = | |
611 lookupMethodType(selector, classElement, selector.source); | |
612 if (memberType.element === compiler.dynamicClass) return null; | |
613 return memberType; | 781 return memberType; |
614 } else { | 782 } else { |
615 Element element = elements[node]; | 783 Element element = elements[node]; |
616 if (element === null) { | 784 if (element === null) { |
617 fail(node, 'unresolved ${node.selector}'); | 785 fail(node, 'unresolved ${node.selector}'); |
618 } else if (element.kind === ElementKind.FUNCTION) { | 786 } else if (element.kind === ElementKind.FUNCTION) { |
| 787 if (element.isInstanceMember()) { |
| 788 // Ensure substitution of declared type variables with inherited |
| 789 // type variables. |
| 790 InterfaceType thisType = currentClass.computeType(compiler); |
| 791 Member member = thisType.lookupMember(compiler, selector.source); |
| 792 if (member != null) { |
| 793 // Should always work! |
| 794 return member.computeType(compiler); |
| 795 } |
| 796 compiler.internalError('Could not lookup resolved member ' |
| 797 '${selector.source} on ${thisType}.'); |
| 798 } |
619 return computeType(element); | 799 return computeType(element); |
620 } else if (element.kind === ElementKind.FOREIGN) { | 800 } else if (element.kind === ElementKind.FOREIGN) { |
621 return null; | 801 return null; |
622 } else if (element.kind === ElementKind.VARIABLE | 802 } else if (element.kind === ElementKind.VARIABLE || |
623 || element.kind === ElementKind.FIELD) { | 803 element.kind === ElementKind.FIELD) { |
624 // TODO(karlklose): handle object invocations. | 804 // TODO(karlklose): handle object invocations. |
625 return null; | 805 return null; |
626 } else { | 806 } else { |
627 fail(node, 'unexpected element kind ${element.kind}'); | 807 fail(node, 'unexpected element kind ${element.kind}'); |
628 } | 808 } |
629 } | 809 } |
630 } | 810 } |
631 FunctionType funType = computeFunType(); | 811 FunctionType funType = computeFunType(); |
632 analyzeArguments(node, funType); | 812 analyzeArguments(node, funType); |
633 return (funType !== null) ? funType.returnType : types.dynamicType; | 813 return (funType !== null) ? funType.returnType : types.dynamicType; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
670 analyze(node.first); | 850 analyze(node.first); |
671 analyze(node.second); | 851 analyze(node.second); |
672 return stringType; | 852 return stringType; |
673 } | 853 } |
674 | 854 |
675 Type visitLiteralNull(LiteralNull node) { | 855 Type visitLiteralNull(LiteralNull node) { |
676 return types.dynamicType; | 856 return types.dynamicType; |
677 } | 857 } |
678 | 858 |
679 Type visitNewExpression(NewExpression node) { | 859 Type visitNewExpression(NewExpression node) { |
| 860 InterfaceType type = elements.getType(node.getTypeAnnotation()); |
680 Element element = elements[node.send]; | 861 Element element = elements[node.send]; |
681 analyzeArguments(node.send, computeType(element)); | 862 var member = new Member(type, |
682 return analyze(node.send.selector); | 863 element.enclosingElement.computeType(compiler), |
| 864 element); |
| 865 analyzeArguments(node.send, member.computeType(compiler)); |
| 866 return type; |
683 } | 867 } |
684 | 868 |
685 Type visitLiteralList(LiteralList node) { | 869 Type visitLiteralList(LiteralList node) { |
686 return listType; | 870 Type elementType = node.type != null |
| 871 ? elements[node.type] |
| 872 : compiler.types.dynamicType; |
| 873 var linkBuilder = new LinkBuilder<Type>(); |
| 874 linkBuilder.addLast(elementType); |
| 875 return new InterfaceType(listType.element, linkBuilder.toLink()); |
687 } | 876 } |
688 | 877 |
689 Type visitNodeList(NodeList node) { | 878 Type visitNodeList(NodeList node) { |
690 Type type = StatementType.NOT_RETURNING; | 879 Type type = StatementType.NOT_RETURNING; |
691 bool reportedDeadCode = false; | 880 bool reportedDeadCode = false; |
692 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { | 881 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { |
693 Type nextType = analyze(link.head); | 882 Type nextType = analyze(link.head); |
694 if (type == StatementType.RETURNING) { | 883 if (type == StatementType.RETURNING) { |
695 if (!reportedDeadCode) { | 884 if (!reportedDeadCode) { |
696 reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE); | 885 reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE); |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
884 } | 1073 } |
885 | 1074 |
886 visitCatchBlock(CatchBlock node) { | 1075 visitCatchBlock(CatchBlock node) { |
887 return unhandledStatement(); | 1076 return unhandledStatement(); |
888 } | 1077 } |
889 | 1078 |
890 visitTypedef(Typedef node) { | 1079 visitTypedef(Typedef node) { |
891 return unhandledStatement(); | 1080 return unhandledStatement(); |
892 } | 1081 } |
893 } | 1082 } |
OLD | NEW |