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