1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "arithmetic.h"
17
18namespace ark::es2panda::checker {
19
20static inline void RepairTypeErrorsInOperands(Type **left, Type **right)
21{
22    if (IsTypeError(*left)) {
23        *left = *right;
24    }
25    if (IsTypeError(*right)) {
26        *right = *left;
27    }
28}
29
30static inline void RepairTypeErrorWithDefault(Type **type, Type *dflt)
31{
32    if (IsTypeError(*type)) {
33        *type = dflt;
34    }
35}
36
37Type *ETSChecker::NegateNumericType(Type *type, ir::Expression *node)
38{
39    ASSERT(type->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
40
41    TypeFlag typeKind = ETSType(type);
42    Type *result = nullptr;
43
44    switch (typeKind) {
45        case TypeFlag::BYTE: {
46            result = CreateByteType(-(type->AsByteType()->GetValue()));
47            break;
48        }
49        case TypeFlag::CHAR: {
50            result = CreateCharType(-(type->AsCharType()->GetValue()));
51            break;
52        }
53        case TypeFlag::SHORT: {
54            result = CreateShortType(-(type->AsShortType()->GetValue()));
55            break;
56        }
57        case TypeFlag::INT: {
58            result = CreateIntType(-(type->AsIntType()->GetValue()));
59            break;
60        }
61        case TypeFlag::LONG: {
62            result = CreateLongType(-(type->AsLongType()->GetValue()));
63            break;
64        }
65        case TypeFlag::FLOAT: {
66            result = CreateFloatType(-(type->AsFloatType()->GetValue()));
67            break;
68        }
69        case TypeFlag::DOUBLE: {
70            result = CreateDoubleType(-(type->AsDoubleType()->GetValue()));
71            break;
72        }
73        default: {
74            UNREACHABLE();
75        }
76    }
77
78    node->SetTsType(result);
79    return result;
80}
81
82Type *ETSChecker::BitwiseNegateNumericType(Type *type, ir::Expression *node)
83{
84    ASSERT(type->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_INTEGRAL));
85
86    TypeFlag typeKind = ETSType(type);
87
88    Type *result = nullptr;
89
90    switch (typeKind) {
91        case TypeFlag::BYTE: {
92            result = CreateByteType(static_cast<int8_t>(~static_cast<uint8_t>(type->AsByteType()->GetValue())));
93            break;
94        }
95        case TypeFlag::CHAR: {
96            result = CreateCharType(~(type->AsCharType()->GetValue()));
97            break;
98        }
99        case TypeFlag::SHORT: {
100            result = CreateShortType(static_cast<int16_t>(~static_cast<uint16_t>(type->AsShortType()->GetValue())));
101            break;
102        }
103        case TypeFlag::INT: {
104            result = CreateIntType(static_cast<int32_t>(~static_cast<uint32_t>(type->AsIntType()->GetValue())));
105            break;
106        }
107        case TypeFlag::LONG: {
108            result = CreateLongType(static_cast<int64_t>(~static_cast<uint64_t>(type->AsLongType()->GetValue())));
109            break;
110        }
111        case TypeFlag::FLOAT: {
112            result = CreateIntType(
113                ~static_cast<uint32_t>(CastFloatToInt<FloatType::UType, int32_t>(type->AsFloatType()->GetValue())));
114            break;
115        }
116        case TypeFlag::DOUBLE: {
117            result = CreateLongType(
118                ~static_cast<uint64_t>(CastFloatToInt<DoubleType::UType, int64_t>(type->AsDoubleType()->GetValue())));
119            break;
120        }
121        default: {
122            UNREACHABLE();
123        }
124    }
125
126    node->SetTsType(result);
127    return result;
128}
129
130Type *ETSChecker::HandleRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
131{
132    ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
133           right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
134
135    if (left->IsDoubleType() || right->IsDoubleType()) {
136        return PerformRelationOperationOnTypes<DoubleType>(left, right, operationType);
137    }
138
139    if (left->IsFloatType() || right->IsFloatType()) {
140        return PerformRelationOperationOnTypes<FloatType>(left, right, operationType);
141    }
142
143    if (left->IsLongType() || right->IsLongType()) {
144        return PerformRelationOperationOnTypes<LongType>(left, right, operationType);
145    }
146
147    return PerformRelationOperationOnTypes<IntType>(left, right, operationType);
148}
149
150bool ETSChecker::CheckBinaryOperatorForBigInt(Type *left, Type *right, lexer::TokenType op)
151{
152    if ((left == nullptr) || (right == nullptr)) {
153        return false;
154    }
155
156    if (!left->IsETSBigIntType()) {
157        return false;
158    }
159
160    if (!right->IsETSBigIntType()) {
161        return false;
162    }
163
164    switch (op) {
165        case lexer::TokenType::PUNCTUATOR_EQUAL:
166        case lexer::TokenType::PUNCTUATOR_NOT_EQUAL:
167        case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
168        case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL:
169        case lexer::TokenType::KEYW_INSTANCEOF:
170            // This is handled in the main CheckBinaryOperator function
171            return false;
172        default:
173            break;
174    }
175
176    return true;
177}
178
179bool ETSChecker::CheckBinaryPlusMultDivOperandsForUnionType(const Type *leftType, const Type *rightType,
180                                                            const ir::Expression *left, const ir::Expression *right)
181{
182    std::stringstream ss;
183    if (leftType->IsETSUnionType()) {
184        leftType->AsETSUnionType()->ToString(ss, false);
185        LogTypeError("Bad operand type: multiple types left in the normalized union type (" + ss.str() +
186                         "). Unions are not allowed in binary expressions except equality.",
187                     left->Start());
188        return false;
189    }
190    if (rightType->IsETSUnionType()) {
191        rightType->AsETSUnionType()->ToString(ss, false);
192        LogTypeError("Bad operand type: multiple types left in the normalized union type (" + ss.str() +
193                         "). Unions are not allowed in binary expressions except equality.",
194                     right->Start());
195        return false;
196    }
197    return true;
198}
199
200checker::Type *ETSChecker::CheckBinaryOperatorMulDivMod(
201    std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp,
202    std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
203{
204    auto [left, right, operationType, pos] = op;
205    auto [leftType, rightType, unboxedL, unboxedR] = types;
206
207    // Try to handle errors on a lower level
208    RepairTypeErrorsInOperands(&leftType, &rightType);
209    RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
210    if (leftType->IsTypeError()) {  // both are errors
211        return GlobalTypeError();
212    }
213
214    checker::Type *tsType {};
215    auto [promotedType, bothConst] =
216        ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC, !isEqualOp);
217
218    FlagExpressionWithUnboxing(leftType, unboxedL, left);
219    FlagExpressionWithUnboxing(rightType, unboxedR, right);
220
221    if (!CheckBinaryPlusMultDivOperandsForUnionType(leftType, rightType, left, right)) {
222        return GlobalTypeError();
223    }
224
225    if (promotedType == nullptr && !bothConst) {
226        LogTypeError("Bad operand type, the types of the operands must be numeric type.", pos);
227        return GlobalTypeError();
228    }
229
230    if (bothConst) {
231        tsType = HandleArithmeticOperationOnTypes(leftType, rightType, operationType);
232    }
233
234    tsType = (tsType != nullptr) ? tsType : promotedType;
235    return tsType;
236}
237
238checker::Type *ETSChecker::CheckBinaryOperatorPlusForEnums(const checker::Type *const leftType,
239                                                           const checker::Type *const rightType)
240{
241    if (leftType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
242        rightType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
243        if (leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) {
244            return GlobalIntType();
245        }
246        if (leftType->IsFloatType() || rightType->IsFloatType()) {
247            return GlobalFloatType();
248        }
249        if (leftType->IsLongType() || rightType->IsLongType()) {
250            return GlobalLongType();
251        }
252        return GlobalIntType();
253    }
254    if ((leftType->IsETSStringEnumType() && (rightType->IsETSStringType() || rightType->IsETSStringEnumType())) ||
255        (rightType->IsETSStringEnumType() && (leftType->IsETSStringType() || leftType->IsETSStringEnumType()))) {
256        return GlobalETSStringLiteralType();
257    }
258    return nullptr;
259}
260
261checker::Type *ETSChecker::CheckBinaryOperatorPlus(
262    std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp,
263    std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
264{
265    auto [left, right, operationType, pos] = op;
266    auto [leftType, rightType, unboxedL, unboxedR] = types;
267
268    // Try to handle errors on a lower level
269    RepairTypeErrorsInOperands(&leftType, &rightType);
270    RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
271    if (leftType->IsTypeError()) {  // both are errors
272        return GlobalTypeError();
273    }
274
275    if (leftType->IsETSStringType() || rightType->IsETSStringType()) {
276        if (operationType == lexer::TokenType::PUNCTUATOR_MINUS ||
277            operationType == lexer::TokenType::PUNCTUATOR_MINUS_EQUAL) {
278            LogTypeError("Bad operand type, the types of the operands must be numeric type.", pos);
279            return GlobalTypeError();
280        }
281
282        return HandleStringConcatenation(leftType, rightType);
283    }
284
285    if (!CheckBinaryPlusMultDivOperandsForUnionType(leftType, rightType, left, right)) {
286        return GlobalTypeError();
287    }
288
289    auto [promotedType, bothConst] =
290        ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC, !isEqualOp);
291
292    FlagExpressionWithUnboxing(leftType, unboxedL, left);
293    FlagExpressionWithUnboxing(rightType, unboxedR, right);
294
295    if (promotedType == nullptr && !bothConst) {
296        auto type = CheckBinaryOperatorPlusForEnums(leftType, rightType);
297        if (type != nullptr) {
298            return type;
299        }
300        LogTypeError("Bad operand type, the types of the operands must be numeric type, enum or String.", pos);
301        return GlobalTypeError();
302    }
303
304    if (bothConst) {
305        return HandleArithmeticOperationOnTypes(leftType, rightType, operationType);
306    }
307
308    return promotedType;
309}
310
311checker::Type *ETSChecker::CheckBinaryOperatorShift(
312    std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp,
313    std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
314{
315    auto [left, right, operationType, pos] = op;
316    auto [leftType, rightType, unboxedL, unboxedR] = types;
317
318    RepairTypeErrorWithDefault(&leftType, GlobalIntType());
319    RepairTypeErrorWithDefault(&rightType, GlobalIntType());
320    RepairTypeErrorWithDefault(&unboxedL, GlobalIntType());
321    RepairTypeErrorWithDefault(&unboxedR, GlobalIntType());
322
323    if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
324        LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
325        return GlobalTypeError();
326    }
327
328    auto promotedLeftType = ApplyUnaryOperatorPromotion(unboxedL, false, !isEqualOp);
329    auto promotedRightType = ApplyUnaryOperatorPromotion(unboxedR, false, !isEqualOp);
330
331    FlagExpressionWithUnboxing(leftType, unboxedL, left);
332    FlagExpressionWithUnboxing(rightType, unboxedR, right);
333
334    if (promotedLeftType == nullptr || !promotedLeftType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) ||
335        promotedRightType == nullptr ||
336        !promotedRightType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
337        LogTypeError("Bad operand type, the types of the operands must be numeric type.", pos);
338        return GlobalTypeError();
339    }
340
341    if (promotedLeftType->HasTypeFlag(TypeFlag::CONSTANT) && promotedRightType->HasTypeFlag(TypeFlag::CONSTANT)) {
342        return HandleBitwiseOperationOnTypes(promotedLeftType, promotedRightType, operationType);
343    }
344
345    switch (ETSType(promotedLeftType)) {
346        case TypeFlag::BYTE: {
347            return GlobalByteType();
348        }
349        case TypeFlag::SHORT: {
350            return GlobalShortType();
351        }
352        case TypeFlag::CHAR: {
353            return GlobalCharType();
354        }
355        case TypeFlag::INT:
356        case TypeFlag::FLOAT: {
357            return GlobalIntType();
358        }
359        case TypeFlag::LONG:
360        case TypeFlag::DOUBLE: {
361            return GlobalLongType();
362        }
363        default: {
364            UNREACHABLE();
365        }
366    }
367    return nullptr;
368}
369
370checker::Type *ETSChecker::CheckBinaryOperatorBitwise(
371    std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp,
372    std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
373{
374    auto [left, right, operationType, pos] = op;
375    auto [leftType, rightType, unboxedL, unboxedR] = types;
376
377    RepairTypeErrorsInOperands(&leftType, &rightType);
378    RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
379    if (leftType->IsTypeError()) {  // both are errors
380        return GlobalTypeError();
381    }
382
383    if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
384        LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
385        return GlobalTypeError();
386    }
387
388    if (unboxedL != nullptr && unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) && unboxedR != nullptr &&
389        unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) {
390        FlagExpressionWithUnboxing(leftType, unboxedL, left);
391        FlagExpressionWithUnboxing(rightType, unboxedR, right);
392        return HandleBooleanLogicalOperators(unboxedL, unboxedR, operationType);
393    }
394
395    auto [promotedType, bothConst] =
396        ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC, !isEqualOp);
397
398    FlagExpressionWithUnboxing(leftType, unboxedL, left);
399    FlagExpressionWithUnboxing(rightType, unboxedR, right);
400
401    if (promotedType == nullptr && !bothConst) {
402        LogTypeError("Bad operand type, the types of the operands must be numeric type.", pos);
403        return GlobalTypeError();
404    }
405
406    if (bothConst) {
407        return HandleBitwiseOperationOnTypes(leftType, rightType, operationType);
408    }
409
410    return SelectGlobalIntegerTypeForNumeric(promotedType);
411}
412
413checker::Type *ETSChecker::CheckBinaryOperatorLogical(ir::Expression *left, ir::Expression *right, ir::Expression *expr,
414                                                      lexer::SourcePosition pos, checker::Type *leftType,
415                                                      checker::Type *rightType, Type *unboxedL, Type *unboxedR)
416{
417    RepairTypeErrorsInOperands(&leftType, &rightType);
418    RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
419    if (leftType->IsTypeError()) {  // both are errors
420        return GlobalTypeError();
421    }
422
423    if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
424        LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
425        return GlobalTypeError();
426    }
427
428    if (unboxedL == nullptr || !unboxedL->IsConditionalExprType() || unboxedR == nullptr ||
429        !unboxedR->IsConditionalExprType()) {
430        LogTypeError("Bad operand type, the types of the operands must be of possible condition type.", pos);
431        return GlobalTypeError();
432    }
433
434    if (unboxedL->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
435        FlagExpressionWithUnboxing(leftType, unboxedL, left);
436    }
437
438    if (unboxedR->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
439        FlagExpressionWithUnboxing(rightType, unboxedR, right);
440    }
441
442    if (expr->IsBinaryExpression()) {
443        return HandleBooleanLogicalOperatorsExtended(unboxedL, unboxedR, expr->AsBinaryExpression());
444    }
445
446    UNREACHABLE();
447}
448
449void ETSChecker::LogOperatorCannotBeApplied(lexer::TokenType operationType, checker::Type *const leftType,
450                                            checker::Type *const rightType, lexer::SourcePosition pos)
451{
452    LogTypeError({"Operator '", operationType, "' cannot be applied to types '", leftType, "' and '", rightType, "'."},
453                 pos);
454}
455
456bool ETSChecker::CheckValidEqualReferenceType(checker::Type *const leftType, checker::Type *const rightType)
457{
458    auto isGlobalObjectType {[](checker::Type *const type) -> bool {
459        return type->IsETSObjectType() && type->AsETSObjectType()->IsGlobalETSObjectType();
460    }};
461
462    // Equality expression is always allowed for Object, undefined and null
463    if (isGlobalObjectType(leftType) || isGlobalObjectType(rightType) || leftType->IsETSUndefinedType() ||
464        rightType->IsETSUndefinedType() || leftType->IsETSNullType() || rightType->IsETSNullType()) {
465        return true;
466    }
467
468    // NOTE (mxlgv): Skip for unions. Required implementation of the specification section:
469    // 7.25.6 Reference Equality Based on Actual Type (Union Equality Operators)
470    if (leftType->IsETSUnionType()) {
471        return leftType->AsETSUnionType()->IsOverlapWith(Relation(), rightType);
472    }
473    if (rightType->IsETSUnionType()) {
474        return rightType->AsETSUnionType()->IsOverlapWith(Relation(), leftType);
475    }
476
477    // NOTE (mxlgv): Skip for generic. Required implementation of the specification section:
478    // 7.25.6 Reference Equality Based on Actual Type (Type Parameter Equality Operators)
479    if (leftType->HasTypeFlag(TypeFlag::GENERIC) || rightType->HasTypeFlag(TypeFlag::GENERIC)) {
480        return true;
481    }
482
483    // Equality expression can only be applied to String and String, and BigInt and BigInt
484    if (leftType->IsETSStringType() || rightType->IsETSStringType() || leftType->IsETSBigIntType() ||
485        rightType->IsETSBigIntType()) {
486        auto *const nonConstLhs = GetNonConstantType(leftType);
487        auto *const nonConstRhs = GetNonConstantType(rightType);
488        if (!Relation()->IsIdenticalTo(nonConstLhs, nonConstRhs) &&
489            !Relation()->IsIdenticalTo(nonConstRhs, nonConstLhs)) {
490            return false;
491        }
492    }
493
494    return true;
495}
496
497std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorStrictEqual(ir::Expression *left,
498                                                                      lexer::TokenType operationType,
499                                                                      lexer::SourcePosition pos,
500                                                                      checker::Type *leftType, checker::Type *rightType)
501{
502    RepairTypeErrorsInOperands(&leftType, &rightType);
503    if (leftType->IsTypeError()) {  // both are errors
504        // We still know that operation result should be boolean, so recover.
505        return {GlobalETSBooleanType(), GlobalETSObjectType()};
506    }
507
508    checker::Type *tsType {};
509    if (!IsReferenceType(leftType) || !IsReferenceType(rightType)) {
510        LogTypeError("Both operands have to be reference types", pos);
511        return {GlobalETSBooleanType(), GlobalETSObjectType()};
512    }
513
514    Relation()->SetNode(left);
515    if (!CheckValidEqualReferenceType(leftType, rightType)) {
516        LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
517        return {GlobalETSBooleanType(), GlobalETSObjectType()};
518    }
519
520    if (!Relation()->IsCastableTo(leftType, rightType) && !Relation()->IsCastableTo(rightType, leftType)) {
521        LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
522        return {GlobalETSBooleanType(), GlobalETSObjectType()};
523    }
524
525    tsType = GlobalETSBooleanType();
526    if (rightType->IsETSDynamicType() && leftType->IsETSDynamicType()) {
527        return {tsType, GlobalBuiltinJSValueType()};
528    }
529
530    return {tsType, GlobalETSObjectType()};
531}
532
533std::optional<std::tuple<Type *, Type *>> ETSChecker::CheckBinaryOperatorEqualError(checker::Type *const leftType,
534                                                                                    checker::Type *const rightType,
535                                                                                    checker::Type *tsType,
536                                                                                    lexer::SourcePosition pos)
537{
538    if (leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) {
539        if (!leftType->AsETSIntEnumType()->IsSameEnumType(rightType->AsETSIntEnumType())) {
540            // We still know that operation result should be boolean, so recover.
541            LogTypeError("Bad operand type, the types of the operands must be the same enum type.", pos);
542            return {{GlobalETSBooleanType(), leftType}};
543        }
544
545        tsType = GlobalETSBooleanType();
546        return std::make_tuple(tsType, leftType);
547    }
548
549    if (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType()) {
550        if (!leftType->AsETSStringEnumType()->IsSameEnumType(rightType->AsETSStringEnumType())) {
551            LogTypeError("Bad operand type, the types of the operands must be the same enum type.", pos);
552            return {{GlobalETSBooleanType(), leftType}};
553        }
554
555        tsType = GlobalETSBooleanType();
556        return std::make_tuple(tsType, leftType);
557    }
558    return std::nullopt;
559}
560
561std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorEqual(ir::Expression *left, ir::Expression *right,
562                                                                lexer::TokenType operationType,
563                                                                lexer::SourcePosition pos, checker::Type *leftType,
564                                                                checker::Type *rightType, Type *unboxedL,
565                                                                Type *unboxedR)
566{
567    RepairTypeErrorsInOperands(&leftType, &rightType);
568    RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
569    if (leftType->IsTypeError()) {  // both are errors
570        return {GlobalETSBooleanType(), GlobalTypeError()};
571    }
572
573    checker::Type *tsType {};
574
575    auto checkError = CheckBinaryOperatorEqualError(leftType, rightType, tsType, pos);
576    if (checkError.has_value()) {
577        return checkError.value();
578    }
579    if (leftType->IsETSDynamicType() || rightType->IsETSDynamicType()) {
580        return CheckBinaryOperatorEqualDynamic(left, right, pos);
581    }
582
583    if (IsReferenceType(leftType) && IsReferenceType(rightType)) {
584        Relation()->SetNode(left);
585        if (!CheckValidEqualReferenceType(leftType, rightType)) {
586            LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
587            return {GlobalETSBooleanType(), leftType};
588        }
589
590        tsType = GlobalETSBooleanType();
591        return {tsType, CreateETSUnionType({leftType, rightType})};
592    }
593
594    if (unboxedL != nullptr && unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) && unboxedR != nullptr &&
595        unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) {
596        if (unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
597            bool res = unboxedL->AsETSBooleanType()->GetValue() == unboxedR->AsETSBooleanType()->GetValue();
598
599            tsType = CreateETSBooleanType(operationType == lexer::TokenType::PUNCTUATOR_EQUAL ? res : !res);
600            return {tsType, tsType};
601        }
602
603        FlagExpressionWithUnboxing(leftType, unboxedL, left);
604        FlagExpressionWithUnboxing(rightType, unboxedR, right);
605
606        tsType = GlobalETSBooleanType();
607        return {tsType, tsType};
608    }
609
610    // Temporary workaround before == and === refactoring
611    if ((rightType->IsETSNullType() && leftType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) ||
612        (leftType->IsETSNullType() && rightType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE))) {
613        return {GlobalETSBooleanType(), CreateETSUnionType({leftType, rightType})};
614    }
615
616    return {nullptr, nullptr};
617}
618
619std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorEqualDynamic(ir::Expression *left, ir::Expression *right,
620                                                                       lexer::SourcePosition pos)
621{
622    // NOTE: vpukhov. enforce intrinsic call in any case?
623    // canonicalize
624    auto *const dynExp = left->TsType()->IsETSDynamicType() ? left : right;
625    auto *const otherExp = dynExp == left ? right : left;
626
627    if (otherExp->TsType()->IsETSDynamicType()) {
628        return {GlobalETSBooleanType(), GlobalBuiltinJSValueType()};
629    }
630    if (dynExp->TsType()->AsETSDynamicType()->IsConvertible(otherExp->TsType())) {
631        // NOTE: vpukhov. boxing flags are not set in dynamic values
632        return {GlobalETSBooleanType(), otherExp->TsType()};
633    }
634    if (IsReferenceType(otherExp->TsType())) {
635        // have to prevent casting dyn_exp via ApplyCast without nullish flag
636        return {GlobalETSBooleanType(), GlobalETSNullishObjectType()};
637    }
638    LogTypeError("Unimplemented case in dynamic type comparison.", pos);
639    return {GlobalETSBooleanType(), GlobalETSNullishObjectType()};
640}
641
642// Satisfying the Chinese checker
643static bool NonNumericTypesAreAppropriateForComparison(Type *leftType, Type *rightType)
644{
645    return (rightType->IsETSStringType() && leftType->IsETSStringType()) ||
646           (((leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) ||
647             (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType())) &&
648            leftType->AsETSEnumType()->IsSameEnumType(rightType->AsETSEnumType()));
649}
650
651std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorLessGreater(ir::Expression *left, ir::Expression *right,
652                                                                      lexer::TokenType operationType,
653                                                                      lexer::SourcePosition pos, bool isEqualOp,
654                                                                      checker::Type *leftType, checker::Type *rightType,
655                                                                      Type *unboxedL, Type *unboxedR)
656{
657    RepairTypeErrorsInOperands(&leftType, &rightType);
658    RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
659    if (leftType->IsTypeError()) {  // both are errors
660        return {GlobalETSBooleanType(), GlobalTypeError()};
661    }
662
663    if ((leftType->IsETSUnionType() || rightType->IsETSUnionType()) &&
664        operationType != lexer::TokenType::PUNCTUATOR_EQUAL &&
665        operationType != lexer::TokenType::PUNCTUATOR_NOT_EQUAL) {
666        LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
667        return {GlobalETSBooleanType(), leftType};
668    }
669
670    checker::Type *tsType {};
671    auto [promotedType, bothConst] =
672        ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_PRIMITIVE, !isEqualOp);
673
674    FlagExpressionWithUnboxing(leftType, unboxedL, left);
675    FlagExpressionWithUnboxing(rightType, unboxedR, right);
676
677    if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
678        return {GlobalETSBooleanType(), CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)})};
679    }
680
681    if ((unboxedL != nullptr) && (unboxedR != nullptr) &&
682        (unboxedL->IsETSBooleanType() != unboxedR->IsETSBooleanType())) {
683        LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
684        return {GlobalETSBooleanType(), leftType};
685    }
686
687    if (promotedType == nullptr && !bothConst) {
688        if (NonNumericTypesAreAppropriateForComparison(leftType, rightType)) {
689            return {GlobalETSBooleanType(), GlobalETSBooleanType()};
690        }
691        if (((leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) ||
692             (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType())) &&
693            leftType->AsETSEnumType()->IsSameEnumType(rightType->AsETSEnumType())) {
694            return {GlobalETSBooleanType(), GlobalETSBooleanType()};
695        }
696
697        LogTypeError("Bad operand type, the types of the operands must be numeric, same enumeration, or boolean type.",
698                     pos);
699        return {GlobalETSBooleanType(), GlobalETSBooleanType()};
700    }
701
702    if (bothConst) {
703        tsType = HandleRelationOperationOnTypes(leftType, rightType, operationType);
704        return {tsType, tsType};
705    }
706
707    tsType = GlobalETSBooleanType();
708    auto *opType = promotedType;
709    return {tsType, opType};
710}
711
712std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorInstanceOf(lexer::SourcePosition pos, checker::Type *leftType,
713                                                                     checker::Type *rightType)
714{
715    RepairTypeErrorsInOperands(&leftType, &rightType);
716    if (leftType->IsTypeError()) {  // both are errors
717        return {GlobalETSBooleanType(), GlobalTypeError()};
718    }
719
720    checker::Type *tsType {};
721    if (!IsReferenceType(leftType) || (!IsReferenceType(rightType) && !rightType->IsETSEnumType())) {
722        LogTypeError("Bad operand type, the types of the operands must be same type.", pos);
723        return {GlobalETSBooleanType(), leftType};
724    }
725
726    if (rightType->IsETSDynamicType() && !rightType->AsETSDynamicType()->HasDecl()) {
727        LogTypeError("Right-hand side of instanceof expression must represent a type.", pos);
728        return {GlobalETSBooleanType(), leftType};
729    }
730
731    tsType = GlobalETSBooleanType();
732    checker::Type *opType = rightType->IsETSDynamicType() ? GlobalBuiltinJSValueType() : GlobalETSObjectType();
733    ComputeApparentType(rightType);
734    RemoveStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT);
735
736    return {tsType, opType};
737}
738
739bool ETSChecker::AdjustNumberLiteralType(ir::NumberLiteral *const literal, Type *literalType, Type *const otherType)
740{
741    if (otherType->IsETSObjectType()) {
742        auto *const objectType = otherType->AsETSObjectType();
743        if (objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE) && !objectType->IsETSStringType()) {
744            literal->RemoveBoxingUnboxingFlags(GetBoxingFlag(literalType));
745            literalType = ETSBuiltinTypeAsPrimitiveType(objectType);
746            literal->SetTsType(literalType);
747            literal->AddBoxingUnboxingFlags(GetBoxingFlag(literalType));
748            return true;
749        }
750    }
751    return false;
752}
753
754Type *ETSChecker::CheckBinaryOperatorNullishCoalescing(ir::Expression *left, ir::Expression *right,
755                                                       lexer::SourcePosition pos)
756{
757    auto *leftType = left->TsType();
758    if (!IsReferenceType(leftType) && !leftType->IsETSEnumType()) {
759        LogTypeError("Left-hand side of nullish-coalescing expression must be a reference or enum type.", pos);
760        return leftType;
761    }
762
763    auto *rightType = right->TsType();
764
765    if (leftType->IsETSEnumType()) {
766        left->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
767        leftType = leftType->AsETSEnumType()->GetDecl()->BoxedClass()->TsType();
768    } else {
769        leftType = GetNonNullishType(leftType);
770    }
771
772    if (rightType->IsETSEnumType()) {
773        right->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
774        rightType = rightType->AsETSEnumType()->GetDecl()->BoxedClass()->TsType();
775    } else {
776        rightType = MaybeBoxExpression(right);
777    }
778
779    if (IsTypeIdenticalTo(leftType, rightType)) {
780        return leftType;
781    }
782
783    //  If possible and required update number literal type to the proper value (identical to left-side type)
784    if (right->IsNumberLiteral() && AdjustNumberLiteralType(right->AsNumberLiteral(), rightType, leftType)) {
785        return leftType;
786    }
787
788    return CreateETSUnionType({leftType, rightType});
789}
790
791using CheckBinaryFunction = std::function<checker::Type *(
792    ETSChecker *, std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op,
793    bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)>;
794
795std::map<lexer::TokenType, CheckBinaryFunction> &GetCheckMap()
796{
797    static std::map<lexer::TokenType, CheckBinaryFunction> checkMap = {
798        {lexer::TokenType::PUNCTUATOR_MULTIPLY, &ETSChecker::CheckBinaryOperatorMulDivMod},
799        {lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
800        {lexer::TokenType::PUNCTUATOR_DIVIDE, &ETSChecker::CheckBinaryOperatorMulDivMod},
801        {lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
802        {lexer::TokenType::PUNCTUATOR_MOD, &ETSChecker::CheckBinaryOperatorMulDivMod},
803        {lexer::TokenType::PUNCTUATOR_MOD_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
804
805        {lexer::TokenType::PUNCTUATOR_MINUS, &ETSChecker::CheckBinaryOperatorPlus},
806        {lexer::TokenType::PUNCTUATOR_MINUS_EQUAL, &ETSChecker::CheckBinaryOperatorPlus},
807        {lexer::TokenType::PUNCTUATOR_PLUS, &ETSChecker::CheckBinaryOperatorPlus},
808        {lexer::TokenType::PUNCTUATOR_PLUS_EQUAL, &ETSChecker::CheckBinaryOperatorPlus},
809
810        {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
811        {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
812        {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
813        {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
814        {lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
815        {lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
816
817        {lexer::TokenType::PUNCTUATOR_BITWISE_OR, &ETSChecker::CheckBinaryOperatorBitwise},
818        {lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
819        {lexer::TokenType::PUNCTUATOR_BITWISE_AND, &ETSChecker::CheckBinaryOperatorBitwise},
820        {lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
821        {lexer::TokenType::PUNCTUATOR_BITWISE_XOR, &ETSChecker::CheckBinaryOperatorBitwise},
822        {lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
823    };
824
825    return checkMap;
826}
827
828struct BinaryOperatorParams {
829    ir::Expression *left;
830    ir::Expression *right;
831    ir::Expression *expr;
832    lexer::TokenType operationType;
833    lexer::SourcePosition pos;
834    bool isEqualOp;
835};
836
837struct TypeParams {
838    checker::Type *leftType;
839    checker::Type *rightType;
840    Type *unboxedL;
841    Type *unboxedR;
842};
843
844static std::tuple<Type *, Type *> CheckBinaryOperatorHelper(ETSChecker *checker,
845                                                            const BinaryOperatorParams &binaryParams,
846                                                            const TypeParams &typeParams)
847{
848    ir::Expression *left = binaryParams.left;
849    ir::Expression *right = binaryParams.right;
850    lexer::SourcePosition pos = binaryParams.pos;
851    checker::Type *const leftType = typeParams.leftType;
852    checker::Type *const rightType = typeParams.rightType;
853    checker::Type *tsType {};
854    switch (binaryParams.operationType) {
855        case lexer::TokenType::PUNCTUATOR_LOGICAL_AND:
856        case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
857            tsType = checker->CheckBinaryOperatorLogical(left, right, binaryParams.expr, pos, leftType, rightType,
858                                                         typeParams.unboxedL, typeParams.unboxedR);
859            break;
860        }
861        case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
862        case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
863            return checker->CheckBinaryOperatorStrictEqual(left, binaryParams.operationType, pos, leftType, rightType);
864        }
865        case lexer::TokenType::PUNCTUATOR_EQUAL:
866        case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
867            std::tuple<Type *, Type *> res =
868                checker->CheckBinaryOperatorEqual(left, right, binaryParams.operationType, pos, leftType, rightType,
869                                                  typeParams.unboxedL, typeParams.unboxedR);
870            if (!(std::get<0>(res) == nullptr && std::get<1>(res) == nullptr)) {
871                return res;
872            }
873            [[fallthrough]];
874        }
875        case lexer::TokenType::PUNCTUATOR_LESS_THAN:
876        case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
877        case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
878        case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
879            return checker->CheckBinaryOperatorLessGreater(left, right, binaryParams.operationType, pos,
880                                                           binaryParams.isEqualOp, leftType, rightType,
881                                                           typeParams.unboxedL, typeParams.unboxedR);
882        }
883        case lexer::TokenType::KEYW_INSTANCEOF: {
884            return checker->CheckBinaryOperatorInstanceOf(pos, leftType, rightType);
885        }
886        case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: {
887            tsType = checker->CheckBinaryOperatorNullishCoalescing(left, right, pos);
888            break;
889        }
890        default: {
891            UNREACHABLE();
892            break;
893        }
894    }
895
896    return {tsType, tsType};
897}
898
899namespace {
900bool IsStringEnum(const ir::Expression *expr)
901{
902    if (expr == nullptr) {
903        return false;
904    }
905    if (expr->TsTypeOrError()->IsTypeError()) {
906        return false;
907    }
908
909    auto type = expr->TsType();
910    if (type == nullptr) {
911        return false;
912    }
913
914    return type->IsETSStringEnumType();
915}
916
917bool IsIntEnum(const ir::Expression *expr)
918{
919    if (expr == nullptr) {
920        return false;
921    }
922    if (expr->TsTypeOrError()->IsTypeError()) {
923        return false;
924    }
925
926    auto type = expr->TsType();
927    if (type == nullptr) {
928        return false;
929    }
930
931    return type->IsETSIntEnumType();
932}
933
934bool CheckNumericOperatorContext(ir::Expression *expression, lexer::TokenType op)
935{
936    const bool isMultiplicative = op == lexer::TokenType::PUNCTUATOR_MULTIPLY ||
937                                  op == lexer::TokenType::PUNCTUATOR_DIVIDE || op == lexer::TokenType::PUNCTUATOR_MOD;
938    const bool isAdditive = op == lexer::TokenType::PUNCTUATOR_PLUS || op == lexer::TokenType::PUNCTUATOR_MINUS;
939    const bool isShift = op == lexer::TokenType::PUNCTUATOR_LEFT_SHIFT ||
940                         op == lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT ||
941                         op == lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT;
942    const bool isRelational =
943        op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL ||
944        op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL;
945    const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL;
946    const bool isBitwise = op == lexer::TokenType::PUNCTUATOR_BITWISE_AND ||
947                           op == lexer::TokenType::PUNCTUATOR_BITWISE_OR ||
948                           op == lexer::TokenType::PUNCTUATOR_BITWISE_XOR;
949    const bool isConditionalAndOr =
950        op == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || op == lexer::TokenType::PUNCTUATOR_LOGICAL_OR;
951
952    if (IsIntEnum(expression)) {
953        if (isMultiplicative || isAdditive || isShift || isRelational || isEquality || isBitwise ||
954            isConditionalAndOr) {
955            expression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
956        }
957        return true;
958    }
959    return false;
960}
961
962void CheckStringOperatorContext(ir::Expression *expression, checker::Type *otherType, lexer::TokenType op)
963{
964    if (IsStringEnum(expression) && (otherType->IsETSStringType() || otherType->IsETSStringEnumType())) {
965        if (op == lexer::TokenType::PUNCTUATOR_PLUS) {
966            expression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
967        }
968    }
969}
970
971bool CheckRelationalOperatorsBetweenEnums(ir::Expression *left, ir::Expression *right, lexer::TokenType op)
972{
973    const bool isRelational =
974        op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL ||
975        op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL;
976    const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL;
977
978    if (((IsStringEnum(left) && IsStringEnum(right)) ||
979         (IsIntEnum(left) && IsIntEnum(right)))) {  // NOTE(psiket) In case of int enums it has been already checked in
980                                                    // the CheckNumericOperatorContext function
981        if (isRelational || isEquality) {
982            left->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
983            right->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
984            return true;
985        }
986    }
987    return false;
988}
989
990void CheckNeedToGenerateGetValueForBinaryExpression(ir::Expression *expression)
991{
992    if (!expression->IsBinaryExpression()) {
993        return;
994    }
995
996    auto binaryExpression = expression->AsBinaryExpression();
997    auto op = binaryExpression->OperatorType();
998    auto leftExp = binaryExpression->Left();
999    auto rightExp = binaryExpression->Right();
1000
1001    // Numeric Operator Context
1002    auto leftIsIntEnum = CheckNumericOperatorContext(leftExp, op);
1003    auto rightIsIntEnum = CheckNumericOperatorContext(rightExp, op);
1004    if (leftIsIntEnum || rightIsIntEnum) {
1005        return;
1006    }
1007
1008    // String Operator Context
1009    CheckStringOperatorContext(leftExp, rightExp->TsTypeOrError(), op);
1010    CheckStringOperatorContext(rightExp, leftExp->TsTypeOrError(), op);
1011
1012    // Relational operators if both are enumeration Types
1013    if (CheckRelationalOperatorsBetweenEnums(leftExp, rightExp, op)) {
1014        return;
1015    }
1016}
1017}  // namespace
1018
1019std::tuple<Type *, Type *> ETSChecker::CheckArithmeticOperations(
1020    ir::Expression *expr, std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op,
1021    bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
1022{
1023    auto [left, right, operationType, pos] = op;
1024    auto [leftType, rightType, unboxedL, unboxedR] = types;
1025
1026    if (leftType->IsETSUnionType()) {
1027        leftType = GetNonConstantType(leftType);
1028    }
1029
1030    if (rightType->IsETSUnionType()) {
1031        rightType = GetNonConstantType(rightType);
1032    }
1033
1034    auto checkMap = GetCheckMap();
1035    if (checkMap.find(operationType) != checkMap.end()) {
1036        auto check = checkMap[operationType];
1037        auto tsType = check(this, std::make_tuple(left, right, operationType, pos), isEqualOp,
1038                            std::make_tuple(leftType, rightType, unboxedL, unboxedR));
1039        return {tsType, tsType};
1040    }
1041
1042    return CheckBinaryOperatorHelper(this, {left, right, expr, operationType, pos, isEqualOp},
1043                                     {leftType, rightType, unboxedL, unboxedR});
1044}
1045
1046std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperator(ir::Expression *left, ir::Expression *right,
1047                                                           ir::Expression *expr, lexer::TokenType operationType,
1048                                                           lexer::SourcePosition pos, bool forcePromotion)
1049{
1050    checker::Type *leftType = left->Check(this);
1051
1052    if (leftType == nullptr) {
1053        LogTypeError("Unexpected type error in binary expression", left->Start());
1054        auto rightType = right->Check(this);
1055        return {rightType, rightType};
1056    }
1057
1058    if (operationType == lexer::TokenType::KEYW_INSTANCEOF) {
1059        AddStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT);
1060    }
1061
1062    Context().CheckTestSmartCastCondition(operationType);
1063
1064    checker::Type *rightType = right->Check(this);
1065    if (right->IsTypeNode()) {
1066        rightType = right->AsTypeNode()->GetType(this);
1067    }
1068
1069    if (rightType == nullptr) {
1070        LogTypeError("Unexpected type error in binary expression", pos);
1071        return {leftType, leftType};
1072    }
1073
1074    CheckNeedToGenerateGetValueForBinaryExpression(expr);
1075
1076    const bool isLogicalExtendedOperator = (operationType == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) ||
1077                                           (operationType == lexer::TokenType::PUNCTUATOR_LOGICAL_OR);
1078    Type *unboxedL =
1079        isLogicalExtendedOperator ? ETSBuiltinTypeAsConditionalType(leftType) : ETSBuiltinTypeAsPrimitiveType(leftType);
1080    Type *unboxedR = isLogicalExtendedOperator ? ETSBuiltinTypeAsConditionalType(rightType)
1081                                               : ETSBuiltinTypeAsPrimitiveType(rightType);
1082
1083    bool isEqualOp = (operationType > lexer::TokenType::PUNCTUATOR_SUBSTITUTION &&
1084                      operationType < lexer::TokenType::PUNCTUATOR_ARROW) &&
1085                     !forcePromotion;
1086
1087    if (CheckBinaryOperatorForBigInt(leftType, rightType, operationType)) {
1088        switch (operationType) {
1089            case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
1090            case lexer::TokenType::PUNCTUATOR_LESS_THAN:
1091            case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL:
1092            case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
1093                return {GlobalETSBooleanType(), GlobalETSBooleanType()};
1094            default:
1095                return {leftType, rightType};
1096        }
1097    }
1098
1099    return CheckArithmeticOperations(expr, std::make_tuple(left, right, operationType, pos), isEqualOp,
1100                                     std::make_tuple(leftType, rightType, unboxedL, unboxedR));
1101}
1102
1103Type *ETSChecker::HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
1104{
1105    ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
1106           right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
1107
1108    if (left->IsDoubleType() || right->IsDoubleType()) {
1109        return PerformArithmeticOperationOnTypes<DoubleType>(left, right, operationType);
1110    }
1111
1112    if (left->IsFloatType() || right->IsFloatType()) {
1113        return PerformArithmeticOperationOnTypes<FloatType>(left, right, operationType);
1114    }
1115
1116    if (left->IsLongType() || right->IsLongType()) {
1117        return PerformArithmeticOperationOnTypes<LongType>(left, right, operationType);
1118    }
1119
1120    return PerformArithmeticOperationOnTypes<IntType>(left, right, operationType);
1121}
1122
1123Type *ETSChecker::HandleBitwiseOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
1124{
1125    ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
1126           right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
1127
1128    if (left->IsDoubleType() || right->IsDoubleType()) {
1129        return HandleBitWiseArithmetic<DoubleType, LongType>(left, right, operationType);
1130    }
1131
1132    if (left->IsFloatType() || right->IsFloatType()) {
1133        return HandleBitWiseArithmetic<FloatType, IntType>(left, right, operationType);
1134    }
1135
1136    if (left->IsLongType() || right->IsLongType()) {
1137        return HandleBitWiseArithmetic<LongType>(left, right, operationType);
1138    }
1139
1140    return HandleBitWiseArithmetic<IntType>(left, right, operationType);
1141}
1142
1143void ETSChecker::FlagExpressionWithUnboxing(Type *type, Type *unboxedType, ir::Expression *typeExpression)
1144{
1145    if (type->IsETSObjectType() && (unboxedType != nullptr)) {
1146        typeExpression->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxedType));
1147    }
1148}
1149
1150}  // namespace ark::es2panda::checker
1151