From 4b803d7571496ee75ec306359ec35192b82e5e3e Mon Sep 17 00:00:00 2001 From: Lyupa Anastasia Date: Tue, 17 Jun 2025 16:57:37 +0300 Subject: [PATCH] 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 Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICF4HV Tests: ninja all tests Change-Id: I98eb1ff29b9602a0724c6b16f87394b9bac8bbb7 Signed-off-by: Anna Antipina Signed-off-by: Lyupa Anastasia --- ets2panda/checker/ETSchecker.cpp | 7 +- ets2panda/checker/ETSchecker.h | 7 +- ets2panda/checker/ets/function.cpp | 22 +++- ets2panda/checker/ets/typeCheckingHelpers.cpp | 3 +- ets2panda/checker/ets/typeCreation.cpp | 5 +- ets2panda/checker/types/ets/etsUnionType.cpp | 62 ++--------- ets2panda/checker/types/ets/etsUnionType.h | 28 +++-- ets2panda/compiler/core/ETSGen.cpp | 104 ++++++++++++++---- ets2panda/compiler/core/ETSGen.h | 9 +- ets2panda/compiler/core/ETSemitter.cpp | 8 +- ets2panda/compiler/scripts/signatures.yaml | 4 +- ets2panda/test/unit/union_emit_test.cpp | 96 +++++++++++++++- 12 files changed, 253 insertions(+), 102 deletions(-) diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index 8b45002d11..deab42bd05 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -706,7 +706,12 @@ const GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes() const return globalArraySignatures_; } -const ArenaSet &ETSChecker::UnionAssemblerTypes() const +const ArenaSet &ETSChecker::UnionAssemblerTypes() const +{ + return unionAssemblerTypes_; +} + +ArenaSet &ETSChecker::UnionAssemblerTypes() { return unionAssemblerTypes_; } diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 4da77afe91..1bf89d60fc 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -174,7 +174,8 @@ public: GlobalArraySignatureMap &GlobalArrayTypes(); const GlobalArraySignatureMap &GlobalArrayTypes() const; - const ArenaSet &UnionAssemblerTypes() const; + const ArenaSet &UnionAssemblerTypes() const; + ArenaSet &UnionAssemblerTypes(); Type *GlobalTypeError() const; [[nodiscard]] Type *InvalidateType(ir::Typed *node); @@ -488,6 +489,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); @@ -1067,7 +1070,7 @@ private: FunctionInterfaceMap arrowToFuncInterfaces_; size_t constraintCheckScopesCount_ {0}; GlobalArraySignatureMap globalArraySignatures_; - ArenaSet unionAssemblerTypes_; + 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 3e78b0fc52..d787746b95 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 77d3cf08eb..822b6d2d75 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -681,8 +681,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 f45e2ac987..be742f86d2 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -163,7 +163,10 @@ Type *ETSChecker::CreateETSUnionType(Span constituentTypes) return newConstituentTypes[0]; } auto *un = ProgramAllocator()->New(this, std::move(newConstituentTypes)); - unionAssemblerTypes_.insert(un); + auto ut = un->GetAssemblerType().Mutf8(); + if (std::count_if(ut.begin(), ut.end(), [](char c) { return c == ','; }) > 0) { + unionAssemblerTypes_.insert(un->GetAssemblerType()); + } return un; } diff --git a/ets2panda/checker/types/ets/etsUnionType.cpp b/ets2panda/checker/types/ets/etsUnionType.cpp index 36a06ed594..10f4f3b134 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -44,6 +44,7 @@ void ETSUnionType::ToDebugInfoType(std::stringstream &ss) const 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) { @@ -69,10 +70,14 @@ void ETSUnionType::InitAssemblerTypeCache(ETSChecker *checker) } else { ss << "{U"; for (size_t idx = 0; idx < assemblerConstituentTypes_.size(); idx++) { - assemblerConstituentTypes_[idx]->ToAssemblerTypeWithRank(ss); - if (idx != assemblerConstituentTypes_.size() - 1) { + if (idx != 0) { ss << ","; } + if (assemblerConstituentTypes_[idx]->IsETSNullType()) { + ss << compiler::Signatures::NULL_ASSEMBLY_TYPE; + continue; + } + assemblerConstituentTypes_[idx]->ToAssemblerTypeWithRank(ss); } ss << "}"; } @@ -93,15 +98,16 @@ void ETSUnionType::CanonicalizedAssemblerType(ETSChecker *checker) } ES2PANDA_ASSERT(constituentTypes_.size() > 1); + bool hasNull = false; 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->IsETSNullType() && !hasNull) { + hasNull = true; + assemblerConstituentTypes_.push_back(type); + continue; } if (type->IsTypeError()) { assemblerConstituentTypes_.clear(); @@ -133,7 +139,6 @@ ETSUnionType::ETSUnionType(ETSChecker *checker, ArenaVector &&constituen assemblerConstituentTypes_(checker->ProgramAllocator()->Adapter()) { ES2PANDA_ASSERT(constituentTypes_.size() > 1); - assemblerLub_ = ComputeAssemblerLUB(checker, this); CanonicalizedAssemblerType(checker); InitAssemblerTypeCache(checker); } @@ -150,49 +155,6 @@ bool ETSUnionType::TypeRelatedToSomeType(TypeRelation *relation, Type *source, E [relation, source](auto *t) { return relation->IsIdenticalTo(source, t); }); } -// This function computes effective runtime representation of union type -Type *ETSUnionType::ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un) -{ - auto *const apparent = checker->GetApparentType(un); - if (!apparent->IsETSUnionType()) { - return apparent; - } - if (apparent != un) { - return apparent->AsETSUnionType()->assemblerLub_; - } - 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; - continue; - } - if (lub == t || t->IsETSUndefinedType()) { - continue; - } - if (t->IsETSNullType()) { - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); - } - 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(); - } - } - return checker->GetNonConstantType(lub); -} - void ETSUnionType::Identical(TypeRelation *relation, Type *other) { if (other->IsETSUnionType()) { diff --git a/ets2panda/checker/types/ets/etsUnionType.h b/ets2panda/checker/types/ets/etsUnionType.h index be1d6c3b29..3a4006b71c 100644 --- a/ets2panda/checker/types/ets/etsUnionType.h +++ b/ets2panda/checker/types/ets/etsUnionType.h @@ -54,12 +54,6 @@ public: [[nodiscard]] ArenaVector GetNonConstantTypes(ETSChecker *checker) const noexcept; - // Do not use it anywhere except codegen - Type *GetAssemblerLUB() const noexcept - { - return assemblerLub_; - } - const util::StringView &GetAssemblerType() const { return assemblerTypeCache_; @@ -70,6 +64,26 @@ public: 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 [[nodiscard]] bool AllOfConstituentTypes(UnaryPredicate p) const noexcept { @@ -115,14 +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_; ArenaVector assemblerConstituentTypes_; util::StringView assemblerTypeCache_; - Type *assemblerLub_ {nullptr}; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 4e62393a95..f60ed50362 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