[WHLSL] Pointers should have automatically-generated equality checks
[WebKit-https.git] / Tools / WebGPUShadingLanguageRI / CallExpression.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 CallExpression extends Expression {
28     constructor(origin, name, argumentList)
29     {
30         super(origin);
31         this._name = name;
32         this._argumentList = argumentList;
33         this.func = null;
34         this._isCast = false;
35         this._returnType = null;
36     }
37     
38     get name() { return this._name; }
39
40     get argumentList() { return this._argumentList; }
41
42     set argumentList(newValue)
43     {
44         this._argumentList = newValue;
45     }
46
47     get isCast() { return this._isCast; }
48     get returnType() { return this._returnType; }
49     
50     static resolve(origin, possibleOverloads, name, argumentList, argumentTypes, returnType, program)
51     {
52         let call = new CallExpression(origin, name, argumentList);
53         call.argumentTypes = argumentTypes.map(argument => argument.visit(new AutoWrapper()));
54         call.possibleOverloads = possibleOverloads;
55         if (returnType)
56             call.setCastData(returnType);
57         return {call, resultType: call.resolve(possibleOverloads, program)};
58     }
59
60     resolve(possibleOverloads, program)
61     {
62         let failures = [];
63         let overload;
64         if (possibleOverloads)
65             overload = resolveOverloadImpl(possibleOverloads, this.argumentTypes, this.returnType);
66
67         if (!overload || !overload.func) {
68             if (!overload)
69                 overload = {};
70             const func = this._resolveByInstantiation(program);
71             if (func)
72                 overload.func = func;
73         }
74
75         if (!overload.func) {
76             if (!overload.failures)
77                 overload.failures = [];
78             failures.push(...overload.failures);
79             let message = "Did not find function named " + this.name + " for call with ";
80             message += "argument types (" + this.argumentTypes + ")";
81             if (this.returnType)
82                 message +=" and return type " + this.returnType;
83             if (failures.length)
84                 message += ", but considered:\n" + failures.join("\n")
85             throw new WTypeError(this.origin.originString, message);
86         }
87
88         for (let i = 0; i < this.argumentTypes.length; ++i) {
89             let argumentType = this.argumentTypes[i];
90             let parameterType = overload.func.parameters[i].type;
91             let result = argumentType.equalsWithCommit(parameterType);
92             if (!result)
93                 throw new Error("At " + this.origin.originString + " argument and parameter types not equal after type argument substitution: argument = " + argumentType + ", parameter = " + parameterType);
94         }
95         return this.resolveToOverload(overload);
96     }
97
98     _resolveByInstantiation(program)
99     {
100         let func;
101         if (this.name == "operator&[]")
102             func = this._resolveWithOperatorAnderIndexer(program);
103         else if (this.name == "operator.length")
104             func = this._resolveWithOperatorLength(program);
105         else if (this.name == "operator==" && this.argumentTypes.length == 2
106             && (this.argumentTypes[0] instanceof NullType || this.argumentTypes[0] instanceof ReferenceType)
107             && (this.argumentTypes[1] instanceof NullType || this.argumentTypes[1] instanceof ReferenceType)
108             && this.argumentTypes[0].equals(this.argumentTypes[1]))
109                 func = this._resolveWithReferenceComparator(program);
110         else
111             return null;
112
113         program.add(func);
114         return func;
115     }
116
117     _resolveWithOperatorAnderIndexer(program)
118     {
119         let arrayRefType = this.argumentTypes[0];
120         if (!arrayRefType.isArrayRef)
121             throw new WTypeError(this.origin.originString, `Expected ${arrayRefType} to be an array ref type for operator&[]`);
122
123         let indexType = this.argumentTypes[1];
124         const addressSpace = arrayRefType.addressSpace;
125
126         // The later checkLiteralTypes stage will verify that the literal can be represented as a uint.
127         const uintType = TypeRef.wrap(program.intrinsics.uint);
128         indexType.type = uintType;
129
130         const elementType = this.argumentTypes[0].elementType;
131         this.resultType = TypeRef.wrap(new PtrType(this.origin, addressSpace, TypeRef.wrap(elementType)))
132
133         let arrayRefAccessor = new OperatorAnderIndexer(this.resultType.toString(), addressSpace);
134         const func = new NativeFunc(this.origin, "operator&[]", this.resultType, [
135             new FuncParameter(this.origin, null, arrayRefType),
136             new FuncParameter(this.origin, null, uintType)
137         ]);
138
139         arrayRefAccessor.instantiateImplementation(func);
140
141         return func;
142     }
143
144     _resolveWithOperatorLength(program)
145     {
146         this.resultType = TypeRef.wrap(program.intrinsics.uint);
147
148         if (this.argumentTypes[0].isArray) {
149             const arrayType = this.argumentTypes[0];
150             const func = new NativeFunc(this.origin, "operator.length", this.resultType, [
151                 new FuncParameter(this.origin, null, arrayType)
152             ]);
153             func.implementation = (args) => EPtr.box(arrayType.numElementsValue);
154             return func;
155         } else if (this.argumentTypes[0].isArrayRef) {
156             const arrayRefType = this.argumentTypes[0];
157             const addressSpace = arrayRefType.addressSpace;
158             const operatorLength = new OperatorArrayRefLength(arrayRefType.toString(), addressSpace);
159             const func = new NativeFunc(this.origin, "operator.length", this.resultType, [
160                 new FuncParameter(this.origin, null, arrayRefType)
161             ]);
162             operatorLength.instantiateImplementation(func);
163             return func;
164         } else
165             throw new WTypeError(this.origin.originString, `Expected ${this.argumentTypes[0]} to be array/array ref type for operator.length`);
166     }
167
168     _resolveWithReferenceComparator(program)
169     {
170         let argumentType = this.argumentTypes[0];
171         if (argumentType instanceof NullType)
172             argumentType = this.argumentTypes[1];
173         if (argumentType instanceof NullType) {
174             // We encountered "null == null".
175             // The type isn't observable, so we can pick whatever we want.
176             // FIXME: This can probably be generalized, using the "preferred type" infrastructure used by generic literals
177             argumentType = new PtrType(this.origin, "thread", program.intrinsics.int);
178         }
179         this.resultType = TypeRef.wrap(program.intrinsics.bool);
180         const func = new NativeFunc(this.origin, "operator==", this.resultType, [
181             new FuncParameter(this.origin, null, argumentType),
182             new FuncParameter(this.origin, null, argumentType)
183         ]);
184         func.implementation = ([lhs, rhs]) => {
185             let left = lhs.loadValue();
186             let right = rhs.loadValue();
187             if (left && right)
188                 return EPtr.box(left.equals(right));
189             return EPtr.box(left == right);
190         };
191         return func;
192     }
193     
194     resolveToOverload(overload)
195     {
196         this.func = overload.func;
197         let result = overload.func.returnType;
198         if (!result)
199             throw new Error("Null return type");
200         result = result.visit(new AutoWrapper());
201         this.resultType = result;
202         return result;
203     }
204     
205     setCastData(returnType)
206     {
207         this._returnType = returnType;
208         this._isCast = true;
209     }
210     
211     toString()
212     {
213         return (this.isCast ? "operator " + this.returnType : this.name) +
214             "(" + this.argumentList + ")";
215     }
216 }
217