blob: 3ba34f62db4755ac4a21a388f0f2ede1f67373ff [file] [log] [blame]
/*
* 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 FuncInstantiator {
constructor(program)
{
this._program = program;
this._instances = new Map();
}
// Returns a Func object that uniquely identifies a particular system of type arguments. You must
// intantiate things with concrete types, because this code casually assumes this. Note that this
// will return a different func from `func` no matter what. This ensures that we can use the
// returned func for rewrites that instantiate types without destroying our ability to do overload
// resolutions on the original Program.
getUnique(func, typeArguments)
{
class FindTypeVariable extends Visitor {
visitTypeRef(node)
{
for (let typeArgument of node.typeArguments)
typeArgument.visit(this);
}
visitTypeVariable(node) {
throw new Error("Unexpected type variable: " + node + " when instantiating " + func + " with arguments " + typeArguments);
}
}
for (let typeArgument of typeArguments)
typeArgument.visit(new FindTypeVariable());
let instances = this._instances.get(func);
if (!instances)
this._instances.set(func, instances = []);
for (let instance of instances) {
let ok = true;
for (let i = instance.typeArguments.length; i--;) {
if (!instance.typeArguments[i].equals(typeArguments[i])) {
ok = false;
break;
}
}
if (!ok)
continue;
return instance.func;
}
let thisInstantiator = this;
class InstantiationSubstitution extends Substitution {
visitCallExpression(node)
{
let result = super.visitCallExpression(node);
// We may have to re-resolve the function call, if it was a call to a protocol
// signature.
if (result.func instanceof ProtocolFuncDecl) {
let overload = resolveOverloadImpl(result.possibleOverloads, result.typeArguments, result.argumentTypes, result.returnType);
if (!overload.func)
throw new Error("Could not resolve protocol signature function call during instantiation: " + result.func + (overload.failures.length ? "; tried:\n" + overload.failures.join("\n") : ""));
result.resolveToOverload(overload);
}
if (result.func.isNative)
result.nativeFuncInstance = thisInstantiator.getUnique(result.func, result.actualTypeArguments);
return result;
}
}
let substitution = new InstantiationSubstitution(func.typeParameters, typeArguments);
class InstantiationInstantiateImmediates extends InstantiateImmediates {
visitCallExpression(node)
{
// We need to preserve certain things that would have instantiated, but that we cannot
// instantiate without breaking chain-instantiations (generic function calls generic
// function so therefore the instantiated generic function must still have the original
// (uninstantiated) types to instantiate the generic function that it calls).
let result = new CallExpression(
node.origin, node.name, node.typeArguments,
node.argumentList.map(argument => Node.visit(argument, this)));
result = this.processDerivedCallData(node, result);
result.argumentTypes = Array.from(node.argumentTypes);
if (node.isCast)
result.setCastData(node.returnType);
result.actualTypeArguments = Array.from(node.actualTypeArguments);
return result;
}
}
let instantiateImmediates = new InstantiationInstantiateImmediates();
class Instantiate {
visitFuncDef(func)
{
let returnType = func.returnType.visit(substitution);
returnType = returnType.visit(instantiateImmediates);
let parameters = func.parameters.map(parameter => parameter.visit(substitution));
parameters = parameters.map(parameter => parameter.visit(instantiateImmediates));
let body = func.body.visit(substitution);
body = body.visit(instantiateImmediates);
return new FuncDef(
func.origin, func.name, returnType, [], parameters, body, func.isCast,
func.shaderType);
}
visitNativeFunc(func)
{
return new NativeFuncInstance(
func,
func.returnType.visit(substitution).visit(instantiateImmediates),
func.parameters.map(parameter => parameter.visit(substitution).visit(instantiateImmediates)),
func.isCast,
func.shaderType,
func.instantiateImplementation(substitution));
}
}
let resultingFunc = func.visit(new Instantiate());
resultingFunc.uninstantiatedReturnType = func.returnType.visit(substitution);
let instance = {func: resultingFunc, typeArguments};
instances.push(instance);
return resultingFunc;
}
}