• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
9 
10 #include "include/core/SkSpan.h"
11 #include "include/core/SkTypes.h"
12 #include "include/private/SkSLDefines.h"
13 #include "include/private/SkSLLayout.h"
14 #include "include/private/SkSLModifiers.h"
15 #include "include/private/SkSLProgramKind.h"
16 #include "include/private/SkSLString.h"
17 #include "include/private/base/SkTo.h"
18 #include "include/sksl/SkSLErrorReporter.h"
19 #include "include/sksl/SkSLPosition.h"
20 #include "src/base/SkStringView.h"
21 #include "src/sksl/SkSLBuiltinTypes.h"
22 #include "src/sksl/SkSLCompiler.h"
23 #include "src/sksl/SkSLContext.h"
24 #include "src/sksl/SkSLModifiersPool.h"
25 #include "src/sksl/SkSLProgramSettings.h"
26 #include "src/sksl/ir/SkSLExpression.h"
27 #include "src/sksl/ir/SkSLSymbolTable.h"
28 #include "src/sksl/ir/SkSLType.h"
29 #include "src/sksl/ir/SkSLVariable.h"
30 
31 #include <algorithm>
32 #include <cstddef>
33 #include <utility>
34 
35 namespace SkSL {
36 
check_modifiers(const Context & context,Position pos,const Modifiers & modifiers)37 static bool check_modifiers(const Context& context,
38                             Position pos,
39                             const Modifiers& modifiers) {
40     const int permitted = Modifiers::kInline_Flag |
41                           Modifiers::kNoInline_Flag |
42                           (context.fConfig->fIsBuiltinCode ? (Modifiers::kES3_Flag |
43                                                               Modifiers::kPure_Flag |
44                                                               Modifiers::kExport_Flag) : 0);
45     modifiers.checkPermitted(context, pos, permitted, /*permittedLayoutFlags=*/0);
46     if ((modifiers.fFlags & Modifiers::kInline_Flag) &&
47         (modifiers.fFlags & Modifiers::kNoInline_Flag)) {
48         context.fErrors->error(pos, "functions cannot be both 'inline' and 'noinline'");
49         return false;
50     }
51     return true;
52 }
53 
check_return_type(const Context & context,Position pos,const Type & returnType)54 static bool check_return_type(const Context& context, Position pos, const Type& returnType) {
55     ErrorReporter& errors = *context.fErrors;
56     if (returnType.isArray()) {
57         errors.error(pos, "functions may not return type '" + returnType.displayName() + "'");
58         return false;
59     }
60     if (context.fConfig->strictES2Mode() && returnType.isOrContainsArray()) {
61         errors.error(pos, "functions may not return structs containing arrays");
62         return false;
63     }
64     if (!context.fConfig->fIsBuiltinCode && returnType.componentType().isOpaque()) {
65         errors.error(pos, "functions may not return opaque type '" + returnType.displayName() +
66                 "'");
67         return false;
68     }
69     return true;
70 }
71 
check_parameters(const Context & context,std::vector<std::unique_ptr<Variable>> & parameters,bool isMain)72 static bool check_parameters(const Context& context,
73                              std::vector<std::unique_ptr<Variable>>& parameters,
74                              bool isMain) {
75     auto typeIsValidForColor = [&](const Type& type) {
76         return type.matches(*context.fTypes.fHalf4) || type.matches(*context.fTypes.fFloat4);
77     };
78 
79     // The first color parameter passed to main() is the input color; the second is the dest color.
80     static constexpr int kBuiltinColorIDs[] = {SK_INPUT_COLOR_BUILTIN, SK_DEST_COLOR_BUILTIN};
81     unsigned int builtinColorIndex = 0;
82 
83     // Check modifiers on each function parameter.
84     for (auto& param : parameters) {
85         const Type& type = param->type();
86         int permittedFlags = Modifiers::kConst_Flag | Modifiers::kIn_Flag;
87         if (!type.isOpaque()) {
88             permittedFlags |= Modifiers::kOut_Flag;
89         }
90         if (type.typeKind() == Type::TypeKind::kTexture) {
91             permittedFlags |= Modifiers::kReadOnly_Flag | Modifiers::kWriteOnly_Flag;
92         }
93         param->modifiers().checkPermitted(context,
94                                           param->modifiersPosition(),
95                                           permittedFlags,
96                                           /*permittedLayoutFlags=*/0);
97         // Only the (builtin) declarations of 'sample' are allowed to have shader/colorFilter or FP
98         // parameters. You can pass other opaque types to functions safely; this restriction is
99         // specific to "child" objects.
100         if (type.isEffectChild() && !context.fConfig->fIsBuiltinCode) {
101             context.fErrors->error(param->fPosition, "parameters of type '" + type.displayName() +
102                                                      "' not allowed");
103             return false;
104         }
105 
106         Modifiers m = param->modifiers();
107         bool modifiersChanged = false;
108 
109         // The `in` modifier on function parameters is implicit, so we can replace `in float x` with
110         // `float x`. This prevents any ambiguity when matching a function by its param types.
111         if (Modifiers::kIn_Flag == (m.fFlags & (Modifiers::kOut_Flag | Modifiers::kIn_Flag))) {
112             m.fFlags &= ~(Modifiers::kOut_Flag | Modifiers::kIn_Flag);
113             modifiersChanged = true;
114         }
115 
116         if (isMain) {
117             if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind) &&
118                 context.fConfig->fKind != ProgramKind::kMeshFragment &&
119                 context.fConfig->fKind != ProgramKind::kMeshVertex) {
120                 // We verify that the signature is fully correct later. For now, if this is a
121                 // runtime effect of any flavor, a float2 param is supposed to be the coords, and a
122                 // half4/float parameter is supposed to be the input or destination color:
123                 if (type.matches(*context.fTypes.fFloat2)) {
124                     m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
125                     modifiersChanged = true;
126                 } else if (typeIsValidForColor(type) &&
127                            builtinColorIndex < std::size(kBuiltinColorIDs)) {
128                     m.fLayout.fBuiltin = kBuiltinColorIDs[builtinColorIndex++];
129                     modifiersChanged = true;
130                 }
131             } else if (ProgramConfig::IsFragment(context.fConfig->fKind)) {
132                 // For testing purposes, we have .sksl inputs that are treated as both runtime
133                 // effects and fragment shaders. To make that work, fragment shaders are allowed to
134                 // have a coords parameter.
135                 if (type.matches(*context.fTypes.fFloat2)) {
136                     m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
137                     modifiersChanged = true;
138                 }
139             }
140         }
141 
142         if (modifiersChanged) {
143             param->setModifiers(context.fModifiersPool->add(m));
144         }
145     }
146     return true;
147 }
148 
check_main_signature(const Context & context,Position pos,const Type & returnType,std::vector<std::unique_ptr<Variable>> & parameters)149 static bool check_main_signature(const Context& context, Position pos, const Type& returnType,
150                                  std::vector<std::unique_ptr<Variable>>& parameters) {
151     ErrorReporter& errors = *context.fErrors;
152     ProgramKind kind = context.fConfig->fKind;
153 
154     auto typeIsValidForColor = [&](const Type& type) {
155         return type.matches(*context.fTypes.fHalf4) || type.matches(*context.fTypes.fFloat4);
156     };
157 
158     auto typeIsValidForAttributes = [&](const Type& type) {
159         return type.isStruct() && type.name() == "Attributes";
160     };
161 
162     auto typeIsValidForVaryings = [&](const Type& type) {
163         return type.isStruct() && type.name() == "Varyings";
164     };
165 
166     auto paramIsCoords = [&](int idx) {
167         const Variable& p = *parameters[idx];
168         return p.type().matches(*context.fTypes.fFloat2) &&
169                p.modifiers().fFlags == 0 &&
170                p.modifiers().fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN;
171     };
172 
173     auto paramIsBuiltinColor = [&](int idx, int builtinID) {
174         const Variable& p = *parameters[idx];
175         return typeIsValidForColor(p.type()) &&
176                p.modifiers().fFlags == 0 &&
177                p.modifiers().fLayout.fBuiltin == builtinID;
178     };
179 
180     auto paramIsConstInAttributes = [&](int idx) {
181         const Variable& p = *parameters[idx];
182         return typeIsValidForAttributes(p.type()) && p.modifiers().fFlags == Modifiers::kConst_Flag;
183     };
184 
185     auto paramIsConstInVaryings = [&](int idx) {
186         const Variable& p = *parameters[idx];
187         return typeIsValidForVaryings(p.type()) && p.modifiers().fFlags == Modifiers::kConst_Flag;
188     };
189 
190     auto paramIsOutColor = [&](int idx) {
191         const Variable& p = *parameters[idx];
192         return typeIsValidForColor(p.type()) && p.modifiers().fFlags == Modifiers::kOut_Flag;
193     };
194 
195     auto paramIsInputColor = [&](int n) { return paramIsBuiltinColor(n, SK_INPUT_COLOR_BUILTIN); };
196     auto paramIsDestColor  = [&](int n) { return paramIsBuiltinColor(n, SK_DEST_COLOR_BUILTIN); };
197 
198     switch (kind) {
199         case ProgramKind::kRuntimeColorFilter:
200         case ProgramKind::kPrivateRuntimeColorFilter: {
201             // (half4|float4) main(half4|float4)
202             if (!typeIsValidForColor(returnType)) {
203                 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
204                 return false;
205             }
206             bool validParams = (parameters.size() == 1 && paramIsInputColor(0));
207             if (!validParams) {
208                 errors.error(pos, "'main' parameter must be 'vec4', 'float4', or 'half4'");
209                 return false;
210             }
211             break;
212         }
213         case ProgramKind::kRuntimeShader:
214         case ProgramKind::kPrivateRuntimeShader: {
215             // (half4|float4) main(float2)
216             if (!typeIsValidForColor(returnType)) {
217                 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
218                 return false;
219             }
220             if (!(parameters.size() == 1 && paramIsCoords(0))) {
221                 errors.error(pos, "'main' parameter must be 'float2' or 'vec2'");
222                 return false;
223             }
224             break;
225         }
226         case ProgramKind::kRuntimeBlender:
227         case ProgramKind::kPrivateRuntimeBlender: {
228             // (half4|float4) main(half4|float4, half4|float4)
229             if (!typeIsValidForColor(returnType)) {
230                 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
231                 return false;
232             }
233             if (!(parameters.size() == 2 &&
234                   paramIsInputColor(0) &&
235                   paramIsDestColor(1))) {
236                 errors.error(pos, "'main' parameters must be (vec4|float4|half4, "
237                         "vec4|float4|half4)");
238                 return false;
239             }
240             break;
241         }
242         case ProgramKind::kMeshVertex: {
243             // Varyings main(const Attributes)
244             if (!typeIsValidForVaryings(returnType)) {
245                 errors.error(pos, "'main' must return 'Varyings'.");
246                 return false;
247             }
248             if (!(parameters.size() == 1 && paramIsConstInAttributes(0))) {
249                 errors.error(pos, "'main' parameter must be 'const Attributes'.");
250                 return false;
251             }
252             break;
253         }
254         case ProgramKind::kMeshFragment: {
255             // float2 main(const Varyings) -or- float2 main(const Varyings, out half4|float4)
256             if (!returnType.matches(*context.fTypes.fFloat2)) {
257                 errors.error(pos, "'main' must return: 'vec2' or 'float2'");
258                 return false;
259             }
260             if (!((parameters.size() == 1 && paramIsConstInVaryings(0)) ||
261                   (parameters.size() == 2 && paramIsConstInVaryings(0) && paramIsOutColor(1)))) {
262                 errors.error(pos,
263                              "'main' parameters must be (const Varyings, (out (half4|float4))?)");
264                 return false;
265             }
266             break;
267         }
268         case ProgramKind::kGeneric:
269             // No rules apply here
270             break;
271         case ProgramKind::kFragment:
272         case ProgramKind::kGraphiteFragment: {
273             bool validParams = (parameters.size() == 0) ||
274                                (parameters.size() == 1 && paramIsCoords(0));
275             if (!validParams) {
276                 errors.error(pos, "shader 'main' must be main() or main(float2)");
277                 return false;
278             }
279             break;
280         }
281         case ProgramKind::kVertex:
282         case ProgramKind::kGraphiteVertex:
283         case ProgramKind::kCompute:
284             if (!returnType.matches(*context.fTypes.fVoid)) {
285                 errors.error(pos, "'main' must return 'void'");
286                 return false;
287             }
288             if (parameters.size()) {
289                 errors.error(pos, "shader 'main' must have zero parameters");
290                 return false;
291             }
292             break;
293     }
294     return true;
295 }
296 
297 /**
298  * Given a concrete type (`float3`) and a generic type (`$genType`), returns the index of the
299  * concrete type within the generic type's typelist. Returns -1 if there is no match.
300  */
find_generic_index(const Type & concreteType,const Type & genericType,bool allowNarrowing)301 static int find_generic_index(const Type& concreteType,
302                               const Type& genericType,
303                               bool allowNarrowing) {
304     SkSpan<const Type* const> genericTypes = genericType.coercibleTypes();
305     for (size_t index = 0; index < genericTypes.size(); ++index) {
306         if (concreteType.canCoerceTo(*genericTypes[index], allowNarrowing)) {
307             return index;
308         }
309     }
310     return -1;
311 }
312 
313 /** Returns true if the types match, or if `concreteType` can be found in `maybeGenericType`. */
type_generically_matches(const Type & concreteType,const Type & maybeGenericType)314 static bool type_generically_matches(const Type& concreteType, const Type& maybeGenericType) {
315     return maybeGenericType.isGeneric()
316                 ? find_generic_index(concreteType, maybeGenericType, /*allowNarrowing=*/false) != -1
317                 : concreteType.matches(maybeGenericType);
318 }
319 
320 /**
321  * Checks a parameter list (params) against the parameters of a function that was declared earlier
322  * (otherParams). Returns true if they match, even if the parameters in `otherParams` contain
323  * generic types.
324  */
parameters_match(const std::vector<std::unique_ptr<Variable>> & params,const std::vector<Variable * > & otherParams)325 static bool parameters_match(const std::vector<std::unique_ptr<Variable>>& params,
326                              const std::vector<Variable*>& otherParams) {
327     // If the param lists are different lengths, they're definitely not a match.
328     if (params.size() != otherParams.size()) {
329         return false;
330     }
331 
332     // Figure out a consistent generic index (or bail if we find a contradiction).
333     int genericIndex = -1;
334     for (size_t i = 0; i < params.size(); ++i) {
335         const Type* paramType = &params[i]->type();
336         const Type* otherParamType = &otherParams[i]->type();
337 
338         if (otherParamType->isGeneric()) {
339             int genericIndexForThisParam = find_generic_index(*paramType, *otherParamType,
340                                                               /*allowNarrowing=*/false);
341             if (genericIndexForThisParam == -1) {
342                 // The type wasn't a match for this generic at all; these params can't be a match.
343                 return false;
344             }
345             if (genericIndex != -1 && genericIndex != genericIndexForThisParam) {
346                 // The generic index mismatches from what we determined on a previous parameter.
347                 return false;
348             }
349             genericIndex = genericIndexForThisParam;
350         }
351     }
352 
353     // Now that we've determined a generic index (if we needed one), do a parameter check.
354     for (size_t i = 0; i < params.size(); i++) {
355         const Type* paramType = &params[i]->type();
356         const Type* otherParamType = &otherParams[i]->type();
357 
358         // Make generic types concrete.
359         if (otherParamType->isGeneric()) {
360             SkASSERT(genericIndex != -1);
361             SkASSERT(genericIndex < (int)otherParamType->coercibleTypes().size());
362             otherParamType = otherParamType->coercibleTypes()[genericIndex];
363         }
364         // Detect type mismatches.
365         if (!paramType->matches(*otherParamType)) {
366             return false;
367         }
368     }
369     return true;
370 }
371 
372 /**
373  * Checks for a previously existing declaration of this function, reporting errors if there is an
374  * incompatible symbol. Returns true and sets outExistingDecl to point to the existing declaration
375  * (or null if none) on success, returns false on error.
376  */
find_existing_declaration(const Context & context,SymbolTable & symbols,Position pos,const Modifiers * modifiers,std::string_view name,std::vector<std::unique_ptr<Variable>> & parameters,Position returnTypePos,const Type * returnType,FunctionDeclaration ** outExistingDecl)377 static bool find_existing_declaration(const Context& context,
378                                       SymbolTable& symbols,
379                                       Position pos,
380                                       const Modifiers* modifiers,
381                                       std::string_view name,
382                                       std::vector<std::unique_ptr<Variable>>& parameters,
383                                       Position returnTypePos,
384                                       const Type* returnType,
385                                       FunctionDeclaration** outExistingDecl) {
386     auto invalidDeclDescription = [&]() -> std::string {
387         std::vector<Variable*> paramPtrs;
388         paramPtrs.reserve(parameters.size());
389         for (std::unique_ptr<Variable>& param : parameters) {
390             paramPtrs.push_back(param.get());
391         }
392         return FunctionDeclaration(pos,
393                                    modifiers,
394                                    name,
395                                    std::move(paramPtrs),
396                                    returnType,
397                                    context.fConfig->fIsBuiltinCode)
398                 .description();
399     };
400 
401     ErrorReporter& errors = *context.fErrors;
402     Symbol* entry = symbols.findMutable(name);
403     *outExistingDecl = nullptr;
404     if (entry) {
405         if (!entry->is<FunctionDeclaration>()) {
406             errors.error(pos, "symbol '" + std::string(name) + "' was already defined");
407             return false;
408         }
409         for (FunctionDeclaration* other = &entry->as<FunctionDeclaration>(); other;
410              other = other->mutableNextOverload()) {
411             SkASSERT(name == other->name());
412             if (!parameters_match(parameters, other->parameters())) {
413                 continue;
414             }
415             if (!type_generically_matches(*returnType, other->returnType())) {
416                 errors.error(returnTypePos,
417                              "functions '" + invalidDeclDescription() + "' and '" +
418                              other->description() + "' differ only in return type");
419                 return false;
420             }
421             for (size_t i = 0; i < parameters.size(); i++) {
422                 if (parameters[i]->modifiers() != other->parameters()[i]->modifiers()) {
423                     errors.error(parameters[i]->fPosition,
424                                  "modifiers on parameter " + std::to_string(i + 1) +
425                                  " differ between declaration and definition");
426                     return false;
427                 }
428             }
429             if (*modifiers != other->modifiers() || other->definition() || other->isIntrinsic()) {
430                 errors.error(pos, "duplicate definition of '" + invalidDeclDescription() + "'");
431                 return false;
432             }
433             *outExistingDecl = other;
434             break;
435         }
436         if (!*outExistingDecl && entry->as<FunctionDeclaration>().isMain()) {
437             errors.error(pos, "duplicate definition of 'main'");
438             return false;
439         }
440     }
441     return true;
442 }
443 
FunctionDeclaration(Position pos,const Modifiers * modifiers,std::string_view name,std::vector<Variable * > parameters,const Type * returnType,bool builtin)444 FunctionDeclaration::FunctionDeclaration(Position pos,
445                                          const Modifiers* modifiers,
446                                          std::string_view name,
447                                          std::vector<Variable*> parameters,
448                                          const Type* returnType,
449                                          bool builtin)
450         : INHERITED(pos, kIRNodeKind, name, /*type=*/nullptr)
451         , fDefinition(nullptr)
452         , fModifiers(modifiers)
453         , fParameters(std::move(parameters))
454         , fReturnType(returnType)
455         , fBuiltin(builtin)
456         , fIsMain(name == "main")
457         , fIntrinsicKind(builtin ? FindIntrinsicKind(name) : kNotIntrinsic) {
458     // None of the parameters are allowed to be be null.
459     SkASSERT(std::count(fParameters.begin(), fParameters.end(), nullptr) == 0);
460 }
461 
Convert(const Context & context,SymbolTable & symbols,Position pos,Position modifiersPosition,const Modifiers * modifiers,std::string_view name,std::vector<std::unique_ptr<Variable>> parameters,Position returnTypePos,const Type * returnType)462 FunctionDeclaration* FunctionDeclaration::Convert(const Context& context,
463                                                   SymbolTable& symbols,
464                                                   Position pos,
465                                                   Position modifiersPosition,
466                                                   const Modifiers* modifiers,
467                                                   std::string_view name,
468                                                   std::vector<std::unique_ptr<Variable>> parameters,
469                                                   Position returnTypePos,
470                                                   const Type* returnType) {
471     bool isMain = (name == "main");
472 
473     FunctionDeclaration* decl = nullptr;
474     if (!check_modifiers(context, modifiersPosition, *modifiers) ||
475         !check_return_type(context, returnTypePos, *returnType) ||
476         !check_parameters(context, parameters, isMain) ||
477         (isMain && !check_main_signature(context, pos, *returnType, parameters)) ||
478         !find_existing_declaration(context, symbols, pos, modifiers, name, parameters,
479                                    returnTypePos, returnType, &decl)) {
480         return nullptr;
481     }
482     std::vector<Variable*> finalParameters;
483     finalParameters.reserve(parameters.size());
484     for (std::unique_ptr<Variable>& param : parameters) {
485         finalParameters.push_back(symbols.takeOwnershipOfSymbol(std::move(param)));
486     }
487     if (decl) {
488         return decl;
489     }
490     auto result = std::make_unique<FunctionDeclaration>(pos,
491                                                         modifiers,
492                                                         name,
493                                                         std::move(finalParameters),
494                                                         returnType,
495                                                         context.fConfig->fIsBuiltinCode);
496     return symbols.add(std::move(result));
497 }
498 
mangledName() const499 std::string FunctionDeclaration::mangledName() const {
500     if ((this->isBuiltin() && !this->definition()) || this->isMain()) {
501         // Builtins without a definition (like `sin` or `sqrt`) must use their real names.
502         return std::string(this->name());
503     }
504     // Built-in functions can have a $ prefix, which will fail to compile in GLSL. Remove the
505     // $ and add a unique mangling specifier, so user code can't conflict with the name.
506     std::string_view name = this->name();
507     const char* builtinMarker = "";
508     if (skstd::starts_with(name, '$')) {
509         name.remove_prefix(1);
510         builtinMarker = "Q";  // a unique, otherwise-unused mangle character
511     }
512     // Rename function to `funcname_returntypeparamtypes`.
513     std::string result = std::string(name) + "_" + builtinMarker +
514                          this->returnType().abbreviatedName();
515     for (const Variable* p : this->parameters()) {
516         result += p->type().abbreviatedName();
517     }
518     return result;
519 }
520 
description() const521 std::string FunctionDeclaration::description() const {
522     int modifierFlags = this->modifiers().fFlags;
523     std::string result =
524             (modifierFlags ? Modifiers::DescribeFlags(modifierFlags) + " " : std::string()) +
525             this->returnType().displayName() + " " + std::string(this->name()) + "(";
526     auto separator = SkSL::String::Separator();
527     for (const Variable* p : this->parameters()) {
528         result += separator();
529         // We can't just say `p->description()` here, because occasionally might have added layout
530         // flags onto parameters (like `layout(builtin=10009)`) and don't want to reproduce that.
531         if (p->modifiers().fFlags) {
532             result += Modifiers::DescribeFlags(p->modifiers().fFlags) + " ";
533         }
534         result += p->type().displayName();
535         result += " ";
536         result += p->name();
537     }
538     result += ")";
539     return result;
540 }
541 
matches(const FunctionDeclaration & f) const542 bool FunctionDeclaration::matches(const FunctionDeclaration& f) const {
543     if (this->name() != f.name()) {
544         return false;
545     }
546     const std::vector<Variable*>& parameters = this->parameters();
547     const std::vector<Variable*>& otherParameters = f.parameters();
548     if (parameters.size() != otherParameters.size()) {
549         return false;
550     }
551     for (size_t i = 0; i < parameters.size(); i++) {
552         if (!parameters[i]->type().matches(otherParameters[i]->type())) {
553             return false;
554         }
555     }
556     return true;
557 }
558 
determineFinalTypes(const ExpressionArray & arguments,ParamTypes * outParameterTypes,const Type ** outReturnType) const559 bool FunctionDeclaration::determineFinalTypes(const ExpressionArray& arguments,
560                                               ParamTypes* outParameterTypes,
561                                               const Type** outReturnType) const {
562     const std::vector<Variable*>& parameters = this->parameters();
563     SkASSERT(SkToSizeT(arguments.size()) == parameters.size());
564 
565     outParameterTypes->reserve_back(arguments.size());
566     int genericIndex = -1;
567     for (int i = 0; i < arguments.size(); i++) {
568         // Non-generic parameters are final as-is.
569         const Type& parameterType = parameters[i]->type();
570         if (!parameterType.isGeneric()) {
571             outParameterTypes->push_back(&parameterType);
572             continue;
573         }
574         // We use the first generic parameter we find to lock in the generic index;
575         // e.g. if we find `float3` here, all `$genType`s will be assumed to be `float3`.
576         if (genericIndex == -1) {
577             genericIndex = find_generic_index(arguments[i]->type(), parameterType,
578                                               /*allowNarrowing=*/true);
579             if (genericIndex == -1) {
580                 // The passed-in type wasn't a match for ANY of the generic possibilities.
581                 // This function isn't a match at all.
582                 return false;
583             }
584         }
585         outParameterTypes->push_back(parameterType.coercibleTypes()[genericIndex]);
586     }
587     // Apply the generic index to our return type.
588     const Type& returnType = this->returnType();
589     if (returnType.isGeneric()) {
590         if (genericIndex == -1) {
591             // We don't support functions with a generic return type and no other generics.
592             return false;
593         }
594         *outReturnType = returnType.coercibleTypes()[genericIndex];
595     } else {
596         *outReturnType = &returnType;
597     }
598     return true;
599 }
600 
601 }  // namespace SkSL
602