1 /**
2 * Copyright (c) 2021-2025 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 "typeRelation.h"
17
18 #include "checker/checker.h"
19 #include "checker/ETSchecker.h"
20 #include "checker/types/ts/indexInfo.h"
21 #include "checker/types/signature.h"
22
23 namespace ark::es2panda::checker {
Allocator()24 ArenaAllocator *TypeRelation::Allocator()
25 {
26 return checker_->ProgramAllocator();
27 }
28
CacheLookup(const Type * source,const Type * target,const RelationHolder & holder,RelationType type) const29 RelationResult TypeRelation::CacheLookup(const Type *source, const Type *target, const RelationHolder &holder,
30 RelationType type) const
31 {
32 if (result_ == RelationResult::CACHE_MISS) {
33 return result_;
34 }
35
36 ES2PANDA_ASSERT(source != nullptr);
37 ES2PANDA_ASSERT(target != nullptr);
38
39 RelationKey relationKey {source->Id(), target->Id()};
40 auto res = holder.cached.find(relationKey);
41 if (res == holder.cached.end()) {
42 return RelationResult::CACHE_MISS;
43 }
44
45 if (res->second.type >= type && res->second.result == RelationResult::TRUE) {
46 return RelationResult::TRUE;
47 }
48
49 if (res->second.type <= type && res->second.result == RelationResult::FALSE) {
50 return RelationResult::FALSE;
51 }
52
53 return RelationResult::CACHE_MISS;
54 }
55
IsIdenticalTo(Type * source,Type * target)56 bool TypeRelation::IsIdenticalTo(Type *source, Type *target)
57 {
58 if (source == nullptr || target == nullptr) {
59 return Result(false);
60 }
61
62 if (source == target) {
63 return Result(true);
64 }
65
66 result_ = CacheLookup(source, target, checker_->IdenticalResults(), RelationType::IDENTICAL);
67 if (result_ == RelationResult::CACHE_MISS) {
68 checker_->ResolveStructuredTypeMembers(source);
69 checker_->ResolveStructuredTypeMembers(target);
70 result_ = RelationResult::FALSE;
71 target->Identical(this, source);
72 checker_->IdenticalResults().cached.insert({{source->Id(), target->Id()}, {result_, RelationType::IDENTICAL}});
73 }
74
75 return IsTrue();
76 }
77
SignatureIsIdenticalTo(Signature * source,Signature * target)78 bool TypeRelation::SignatureIsIdenticalTo(Signature *source, Signature *target)
79 {
80 if (source == target) {
81 return Result(true);
82 }
83
84 Result(false);
85 if (target->IsSubtypeOf(this, source), IsTrue()) {
86 if (source->IsSubtypeOf(this, target), IsTrue()) {
87 return Result(true);
88 }
89 }
90 return Result(false);
91 }
92
SignatureIsSupertypeOf(Signature * super,Signature * sub)93 bool TypeRelation::SignatureIsSupertypeOf(Signature *super, Signature *sub)
94 {
95 if (super == sub) {
96 return Result(true);
97 }
98
99 Result(false);
100 sub->IsSubtypeOf(this, super);
101 return IsTrue();
102 }
103
IsIdenticalTo(IndexInfo * source,IndexInfo * target)104 bool TypeRelation::IsIdenticalTo(IndexInfo *source, IndexInfo *target)
105 {
106 if (source == target) {
107 Result(true);
108 return true;
109 }
110
111 result_ = RelationResult::FALSE;
112 target->Identical(this, source);
113
114 return result_ == RelationResult::TRUE;
115 }
116
117 // NOTE: applyNarrowing -> flag
IsAssignableTo(Type * source,Type * target)118 bool TypeRelation::IsAssignableTo(Type *source, Type *target)
119 {
120 if (source == target) {
121 return Result(true);
122 }
123
124 ES2PANDA_ASSERT(source);
125 ES2PANDA_ASSERT(target);
126 result_ = CacheLookup(source, target, checker_->AssignableResults(), RelationType::ASSIGNABLE);
127 if (result_ == RelationResult::CACHE_MISS) {
128 // NOTE: we support assigning T to Readonly<T>, but do not support assigning Readonly<T> to T
129 // more details in spec
130 ES2PANDA_ASSERT(source != nullptr);
131 ES2PANDA_ASSERT(target != nullptr);
132 if (source->HasTypeFlag(TypeFlag::READONLY) && !target->HasTypeFlag(TypeFlag::READONLY)) {
133 result_ = RelationResult::FALSE;
134 }
135
136 if (result_ != RelationResult::FALSE && IsIdenticalTo(source, target)) {
137 return true;
138 }
139
140 if (result_ == RelationResult::ERROR) {
141 // Return early to prevent logging same errors again
142 return false;
143 }
144
145 result_ = RelationResult::FALSE;
146 if (!source->AssignmentSource(this, target)) {
147 target->AssignmentTarget(this, source);
148 }
149
150 if (flags_ == TypeRelationFlag::NONE) {
151 checker_->AssignableResults().cached.insert(
152 {{source->Id(), target->Id()}, {result_, RelationType::ASSIGNABLE}});
153 }
154 }
155
156 return result_ == RelationResult::TRUE;
157 }
158
IsComparableTo(Type * source,Type * target)159 bool TypeRelation::IsComparableTo(Type *source, Type *target)
160 {
161 result_ = CacheLookup(source, target, checker_->ComparableResults(), RelationType::COMPARABLE);
162
163 // NOTE: vpukhov. reimplement dynamic comparison and remove this check
164 ES2PANDA_ASSERT(source != nullptr);
165 ES2PANDA_ASSERT(target != nullptr);
166 if (source->IsETSDynamicType() || target->IsETSDynamicType()) {
167 if (!(source->IsETSDynamicType() && target->IsETSDynamicType())) {
168 return false;
169 }
170 }
171
172 if (result_ == RelationResult::CACHE_MISS) {
173 if (IsAssignableTo(source, target)) {
174 return true;
175 }
176
177 result_ = RelationResult::FALSE;
178 target->Compare(this, source);
179 checker_->ComparableResults().cached.insert(
180 {{source->Id(), target->Id()}, {result_, RelationType::COMPARABLE}});
181 }
182
183 return result_ == RelationResult::TRUE;
184 }
185
IsCastableTo(Type * const source,Type * const target)186 bool TypeRelation::IsCastableTo(Type *const source, Type *const target)
187 {
188 result_ = CacheLookup(source, target, checker_->UncheckedCastableResult(), RelationType::UNCHECKED_CASTABLE);
189 if (result_ == RelationResult::CACHE_MISS) {
190 result_ = RelationResult::FALSE;
191 flags_ |= TypeRelationFlag::UNCHECKED_CAST;
192
193 source->Cast(this, target);
194 if (!IsTrue()) {
195 target->CastTarget(this, source);
196 }
197
198 if (!IsTrue()) {
199 return false;
200 }
201
202 // NOTE: Can't cache if the node has BoxingUnboxingFlags. These flags should be stored and restored on the node
203 // on cache hit.
204 if (UncheckedCast() && node_->GetBoxingUnboxingFlags() == ir::BoxingUnboxingFlags::NONE &&
205 !node_->HasAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF)) {
206 checker_->UncheckedCastableResult().cached.insert(
207 {{source->Id(), target->Id()}, {result_, RelationType::UNCHECKED_CASTABLE}});
208 }
209
210 return true;
211 }
212
213 return result_ == RelationResult::TRUE;
214 }
215
IsLegalBoxedPrimitiveConversion(Type * target,Type * source)216 bool TypeRelation::IsLegalBoxedPrimitiveConversion(Type *target, Type *source)
217 {
218 if (!target->IsETSReferenceType() || !source->IsETSReferenceType()) {
219 return false;
220 }
221 if (!target->IsETSObjectType() || !source->IsETSObjectType()) {
222 return false;
223 }
224 if (!target->AsETSObjectType()->IsBoxedPrimitive() || !source->AsETSObjectType()->IsBoxedPrimitive()) {
225 return false;
226 }
227
228 ETSChecker *checker = this->GetChecker()->AsETSChecker();
229
230 Type *targetUnboxedType = checker->MaybeUnboxType(target);
231 Type *sourceUnboxedType = checker->MaybeUnboxType(source);
232
233 if (targetUnboxedType == nullptr || sourceUnboxedType == nullptr) {
234 return false;
235 }
236 if (!targetUnboxedType->IsETSPrimitiveType() || !sourceUnboxedType->IsETSPrimitiveType()) {
237 return false;
238 }
239
240 return this->Result(this->IsAssignableTo(sourceUnboxedType, targetUnboxedType));
241 }
242
IsSupertypeOf(Type * super,Type * sub)243 bool TypeRelation::IsSupertypeOf(Type *super, Type *sub)
244 {
245 if (super == sub) {
246 return Result(true);
247 }
248
249 if (sub == nullptr) {
250 return false;
251 }
252
253 result_ = CacheLookup(super, sub, checker_->SupertypeResults(), RelationType::SUPERTYPE);
254 if (result_ == RelationResult::CACHE_MISS) {
255 if (IsIdenticalTo(super, sub)) {
256 return true;
257 }
258
259 result_ = RelationResult::FALSE;
260 if (super->IsSupertypeOf(this, sub), !IsTrue()) {
261 sub->IsSubtypeOf(this, super);
262 }
263
264 if (flags_ == TypeRelationFlag::NONE) {
265 checker_->SupertypeResults().cached.insert({{super->Id(), sub->Id()}, {result_, RelationType::SUPERTYPE}});
266 }
267 }
268
269 return result_ == RelationResult::TRUE;
270 }
271
CheckVarianceRecursively(Type * type,VarianceFlag varianceFlag)272 bool TypeRelation::CheckVarianceRecursively(Type *type, VarianceFlag varianceFlag)
273 {
274 type->CheckVarianceRecursively(this, varianceFlag);
275 return result_ == RelationResult::TRUE;
276 }
277
TransferVariant(VarianceFlag variance,VarianceFlag posVariance)278 VarianceFlag TypeRelation::TransferVariant(VarianceFlag variance, VarianceFlag posVariance)
279 {
280 if (posVariance == VarianceFlag::INVARIANT || variance == VarianceFlag::INVARIANT) {
281 return VarianceFlag::INVARIANT;
282 }
283
284 if (posVariance == VarianceFlag::COVARIANT) {
285 return variance;
286 }
287
288 return variance == VarianceFlag::CONTRAVARIANT ? VarianceFlag::COVARIANT : VarianceFlag::CONTRAVARIANT;
289 }
290
RaiseError(const diagnostic::DiagnosticKind & kind,const lexer::SourcePosition & loc) const291 void TypeRelation::RaiseError(const diagnostic::DiagnosticKind &kind, const lexer::SourcePosition &loc) const
292 {
293 RaiseError(kind, {}, loc);
294 }
295
RaiseError(const diagnostic::DiagnosticKind & kind,const util::DiagnosticMessageParams & list,const lexer::SourcePosition & loc) const296 void TypeRelation::RaiseError(const diagnostic::DiagnosticKind &kind, const util::DiagnosticMessageParams &list,
297 const lexer::SourcePosition &loc) const
298 {
299 checker_->LogError(kind, list, loc);
300 }
301 } // namespace ark::es2panda::checker
302