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 14namespace SkSL { 15 16std::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 56std::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