xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLBinaryExpression.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/SkSLBinaryExpression.h"
9 
10 #include "src/sksl/SkSLAnalysis.h"
11 #include "src/sksl/SkSLConstantFolder.h"
12 #include "src/sksl/SkSLContext.h"
13 #include "src/sksl/SkSLErrorReporter.h"
14 #include "src/sksl/SkSLProgramSettings.h"
15 #include "src/sksl/ir/SkSLFieldAccess.h"
16 #include "src/sksl/ir/SkSLIndexExpression.h"
17 #include "src/sksl/ir/SkSLSwizzle.h"
18 #include "src/sksl/ir/SkSLTernaryExpression.h"
19 #include "src/sksl/ir/SkSLType.h"
20 #include "src/sksl/ir/SkSLVariableReference.h"
21 
22 namespace SkSL {
23 
Convert(const Context & context,Position pos,std::unique_ptr<Expression> left,Operator op,std::unique_ptr<Expression> right)24 std::unique_ptr<Expression> BinaryExpression::Convert(const Context& context,
25                                                       Position pos,
26                                                       std::unique_ptr<Expression> left,
27                                                       Operator op,
28                                                       std::unique_ptr<Expression> right) {
29     if (!left || !right) {
30         return nullptr;
31     }
32     const Type* rawLeftType = (left->isIntLiteral() && right->type().isInteger())
33             ? &right->type()
34             : &left->type();
35     const Type* rawRightType = (right->isIntLiteral() && left->type().isInteger())
36             ? &left->type()
37             : &right->type();
38 
39     bool isAssignment = op.isAssignment();
40     if (isAssignment &&
41         !Analysis::UpdateVariableRefKind(left.get(),
42                                          op.kind() != Operator::Kind::EQ
43                                                  ? VariableReference::RefKind::kReadWrite
44                                                  : VariableReference::RefKind::kWrite,
45                                          context.fErrors)) {
46         return nullptr;
47     }
48 
49     const Type* leftType;
50     const Type* rightType;
51     const Type* resultType;
52     if (!op.determineBinaryType(context, *rawLeftType, *rawRightType,
53                                 &leftType, &rightType, &resultType)) {
54         context.fErrors->error(pos, "type mismatch: '" + std::string(op.tightOperatorName()) +
55                                     "' cannot operate on '" + left->type().displayName() + "', '" +
56                                     right->type().displayName() + "'");
57         return nullptr;
58     }
59 
60     if (isAssignment && (leftType->componentType().isOpaque() || leftType->isOrContainsAtomic())) {
61         context.fErrors->error(pos, "assignments to opaque type '" + left->type().displayName() +
62                                     "' are not permitted");
63         return nullptr;
64     }
65     if (context.fConfig->strictES2Mode() && !op.isAllowedInStrictES2Mode()) {
66         context.fErrors->error(pos, "operator '" + std::string(op.tightOperatorName()) +
67                                     "' is not allowed");
68         return nullptr;
69     }
70     if (context.fConfig->strictES2Mode() || op.kind() == OperatorKind::COMMA) {
71         // Most operators are already rejected on arrays, but GLSL ES 1.0 is very explicit that the
72         // *only* operator allowed on arrays is subscripting (and the rules against assignment,
73         // comparison, and even sequence apply to structs containing arrays as well).
74         // WebGL2 also restricts the usage of the sequence operator with arrays (section 5.26,
75         // "Disallowed variants of GLSL ES 3.00 operators"). Since there is very little practical
76         // application for sequenced array expressions, we disallow it in SkSL.
77         const Expression* arrayExpr =  leftType->isOrContainsArray() ? left.get() :
78                                       rightType->isOrContainsArray() ? right.get() :
79                                                                        nullptr;
80         if (arrayExpr) {
81             context.fErrors->error(arrayExpr->position(),
82                                    "operator '" + std::string(op.tightOperatorName()) +
83                                    "' can not operate on arrays (or structs containing arrays)");
84             return nullptr;
85         }
86     }
87 
88     left = leftType->coerceExpression(std::move(left), context);
89     right = rightType->coerceExpression(std::move(right), context);
90     if (!left || !right) {
91         return nullptr;
92     }
93 
94     return BinaryExpression::Make(context, pos, std::move(left), op, std::move(right), resultType);
95 }
96 
Make(const Context & context,Position pos,std::unique_ptr<Expression> left,Operator op,std::unique_ptr<Expression> right)97 std::unique_ptr<Expression> BinaryExpression::Make(const Context& context,
98                                                    Position pos,
99                                                    std::unique_ptr<Expression> left,
100                                                    Operator op,
101                                                    std::unique_ptr<Expression> right) {
102     // Determine the result type of the binary expression.
103     const Type* leftType;
104     const Type* rightType;
105     const Type* resultType;
106     SkAssertResult(op.determineBinaryType(context, left->type(), right->type(),
107                                           &leftType, &rightType, &resultType));
108 
109     return BinaryExpression::Make(context, pos, std::move(left), op, std::move(right), resultType);
110 }
111 
Make(const Context & context,Position pos,std::unique_ptr<Expression> left,Operator op,std::unique_ptr<Expression> right,const Type * resultType)112 std::unique_ptr<Expression> BinaryExpression::Make(const Context& context,
113                                                    Position pos,
114                                                    std::unique_ptr<Expression> left,
115                                                    Operator op,
116                                                    std::unique_ptr<Expression> right,
117                                                    const Type* resultType) {
118     // We should have detected non-ES2 compliant behavior in Convert.
119     SkASSERT(!context.fConfig->strictES2Mode() || op.isAllowedInStrictES2Mode());
120     SkASSERT(!context.fConfig->strictES2Mode() || !left->type().isOrContainsArray());
121 
122     // We should have detected non-assignable assignment expressions in Convert.
123     SkASSERT(!op.isAssignment() || Analysis::IsAssignable(*left));
124     SkASSERT(!op.isAssignment() || !left->type().componentType().isOpaque());
125 
126     // For simple assignments, detect and report out-of-range literal values.
127     if (op.kind() == Operator::Kind::EQ) {
128         left->type().checkForOutOfRangeLiteral(context, *right);
129     }
130 
131     // Perform constant-folding on the expression.
132     if (std::unique_ptr<Expression> result = ConstantFolder::Simplify(context, pos, *left,
133                                                                       op, *right, *resultType)) {
134         return result;
135     }
136 
137     return std::make_unique<BinaryExpression>(pos, std::move(left), op,
138                                               std::move(right), resultType);
139 }
140 
CheckRef(const Expression & expr)141 bool BinaryExpression::CheckRef(const Expression& expr) {
142     switch (expr.kind()) {
143         case Expression::Kind::kFieldAccess:
144             return CheckRef(*expr.as<FieldAccess>().base());
145 
146         case Expression::Kind::kIndex:
147             return CheckRef(*expr.as<IndexExpression>().base());
148 
149         case Expression::Kind::kSwizzle:
150             return CheckRef(*expr.as<Swizzle>().base());
151 
152         case Expression::Kind::kTernary: {
153             const TernaryExpression& t = expr.as<TernaryExpression>();
154             return CheckRef(*t.ifTrue()) && CheckRef(*t.ifFalse());
155         }
156         case Expression::Kind::kVariableReference: {
157             const VariableReference& ref = expr.as<VariableReference>();
158             return ref.refKind() == VariableRefKind::kWrite ||
159                    ref.refKind() == VariableRefKind::kReadWrite;
160         }
161         default:
162             return false;
163     }
164 }
165 
clone(Position pos) const166 std::unique_ptr<Expression> BinaryExpression::clone(Position pos) const {
167     return std::make_unique<BinaryExpression>(pos,
168                                               this->left()->clone(),
169                                               this->getOperator(),
170                                               this->right()->clone(),
171                                               &this->type());
172 }
173 
description(OperatorPrecedence parentPrecedence) const174 std::string BinaryExpression::description(OperatorPrecedence parentPrecedence) const {
175     OperatorPrecedence operatorPrecedence = this->getOperator().getBinaryPrecedence();
176     bool needsParens = (operatorPrecedence >= parentPrecedence);
177     return std::string(needsParens ? "(" : "") +
178            this->left()->description(operatorPrecedence) +
179            this->getOperator().operatorName() +
180            this->right()->description(operatorPrecedence) +
181            std::string(needsParens ? ")" : "");
182 }
183 
isAssignmentIntoVariable()184 VariableReference* BinaryExpression::isAssignmentIntoVariable() {
185     if (this->getOperator().isAssignment()) {
186         Analysis::AssignmentInfo assignmentInfo;
187         if (Analysis::IsAssignable(*this->left(), &assignmentInfo, /*errors=*/nullptr)) {
188             return assignmentInfo.fAssignedVar;
189         }
190     }
191     return nullptr;
192 }
193 
194 }  // namespace SkSL
195