• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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