Flutter Engine
The Flutter Engine
SkSLTernaryExpression.cpp
Go to the documentation of this file.
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
9
21
22namespace SkSL {
23
24std::unique_ptr<Expression> TernaryExpression::Convert(const Context& context,
26 std::unique_ptr<Expression> test,
27 std::unique_ptr<Expression> ifTrue,
28 std::unique_ptr<Expression> ifFalse) {
29 test = context.fTypes.fBool->coerceExpression(std::move(test), context);
30 if (!test || !ifTrue || !ifFalse) {
31 return nullptr;
32 }
33 if (ifTrue->type().componentType().isOpaque()) {
34 context.fErrors->error(pos, "ternary expression of opaque type '" +
35 ifTrue->type().displayName() + "' is not allowed");
36 return nullptr;
37 }
38 const Type* trueType;
39 const Type* falseType;
40 const Type* resultType;
41 Operator equalityOp(Operator::Kind::EQEQ);
42 if (!equalityOp.determineBinaryType(context, ifTrue->type(), ifFalse->type(),
43 &trueType, &falseType, &resultType) ||
44 !trueType->matches(*falseType)) {
45 Position errorPos = ifTrue->fPosition.rangeThrough(ifFalse->fPosition);
46 if (ifTrue->type().isVoid()) {
47 context.fErrors->error(errorPos, "ternary expression of type 'void' is not allowed");
48 } else {
49 context.fErrors->error(errorPos, "ternary operator result mismatch: '" +
50 ifTrue->type().displayName() + "', '" +
51 ifFalse->type().displayName() + "'");
52 }
53 return nullptr;
54 }
55 if (trueType->isOrContainsArray()) {
56 context.fErrors->error(pos, "ternary operator result may not be an array (or struct "
57 "containing an array)");
58 return nullptr;
59 }
60 ifTrue = trueType->coerceExpression(std::move(ifTrue), context);
61 if (!ifTrue) {
62 return nullptr;
63 }
64 ifFalse = falseType->coerceExpression(std::move(ifFalse), context);
65 if (!ifFalse) {
66 return nullptr;
67 }
68 return TernaryExpression::Make(context, pos, std::move(test), std::move(ifTrue),
69 std::move(ifFalse));
70}
71
72std::unique_ptr<Expression> TernaryExpression::Make(const Context& context,
74 std::unique_ptr<Expression> test,
75 std::unique_ptr<Expression> ifTrue,
76 std::unique_ptr<Expression> ifFalse) {
77 SkASSERT(ifTrue->type().matches(ifFalse->type()));
78 SkASSERT(!ifTrue->type().componentType().isOpaque());
79 SkASSERT(!context.fConfig->strictES2Mode() || !ifTrue->type().isOrContainsArray());
80
82 if (testExpr->isBoolLiteral()) {
83 // static boolean test, just return one of the branches
84 if (testExpr->as<Literal>().boolValue()) {
85 ifTrue->fPosition = pos;
86 return ifTrue;
87 } else {
88 ifFalse->fPosition = pos;
89 return ifFalse;
90 }
91 }
92
93 if (context.fConfig->fSettings.fOptimize) {
96
97 // A ternary with matching true- and false-cases does not need to branch.
98 if (Analysis::IsSameExpressionTree(*ifTrueExpr, *ifFalseExpr)) {
99 // If `test` has no side-effects, we can eliminate it too, and just return `ifTrue`.
101 ifTrue->fPosition = pos;
102 return ifTrue;
103 }
104 // Return a comma-expression containing `(test, ifTrue)`.
105 return BinaryExpression::Make(context, pos, std::move(test),
106 Operator::Kind::COMMA, std::move(ifTrue));
107 }
108
109 // A ternary of the form `test ? expr : false` can be simplified to `test && expr`.
110 if (ifFalseExpr->isBoolLiteral() && !ifFalseExpr->as<Literal>().boolValue()) {
111 return BinaryExpression::Make(context, pos, std::move(test),
112 Operator::Kind::LOGICALAND, std::move(ifTrue));
113 }
114
115 // A ternary of the form `test ? true : expr` can be simplified to `test || expr`.
116 if (ifTrueExpr->isBoolLiteral() && ifTrueExpr->as<Literal>().boolValue()) {
117 return BinaryExpression::Make(context, pos, std::move(test),
118 Operator::Kind::LOGICALOR, std::move(ifFalse));
119 }
120
121 // A ternary of the form `test ? false : true` can be simplified to `!test`.
122 if (ifTrueExpr->isBoolLiteral() && !ifTrueExpr->as<Literal>().boolValue() &&
123 ifFalseExpr->isBoolLiteral() && ifFalseExpr->as<Literal>().boolValue()) {
124 return PrefixExpression::Make(context, pos, Operator::Kind::LOGICALNOT,
125 std::move(test));
126 }
127
128 // A ternary of the form `test ? 1 : 0` can be simplified to `cast(test)`.
129 if (ifTrueExpr->is<Literal>() && ifTrueExpr->as<Literal>().value() == 1.0 &&
130 ifFalseExpr->is<Literal>() && ifFalseExpr->as<Literal>().value() == 0.0) {
131 return ConstructorScalarCast::Make(context, pos, ifTrue->type(), std::move(test));
132 }
133 }
134
135 return std::make_unique<TernaryExpression>(pos, std::move(test), std::move(ifTrue),
136 std::move(ifFalse));
137}
138
139std::string TernaryExpression::description(OperatorPrecedence parentPrecedence) const {
140 bool needsParens = (OperatorPrecedence::kTernary >= parentPrecedence);
141 return std::string(needsParens ? "(" : "") +
142 this->test()->description(OperatorPrecedence::kTernary) + " ? " +
143 this->ifTrue()->description(OperatorPrecedence::kTernary) + " : " +
144 this->ifFalse()->description(OperatorPrecedence::kTernary) +
145 std::string(needsParens ? ")" : "");
146}
147
148} // namespace SkSL
#define COMMA
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > left, Operator op, std::unique_ptr< Expression > right)
const std::unique_ptr< Type > fBool
static const Expression * GetConstantValueForVariable(const Expression &value)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
const BuiltinTypes & fTypes
Definition: SkSLContext.h:30
ErrorReporter * fErrors
Definition: SkSLContext.h:36
ProgramConfig * fConfig
Definition: SkSLContext.h:33
void error(Position position, std::string_view msg)
bool isBoolLiteral() const
std::string description() const final
bool is() const
Definition: SkSLIRNode.h:124
const T & as() const
Definition: SkSLIRNode.h:133
SKSL_INT boolValue() const
Definition: SkSLLiteral.h:102
double value() const
Definition: SkSLLiteral.h:107
bool determineBinaryType(const Context &context, const Type &left, const Type &right, const Type **outLeftType, const Type **outRightType, const Type **outResultType) const
static std::unique_ptr< Expression > Make(const Context &context, Position pos, Operator op, std::unique_ptr< Expression > base)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > test, std::unique_ptr< Expression > ifTrue, std::unique_ptr< Expression > ifFalse)
std::unique_ptr< Expression > & ifTrue()
std::unique_ptr< Expression > & test()
static std::unique_ptr< Expression > Convert(const Context &context, Position pos, std::unique_ptr< Expression > test, std::unique_ptr< Expression > ifTrue, std::unique_ptr< Expression > ifFalse)
std::unique_ptr< Expression > & ifFalse()
std::unique_ptr< Expression > coerceExpression(std::unique_ptr< Expression > expr, const Context &context) const
virtual bool isOrContainsArray() const
Definition: SkSLType.h:578
bool matches(const Type &other) const
Definition: SkSLType.h:269
bool IsSameExpressionTree(const Expression &left, const Expression &right)
bool HasSideEffects(const Expression &expr)
OperatorPrecedence
Definition: SkSLOperator.h:57
ProgramSettings fSettings