xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLTernaryExpression.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "src/sksl/ir/SkSLTernaryExpression.h"
9 
10 #include "src/sksl/SkSLAnalysis.h"
11 #include "src/sksl/SkSLBuiltinTypes.h"
12 #include "src/sksl/SkSLConstantFolder.h"
13 #include "src/sksl/SkSLContext.h"
14 #include "src/sksl/SkSLErrorReporter.h"
15 #include "src/sksl/SkSLOperator.h"
16 #include "src/sksl/SkSLProgramSettings.h"
17 #include "src/sksl/ir/SkSLBinaryExpression.h"
18 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
19 #include "src/sksl/ir/SkSLLiteral.h"
20 #include "src/sksl/ir/SkSLPrefixExpression.h"
21 
22 namespace SkSL {
23 
Convert(const Context & context,Position pos,std::unique_ptr<Expression> test,std::unique_ptr<Expression> ifTrue,std::unique_ptr<Expression> ifFalse)24 std::unique_ptr<Expression> TernaryExpression::Convert(const Context& context,
25                                                        Position pos,
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 
Make(const Context & context,Position pos,std::unique_ptr<Expression> test,std::unique_ptr<Expression> ifTrue,std::unique_ptr<Expression> ifFalse)72 std::unique_ptr<Expression> TernaryExpression::Make(const Context& context,
73                                                     Position pos,
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 
81     const Expression* testExpr = ConstantFolder::GetConstantValueForVariable(*test);
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) {
94         const Expression* ifTrueExpr  = ConstantFolder::GetConstantValueForVariable(*ifTrue);
95         const Expression* ifFalseExpr = ConstantFolder::GetConstantValueForVariable(*ifFalse);
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`.
100             if (!Analysis::HasSideEffects(*test)) {
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 
description(OperatorPrecedence parentPrecedence) const139 std::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
149