[WHLSL] Allow uniform buffers to be used in the interpreter
[WebKit-https.git] / Tools / WebGPUShadingLanguageRI / Checker.js
1 /*
2  * Copyright (C) 2018 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         this._computeEntryPoints = new Set();
36     }
37
38     visitProgram(node)
39     {
40         let doStatement = statement => {
41             this._currentStatement = statement;
42             statement.visit(this);
43         }
44
45         for (let type of node.types.values()) {
46             if (type instanceof Array) {
47                 for (let constituentType of type)
48                     doStatement(constituentType);
49             } else
50                 doStatement(type);
51         }
52         for (let funcs of node.functions.values()) {
53             for (let func of funcs) {
54                 this.visitFunc(func);
55             }
56         }
57         for (let funcs of node.functions.values()) {
58             for (let func of funcs)
59                 doStatement(func);
60         }
61     }
62
63     _checkSemantics(node)
64     {
65         class Item {
66             constructor(type, semantic)
67             {
68                 if (node.shaderType != "test" && !semantic)
69                     throw new WTypeError(node.origin.originString, "An entry-point input/output exists which doesn't have a semantic");
70                 this._type = type;
71                 this._semantic = semantic;
72             }
73
74             get type() { return this._type; }
75             get semantic() { return this._semantic; }
76         }
77
78         let program = this._program;
79         class Gatherer extends Visitor {
80             constructor(currentSemantic = null)
81             {
82                 super();
83                 this._currentSemantic = currentSemantic;
84                 this._result = [];
85             }
86
87             reset(currentSemantic = null)
88             {
89                 this.currentSemantic = currentSemantic;
90             }
91
92             set currentSemantic(value) { this._currentSemantic = value; }
93             get currentSemantic() { return this._currentSemantic; }
94             get result() { return this._result; }
95
96             visitEnumType(node)
97             {
98                 this.result.push(new Item(node, this.currentSemantic));
99             }
100
101             visitVectorType(node)
102             {
103                 this.result.push(new Item(node, this.currentSemantic));
104             }
105
106             visitMatrixType(node)
107             {
108                 this.result.push(new Item(node, this.currentSemantic));
109             }
110
111             visitNativeType(node)
112             {
113                 if (program.intrinsics.void.equals(node)) {
114                     if (this.currentSemantic)
115                         throw new WTypeError(node.origin.originString, "Void can't have a semantic");
116                 } else
117                     this.result.push(new Item(node, this.currentSemantic));
118             }
119
120             visitStructType(node)
121             {
122                 if (this.currentSemantic != null)
123                     throw new WTypeError(node.origin.originString, "Structs inside entry point signatures can't have semantics.");
124                 for (let field of node.fields) {
125                     this.currentSemantic = field.semantic;
126                     field.type.visit(this);
127                 }
128             }
129
130             visitTypeRef(node)
131             {
132                 node.type.visit(this);
133             }
134
135             visitPtrType(node)
136             {
137                 this.result.push(new Item(node, this.currentSemantic));
138             }
139
140             visitArrayRefType(node)
141             {
142                 this.result.push(new Item(node, this.currentSemantic));
143             }
144
145             visitArrayType(node)
146             {
147                 this.result.push(new Item(node, this.currentSemantic));
148             }
149
150             visitFuncParameter(node)
151             {
152                 this._currentSemantic = node.semantic;
153                 node.type.visit(this);
154             }
155         }
156
157         let inputGatherer = new Gatherer();
158         for (let parameter of node.parameters) {
159             inputGatherer.reset();
160             parameter.visit(inputGatherer);
161         }
162         let outputGatherer = new Gatherer(node.semantic);
163         node.returnType.visit(outputGatherer);
164
165         function checkDuplicateSemantics(items) {
166             // FIXME: Make this faster than O(n^2)
167             for (let i = 0; i < items.length; ++i) {
168                 for (let j = i + 1; j < items.length; ++j) {
169                     if (items[i].semantic && items[i].semantic.equalToOtherSemantic(items[j].semantic))
170                         throw new WTypeError(node.origin.originString, `Duplicate semantic found in entry point: ${items[i].semantic}`);
171                 }
172             }
173         }
174         checkDuplicateSemantics(inputGatherer.result);
175         checkDuplicateSemantics(outputGatherer.result);
176
177         function checkSemanticTypes(items) {
178             for (let item of items) {
179                 if (item.semantic && !item.semantic.isAcceptableType(item.type, program))
180                     throw new WTypeError(node.origin.originString, `Semantic ${item.semantic} is unnacceptable type ${item.type}`);
181             }
182         }
183         checkSemanticTypes(inputGatherer.result);
184         checkSemanticTypes(outputGatherer.result);
185
186         function checkSemanticForShaderType(items, direction) {
187             for (let item of items) {
188                 if (item.semantic && !item.semantic.isAcceptableForShaderType(direction, node.shaderType))
189                     throw new WTypeError(node.origin.originString, `Semantic ${item.semantic} is unnacceptable as an ${direction} of shader type ${node.shaderType}`);
190             }
191         }
192         checkSemanticForShaderType(inputGatherer.result, "input");
193         checkSemanticForShaderType(outputGatherer.result, "output");
194
195         class PODChecker extends Visitor {
196             visitEnumType(node)
197             {
198                 return true;
199             }
200
201             visitArrayType(node)
202             {
203                 return node.elementType.visit(this);
204             }
205
206             visitVectorType(node)
207             {
208                 return true;
209             }
210
211             visitMatrixType(node)
212             {
213                 return true;
214             }
215
216             visitNativeType(node)
217             {
218                 return node.isNumber;
219             }
220
221             visitPtrType(node)
222             {
223                 return false;
224             }
225
226             visitArrayRefType(node)
227             {
228                 return false;
229             }
230
231             visitStructType(node)
232             {
233                 let result = true;
234                 for (let field of node.fields)
235                     result = result && field.visit(this);
236                 return result;
237             }
238
239             visitTypeRef(node)
240             {
241                 return node.type.visit(this);
242             }
243         }
244         function checkPODData(items) {
245             for (let item of items) {
246                 if ((item.type instanceof PtrType) || (item.type instanceof ArrayRefType) || (item.type instanceof ArrayType)) {
247                     if (!item.type.elementType.visit(new PODChecker()))
248                         throw new WTypeError(node.origin.originString, "Buffer attached to entry point needs to only contain POD types");
249                 }
250             }
251         }
252         checkPODData(inputGatherer.result);
253         checkPODData(outputGatherer.result);
254     }
255
256     _checkShaderType(node)
257     {
258         switch (node.shaderType) {
259         case "vertex":
260             if (this._vertexEntryPoints.has(node.name))
261                 throw new WTypeError(node.origin.originString, "Duplicate vertex entry point name " + node.name);
262             this._vertexEntryPoints.add(node.name);
263             break;
264         case "fragment":
265             if (this._fragmentEntryPoints.has(node.name))
266                 throw new WTypeError(node.origin.originString, "Duplicate fragment entry point name " + node.name);
267             this._fragmentEntryPoints.add(node.name);
268             break;
269         case "compute":
270             if (this._computeEntryPoints.has(node.name))
271                 throw new WTypeError(node.origin.originString, "Duplicate compute entry point name " + node.name);
272             this._computeEntryPoints.add(node.name);
273             break;
274         case "test":
275             break;
276         default:
277             throw new Error("Bad shader type: " + node.shaderType);
278         }
279     }
280
281     _checkOperatorOverload(func, resolveFuncs)
282     {
283         if (Lexer.textIsIdentifier(func.name))
284             return; // Not operator!
285
286         if (!func.name.startsWith("operator"))
287             throw new Error("Bad operator overload name: " + func.name);
288
289         let checkGetter = (kind) => {
290             let numExpectedParameters = kind == "index" ? 2 : 1;
291             if (func.parameters.length != numExpectedParameters)
292                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected " + numExpectedParameters + ", got " + func.parameters.length + ")");
293             if (func.parameterTypes[0].unifyNode.isPtr)
294                 throw new WTypeError(func.origin.originString, "Cannot have getter for pointer type: " + func.parameterTypes[0]);
295         };
296
297         let checkSetter = (kind) => {
298             let numExpectedParameters = kind == "index" ? 3 : 2;
299             if (func.parameters.length != numExpectedParameters)
300                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected " + numExpectedParameters + ", got " + func.parameters.length + ")");
301             if (func.parameterTypes[0].unifyNode.isPtr)
302                 throw new WTypeError(func.origin.originString, "Cannot have setter for pointer type: " + func.parameterTypes[0]);
303             if (!func.returnType.equals(func.parameterTypes[0]))
304                 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 + ")");
305             let valueType = func.parameterTypes[numExpectedParameters - 1];
306             let getterName = func.name.substr(0, func.name.length - 1);
307             let getterFuncs = resolveFuncs(getterName);
308             if (!getterFuncs)
309                 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);
310             let argumentTypes = func.parameterTypes.slice(0, numExpectedParameters - 1);
311             let overload = resolveOverloadImpl(getterFuncs, argumentTypes, null);
312             if (!overload.func)
313                 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") : ""));
314             let resultType = overload.func.returnType;
315             if (!resultType.equals(valueType))
316                 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 + ")");
317         };
318
319         let checkAnder = (kind) => {
320             let numExpectedParameters = kind == "index" ? 2 : 1;
321             if (func.parameters.length != numExpectedParameters)
322                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected " + numExpectedParameters + ", got " + func.parameters.length + ")");
323             if (!func.returnType.unifyNode.isPtr)
324                 throw new WTypeError(func.origin.originString, "Return type of ander is not a pointer: " + func.returnType);
325             if (!func.parameterTypes[0].unifyNode.isRef)
326                 throw new WTypeError(func.origin.originString, "Parameter to ander is not a reference: " + func.parameterTypes[0]);
327         };
328
329         switch (func.name) {
330         case "operator cast":
331             break;
332         case "operator++":
333         case "operator--":
334             if (func.parameters.length != 1)
335                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 1, got " + func.parameters.length + ")");
336             if (!func.parameterTypes[0].equals(func.returnType))
337                 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 + ")");
338             break;
339         case "operator+":
340         case "operator-":
341             if (func.parameters.length != 1 && func.parameters.length != 2)
342                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 1 or 2, got " + func.parameters.length + ")");
343             break;
344         case "operator*":
345         case "operator/":
346         case "operator%":
347         case "operator&":
348         case "operator|":
349         case "operator^":
350         case "operator<<":
351         case "operator>>":
352             if (func.parameters.length != 2)
353                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 2, got " + func.parameters.length + ")");
354             break;
355         case "operator~":
356             if (func.parameters.length != 1)
357                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 1, got " + func.parameters.length + ")");
358             break;
359         case "operator==":
360         case "operator<":
361         case "operator<=":
362         case "operator>":
363         case "operator>=":
364             if (func.parameters.length != 2)
365                 throw new WTypeError(func.origin.originString, "Incorrect number of parameters for " + func.name + " (expected 2, got " + func.parameters.length + ")");
366             if (!func.returnType.equals(this._program.intrinsics.bool))
367                 throw new WTypeError(func.origin.originString, "Return type of " + func.name + " must be bool but was " + func.returnType);
368             break;
369         case "operator[]":
370             checkGetter("index");
371             break;
372         case "operator[]=":
373             checkSetter("index");
374             break;
375         case "operator&[]":
376             checkAnder("index");
377             break;
378         default:
379             if (func.name.startsWith("operator.")) {
380                 if (func.name.endsWith("="))
381                     checkSetter("dot");
382                 else
383                     checkGetter("dot");
384                 break;
385             }
386             if (func.name.startsWith("operator&.")) {
387                 checkAnder("dot");
388                 break;
389             }
390             throw new Error("Parser accepted unrecognized operator: " + func.name);
391         }
392     }
393
394     visitFuncDef(node)
395     {
396         if (node.shaderType) {
397             this._checkShaderType(node);
398             this._checkSemantics(node);
399         }
400         this._checkOperatorOverload(node, name => this._program.functions.get(name));
401         node.body.visit(this);
402     }
403
404     visitEnumType(node)
405     {
406         node.baseType.visit(this);
407
408         let baseType = node.baseType.unifyNode;
409
410         if (!baseType.isInt)
411             throw new WTypeError(node.origin.originString, "Base type of enum is not an integer: " + node.baseType);
412
413         for (let member of node.members) {
414             if (!member.value)
415                 continue;
416
417             let memberType = member.value.visit(this);
418             if (!baseType.equalsWithCommit(memberType))
419                 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 + ")");
420         }
421
422         let nextValue = baseType.defaultValue;
423         for (let member of node.members) {
424             if (member.value) {
425                 nextValue = baseType.successorValue(member.value.unifyNode.valueForSelectedType);
426                 continue;
427             }
428
429             member.value = baseType.createLiteral(member.origin, nextValue);
430             nextValue = baseType.successorValue(nextValue);
431         }
432
433         let memberArray = Array.from(node.members);
434         for (let i = 0; i < memberArray.length; ++i) {
435             let member = memberArray[i];
436             for (let j = i + 1; j < memberArray.length; ++j) {
437                 let otherMember = memberArray[j];
438                 if (baseType.valuesEqual(member.value.unifyNode.valueForSelectedType, otherMember.value.unifyNode.valueForSelectedType))
439                     throw new WTypeError(otherMember.origin.originString, "Duplicate enum member value (" + member.name + " has " + member.value + " while " + otherMember.name + " has " + otherMember.value + ")");
440             }
441         }
442
443         let foundZero = false;
444         for (let member of node.members) {
445             if (baseType.valuesEqual(member.value.unifyNode.valueForSelectedType, baseType.defaultValue)) {
446                 foundZero = true;
447                 break;
448             }
449         }
450         if (!foundZero)
451             throw new WTypeError(node.origin.originString, "Enum does not have a member with the value zero");
452     }
453
454     visitTypeRef(node)
455     {
456         if (!node.type)
457             throw new Error("Type reference without a type in checker: " + node + " at " + node.origin);
458         // All the structs will be visited by visitProgram() iterating through each top-level type.
459         // We don't want to recurse here because the contents of structs can refer to themselves (e.g. a linked list),
460         // and this would can an infinite loop.
461         // Typedefs can't refer to themselves because we check that in TypeDefResolver.
462         if (!(node.type instanceof StructType))
463             node.type.visit(this);
464     }
465
466     visitVectorType(node)
467     {
468         node.elementType.visit(this);
469         node.numElements.visit(this);
470
471         let isKnownAllowedVectorElementType = false;
472         for (let vectorElementTypeName of VectorElementTypes) {
473             const vectorElementType = this._program.globalNameContext.get(Type, vectorElementTypeName);
474             if (!vectorElementType)
475                 throw new WTypeError(`${vectorElementType} is listed in VectorElementTypes, but it is not a known native type in the standard library or intrinsics.`);
476             if (vectorElementType.equals(node.elementType)) {
477                 isKnownAllowedVectorElementType = true;
478                 break;
479             }
480         }
481
482         if (!isKnownAllowedVectorElementType)
483             throw new WTypeError(`${node.elementType} is not a permitted vector element type.`);
484         if (node.numElementsValue != 2 && node.numElementsValue != 3 && node.numElementsValue != 4)
485             throw new WTypeError(`${node.toString()}: ${node.numElementsValue} is not 2, 3, or 4.`);
486     }
487
488     visitMatrixType(node)
489     {
490         node.elementType.visit(this);
491         node.numRows.visit(this);
492         node.numColumns.visit(this);
493
494         let isKnownAllowedVectorElementType = false;
495         for (let elementTypeName of ["half", "float"]) {
496             const elementType = this._program.globalNameContext.get(Type, elementTypeName);
497             if (!elementType)
498                 throw new WTypeError(`${elementTypeName} is not a known native type in the standard library or intrinsics.`);
499             if (elementType.equals(node.elementType)) {
500                 isKnownAllowedVectorElementType = true;
501                 break;
502             }
503         }
504
505         if (!isKnownAllowedVectorElementType)
506             throw new WTypeError(`${node.elementType} is not a permitted vector element type.`);
507         if (node.numRowsValue != 2 && node.numRowsValue != 3 && node.numRowsValue != 4)
508             throw new WTypeError(`${node.toString()}: ${node.numRowsValue} is not 2, 3, or 4.`);
509         if (node.numColumnsValue != 2 && node.numColumnsValue != 3 && node.numColumnsValue != 4)
510             throw new WTypeError(`${node.toString()}: ${node.numColumnsValue} is not 2, 3, or 4.`);
511     }
512
513     visitArrayType(node)
514     {
515         node.elementType.visit(this);
516
517         if (!node.numElements.isConstexpr)
518             throw new WTypeError(node.origin.originString, "Array length must be constexpr");
519
520         let type = node.numElements.visit(this);
521
522         if (!type.equalsWithCommit(this._program.intrinsics.uint))
523             throw new WTypeError(node.origin.originString, "Array length must be a uint");
524     }
525
526     visitVariableDecl(node)
527     {
528         node.type.visit(this);
529         if (node.initializer) {
530             let lhsType = node.type;
531             let rhsType = node.initializer.visit(this);
532             if (!lhsType.equalsWithCommit(rhsType))
533                 throw new WTypeError(node.origin.originString, "Type mismatch in variable initialization: " + lhsType + " versus " + rhsType);
534         }
535     }
536
537     visitAssignment(node)
538     {
539         let lhsType = node.lhs.visit(this);
540         if (!node.lhs.isLValue)
541             throw new WTypeError(node.origin.originString, "LHS of assignment is not an LValue: " + node.lhs + node.lhs.notLValueReasonString);
542         if (!isAddressSpace(node.lhs.addressSpace))
543             throw new Error(`${node.origin.originString}: Unknown address space in node ${node.lhs}`);
544         if (node.lhs.addressSpace == "constant")
545             throw new WTypeError(node.origin.originString, "Cannot assign to variable in the constant address space.");
546         let rhsType = node.rhs.visit(this);
547         if (!lhsType.equalsWithCommit(rhsType))
548             throw new WTypeError(node.origin.originString, "Type mismatch in assignment: " + lhsType + " versus " + rhsType);
549         node.type = lhsType;
550         return lhsType;
551     }
552
553     visitIdentityExpression(node)
554     {
555         return node.target.visit(this);
556     }
557
558     visitReadModifyWriteExpression(node)
559     {
560         let lhsType = node.lValue.visit(this);
561         if (!node.lValue.isLValue)
562             throw new WTypeError(node.origin.originString, "LHS of read-modify-write is not an LValue: " + node.lValue + node.lValue.notLValueReasonString);
563         node.oldValueVar.type = lhsType;
564         node.newValueVar.type = lhsType;
565         node.oldValueVar.visit(this);
566         node.newValueVar.visit(this);
567         let newValueType = node.newValueExp.visit(this);
568         if (!lhsType.equalsWithCommit(newValueType))
569             return new WTypeError(node.origin.originString, "Type mismatch in read-modify-write: " + lhsType + " versus " + newValueType);
570         return node.resultExp.visit(this);
571     }
572
573     visitAnonymousVariable(node)
574     {
575         if (!node.type)
576             throw new Error("Anonymous variable must know type before first appearance");
577     }
578
579     visitDereferenceExpression(node)
580     {
581         let type = node.ptr.visit(this).unifyNode;
582         if (!type.isPtr)
583             throw new WTypeError(node.origin.originString, "Type passed to dereference is not a pointer: " + type);
584         node.type = type.elementType;
585         node.addressSpace = type.addressSpace;
586         if (!node.addressSpace)
587             throw new Error("Null address space in type: " + type);
588         return node.type;
589     }
590
591     visitMakePtrExpression(node)
592     {
593         let elementType = node.lValue.visit(this).unifyNode;
594         if (!node.lValue.isLValue)
595             throw new WTypeError(node.origin.originString, "Operand to & is not an LValue: " + node.lValue + node.lValue.notLValueReasonString);
596
597         return new PtrType(node.origin, node.lValue.addressSpace, elementType);
598     }
599
600     visitMakeArrayRefExpression(node)
601     {
602         let elementType = node.lValue.visit(this).unifyNode;
603         if (elementType.isPtr) {
604             node.become(new ConvertPtrToArrayRefExpression(node.origin, node.lValue));
605             return new ArrayRefType(node.origin, elementType.addressSpace, elementType.elementType);
606         }
607
608         if (!node.lValue.isLValue)
609             throw new WTypeError(node.origin.originString, "Operand to @ is not an LValue: " + node.lValue + node.lValue.notLValueReasonString);
610
611         if (elementType.isArray) {
612             node.numElements = elementType.numElements;
613             elementType = elementType.elementType;
614         } else
615             node.numElements = UintLiteral.withType(node.origin, 1, this._program.intrinsics.uint);
616
617         return new ArrayRefType(node.origin, node.lValue.addressSpace, elementType);
618     }
619
620     visitConvertToArrayRefExpression(node)
621     {
622         throw new Error("Should not exist yet.");
623     }
624
625     _finishVisitingPropertyAccess(node, baseType, extraArgs, extraArgTypes)
626     {
627         baseType = baseType.visit(new AutoWrapper())
628         node.baseType = baseType;
629
630         // Such a type must exist. This may throw if it doesn't.
631         let typeForAnd = baseType.argumentTypeForAndOverload(node.origin);
632         if (!typeForAnd)
633             throw new Error("Cannot get typeForAnd");
634
635         let errorForGet;
636         let errorForAnd;
637
638         try {
639             let result = CallExpression.resolve(
640                 node.origin, node.possibleGetOverloads,
641                 node.getFuncName, [node.base, ...extraArgs], [baseType, ...extraArgTypes], null, this._program);
642             node.callForGet = result.call;
643             node.resultTypeForGet = result.resultType;
644         } catch (e) {
645             if (!(e instanceof WTypeError))
646                 throw e;
647             errorForGet = e;
648         }
649
650         try {
651             let baseForAnd = baseType.argumentForAndOverload(node.origin, node.base);
652
653             let result = CallExpression.resolve(
654                 node.origin, node.possibleAndOverloads,
655                 node.andFuncName, [baseForAnd, ...extraArgs], [typeForAnd, ...extraArgTypes],
656                 null, this._program);
657             node.callForAnd = result.call;
658             node.resultTypeForAnd = result.resultType.unifyNode.returnTypeFromAndOverload(node.origin);
659         } catch (e) {
660             if (!(e instanceof WTypeError))
661                 throw e;
662             errorForAnd = e;
663         }
664
665         if (!node.resultTypeForGet && !node.resultTypeForAnd) {
666             throw new WTypeError(
667                 node.origin.originString,
668                 "Cannot resolve access; tried by-value:\n" +
669                 errorForGet.typeErrorMessage + "\n" +
670                 "and tried by-pointer:\n" +
671                 errorForAnd.typeErrorMessage);
672         }
673
674         if (node.resultTypeForGet && node.resultTypeForAnd
675             && !node.resultTypeForGet.equals(node.resultTypeForAnd))
676             throw new WTypeError(node.origin.originString, "Result type resolved by-value (" + node.resultTypeForGet + ") does not match result type resolved by-pointer (" + node.resultTypeForAnd + ")");
677
678         try {
679             let result = CallExpression.resolve(
680                 node.origin, node.possibleSetOverloads,
681                 node.setFuncName, [node.base, ...extraArgs, null], [baseType, ...extraArgTypes, node.resultType], null, this._program);
682             node.callForSet = result.call;
683             if (!result.resultType.equals(baseType))
684                 throw new WTypeError(node.origin.originString, "Result type of setter " + result.call.func + " is not the base type " + baseType);
685         } catch (e) {
686             if (!(e instanceof WTypeError))
687                 throw e;
688             node.errorForSet = e;
689         }
690
691         // OK, now we need to determine if we are an lvalue. We are an lvalue if we can be assigned to. We can
692         // be assigned to if we have an ander or setter. But it's weirder than that. We also need the base to be
693         // an lvalue, except unless the base is an array reference.
694         if (!node.callForAnd && !node.callForSet) {
695             node.isLValue = false;
696             node.notLValueReason =
697                 "Have neither ander nor setter. Tried setter:\n" +
698                 node.errorForSet.typeErrorMessage + "\n" +
699                 "and tried ander:\n" +
700                 errorForAnd.typeErrorMessage;
701         } else if (!node.base.isLValue && !baseType.isArrayRef) {
702             node.isLValue = false;
703             node.notLValueReason = "Base of property access is neither a lvalue nor an array reference";
704         } else {
705             node.isLValue = true;
706             node.addressSpace = baseType.addressSpace ? baseType.addressSpace : node.base.addressSpace;
707         }
708
709         return node.resultType;
710     }
711
712     visitDotExpression(node)
713     {
714         let structType = node.struct.visit(this).unifyNode;
715         return this._finishVisitingPropertyAccess(node, structType, [], []);
716     }
717
718     visitIndexExpression(node)
719     {
720         let arrayType = node.array.visit(this).unifyNode;
721         let indexType = node.index.visit(this);
722
723         return this._finishVisitingPropertyAccess(node, arrayType, [node.index], [indexType]);
724     }
725
726     visitVariableRef(node)
727     {
728         if (!node.variable.type)
729             throw new Error("Variable has no type: " + node.variable);
730         return node.variable.type;
731     }
732
733     visitReturn(node)
734     {
735         if (node.value) {
736             let resultType = node.value.visit(this);
737             if (!resultType)
738                 throw new Error("Null result type from " + node.value);
739             if (!node.func.returnType.equalsWithCommit(resultType))
740                 throw new WTypeError(node.origin.originString, "Trying to return " + resultType + " in a function that returns " + node.func.returnType);
741             return;
742         }
743
744         if (!node.func.returnType.equalsWithCommit(this._program.intrinsics.void))
745             throw new WTypeError(node.origin.originString, "Non-void function must return a value");
746     }
747
748     visitGenericLiteral(node)
749     {
750         return node.type;
751     }
752
753     visitNullLiteral(node)
754     {
755         return node.type;
756     }
757
758     visitBoolLiteral(node)
759     {
760         return this._program.intrinsics.bool;
761     }
762
763     visitEnumLiteral(node)
764     {
765         return node.member.enumType;
766     }
767
768     _requireBool(expression)
769     {
770         let type = expression.visit(this);
771         if (!type)
772             throw new Error("Expression has no type, but should be bool: " + expression);
773         if (!type.equals(this._program.intrinsics.bool))
774             throw new WTypeError("Expression isn't a bool: " + expression);
775     }
776
777     visitLogicalNot(node)
778     {
779         this._requireBool(node.operand);
780         return this._program.intrinsics.bool;
781     }
782
783     visitLogicalExpression(node)
784     {
785         this._requireBool(node.left);
786         this._requireBool(node.right);
787         return this._program.intrinsics.bool;
788     }
789
790     visitIfStatement(node)
791     {
792         this._requireBool(node.conditional);
793         node.body.visit(this);
794         if (node.elseBody)
795             node.elseBody.visit(this);
796     }
797
798     visitWhileLoop(node)
799     {
800         this._requireBool(node.conditional);
801         node.body.visit(this);
802     }
803
804     visitDoWhileLoop(node)
805     {
806         node.body.visit(this);
807         this._requireBool(node.conditional);
808     }
809
810     visitForLoop(node)
811     {
812         if (node.initialization)
813             node.initialization.visit(this);
814         if (node.condition)
815             this._requireBool(node.condition);
816         if (node.increment)
817             node.increment.visit(this);
818         node.body.visit(this);
819     }
820
821     visitSwitchStatement(node)
822     {
823         let type = node.value.visit(this).commit();
824
825         if (!type.unifyNode.isInt && !(type.unifyNode instanceof EnumType))
826             throw new WTypeError(node.origin.originString, "Cannot switch on non-integer/non-enum type: " + type);
827
828         node.type = type;
829
830         let hasDefault = false;
831
832         for (let switchCase of node.switchCases) {
833             switchCase.body.visit(this);
834
835             if (switchCase.isDefault) {
836                 hasDefault = true;
837                 continue;
838             }
839
840             if (!switchCase.value.isConstexpr)
841                 throw new WTypeError(switchCase.origin.originString, "Switch case not constexpr: " + switchCase.value);
842
843             let caseType = switchCase.value.visit(this);
844             if (!type.equalsWithCommit(caseType))
845                 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 + ")");
846         }
847
848         for (let i = 0; i < node.switchCases.length; ++i) {
849             let firstCase = node.switchCases[i];
850             for (let j = i + 1; j < node.switchCases.length; ++j) {
851                 let secondCase = node.switchCases[j];
852
853                 if (firstCase.isDefault != secondCase.isDefault)
854                     continue;
855
856                 if (firstCase.isDefault)
857                     throw new WTypeError(secondCase.origin.originString, "Duplicate default case in switch statement");
858
859                 let valuesEqual = type.unifyNode.valuesEqual(
860                     firstCase.value.unifyNode.valueForSelectedType,
861                     secondCase.value.unifyNode.valueForSelectedType);
862                 if (valuesEqual)
863                     throw new WTypeError(secondCase.origin.originString, "Duplicate case in switch statement for value " + firstCase.value.unifyNode.valueForSelectedType);
864             }
865         }
866
867         if (!hasDefault) {
868             let includedValues = new Set();
869             for (let switchCase of node.switchCases)
870                 includedValues.add(switchCase.value.unifyNode.valueForSelectedType);
871
872             for (let {value, name} of type.unifyNode.allValues()) {
873                 if (!includedValues.has(value))
874                     throw new WTypeError(node.origin.originString, "Value not handled by switch statement: " + name);
875             }
876         }
877     }
878
879     visitCommaExpression(node)
880     {
881         let result = null;
882         for (let expression of node.list)
883             result = expression.visit(this);
884         return result;
885     }
886
887     visitTernaryExpression(node)
888     {
889         this._requireBool(node.predicate);
890         let bodyType = node.bodyExpression.visit(this);
891         let elseType = node.elseExpression.visit(this);
892         if (!bodyType)
893             throw new Error("Ternary expression body has no type: " + node.bodyExpression);
894         if (!elseType)
895             throw new Error("Ternary expression else has no type: " + node.elseExpression);
896         if (!bodyType.equalsWithCommit(elseType))
897             throw new WTypeError(node.origin.originString, "Body and else clause of ternary statement don't have the same type: " + node);
898         return bodyType;
899     }
900     
901     visitCallExpression(node)
902     {
903         let argumentTypes = node.argumentList.map(argument => {
904             let newArgument = argument.visit(this);
905             if (!newArgument)
906                 throw new Error("visitor returned null for " + argument);
907             return newArgument.visit(new AutoWrapper());
908         });
909
910         node.argumentTypes = argumentTypes;
911         if (node.returnType)
912             node.returnType.visit(this);
913
914         let result = node.resolve(node.possibleOverloads, this._program);
915         return result;
916     }
917 }
918