| /* | |
| * Copyright (C) 2017 Apple Inc. All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| "use strict"; | |
| class CallExpression extends Expression { | |
| constructor(origin, name, typeArguments, argumentList) | |
| { | |
| super(origin); | |
| this._name = name; | |
| this._typeArguments = typeArguments; | |
| this._argumentList = argumentList; | |
| this.func = null; | |
| this._isCast = false; | |
| this._returnType = null; | |
| } | |
| get name() { return this._name; } | |
| get typeArguments() { return this._typeArguments; } | |
| get argumentList() { return this._argumentList; } | |
| get isCast() { return this._isCast; } | |
| get returnType() { return this._returnType; } | |
| static resolve(origin, possibleOverloads, typeParametersInScope, name, typeArguments, argumentList, argumentTypes, returnType) | |
| { | |
| let call = new CallExpression(origin, name, typeArguments, argumentList); | |
| call.argumentTypes = argumentTypes.map(argument => argument.visit(new AutoWrapper())); | |
| call.possibleOverloads = possibleOverloads; | |
| if (returnType) | |
| call.setCastData(returnType); | |
| return {call, resultType: call.resolve(possibleOverloads, typeParametersInScope, typeArguments)}; | |
| } | |
| resolve(possibleOverloads, typeParametersInScope, typeArguments) | |
| { | |
| if (!possibleOverloads) | |
| throw new WTypeError(this.origin.originString, "Did not find any functions named " + this.name); | |
| let overload = null; | |
| let failures = []; | |
| for (let typeParameter of typeParametersInScope) { | |
| if (!(typeParameter instanceof TypeVariable)) | |
| continue; | |
| if (!typeParameter.protocol) | |
| continue; | |
| let signatures = | |
| typeParameter.protocol.protocolDecl.signaturesByNameWithTypeVariable(this.name, typeParameter); | |
| if (!signatures) | |
| continue; | |
| overload = resolveOverloadImpl(signatures, this.typeArguments, this.argumentTypes, this.returnType); | |
| if (overload.func) | |
| break; | |
| failures.push(...overload.failures); | |
| overload = null; | |
| } | |
| if (!overload) { | |
| overload = resolveOverloadImpl( | |
| possibleOverloads, this.typeArguments, this.argumentTypes, this.returnType); | |
| if (!overload.func) { | |
| failures.push(...overload.failures); | |
| let message = "Did not find function named " + this.name + " for call with "; | |
| if (this.typeArguments.length) | |
| message += "type arguments <" + this.typeArguments + "> and "; | |
| message += "argument types (" + this.argumentTypes + ")"; | |
| if (this.returnType) | |
| message +=" and return type " + this.returnType; | |
| if (failures.length) | |
| message += ", but considered:\n" + failures.join("\n") | |
| throw new WTypeError(this.origin.originString, message); | |
| } | |
| } | |
| for (let i = 0; i < typeArguments.length; ++i) { | |
| let typeArgumentType = typeArguments[i]; | |
| let typeParameter = overload.func.typeParameters[i]; | |
| if (!(typeParameter instanceof ConstexprTypeParameter)) | |
| continue; | |
| if (!typeParameter.type.equalsWithCommit(typeArgumentType)) | |
| throw new Error("At " + this.origin.originString + " constexpr type argument and parameter types not equal: argument = " + typeArgumentType + ", parameter = " + typeParameter.type); | |
| } | |
| for (let i = 0; i < this.argumentTypes.length; ++i) { | |
| let argumentType = this.argumentTypes[i]; | |
| let parameterType = overload.func.parameters[i].type.substituteToUnification( | |
| overload.func.typeParameters, overload.unificationContext); | |
| let result = argumentType.equalsWithCommit(parameterType); | |
| if (!result) | |
| throw new Error("At " + this.origin.originString + " argument and parameter types not equal after type argument substitution: argument = " + argumentType + ", parameter = " + parameterType); | |
| } | |
| return this.resolveToOverload(overload); | |
| } | |
| resolveToOverload(overload) | |
| { | |
| this.func = overload.func; | |
| this.actualTypeArguments = overload.typeArguments.map(typeArgument => typeArgument instanceof Type ? typeArgument.visit(new AutoWrapper()) : typeArgument); | |
| this.instantiatedActualTypeArguments = this.actualTypeArguments; | |
| let result = overload.func.returnType.substituteToUnification( | |
| overload.func.typeParameters, overload.unificationContext); | |
| if (!result) | |
| throw new Error("Null return type"); | |
| result = result.visit(new AutoWrapper()); | |
| this.resultType = result; | |
| return result; | |
| } | |
| becomeCast(returnType) | |
| { | |
| this._returnType = new TypeRef(this.origin, this.name, this._typeArguments); | |
| this._returnType.type = returnType; | |
| this._name = "operator cast"; | |
| this._isCast = true; | |
| this._typeArguments = []; | |
| } | |
| setCastData(returnType) | |
| { | |
| this._returnType = returnType; | |
| this._isCast = true; | |
| } | |
| toString() | |
| { | |
| return (this.isCast ? "operator " + this.returnType : this.name) + | |
| "<" + this.typeArguments + ">" + | |
| (this.actualTypeArguments ? "<<" + this.actualTypeArguments + ">>" : "") + | |
| "(" + this.argumentList + ")"; | |
| } | |
| } | |