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 panda::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 AssignmentContext(relation, currentArrayElem, currentArrayElem->Check(relation->GetChecker()->AsETSChecker()),
36 target->ElementType(), currentArrayElem->Start(),
37 {"Array element at index ", index, " is not compatible with the target array element type."});
38 }
39 }
40
ValidateTypeArguments(ETSObjectType * type,ir::TSTypeParameterInstantiation * typeArgs,const lexer::SourcePosition & pos)41 bool InstantiationContext::ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs,
42 const lexer::SourcePosition &pos)
43 {
44 checker_->CheckNumberOfTypeArguments(type, typeArgs, pos);
45 if (type->TypeArguments().empty()) {
46 result_ = type;
47 return true;
48 }
49
50 /*
51 The first loop is to create a substitution of typeParams & typeArgs.
52 so that we can replace the typeParams in constaints by the right type.
53 e.g:
54 class X <K extends Comparable<T>,T> {}
55 function main(){
56 const myCharClass = new X<Char,String>();
57 }
58 In the case above, the constraintsSubstitution should store "K->Char" and "T->String".
59 And in the second loop, we use this substitution to replace typeParams in constraints.
60 In this case, we will check "Comparable<String>" with "Char", since "Char" doesn't
61 extends "Comparable<String>", we will get an error here.
62 */
63
64 auto const isDefaulted = [typeArgs](size_t idx) { return typeArgs == nullptr || idx >= typeArgs->Params().size(); };
65
66 auto const getTypes = [this, &typeArgs, type, isDefaulted](size_t idx) -> std::pair<ETSTypeParameter *, Type *> {
67 auto *typeParam = type->TypeArguments().at(idx)->AsETSTypeParameter();
68 return {typeParam, isDefaulted(idx)
69 ? typeParam->GetDefaultType()
70 : checker_->MaybePromotedBuiltinType(typeArgs->Params().at(idx)->GetType(checker_))};
71 };
72
73 auto *const substitution = checker_->NewSubstitution();
74
75 for (size_t idx = 0; idx < type->TypeArguments().size(); ++idx) {
76 auto const [typeParam, typeArg] = getTypes(idx);
77 checker_->CheckValidGenericTypeParameter(typeArg, pos);
78 typeArg->Substitute(checker_->Relation(), substitution);
79 ETSChecker::EmplaceSubstituted(substitution, typeParam, typeArg);
80 }
81
82 for (size_t idx = 0; idx < type->TypeArguments().size(); ++idx) {
83 auto const [typeParam, typeArg] = getTypes(idx);
84 if (typeParam->GetConstraintType() == nullptr) {
85 continue;
86 }
87 auto *const constraint = typeParam->GetConstraintType()->Substitute(checker_->Relation(), substitution);
88
89 if (!ValidateTypeArg(constraint, typeArg) && typeArgs != nullptr &&
90 !checker_->Relation()->NoThrowGenericTypeAlias()) {
91 checker_->ThrowTypeError({"Type '", typeArg, "' is not assignable to constraint type '", constraint, "'."},
92 isDefaulted(idx) ? pos : typeArgs->Params().at(idx)->Start());
93 }
94 }
95
96 return false;
97 }
98
ValidateTypeArg(Type * constraintType,Type * typeArg)99 bool InstantiationContext::ValidateTypeArg(Type *constraintType, Type *typeArg)
100 {
101 // NOTE: #14993 enforce ETSChecker::IsReferenceType
102 if (typeArg->IsWildcardType()) {
103 return true;
104 }
105 return checker_->Relation()->IsAssignableTo(typeArg, constraintType);
106 }
107
InstantiateType(ETSObjectType * type,ir::TSTypeParameterInstantiation * typeArgs)108 void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs)
109 {
110 ArenaVector<Type *> typeArgTypes(checker_->Allocator()->Adapter());
111 typeArgTypes.reserve(type->TypeArguments().size());
112
113 auto flags = ETSObjectFlags::NO_OPTS;
114
115 if (typeArgs != nullptr) {
116 for (auto *const it : typeArgs->Params()) {
117 auto *paramType = checker_->GetTypeFromTypeAnnotation(it);
118
119 if (paramType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
120 checker_->Relation()->SetNode(it);
121 auto *const boxedTypeArg = checker_->PrimitiveTypeAsETSBuiltinType(paramType);
122 ASSERT(boxedTypeArg);
123 paramType = boxedTypeArg->Instantiate(checker_->Allocator(), checker_->Relation(),
124 checker_->GetGlobalTypesHolder());
125 }
126
127 typeArgTypes.push_back(paramType);
128 }
129 }
130
131 while (typeArgTypes.size() < type->TypeArguments().size()) {
132 typeArgTypes.push_back(type->TypeArguments().at(typeArgTypes.size()));
133 }
134
135 InstantiateType(type, typeArgTypes, (typeArgs == nullptr) ? lexer::SourcePosition() : typeArgs->Range().start);
136 result_->AddObjectFlag(flags);
137 }
138
InstantiateType(ETSObjectType * type,ArenaVector<Type * > & typeArgTypes,const lexer::SourcePosition & pos)139 void InstantiationContext::InstantiateType(ETSObjectType *type, ArenaVector<Type *> &typeArgTypes,
140 const lexer::SourcePosition &pos)
141 {
142 util::StringView hash = checker_->GetHashFromTypeArguments(typeArgTypes);
143 auto const &typeParams = type->TypeArguments();
144
145 while (typeArgTypes.size() < typeParams.size()) {
146 typeArgTypes.push_back(typeParams.at(typeArgTypes.size()));
147 }
148
149 auto *substitution = checker_->NewSubstitution();
150 auto *constraintsSubstitution = checker_->NewSubstitution();
151 for (size_t ix = 0; ix < typeParams.size(); ix++) {
152 if (!typeParams[ix]->IsETSTypeParameter()) {
153 continue;
154 }
155 ETSChecker::EmplaceSubstituted(constraintsSubstitution, typeParams[ix]->AsETSTypeParameter(), typeArgTypes[ix]);
156 }
157 for (size_t ix = 0; ix < typeParams.size(); ix++) {
158 auto *typeParam = typeParams[ix];
159 if (!typeParam->IsETSTypeParameter()) {
160 continue;
161 }
162 if (!checker_->IsCompatibleTypeArgument(typeParam->AsETSTypeParameter(), typeArgTypes[ix],
163 constraintsSubstitution) &&
164 !checker_->Relation()->NoThrowGenericTypeAlias()) {
165 checker_->ThrowTypeError(
166 {"Type ", typeArgTypes[ix], " is not assignable to", " type parameter ", typeParams[ix]}, pos);
167 }
168 ETSChecker::EmplaceSubstituted(substitution, typeParam->AsETSTypeParameter(), typeArgTypes[ix]);
169 }
170 result_ = type->Substitute(checker_->Relation(), substitution)->AsETSObjectType();
171
172 type->GetInstantiationMap().try_emplace(hash, result_);
173 result_->AddTypeFlag(TypeFlag::GENERIC);
174 }
175 } // namespace panda::es2panda::checker
176