| Index: src/sksl/ir/SkSLFunctionDeclaration.h | 
| diff --git a/src/sksl/ir/SkSLFunctionDeclaration.h b/src/sksl/ir/SkSLFunctionDeclaration.h | 
| index ffde0c66c130aedb069da390899b5011eb1cc1af..52a579a89b9e13dbb2f36e22feaf224d0b04fbe8 100644 | 
| --- a/src/sksl/ir/SkSLFunctionDeclaration.h | 
| +++ b/src/sksl/ir/SkSLFunctionDeclaration.h | 
| @@ -8,6 +8,7 @@ | 
| #ifndef SKSL_FUNCTIONDECLARATION | 
| #define SKSL_FUNCTIONDECLARATION | 
|  | 
| +#include "SkSLExpression.h" | 
| #include "SkSLModifiers.h" | 
| #include "SkSLSymbol.h" | 
| #include "SkSLSymbolTable.h" | 
| @@ -55,6 +56,50 @@ struct FunctionDeclaration : public Symbol { | 
| return true; | 
| } | 
|  | 
| +    /** | 
| +     * Determine the effective types of this function's parameters and return value when called with | 
| +     * the given arguments. This is relevant for functions with generic parameter types, where this | 
| +     * will collapse the generic types down into specific concrete types. | 
| +     * | 
| +     * Returns true if it was able to select a concrete set of types for the generic function, false | 
| +     * if there is no possible way this can match the argument types. Note that even a true return | 
| +     * does not guarantee that the function can be successfully called with those arguments, merely | 
| +     * indicates that an attempt should be made. If false is returned, the state of | 
| +     * outParameterTypes and outReturnType are undefined. | 
| +     */ | 
| +    bool determineFinalTypes(const std::vector<std::unique_ptr<Expression>>& arguments, | 
| +                             std::vector<const Type*>* outParameterTypes, | 
| +                             const Type** outReturnType) const { | 
| +        assert(arguments.size() == fParameters.size()); | 
| +        int genericIndex = -1; | 
| +        for (size_t i = 0; i < arguments.size(); i++) { | 
| +            if (fParameters[i]->fType.kind() == Type::kGeneric_Kind) { | 
| +                std::vector<const Type*> types = fParameters[i]->fType.coercibleTypes(); | 
| +                if (genericIndex == -1) { | 
| +                    for (size_t j = 0; j < types.size(); j++) { | 
| +                        if (arguments[i]->fType.canCoerceTo(*types[j])) { | 
| +                            genericIndex = j; | 
| +                            break; | 
| +                        } | 
| +                    } | 
| +                    if (genericIndex == -1) { | 
| +                        return false; | 
| +                    } | 
| +                } | 
| +                outParameterTypes->push_back(types[genericIndex]); | 
| +            } else { | 
| +                outParameterTypes->push_back(&fParameters[i]->fType); | 
| +            } | 
| +        } | 
| +        if (fReturnType.kind() == Type::kGeneric_Kind) { | 
| +            assert(genericIndex != -1); | 
| +            *outReturnType = fReturnType.coercibleTypes()[genericIndex]; | 
| +        } else { | 
| +            *outReturnType = &fReturnType; | 
| +        } | 
| +        return true; | 
| +    } | 
| + | 
| mutable bool fDefined; | 
| bool fBuiltin; | 
| const std::vector<const Variable*> fParameters; | 
|  |