• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "unionLowering.h"
17 #include "compiler/core/ASTVerifier.h"
18 #include "varbinder/variableFlags.h"
19 #include "varbinder/ETSBinder.h"
20 #include "checker/ETSchecker.h"
21 #include "checker/ets/conversion.h"
22 #include "checker/ets/boxingConverter.h"
23 #include "compiler/core/compilerContext.h"
24 #include "compiler/lowering/util.h"
25 #include "ir/base/classDefinition.h"
26 #include "ir/base/classProperty.h"
27 #include "ir/astNode.h"
28 #include "ir/expression.h"
29 #include "ir/opaqueTypeNode.h"
30 #include "ir/expressions/literals/nullLiteral.h"
31 #include "ir/expressions/literals/undefinedLiteral.h"
32 #include "ir/expressions/binaryExpression.h"
33 #include "ir/expressions/identifier.h"
34 #include "ir/expressions/memberExpression.h"
35 #include "ir/statements/blockStatement.h"
36 #include "ir/statements/classDeclaration.h"
37 #include "ir/statements/variableDeclaration.h"
38 #include "ir/ts/tsAsExpression.h"
39 #include "type_helper.h"
40 
41 namespace panda::es2panda::compiler {
GetUnionFieldClass(checker::ETSChecker * checker,varbinder::VarBinder * varbinder)42 ir::ClassDefinition *GetUnionFieldClass(checker::ETSChecker *checker, varbinder::VarBinder *varbinder)
43 {
44     // Create the name for the synthetic class node
45     util::UString unionFieldClassName(util::StringView(panda_file::GetDummyClassName()), checker->Allocator());
46     varbinder::Variable *foundVar = nullptr;
47     if ((foundVar = checker->Scope()->FindLocal(unionFieldClassName.View(),
48                                                 varbinder::ResolveBindingOptions::BINDINGS)) != nullptr) {
49         return foundVar->Declaration()->Node()->AsClassDeclaration()->Definition();
50     }
51     auto *ident = checker->AllocNode<ir::Identifier>(unionFieldClassName.View(), checker->Allocator());
52     auto [decl, var] = varbinder->NewVarDecl<varbinder::ClassDecl>(ident->Start(), ident->Name());
53     ident->SetVariable(var);
54 
55     auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>(varbinder);
56     auto *classDef =
57         checker->AllocNode<ir::ClassDefinition>(checker->Allocator(), ident, ir::ClassDefinitionModifiers::GLOBAL,
58                                                 ir::ModifierFlags::NONE, Language(Language::Id::ETS));
59     classDef->SetScope(classCtx.GetScope());
60     auto *classDecl = checker->AllocNode<ir::ClassDeclaration>(classDef, checker->Allocator());
61     classDef->Scope()->BindNode(classDecl);
62     classDef->SetTsType(checker->GlobalETSObjectType());
63     decl->BindNode(classDecl);
64     var->SetScope(classDef->Scope());
65 
66     varbinder->AsETSBinder()->BuildClassDefinition(classDef);
67     return classDef;
68 }
69 
CreateUnionFieldClassProperty(checker::ETSChecker * checker,varbinder::VarBinder * varbinder,checker::Type * fieldType,const util::StringView & propName)70 varbinder::LocalVariable *CreateUnionFieldClassProperty(checker::ETSChecker *checker, varbinder::VarBinder *varbinder,
71                                                         checker::Type *fieldType, const util::StringView &propName)
72 {
73     auto *const allocator = checker->Allocator();
74     auto *const dummyClass = GetUnionFieldClass(checker, varbinder);
75     auto *classScope = dummyClass->Scope()->AsClassScope();
76 
77     // Enter the union filed class instance field scope
78     auto fieldCtx = varbinder::LexicalScope<varbinder::LocalScope>::Enter(varbinder, classScope->InstanceFieldScope());
79 
80     if (auto *var = classScope->FindLocal(propName, varbinder::ResolveBindingOptions::VARIABLES); var != nullptr) {
81         return var->AsLocalVariable();
82     }
83 
84     // Create field name for synthetic class
85     auto *fieldIdent = allocator->New<ir::Identifier>(propName, allocator);
86 
87     // Create the synthetic class property node
88     auto *field =
89         allocator->New<ir::ClassProperty>(fieldIdent, nullptr, nullptr, ir::ModifierFlags::NONE, allocator, false);
90 
91     // Add the declaration to the scope
92     auto [decl, var] = varbinder->NewVarDecl<varbinder::LetDecl>(fieldIdent->Start(), fieldIdent->Name());
93     var->AddFlag(varbinder::VariableFlags::PROPERTY);
94     var->SetTsType(fieldType);
95     fieldIdent->SetVariable(var);
96     field->SetTsType(fieldType);
97     decl->BindNode(field);
98 
99     ArenaVector<ir::AstNode *> fieldDecl {allocator->Adapter()};
100     fieldDecl.push_back(field);
101     dummyClass->AddProperties(std::move(fieldDecl));
102     return var->AsLocalVariable();
103 }
104 
HandleUnionPropertyAccess(checker::ETSChecker * checker,varbinder::VarBinder * vbind,ir::MemberExpression * expr)105 void HandleUnionPropertyAccess(checker::ETSChecker *checker, varbinder::VarBinder *vbind, ir::MemberExpression *expr)
106 {
107     ASSERT(expr->PropVar() == nullptr);
108     expr->SetPropVar(
109         CreateUnionFieldClassProperty(checker, vbind, expr->TsType(), expr->Property()->AsIdentifier()->Name()));
110     ASSERT(expr->PropVar() != nullptr);
111 }
112 
GenAsExpression(checker::ETSChecker * checker,checker::Type * const opaqueType,ir::Expression * const node,ir::AstNode * const parent)113 ir::TSAsExpression *GenAsExpression(checker::ETSChecker *checker, checker::Type *const opaqueType,
114                                     ir::Expression *const node, ir::AstNode *const parent)
115 {
116     auto *const typeNode = checker->AllocNode<ir::OpaqueTypeNode>(opaqueType);
117     auto *const asExpression = checker->AllocNode<ir::TSAsExpression>(node, typeNode, false);
118     asExpression->SetParent(parent);
119     node->SetParent(asExpression);
120     asExpression->Check(checker);
121     return asExpression;
122 }
123 
124 /*
125  *  Function that generates conversion from (union) to (primitive) type as to `as` expressions:
126  *      (union) as (prim) => ((union) as (ref)) as (prim),
127  *      where (ref) is some unboxable type from union constituent types.
128  *  Finally, `(union) as (prim)` expression replaces union_node that came above.
129  */
UnionCastToPrimitive(checker::ETSChecker * checker,checker::ETSObjectType * unboxableRef,checker::Type * unboxedPrim,ir::Expression * unionNode)130 ir::TSAsExpression *UnionCastToPrimitive(checker::ETSChecker *checker, checker::ETSObjectType *unboxableRef,
131                                          checker::Type *unboxedPrim, ir::Expression *unionNode)
132 {
133     auto *const unionAsRefExpression = GenAsExpression(checker, unboxableRef, unionNode, nullptr);
134     unionAsRefExpression->SetBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxedPrim));
135     unionNode->SetParent(unionAsRefExpression);
136 
137     auto *const refAsPrimExpression = GenAsExpression(checker, unboxedPrim, unionAsRefExpression, unionNode->Parent());
138     unionAsRefExpression->SetParent(refAsPrimExpression);
139 
140     return refAsPrimExpression;
141 }
142 
HandleUnionCastToPrimitive(checker::ETSChecker * checker,ir::TSAsExpression * expr)143 ir::TSAsExpression *HandleUnionCastToPrimitive(checker::ETSChecker *checker, ir::TSAsExpression *expr)
144 {
145     auto *const unionType = expr->Expr()->TsType()->AsETSUnionType();
146     auto *sourceType = unionType->FindExactOrBoxedType(checker, expr->TsType());
147     if (sourceType == nullptr) {
148         sourceType = unionType->AsETSUnionType()->FindTypeIsCastableToSomeType(expr->Expr(), checker->Relation(),
149                                                                                expr->TsType());
150     }
151     if (sourceType != nullptr && expr->Expr()->GetBoxingUnboxingFlags() != ir::BoxingUnboxingFlags::NONE) {
152         if (expr->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
153             auto *const asExpr = GenAsExpression(checker, sourceType, expr->Expr(), expr);
154             asExpr->SetBoxingUnboxingFlags(
155                 checker->GetUnboxingFlag(checker->ETSBuiltinTypeAsPrimitiveType(sourceType)));
156             expr->Expr()->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
157             expr->SetExpr(asExpr);
158         }
159         return expr;
160     }
161     auto *const unboxableUnionType = sourceType != nullptr ? sourceType : unionType->FindUnboxableType();
162     auto *const unboxedUnionType = checker->ETSBuiltinTypeAsPrimitiveType(unboxableUnionType);
163     expr->SetExpr(UnionCastToPrimitive(checker, unboxableUnionType->AsETSObjectType(), unboxedUnionType, expr->Expr()));
164     return expr;
165 }
166 
GenInstanceofExpr(checker::ETSChecker * checker,ir::Expression * unionNode,checker::Type * constituentType)167 ir::BinaryExpression *GenInstanceofExpr(checker::ETSChecker *checker, ir::Expression *unionNode,
168                                         checker::Type *constituentType)
169 {
170     auto *const lhsExpr = unionNode->Clone(checker->Allocator())->AsExpression();
171     lhsExpr->Check(checker);
172     lhsExpr->SetBoxingUnboxingFlags(unionNode->GetBoxingUnboxingFlags());
173     auto *rhsType = constituentType;
174     if (!constituentType->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) {
175         checker->Relation()->SetNode(unionNode);
176         rhsType = checker::conversion::Boxing(checker->Relation(), constituentType);
177         checker->Relation()->SetNode(nullptr);
178     }
179     if (constituentType->IsETSStringType()) {
180         rhsType = checker->GlobalBuiltinETSStringType();
181     }
182     ir::Expression *rhsExpr;
183     if (rhsType->IsETSUndefinedType()) {
184         rhsExpr = checker->Allocator()->New<ir::UndefinedLiteral>();
185     } else if (rhsType->IsETSNullType()) {
186         rhsExpr = checker->Allocator()->New<ir::NullLiteral>();
187     } else {
188         rhsExpr = checker->Allocator()->New<ir::Identifier>(rhsType->AsETSObjectType()->Name(), checker->Allocator());
189         auto rhsVar = NearestScope(unionNode)->Find(rhsExpr->AsIdentifier()->Name());
190         rhsExpr->AsIdentifier()->SetVariable(rhsVar.variable);
191     }
192     auto *const instanceofExpr =
193         checker->Allocator()->New<ir::BinaryExpression>(rhsExpr, rhsExpr, lexer::TokenType::KEYW_INSTANCEOF);
194     rhsExpr->SetParent(instanceofExpr);
195     rhsExpr->SetParent(instanceofExpr);
196     rhsExpr->SetTsType(rhsType);
197     instanceofExpr->SetOperationType(checker->GlobalETSObjectType());
198     instanceofExpr->SetTsType(checker->GlobalETSBooleanType());
199     return instanceofExpr;
200 }
201 
GenVariableDeclForBinaryExpr(checker::ETSChecker * checker,varbinder::Scope * scope,ir::BinaryExpression * expr)202 ir::VariableDeclaration *GenVariableDeclForBinaryExpr(checker::ETSChecker *checker, varbinder::Scope *scope,
203                                                       ir::BinaryExpression *expr)
204 {
205     ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EQUAL ||
206            expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NOT_EQUAL);
207     auto *varId = Gensym(checker->Allocator());
208     auto *var = scope->AddDecl<varbinder::LetDecl, varbinder::LocalVariable>(checker->Allocator(), varId->Name(),
209                                                                              varbinder::VariableFlags::LOCAL);
210     var->SetTsType(checker->GlobalETSBooleanType());
211     varId->SetVariable(var);
212     varId->SetTsType(var->TsType());
213 
214     auto declarator = checker->AllocNode<ir::VariableDeclarator>(ir::VariableDeclaratorFlag::LET, varId);
215     ArenaVector<ir::VariableDeclarator *> declarators(checker->Allocator()->Adapter());
216     declarators.push_back(declarator);
217 
218     auto varKind = ir::VariableDeclaration::VariableDeclarationKind::LET;
219     auto *binaryVarDecl =
220         checker->AllocNode<ir::VariableDeclaration>(varKind, checker->Allocator(), std::move(declarators), false);
221     binaryVarDecl->SetRange({expr->Start(), expr->End()});
222     return binaryVarDecl;
223 }
224 
GenExpressionStmtWithAssignment(checker::ETSChecker * checker,ir::Identifier * varDeclId,ir::Expression * expr)225 ir::ExpressionStatement *GenExpressionStmtWithAssignment(checker::ETSChecker *checker, ir::Identifier *varDeclId,
226                                                          ir::Expression *expr)
227 {
228     auto *assignmentForBinary =
229         checker->AllocNode<ir::AssignmentExpression>(varDeclId, expr, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
230     assignmentForBinary->SetTsType(expr->TsType());
231     return checker->AllocNode<ir::ExpressionStatement>(assignmentForBinary);
232 }
233 
GenBlockStmtForAssignmentBinary(checker::ETSChecker * checker,ir::Identifier * varDeclId,ir::Expression * expr)234 ir::BlockStatement *GenBlockStmtForAssignmentBinary(checker::ETSChecker *checker, ir::Identifier *varDeclId,
235                                                     ir::Expression *expr)
236 {
237     auto localCtx = varbinder::LexicalScope<varbinder::LocalScope>(checker->VarBinder());
238     ArenaVector<ir::Statement *> stmts(checker->Allocator()->Adapter());
239     auto *stmt = GenExpressionStmtWithAssignment(checker, varDeclId, expr);
240     stmts.push_back(stmt);
241     auto *const localBlockStmt = checker->AllocNode<ir::BlockStatement>(checker->Allocator(), std::move(stmts));
242     localBlockStmt->SetScope(localCtx.GetScope());
243     stmt->SetParent(localBlockStmt);
244     localBlockStmt->SetRange(stmt->Range());
245     localCtx.GetScope()->BindNode(localBlockStmt);
246     return localBlockStmt;
247 }
248 
SetBoxFlagOrGenAsExpression(checker::ETSChecker * checker,checker::Type * constituentType,ir::Expression * otherNode)249 ir::Expression *SetBoxFlagOrGenAsExpression(checker::ETSChecker *checker, checker::Type *constituentType,
250                                             ir::Expression *otherNode)
251 {
252     if (constituentType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::UNBOXABLE_TYPE) &&
253         !otherNode->IsETSUnionType() && otherNode->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
254         auto *unboxedConstituentType = checker->ETSBuiltinTypeAsPrimitiveType(constituentType);
255         if (unboxedConstituentType != otherNode->TsType()) {
256             auto *const primAsExpression =
257                 GenAsExpression(checker, unboxedConstituentType, otherNode, otherNode->Parent());
258             primAsExpression->SetBoxingUnboxingFlags(checker->GetBoxingFlag(constituentType));
259             return primAsExpression;
260         }
261         return otherNode;
262     }
263     if (otherNode->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
264         otherNode->SetBoxingUnboxingFlags(
265             checker->GetBoxingFlag(checker::BoxingConverter::ETSTypeFromSource(checker, otherNode->TsType())));
266     }
267     return otherNode;
268 }
269 
ProcessOperandsInBinaryExpr(checker::ETSChecker * checker,ir::BinaryExpression * expr,checker::Type * constituentType)270 ir::Expression *ProcessOperandsInBinaryExpr(checker::ETSChecker *checker, ir::BinaryExpression *expr,
271                                             checker::Type *constituentType)
272 {
273     ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EQUAL ||
274            expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NOT_EQUAL);
275     bool isLhsUnion = false;
276     ir::Expression *unionNode = (isLhsUnion = expr->Left()->TsType()->IsETSUnionType()) ? expr->Left() : expr->Right();
277     checker::Type *typeToCast = constituentType->IsETSNullLike()
278                                     ? unionNode->TsType()->AsETSUnionType()->GetLeastUpperBoundType()
279                                     : constituentType;
280     auto *const asExpression = GenAsExpression(checker, typeToCast, unionNode, expr);
281     if (isLhsUnion) {
282         expr->SetLeft(asExpression);
283         expr->SetRight(SetBoxFlagOrGenAsExpression(checker, constituentType, expr->Right()));
284     } else {
285         expr->SetRight(asExpression);
286         expr->SetLeft(SetBoxFlagOrGenAsExpression(checker, constituentType, expr->Left()));
287     }
288     expr->SetOperationType(checker->GlobalETSObjectType());
289     expr->SetTsType(checker->GlobalETSBooleanType());
290     return expr;
291 }
292 
FindStatementFromNode(ir::Expression * expr)293 ir::Statement *FindStatementFromNode(ir::Expression *expr)
294 {
295     ir::AstNode *node = expr;
296     while (!node->IsStatement()) {
297         node = node->Parent();
298     }
299     ASSERT(node->IsStatement());
300     return node->AsStatement();
301 }
302 
InsertInstanceofTreeBeforeStmt(ir::Statement * stmt,ir::VariableDeclaration * binaryVarDecl,ir::Statement * instanceofTree)303 void InsertInstanceofTreeBeforeStmt(ir::Statement *stmt, ir::VariableDeclaration *binaryVarDecl,
304                                     ir::Statement *instanceofTree)
305 {
306     if (stmt->IsVariableDeclarator()) {
307         ASSERT(stmt->Parent()->IsVariableDeclaration());
308         stmt = stmt->Parent()->AsVariableDeclaration();
309     }
310     ASSERT(stmt->Parent()->IsBlockStatement());
311     auto *block = stmt->Parent()->AsBlockStatement();
312     binaryVarDecl->SetParent(block);
313     instanceofTree->SetParent(block);
314     auto itStmt = std::find(block->Statements().begin(), block->Statements().end(), stmt);
315     block->Statements().insert(itStmt, {binaryVarDecl, instanceofTree});
316 }
317 
ReplaceBinaryExprInStmt(checker::ETSChecker * checker,ir::Expression * unionNode,ir::BlockStatement * block,ir::BinaryExpression * expr)318 ir::BlockStatement *ReplaceBinaryExprInStmt(checker::ETSChecker *checker, ir::Expression *unionNode,
319                                             ir::BlockStatement *block, ir::BinaryExpression *expr)
320 {
321     auto *stmt = FindStatementFromNode(expr);
322     ASSERT(stmt->IsVariableDeclarator() || block == stmt->Parent());  // statement with union
323     auto *const binaryVarDecl = GenVariableDeclForBinaryExpr(checker, NearestScope(stmt), expr);
324     auto *const varDeclId = binaryVarDecl->Declarators().front()->Id();  // only one declarator was generated
325     ir::IfStatement *instanceofTree = nullptr;
326     for (auto *uType : unionNode->TsType()->AsETSUnionType()->ConstituentTypes()) {
327         auto *const test = GenInstanceofExpr(checker, unionNode, uType);
328         auto *clonedBinary = expr->Clone(checker->Allocator(), expr->Parent())->AsBinaryExpression();
329         clonedBinary->Check(checker);
330         auto *const consequent = GenBlockStmtForAssignmentBinary(
331             checker, varDeclId->AsIdentifier(), ProcessOperandsInBinaryExpr(checker, clonedBinary, uType));
332         instanceofTree = checker->Allocator()->New<ir::IfStatement>(test, consequent, instanceofTree);
333         test->SetParent(instanceofTree);
334         consequent->SetParent(instanceofTree);
335         if (instanceofTree->Alternate() != nullptr) {
336             instanceofTree->Alternate()->SetParent(instanceofTree);
337         }
338     }
339     ASSERT(instanceofTree != nullptr);
340     // Replacing a binary expression with an identifier
341     // that was set in one of the branches of the `instanceof_tree` tree
342     stmt->TransformChildrenRecursively([varDeclId](ir::AstNode *ast) -> ir::AstNode * {
343         if (ast->IsBinaryExpression() && ast->AsBinaryExpression()->OperationType() != nullptr &&
344             ast->AsBinaryExpression()->OperationType()->IsETSUnionType()) {
345             return varDeclId;
346         }
347 
348         return ast;
349     });
350     InsertInstanceofTreeBeforeStmt(stmt, binaryVarDecl, instanceofTree);
351     return block;
352 }
353 
HandleBlockWithBinaryAndUnion(checker::ETSChecker * checker,ir::BlockStatement * block,ir::BinaryExpression * binExpr)354 ir::BlockStatement *HandleBlockWithBinaryAndUnion(checker::ETSChecker *checker, ir::BlockStatement *block,
355                                                   ir::BinaryExpression *binExpr)
356 {
357     if (binExpr->OperatorType() != lexer::TokenType::PUNCTUATOR_EQUAL &&
358         binExpr->OperatorType() != lexer::TokenType::PUNCTUATOR_NOT_EQUAL) {
359         checker->ThrowTypeError("Bad operand type, unions are not allowed in binary expressions except equality.",
360                                 binExpr->Start());
361     }
362     ir::Expression *unionNode = binExpr->Left()->TsType()->IsETSUnionType() ? binExpr->Left() : binExpr->Right();
363     return ReplaceBinaryExprInStmt(checker, unionNode, block, binExpr);
364 }
365 
HandleBlockWithBinaryAndUnions(checker::ETSChecker * checker,ir::BlockStatement * block,const ir::NodePredicate & handleBinary)366 ir::BlockStatement *HandleBlockWithBinaryAndUnions(checker::ETSChecker *checker, ir::BlockStatement *block,
367                                                    const ir::NodePredicate &handleBinary)
368 {
369     ir::BlockStatement *modifiedAstBlock = block;
370     while (modifiedAstBlock->IsAnyChild(handleBinary)) {
371         modifiedAstBlock = HandleBlockWithBinaryAndUnion(
372             checker, modifiedAstBlock, modifiedAstBlock->FindChild(handleBinary)->AsBinaryExpression());
373     }
374     return modifiedAstBlock;
375 }
376 
Perform(public_lib::Context * ctx,parser::Program * program)377 bool UnionLowering::Perform(public_lib::Context *ctx, parser::Program *program)
378 {
379     for (auto &[_, ext_programs] : program->ExternalSources()) {
380         (void)_;
381         for (auto *extProg : ext_programs) {
382             Perform(ctx, extProg);
383         }
384     }
385 
386     checker::ETSChecker *checker = ctx->checker->AsETSChecker();
387 
388     program->Ast()->TransformChildrenRecursively([checker](ir::AstNode *ast) -> ir::AstNode * {
389         if (ast->IsMemberExpression() && ast->AsMemberExpression()->Object()->TsType() != nullptr &&
390             ast->AsMemberExpression()->Object()->TsType()->IsETSUnionType()) {
391             HandleUnionPropertyAccess(checker, checker->VarBinder(), ast->AsMemberExpression());
392             return ast;
393         }
394 
395         if (ast->IsTSAsExpression() && ast->AsTSAsExpression()->Expr()->TsType() != nullptr &&
396             ast->AsTSAsExpression()->Expr()->TsType()->IsETSUnionType() &&
397             ast->AsTSAsExpression()->TsType() != nullptr &&
398             ast->AsTSAsExpression()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
399             return HandleUnionCastToPrimitive(checker, ast->AsTSAsExpression());
400         }
401 
402         auto handleBinary = [](const ir::AstNode *astNode) {
403             return astNode->IsBinaryExpression() && astNode->AsBinaryExpression()->OperationType() != nullptr &&
404                    astNode->AsBinaryExpression()->OperationType()->IsETSUnionType();
405         };
406         if (ast->IsBlockStatement() && ast->IsAnyChild(handleBinary)) {
407             return HandleBlockWithBinaryAndUnions(checker, ast->AsBlockStatement(), handleBinary);
408         }
409 
410         return ast;
411     });
412 
413     return true;
414 }
415 
Postcondition(public_lib::Context * ctx,const parser::Program * program)416 bool UnionLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program)
417 {
418     bool current = !program->Ast()->IsAnyChild([](const ir::AstNode *ast) {
419         return ast->IsMemberExpression() && ast->AsMemberExpression()->Object()->TsType() != nullptr &&
420                ast->AsMemberExpression()->Object()->TsType()->IsETSUnionType() &&
421                ast->AsMemberExpression()->PropVar() == nullptr;
422     });
423     if (!current || ctx->compilerContext->Options()->compilationMode != CompilationMode::GEN_STD_LIB) {
424         return current;
425     }
426 
427     for (auto &[_, ext_programs] : program->ExternalSources()) {
428         (void)_;
429         for (auto *extProg : ext_programs) {
430             if (!Postcondition(ctx, extProg)) {
431                 return false;
432             }
433         }
434     }
435     return true;
436 }
437 
438 }  // namespace panda::es2panda::compiler
439