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 "typeRelationContext.h"
17 #include "boxingConverter.h"
18 #include "varbinder/scope.h"
19 #include "varbinder/variable.h"
20 #include "varbinder/declaration.h"
21 #include "checker/types/ets/etsUnionType.h"
22 #include "ir/expressions/arrayExpression.h"
23 #include "ir/ts/tsTypeParameter.h"
24
25 namespace ark::es2panda::checker {
ValidateArrayTypeInitializerByElement(TypeRelation * relation,ir::ArrayExpression * node,ETSArrayType * target)26 void AssignmentContext::ValidateArrayTypeInitializerByElement(TypeRelation *relation, ir::ArrayExpression *node,
27 ETSArrayType *target)
28 {
29 if (target->IsETSTupleType()) {
30 return;
31 }
32
33 for (uint32_t index = 0; index < node->Elements().size(); index++) {
34 ir::Expression *currentArrayElem = node->Elements()[index];
35 auto *const currentArrayElementType = currentArrayElem->Check(relation->GetChecker()->AsETSChecker());
36
37 AssignmentContext(relation, currentArrayElem, currentArrayElem->Check(relation->GetChecker()->AsETSChecker()),
38 target->ElementType(), currentArrayElem->Start(),
39 {"Array element at index ", index, " with type '", currentArrayElementType,
40 "' is not compatible with the target array element type '", target->ElementType(), "'"});
41 }
42 }
43
ValidateTypeArguments(ETSObjectType * type,ir::TSTypeParameterInstantiation * typeArgs,const lexer::SourcePosition & pos)44 bool InstantiationContext::ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs,
45 const lexer::SourcePosition &pos)
46 {
47 if (checker_->HasStatus(CheckerStatus::IN_INSTANCEOF_CONTEXT)) {
48 if (typeArgs != nullptr) {
49 checker_->ReportWarning(
50 {"Type parameter is erased from type '", type->Name(), "' when used in instanceof expression."}, pos);
51 }
52 result_ = type;
53 return true;
54 }
55 checker_->CheckNumberOfTypeArguments(type, typeArgs, pos);
56 if (type->TypeArguments().empty()) {
57 result_ = type;
58 return true;
59 }
60 return false;
61 }
62
InstantiateType(ETSObjectType * type,ir::TSTypeParameterInstantiation * typeArgs)63 void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs)
64 {
65 ArenaVector<Type *> typeArgTypes(checker_->Allocator()->Adapter());
66 typeArgTypes.reserve(type->TypeArguments().size());
67
68 if (typeArgs != nullptr) {
69 for (auto *const it : typeArgs->Params()) {
70 auto *paramType = it->GetType(checker_);
71
72 if (paramType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
73 checker_->Relation()->SetNode(it);
74
75 auto *const boxedTypeArg = checker_->PrimitiveTypeAsETSBuiltinType(paramType);
76 ASSERT(boxedTypeArg);
77 paramType = boxedTypeArg->Instantiate(checker_->Allocator(), checker_->Relation(),
78 checker_->GetGlobalTypesHolder());
79 }
80
81 typeArgTypes.push_back(paramType);
82 }
83 }
84
85 while (typeArgTypes.size() < type->TypeArguments().size()) {
86 auto *defaultType = type->TypeArguments().at(typeArgTypes.size())->AsETSTypeParameter()->GetDefaultType();
87 typeArgTypes.push_back(defaultType);
88 }
89
90 auto pos = (typeArgs == nullptr) ? lexer::SourcePosition() : typeArgs->Range().start;
91 InstantiateType(type, std::move(typeArgTypes), pos);
92 result_->AddObjectFlag(ETSObjectFlags::NO_OPTS);
93 }
94
CheckInstantiationConstraints(ETSChecker * checker,ArenaVector<Type * > const & typeParams,const Substitution * substitution,lexer::SourcePosition pos)95 static void CheckInstantiationConstraints(ETSChecker *checker, ArenaVector<Type *> const &typeParams,
96 const Substitution *substitution, lexer::SourcePosition pos)
97 {
98 auto relation = checker->Relation();
99
100 for (auto type : typeParams) {
101 if (!type->IsETSTypeParameter()) {
102 continue;
103 }
104 auto typeParam = type->AsETSTypeParameter();
105 auto typeArg = typeParam->Substitute(relation, substitution);
106 if (typeArg->IsWildcardType()) {
107 continue;
108 }
109 ASSERT(typeArg->IsETSReferenceType() || typeArg->IsETSVoidType());
110 auto constraint = typeParam->GetConstraintType()->Substitute(relation, substitution);
111 if (!relation->IsAssignableTo(typeArg, constraint)) {
112 checker->ThrowTypeError( // NOTE(vpukhov): refine message
113 {"Type ", typeArg, " is not assignable to", " constraint type ", constraint}, pos);
114 }
115 }
116 }
117
TryCheckConstraints()118 void ConstraintCheckScope::TryCheckConstraints()
119 {
120 if (Unlock()) {
121 auto &records = checker_->PendingConstraintCheckRecords();
122 for (auto const &[typeParams, substitution, pos] : records) {
123 CheckInstantiationConstraints(checker_, *typeParams, substitution, pos);
124 }
125 records.clear();
126 }
127 }
128
InstantiateType(ETSObjectType * type,ArenaVector<Type * > && typeArgTypes,const lexer::SourcePosition & pos)129 void InstantiationContext::InstantiateType(ETSObjectType *type, ArenaVector<Type *> &&typeArgTypes,
130 const lexer::SourcePosition &pos)
131 {
132 util::StringView hash = checker_->GetHashFromTypeArguments(typeArgTypes);
133 auto const &typeParams = type->TypeArguments();
134
135 while (typeArgTypes.size() < typeParams.size()) {
136 typeArgTypes.push_back(typeParams.at(typeArgTypes.size()));
137 }
138
139 auto *substitution = checker_->NewSubstitution();
140 for (size_t idx = 0; idx < typeParams.size(); idx++) {
141 if (!typeParams[idx]->IsETSTypeParameter()) {
142 continue;
143 }
144 ETSChecker::EmplaceSubstituted(substitution, typeParams[idx]->AsETSTypeParameter(), typeArgTypes[idx]);
145 }
146
147 ConstraintCheckScope ctScope(checker_);
148 if (!checker_->Relation()->NoThrowGenericTypeAlias()) {
149 checker_->PendingConstraintCheckRecords().push_back({&typeParams, substitution, pos});
150 }
151
152 result_ = type->Substitute(checker_->Relation(), substitution)->AsETSObjectType();
153 type->GetInstantiationMap().try_emplace(hash, result_);
154 result_->AddTypeFlag(TypeFlag::GENERIC);
155
156 ctScope.TryCheckConstraints();
157 }
158
159 } // namespace ark::es2panda::checker
160