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