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/SkSLConstructorArray.h"
9
10 #include "include/core/SkTypes.h"
11 #include "src/sksl/SkSLContext.h"
12 #include "src/sksl/SkSLErrorReporter.h"
13 #include "src/sksl/SkSLProgramSettings.h"
14 #include "src/sksl/SkSLString.h"
15 #include "src/sksl/ir/SkSLConstructorArrayCast.h"
16 #include "src/sksl/ir/SkSLType.h"
17
18 #include <algorithm>
19 #include <string>
20
21 namespace SkSL {
22
Convert(const Context & context,Position pos,const Type & type,ExpressionArray args)23 std::unique_ptr<Expression> ConstructorArray::Convert(const Context& context,
24 Position pos,
25 const Type& type,
26 ExpressionArray args) {
27 SkASSERTF(type.isArray() && type.columns() > 0, "%s", type.description().c_str());
28
29 // ES2 doesn't support first-class array types.
30 if (context.fConfig->strictES2Mode()) {
31 context.fErrors->error(pos, "construction of array type '" + type.displayName() +
32 "' is not supported");
33 return nullptr;
34 }
35
36 // An array of atomics cannot be constructed.
37 if (type.isOrContainsAtomic()) {
38 context.fErrors->error(
39 pos,
40 String::printf("construction of array type '%s' with atomic member is not allowed",
41 type.displayName().c_str()));
42 return nullptr;
43 }
44
45 // If there is a single argument containing an array of matching size and the types are
46 // coercible, this is actually a cast. i.e., `half[10](myFloat10Array)`. This isn't a GLSL
47 // feature, but the Pipeline stage code generator needs this functionality so that code which
48 // was originally compiled with "allow narrowing conversions" enabled can be later recompiled
49 // without narrowing conversions (we patch over these conversions with an explicit cast).
50 if (args.size() == 1) {
51 const Expression& expr = *args.front();
52 const Type& exprType = expr.type();
53
54 if (exprType.isArray() && exprType.canCoerceTo(type, /*allowNarrowing=*/true)) {
55 return ConstructorArrayCast::Make(context, pos, type, std::move(args.front()));
56 }
57 }
58
59 // Check that the number of constructor arguments matches the array size.
60 if (type.columns() != args.size()) {
61 context.fErrors->error(pos, String::printf("invalid arguments to '%s' constructor "
62 "(expected %d elements, but found %d)", type.displayName().c_str(), type.columns(),
63 args.size()));
64 return nullptr;
65 }
66
67 // Convert each constructor argument to the array's component type.
68 const Type& baseType = type.componentType();
69 for (std::unique_ptr<Expression>& argument : args) {
70 argument = baseType.coerceExpression(std::move(argument), context);
71 if (!argument) {
72 return nullptr;
73 }
74 }
75
76 return ConstructorArray::Make(context, pos, type, std::move(args));
77 }
78
Make(const Context & context,Position pos,const Type & type,ExpressionArray args)79 std::unique_ptr<Expression> ConstructorArray::Make(const Context& context,
80 Position pos,
81 const Type& type,
82 ExpressionArray args) {
83 SkASSERT(!context.fConfig->strictES2Mode());
84 SkASSERT(type.isAllowedInES2(context));
85 SkASSERT(type.columns() == args.size());
86 SkASSERT(!type.isOrContainsAtomic());
87 SkASSERT(std::all_of(args.begin(), args.end(), [&](const std::unique_ptr<Expression>& arg) {
88 return type.componentType().matches(arg->type());
89 }));
90
91 return std::make_unique<ConstructorArray>(pos, type, std::move(args));
92 }
93
94 } // namespace SkSL
95