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