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/SkSLConstantFolder.h"
10 #include "src/sksl/SkSLProgramSettings.h"
11 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
12 #include "src/sksl/ir/SkSLLiteral.h"
13
14 namespace SkSL {
15
Convert(const Context & context,int line,const Type & rawType,ExpressionArray args)16 std::unique_ptr<Expression> ConstructorScalarCast::Convert(const Context& context,
17 int line,
18 const Type& rawType,
19 ExpressionArray args) {
20 // As you might expect, scalar-cast constructors should only be created with scalar types.
21 const Type& type = rawType.scalarTypeForLiteral();
22 SkASSERT(type.isScalar());
23
24 if (args.size() != 1) {
25 context.fErrors->error(line, "invalid arguments to '" + type.displayName() +
26 "' constructor, (expected exactly 1 argument, but found " +
27 to_string((uint64_t)args.size()) + ")");
28 return nullptr;
29 }
30
31 const Type& argType = args[0]->type();
32 if (!argType.isScalar()) {
33 // Casting a vector-type into its scalar component type is treated as a slice in GLSL.
34 // We don't allow those casts in SkSL; recommend a .x swizzle instead.
35 const char* swizzleHint = "";
36 if (argType.componentType() == type) {
37 if (argType.isVector()) {
38 swizzleHint = "; use '.x' instead";
39 } else if (argType.isMatrix()) {
40 swizzleHint = "; use '[0][0]' instead";
41 }
42 }
43
44 context.fErrors->error(line,
45 "'" + argType.displayName() + "' is not a valid parameter to '" +
46 type.displayName() + "' constructor" + swizzleHint);
47 return nullptr;
48 }
49 if (type.checkForOutOfRangeLiteral(context, *args[0])) {
50 return nullptr;
51 }
52
53 return ConstructorScalarCast::Make(context, line, type, std::move(args[0]));
54 }
55
Make(const Context & context,int line,const Type & type,std::unique_ptr<Expression> arg)56 std::unique_ptr<Expression> ConstructorScalarCast::Make(const Context& context,
57 int line,
58 const Type& type,
59 std::unique_ptr<Expression> arg) {
60 SkASSERT(type.isScalar());
61 SkASSERT(type.isAllowedInES2(context));
62 SkASSERT(arg->type().isScalar());
63
64 // No cast required when the types match.
65 if (arg->type() == type) {
66 return arg;
67 }
68 // Look up the value of constant variables. This allows constant-expressions like `int(zero)` to
69 // be replaced with a literal zero.
70 arg = ConstantFolder::MakeConstantValueForVariable(std::move(arg));
71
72 // We can cast scalar literals at compile-time when possible. (If the resulting literal would be
73 // out of range for its type, we report an error and return the constructor. This can occur when
74 // code is inlined, so we can't necessarily catch it during Convert. As such, it's not safe to
75 // return null or assert.)
76 if (arg->is<Literal>() &&
77 !type.checkForOutOfRangeLiteral(context, arg->as<Literal>().value(), arg->fLine)) {
78 return Literal::Make(line, arg->as<Literal>().value(), &type);
79 }
80 return std::make_unique<ConstructorScalarCast>(line, type, std::move(arg));
81 }
82
83 } // namespace SkSL
84