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