• 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 "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