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/types/ets/etsTupleType.h"
19#include "ir/astDump.h"
20
21namespace ark::es2panda::ir {
22
23void ETSTuple::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
24{
25    for (auto *&it : GetTupleTypeAnnotationsList()) {
26        if (auto *transformedNode = cb(it); it != transformedNode) {
27            it->SetTransformedNode(transformationName, transformedNode);
28            it = static_cast<TypeNode *>(transformedNode);
29        }
30    }
31
32    if (HasSpreadType()) {
33        if (auto *transformedNode = cb(spreadType_); spreadType_ != transformedNode) {
34            spreadType_->SetTransformedNode(transformationName, transformedNode);
35            spreadType_ = static_cast<TypeNode *>(transformedNode);
36        }
37    }
38}
39
40void ETSTuple::Iterate(const NodeTraverser &cb) const
41{
42    for (auto *const it : GetTupleTypeAnnotationsList()) {
43        cb(it);
44    }
45
46    if (HasSpreadType()) {
47        cb(spreadType_);
48    }
49}
50
51void ETSTuple::Dump(ir::AstDumper *const dumper) const
52{
53    dumper->Add({{"type", "ETSTuple"},
54                 {"types", AstDumper::Optional(typeAnnotationList_)},
55                 {"spreadType", AstDumper::Nullish(spreadType_)}});
56}
57
58void ETSTuple::Dump(ir::SrcDumper *const dumper) const
59{
60    dumper->Add("[");
61    for (const auto *const typeAnnot : typeAnnotationList_) {
62        typeAnnot->Dump(dumper);
63        if ((typeAnnot != typeAnnotationList_.back()) || (spreadType_ != nullptr)) {
64            dumper->Add(", ");
65        }
66    }
67    if (spreadType_ != nullptr) {
68        dumper->Add("...");
69        spreadType_->Dump(dumper);
70    }
71    dumper->Add("]");
72}
73
74void ETSTuple::Compile([[maybe_unused]] compiler::PandaGen *const pg) const {}
75void ETSTuple::Compile([[maybe_unused]] compiler::ETSGen *const etsg) const {}
76
77checker::Type *ETSTuple::Check([[maybe_unused]] checker::TSChecker *const checker)
78{
79    return nullptr;
80}
81
82checker::Type *ETSTuple::Check([[maybe_unused]] checker::ETSChecker *const checker)
83{
84    return GetType(checker);
85}
86
87checker::Type *ETSTuple::CalculateLUBForTuple(checker::ETSChecker *const checker,
88                                              ArenaVector<checker::Type *> &typeList, checker::Type **spreadTypePtr)
89{
90    auto &spreadType = *spreadTypePtr;
91    if (typeList.empty()) {
92        return spreadType == nullptr ? checker->GlobalETSObjectType() : spreadType;
93    }
94
95    bool allElementsAreSame = std::all_of(typeList.begin(), typeList.end(), [&checker, &typeList](auto *element) {
96        return checker->Relation()->IsIdenticalTo(typeList[0], element);
97    });
98
99    if (spreadType != nullptr) {
100        allElementsAreSame = allElementsAreSame && checker->Relation()->IsIdenticalTo(typeList[0], spreadType);
101    }
102
103    // If only one type present in the tuple, that will be the holder array type. If any two not identical types
104    // present, primitives will be boxed, and LUB is calculated for all of them.
105    // That makes it possible to assign eg. `[int, int, ...int[]]` tuple type to `int[]` array type. Because a
106    // `short[]` array already isn't assignable to `int[]` array, that preserve that the `[int, short, ...int[]]`
107    // tuple type's element type will be calculated to `Object[]`, which is not assignable to `int[]` array either.
108    if (allElementsAreSame) {
109        return typeList[0];
110    }
111    // Other case - promote element types
112    // NOTE(vpukhov): #15570 normalization happens or not?
113    std::for_each(typeList.begin(), typeList.end(), [checker](auto &t) { t = checker->MaybePromotedBuiltinType(t); });
114
115    auto ctypes = typeList;
116    if (spreadType != nullptr) {
117        spreadType = checker->MaybePromotedBuiltinType(spreadType);
118        ctypes.push_back(spreadType);
119    }
120    return checker->CreateETSUnionType(std::move(ctypes));
121}
122
123checker::Type *ETSTuple::GetType(checker::ETSChecker *const checker)
124{
125    if (TsType() != nullptr) {
126        return TsType();
127    }
128
129    ArenaVector<checker::Type *> typeList(checker->Allocator()->Adapter());
130
131    for (auto *const typeAnnotation : GetTupleTypeAnnotationsList()) {
132        auto *const checkedType = typeAnnotation->GetType(checker);
133        typeList.emplace_back(checkedType);
134    }
135
136    if (HasSpreadType()) {
137        ASSERT(spreadType_->IsTSArrayType());
138        auto *const arrayType = spreadType_->GetType(checker);
139        ASSERT(arrayType->IsETSArrayType());
140        spreadType_->SetTsType(arrayType->AsETSArrayType()->ElementType());
141    }
142
143    auto *spreadElementType = spreadType_ != nullptr ? spreadType_->TsType() : nullptr;
144
145    auto *const tupleType = checker->Allocator()->New<checker::ETSTupleType>(
146        typeList, CalculateLUBForTuple(checker, typeList, &spreadElementType), spreadElementType);
147
148    SetTsType(tupleType);
149    return TsType();
150}
151
152}  // namespace ark::es2panda::ir
153