• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-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 "etsTuple.h"
17 
18 #include "checker/ETSchecker.h"
19 #include "checker/types/ets/etsTupleType.h"
20 #include "ir/astDump.h"
21 
22 namespace panda::es2panda::ir {
23 
TransformChildren(const NodeTransformer & cb)24 void ETSTuple::TransformChildren([[maybe_unused]] const NodeTransformer &cb)
25 {
26     for (auto *&it : GetTupleTypeAnnotationsList()) {
27         it = static_cast<TypeNode *>(cb(it));
28     }
29 
30     if (HasSpreadType()) {
31         cb(spreadType_);
32     }
33 }
34 
Iterate(const NodeTraverser & cb) const35 void ETSTuple::Iterate([[maybe_unused]] const NodeTraverser &cb) const
36 {
37     for (auto *const it : GetTupleTypeAnnotationsList()) {
38         cb(it);
39     }
40 
41     if (HasSpreadType()) {
42         cb(spreadType_);
43     }
44 }
45 
Dump(ir::AstDumper * const dumper) const46 void ETSTuple::Dump(ir::AstDumper *const dumper) const
47 {
48     dumper->Add({{"type", "ETSTuple"},
49                  {"types", AstDumper::Optional(typeAnnotationList_)},
50                  {"spreadType", AstDumper::Nullish(spreadType_)}});
51 }
52 
Dump(ir::SrcDumper * const dumper) const53 void ETSTuple::Dump(ir::SrcDumper *const dumper) const
54 {
55     dumper->Add("[");
56     for (const auto *const typeAnnot : typeAnnotationList_) {
57         typeAnnot->Dump(dumper);
58         if ((typeAnnot != typeAnnotationList_.back()) || (spreadType_ != nullptr)) {
59             dumper->Add(", ");
60         }
61     }
62     if (spreadType_ != nullptr) {
63         dumper->Add("...");
64         spreadType_->Dump(dumper);
65     }
66     dumper->Add("]");
67 }
68 
Compile(compiler::PandaGen * const pg) const69 void ETSTuple::Compile([[maybe_unused]] compiler::PandaGen *const pg) const {}
Compile(compiler::ETSGen * const etsg) const70 void ETSTuple::Compile([[maybe_unused]] compiler::ETSGen *const etsg) const {}
71 
Check(checker::TSChecker * const checker)72 checker::Type *ETSTuple::Check([[maybe_unused]] checker::TSChecker *const checker)
73 {
74     return nullptr;
75 }
76 
Check(checker::ETSChecker * const checker)77 checker::Type *ETSTuple::Check([[maybe_unused]] checker::ETSChecker *const checker)
78 {
79     return GetType(checker);
80 }
81 
SetNullUndefinedFlags(std::pair<bool,bool> & containsNullOrUndefined,const checker::Type * const type)82 void ETSTuple::SetNullUndefinedFlags(std::pair<bool, bool> &containsNullOrUndefined, const checker::Type *const type)
83 {
84     if (type->HasTypeFlag(checker::TypeFlag::NULLISH)) {
85         containsNullOrUndefined.first = true;
86     }
87 
88     if (type->HasTypeFlag(checker::TypeFlag::UNDEFINED)) {
89         containsNullOrUndefined.second = true;
90     }
91 }
92 
CalculateLUBForTuple(checker::ETSChecker * const checker,ArenaVector<checker::Type * > & typeList,checker::Type * const spreadType)93 checker::Type *ETSTuple::CalculateLUBForTuple(checker::ETSChecker *const checker,
94                                               ArenaVector<checker::Type *> &typeList, checker::Type *const spreadType)
95 {
96     if (typeList.empty()) {
97         return spreadType == nullptr ? checker->GlobalETSObjectType() : spreadType;
98     }
99 
100     std::pair<bool, bool> containsNullOrUndefined = {false, false};
101 
102     bool allElementsAreSame =
103         std::all_of(typeList.begin(), typeList.end(),
104                     [this, &checker, &typeList, &containsNullOrUndefined](checker::Type *const element) {
105                         SetNullUndefinedFlags(containsNullOrUndefined, element);
106                         return checker->Relation()->IsIdenticalTo(typeList[0], element);
107                     });
108 
109     if (spreadType != nullptr) {
110         SetNullUndefinedFlags(containsNullOrUndefined, spreadType);
111         allElementsAreSame = allElementsAreSame && checker->Relation()->IsIdenticalTo(typeList[0], spreadType);
112     }
113 
114     // If only one type present in the tuple, that will be the holder array type. If any two not identical types
115     // present, primitives will be boxed, and LUB is calculated for all of them.
116     // That makes it possible to assign eg. `[int, int, ...int[]]` tuple type to `int[]` array type. Because a `short[]`
117     // array already isn't assignable to `int[]` array, that preserve that the `[int, short, ...int[]]` tuple type's
118     // element type will be calculated to `Object[]`, which is not assignable to `int[]` array either.
119     if (allElementsAreSame) {
120         return typeList[0];
121     }
122 
123     auto *const savedRelationNode = checker->Relation()->GetNode();
124     checker->Relation()->SetNode(this);
125 
126     auto getBoxedTypeOrType = [&checker](checker::Type *const type) {
127         auto *const boxedType = checker->PrimitiveTypeAsETSBuiltinType(type);
128         return boxedType == nullptr ? type : boxedType;
129     };
130 
131     checker::Type *lubType = getBoxedTypeOrType(typeList[0]);
132 
133     for (std::size_t idx = 1; idx < typeList.size(); ++idx) {
134         lubType = checker->FindLeastUpperBound(lubType, getBoxedTypeOrType(typeList[idx]));
135     }
136 
137     if (spreadType != nullptr) {
138         lubType = checker->FindLeastUpperBound(lubType, getBoxedTypeOrType(spreadType));
139     }
140 
141     const auto nullishUndefinedFlags =
142         (containsNullOrUndefined.first ? checker::TypeFlag::NULLISH | checker::TypeFlag::NULL_TYPE
143                                        : checker::TypeFlag::NONE) |
144         (containsNullOrUndefined.second ? checker::TypeFlag::UNDEFINED : checker::TypeFlag::NONE);
145 
146     if (nullishUndefinedFlags != checker::TypeFlag::NONE) {
147         lubType = checker->CreateNullishType(lubType, nullishUndefinedFlags, checker->Allocator(), checker->Relation(),
148                                              checker->GetGlobalTypesHolder());
149     }
150 
151     checker->Relation()->SetNode(savedRelationNode);
152 
153     return lubType;
154 }
155 
GetType(checker::ETSChecker * const checker)156 checker::Type *ETSTuple::GetType(checker::ETSChecker *const checker)
157 {
158     if (TsType() != nullptr) {
159         return TsType();
160     }
161 
162     ArenaVector<checker::Type *> typeList(checker->Allocator()->Adapter());
163 
164     for (auto *const typeAnnotation : GetTupleTypeAnnotationsList()) {
165         auto *const checkedType = checker->GetTypeFromTypeAnnotation(typeAnnotation);
166         typeList.emplace_back(checkedType);
167     }
168 
169     if (HasSpreadType()) {
170         ASSERT(spreadType_->IsTSArrayType());
171         auto *const arrayType = spreadType_->GetType(checker);
172         ASSERT(arrayType->IsETSArrayType());
173         spreadType_->SetTsType(arrayType->AsETSArrayType()->ElementType());
174     }
175 
176     auto *const spreadElementType = spreadType_ != nullptr ? spreadType_->TsType() : nullptr;
177 
178     auto *const tupleType = checker->Allocator()->New<checker::ETSTupleType>(
179         typeList, CalculateLUBForTuple(checker, typeList, spreadElementType), spreadElementType);
180 
181     SetTsType(tupleType);
182     return TsType();
183 }
184 
185 }  // namespace panda::es2panda::ir
186