xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLFunctionDeclaration.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/base/SkTo.h"
13 #include "src/base/SkEnumBitMask.h"
14 #include "src/base/SkStringView.h"
15 #include "src/sksl/SkSLBuiltinTypes.h"
16 #include "src/sksl/SkSLContext.h"
17 #include "src/sksl/SkSLDefines.h"
18 #include "src/sksl/SkSLErrorReporter.h"
19 #include "src/sksl/SkSLPosition.h"
20 #include "src/sksl/SkSLProgramKind.h"
21 #include "src/sksl/SkSLProgramSettings.h"
22 #include "src/sksl/SkSLString.h"
23 #include "src/sksl/ir/SkSLExpression.h"
24 #include "src/sksl/ir/SkSLLayout.h"
25 #include "src/sksl/ir/SkSLModifierFlags.h"
26 #include "src/sksl/ir/SkSLModifiers.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 <cstddef>
32 #include <utility>
33 
34 using namespace skia_private;
35 
36 namespace SkSL {
37 
check_modifiers(const Context & context,Position pos,ModifierFlags modifierFlags)38 static bool check_modifiers(const Context& context, Position pos, ModifierFlags modifierFlags) {
39     const ModifierFlags permitted = ModifierFlag::kInline |
40                                     ModifierFlag::kNoInline |
41                                     (context.fConfig->isBuiltinCode() ? ModifierFlag::kES3 |
42                                                                         ModifierFlag::kPure |
43                                                                         ModifierFlag::kExport
44                                                                       : ModifierFlag::kNone);
45     modifierFlags.checkPermittedFlags(context, pos, permitted);
46     if (modifierFlags.isInline() && modifierFlags.isNoInline()) {
47         context.fErrors->error(pos, "functions cannot be both 'inline' and 'noinline'");
48         return false;
49     }
50     return true;
51 }
52 
check_return_type(const Context & context,Position pos,const Type & returnType)53 static bool check_return_type(const Context& context, Position pos, const Type& returnType) {
54     ErrorReporter& errors = *context.fErrors;
55     if (returnType.isArray()) {
56         errors.error(pos, "functions may not return type '" + returnType.displayName() + "'");
57         return false;
58     }
59     if (context.fConfig->strictES2Mode() && returnType.isOrContainsArray()) {
60         errors.error(pos, "functions may not return structs containing arrays");
61         return false;
62     }
63     if (!context.fConfig->isBuiltinCode() && returnType.componentType().isOpaque()) {
64         errors.error(pos, "functions may not return opaque type '" + returnType.displayName() +
65                 "'");
66         return false;
67     }
68     return true;
69 }
70 
check_parameters(const Context & context,TArray<std::unique_ptr<Variable>> & parameters,ModifierFlags modifierFlags,IntrinsicKind intrinsicKind)71 static bool check_parameters(const Context& context,
72                              TArray<std::unique_ptr<Variable>>& parameters,
73                              ModifierFlags modifierFlags,
74                              IntrinsicKind intrinsicKind) {
75     // Check modifiers on each function parameter.
76     for (auto& param : parameters) {
77         const Type& type = param->type();
78         ModifierFlags permittedFlags = ModifierFlag::kConst | ModifierFlag::kIn;
79         LayoutFlags permittedLayoutFlags = LayoutFlag::kNone;
80         if (!type.isOpaque()) {
81             permittedFlags |= ModifierFlag::kOut;
82         }
83         if (type.isStorageTexture()) {
84             // We allow `readonly`, `writeonly` and `layout(pixel-format)` on storage textures.
85             permittedFlags |= ModifierFlag::kReadOnly | ModifierFlag::kWriteOnly;
86             permittedLayoutFlags |= LayoutFlag::kAllPixelFormats;
87 
88             // Intrinsics are allowed to accept any pixel format, but user code must explicitly
89             // specify a pixel format like `layout(rgba32f)`.
90             if (intrinsicKind == kNotIntrinsic &&
91                 !(param->layout().fFlags & LayoutFlag::kAllPixelFormats)) {
92                 context.fErrors->error(param->fPosition, "storage texture parameters must specify "
93                                                          "a pixel format layout-qualifier");
94                 return false;
95             }
96         }
97         param->modifierFlags().checkPermittedFlags(context, param->modifiersPosition(),
98                                                    permittedFlags);
99         param->layout().checkPermittedLayout(context, param->modifiersPosition(),
100                                              permittedLayoutFlags);
101 
102         // Public Runtime Effects aren't allowed to pass shader/colorFilter/blender types to
103         // function calls. You can pass other opaque types to functions safely; this restriction is
104         // specific to "child" objects.
105         if (!ProgramConfig::AllowsPrivateIdentifiers(context.fConfig->fKind) &&
106             type.isEffectChild()) {
107             context.fErrors->error(param->fPosition, "parameters of type '" + type.displayName() +
108                                                      "' not allowed");
109             return false;
110         }
111 
112         // Pure functions should not change any state, and should be safe to eliminate if their
113         // result is not used; this is incompatible with out-parameters, so we forbid it here.
114         // (We don't exhaustively guard against pure functions changing global state in other ways,
115         // though, since they aren't allowed in user code.)
116         if (modifierFlags.isPure() && (param->modifierFlags() & ModifierFlag::kOut)) {
117             context.fErrors->error(param->modifiersPosition(),
118                                    "pure functions cannot have out parameters");
119             return false;
120         }
121     }
122     return true;
123 }
124 
type_is_valid_for_color(const Type & type)125 static bool type_is_valid_for_color(const Type& type) {
126     return type.isVector() && type.columns() == 4 && type.componentType().isFloat();
127 }
128 
type_is_valid_for_coords(const Type & type)129 static bool type_is_valid_for_coords(const Type& type) {
130     return type.isVector() && type.highPrecision() && type.columns() == 2 &&
131            type.componentType().isFloat();
132 }
133 
check_main_signature(const Context & context,Position pos,const Type & returnType,TArray<std::unique_ptr<Variable>> & parameters)134 static bool check_main_signature(const Context& context, Position pos, const Type& returnType,
135                                  TArray<std::unique_ptr<Variable>>& parameters) {
136     ErrorReporter& errors = *context.fErrors;
137     ProgramKind kind = context.fConfig->fKind;
138 
139     auto typeIsValidForAttributes = [](const Type& type) {
140         return type.isStruct() && type.name() == "Attributes";
141     };
142 
143     auto typeIsValidForVaryings = [](const Type& type) {
144         return type.isStruct() && type.name() == "Varyings";
145     };
146 
147     auto paramIsCoords = [&](int idx) {
148         const Variable& p = *parameters[idx];
149         return type_is_valid_for_coords(p.type()) && p.modifierFlags() == ModifierFlag::kNone;
150     };
151 
152     auto paramIsColor = [&](int idx) {
153         const Variable& p = *parameters[idx];
154         return type_is_valid_for_color(p.type()) && p.modifierFlags() == ModifierFlag::kNone;
155     };
156 
157     auto paramIsConstInAttributes = [&](int idx) {
158         const Variable& p = *parameters[idx];
159         return typeIsValidForAttributes(p.type()) && p.modifierFlags() == ModifierFlag::kConst;
160     };
161 
162     auto paramIsConstInVaryings = [&](int idx) {
163         const Variable& p = *parameters[idx];
164         return typeIsValidForVaryings(p.type()) && p.modifierFlags() == ModifierFlag::kConst;
165     };
166 
167     auto paramIsOutColor = [&](int idx) {
168         const Variable& p = *parameters[idx];
169         return type_is_valid_for_color(p.type()) && p.modifierFlags() == ModifierFlag::kOut;
170     };
171 
172     switch (kind) {
173         case ProgramKind::kRuntimeColorFilter:
174         case ProgramKind::kPrivateRuntimeColorFilter: {
175             // (half4|float4) main(half4|float4)
176             if (!type_is_valid_for_color(returnType)) {
177                 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
178                 return false;
179             }
180             bool validParams = (parameters.size() == 1 && paramIsColor(0));
181             if (!validParams) {
182                 errors.error(pos, "'main' parameter must be 'vec4', 'float4', or 'half4'");
183                 return false;
184             }
185             break;
186         }
187         case ProgramKind::kRuntimeShader:
188         case ProgramKind::kPrivateRuntimeShader: {
189             // (half4|float4) main(float2)
190             if (!type_is_valid_for_color(returnType)) {
191                 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
192                 return false;
193             }
194             if (!(parameters.size() == 1 && paramIsCoords(0))) {
195                 errors.error(pos, "'main' parameter must be 'float2' or 'vec2'");
196                 return false;
197             }
198             break;
199         }
200         case ProgramKind::kRuntimeBlender:
201         case ProgramKind::kPrivateRuntimeBlender: {
202             // (half4|float4) main(half4|float4, half4|float4)
203             if (!type_is_valid_for_color(returnType)) {
204                 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
205                 return false;
206             }
207             if (!(parameters.size() == 2 && paramIsColor(0) && paramIsColor(1))) {
208                 errors.error(pos, "'main' parameters must be (vec4|float4|half4, "
209                                   "vec4|float4|half4)");
210                 return false;
211             }
212             break;
213         }
214         case ProgramKind::kMeshVertex: {
215             // Varyings main(const Attributes)
216             if (!typeIsValidForVaryings(returnType)) {
217                 errors.error(pos, "'main' must return 'Varyings'.");
218                 return false;
219             }
220             if (!(parameters.size() == 1 && paramIsConstInAttributes(0))) {
221                 errors.error(pos, "'main' parameter must be 'const Attributes'.");
222                 return false;
223             }
224             break;
225         }
226         case ProgramKind::kMeshFragment: {
227             // float2 main(const Varyings) -or- float2 main(const Varyings, out half4|float4)
228             if (!type_is_valid_for_coords(returnType)) {
229                 errors.error(pos, "'main' must return: 'vec2' or 'float2'");
230                 return false;
231             }
232             if (!((parameters.size() == 1 && paramIsConstInVaryings(0)) ||
233                   (parameters.size() == 2 && paramIsConstInVaryings(0) && paramIsOutColor(1)))) {
234                 errors.error(pos,
235                              "'main' parameters must be (const Varyings, (out (half4|float4))?)");
236                 return false;
237             }
238             break;
239         }
240         case ProgramKind::kFragment:
241         case ProgramKind::kGraphiteFragment:
242         case ProgramKind::kGraphiteFragmentES2: {
243             bool validParams = (parameters.size() == 0) ||
244                                (parameters.size() == 1 && paramIsCoords(0));
245             if (!validParams) {
246                 errors.error(pos, "shader 'main' must be main() or main(float2)");
247                 return false;
248             }
249             break;
250         }
251         case ProgramKind::kVertex:
252         case ProgramKind::kGraphiteVertex:
253         case ProgramKind::kGraphiteVertexES2:
254         case ProgramKind::kCompute:
255             if (!returnType.matches(*context.fTypes.fVoid)) {
256                 errors.error(pos, "'main' must return 'void'");
257                 return false;
258             }
259             if (parameters.size()) {
260                 errors.error(pos, "shader 'main' must have zero parameters");
261                 return false;
262             }
263             break;
264     }
265     return true;
266 }
267 
268 /**
269  * Given a concrete type (`float3`) and a generic type (`$genType`), returns the index of the
270  * concrete type within the generic type's typelist. Returns -1 if there is no match.
271  */
find_generic_index(const Type & concreteType,const Type & genericType,bool allowNarrowing)272 static int find_generic_index(const Type& concreteType,
273                               const Type& genericType,
274                               bool allowNarrowing) {
275     SkSpan<const Type* const> genericTypes = genericType.coercibleTypes();
276     for (size_t index = 0; index < genericTypes.size(); ++index) {
277         if (concreteType.canCoerceTo(*genericTypes[index], allowNarrowing)) {
278             return index;
279         }
280     }
281     return -1;
282 }
283 
284 /** Returns true if the types match, or if `concreteType` can be found in `maybeGenericType`. */
type_generically_matches(const Type & concreteType,const Type & maybeGenericType)285 static bool type_generically_matches(const Type& concreteType, const Type& maybeGenericType) {
286     return maybeGenericType.isGeneric()
287                 ? find_generic_index(concreteType, maybeGenericType, /*allowNarrowing=*/false) != -1
288                 : concreteType.matches(maybeGenericType);
289 }
290 
291 /**
292  * Checks a parameter list (params) against the parameters of a function that was declared earlier
293  * (otherParams). Returns true if they match, even if the parameters in `otherParams` contain
294  * generic types.
295  */
parameters_match(SkSpan<const std::unique_ptr<Variable>> params,SkSpan<Variable * const> otherParams)296 static bool parameters_match(SkSpan<const std::unique_ptr<Variable>> params,
297                              SkSpan<Variable* const> otherParams) {
298     // If the param lists are different lengths, they're definitely not a match.
299     if (params.size() != otherParams.size()) {
300         return false;
301     }
302 
303     // Figure out a consistent generic index (or bail if we find a contradiction).
304     int genericIndex = -1;
305     for (size_t i = 0; i < params.size(); ++i) {
306         const Type* paramType = &params[i]->type();
307         const Type* otherParamType = &otherParams[i]->type();
308 
309         if (otherParamType->isGeneric()) {
310             int genericIndexForThisParam = find_generic_index(*paramType, *otherParamType,
311                                                               /*allowNarrowing=*/false);
312             if (genericIndexForThisParam == -1) {
313                 // The type wasn't a match for this generic at all; these params can't be a match.
314                 return false;
315             }
316             if (genericIndex != -1 && genericIndex != genericIndexForThisParam) {
317                 // The generic index mismatches from what we determined on a previous parameter.
318                 return false;
319             }
320             genericIndex = genericIndexForThisParam;
321         }
322     }
323 
324     // Now that we've determined a generic index (if we needed one), do a parameter check.
325     for (size_t i = 0; i < params.size(); i++) {
326         const Type* paramType = &params[i]->type();
327         const Type* otherParamType = &otherParams[i]->type();
328 
329         // Make generic types concrete.
330         if (otherParamType->isGeneric()) {
331             SkASSERT(genericIndex != -1);
332             SkASSERT(genericIndex < (int)otherParamType->coercibleTypes().size());
333             otherParamType = otherParamType->coercibleTypes()[genericIndex];
334         }
335         // Detect type mismatches.
336         if (!paramType->matches(*otherParamType)) {
337             return false;
338         }
339     }
340     return true;
341 }
342 
343 /**
344  * Checks for a previously existing declaration of this function, reporting errors if there is an
345  * incompatible symbol. Returns true and sets outExistingDecl to point to the existing declaration
346  * (or null if none) on success, returns false on error.
347  */
find_existing_declaration(const Context & context,Position pos,ModifierFlags modifierFlags,IntrinsicKind intrinsicKind,std::string_view name,TArray<std::unique_ptr<Variable>> & parameters,Position returnTypePos,const Type * returnType,FunctionDeclaration ** outExistingDecl)348 static bool find_existing_declaration(const Context& context,
349                                       Position pos,
350                                       ModifierFlags modifierFlags,
351                                       IntrinsicKind intrinsicKind,
352                                       std::string_view name,
353                                       TArray<std::unique_ptr<Variable>>& parameters,
354                                       Position returnTypePos,
355                                       const Type* returnType,
356                                       FunctionDeclaration** outExistingDecl) {
357     auto invalidDeclDescription = [&]() -> std::string {
358         TArray<Variable*> paramPtrs;
359         paramPtrs.reserve_exact(parameters.size());
360         for (std::unique_ptr<Variable>& param : parameters) {
361             paramPtrs.push_back(param.get());
362         }
363         return FunctionDeclaration(context,
364                                    pos,
365                                    modifierFlags,
366                                    name,
367                                    std::move(paramPtrs),
368                                    returnType,
369                                    intrinsicKind)
370                 .description();
371     };
372 
373     ErrorReporter& errors = *context.fErrors;
374     Symbol* entry = context.fSymbolTable->findMutable(name);
375     *outExistingDecl = nullptr;
376     if (entry) {
377         if (!entry->is<FunctionDeclaration>()) {
378             errors.error(pos, "symbol '" + std::string(name) + "' was already defined");
379             return false;
380         }
381         for (FunctionDeclaration* other = &entry->as<FunctionDeclaration>(); other;
382              other = other->mutableNextOverload()) {
383             SkASSERT(name == other->name());
384             if (!parameters_match(parameters, other->parameters())) {
385                 continue;
386             }
387             if (!type_generically_matches(*returnType, other->returnType())) {
388                 errors.error(returnTypePos, "functions '" + invalidDeclDescription() + "' and '" +
389                                             other->description() + "' differ only in return type");
390                 return false;
391             }
392             for (int i = 0; i < parameters.size(); i++) {
393                 if (parameters[i]->modifierFlags() != other->parameters()[i]->modifierFlags() ||
394                     parameters[i]->layout() != other->parameters()[i]->layout()) {
395                     errors.error(parameters[i]->fPosition,
396                                  "modifiers on parameter " + std::to_string(i + 1) +
397                                  " differ between declaration and definition");
398                     return false;
399                 }
400             }
401             if (other->isIntrinsic()) {
402                 errors.error(pos, "duplicate definition of intrinsic function '" +
403                                   std::string(name) + "'");
404                 return false;
405             }
406             if (modifierFlags != other->modifierFlags()) {
407                 errors.error(pos, "functions '" + invalidDeclDescription() + "' and '" +
408                                   other->description() + "' differ only in modifiers");
409                 return false;
410             }
411             *outExistingDecl = other;
412             break;
413         }
414         if (!*outExistingDecl && entry->as<FunctionDeclaration>().isMain()) {
415             errors.error(pos, "duplicate definition of 'main'");
416             return false;
417         }
418     }
419     return true;
420 }
421 
FunctionDeclaration(const Context & context,Position pos,ModifierFlags modifierFlags,std::string_view name,TArray<Variable * > parameters,const Type * returnType,IntrinsicKind intrinsicKind)422 FunctionDeclaration::FunctionDeclaration(const Context& context,
423                                          Position pos,
424                                          ModifierFlags modifierFlags,
425                                          std::string_view name,
426                                          TArray<Variable*> parameters,
427                                          const Type* returnType,
428                                          IntrinsicKind intrinsicKind)
429         : INHERITED(pos, kIRNodeKind, name, /*type=*/nullptr)
430         , fDefinition(nullptr)
431         , fParameters(std::move(parameters))
432         , fReturnType(returnType)
433         , fModifierFlags(modifierFlags)
434         , fIntrinsicKind(intrinsicKind)
435         , fModuleType(context.fConfig->fModuleType)
436         , fIsMain(name == "main") {
437     int builtinColorIndex = 0;
438     for (const Variable* param : fParameters) {
439         // None of the parameters are allowed to be be null.
440         SkASSERT(param);
441 
442         // Keep track of arguments to main for runtime effects.
443         if (fIsMain) {
444             if (ProgramConfig::IsRuntimeShader(context.fConfig->fKind) ||
445                 ProgramConfig::IsFragment(context.fConfig->fKind)) {
446                 // If this is a runtime shader, a float2 param is supposed to be the coords.
447                 // For testing purposes, we have .sksl inputs that are treated as both runtime
448                 // effects and fragment shaders. To make that work, fragment shaders are allowed to
449                 // have a coords parameter as well.
450                 if (type_is_valid_for_coords(param->type())) {
451                     fHasMainCoordsParameter = true;
452                 }
453             } else if (ProgramConfig::IsRuntimeColorFilter(context.fConfig->fKind) ||
454                        ProgramConfig::IsRuntimeBlender(context.fConfig->fKind)) {
455                 // If this is a runtime color filter or blender, the params are an input color,
456                 // followed by a destination color for blenders.
457                 if (type_is_valid_for_color(param->type())) {
458                     switch (builtinColorIndex++) {
459                         case 0:  fHasMainInputColorParameter = true; break;
460                         case 1:  fHasMainDestColorParameter = true;  break;
461                         default: /* unknown color parameter */       break;
462                     }
463                 }
464             }
465         }
466     }
467 }
468 
Convert(const Context & context,Position pos,const Modifiers & modifiers,std::string_view name,TArray<std::unique_ptr<Variable>> parameters,Position returnTypePos,const Type * returnType)469 FunctionDeclaration* FunctionDeclaration::Convert(const Context& context,
470                                                   Position pos,
471                                                   const Modifiers& modifiers,
472                                                   std::string_view name,
473                                                   TArray<std::unique_ptr<Variable>> parameters,
474                                                   Position returnTypePos,
475                                                   const Type* returnType) {
476     // No layout flag is permissible on a function.
477     modifiers.fLayout.checkPermittedLayout(context, pos,
478                                            /*permittedLayoutFlags=*/LayoutFlag::kNone);
479 
480     // If requested, apply the `noinline` modifier to every function. This allows us to test Runtime
481     // Effects without any inlining, even when the code is later added to a paint.
482     ModifierFlags modifierFlags = modifiers.fFlags;
483     if (context.fConfig->fSettings.fForceNoInline) {
484         modifierFlags &= ~ModifierFlag::kInline;
485         modifierFlags |= ModifierFlag::kNoInline;
486     }
487 
488     bool isMain = (name == "main");
489     IntrinsicKind intrinsicKind = context.fConfig->isBuiltinCode() ? FindIntrinsicKind(name)
490                                                                    : kNotIntrinsic;
491     FunctionDeclaration* decl = nullptr;
492     if (!check_modifiers(context, modifiers.fPosition, modifierFlags) ||
493         !check_return_type(context, returnTypePos, *returnType) ||
494         !check_parameters(context, parameters, modifierFlags, intrinsicKind) ||
495         (isMain && !check_main_signature(context, pos, *returnType, parameters)) ||
496         !find_existing_declaration(context, pos, modifierFlags, intrinsicKind, name, parameters,
497                                    returnTypePos, returnType, &decl)) {
498         return nullptr;
499     }
500     TArray<Variable*> finalParameters;
501     finalParameters.reserve_exact(parameters.size());
502     for (std::unique_ptr<Variable>& param : parameters) {
503         finalParameters.push_back(context.fSymbolTable->takeOwnershipOfSymbol(std::move(param)));
504     }
505     if (decl) {
506         return decl;
507     }
508     return context.fSymbolTable->add(
509             context,
510             std::make_unique<FunctionDeclaration>(context,
511                                                   pos,
512                                                   modifierFlags,
513                                                   name,
514                                                   std::move(finalParameters),
515                                                   returnType,
516                                                   intrinsicKind));
517 }
518 
mangledName() const519 std::string FunctionDeclaration::mangledName() const {
520     if ((this->isBuiltin() && !this->definition()) || this->isMain()) {
521         // Builtins without a definition (like `sin` or `sqrt`) must use their real names.
522         return std::string(this->name());
523     }
524     // Built-in functions can have a $ prefix, which will fail to compile in GLSL. Remove the
525     // $ and add a unique mangling specifier, so user code can't conflict with the name.
526     std::string_view name = this->name();
527     const char* builtinMarker = "";
528     if (skstd::starts_with(name, '$')) {
529         name.remove_prefix(1);
530         builtinMarker = "Q";  // a unique, otherwise-unused mangle character
531     }
532     // Rename function to `funcname_returntypeparamtypes`.
533     std::string result = std::string(name) + "_" + builtinMarker +
534                          this->returnType().abbreviatedName();
535     for (const Variable* p : this->parameters()) {
536         result += p->type().abbreviatedName();
537     }
538     return result;
539 }
540 
description() const541 std::string FunctionDeclaration::description() const {
542     std::string result = (fModifierFlags ? fModifierFlags.description() + " " : std::string()) +
543                          this->returnType().displayName() + " " + std::string(this->name()) + "(";
544     auto separator = SkSL::String::Separator();
545     for (const Variable* p : this->parameters()) {
546         result += separator();
547         result += p->description();
548     }
549     result += ")";
550     return result;
551 }
552 
matches(const FunctionDeclaration & f) const553 bool FunctionDeclaration::matches(const FunctionDeclaration& f) const {
554     if (this->name() != f.name()) {
555         return false;
556     }
557     SkSpan<Variable* const> parameters = this->parameters();
558     SkSpan<Variable* const> otherParameters = f.parameters();
559     if (parameters.size() != otherParameters.size()) {
560         return false;
561     }
562     for (size_t i = 0; i < parameters.size(); i++) {
563         if (!parameters[i]->type().matches(otherParameters[i]->type())) {
564             return false;
565         }
566     }
567     return true;
568 }
569 
determineFinalTypes(const ExpressionArray & arguments,ParamTypes * outParameterTypes,const Type ** outReturnType) const570 bool FunctionDeclaration::determineFinalTypes(const ExpressionArray& arguments,
571                                               ParamTypes* outParameterTypes,
572                                               const Type** outReturnType) const {
573     SkSpan<Variable* const> parameters = this->parameters();
574     SkASSERT(SkToSizeT(arguments.size()) == parameters.size());
575 
576     outParameterTypes->reserve_exact(arguments.size());
577     int genericIndex = -1;
578     for (int i = 0; i < arguments.size(); i++) {
579         // Non-generic parameters are final as-is.
580         const Type& parameterType = parameters[i]->type();
581         if (!parameterType.isGeneric()) {
582             outParameterTypes->push_back(&parameterType);
583             continue;
584         }
585         // We use the first generic parameter we find to lock in the generic index;
586         // e.g. if we find `float3` here, all `$genType`s will be assumed to be `float3`.
587         if (genericIndex == -1) {
588             genericIndex = find_generic_index(arguments[i]->type(), parameterType,
589                                               /*allowNarrowing=*/true);
590             if (genericIndex == -1) {
591                 // The passed-in type wasn't a match for ANY of the generic possibilities.
592                 // This function isn't a match at all.
593                 return false;
594             }
595         }
596         outParameterTypes->push_back(parameterType.coercibleTypes()[genericIndex]);
597     }
598     // Apply the generic index to our return type.
599     const Type& returnType = this->returnType();
600     if (returnType.isGeneric()) {
601         if (genericIndex == -1) {
602             // We don't support functions with a generic return type and no other generics.
603             return false;
604         }
605         *outReturnType = returnType.coercibleTypes()[genericIndex];
606     } else {
607         *outReturnType = &returnType;
608     }
609     return true;
610 }
611 
612 }  // namespace SkSL
613