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