Make JetStream 2
[WebKit-https.git] / PerformanceTests / JetStream2 / WSL / Checker.js
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25 "use strict";
26
27 class Checker extends Visitor {
28     constructor(program)
29     {
30         super();
31         this._program = program;
32         this._currentStatement = null;
33         this._vertexEntryPoints = new Set();
34         this._fragmentEntryPoints = new Set();
35     }
36     
37     visitProgram(node)
38     {
39         let doStatement = statement => {
40             this._currentStatement = statement;
41             statement.visit(this);
42         }
43         
44         for (let type of node.types.values())
45             doStatement(type);
46         for (let protocol of node.protocols.values())
47             doStatement(protocol);
48         for (let funcs of node.functions.values()) {
49             for (let func of funcs) {
50                 this.visitFunc(func);
51             }
52         }
53         for (let funcs of node.functions.values()) {
54             for (let func of funcs)
55                 doStatement(func);
56         }
57     }
58
59     _checkShaderType(node)
60     {
61         // FIXME: Relax these checks once we have implemented support for textures and samplers.
62         if (node.typeParameters.length != 0)
63             throw new WTypeError(node.origin.originString, "Entry point " + node.name + " must not have type arguments.");
64         let shaderFunc = node;
65         switch (node.shaderType) {
66         case "vertex":
67             if (this._vertexEntryPoints.has(node.name))
68                 throw new WTypeError(node.origin.originString, "Duplicate vertex entry point name " + node.name);
69             this._vertexEntryPoints.add(node.name);
70             break;
71         case "fragment":
72             if (this._fragmentEntryPoints.has(node.name))
73                 throw new WTypeError(node.origin.originString, "Duplicate fragment entry point name " + node.name);
74             this._fragmentEntryPoints.add(node.name);
75             break;
76         }
77     }
78
79     _checkOperatorOverload(func, resolveFuncs)
80     {
81         if (Lexer.textIsIdentifier(func.name))
82             return; // Not operator!
83
84         if (!func.name.startsWith("operator"))
85             throw new Error("Bad operator overload name: " + func.name);
86         
87         let typeVariableTracker = new TypeVariableTracker();
88         for (let parameterType of func.parameterTypes)
89             parameterType.visit(typeVariableTracker);
90         Node.visit(func.returnTypeForOverloadResolution, typeVariableTracker);
91         for (let typeParameter of func.typeParameters) {
92             if (!typeVariableTracker.set.has(typeParameter))
93                 throw new WTypeError(typeParameter.origin.originString, "Type parameter " + typeParameter + " to operator " + func.toDeclString() + " is not inferrable from value parameters");
94         }
95         
96         let checkGetter = (kind) => {
97             let numExpectedParameters = kind == "index" ? 2 : 1;
98             if (func.parameters.length != numExpectedParameters)
99                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected " + numExpectedParameters + ", got " + func.parameters.length + ")");
100             if (func.parameterTypes[0].unifyNode.isPtr)
101                 throw new WTypeError(func.origin.originString, "Cannot have getter for pointer type: " + func.parameterTypes[0]);
102         };
103         
104         let checkSetter = (kind) => {
105             let numExpectedParameters = kind == "index" ? 3 : 2;
106             if (func.parameters.length != numExpectedParameters)
107                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected " + numExpectedParameters + ", got " + func.parameters.length + ")");
108             if (func.parameterTypes[0].unifyNode.isPtr)
109                 throw new WTypeError(func.origin.originString, "Cannot have setter for pointer type: " + func.parameterTypes[0]);
110             if (!func.returnType.equals(func.parameterTypes[0]))
111                 throw new WTypeError(func.origin.originString, "First parameter type and return type of setter must match (parameter was " + func.parameterTypes[0] + " but return was " + func.returnType + ")");
112             let valueType = func.parameterTypes[numExpectedParameters - 1];
113             let getterName = func.name.substr(0, func.name.length - 1);
114             let getterFuncs = resolveFuncs(getterName);
115             if (!getterFuncs)
116                 throw new WTypeError(func.origin.originString, "Every setter must have a matching getter, but did not find any function named " + getterName + " to match " + func.name);
117             let argumentTypes = func.parameterTypes.slice(0, numExpectedParameters - 1);
118             let overload = resolveOverloadImpl(getterFuncs, [], argumentTypes, null);
119             if (!overload.func)
120                 throw new WTypeError(func.origin.originString, "Did not find function named " + func.name + " with arguments " + argumentTypes + (overload.failures.length ? "; tried:\n" + overload.failures.join("\n") : ""));
121             let resultType = overload.func.returnType.substituteToUnification(
122                 overload.func.typeParameters, overload.unificationContext);
123             if (!resultType.equals(valueType))
124                 throw new WTypeError(func.origin.originString, "Setter and getter must agree on value type (getter at " + overload.func.origin.originString + " says " + resultType + " while this setter says " + valueType + ")");
125         };
126         
127         let checkAnder = (kind) => {
128             let numExpectedParameters = kind == "index" ? 2 : 1;
129             if (func.parameters.length != numExpectedParameters)
130                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected " + numExpectedParameters + ", got " + func.parameters.length + ")");
131             if (!func.returnType.unifyNode.isPtr)
132                 throw new WTypeError(func.origin.originString, "Return type of ander is not a pointer: " + func.returnType);
133             if (!func.parameterTypes[0].unifyNode.isRef)
134                 throw new WTypeError(func.origin.originString, "Parameter to ander is not a reference: " + func.parameterTypes[0]);
135         };
136         
137         switch (func.name) {
138         case "operator cast":
139             break;
140         case "operator++":
141         case "operator--":
142             if (func.parameters.length != 1)
143                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 1, got " + func.parameters.length + ")");
144             if (!func.parameterTypes[0].equals(func.returnType))
145                 throw new WTypeError(func.origin.originString, "Parameter type and return type must match for " + func.name + " (parameter is " + func.parameterTypes[0] + " while return is " + func.returnType + ")");
146             break;
147         case "operator+":
148         case "operator-":
149             if (func.parameters.length != 1 && func.parameters.length != 2)
150                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 1 or 2, got " + func.parameters.length + ")");
151             break;
152         case "operator*":
153         case "operator/":
154         case "operator%":
155         case "operator&":
156         case "operator|":
157         case "operator^":
158         case "operator<<":
159         case "operator>>":
160             if (func.parameters.length != 2)
161                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 2, got " + func.parameters.length + ")");
162             break;
163         case "operator~":
164             if (func.parameters.length != 1)
165                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 1, got " + func.parameters.length + ")");
166             break;
167         case "operator==":
168         case "operator<":
169         case "operator<=":
170         case "operator>":
171         case "operator>=":
172             if (func.parameters.length != 2)
173                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 2, got " + func.parameters.length + ")");
174             if (!func.returnType.equals(this._program.intrinsics.bool))
175                 throw new WTypeError(func.origin.originString, "Return type of " + func.name + " must be bool but was " + func.returnType);
176             break;
177         case "operator[]":
178             checkGetter("index");
179             break;
180         case "operator[]=":
181             checkSetter("index");
182             break;
183         case "operator&[]":
184             checkAnder("index");
185             break;
186         default:
187             if (func.name.startsWith("operator.")) {
188                 if (func.name.endsWith("="))
189                     checkSetter("dot");
190                 else
191                     checkGetter("dot");
192                 break;
193             }
194             if (func.name.startsWith("operator&.")) {
195                 checkAnder("dot");
196                 break;
197             }
198             throw new Error("Parser accepted unrecognized operator: " + func.name);
199         }
200     }
201     
202     visitFuncDef(node)
203     {
204         if (node.shaderType)
205             this._checkShaderType(node);
206         this._checkOperatorOverload(node, name => this._program.functions.get(name));
207         node.body.visit(this);
208     }
209     
210     visitNativeFunc(node)
211     {
212     }
213     
214     visitProtocolDecl(node)
215     {
216         for (let signature of node.signatures) {
217             let typeVariableTracker = new TypeVariableTracker();
218             for (let parameterType of signature.parameterTypes)
219                 parameterType.visit(typeVariableTracker);
220             Node.visit(signature.returnTypeForOverloadResolution, typeVariableTracker);
221             for (let typeParameter of signature.typeParameters) {
222                 if (!typeVariableTracker.set.has(typeParameter))
223                     throw WTypeError(typeParameter.origin.originString, "Type parameter to protocol signature not inferrable from value parameters");
224             }
225             if (!typeVariableTracker.set.has(node.typeVariable))
226                 throw new WTypeError(signature.origin.originString, "Protocol's type variable (" + node.name + ") not mentioned in signature: " + signature);
227             this._checkOperatorOverload(signature, name => node.signaturesByName(name));
228         }
229     }
230     
231     visitEnumType(node)
232     {
233         node.baseType.visit(this);
234         
235         let baseType = node.baseType.unifyNode;
236         
237         if (!baseType.isInt)
238             throw new WTypeError(node.origin.originString, "Base type of enum is not an integer: " + node.baseType);
239         
240         for (let member of node.members) {
241             if (!member.value)
242                 continue;
243             
244             let memberType = member.value.visit(this);
245             if (!baseType.equalsWithCommit(memberType))
246                 throw new WTypeError(member.origin.originString, "Type of enum member " + member.value.name + " does not patch enum base type (member type is " + memberType + ", enum base type is " + node.baseType + ")");
247         }
248         
249         let nextValue = baseType.defaultValue;
250         for (let member of node.members) {
251             if (member.value) {
252                 nextValue = baseType.successorValue(member.value.unifyNode.valueForSelectedType);
253                 continue;
254             }
255             
256             member.value = baseType.createLiteral(member.origin, nextValue);
257             nextValue = baseType.successorValue(nextValue);
258         }
259         
260         let memberArray = Array.from(node.members);
261         for (let i = 0; i < memberArray.length; ++i) {
262             let member = memberArray[i];
263             for (let j = i + 1; j < memberArray.length; ++j) {
264                 let otherMember = memberArray[j];
265                 if (baseType.valuesEqual(member.value.unifyNode.valueForSelectedType, otherMember.value.unifyNode.valueForSelectedType))
266                     throw new WTypeError(otherMember.origin.originString, "Duplicate enum member value (" + member.name + " has " + member.value + " while " + otherMember.name + " has " + otherMember.value + ")");
267             }
268         }
269         
270         let foundZero = false;
271         for (let member of node.members) {
272             if (baseType.valuesEqual(member.value.unifyNode.valueForSelectedType, baseType.defaultValue)) {
273                 foundZero = true;
274                 break;
275             }
276         }
277         if (!foundZero)
278             throw new WTypeError(node.origin.originString, "Enum does not have a member with the value zero");
279     }
280     
281     _checkTypeArguments(origin, typeParameters, typeArguments)
282     {
283         for (let i = 0; i < typeParameters.length; ++i) {
284             let argumentIsType = typeArguments[i] instanceof Type;
285             let result = typeArguments[i].visit(this);
286             if (argumentIsType) {
287                 let result = typeArguments[i].inherits(typeParameters[i].protocol);
288                 if (!result.result)
289                     throw new WTypeError(origin.originString, "Type argument does not inherit protocol: " + result.reason);
290             } else {
291                 if (!result.equalsWithCommit(typeParameters[i].type))
292                     throw new WTypeError(origin.originString, "Wrong type for constexpr");
293             }
294         }
295     }
296     
297     visitTypeRef(node)
298     {
299         if (!node.type)
300             throw new Error("Type reference without a type in checker: " + node + " at " + node.origin);
301         if (!(node.type instanceof StructType))
302             node.type.visit(this);
303         this._checkTypeArguments(node.origin, node.type.typeParameters, node.typeArguments);
304     }
305     
306     visitArrayType(node)
307     {
308         node.elementType.visit(this);
309         
310         if (!node.numElements.isConstexpr)
311             throw new WTypeError(node.origin.originString, "Array length must be constexpr");
312         
313         let type = node.numElements.visit(this);
314         
315         if (!type.equalsWithCommit(this._program.intrinsics.uint32))
316             throw new WTypeError(node.origin.originString, "Array length must be a uint32");
317     }
318     
319     visitVariableDecl(node)
320     {
321         node.type.visit(this);
322         if (node.initializer) {
323             let lhsType = node.type;
324             let rhsType = node.initializer.visit(this);
325             if (!lhsType.equalsWithCommit(rhsType))
326                 throw new WTypeError(node.origin.originString, "Type mismatch in variable initialization: " + lhsType + " versus " + rhsType);
327         }
328     }
329     
330     visitAssignment(node)
331     {
332         let lhsType = node.lhs.visit(this);
333         if (!node.lhs.isLValue)
334             throw new WTypeError(node.origin.originString, "LHS of assignment is not an LValue: " + node.lhs + node.lhs.notLValueReasonString);
335         let rhsType = node.rhs.visit(this);
336         if (!lhsType.equalsWithCommit(rhsType))
337             throw new WTypeError(node.origin.originString, "Type mismatch in assignment: " + lhsType + " versus " + rhsType);
338         node.type = lhsType;
339         return lhsType;
340     }
341     
342     visitIdentityExpression(node)
343     {
344         return node.target.visit(this);
345     }
346     
347     visitReadModifyWriteExpression(node)
348     {
349         let lhsType = node.lValue.visit(this);
350         if (!node.lValue.isLValue)
351             throw new WTypeError(node.origin.originString, "LHS of read-modify-write is not an LValue: " + node.lValue + node.lValue.notLValueReasonString);
352         node.oldValueVar.type = lhsType;
353         node.newValueVar.type = lhsType;
354         node.oldValueVar.visit(this);
355         node.newValueVar.visit(this);
356         let newValueType = node.newValueExp.visit(this);
357         if (!lhsType.equalsWithCommit(newValueType))
358             return new WTypeError(node.origin.originString, "Type mismatch in read-modify-write: " + lhsType + " versus " + newValueType);
359         return node.resultExp.visit(this);
360     }
361     
362     visitAnonymousVariable(node)
363     {
364         if (!node.type)
365             throw new Error("Anonymous variable must know type before first appearance");
366     }
367     
368     visitDereferenceExpression(node)
369     {
370         let type = node.ptr.visit(this).unifyNode;
371         if (!type.isPtr)
372             throw new WTypeError(node.origin.originString, "Type passed to dereference is not a pointer: " + type);
373         node.type = type.elementType;
374         node.addressSpace = type.addressSpace;
375         if (!node.addressSpace)
376             throw new Error("Null address space in type: " + type);
377         return node.type;
378     }
379     
380     visitMakePtrExpression(node)
381     {
382         let elementType = node.lValue.visit(this).unifyNode;
383         if (!node.lValue.isLValue)
384             throw new WTypeError(node.origin.originString, "Operand to & is not an LValue: " + node.lValue + node.lValue.notLValueReasonString);
385         
386         return new PtrType(node.origin, node.lValue.addressSpace, elementType);
387     }
388     
389     visitMakeArrayRefExpression(node)
390     {
391         let elementType = node.lValue.visit(this).unifyNode;
392         if (elementType.isPtr) {
393             node.become(new ConvertPtrToArrayRefExpression(node.origin, node.lValue));
394             return new ArrayRefType(node.origin, elementType.addressSpace, elementType.elementType);
395         }
396         
397         if (!node.lValue.isLValue)
398             throw new WTypeError(node.origin.originString, "Operand to @ is not an LValue: " + node.lValue + node.lValue.notLValueReasonString);
399         
400         if (elementType.isArray) {
401             node.numElements = elementType.numElements;
402             elementType = elementType.elementType;
403         } else
404             node.numElements = UintLiteral.withType(node.origin, 1, this._program.intrinsics.uint32);
405             
406         return new ArrayRefType(node.origin, node.lValue.addressSpace, elementType);
407     }
408     
409     visitConvertToArrayRefExpression(node)
410     {
411         throw new Error("Should not exist yet.");
412     }
413     
414     _finishVisitingPropertyAccess(node, baseType, extraArgs, extraArgTypes)
415     {
416         baseType = baseType.visit(new AutoWrapper())
417         node.baseType = baseType;
418         
419         // Such a type must exist. This may throw if it doesn't.
420         let typeForAnd = baseType.argumentTypeForAndOverload(node.origin);
421         if (!typeForAnd)
422             throw new Error("Cannot get typeForAnd");
423         
424         let errorForGet;
425         let errorForAnd;
426         
427         try {
428             let result = CallExpression.resolve(
429                 node.origin, node.possibleGetOverloads, this._currentStatement.typeParameters,
430                 node.getFuncName, [], [node.base, ...extraArgs], [baseType, ...extraArgTypes], null);
431             node.callForGet = result.call;
432             node.resultTypeForGet = result.resultType;
433         } catch (e) {
434             if (!(e instanceof WTypeError))
435                 throw e;
436             errorForGet = e;
437         }
438         
439         try {
440             let baseForAnd = baseType.argumentForAndOverload(node.origin, node.base);
441             
442             let result = CallExpression.resolve(
443                 node.origin, node.possibleAndOverloads, this._currentStatement.typeParameters,
444                 node.andFuncName, [], [baseForAnd, ...extraArgs], [typeForAnd, ...extraArgTypes],
445                 null);
446             node.callForAnd = result.call;
447             node.resultTypeForAnd = result.resultType.unifyNode.returnTypeFromAndOverload(node.origin);
448         } catch (e) {
449             if (!(e instanceof WTypeError))
450                 throw e;
451             errorForAnd = e;
452         }
453         
454         if (!node.resultTypeForGet && !node.resultTypeForAnd) {
455             throw new WTypeError(
456                 node.origin.originString,
457                 "Cannot resolve access; tried by-value:\n" +
458                 errorForGet.typeErrorMessage + "\n" +
459                 "and tried by-pointer:\n" +
460                 errorForAnd.typeErrorMessage);
461         }
462         
463         if (node.resultTypeForGet && node.resultTypeForAnd
464             && !node.resultTypeForGet.equals(node.resultTypeForAnd))
465             throw new WTypeError(node.origin.originString, "Result type resolved by-value (" + node.resultTypeForGet + ") does not match result type resolved by-pointer (" + node.resultTypeForAnd + ")");
466         
467         try {
468             let result = CallExpression.resolve(
469                 node.origin, node.possibleSetOverloads, this._currentStatement.typeParameters,
470                 node.setFuncName, [], [node.base, ...extraArgs, null], [baseType, ...extraArgTypes, node.resultType], null);
471             node.callForSet = result.call;
472             if (!result.resultType.equals(baseType))
473                 throw new WTypeError(node.origin.originString, "Result type of setter " + result.call.func + " is not the base type " + baseType);
474         } catch (e) {
475             if (!(e instanceof WTypeError))
476                 throw e;
477             node.errorForSet = e;
478         }
479         
480         // OK, now we need to determine if we are an lvalue. We are an lvalue if we can be assigned to. We can
481         // be assigned to if we have an ander or setter. But it's weirder than that. We also need the base to be
482         // an lvalue, except unless the base is an array reference.
483         if (!node.callForAnd && !node.callForSet) {
484             node.isLValue = false;
485             node.notLValueReason =
486                 "Have neither ander nor setter. Tried setter:\n" +
487                 node.errorForSet.typeErrorMessage + "\n" +
488                 "and tried ander:\n" +
489                 errorForAnd.typeErrorMessage;
490         } else if (!node.base.isLValue && !baseType.isArrayRef) {
491             node.isLValue = false;
492             node.notLValueReason = "Base of property access is neither a lvalue nor an array reference";
493         } else {
494             node.isLValue = true;
495             node.addressSpace = node.base.isLValue ? node.base.addressSpace : baseType.addressSpace;
496         }
497         
498         return node.resultType;
499     }
500     
501     visitDotExpression(node)
502     {
503         let structType = node.struct.visit(this).unifyNode;
504         return this._finishVisitingPropertyAccess(node, structType, [], []);
505     }
506     
507     visitIndexExpression(node)
508     {
509         let arrayType = node.array.visit(this).unifyNode;
510         let indexType = node.index.visit(this);
511         
512         return this._finishVisitingPropertyAccess(node, arrayType, [node.index], [indexType]);
513     }
514     
515     visitVariableRef(node)
516     {
517         if (!node.variable.type)
518             throw new Error("Variable has no type: " + node.variable);
519         return node.variable.type;
520     }
521     
522     visitReturn(node)
523     {
524         if (node.value) {
525             let resultType = node.value.visit(this);
526             if (!resultType)
527                 throw new Error("Null result type from " + node.value);
528             if (!node.func.returnType.equalsWithCommit(resultType))
529                 throw new WTypeError(node.origin.originString, "Trying to return " + resultType + " in a function that returns " + node.func.returnType);
530             return;
531         }
532         
533         if (!node.func.returnType.equalsWithCommit(this._program.intrinsics.void))
534             throw new WTypeError(node.origin.originString, "Non-void function must return a value");
535     }
536     
537     visitGenericLiteral(node)
538     {
539         return node.type;
540     }
541     
542     visitNullLiteral(node)
543     {
544         return node.type;
545     }
546     
547     visitBoolLiteral(node)
548     {
549         return this._program.intrinsics.bool;
550     }
551     
552     visitEnumLiteral(node)
553     {
554         return node.member.enumType;
555     }
556
557     _requireBool(expression)
558     {
559         let type = expression.visit(this);
560         if (!type)
561             throw new Error("Expression has no type, but should be bool: " + expression);
562         if (!type.equals(this._program.intrinsics.bool))
563             throw new WTypeError("Expression isn't a bool: " + expression);
564     }
565
566     visitLogicalNot(node)
567     {
568         this._requireBool(node.operand);
569         return this._program.intrinsics.bool;
570     }
571
572     visitLogicalExpression(node)
573     {
574         this._requireBool(node.left);
575         this._requireBool(node.right);
576         return this._program.intrinsics.bool;
577     }
578
579     visitIfStatement(node)
580     {
581         this._requireBool(node.conditional);
582         node.body.visit(this);
583         if (node.elseBody)
584             node.elseBody.visit(this);
585     }
586
587     visitWhileLoop(node)
588     {
589         this._requireBool(node.conditional);
590         node.body.visit(this);
591     }
592
593     visitDoWhileLoop(node)
594     {
595         node.body.visit(this);
596         this._requireBool(node.conditional);
597     }
598
599     visitForLoop(node)
600     {
601         if (node.initialization)
602             node.initialization.visit(this);
603         if (node.condition)
604             this._requireBool(node.condition);
605         if (node.increment)
606             node.increment.visit(this);
607         node.body.visit(this);
608     }
609
610     visitSwitchStatement(node)
611     {
612         let type = node.value.visit(this).commit();
613         
614         if (!type.unifyNode.isInt && !(type.unifyNode instanceof EnumType))
615             throw new WTypeError(node.origin.originString, "Cannot switch on non-integer/non-enum type: " + type);
616
617         node.type = type;
618         
619         let hasDefault = false;
620         
621         for (let switchCase of node.switchCases) {
622             switchCase.body.visit(this);
623
624             if (switchCase.isDefault) {
625                 hasDefault = true;
626                 continue;
627             }
628             
629             if (!switchCase.value.isConstexpr)
630                 throw new WTypeError(switchCase.origin.originString, "Switch case not constexpr: " + switchCase.value);
631             
632             let caseType = switchCase.value.visit(this);
633             if (!type.equalsWithCommit(caseType))
634                 throw new WTypeError(switchCase.origin.originString, "Switch case type does not match switch value type (case type is " + caseType + " but switch value type is " + type + ")");
635         }
636         
637         for (let i = 0; i < node.switchCases.length; ++i) {
638             let firstCase = node.switchCases[i];
639             for (let j = i + 1; j < node.switchCases.length; ++j) {
640                 let secondCase = node.switchCases[j];
641                 
642                 if (firstCase.isDefault != secondCase.isDefault)
643                     continue;
644                 
645                 if (firstCase.isDefault)
646                     throw new WTypeError(secondCase.origin.originString, "Duplicate default case in switch statement");
647                 
648                 let valuesEqual = type.unifyNode.valuesEqual(
649                     firstCase.value.unifyNode.valueForSelectedType,
650                     secondCase.value.unifyNode.valueForSelectedType);
651                 if (valuesEqual)
652                     throw new WTypeError(secondCase.origin.originString, "Duplicate case in switch statement for value " + firstCase.value.unifyNode.valueForSelectedType);
653             }
654         }
655         
656         if (!hasDefault) {
657             let includedValues = new Set();
658             for (let switchCase of node.switchCases)
659                 includedValues.add(switchCase.value.unifyNode.valueForSelectedType);
660             
661             for (let {value, name} of type.unifyNode.allValues()) {
662                 if (!includedValues.has(value))
663                     throw new WTypeError(node.origin.originString, "Value not handled by switch statement: " + name);
664             }
665         }
666     }
667     
668     visitCommaExpression(node)
669     {
670         let result = null;
671         for (let expression of node.list)
672             result = expression.visit(this);
673         return result;
674     }
675     
676     visitCallExpression(node)
677     {
678         let typeArguments = node.typeArguments.map(typeArgument => typeArgument.visit(this));
679         let argumentTypes = node.argumentList.map(argument => {
680             let newArgument = argument.visit(this);
681             if (!newArgument)
682                 throw new Error("visitor returned null for " + argument);
683             return newArgument.visit(new AutoWrapper());
684         });
685         
686         node.argumentTypes = argumentTypes;
687         if (node.returnType)
688             node.returnType.visit(this);
689
690         let result = node.resolve(node.possibleOverloads, this._currentStatement.typeParameters, typeArguments);
691         return result;
692     }
693 }
694