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 = ¶ms[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 = ¶ms[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(¶meterType);
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