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 return Result(true);
78 }
79
80 result_ = RelationResult::FALSE;
81 target->Compatible(this, source);
82
83 return result_ == RelationResult::TRUE;
84 }
85
IsIdenticalTo(IndexInfo * source,IndexInfo * target)86 bool TypeRelation::IsIdenticalTo(IndexInfo *source, IndexInfo *target)
87 {
88 if (source == target) {
89 Result(true);
90 return true;
91 }
92
93 result_ = RelationResult::FALSE;
94 target->Identical(this, source);
95
96 return result_ == RelationResult::TRUE;
97 }
98
99 // NOTE: applyNarrowing -> flag
IsAssignableTo(Type * source,Type * target)100 bool TypeRelation::IsAssignableTo(Type *source, Type *target)
101 {
102 result_ = CacheLookup(source, target, checker_->AssignableResults(), RelationType::ASSIGNABLE);
103 if (result_ == RelationResult::CACHE_MISS) {
104 // NOTE: we support assigning T to Readonly<T>, but do not support assigning Readonly<T> to T
105 // more details in spec
106 if (source->HasTypeFlag(TypeFlag::READONLY) && !target->HasTypeFlag(TypeFlag::READONLY)) {
107 result_ = RelationResult::FALSE;
108 }
109
110 if (result_ != RelationResult::FALSE && IsIdenticalTo(source, target)) {
111 return true;
112 }
113
114 if (result_ == RelationResult::ERROR) {
115 // Return early to prevent logging same errors again
116 return false;
117 }
118
119 result_ = RelationResult::FALSE;
120
121 if (!source->AssignmentSource(this, target)) {
122 target->AssignmentTarget(this, source);
123 }
124
125 if (flags_ == TypeRelationFlag::NONE) {
126 checker_->AssignableResults().cached.insert(
127 {{source->Id(), target->Id()}, {result_, RelationType::ASSIGNABLE}});
128 }
129 }
130
131 return result_ == RelationResult::TRUE;
132 }
133
IsComparableTo(Type * source,Type * target)134 bool TypeRelation::IsComparableTo(Type *source, Type *target)
135 {
136 result_ = CacheLookup(source, target, checker_->ComparableResults(), RelationType::COMPARABLE);
137
138 // NOTE: vpukhov. reimplement dynamic comparison and remove this check
139 if (source->IsETSDynamicType() || target->IsETSDynamicType()) {
140 if (!(source->IsETSDynamicType() && target->IsETSDynamicType())) {
141 return false;
142 }
143 }
144
145 if (result_ == RelationResult::CACHE_MISS) {
146 if (IsAssignableTo(source, target)) {
147 return true;
148 }
149
150 result_ = RelationResult::FALSE;
151 target->Compare(this, source);
152 checker_->ComparableResults().cached.insert(
153 {{source->Id(), target->Id()}, {result_, RelationType::COMPARABLE}});
154 }
155
156 return result_ == RelationResult::TRUE;
157 }
158
IsCastableTo(Type * const source,Type * const target)159 bool TypeRelation::IsCastableTo(Type *const source, Type *const target)
160 {
161 result_ = CacheLookup(source, target, checker_->UncheckedCastableResult(), RelationType::UNCHECKED_CASTABLE);
162 if (result_ == RelationResult::CACHE_MISS) {
163 result_ = RelationResult::FALSE;
164 flags_ |= TypeRelationFlag::UNCHECKED_CAST;
165
166 source->Cast(this, target);
167 if (!IsTrue()) {
168 target->CastTarget(this, source);
169 }
170
171 if (!IsTrue()) {
172 return false;
173 }
174
175 // NOTE: Can't cache if the node has BoxingUnboxingFlags. These flags should be stored and restored on the node
176 // on cache hit.
177 if (UncheckedCast() && node_->GetBoxingUnboxingFlags() == ir::BoxingUnboxingFlags::NONE) {
178 checker_->UncheckedCastableResult().cached.insert(
179 {{source->Id(), target->Id()}, {result_, RelationType::UNCHECKED_CASTABLE}});
180 }
181
182 return true;
183 }
184
185 return result_ == RelationResult::TRUE;
186 }
187
IsSupertypeOf(Type * super,Type * sub)188 bool TypeRelation::IsSupertypeOf(Type *super, Type *sub)
189 {
190 result_ = CacheLookup(super, sub, checker_->SupertypeResults(), RelationType::SUPERTYPE);
191 if (result_ == RelationResult::CACHE_MISS) {
192 if (IsIdenticalTo(super, sub)) {
193 return true;
194 }
195
196 result_ = RelationResult::FALSE;
197
198 if (super->IsSupertypeOf(this, sub), !IsTrue()) {
199 sub->IsSubtypeOf(this, super);
200 }
201
202 if (flags_ == TypeRelationFlag::NONE) {
203 checker_->SupertypeResults().cached.insert({{super->Id(), sub->Id()}, {result_, RelationType::SUPERTYPE}});
204 }
205 }
206
207 return result_ == RelationResult::TRUE;
208 }
209
RaiseError(const std::string & errMsg,const lexer::SourcePosition & loc) const210 void TypeRelation::RaiseError(const std::string &errMsg, const lexer::SourcePosition &loc) const
211 {
212 checker_->LogTypeError(errMsg, loc);
213 }
214
RaiseError(std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & loc) const215 void TypeRelation::RaiseError(std::initializer_list<TypeErrorMessageElement> list,
216 const lexer::SourcePosition &loc) const
217 {
218 checker_->LogTypeError(list, loc);
219 }
220 } // namespace ark::es2panda::checker
221