diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index ce5dd58750cf2af73e27ba167dafc524205d481a..fa504c4e41dae37fdd9b966a8361ea5d0f9b0d4a 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -696,6 +696,11 @@ const GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes() const return globalArraySignatures_; } +const ArenaSet &ETSChecker::UnionAssemblerTypes() const +{ + return unionAssemblerTypes_; +} + Type *ETSChecker::GlobalTypeError() const { return GetGlobalTypesHolder()->GlobalTypeError(); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index b3ced7b7d372663b02d01071966e28c29a3ef118..3fb2cc8f96fe513d7b61e4fd155dca0fdfb0d25b 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -92,6 +92,7 @@ public: invokeToArrowSignatures_(Allocator()->Adapter()), arrowToFuncInterfaces_(Allocator()->Adapter()), globalArraySignatures_(Allocator()->Adapter()), + unionAssemblerTypes_(Allocator()->Adapter()), dynamicIntrinsics_ {DynamicCallIntrinsicsMap {Allocator()->Adapter()}, DynamicCallIntrinsicsMap {Allocator()->Adapter()}}, dynamicClasses_ {DynamicClassIntrinsicsMap(Allocator()->Adapter()), @@ -171,6 +172,8 @@ public: GlobalArraySignatureMap &GlobalArrayTypes(); const GlobalArraySignatureMap &GlobalArrayTypes() const; + const ArenaSet &UnionAssemblerTypes() const; + Type *GlobalTypeError() const; [[nodiscard]] Type *InvalidateType(ir::Typed *node); [[nodiscard]] Type *TypeError(ir::Typed *node, const diagnostic::DiagnosticKind &diagKind, @@ -901,6 +904,7 @@ public: pendingConstraintCheckRecords_.clear(); constraintCheckScopesCount_ = 0; globalArraySignatures_.clear(); + unionAssemblerTypes_.clear(); GetCachedComputedAbstracts()->clear(); for (auto &dynamicCallIntrinsicsMap : dynamicIntrinsics_) { dynamicCallIntrinsicsMap.clear(); @@ -1059,6 +1063,7 @@ private: FunctionInterfaceMap arrowToFuncInterfaces_; size_t constraintCheckScopesCount_ {0}; GlobalArraySignatureMap globalArraySignatures_; + ArenaSet unionAssemblerTypes_; ComputedAbstracts *cachedComputedAbstracts_ {nullptr}; // NOTE(aleksisch): Extract dynamic from checker to separate class std::array dynamicIntrinsics_; diff --git a/ets2panda/checker/ets/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 1c8c6c9a98910a1e00c65c7f932c68aaca91f26f..c4ef7ae5d8f3dd69e699f49c9b5aa2c091cfe2f6 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -137,8 +137,9 @@ Type *ETSChecker::CreateETSUnionType(Span constituentTypes) if (newConstituentTypes.size() == 1) { return newConstituentTypes[0]; } - - return ProgramAllocator()->New(this, std::move(newConstituentTypes)); + auto *un = ProgramAllocator()->New(this, std::move(newConstituentTypes)); + unionAssemblerTypes_.insert(un); + return un; } ETSTypeAliasType *ETSChecker::CreateETSTypeAliasType(util::StringView name, const ir::AstNode *declNode, diff --git a/ets2panda/checker/types/ets/etsUnionType.cpp b/ets2panda/checker/types/ets/etsUnionType.cpp index b5a4244e54630cac84ef1aa5d1c4be03e601ed8b..5bf74edc9905d5ad3b451c3a291fa355bfdfd2a0 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -34,19 +34,108 @@ void ETSUnionType::ToString(std::stringstream &ss, bool precise) const void ETSUnionType::ToAssemblerType(std::stringstream &ss) const { - assemblerLub_->ToAssemblerTypeWithRank(ss); + ss << GetAssemblerType(); } void ETSUnionType::ToDebugInfoType(std::stringstream &ss) const { - assemblerLub_->ToDebugInfoType(ss); + if (assemblerConstituentTypes_.size() == 1) { + assemblerConstituentTypes_[0]->ToDebugInfoType(ss); + return; + } + ss << "{U"; + for (size_t idx = 0; idx < assemblerConstituentTypes_.size(); idx++) { + assemblerConstituentTypes_[idx]->ToDebugInfoType(ss); + if (idx != assemblerConstituentTypes_.size() - 1) { + ss << ","; + } + } + ss << "}"; +} + +static std::string GetAssemblerTypeString(Type *type) +{ + std::stringstream ss; + type->ToAssemblerTypeWithRank(ss); + return ss.str(); +} + +void ETSUnionType::InitAssemblerTypeCache(ETSChecker *checker) +{ + ES2PANDA_ASSERT(!assemblerConstituentTypes_.empty()); + std::stringstream ss; + if (assemblerConstituentTypes_.size() == 1) { + assemblerConstituentTypes_[0]->ToAssemblerTypeWithRank(ss); + } else { + ss << "{U"; + for (size_t idx = 0; idx < assemblerConstituentTypes_.size(); idx++) { + assemblerConstituentTypes_[idx]->ToAssemblerTypeWithRank(ss); + if (idx != assemblerConstituentTypes_.size() - 1) { + ss << ","; + } + } + ss << "}"; + } + assemblerTypeCache_ = util::UString(ss.str(), checker->ProgramAllocator()).View(); +} + +void ETSUnionType::CanonicalizedAssemblerType(ETSChecker *checker) +{ + auto *const apparent = checker->GetApparentType(this); + if (!apparent->IsETSUnionType()) { + assemblerConstituentTypes_.push_back(apparent); + return; + } + if (apparent != this) { + const auto &types = apparent->AsETSUnionType()->GetAssemblerTypes(); + assemblerConstituentTypes_.insert(assemblerConstituentTypes_.begin(), types.begin(), types.end()); + return; + } + + ES2PANDA_ASSERT(constituentTypes_.size() > 1); + for (auto *type : constituentTypes_) { + ES2PANDA_ASSERT(!type->IsETSUnionType()); + if (type->IsETSUndefinedType() || type->IsETSVoidType()) { + continue; + } + if (type->IsETSNullType()) { + assemblerConstituentTypes_.clear(); + assemblerConstituentTypes_.push_back(checker->GlobalETSObjectType()); + return; + } + if (type->IsTypeError()) { + assemblerConstituentTypes_.clear(); + assemblerConstituentTypes_.push_back(checker->GlobalTypeError()); + return; + } + auto found = + std::find_if(assemblerConstituentTypes_.begin(), assemblerConstituentTypes_.end(), + [&type](Type *t) { return GetAssemblerTypeString(type) == GetAssemblerTypeString(t); }); + if (found == assemblerConstituentTypes_.end()) { + assemblerConstituentTypes_.push_back(type); + } + } + if (assemblerConstituentTypes_.empty()) { + assemblerConstituentTypes_.push_back(checker->GlobalETSObjectType()); + return; + } + if (assemblerConstituentTypes_.size() == 1) { + return; + } + + std::sort(assemblerConstituentTypes_.begin(), assemblerConstituentTypes_.end(), + [](Type *a, Type *b) { return GetAssemblerTypeString(a) < GetAssemblerTypeString(b); }); } ETSUnionType::ETSUnionType(ETSChecker *checker, ArenaVector &&constituentTypes) - : Type(TypeFlag::ETS_UNION), constituentTypes_(std::move(constituentTypes)) + : Type(TypeFlag::ETS_UNION), + constituentTypes_(std::move(constituentTypes)), + assemblerConstituentTypes_(checker->ProgramAllocator()->Adapter()) { ES2PANDA_ASSERT(constituentTypes_.size() > 1); assemblerLub_ = ComputeAssemblerLUB(checker, this); + CanonicalizedAssemblerType(checker); + InitAssemblerTypeCache(checker); } bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target) diff --git a/ets2panda/checker/types/ets/etsUnionType.h b/ets2panda/checker/types/ets/etsUnionType.h index 54c15370d85c9d453a8183ea0844b4a576856743..be1d6c3b29afd21e78ac11dc7f57904e714fb2a0 100644 --- a/ets2panda/checker/types/ets/etsUnionType.h +++ b/ets2panda/checker/types/ets/etsUnionType.h @@ -60,6 +60,16 @@ public: return assemblerLub_; } + const util::StringView &GetAssemblerType() const + { + return assemblerTypeCache_; + } + + const ArenaVector &GetAssemblerTypes() const + { + return assemblerConstituentTypes_; + } + template [[nodiscard]] bool AllOfConstituentTypes(UnaryPredicate p) const noexcept { @@ -106,8 +116,12 @@ private: std::map &numericTypes) const noexcept; static Type *ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un); + void CanonicalizedAssemblerType(ETSChecker *checker); + void InitAssemblerTypeCache(ETSChecker *checker); ArenaVector const constituentTypes_; + ArenaVector assemblerConstituentTypes_; + util::StringView assemblerTypeCache_; Type *assemblerLub_ {nullptr}; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/compiler/core/ETSemitter.cpp b/ets2panda/compiler/core/ETSemitter.cpp index 81ff05cfe2b47612f29a5eb7a8a7bb1523c9432a..c5b353c33ffef4be285cf463877d448cb4bf295c 100644 --- a/ets2panda/compiler/core/ETSemitter.cpp +++ b/ets2panda/compiler/core/ETSemitter.cpp @@ -104,7 +104,9 @@ static pandasm::Type PandasmTypeWithRank(checker::Type const *type, uint32_t ran return PandasmTypeWithRank(type->AsETSPartialTypeParameter()->GetUnderlying()); } if (type->IsETSUnionType()) { - return PandasmTypeWithRank(type->AsETSUnionType()->GetAssemblerLUB()); + if (type->AsETSUnionType()->GetAssemblerTypes().size() == 1) { + return pandasm::Type::FromName(type->AsETSUnionType()->GetAssemblerType().Mutf8()); + } } std::stringstream ss; @@ -309,6 +311,13 @@ void ETSEmitter::GenAnnotation() for (auto [arrType, signature] : checker->GlobalArrayTypes()) { GenGlobalArrayRecord(arrType, signature); } + for (auto unionType : checker->UnionAssemblerTypes()) { + if (unionType->GetAssemblerTypes().size() > 1) { + auto unionRecord = pandasm::Record(unionType->GetAssemblerType().Mutf8(), Program()->lang); + unionRecord.metadata->SetAttribute(Signatures::EXTERNAL); + Program()->recordTable.emplace(unionRecord.name, std::move(unionRecord)); + } + } if (Context()->config->options->WasSetWithExportTable()) { auto result = StoreExportNodes(Context()->parserProgram->DeclGenExportNodes(), Program()); Program()->exportStrMap = std::move(result); diff --git a/ets2panda/evaluate/debugInfoStorage.cpp b/ets2panda/evaluate/debugInfoStorage.cpp index 1aa286225d616c9afb852a66bd59efa962c082b0..20163df7651cfe096002170d3259eedeb67bb862 100644 --- a/ets2panda/evaluate/debugInfoStorage.cpp +++ b/ets2panda/evaluate/debugInfoStorage.cpp @@ -30,7 +30,7 @@ std::string GetFullRecordName(const panda_file::File &pf, const panda_file::File std::string name = utf::Mutf8AsCString(pf.GetStringData(classId).data); auto type = pandasm::Type::FromDescriptor(name); - type = pandasm::Type(type.GetComponentName(), type.GetRank()); + type = pandasm::Type(type.GetNameWithoutRank(), type.GetRank()); return type.GetPandasmName(); } diff --git a/ets2panda/test/unit/CMakeLists.txt b/ets2panda/test/unit/CMakeLists.txt index c84184cf5fed4406a6915b5ecb8eca0d3f138e9f..625d891fc46c3eabdee5d88ad68befb6c9e8c23a 100644 --- a/ets2panda/test/unit/CMakeLists.txt +++ b/ets2panda/test/unit/CMakeLists.txt @@ -42,6 +42,10 @@ ets2panda_add_gtest(es2panda_globalETSObjectType_tests CPP_SOURCES globalETSObjectType_test.cpp ) +ets2panda_add_gtest(es2panda_union_emit_standard_test + CPP_SOURCES union_emit_test.cpp +) + if (PANDA_TARGET_LINUX AND PANDA_TARGET_64) ets2panda_add_gtest(sizeof_node_tests CPP_SOURCES sizeof_node_test.cpp diff --git a/ets2panda/test/unit/union_emit_test.cpp b/ets2panda/test/unit/union_emit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d63214db562d4f5843a61748f57149cc8c9ea858 --- /dev/null +++ b/ets2panda/test/unit/union_emit_test.cpp @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "test/utils/asm_test.h" + +namespace ark::es2panda::compiler::test { + +class UnionAsmTest : public ::test::utils::AsmTest { +public: + UnionAsmTest() = default; + + ~UnionAsmTest() override = default; + + void CheckUnionType(std::string_view recordName) + { + pandasm::Record *rec = GetRecord(recordName, program_); + ASSERT_TRUE(rec != nullptr) << "Type '" << recordName << "' not found"; + } + +private: + NO_COPY_SEMANTIC(UnionAsmTest); + NO_MOVE_SEMANTIC(UnionAsmTest); +}; + +TEST_F(UnionAsmTest, union_test) +{ + SetCurrentProgram(R"( + class A {} + class B {} + class C {} + class D {} + function test1(v:A|B) {} + function test2(v:A|B|C) {} + function test3(v:A|B|FixedArray) {} + function test4(v:B|C|D|int) {} + )"); + + CheckUnionType("{Udummy.A,dummy.B}"); + CheckUnionType("{Udummy.A,dummy.B,dummy.C[]}"); + CheckUnionType("{Udummy.A,dummy.B,dummy.C}"); + CheckUnionType("{Udummy.B,dummy.C,dummy.D,std.core.Int}"); +} + +TEST_F(UnionAsmTest, union_test_extends) +{ + SetCurrentProgram(R"( + class A {} + class B {} + class C extends B {} + class D extends B {} + function test1(v:A|B) {} + function test2(v:A|B|C) {} + function test3(v:A|B|FixedArray) {} + function test4(v:B|C|D|int) {} + )"); + + CheckUnionType("{Udummy.A,dummy.B}"); + CheckUnionType("{Udummy.A,dummy.B,dummy.C[]}"); + CheckUnionType("{Udummy.B,std.core.Int}"); +} + +TEST_F(UnionAsmTest, union_test_2) +{ + SetCurrentProgram(R"( + class A {} + class B {} + class C {} + class D {} + function test1(v:C|C|B|C|B) {} + function test2(v:D|C|B|A) {} + function test3(v:A|A|A|B) {} + function test4(v:A|double) {} + function test5(v:double|double|long) {} + function test6(v:double|int|long) {} + function test7(v:B|C|B|A) {} + function test8(v:A|D|D|D) {} + )"); + + CheckUnionType("{Udummy.A,dummy.B,dummy.C,dummy.D}"); + CheckUnionType("{Udummy.A,dummy.B,dummy.C}"); + CheckUnionType("{Udummy.A,dummy.B}"); + CheckUnionType("{Udummy.A,dummy.D}"); + CheckUnionType("{Udummy.A,std.core.Double}"); + CheckUnionType("{Udummy.B,dummy.C}"); +} + +TEST_F(UnionAsmTest, union_test_arrays) +{ + SetCurrentProgram(R"( + class A {} + class B {} + class C {} + class D {} + function test1(v:B|C|FixedArray) {} + function test1(v:FixedArray|FixedArray|FixedArray) {} + function test1(v:B|C|FixedArray) {} + function test1(v:B|FixedArray|FixedArray|FixedArray|FixedArray) {} + function test1(v:FixedArray|FixedArray|FixedArray|FixedArray) {} + function test1(v:FixedArray|FixedArray) {} + function test1(v:FixedArray|FixedArray) {} + function test1(v:FixedArray|C|B) {} + function test1(v:FixedArray|long|double) {} + function test1(v:FixedArray|FixedArray>|FixedArray|FixedArray) {} + )"); + + CheckUnionType("{Udummy.A,dummy.B}"); + CheckUnionType("{Udummy.A,dummy.B}[]"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Double}"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Double}[]"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Int}"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Int}[]"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Long}"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Long}[]"); + CheckUnionType("{Udummy.A,dummy.D}"); + CheckUnionType("{Udummy.A,dummy.D}[]"); + CheckUnionType("{Udummy.B,dummy.C,{Udummy.A,dummy.B}[]}"); + CheckUnionType("{Udummy.B,dummy.C,{Udummy.A,dummy.D,std.core.Double}[]}"); + CheckUnionType("{Udummy.B,dummy.C,{Udummy.C,dummy.D}[]}"); + CheckUnionType("{Udummy.B,dummy.C}"); + CheckUnionType("{Udummy.B,dummy.C}[]"); + CheckUnionType("{Udummy.B,dummy.D}"); + CheckUnionType("{Udummy.B,dummy.D}[]"); + CheckUnionType("{Udummy.B,{Udummy.A,dummy.D}[],{Udummy.B,dummy.C}[],{Udummy.B,dummy.D}[],{Udummy.C,dummy.D}[]}"); + CheckUnionType("{Udummy.C,dummy.D}"); + CheckUnionType("{Udummy.C,dummy.D}[]"); + CheckUnionType( + "{U{Udummy.A,dummy.D,std.core.Double}[],{Udummy.A,dummy.D,std.core.Int}[],{Udummy.A,dummy.D,std.core.Long}[]}"); + CheckUnionType("{U{Udummy.A,dummy.D}[],{Udummy.B,dummy.C}[],{Udummy.B,dummy.D}[],{Udummy.C,dummy.D}[]}"); + CheckUnionType("{U{Udummy.A,dummy.D}[],{Udummy.B,dummy.D}[]}"); + CheckUnionType("{U{Udummy.B,dummy.C}[],{Udummy.C,dummy.D}[]}"); +} + +} // namespace ark::es2panda::compiler::test