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 panda::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 == target) {
55 Result(true);
56 return true;
57 }
58
59 result_ = CacheLookup(source, target, checker_->IdenticalResults(), RelationType::IDENTICAL);
60 if (result_ == RelationResult::CACHE_MISS) {
61 checker_->ResolveStructuredTypeMembers(source);
62 checker_->ResolveStructuredTypeMembers(target);
63 result_ = RelationResult::FALSE;
64 target->Identical(this, source);
65 checker_->IdenticalResults().cached.insert({{source->Id(), target->Id()}, {result_, RelationType::IDENTICAL}});
66 }
67
68 return result_ == RelationResult::TRUE;
69 }
70
IsIdenticalTo(Signature * source,Signature * target)71 bool TypeRelation::IsIdenticalTo(Signature *source, Signature *target)
72 {
73 if (source == target) {
74 Result(true);
75 return true;
76 }
77
78 result_ = RelationResult::FALSE;
79 target->Identical(this, source);
80
81 return result_ == RelationResult::TRUE;
82 }
83
IsIdenticalTo(IndexInfo * source,IndexInfo * target)84 bool TypeRelation::IsIdenticalTo(IndexInfo *source, IndexInfo *target)
85 {
86 if (source == target) {
87 Result(true);
88 return true;
89 }
90
91 result_ = RelationResult::FALSE;
92 target->Identical(this, source);
93
94 return result_ == RelationResult::TRUE;
95 }
96
97 // NOTE: applyNarrowing -> flag
IsAssignableTo(Type * source,Type * target)98 bool TypeRelation::IsAssignableTo(Type *source, Type *target)
99 {
100 result_ = CacheLookup(source, target, checker_->AssignableResults(), RelationType::ASSIGNABLE);
101 if (result_ == RelationResult::CACHE_MISS) {
102 if (IsIdenticalTo(source, target)) {
103 return true;
104 }
105
106 result_ = RelationResult::FALSE;
107
108 if (!source->AssignmentSource(this, target)) {
109 target->AssignmentTarget(this, source);
110 }
111
112 if (flags_ == TypeRelationFlag::NONE) {
113 checker_->AssignableResults().cached.insert(
114 {{source->Id(), target->Id()}, {result_, RelationType::ASSIGNABLE}});
115 }
116 }
117
118 return result_ == RelationResult::TRUE;
119 }
120
IsComparableTo(Type * source,Type * target)121 bool TypeRelation::IsComparableTo(Type *source, Type *target)
122 {
123 result_ = CacheLookup(source, target, checker_->ComparableResults(), RelationType::COMPARABLE);
124
125 // NOTE: vpukhov. reimplement dynamic comparison and remove this check
126 if (source->IsETSDynamicType() || target->IsETSDynamicType()) {
127 if (!(source->IsETSDynamicType() && target->IsETSDynamicType())) {
128 return false;
129 }
130 }
131
132 if (result_ == RelationResult::CACHE_MISS) {
133 if (IsAssignableTo(source, target)) {
134 return true;
135 }
136
137 result_ = RelationResult::FALSE;
138 target->Compare(this, source);
139 checker_->ComparableResults().cached.insert(
140 {{source->Id(), target->Id()}, {result_, RelationType::COMPARABLE}});
141 }
142
143 return result_ == RelationResult::TRUE;
144 }
145
IsCastableTo(Type * const source,Type * const target)146 bool TypeRelation::IsCastableTo(Type *const source, Type *const target)
147 {
148 result_ = CacheLookup(source, target, checker_->UncheckedCastableResult(), RelationType::UNCHECKED_CASTABLE);
149 if (result_ == RelationResult::CACHE_MISS) {
150 result_ = RelationResult::FALSE;
151 flags_ |= TypeRelationFlag::UNCHECKED_CAST;
152
153 source->Cast(this, target);
154 if (!IsTrue()) {
155 target->CastTarget(this, source);
156 }
157
158 if (!IsTrue()) {
159 return false;
160 }
161
162 // NOTE: Can't cache if the node has BoxingUnboxingFlags. These flags should be stored and restored on the node
163 // on cache hit.
164 if (UncheckedCast() && node_->GetBoxingUnboxingFlags() == ir::BoxingUnboxingFlags::NONE) {
165 checker_->UncheckedCastableResult().cached.insert(
166 {{source->Id(), target->Id()}, {result_, RelationType::UNCHECKED_CASTABLE}});
167 }
168
169 return true;
170 }
171
172 return result_ == RelationResult::TRUE;
173 }
174
IsSupertypeOf(Type * super,Type * sub)175 bool TypeRelation::IsSupertypeOf(Type *super, Type *sub)
176 {
177 result_ = CacheLookup(super, sub, checker_->SupertypeResults(), RelationType::SUPERTYPE);
178 if (result_ == RelationResult::CACHE_MISS) {
179 if (IsIdenticalTo(super, sub)) {
180 return true;
181 }
182
183 result_ = RelationResult::FALSE;
184
185 if (super->IsSupertypeOf(this, sub), !IsTrue()) {
186 sub->IsSubtypeOf(this, super);
187 }
188
189 if (flags_ == TypeRelationFlag::NONE) {
190 checker_->SupertypeResults().cached.insert({{super->Id(), sub->Id()}, {result_, RelationType::SUPERTYPE}});
191 }
192 }
193
194 return result_ == RelationResult::TRUE;
195 }
196
RaiseError(const std::string & errMsg,const lexer::SourcePosition & loc) const197 void TypeRelation::RaiseError(const std::string &errMsg, const lexer::SourcePosition &loc) const
198 {
199 checker_->ThrowTypeError(errMsg, loc);
200 }
201
RaiseError(std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & loc) const202 void TypeRelation::RaiseError(std::initializer_list<TypeErrorMessageElement> list,
203 const lexer::SourcePosition &loc) const
204 {
205 checker_->ThrowTypeError(list, loc);
206 }
207 } // namespace panda::es2panda::checker
208