diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 9e5a1738d944328abba7c6447f14e376589b03ff..c2c3e2b547806a8210da666cfa540794612cbaba 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -998,23 +998,28 @@ checker::Type *ETSChecker::GetExtensionAccessorReturnType(ir::MemberExpression * // Smart cast support //==============================================================================// +static checker::Type *MaybeReadonlyType(ETSChecker *checker, checker::Type *sourceType, checker::Type const *targetType) +{ + if (targetType->HasTypeFlag(TypeFlag::READONLY)) { + sourceType = sourceType->Clone(checker); + sourceType->AddTypeFlag(TypeFlag::READONLY); + } + return sourceType; +} + checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker::Type *targetType, std::optional value) { // For left-hand variable of primitive type leave it as is. ES2PANDA_ASSERT(!targetType->IsETSPrimitiveType() && !sourceType->IsETSPrimitiveType()); - if (Relation()->IsSupertypeOf(targetType, sourceType)) { + // For left-hand invalid variable set smart type to right-hand type. + if (targetType->IsTypeError()) { return sourceType; } - // For left-hand variable of tuple type leave it as is. - if (targetType->IsETSTupleType()) { - return targetType; - } - - // For left-hand invalid variable set smart type to right-hand type. - if (targetType->IsTypeError()) { + // For type parameter, null or undefined source type return it as is. + if (sourceType->IsETSTypeParameter() || sourceType->DefinitelyETSNullish()) { return sourceType; } @@ -1023,20 +1028,17 @@ checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker:: return targetType; } - // For type parameter, null or undefined source type return it as is. - if (sourceType->IsETSTypeParameter() || sourceType->DefinitelyETSNullish()) { - return sourceType; - } - - // In case of Union left-hand type we have to select the proper type from the Union - // Because now we have logging of errors we have to continue analyze incorrect program, for - // this case we change typeError to source type. - if (targetType->IsETSUnionType()) { - return targetType->AsETSUnionType()->GetAssignableType(this, sourceType, value); + // In case of Union left-hand type we try to select the proper type from the Union + if (targetType->IsETSUnionType() && !sourceType->IsUnionType()) { + auto *constituentType = targetType->AsETSUnionType()->GetAssignableType(this, sourceType, value); + if (constituentType != nullptr) { + return MaybeReadonlyType(this, sourceType, constituentType); + } } + // General case - return more specific subtype if (Relation()->IsSupertypeOf(targetType, sourceType)) { - return sourceType; + return MaybeReadonlyType(this, sourceType, targetType); } return targetType; diff --git a/ets2panda/checker/types/ets/etsUnionType.cpp b/ets2panda/checker/types/ets/etsUnionType.cpp index 9de7883161965cde39333714c5e62dc07032756a..7e1743576091523ad929e9da1a0494758e5bc800 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -296,22 +296,20 @@ void ETSUnionType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag } } -bool ETSUnionType::IsAssignableType(checker::Type *sourceType) const noexcept -{ - return (sourceType->IsETSUnionType() || sourceType->IsETSArrayType() || sourceType->IsETSFunctionType() || - sourceType->IsETSTypeParameter() || sourceType->IsETSTupleType() || sourceType->IsTypeError()); -} - // ATTENTION! When calling this method we assume that 'AssignmentTarget(...)' check was passes successfully, // thus the required assignable type (or corresponding supertype) always exists. checker::Type *ETSUnionType::GetAssignableType(checker::ETSChecker *checker, checker::Type *sourceType, [[maybe_unused]] std::optional value) const noexcept { - if (IsAssignableType(sourceType)) { - return sourceType; + for (auto *ctype : ConstituentTypes()) { + if (checker->Relation()->IsSupertypeOf(ctype, sourceType)) { + return ctype; + } } - ES2PANDA_ASSERT(sourceType->IsETSObjectType()); + if (!sourceType->IsETSObjectType()) { + return nullptr; + } auto *objectType = checker->GetNonConstantType(sourceType)->AsETSObjectType(); if (!objectType->IsBuiltinNumeric()) { @@ -320,7 +318,7 @@ checker::Type *ETSUnionType::GetAssignableType(checker::ETSChecker *checker, che // NOTE (DZ): we still keep 'numericTypes` collection for possible processing cases like 'let x: short|double = 1` // Waiting for complete clearness in spec - now return the highest type in such a case or type itself. - // 'value' will be used for the same purpose + // Maybe 'value' will be used for this purpose std::map numericTypes {}; if (auto *assignableType = GetAssignableBuiltinType(checker, objectType, numericTypes); assignableType != nullptr) { return assignableType; diff --git a/ets2panda/checker/types/ets/etsUnionType.h b/ets2panda/checker/types/ets/etsUnionType.h index b027585b57e24ab8ffda08e3596caa9ac7ebbc08..54c15370d85c9d453a8183ea0844b4a576856743 100644 --- a/ets2panda/checker/types/ets/etsUnionType.h +++ b/ets2panda/checker/types/ets/etsUnionType.h @@ -105,8 +105,6 @@ private: checker::ETSChecker *checker, checker::ETSObjectType *sourceType, std::map &numericTypes) const noexcept; - bool IsAssignableType(checker::Type *sourceType) const noexcept; - static Type *ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un); ArenaVector const constituentTypes_;