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 "typeRelation.h"
17
18 #include "checker/checker.h"
19 #include "checker/types/ts/indexInfo.h"
20 #include "checker/types/signature.h"
21
22 namespace ark::es2panda::checker {
Allocator()23 ArenaAllocator *TypeRelation::Allocator()
24 {
25 return checker_->Allocator();
26 }
27
CacheLookup(const Type * source,const Type * target,const RelationHolder & holder,RelationType type) const28 RelationResult TypeRelation::CacheLookup(const Type *source, const Type *target, const RelationHolder &holder,
29 RelationType type) const
30 {
31 if (result_ == RelationResult::CACHE_MISS) {
32 return result_;
33 }
34
35 RelationKey relationKey {source->Id(), target->Id()};
36 auto res = holder.cached.find(relationKey);
37 if (res == holder.cached.end()) {
38 return RelationResult::CACHE_MISS;
39 }
40
41 if (res->second.type >= type && res->second.result == RelationResult::TRUE) {
42 return RelationResult::TRUE;
43 }
44
45 if (res->second.type <= type && res->second.result == RelationResult::FALSE) {
46 return RelationResult::FALSE;
47 }
48
49 return RelationResult::CACHE_MISS;
50 }
51
IsIdenticalTo(Type * source,Type * target)52 bool TypeRelation::IsIdenticalTo(Type *source, Type *target)
53 {
54 if (source == nullptr || target == nullptr) {
55 return Result(false);
56 }
57
58 if (source == target) {
59 return Result(true);
60 }
61
62 result_ = CacheLookup(source, target, checker_->IdenticalResults(), RelationType::IDENTICAL);
63 if (result_ == RelationResult::CACHE_MISS) {
64 checker_->ResolveStructuredTypeMembers(source);
65 checker_->ResolveStructuredTypeMembers(target);
66 result_ = RelationResult::FALSE;
67 target->Identical(this, source);
68 checker_->IdenticalResults().cached.insert({{source->Id(), target->Id()}, {result_, RelationType::IDENTICAL}});
69 }
70
71 return IsTrue();
72 }
73
IsCompatibleTo(Signature * source,Signature * target)74 bool TypeRelation::IsCompatibleTo(Signature *source, Signature *target)
75 {
76 if (source == target) {
77 Result(true);
78 return true;
79 }
80
81 result_ = RelationResult::FALSE;
82 target->Compatible(this, source);
83
84 return result_ == RelationResult::TRUE;
85 }
86
IsIdenticalTo(IndexInfo * source,IndexInfo * target)87 bool TypeRelation::IsIdenticalTo(IndexInfo *source, IndexInfo *target)
88 {
89 if (source == target) {
90 Result(true);
91 return true;
92 }
93
94 result_ = RelationResult::FALSE;
95 target->Identical(this, source);
96
97 return result_ == RelationResult::TRUE;
98 }
99
100 // NOTE: applyNarrowing -> flag
IsAssignableTo(Type * source,Type * target)101 bool TypeRelation::IsAssignableTo(Type *source, Type *target)
102 {
103 result_ = CacheLookup(source, target, checker_->AssignableResults(), RelationType::ASSIGNABLE);
104 if (result_ == RelationResult::CACHE_MISS) {
105 if (IsIdenticalTo(source, target)) {
106 return true;
107 }
108
109 result_ = RelationResult::FALSE;
110
111 if (!source->AssignmentSource(this, target)) {
112 target->AssignmentTarget(this, source);
113 }
114
115 if (flags_ == TypeRelationFlag::NONE) {
116 checker_->AssignableResults().cached.insert(
117 {{source->Id(), target->Id()}, {result_, RelationType::ASSIGNABLE}});
118 }
119 }
120
121 return result_ == RelationResult::TRUE;
122 }
123
IsComparableTo(Type * source,Type * target)124 bool TypeRelation::IsComparableTo(Type *source, Type *target)
125 {
126 result_ = CacheLookup(source, target, checker_->ComparableResults(), RelationType::COMPARABLE);
127
128 // NOTE: vpukhov. reimplement dynamic comparison and remove this check
129 if (source->IsETSDynamicType() || target->IsETSDynamicType()) {
130 if (!(source->IsETSDynamicType() && target->IsETSDynamicType())) {
131 return false;
132 }
133 }
134
135 if (result_ == RelationResult::CACHE_MISS) {
136 if (IsAssignableTo(source, target)) {
137 return true;
138 }
139
140 result_ = RelationResult::FALSE;
141 target->Compare(this, source);
142 checker_->ComparableResults().cached.insert(
143 {{source->Id(), target->Id()}, {result_, RelationType::COMPARABLE}});
144 }
145
146 return result_ == RelationResult::TRUE;
147 }
148
IsCastableTo(Type * const source,Type * const target)149 bool TypeRelation::IsCastableTo(Type *const source, Type *const target)
150 {
151 result_ = CacheLookup(source, target, checker_->UncheckedCastableResult(), RelationType::UNCHECKED_CASTABLE);
152 if (result_ == RelationResult::CACHE_MISS) {
153 result_ = RelationResult::FALSE;
154 flags_ |= TypeRelationFlag::UNCHECKED_CAST;
155
156 source->Cast(this, target);
157 if (!IsTrue()) {
158 target->CastTarget(this, source);
159 }
160
161 if (!IsTrue()) {
162 return false;
163 }
164
165 // NOTE: Can't cache if the node has BoxingUnboxingFlags. These flags should be stored and restored on the node
166 // on cache hit.
167 if (UncheckedCast() && node_->GetBoxingUnboxingFlags() == ir::BoxingUnboxingFlags::NONE) {
168 checker_->UncheckedCastableResult().cached.insert(
169 {{source->Id(), target->Id()}, {result_, RelationType::UNCHECKED_CASTABLE}});
170 }
171
172 return true;
173 }
174
175 return result_ == RelationResult::TRUE;
176 }
177
IsSupertypeOf(Type * super,Type * sub)178 bool TypeRelation::IsSupertypeOf(Type *super, Type *sub)
179 {
180 result_ = CacheLookup(super, sub, checker_->SupertypeResults(), RelationType::SUPERTYPE);
181 if (result_ == RelationResult::CACHE_MISS) {
182 if (IsIdenticalTo(super, sub)) {
183 return true;
184 }
185
186 result_ = RelationResult::FALSE;
187
188 if (super->IsSupertypeOf(this, sub), !IsTrue()) {
189 sub->IsSubtypeOf(this, super);
190 }
191
192 if (flags_ == TypeRelationFlag::NONE) {
193 checker_->SupertypeResults().cached.insert({{super->Id(), sub->Id()}, {result_, RelationType::SUPERTYPE}});
194 }
195 }
196
197 return result_ == RelationResult::TRUE;
198 }
199
RaiseError(const std::string & errMsg,const lexer::SourcePosition & loc) const200 void TypeRelation::RaiseError(const std::string &errMsg, const lexer::SourcePosition &loc) const
201 {
202 checker_->ThrowTypeError(errMsg, loc);
203 }
204
RaiseError(std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & loc) const205 void TypeRelation::RaiseError(std::initializer_list<TypeErrorMessageElement> list,
206 const lexer::SourcePosition &loc) const
207 {
208 checker_->ThrowTypeError(list, loc);
209 }
210 } // namespace ark::es2panda::checker
211