| /* |
| * Copyright 2021 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/sksl/ir/SkSLTernaryExpression.h" |
| |
| #include "include/sksl/SkSLErrorReporter.h" |
| #include "include/sksl/SkSLOperator.h" |
| #include "src/sksl/SkSLAnalysis.h" |
| #include "src/sksl/SkSLBuiltinTypes.h" |
| #include "src/sksl/SkSLConstantFolder.h" |
| #include "src/sksl/SkSLContext.h" |
| #include "src/sksl/SkSLProgramSettings.h" |
| #include "src/sksl/ir/SkSLBinaryExpression.h" |
| #include "src/sksl/ir/SkSLLiteral.h" |
| |
| namespace SkSL { |
| |
| std::unique_ptr<Expression> TernaryExpression::Convert(const Context& context, |
| Position pos, |
| std::unique_ptr<Expression> test, |
| std::unique_ptr<Expression> ifTrue, |
| std::unique_ptr<Expression> ifFalse) { |
| test = context.fTypes.fBool->coerceExpression(std::move(test), context); |
| if (!test || !ifTrue || !ifFalse) { |
| return nullptr; |
| } |
| if (ifTrue->type().componentType().isOpaque()) { |
| context.fErrors->error(pos, "ternary expression of opaque type '" + |
| ifTrue->type().displayName() + "' not allowed"); |
| return nullptr; |
| } |
| const Type* trueType; |
| const Type* falseType; |
| const Type* resultType; |
| Operator equalityOp(Operator::Kind::EQEQ); |
| if (!equalityOp.determineBinaryType(context, ifTrue->type(), ifFalse->type(), |
| &trueType, &falseType, &resultType) || |
| !trueType->matches(*falseType)) { |
| context.fErrors->error(ifTrue->fPosition.rangeThrough(ifFalse->fPosition), |
| "ternary operator result mismatch: '" + ifTrue->type().displayName() + "', '" + |
| ifFalse->type().displayName() + "'"); |
| return nullptr; |
| } |
| if (context.fConfig->strictES2Mode() && trueType->isOrContainsArray()) { |
| context.fErrors->error(pos, "ternary operator result may not be an array (or struct " |
| "containing an array)"); |
| return nullptr; |
| } |
| ifTrue = trueType->coerceExpression(std::move(ifTrue), context); |
| if (!ifTrue) { |
| return nullptr; |
| } |
| ifFalse = falseType->coerceExpression(std::move(ifFalse), context); |
| if (!ifFalse) { |
| return nullptr; |
| } |
| return TernaryExpression::Make(context, pos, std::move(test), std::move(ifTrue), |
| std::move(ifFalse)); |
| } |
| |
| std::unique_ptr<Expression> TernaryExpression::Make(const Context& context, |
| Position pos, |
| std::unique_ptr<Expression> test, |
| std::unique_ptr<Expression> ifTrue, |
| std::unique_ptr<Expression> ifFalse) { |
| SkASSERT(ifTrue->type().matches(ifFalse->type())); |
| SkASSERT(!ifTrue->type().componentType().isOpaque()); |
| SkASSERT(!context.fConfig->strictES2Mode() || !ifTrue->type().isOrContainsArray()); |
| |
| const Expression* testExpr = ConstantFolder::GetConstantValueForVariable(*test); |
| if (testExpr->isBoolLiteral()) { |
| // static boolean test, just return one of the branches |
| if (testExpr->as<Literal>().boolValue()) { |
| ifTrue->fPosition = pos; |
| return ifTrue; |
| } else { |
| ifFalse->fPosition = pos; |
| return ifFalse; |
| } |
| } |
| |
| // A ternary with matching true- and false-cases does not need to branch. |
| if (context.fConfig->fSettings.fOptimize) { |
| const Expression* ifTrueExpr = ConstantFolder::GetConstantValueForVariable(*ifTrue); |
| const Expression* ifFalseExpr = ConstantFolder::GetConstantValueForVariable(*ifFalse); |
| |
| if (Analysis::IsSameExpressionTree(*ifTrueExpr, *ifFalseExpr)) { |
| // If `test` has no side-effects, we can eliminate it too, and just return `ifTrue`. |
| if (!Analysis::HasSideEffects(*test)) { |
| ifTrue->fPosition = pos; |
| return ifTrue; |
| } |
| // Return a comma-expression containing `(test, ifTrue)`. |
| return BinaryExpression::Make(context, pos, std::move(test), |
| Operator::Kind::COMMA, std::move(ifTrue)); |
| } |
| } |
| |
| return std::make_unique<TernaryExpression>(pos, std::move(test), std::move(ifTrue), |
| std::move(ifFalse)); |
| } |
| |
| std::string TernaryExpression::description(OperatorPrecedence parentPrecedence) const { |
| bool needsParens = (OperatorPrecedence::kTernary >= parentPrecedence); |
| return std::string(needsParens ? "(" : "") + |
| this->test()->description(OperatorPrecedence::kTernary) + " ? " + |
| this->ifTrue()->description(OperatorPrecedence::kTernary) + " : " + |
| this->ifFalse()->description(OperatorPrecedence::kTernary) + |
| std::string(needsParens ? ")" : ""); |
| } |
| |
| } // namespace SkSL |