1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLTernaryExpression.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLBuiltinTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLConstantFolder.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLOperator.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBinaryExpression.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorScalarCast.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLiteral.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPrefixExpression.h"
21*c8dee2aaSAndroid Build Coastguard Worker
22*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
23*c8dee2aaSAndroid Build Coastguard Worker
Convert(const Context & context,Position pos,std::unique_ptr<Expression> test,std::unique_ptr<Expression> ifTrue,std::unique_ptr<Expression> ifFalse)24*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> TernaryExpression::Convert(const Context& context,
25*c8dee2aaSAndroid Build Coastguard Worker Position pos,
26*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> test,
27*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> ifTrue,
28*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> ifFalse) {
29*c8dee2aaSAndroid Build Coastguard Worker test = context.fTypes.fBool->coerceExpression(std::move(test), context);
30*c8dee2aaSAndroid Build Coastguard Worker if (!test || !ifTrue || !ifFalse) {
31*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker if (ifTrue->type().componentType().isOpaque()) {
34*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(pos, "ternary expression of opaque type '" +
35*c8dee2aaSAndroid Build Coastguard Worker ifTrue->type().displayName() + "' is not allowed");
36*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
37*c8dee2aaSAndroid Build Coastguard Worker }
38*c8dee2aaSAndroid Build Coastguard Worker const Type* trueType;
39*c8dee2aaSAndroid Build Coastguard Worker const Type* falseType;
40*c8dee2aaSAndroid Build Coastguard Worker const Type* resultType;
41*c8dee2aaSAndroid Build Coastguard Worker Operator equalityOp(Operator::Kind::EQEQ);
42*c8dee2aaSAndroid Build Coastguard Worker if (!equalityOp.determineBinaryType(context, ifTrue->type(), ifFalse->type(),
43*c8dee2aaSAndroid Build Coastguard Worker &trueType, &falseType, &resultType) ||
44*c8dee2aaSAndroid Build Coastguard Worker !trueType->matches(*falseType)) {
45*c8dee2aaSAndroid Build Coastguard Worker Position errorPos = ifTrue->fPosition.rangeThrough(ifFalse->fPosition);
46*c8dee2aaSAndroid Build Coastguard Worker if (ifTrue->type().isVoid()) {
47*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(errorPos, "ternary expression of type 'void' is not allowed");
48*c8dee2aaSAndroid Build Coastguard Worker } else {
49*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(errorPos, "ternary operator result mismatch: '" +
50*c8dee2aaSAndroid Build Coastguard Worker ifTrue->type().displayName() + "', '" +
51*c8dee2aaSAndroid Build Coastguard Worker ifFalse->type().displayName() + "'");
52*c8dee2aaSAndroid Build Coastguard Worker }
53*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
54*c8dee2aaSAndroid Build Coastguard Worker }
55*c8dee2aaSAndroid Build Coastguard Worker if (trueType->isOrContainsArray()) {
56*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(pos, "ternary operator result may not be an array (or struct "
57*c8dee2aaSAndroid Build Coastguard Worker "containing an array)");
58*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
59*c8dee2aaSAndroid Build Coastguard Worker }
60*c8dee2aaSAndroid Build Coastguard Worker ifTrue = trueType->coerceExpression(std::move(ifTrue), context);
61*c8dee2aaSAndroid Build Coastguard Worker if (!ifTrue) {
62*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
63*c8dee2aaSAndroid Build Coastguard Worker }
64*c8dee2aaSAndroid Build Coastguard Worker ifFalse = falseType->coerceExpression(std::move(ifFalse), context);
65*c8dee2aaSAndroid Build Coastguard Worker if (!ifFalse) {
66*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
67*c8dee2aaSAndroid Build Coastguard Worker }
68*c8dee2aaSAndroid Build Coastguard Worker return TernaryExpression::Make(context, pos, std::move(test), std::move(ifTrue),
69*c8dee2aaSAndroid Build Coastguard Worker std::move(ifFalse));
70*c8dee2aaSAndroid Build Coastguard Worker }
71*c8dee2aaSAndroid Build Coastguard Worker
Make(const Context & context,Position pos,std::unique_ptr<Expression> test,std::unique_ptr<Expression> ifTrue,std::unique_ptr<Expression> ifFalse)72*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> TernaryExpression::Make(const Context& context,
73*c8dee2aaSAndroid Build Coastguard Worker Position pos,
74*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> test,
75*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> ifTrue,
76*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> ifFalse) {
77*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(ifTrue->type().matches(ifFalse->type()));
78*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!ifTrue->type().componentType().isOpaque());
79*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!context.fConfig->strictES2Mode() || !ifTrue->type().isOrContainsArray());
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker const Expression* testExpr = ConstantFolder::GetConstantValueForVariable(*test);
82*c8dee2aaSAndroid Build Coastguard Worker if (testExpr->isBoolLiteral()) {
83*c8dee2aaSAndroid Build Coastguard Worker // static boolean test, just return one of the branches
84*c8dee2aaSAndroid Build Coastguard Worker if (testExpr->as<Literal>().boolValue()) {
85*c8dee2aaSAndroid Build Coastguard Worker ifTrue->fPosition = pos;
86*c8dee2aaSAndroid Build Coastguard Worker return ifTrue;
87*c8dee2aaSAndroid Build Coastguard Worker } else {
88*c8dee2aaSAndroid Build Coastguard Worker ifFalse->fPosition = pos;
89*c8dee2aaSAndroid Build Coastguard Worker return ifFalse;
90*c8dee2aaSAndroid Build Coastguard Worker }
91*c8dee2aaSAndroid Build Coastguard Worker }
92*c8dee2aaSAndroid Build Coastguard Worker
93*c8dee2aaSAndroid Build Coastguard Worker if (context.fConfig->fSettings.fOptimize) {
94*c8dee2aaSAndroid Build Coastguard Worker const Expression* ifTrueExpr = ConstantFolder::GetConstantValueForVariable(*ifTrue);
95*c8dee2aaSAndroid Build Coastguard Worker const Expression* ifFalseExpr = ConstantFolder::GetConstantValueForVariable(*ifFalse);
96*c8dee2aaSAndroid Build Coastguard Worker
97*c8dee2aaSAndroid Build Coastguard Worker // A ternary with matching true- and false-cases does not need to branch.
98*c8dee2aaSAndroid Build Coastguard Worker if (Analysis::IsSameExpressionTree(*ifTrueExpr, *ifFalseExpr)) {
99*c8dee2aaSAndroid Build Coastguard Worker // If `test` has no side-effects, we can eliminate it too, and just return `ifTrue`.
100*c8dee2aaSAndroid Build Coastguard Worker if (!Analysis::HasSideEffects(*test)) {
101*c8dee2aaSAndroid Build Coastguard Worker ifTrue->fPosition = pos;
102*c8dee2aaSAndroid Build Coastguard Worker return ifTrue;
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker // Return a comma-expression containing `(test, ifTrue)`.
105*c8dee2aaSAndroid Build Coastguard Worker return BinaryExpression::Make(context, pos, std::move(test),
106*c8dee2aaSAndroid Build Coastguard Worker Operator::Kind::COMMA, std::move(ifTrue));
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker // A ternary of the form `test ? expr : false` can be simplified to `test && expr`.
110*c8dee2aaSAndroid Build Coastguard Worker if (ifFalseExpr->isBoolLiteral() && !ifFalseExpr->as<Literal>().boolValue()) {
111*c8dee2aaSAndroid Build Coastguard Worker return BinaryExpression::Make(context, pos, std::move(test),
112*c8dee2aaSAndroid Build Coastguard Worker Operator::Kind::LOGICALAND, std::move(ifTrue));
113*c8dee2aaSAndroid Build Coastguard Worker }
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker // A ternary of the form `test ? true : expr` can be simplified to `test || expr`.
116*c8dee2aaSAndroid Build Coastguard Worker if (ifTrueExpr->isBoolLiteral() && ifTrueExpr->as<Literal>().boolValue()) {
117*c8dee2aaSAndroid Build Coastguard Worker return BinaryExpression::Make(context, pos, std::move(test),
118*c8dee2aaSAndroid Build Coastguard Worker Operator::Kind::LOGICALOR, std::move(ifFalse));
119*c8dee2aaSAndroid Build Coastguard Worker }
120*c8dee2aaSAndroid Build Coastguard Worker
121*c8dee2aaSAndroid Build Coastguard Worker // A ternary of the form `test ? false : true` can be simplified to `!test`.
122*c8dee2aaSAndroid Build Coastguard Worker if (ifTrueExpr->isBoolLiteral() && !ifTrueExpr->as<Literal>().boolValue() &&
123*c8dee2aaSAndroid Build Coastguard Worker ifFalseExpr->isBoolLiteral() && ifFalseExpr->as<Literal>().boolValue()) {
124*c8dee2aaSAndroid Build Coastguard Worker return PrefixExpression::Make(context, pos, Operator::Kind::LOGICALNOT,
125*c8dee2aaSAndroid Build Coastguard Worker std::move(test));
126*c8dee2aaSAndroid Build Coastguard Worker }
127*c8dee2aaSAndroid Build Coastguard Worker
128*c8dee2aaSAndroid Build Coastguard Worker // A ternary of the form `test ? 1 : 0` can be simplified to `cast(test)`.
129*c8dee2aaSAndroid Build Coastguard Worker if (ifTrueExpr->is<Literal>() && ifTrueExpr->as<Literal>().value() == 1.0 &&
130*c8dee2aaSAndroid Build Coastguard Worker ifFalseExpr->is<Literal>() && ifFalseExpr->as<Literal>().value() == 0.0) {
131*c8dee2aaSAndroid Build Coastguard Worker return ConstructorScalarCast::Make(context, pos, ifTrue->type(), std::move(test));
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<TernaryExpression>(pos, std::move(test), std::move(ifTrue),
136*c8dee2aaSAndroid Build Coastguard Worker std::move(ifFalse));
137*c8dee2aaSAndroid Build Coastguard Worker }
138*c8dee2aaSAndroid Build Coastguard Worker
description(OperatorPrecedence parentPrecedence) const139*c8dee2aaSAndroid Build Coastguard Worker std::string TernaryExpression::description(OperatorPrecedence parentPrecedence) const {
140*c8dee2aaSAndroid Build Coastguard Worker bool needsParens = (OperatorPrecedence::kTernary >= parentPrecedence);
141*c8dee2aaSAndroid Build Coastguard Worker return std::string(needsParens ? "(" : "") +
142*c8dee2aaSAndroid Build Coastguard Worker this->test()->description(OperatorPrecedence::kTernary) + " ? " +
143*c8dee2aaSAndroid Build Coastguard Worker this->ifTrue()->description(OperatorPrecedence::kTernary) + " : " +
144*c8dee2aaSAndroid Build Coastguard Worker this->ifFalse()->description(OperatorPrecedence::kTernary) +
145*c8dee2aaSAndroid Build Coastguard Worker std::string(needsParens ? ")" : "");
146*c8dee2aaSAndroid Build Coastguard Worker }
147*c8dee2aaSAndroid Build Coastguard Worker
148*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkSL
149