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