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