From 5e7aaa6c27f1216305878cdb2f27910ab997f4e2 Mon Sep 17 00:00:00 2001 From: Lyupa Anastasia Date: Mon, 30 Jun 2025 18:19:20 +0300 Subject: [PATCH] Implement union type Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICIYJL Revert "Revert Union Type" This reverts commit 032615c5baa080fd5165fb07473288d487a0997b. UnionType checkcast/isinstance codegen; null type FE support implementing union class in runtime - Patch checkcast/isinstance codegen for Union types; - Emit unions with null as {...|null} instead of simple 'object' - Emit null type as std.core.Null Signed-off-by: Anna Antipina Signed-off-by: Lyupa Anastasia Change-Id: I5b659865f3cbe9bb3dc411423bb8eb9c989ec8c1 --- ets2panda/checker/ETSchecker.cpp | 10 + ets2panda/checker/ETSchecker.h | 8 + ets2panda/checker/ets/function.cpp | 22 +- ets2panda/checker/ets/typeCheckingHelpers.cpp | 3 +- ets2panda/checker/ets/typeCreation.cpp | 8 +- ets2panda/checker/types/ets/etsUnionType.cpp | 139 ++++++---- ets2panda/checker/types/ets/etsUnionType.h | 36 ++- ets2panda/compiler/core/ETSGen.cpp | 104 ++++++-- ets2panda/compiler/core/ETSGen.h | 9 +- ets2panda/compiler/core/ETSemitter.cpp | 9 +- ets2panda/compiler/scripts/signatures.yaml | 4 +- ets2panda/evaluate/debugInfoStorage.cpp | 2 +- ets2panda/test/unit/CMakeLists.txt | 4 + .../test/unit/any_ins_test/any_ins_test.cpp | 2 +- ets2panda/test/unit/union_emit_test.cpp | 238 ++++++++++++++++++ 15 files changed, 511 insertions(+), 87 deletions(-) create mode 100644 ets2panda/test/unit/union_emit_test.cpp diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index abf418beae8..5f08f3c6183 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -713,6 +713,16 @@ const GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes() const return globalArraySignatures_; } +const ArenaSet &ETSChecker::UnionAssemblerTypes() const +{ + return unionAssemblerTypes_; +} + +ArenaSet &ETSChecker::UnionAssemblerTypes() +{ + return unionAssemblerTypes_; +} + Type *ETSChecker::GlobalTypeError() const { return GetGlobalTypesHolder()->GlobalTypeError(); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 62c5776c6b2..cccc9283e55 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -93,6 +93,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()), @@ -174,6 +175,9 @@ public: GlobalArraySignatureMap &GlobalArrayTypes(); const GlobalArraySignatureMap &GlobalArrayTypes() const; + const ArenaSet &UnionAssemblerTypes() const; + ArenaSet &UnionAssemblerTypes(); + Type *GlobalTypeError() const; [[nodiscard]] Type *InvalidateType(ir::Typed *node); [[nodiscard]] Type *TypeError(ir::Typed *node, const diagnostic::DiagnosticKind &diagKind, @@ -513,6 +517,8 @@ public: void SearchAmongMostSpecificTypes(Type *&mostSpecificType, Signature *&prevSig, std::tuple info, bool lookForClassType); + void CheckAmbiguousCall(Type *&mostSpecificType, Type *sigType, Signature *prevSig, Signature *sig, + const lexer::SourcePosition &pos); void CollectSuitableSignaturesForTypeInference(size_t paramIdx, ArenaVector &signatures, ArenaMultiMap &bestSignaturesForParameter, const ArenaVector &arguments); @@ -940,6 +946,7 @@ public: pendingConstraintCheckRecords_.clear(); constraintCheckScopesCount_ = 0; globalArraySignatures_.clear(); + unionAssemblerTypes_.clear(); GetCachedComputedAbstracts()->clear(); for (auto &dynamicCallIntrinsicsMap : dynamicIntrinsics_) { dynamicCallIntrinsicsMap.clear(); @@ -1099,6 +1106,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/function.cpp b/ets2panda/checker/ets/function.cpp index c31ff616071..a7aa48c0e24 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -1009,6 +1009,21 @@ static void InitMostSpecificType(TypeRelation *relation, const ArenaVectorIsETSObjectType() && mostSpecificType->IsETSObjectType()) || + (sigType->IsETSUnionType() && mostSpecificType->IsETSUnionType() && + ((sigType->AsETSUnionType()->ContainsNull() && mostSpecificType->AsETSUnionType()->ContainsNull()) || + (sigType->AsETSUnionType()->ContainsUndefined() && + mostSpecificType->AsETSUnionType()->ContainsUndefined())))) && + !Relation()->IsAssignableTo(mostSpecificType, sigType) && + !Relation()->IsLegalBoxedPrimitiveConversion(sigType, mostSpecificType)) { + auto funcName = sig->Function()->Id()->Name(); + LogError(diagnostic::AMBIGUOUS_CALL, {funcName, funcName, funcName, prevSig, funcName, sig}, pos); + } +} + void ETSChecker::SearchAmongMostSpecificTypes(Type *&mostSpecificType, Signature *&prevSig, std::tuple info, bool lookForClassType) @@ -1056,11 +1071,8 @@ void ETSChecker::SearchAmongMostSpecificTypes(Type *&mostSpecificType, Signature if (Relation()->IsAssignableTo(sigType, mostSpecificType)) { mostSpecificType = sigType; prevSig = sig; - } else if (sigType->IsETSObjectType() && mostSpecificType->IsETSObjectType() && - !Relation()->IsAssignableTo(mostSpecificType, sigType) && - !Relation()->IsLegalBoxedPrimitiveConversion(sigType, mostSpecificType)) { - auto funcName = sig->Function()->Id()->Name(); - LogError(diagnostic::AMBIGUOUS_CALL, {funcName, funcName, funcName, prevSig, funcName, sig}, pos); + } else { + CheckAmbiguousCall(mostSpecificType, sigType, prevSig, sig, pos); } } } diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index f2a5fd239c3..96773da5281 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -705,8 +705,7 @@ Type *ETSChecker::ResolveUnionUncheckedType(ArenaVector &&appar } auto *unionType = CreateETSUnionType(std::move(apparentTypes)); if (unionType->IsETSUnionType()) { - checker::Type *typeLUB = unionType->AsETSUnionType()->GetAssemblerLUB(); - return typeLUB; + return unionType->AsETSUnionType(); } // Is case of single apparent type, just return itself return unionType; diff --git a/ets2panda/checker/ets/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 725024a0bb1..c99b6e1ccd4 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -162,8 +162,12 @@ 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)); + auto ut = un->GetAssemblerType().Mutf8(); + if (std::count_if(ut.begin(), ut.end(), [](char c) { return c == ','; }) > 0) { + unionAssemblerTypes_.insert(un->GetAssemblerType()); + } + 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 34f539ad856..cd3902673a1 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -34,74 +34,125 @@ 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); -} - -ETSUnionType::ETSUnionType(ETSChecker *checker, ArenaVector &&constituentTypes) - : Type(TypeFlag::ETS_UNION), constituentTypes_(std::move(constituentTypes)) -{ - ES2PANDA_ASSERT(constituentTypes_.size() > 1); - assemblerLub_ = ComputeAssemblerLUB(checker, this); + if (assemblerConstituentTypes_.size() == 1) { + assemblerConstituentTypes_[0]->ToDebugInfoType(ss); + return; + } + ss << "{U"; + // NOLINTNEXTLINE(modernize-loop-convert) + for (size_t idx = 0; idx < assemblerConstituentTypes_.size(); idx++) { + assemblerConstituentTypes_[idx]->ToDebugInfoType(ss); + if (idx != assemblerConstituentTypes_.size() - 1) { + ss << ","; + } + } + ss << "}"; } -bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target) +static std::string GetAssemblerTypeString(Type *type) { - return std::all_of(source->constituentTypes_.begin(), source->constituentTypes_.end(), - [relation, target](auto *s) { return TypeRelatedToSomeType(relation, s, target); }); + std::stringstream ss; + type->ToAssemblerTypeWithRank(ss); + return ss.str(); } -bool ETSUnionType::TypeRelatedToSomeType(TypeRelation *relation, Type *source, ETSUnionType *target) +void ETSUnionType::InitAssemblerTypeCache(ETSChecker *checker) { - return std::any_of(target->constituentTypes_.begin(), target->constituentTypes_.end(), - [relation, source](auto *t) { return relation->IsIdenticalTo(source, t); }); + 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++) { + if (idx != 0) { + ss << ","; + } + if (assemblerConstituentTypes_[idx]->IsETSNullType()) { + ss << compiler::Signatures::NULL_ASSEMBLY_TYPE; + continue; + } + assemblerConstituentTypes_[idx]->ToAssemblerTypeWithRank(ss); + } + ss << "}"; + } + assemblerTypeCache_ = util::UString(ss.str(), checker->ProgramAllocator()).View(); } -// This function computes effective runtime representation of union type -Type *ETSUnionType::ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un) +void ETSUnionType::CanonicalizedAssemblerType(ETSChecker *checker) { - auto *const apparent = checker->GetApparentType(un); + auto *const apparent = checker->GetApparentType(this); if (!apparent->IsETSUnionType()) { - return apparent; + assemblerConstituentTypes_.push_back(apparent); + return; } - if (apparent != un) { - return apparent->AsETSUnionType()->assemblerLub_; + if (apparent != this) { + const auto &types = apparent->AsETSUnionType()->GetAssemblerTypes(); + assemblerConstituentTypes_.insert(assemblerConstituentTypes_.begin(), types.begin(), types.end()); + return; } - un = apparent->AsETSUnionType(); - - Type *lub = nullptr; - for (auto *t : un->ConstituentTypes()) { - if (t->IsTypeError()) { - return checker->GlobalTypeError(); - } - // NOTE(vpukhov): #19701 void refactoring - ES2PANDA_ASSERT(t->IsETSReferenceType() || t->IsETSVoidType()); - t = t->IsETSVoidType() ? checker->GlobalETSUndefinedType() : t; - if (lub == nullptr || lub->IsETSUndefinedType()) { - lub = t; + ES2PANDA_ASSERT(constituentTypes_.size() > 1); + bool hasNull = false; + for (auto *type : constituentTypes_) { + ES2PANDA_ASSERT(!type->IsETSUnionType()); + if (type->IsETSUndefinedType() || type->IsETSVoidType()) { continue; } - if (lub == t || t->IsETSUndefinedType()) { + if (type->IsETSNullType() && !hasNull) { + hasNull = true; + assemblerConstituentTypes_.push_back(type); continue; } - if (t->IsETSNullType()) { - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); + if (type->IsTypeError()) { + assemblerConstituentTypes_.clear(); + assemblerConstituentTypes_.push_back(checker->GlobalTypeError()); + return; } - if (t->IsETSObjectType() && lub->IsETSObjectType()) { - lub = checker->GetClosestCommonAncestor(lub->AsETSObjectType(), t->AsETSObjectType()); - } else if (t->IsETSArrayType() && lub->IsETSArrayType()) { - // NOTE: can compute "common(lub, t)[]" - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); - } else { - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); + 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); } } - return checker->GetNonConstantType(lub); + 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)), + assemblerConstituentTypes_(checker->ProgramAllocator()->Adapter()) +{ + ES2PANDA_ASSERT(constituentTypes_.size() > 1); + CanonicalizedAssemblerType(checker); + InitAssemblerTypeCache(checker); +} + +bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target) +{ + return std::all_of(source->constituentTypes_.begin(), source->constituentTypes_.end(), + [relation, target](auto *s) { return TypeRelatedToSomeType(relation, s, target); }); +} + +bool ETSUnionType::TypeRelatedToSomeType(TypeRelation *relation, Type *source, ETSUnionType *target) +{ + return std::any_of(target->constituentTypes_.begin(), target->constituentTypes_.end(), + [relation, source](auto *t) { return relation->IsIdenticalTo(source, t); }); } void ETSUnionType::Identical(TypeRelation *relation, Type *other) diff --git a/ets2panda/checker/types/ets/etsUnionType.h b/ets2panda/checker/types/ets/etsUnionType.h index 54c15370d85..3a4006b71c7 100644 --- a/ets2panda/checker/types/ets/etsUnionType.h +++ b/ets2panda/checker/types/ets/etsUnionType.h @@ -54,10 +54,34 @@ public: [[nodiscard]] ArenaVector GetNonConstantTypes(ETSChecker *checker) const noexcept; - // Do not use it anywhere except codegen - Type *GetAssemblerLUB() const noexcept + const util::StringView &GetAssemblerType() const { - return assemblerLub_; + return assemblerTypeCache_; + } + + const ArenaVector &GetAssemblerTypes() const + { + return assemblerConstituentTypes_; + } + + bool ContainsNull() const + { + for (auto *type : ConstituentTypes()) { + if (type->IsETSNullType()) { + return true; + } + } + return false; + } + + bool ContainsUndefined() const + { + for (auto *type : ConstituentTypes()) { + if (type->IsETSUndefinedType()) { + return true; + } + } + return false; } template @@ -105,10 +129,12 @@ private: checker::ETSChecker *checker, checker::ETSObjectType *sourceType, std::map &numericTypes) const noexcept; - static Type *ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un); + void CanonicalizedAssemblerType(ETSChecker *checker); + void InitAssemblerTypeCache(ETSChecker *checker); ArenaVector const constituentTypes_; - Type *assemblerLub_ {nullptr}; + ArenaVector assemblerConstituentTypes_; + util::StringView assemblerTypeCache_; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 4e62393a954..f60ed50362c 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -586,14 +586,23 @@ void ETSGen::ReturnAcc(const ir::AstNode *node) static bool IsNullUnsafeObjectType(checker::Type const *type) { + if (type->IsETSUnionType()) { + for (auto *t : type->AsETSUnionType()->GetAssemblerTypes()) { + if (IsNullUnsafeObjectType(t)) { + return true; + } + } + return false; + } return type->IsETSObjectType() && type->AsETSObjectType()->IsGlobalETSObjectType(); } // Implemented on top of the runtime type system, do not relax checks, do not introduce new types -void ETSGen::TestIsInstanceConstituent(const ir::AstNode *const node, std::tuple