diff --git a/ets2panda/compiler/lowering/ets/unboxLowering.cpp b/ets2panda/compiler/lowering/ets/unboxLowering.cpp index a6724b94bc4d85c8e1274fa6e2c05835645e023a..577bb6cd48194b21f31b846f6247cb540a44c688 100644 --- a/ets2panda/compiler/lowering/ets/unboxLowering.cpp +++ b/ets2panda/compiler/lowering/ets/unboxLowering.cpp @@ -106,40 +106,43 @@ static bool IsUnboxingApplicableReference(checker::Type *t) IsUnboxingApplicableReference)); } -static checker::Type *MaybeRecursivelyUnboxReferenceType(UnboxContext *uctx, checker::Type *t); +using TypeIdStorage = std::vector; // Long recursion chains are unlikely, use vector +static checker::Type *NormalizeType(UnboxContext *uctx, checker::Type *t, TypeIdStorage *alreadySeen = nullptr); +static checker::Type *MaybeRecursivelyUnboxReferenceType(UnboxContext *uctx, checker::Type *t, + TypeIdStorage *alreadySeen); -static checker::Type *MaybeRecursivelyUnboxType(UnboxContext *uctx, checker::Type *t) +static checker::Type *MaybeRecursivelyUnboxType(UnboxContext *uctx, checker::Type *t, + TypeIdStorage *alreadySeen = nullptr) { if (TypeIsBoxedPrimitive(t)) { return uctx->checker->MaybeUnboxType(t); } - return MaybeRecursivelyUnboxReferenceType(uctx, t); + return NormalizeType(uctx, t, alreadySeen); } -static checker::Type *MaybeRecursivelyUnboxTypeParameter(UnboxContext *uctx, checker::Type *t, std::uint64_t *typeId) +static checker::Type *MaybeRecursivelyUnboxTypeParameter(UnboxContext *uctx, checker::Type *t, + TypeIdStorage *alreadySeen) { + /* Any recursion involves type parameters */ + if (std::find(alreadySeen->begin(), alreadySeen->end(), t->Id()) != alreadySeen->end()) { + return t; + } + alreadySeen->push_back(t->Id()); + auto typeParameter = t->AsETSTypeParameter(); auto constraintType = typeParameter->GetConstraintType(); - // We need to avoid endless recursion in case of recursive generic types, say 'class A>' - if (*typeId == 0U) { - *typeId = t->Id(); - typeParameter->SetConstraintType(MaybeRecursivelyUnboxReferenceType(uctx, constraintType)); - *typeId = 0U; - } else if (t->Id() != *typeId) { - *typeId = t->Id(); - typeParameter->SetConstraintType(MaybeRecursivelyUnboxReferenceType(uctx, constraintType)); - } + typeParameter->SetConstraintType(MaybeRecursivelyUnboxReferenceType(uctx, constraintType, alreadySeen)); return t; } -static checker::Type *MaybeRecursivelyUnboxTupleType(UnboxContext *uctx, checker::Type *t) +static checker::Type *MaybeRecursivelyUnboxTupleType(UnboxContext *uctx, checker::Type *t, TypeIdStorage *alreadySeen) { bool anyChange = false; auto *srcTup = t->AsETSTupleType(); ArenaVector newTps {uctx->allocator->Adapter()}; for (auto *e : srcTup->GetTupleTypesList()) { - auto *newE = MaybeRecursivelyUnboxReferenceType(uctx, e); + auto *newE = MaybeRecursivelyUnboxReferenceType(uctx, e, alreadySeen); newTps.push_back(newE); anyChange |= (newE != e); } @@ -147,76 +150,80 @@ static checker::Type *MaybeRecursivelyUnboxTupleType(UnboxContext *uctx, checker return anyChange ? uctx->allocator->New(uctx->checker, newTps) : t; } -static checker::Type *MaybeRecursivelyUnboxUnionType(UnboxContext *uctx, checker::Type *t) +static checker::Type *MaybeRecursivelyUnboxUnionType(UnboxContext *uctx, checker::Type *t, TypeIdStorage *alreadySeen) { bool anyChange = false; auto *srcUnion = t->AsETSUnionType(); ArenaVector newTps {uctx->allocator->Adapter()}; for (auto *e : srcUnion->ConstituentTypes()) { - auto *newE = MaybeRecursivelyUnboxReferenceType(uctx, e); + auto *newE = MaybeRecursivelyUnboxReferenceType(uctx, e, alreadySeen); newTps.push_back(newE); anyChange |= (newE != e); } return anyChange ? uctx->checker->CreateETSUnionType(std::move(newTps)) : t; } -static checker::Type *MaybeRecursivelyUnboxObjectType(UnboxContext *uctx, checker::Type *t) +static checker::Type *MaybeRecursivelyUnboxObjectType(UnboxContext *uctx, checker::Type *t, TypeIdStorage *alreadySeen) { bool anyChange = false; auto *objTp = t->AsETSObjectType(); ArenaVector newTps {uctx->allocator->Adapter()}; for (auto *e : objTp->TypeArguments()) { - auto *newE = MaybeRecursivelyUnboxReferenceType(uctx, e); + auto *newE = MaybeRecursivelyUnboxReferenceType(uctx, e, alreadySeen); newTps.push_back(newE); anyChange |= (newE != e); } return anyChange ? objTp->GetOriginalBaseType()->SubstituteArguments(uctx->checker->Relation(), newTps) : t; } -static checker::Type *MaybeRecursivelyUnboxReferenceType(UnboxContext *uctx, checker::Type *t) +static checker::Type *MaybeRecursivelyUnboxReferenceType(UnboxContext *uctx, checker::Type *t, + TypeIdStorage *alreadySeen) { - static std::uint64_t typeId = 0U; - if (t == nullptr) { return t; } if (t->IsETSTypeParameter()) { - return MaybeRecursivelyUnboxTypeParameter(uctx, t, &typeId); + return MaybeRecursivelyUnboxTypeParameter(uctx, t, alreadySeen); } if (t->IsETSTupleType()) { - return MaybeRecursivelyUnboxTupleType(uctx, t); + return MaybeRecursivelyUnboxTupleType(uctx, t, alreadySeen); } if (t->IsETSArrayType()) { auto *srcArr = t->AsETSArrayType(); - auto *newE = MaybeRecursivelyUnboxType(uctx, srcArr->ElementType()); + auto *newE = MaybeRecursivelyUnboxType(uctx, srcArr->ElementType(), alreadySeen); return (newE == srcArr->ElementType()) ? t : uctx->checker->CreateETSArrayType(newE); } if (t->IsETSResizableArrayType()) { auto *srcArr = t->AsETSResizableArrayType(); - auto *newE = MaybeRecursivelyUnboxReferenceType(uctx, srcArr->ElementType()); + auto *newE = MaybeRecursivelyUnboxReferenceType(uctx, srcArr->ElementType(), alreadySeen); return (newE == srcArr->ElementType()) ? t : uctx->checker->CreateETSResizableArrayType(newE); } if (t->IsETSUnionType()) { - return MaybeRecursivelyUnboxUnionType(uctx, t); + return MaybeRecursivelyUnboxUnionType(uctx, t, alreadySeen); } if (t->IsETSObjectType()) { - return MaybeRecursivelyUnboxObjectType(uctx, t); + return MaybeRecursivelyUnboxObjectType(uctx, t, alreadySeen); } return t; } // We should never see an array of boxed primitives, even as a component of some bigger type construction -static checker::Type *NormalizeType(UnboxContext *uctx, checker::Type *tp) +static checker::Type *NormalizeType(UnboxContext *uctx, checker::Type *tp, TypeIdStorage *alreadySeen) { - return MaybeRecursivelyUnboxReferenceType(uctx, tp); + if (alreadySeen == nullptr) { + TypeIdStorage newAlreadySeen {}; + return MaybeRecursivelyUnboxReferenceType(uctx, tp, &newAlreadySeen); + } else { + return MaybeRecursivelyUnboxReferenceType(uctx, tp, alreadySeen); + } } static void NormalizeAllTypes(UnboxContext *uctx, ir::AstNode *ast)