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