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