• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "checker/checker.h"
17 #include "checker/ets/narrowingWideningConverter.h"
18 #include "checker/types/globalTypesHolder.h"
19 #include "checker/types/ets/etsObjectType.h"
20 #include "ir/astNode.h"
21 #include "ir/base/catchClause.h"
22 #include "ir/expression.h"
23 #include "ir/typeNode.h"
24 #include "ir/base/scriptFunction.h"
25 #include "ir/base/classProperty.h"
26 #include "ir/base/methodDefinition.h"
27 #include "ir/statements/variableDeclarator.h"
28 #include "ir/statements/switchCaseStatement.h"
29 #include "ir/expressions/identifier.h"
30 #include "ir/expressions/callExpression.h"
31 #include "ir/expressions/memberExpression.h"
32 #include "ir/expressions/arrowFunctionExpression.h"
33 #include "ir/statements/labelledStatement.h"
34 #include "ir/statements/tryStatement.h"
35 #include "ir/ets/etsNewClassInstanceExpression.h"
36 #include "ir/ets/etsParameterExpression.h"
37 #include "ir/ts/tsTypeAliasDeclaration.h"
38 #include "ir/ts/tsEnumMember.h"
39 #include "ir/ts/tsTypeParameter.h"
40 #include "ir/ets/etsUnionType.h"
41 #include "ir/ets/etsTypeReference.h"
42 #include "ir/ets/etsTypeReferencePart.h"
43 #include "varbinder/variable.h"
44 #include "varbinder/scope.h"
45 #include "varbinder/declaration.h"
46 #include "parser/program/program.h"
47 #include "checker/ETSchecker.h"
48 #include "varbinder/ETSBinder.h"
49 #include "checker/ets/typeRelationContext.h"
50 #include "checker/ets/boxingConverter.h"
51 #include "checker/ets/unboxingConverter.h"
52 #include "util/helpers.h"
53 
54 namespace ark::es2panda::checker {
CheckTruthinessOfType(ir::Expression * expr)55 void ETSChecker::CheckTruthinessOfType(ir::Expression *expr)
56 {
57     auto *const testType = expr->Check(this);
58     auto *const conditionType = ETSBuiltinTypeAsConditionalType(testType);
59 
60     expr->SetTsType(conditionType);
61 
62     if (conditionType == nullptr || (!conditionType->IsTypeError() && !conditionType->IsConditionalExprType())) {
63         LogTypeError("Condition must be of possible condition type", expr->Start());
64         return;
65     }
66 
67     if (conditionType->IsETSVoidType()) {
68         LogTypeError("An expression of type 'void' cannot be tested for truthiness", expr->Start());
69         return;
70     }
71 
72     if (conditionType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
73         FlagExpressionWithUnboxing(testType, conditionType, expr);
74     }
75 
76     if (conditionType->IsETSEnumType()) {
77         expr->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
78     }
79 }
80 
CheckNonNullish(ir::Expression const * expr)81 void ETSChecker::CheckNonNullish(ir::Expression const *expr)
82 {
83     if (expr->TsTypeOrError()->PossiblyETSNullish()) {
84         ThrowTypeError("Value is possibly nullish.", expr->Start());
85     }
86 }
87 
GetNonNullishType(Type * type)88 Type *ETSChecker::GetNonNullishType(Type *type)
89 {
90     if (type->DefinitelyNotETSNullish()) {
91         return type;
92     }
93     if (type->IsETSTypeParameter()) {
94         return Allocator()->New<ETSNonNullishType>(type->AsETSTypeParameter());
95     }
96 
97     if (type->IsETSNullType() || type->IsETSUndefinedType()) {
98         return GetGlobalTypesHolder()->GlobalBuiltinNeverType();
99     }
100 
101     ArenaVector<Type *> copied(Allocator()->Adapter());
102     for (auto const &t : type->AsETSUnionType()->ConstituentTypes()) {
103         if (t->IsETSNullType() || t->IsETSUndefinedType()) {
104             continue;
105         }
106         copied.push_back(GetNonNullishType(t));
107     }
108     return copied.empty() ? GetGlobalTypesHolder()->GlobalBuiltinNeverType() : CreateETSUnionType(std::move(copied));
109 }
110 
RemoveNullType(Type * const type)111 Type *ETSChecker::RemoveNullType(Type *const type)
112 {
113     if (type->DefinitelyNotETSNullish() || type->IsETSUndefinedType()) {
114         return type;
115     }
116 
117     if (type->IsETSTypeParameter()) {
118         return Allocator()->New<ETSNonNullishType>(type->AsETSTypeParameter());
119     }
120 
121     if (type->IsETSNullType()) {
122         return GetGlobalTypesHolder()->GlobalBuiltinNeverType();
123     }
124 
125     ASSERT(type->IsETSUnionType());
126     ArenaVector<Type *> copiedTypes(Allocator()->Adapter());
127 
128     for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) {
129         if (!constituentType->IsETSNullType()) {
130             copiedTypes.push_back(RemoveNullType(constituentType));
131         }
132     }
133 
134     return copiedTypes.empty() ? GetGlobalTypesHolder()->GlobalBuiltinNeverType()
135                                : CreateETSUnionType(std::move(copiedTypes));
136 }
137 
RemoveUndefinedType(Type * const type)138 Type *ETSChecker::RemoveUndefinedType(Type *const type)
139 {
140     if (type->DefinitelyNotETSNullish() || type->IsETSNullType()) {
141         return type;
142     }
143 
144     if (type->IsETSTypeParameter()) {
145         return Allocator()->New<ETSNonNullishType>(type->AsETSTypeParameter());
146     }
147 
148     if (type->IsETSUndefinedType()) {
149         return GetGlobalTypesHolder()->GlobalBuiltinNeverType();
150     }
151 
152     ASSERT(type->IsETSUnionType());
153     ArenaVector<Type *> copiedTypes(Allocator()->Adapter());
154 
155     for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) {
156         if (!constituentType->IsETSUndefinedType()) {
157             copiedTypes.push_back(RemoveUndefinedType(constituentType));
158         }
159     }
160 
161     return copiedTypes.empty() ? GetGlobalTypesHolder()->GlobalBuiltinNeverType()
162                                : CreateETSUnionType(std::move(copiedTypes));
163 }
164 
RemoveNullishTypes(Type * type)165 std::pair<Type *, Type *> ETSChecker::RemoveNullishTypes(Type *type)
166 {
167     if (type->DefinitelyNotETSNullish()) {
168         return {GetGlobalTypesHolder()->GlobalBuiltinNeverType(), type};
169     }
170 
171     if (type->IsETSTypeParameter()) {
172         return {GetGlobalTypesHolder()->GlobalETSNullishType(),
173                 Allocator()->New<ETSNonNullishType>(type->AsETSTypeParameter())};
174     }
175 
176     if (type->IsETSUndefinedType() || type->IsETSNullType()) {
177         return {type, GetGlobalTypesHolder()->GlobalBuiltinNeverType()};
178     }
179 
180     ASSERT(type->IsETSUnionType());
181     ArenaVector<Type *> nullishTypes(Allocator()->Adapter());
182     ArenaVector<Type *> notNullishTypes(Allocator()->Adapter());
183 
184     for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) {
185         if (constituentType->IsETSUndefinedType() || constituentType->IsETSNullType()) {
186             nullishTypes.push_back(constituentType);
187         } else {
188             notNullishTypes.push_back(!constituentType->IsETSTypeParameter()
189                                           ? constituentType
190                                           : Allocator()->New<ETSNonNullishType>(constituentType->AsETSTypeParameter()));
191         }
192     }
193 
194     Type *nullishType = nullishTypes.empty() ? GetGlobalTypesHolder()->GlobalBuiltinNeverType()
195                                              : CreateETSUnionType(std::move(nullishTypes));
196     Type *notNullishType = notNullishTypes.empty() ? GetGlobalTypesHolder()->GlobalBuiltinNeverType()
197                                                    : CreateETSUnionType(std::move(notNullishTypes));
198     return {nullishType, notNullishType};
199 }
200 
201 // NOTE(vpukhov): can be implemented with relation if etscompiler will support it
202 template <typename Pred, typename Trv>
MatchConstituentOrConstraint(const Type * type,Pred const & pred,Trv const & trv)203 static bool MatchConstituentOrConstraint(const Type *type, Pred const &pred, Trv const &trv)
204 {
205     auto const traverse = [&pred, &trv](const Type *ttype) {
206         return MatchConstituentOrConstraint<Pred, Trv>(ttype, pred, trv);
207     };
208     if (pred(type)) {
209         return true;
210     }
211     if (!trv(type)) {
212         return false;
213     }
214     if (type->IsETSUnionType()) {
215         for (auto const &ctype : type->AsETSUnionType()->ConstituentTypes()) {
216             if (traverse(ctype)) {
217                 return true;
218             }
219         }
220         return false;
221     }
222     if (type->IsETSTypeParameter()) {
223         return traverse(type->AsETSTypeParameter()->GetConstraintType());
224     }
225     if (type->IsETSNonNullishType()) {
226         auto tparam = type->AsETSNonNullishType()->GetUnderlying();
227         return traverse(tparam->GetConstraintType());
228     }
229     return false;
230 }
231 
232 template <typename Pred>
MatchConstituentOrConstraint(const Type * type,Pred const & pred)233 static bool MatchConstituentOrConstraint(const Type *type, Pred const &pred)
234 {
235     return MatchConstituentOrConstraint(type, pred, []([[maybe_unused]] const Type *t) { return true; });
236 }
237 
PossiblyETSNull() const238 bool Type::PossiblyETSNull() const
239 {
240     return MatchConstituentOrConstraint(
241         this, [](const Type *t) { return t->IsETSNullType(); },
242         [](const Type *t) { return !t->IsETSNonNullishType(); });
243 }
244 
PossiblyETSUndefined() const245 bool Type::PossiblyETSUndefined() const
246 {
247     return MatchConstituentOrConstraint(
248         this, [](const Type *t) { return t->IsETSUndefinedType(); },
249         [](const Type *t) { return !t->IsETSNonNullishType(); });
250 }
251 
PossiblyETSNullish() const252 bool Type::PossiblyETSNullish() const
253 {
254     return MatchConstituentOrConstraint(
255         this, [](const Type *t) { return t->IsETSNullType() || t->IsETSUndefinedType(); },
256         [](const Type *t) { return !t->IsETSNonNullishType(); });
257 }
258 
DefinitelyETSNullish() const259 bool Type::DefinitelyETSNullish() const
260 {
261     return !MatchConstituentOrConstraint(
262         this,
263         [](const Type *t) {
264             return !(t->IsTypeParameter() || t->IsETSUnionType() || t->IsETSNullType() || t->IsETSUndefinedType());
265         },
266         [](const Type *t) { return !t->IsETSNonNullishType(); });
267 }
268 
DefinitelyNotETSNullish() const269 bool Type::DefinitelyNotETSNullish() const
270 {
271     return !PossiblyETSNullish();
272 }
273 
PossiblyETSString() const274 bool Type::PossiblyETSString() const
275 {
276     return MatchConstituentOrConstraint(this, [](const Type *t) {
277         return t->IsETSStringType() || (t->IsETSObjectType() && t->AsETSObjectType()->IsGlobalETSObjectType());
278     });
279 }
280 
IsValueTypedObjectType(ETSObjectType const * t)281 static bool IsValueTypedObjectType(ETSObjectType const *t)
282 {
283     return t->IsGlobalETSObjectType() || t->HasObjectFlag(ETSObjectFlags::VALUE_TYPED);
284 }
285 
PossiblyETSValueTyped() const286 bool Type::PossiblyETSValueTyped() const
287 {
288     return MatchConstituentOrConstraint(this, [](const Type *t) {
289         return t->IsNullType() || t->IsUndefinedType() ||
290                (t->IsETSObjectType() && IsValueTypedObjectType(t->AsETSObjectType()));
291     });
292 }
293 
PossiblyETSValueTypedExceptNullish() const294 bool Type::PossiblyETSValueTypedExceptNullish() const
295 {
296     return MatchConstituentOrConstraint(
297         this, [](const Type *t) { return t->IsETSObjectType() && IsValueTypedObjectType(t->AsETSObjectType()); });
298 }
299 
IsETSReferenceType() const300 bool Type::IsETSReferenceType() const
301 {
302     return IsETSObjectType() || IsETSArrayType() || IsETSNullType() || IsETSUndefinedType() || IsETSStringType() ||
303            IsETSTypeParameter() || IsETSUnionType() || IsETSNonNullishType() || IsETSBigIntType() ||
304            IsETSFunctionType();
305 }
306 
IsETSUnboxableObject() const307 bool Type::IsETSUnboxableObject() const
308 {
309     return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE);
310 }
311 
IsConstantExpression(ir::Expression * expr,Type * type)312 bool ETSChecker::IsConstantExpression(ir::Expression *expr, Type *type)
313 {
314     return (type->HasTypeFlag(TypeFlag::CONSTANT) && (expr->IsIdentifier() || expr->IsMemberExpression()));
315 }
316 
GetNonConstantTypeFromPrimitiveType(Type * type) const317 Type *ETSChecker::GetNonConstantTypeFromPrimitiveType(Type *type) const
318 {
319     if (type->IsETSStringType()) {
320         return GlobalBuiltinETSStringType();
321     }
322 
323     if (!type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
324         return type;
325     }
326 
327     if (type->HasTypeFlag(TypeFlag::LONG)) {
328         return GlobalLongType();
329     }
330 
331     if (type->HasTypeFlag(TypeFlag::BYTE)) {
332         return GlobalByteType();
333     }
334 
335     if (type->HasTypeFlag(TypeFlag::SHORT)) {
336         return GlobalShortType();
337     }
338 
339     if (type->HasTypeFlag(TypeFlag::CHAR)) {
340         return GlobalCharType();
341     }
342 
343     if (type->HasTypeFlag(TypeFlag::INT)) {
344         return GlobalIntType();
345     }
346 
347     if (type->HasTypeFlag(TypeFlag::FLOAT)) {
348         return GlobalFloatType();
349     }
350 
351     if (type->HasTypeFlag(TypeFlag::DOUBLE)) {
352         return GlobalDoubleType();
353     }
354 
355     if (type->IsETSBooleanType()) {
356         return GlobalETSBooleanType();
357     }
358     return type;
359 }
360 
GetTypeOfSetterGetter(varbinder::Variable * const var)361 Type *ETSChecker::GetTypeOfSetterGetter(varbinder::Variable *const var)
362 {
363     auto *propType = var->TsType()->AsETSFunctionType();
364     if (propType->HasTypeFlag(checker::TypeFlag::GETTER)) {
365         return propType->FindGetter()->ReturnType();
366     }
367     return propType->FindSetter()->Params()[0]->TsType();
368 }
369 
IterateInVariableContext(varbinder::Variable * const var)370 void ETSChecker::IterateInVariableContext(varbinder::Variable *const var)
371 {
372     // Before computing the given variables type, we have to make a new checker context frame so that the checking is
373     // done in the proper context, and have to enter the scope where the given variable is declared, so reference
374     // resolution works properly
375     auto *iter = var->Declaration()->Node()->Parent();
376     while (iter != nullptr) {
377         if (iter->IsMethodDefinition()) {
378             auto *methodDef = iter->AsMethodDefinition();
379             ASSERT(methodDef->TsType());
380             Context().SetContainingSignature(methodDef->Function()->Signature());
381         }
382 
383         if (iter->IsClassDefinition()) {
384             auto *classDef = iter->AsClassDefinition();
385             ETSObjectType *containingClass {};
386 
387             if (classDef->TsType() == nullptr) {
388                 containingClass = BuildBasicClassProperties(classDef);
389                 ResolveDeclaredMembersOfObject(containingClass);
390             } else {
391                 containingClass = classDef->TsType()->AsETSObjectType();
392             }
393 
394             ASSERT(classDef->TsType());
395             Context().SetContainingClass(containingClass);
396         }
397 
398         iter = iter->Parent();
399     }
400 }
401 
GetTypeOfVariable(varbinder::Variable * const var)402 Type *ETSChecker::GetTypeOfVariable(varbinder::Variable *const var)
403 {
404     if (IsVariableGetterSetter(var)) {
405         return GetTypeOfSetterGetter(var);
406     }
407 
408     if (var->TsTypeOrError() != nullptr) {
409         return var->TsTypeOrError();
410     }
411 
412     // NOTE: kbaladurin. forbid usage of imported entities as types without declarations
413     if (VarBinder()->AsETSBinder()->IsDynamicModuleVariable(var)) {
414         auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(var);
415         if (importData->import->IsPureDynamic()) {
416             return GlobalBuiltinDynamicType(importData->import->Language());
417         }
418     }
419 
420     checker::SavedCheckerContext savedContext(this, CheckerStatus::NO_OPTS);
421     checker::ScopeContext scopeCtx(this, var->GetScope());
422     IterateInVariableContext(var);
423 
424     switch (var->Declaration()->Type()) {
425         case varbinder::DeclType::CLASS: {
426             auto *classDef = var->Declaration()->Node()->AsClassDefinition();
427             BuildBasicClassProperties(classDef);
428             return classDef->TsType();
429         }
430         case varbinder::DeclType::ENUM_LITERAL:
431         case varbinder::DeclType::CONST:
432         case varbinder::DeclType::READONLY:
433         case varbinder::DeclType::LET:
434         case varbinder::DeclType::VAR: {
435             auto *declNode = var->Declaration()->Node();
436 
437             if (var->Declaration()->Node()->IsIdentifier()) {
438                 declNode = declNode->Parent();
439             }
440 
441             return declNode->Check(this);
442         }
443         case varbinder::DeclType::FUNC:
444         case varbinder::DeclType::IMPORT: {
445             return var->Declaration()->Node()->Check(this);
446         }
447         case varbinder::DeclType::TYPE_ALIAS: {
448             return GetTypeFromTypeAliasReference(var);
449         }
450         case varbinder::DeclType::INTERFACE: {
451             return BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration());
452         }
453         default: {
454             UNREACHABLE();
455         }
456     }
457 
458     return var->TsType();
459 }
460 
461 // Determine if unchecked cast is needed and yield guaranteed source type
GuaranteedTypeForUncheckedCast(Type * base,Type * substituted)462 Type *ETSChecker::GuaranteedTypeForUncheckedCast(Type *base, Type *substituted)
463 {
464     // Apparent type acts as effective representation for type.
465     //  For T extends SomeClass|undefined
466     //  Apparent(Int|T|null) is Int|SomeClass|undefined|null
467     auto *appBase = GetApparentType(base);
468     auto *appSubst = GetApparentType(substituted);
469     // Base is supertype of Substituted AND Substituted is supertype of Base
470     return Relation()->IsIdenticalTo(appSubst, appBase) ? nullptr : appBase;
471 }
472 
473 // Determine if substituted property access requires cast from erased type
GuaranteedTypeForUncheckedPropertyAccess(varbinder::Variable * const prop)474 Type *ETSChecker::GuaranteedTypeForUncheckedPropertyAccess(varbinder::Variable *const prop)
475 {
476     if (IsVariableStatic(prop)) {
477         return nullptr;
478     }
479     if (IsVariableGetterSetter(prop)) {
480         auto *method = prop->TsType()->AsETSFunctionType();
481         if (!method->HasTypeFlag(checker::TypeFlag::GETTER)) {
482             return nullptr;
483         }
484         return GuaranteedTypeForUncheckedCallReturn(method->FindGetter());
485     }
486     // NOTE(vpukhov): mark ETSDynamicType properties
487     if (prop->Declaration() == nullptr || prop->Declaration()->Node() == nullptr) {
488         return nullptr;
489     }
490 
491     varbinder::Variable *baseProp = nullptr;
492     switch (auto node = prop->Declaration()->Node(); node->Type()) {
493         case ir::AstNodeType::CLASS_PROPERTY:
494             baseProp = node->AsClassProperty()->Id()->Variable();
495             break;
496         case ir::AstNodeType::METHOD_DEFINITION:
497             baseProp = node->AsMethodDefinition()->Variable();
498             break;
499         case ir::AstNodeType::CLASS_DEFINITION:
500             baseProp = node->AsClassDefinition()->Ident()->Variable();
501             break;
502         default:
503             UNREACHABLE();
504     }
505 
506     if (baseProp == prop) {
507         return nullptr;
508     }
509     return GuaranteedTypeForUncheckedCast(GetTypeOfVariable(baseProp), GetTypeOfVariable(prop));
510 }
511 
512 // Determine if substituted method cast requires cast from erased type
GuaranteedTypeForUncheckedCallReturn(Signature * sig)513 Type *ETSChecker::GuaranteedTypeForUncheckedCallReturn(Signature *sig)
514 {
515     if (sig->HasSignatureFlag(checker::SignatureFlags::THIS_RETURN_TYPE)) {
516         return sig->ReturnType();
517     }
518     auto *baseSig = sig->Function()->Signature();
519     if (baseSig == sig) {
520         return nullptr;
521     }
522     return GuaranteedTypeForUncheckedCast(baseSig->ReturnType(), sig->ReturnType());
523 }
524 
CheckEtsFunctionType(ir::Identifier * const ident,ir::Identifier const * const id,ir::TypeNode const * const annotation)525 void ETSChecker::CheckEtsFunctionType(ir::Identifier *const ident, ir::Identifier const *const id,
526                                       ir::TypeNode const *const annotation)
527 {
528     if (annotation == nullptr) {
529         ThrowTypeError(
530             {"Cannot infer type for ", id->Name(), " because method reference needs an explicit target type"},
531             id->Start());
532     }
533 
534     const auto *const targetType = GetTypeOfVariable(id->Variable());
535     ASSERT(targetType != nullptr);
536 
537     if (!targetType->IsETSObjectType()) {
538         ThrowTypeError("Initializers type is not assignable to the target type", ident->Start());
539     }
540 }
541 
GetTypeFromTypeAliasReference(varbinder::Variable * var)542 Type *ETSChecker::GetTypeFromTypeAliasReference(varbinder::Variable *var)
543 {
544     if (var->TsType() != nullptr) {
545         return var->TsType();
546     }
547 
548     auto *const aliasTypeNode = var->Declaration()->Node()->AsTSTypeAliasDeclaration();
549     TypeStackElement tse(this, aliasTypeNode, "Circular type alias reference", aliasTypeNode->Start());
550     aliasTypeNode->Check(this);
551     auto *const aliasedType = aliasTypeNode->TypeAnnotation()->GetType(this);
552 
553     var->SetTsType(aliasedType);
554     return aliasedType;
555 }
556 
GetTypeFromInterfaceReference(varbinder::Variable * var)557 Type *ETSChecker::GetTypeFromInterfaceReference(varbinder::Variable *var)
558 {
559     if (var->TsType() != nullptr) {
560         return var->TsType();
561     }
562 
563     auto *interfaceType = BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration());
564     var->SetTsType(interfaceType);
565     return interfaceType;
566 }
567 
GetTypeFromClassReference(varbinder::Variable * var)568 Type *ETSChecker::GetTypeFromClassReference(varbinder::Variable *var)
569 {
570     if (var->TsType() != nullptr) {
571         return var->TsType();
572     }
573 
574     auto *classType = BuildBasicClassProperties(var->Declaration()->Node()->AsClassDefinition());
575     var->SetTsType(classType);
576     return classType;
577 }
578 
GetTypeFromEnumReference(varbinder::Variable * var)579 Type *ETSChecker::GetTypeFromEnumReference([[maybe_unused]] varbinder::Variable *var)
580 {
581     if (var->TsType() != nullptr) {
582         return var->TsType();
583     }
584 
585     auto *const enumDecl = var->Declaration()->Node()->AsTSEnumDeclaration();
586     if (auto *const itemInit = enumDecl->Members().front()->AsTSEnumMember()->Init(); itemInit->IsNumberLiteral()) {
587         return CreateEnumIntTypeFromEnumDeclaration(enumDecl);
588     } else if (itemInit->IsStringLiteral()) {  // NOLINT(readability-else-after-return)
589         return CreateEnumStringTypeFromEnumDeclaration(enumDecl);
590     } else {  // NOLINT(readability-else-after-return)
591         ThrowTypeError("Invalid enumeration value type.", enumDecl->Start());
592     }
593 }
594 
GetTypeFromTypeParameterReference(varbinder::LocalVariable * var,const lexer::SourcePosition & pos)595 Type *ETSChecker::GetTypeFromTypeParameterReference(varbinder::LocalVariable *var, const lexer::SourcePosition &pos)
596 {
597     ASSERT(var->Declaration()->Node()->IsTSTypeParameter());
598     if ((var->Declaration()->Node()->AsTSTypeParameter()->Parent()->Parent()->IsClassDefinition() ||
599          var->Declaration()->Node()->AsTSTypeParameter()->Parent()->Parent()->IsTSInterfaceDeclaration()) &&
600         HasStatus(CheckerStatus::IN_STATIC_CONTEXT)) {
601         ThrowTypeError({"Cannot make a static reference to the non-static type ", var->Name()}, pos);
602     }
603 
604     return var->TsType();
605 }
606 
IsTypeBuiltinType(const Type * type) const607 bool ETSChecker::IsTypeBuiltinType(const Type *type) const
608 {
609     if (!type->IsETSObjectType()) {
610         return false;
611     }
612 
613     switch (type->AsETSObjectType()->BuiltInKind()) {
614         case ETSObjectFlags::BUILTIN_BOOLEAN:
615         case ETSObjectFlags::BUILTIN_BYTE:
616         case ETSObjectFlags::BUILTIN_SHORT:
617         case ETSObjectFlags::BUILTIN_CHAR:
618         case ETSObjectFlags::BUILTIN_INT:
619         case ETSObjectFlags::BUILTIN_LONG:
620         case ETSObjectFlags::BUILTIN_FLOAT:
621         case ETSObjectFlags::BUILTIN_DOUBLE: {
622             return true;
623         }
624         default:
625             return false;
626     }
627 }
628 
ETSBuiltinTypeAsPrimitiveType(Type * objectType)629 Type *ETSChecker::ETSBuiltinTypeAsPrimitiveType(Type *objectType)
630 {
631     if (objectType == nullptr) {
632         return nullptr;
633     }
634 
635     if (objectType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) || objectType->IsETSEnumType()) {
636         return objectType;
637     }
638 
639     if (!objectType->IsETSObjectType() ||
640         !objectType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE)) {
641         return nullptr;
642     }
643 
644     auto savedResult = Relation()->IsTrue();
645     Relation()->Result(false);
646 
647     UnboxingConverter converter = UnboxingConverter(AsETSChecker(), Relation(), objectType, objectType);
648     Relation()->Result(savedResult);
649     return converter.Result();
650 }
651 
ETSBuiltinTypeAsConditionalType(Type * const objectType)652 Type *ETSChecker::ETSBuiltinTypeAsConditionalType(Type *const objectType)
653 {
654     if (objectType->IsTypeError()) {
655         return objectType;
656     }
657 
658     if ((objectType == nullptr) || !objectType->IsConditionalExprType()) {
659         return nullptr;
660     }
661 
662     if (auto *unboxed = ETSBuiltinTypeAsPrimitiveType(objectType); unboxed != nullptr) {
663         return unboxed;
664     }
665 
666     return objectType;
667 }
668 
PrimitiveTypeAsETSBuiltinType(Type * objectType)669 Type *ETSChecker::PrimitiveTypeAsETSBuiltinType(Type *objectType)
670 {
671     if (objectType == nullptr) {
672         return nullptr;
673     }
674 
675     if (objectType->IsETSObjectType() && objectType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE)) {
676         return objectType;
677     }
678 
679     if (!objectType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
680         return nullptr;
681     }
682 
683     auto savedResult = Relation()->IsTrue();
684     Relation()->Result(false);
685 
686     BoxingConverter converter = BoxingConverter(AsETSChecker(), Relation(), objectType,
687                                                 Checker::GetGlobalTypesHolder()->GlobalIntegerBuiltinType());
688     Relation()->Result(savedResult);
689     return converter.Result();
690 }
691 
MaybePromotedBuiltinType(Type * type) const692 Type *ETSChecker::MaybePromotedBuiltinType(Type *type) const
693 {
694     return type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && !type->IsETSVoidType()
695                ? checker::BoxingConverter::ETSTypeFromSource(this, type)
696                : type;
697 }
698 
MaybePromotedBuiltinType(Type const * type) const699 Type const *ETSChecker::MaybePromotedBuiltinType(Type const *type) const
700 {
701     return type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) ? checker::BoxingConverter::ETSTypeFromSource(this, type) : type;
702 }
703 
MaybePrimitiveBuiltinType(Type * type) const704 Type *ETSChecker::MaybePrimitiveBuiltinType(Type *type) const
705 {
706     return type->IsETSObjectType() ? UnboxingConverter::GlobalTypeFromSource(this, type->AsETSObjectType()) : type;
707 }
708 
GetBoxingFlag(Type * const boxingType)709 ir::BoxingUnboxingFlags ETSChecker::GetBoxingFlag(Type *const boxingType)
710 {
711     auto typeKind = TypeKind(ETSBuiltinTypeAsPrimitiveType(boxingType));
712     switch (typeKind) {
713         case TypeFlag::ETS_BOOLEAN: {
714             return ir::BoxingUnboxingFlags::BOX_TO_BOOLEAN;
715         }
716         case TypeFlag::BYTE: {
717             return ir::BoxingUnboxingFlags::BOX_TO_BYTE;
718         }
719         case TypeFlag::CHAR: {
720             return ir::BoxingUnboxingFlags::BOX_TO_CHAR;
721         }
722         case TypeFlag::SHORT: {
723             return ir::BoxingUnboxingFlags::BOX_TO_SHORT;
724         }
725         case TypeFlag::INT: {
726             return ir::BoxingUnboxingFlags::BOX_TO_INT;
727         }
728         case TypeFlag::LONG: {
729             return ir::BoxingUnboxingFlags::BOX_TO_LONG;
730         }
731         case TypeFlag::FLOAT: {
732             return ir::BoxingUnboxingFlags::BOX_TO_FLOAT;
733         }
734         case TypeFlag::DOUBLE: {
735             return ir::BoxingUnboxingFlags::BOX_TO_DOUBLE;
736         }
737         default:
738             UNREACHABLE();
739     }
740 }
741 
GetUnboxingFlag(Type const * const unboxingType) const742 ir::BoxingUnboxingFlags ETSChecker::GetUnboxingFlag(Type const *const unboxingType) const
743 {
744     auto typeKind = TypeKind(unboxingType);
745     switch (typeKind) {
746         case TypeFlag::ETS_BOOLEAN: {
747             return ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN;
748         }
749         case TypeFlag::BYTE: {
750             return ir::BoxingUnboxingFlags::UNBOX_TO_BYTE;
751         }
752         case TypeFlag::CHAR: {
753             return ir::BoxingUnboxingFlags::UNBOX_TO_CHAR;
754         }
755         case TypeFlag::SHORT: {
756             return ir::BoxingUnboxingFlags::UNBOX_TO_SHORT;
757         }
758         case TypeFlag::INT: {
759             return ir::BoxingUnboxingFlags::UNBOX_TO_INT;
760         }
761         case TypeFlag::LONG: {
762             return ir::BoxingUnboxingFlags::UNBOX_TO_LONG;
763         }
764         case TypeFlag::FLOAT: {
765             return ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT;
766         }
767         case TypeFlag::DOUBLE: {
768             return ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE;
769         }
770         default:
771             UNREACHABLE();
772     }
773 }
774 
AddBoxingFlagToPrimitiveType(TypeRelation * relation,Type * target)775 void ETSChecker::AddBoxingFlagToPrimitiveType(TypeRelation *relation, Type *target)
776 {
777     auto boxingResult = PrimitiveTypeAsETSBuiltinType(target);
778     if ((boxingResult != nullptr) && !relation->OnlyCheckBoxingUnboxing()) {
779         relation->GetNode()->AddBoxingUnboxingFlags(GetBoxingFlag(boxingResult));
780         relation->Result(true);
781     }
782 }
783 
AddUnboxingFlagToPrimitiveType(TypeRelation * relation,Type * source,Type * self)784 void ETSChecker::AddUnboxingFlagToPrimitiveType(TypeRelation *relation, Type *source, Type *self)
785 {
786     auto unboxingResult = UnboxingConverter(this, relation, source, self).Result();
787     if ((unboxingResult != nullptr) && relation->IsTrue() && !relation->OnlyCheckBoxingUnboxing()) {
788         relation->GetNode()->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxingResult));
789     }
790 }
791 
CheckUnboxedTypeWidenable(TypeRelation * relation,Type * target,Type * self)792 void ETSChecker::CheckUnboxedTypeWidenable(TypeRelation *relation, Type *target, Type *self)
793 {
794     checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx(
795         relation, TypeRelationFlag::ONLY_CHECK_WIDENING |
796                       (relation->ApplyNarrowing() ? TypeRelationFlag::NARROWING : TypeRelationFlag::NONE));
797     // NOTE: vpukhov. handle union type
798     auto unboxedType = ETSBuiltinTypeAsPrimitiveType(target);
799     if (unboxedType == nullptr) {
800         return;
801     }
802     NarrowingWideningConverter(this, relation, unboxedType, self);
803     if (!relation->IsTrue()) {
804         relation->Result(relation->IsAssignableTo(self, unboxedType));
805     }
806 }
807 
CheckUnboxedTypesAssignable(TypeRelation * relation,Type * source,Type * target)808 void ETSChecker::CheckUnboxedTypesAssignable(TypeRelation *relation, Type *source, Type *target)
809 {
810     auto *unboxedSourceType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(source);
811     auto *unboxedTargetType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(target);
812     if (unboxedSourceType == nullptr || unboxedTargetType == nullptr) {
813         return;
814     }
815     relation->IsAssignableTo(unboxedSourceType, unboxedTargetType);
816     if (relation->IsTrue()) {
817         relation->GetNode()->AddBoxingUnboxingFlags(
818             relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(unboxedSourceType));
819     }
820 }
821 
CheckBoxedSourceTypeAssignable(TypeRelation * relation,Type * source,Type * target)822 void ETSChecker::CheckBoxedSourceTypeAssignable(TypeRelation *relation, Type *source, Type *target)
823 {
824     ASSERT(relation != nullptr);
825     checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx(
826         relation, (relation->ApplyWidening() ? TypeRelationFlag::WIDENING : TypeRelationFlag::NONE) |
827                       (relation->ApplyNarrowing() ? TypeRelationFlag::NARROWING : TypeRelationFlag::NONE) |
828                       (relation->OnlyCheckBoxingUnboxing() ? TypeRelationFlag::ONLY_CHECK_BOXING_UNBOXING
829                                                            : TypeRelationFlag::NONE));
830 
831     if (source->IsETSEnumType()) {
832         if (target->IsETSObjectType() && target->AsETSObjectType()->IsGlobalETSObjectType()) {
833             relation->Result(true);
834             return;
835         }
836     }
837 
838     auto *boxedSourceType = relation->GetChecker()->AsETSChecker()->PrimitiveTypeAsETSBuiltinType(source);
839     if (boxedSourceType == nullptr) {
840         return;
841     }
842     ASSERT(target != nullptr);
843     // Do not box primitive in case of cast to dynamic types
844     if (target->IsETSDynamicType()) {
845         return;
846     }
847     relation->IsAssignableTo(boxedSourceType, target);
848     if (relation->IsTrue()) {
849         AddBoxingFlagToPrimitiveType(relation, boxedSourceType);
850     } else {
851         auto unboxedTargetType = ETSBuiltinTypeAsPrimitiveType(target);
852         if (unboxedTargetType == nullptr) {
853             return;
854         }
855         NarrowingWideningConverter(this, relation, unboxedTargetType, source);
856         if (relation->IsTrue()) {
857             AddBoxingFlagToPrimitiveType(relation, target);
858         }
859     }
860 }
861 
CheckUnboxedSourceTypeWithWideningAssignable(TypeRelation * relation,Type * source,Type * target)862 void ETSChecker::CheckUnboxedSourceTypeWithWideningAssignable(TypeRelation *relation, Type *source, Type *target)
863 {
864     auto *unboxedSourceType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(source);
865     if (unboxedSourceType == nullptr) {
866         return;
867     }
868     relation->IsAssignableTo(unboxedSourceType, target);
869     if (!relation->IsTrue() && relation->ApplyWidening()) {
870         relation->GetChecker()->AsETSChecker()->CheckUnboxedTypeWidenable(relation, target, unboxedSourceType);
871     }
872     if (!relation->OnlyCheckBoxingUnboxing()) {
873         relation->GetNode()->AddBoxingUnboxingFlags(
874             relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(unboxedSourceType));
875     }
876 }
877 
DerefETSTypeReference(ir::AstNode * node)878 static ir::AstNode *DerefETSTypeReference(ir::AstNode *node)
879 {
880     ASSERT(node->IsETSTypeReference());
881     do {
882         auto *name = node->AsETSTypeReference()->Part()->Name();
883         ASSERT(name->IsIdentifier());
884         auto *var = name->AsIdentifier()->Variable();
885         ASSERT(var != nullptr);
886         auto *declNode = var->Declaration()->Node();
887         if (!declNode->IsTSTypeAliasDeclaration()) {
888             return declNode;
889         }
890         node = declNode->AsTSTypeAliasDeclaration()->TypeAnnotation();
891     } while (node->IsETSTypeReference());
892     return node;
893 }
894 
CheckLambdaAssignable(ir::Expression * param,ir::ScriptFunction * lambda)895 bool ETSChecker::CheckLambdaAssignable(ir::Expression *param, ir::ScriptFunction *lambda)
896 {
897     ASSERT(param->IsETSParameterExpression());
898     ir::AstNode *typeAnn = param->AsETSParameterExpression()->Ident()->TypeAnnotation();
899     if (typeAnn->IsETSTypeReference()) {
900         typeAnn = DerefETSTypeReference(typeAnn);
901     }
902     if (!typeAnn->IsETSFunctionType()) {
903         if (typeAnn->IsETSUnionType()) {
904             return CheckLambdaAssignableUnion(typeAnn, lambda);
905         }
906 
907         return false;
908     }
909     ir::ETSFunctionType *calleeType = typeAnn->AsETSFunctionType();
910     return lambda->Params().size() == calleeType->Params().size();
911 }
912 
CheckLambdaInvocable(ir::AstNode * typeAnnotation,ir::ArrowFunctionExpression * const arrowFuncExpr,Type * const parameterType,TypeRelationFlag flags)913 bool ETSChecker::CheckLambdaInvocable(ir::AstNode *typeAnnotation, ir::ArrowFunctionExpression *const arrowFuncExpr,
914                                       Type *const parameterType, TypeRelationFlag flags)
915 {
916     if (typeAnnotation->IsETSTypeReference()) {
917         typeAnnotation = DerefETSTypeReference(typeAnnotation);
918     }
919 
920     if (!typeAnnotation->IsETSFunctionType()) {
921         return false;
922     }
923 
924     flags |= TypeRelationFlag::NO_THROW;
925     ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
926 
927     InferTypesForLambda(lambda, typeAnnotation->AsETSFunctionType());
928     Type *const argumentType = arrowFuncExpr->Check(this);
929 
930     checker::InvocationContext invocationCtx(Relation(), arrowFuncExpr, argumentType, parameterType,
931                                              arrowFuncExpr->Start(), {}, flags);
932     return invocationCtx.IsInvocable();
933 }
934 
CheckLambdaTypeAnnotation(ir::AstNode * typeAnnotation,ir::ArrowFunctionExpression * const arrowFuncExpr,Type * const parameterType,TypeRelationFlag flags)935 bool ETSChecker::CheckLambdaTypeAnnotation(ir::AstNode *typeAnnotation,
936                                            ir::ArrowFunctionExpression *const arrowFuncExpr, Type *const parameterType,
937                                            TypeRelationFlag flags)
938 {
939     //  process `single` type as usual.
940     if (!typeAnnotation->IsETSUnionType()) {
941         return CheckLambdaInvocable(typeAnnotation, arrowFuncExpr, parameterType, flags);
942     }
943 
944     // Preserve actual lambda types
945     ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
946     ArenaVector<ir::TypeNode *> lambdaParamTypes {Allocator()->Adapter()};
947     for (auto *const lambdaParam : lambda->Params()) {
948         lambdaParamTypes.emplace_back(lambdaParam->AsETSParameterExpression()->Ident()->TypeAnnotation());
949     }
950     auto *const lambdaReturnTypeAnnotation = lambda->ReturnTypeAnnotation();
951 
952     for (ir::AstNode *typeNode : typeAnnotation->AsETSUnionType()->Types()) {
953         if (CheckLambdaInvocable(typeNode, arrowFuncExpr, parameterType, flags)) {
954             return true;
955         }
956 
957         //  Restore inferring lambda types:
958         for (std::size_t i = 0U; i < lambda->Params().size(); ++i) {
959             auto *const lambdaParamTypeAnnotation = lambdaParamTypes[i];
960             if (lambdaParamTypeAnnotation == nullptr) {
961                 lambda->Params()[i]->AsETSParameterExpression()->Ident()->SetTsTypeAnnotation(nullptr);
962             }
963         }
964         if (lambdaReturnTypeAnnotation == nullptr) {
965             lambda->SetReturnTypeAnnotation(nullptr);
966         }
967     }
968 
969     return false;
970 }
971 
TypeInference(Signature * signature,const ArenaVector<ir::Expression * > & arguments,TypeRelationFlag flags)972 bool ETSChecker::TypeInference(Signature *signature, const ArenaVector<ir::Expression *> &arguments,
973                                TypeRelationFlag flags)
974 {
975     bool invocable = true;
976     auto const argumentCount = arguments.size();
977     auto const parameterCount = signature->Params().size();
978     auto const count = std::min(parameterCount, argumentCount);
979 
980     for (size_t index = 0U; index < count; ++index) {
981         auto const &argument = arguments[index];
982         if (!argument->IsArrowFunctionExpression()) {
983             continue;
984         }
985 
986         if (index == arguments.size() - 1 && (flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0) {
987             continue;
988         }
989 
990         auto *const arrowFuncExpr = argument->AsArrowFunctionExpression();
991         ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
992         if (!NeedTypeInference(lambda)) {
993             continue;
994         }
995 
996         auto const *const param = signature->Function()->Params()[index]->AsETSParameterExpression()->Ident();
997         ir::AstNode *typeAnn = param->TypeAnnotation();
998         Type *const parameterType = signature->Params()[index]->TsType();
999 
1000         bool const rc = CheckLambdaTypeAnnotation(typeAnn, arrowFuncExpr, parameterType, flags);
1001         if (!rc && (flags & TypeRelationFlag::NO_THROW) == 0) {
1002             Type *const argumentType = arrowFuncExpr->Check(this);
1003             const Type *targetType = TryGettingFunctionTypeFromInvokeFunction(parameterType);
1004             const std::initializer_list<TypeErrorMessageElement> list = {
1005                 "Type '", argumentType, "' is not compatible with type '", targetType, "' at index ", index + 1};
1006             ThrowTypeError(list, arrowFuncExpr->Start());
1007         }
1008 
1009         invocable &= rc;
1010     }
1011 
1012     return invocable;
1013 }
1014 
ExtensionETSFunctionType(checker::Type * type)1015 bool ETSChecker::ExtensionETSFunctionType(checker::Type *type)
1016 {
1017     if (!type->IsETSFunctionType()) {
1018         return false;
1019     }
1020 
1021     for (auto *signature : type->AsETSFunctionType()->CallSignatures()) {
1022         if (signature->Function()->IsExtensionMethod()) {
1023             return true;
1024         }
1025     }
1026 
1027     return false;
1028 }
1029 
CheckExceptionClauseType(const std::vector<checker::ETSObjectType * > & exceptions,ir::CatchClause * catchClause,checker::Type * clauseType)1030 void ETSChecker::CheckExceptionClauseType(const std::vector<checker::ETSObjectType *> &exceptions,
1031                                           ir::CatchClause *catchClause, checker::Type *clauseType)
1032 {
1033     for (auto *exception : exceptions) {
1034         this->Relation()->IsIdenticalTo(clauseType, exception);
1035         if (this->Relation()->IsTrue()) {
1036             this->ThrowTypeError("Redeclaration of exception type", catchClause->Start());
1037         }
1038     }
1039 }
1040 }  // namespace ark::es2panda::checker
1041