Code review comment: We call this method for any `BLangRecordLiteral` [here](https://github.com/ballerina-platform/ballerina-lang/blob/bc7e5e948b6dadc96ea5c781c751904e98f3f3fc/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java#L6088), so we need to determine if the compiler need to explicitly add fields to the RecordLiteral or not in this method.

Code under review:
private BLangBlockStmt desugarForeachToWhileWithIterator(BLangForeach foreach, BLangSimpleVariableDef varDef) {

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        
        

        

        BVarSymbol iteratorSymbol = varDef.var.symbol;

        
        BVarSymbol resultSymbol = new BVarSymbol(0, Names.fromString("$result$"), this.env.scope.owner.pkgID,
                                                 foreach.nillableResultType, this.env.scope.owner, foreach.pos,
                                                 VIRTUAL);

        
        BLangSimpleVariableDef resultVariableDefinition = getIteratorNextVariableDefinition(foreach.pos,
                foreach.nillableResultType, iteratorSymbol, resultSymbol);

        
        BLangSimpleVarRef resultReferenceInWhile = ASTBuilderUtil.createVariableRef(foreach.pos, resultSymbol);
        BLangStatementExpression statementExpression = ASTBuilderUtil.createStatementExpression(
                resultVariableDefinition, resultReferenceInWhile);
        statementExpression.setBType(foreach.nillableResultType);

        
        BLangType userDefineType = getUserDefineTypeNode(foreach.resultType);
        BLangTypeTestExpr typeTestExpr = ASTBuilderUtil
                .createTypeTestExpr(foreach.pos, statementExpression, userDefineType);
        
        BLangWhile whileNode = (BLangWhile) TreeBuilder.createWhileNode();
        whileNode.pos = foreach.pos;
        whileNode.expr = typeTestExpr;
        whileNode.body = foreach.body;

        VariableDefinitionNode variableDefinitionNode = foreach.variableDefinitionNode;

        
        
        
        BLangFieldBasedAccess valueAccessExpr = getValueAccessExpression(foreach.pos, foreach.varType, resultSymbol);

        BLangExpression expr = valueAccessExpr.expr;
        
        
        valueAccessExpr.expr = types.addConversionExprIfRequired(expr, symTable.mapAllType);
        variableDefinitionNode.getVariable()
                .setInitialExpression(types.addConversionExprIfRequired(valueAccessExpr, foreach.varType));
        whileNode.body.stmts.add(0, (BLangStatement) variableDefinitionNode);

        
        BLangBlockStmt blockNode = ASTBuilderUtil.createBlockStmt(foreach.pos);

        
        blockNode.addStatement(varDef);

        
        blockNode.addStatement(whileNode);
        return blockNode;
    }

    private BLangType getUserDefineTypeNode(BType type) {
        BLangUserDefinedType recordType =
                new BLangUserDefinedType(ASTBuilderUtil.createIdentifier(null, ""),
                                         ASTBuilderUtil.createIdentifier(null, ""));
        recordType.setBType(type);
        return recordType;
    }

    @Override
    public void visit(BLangWhile whileNode) {
        if (whileNode.onFailClause != null) {
            BLangOnFailClause onFailClause = whileNode.onFailClause;
            whileNode.onFailClause = null;
            whileNode.body.failureBreakMode = BLangBlockStmt.FailureBreakMode.NOT_BREAKABLE;
            BLangDo doStmt = wrapStatementWithinDo(whileNode.pos, whileNode, onFailClause);
            result = rewrite(doStmt, env);
        } else {
            whileNode.expr = rewriteExpr(whileNode.expr);
            whileNode.body = rewrite(whileNode.body, env);
            result = whileNode;
        }
    }

    private BLangDo wrapStatementWithinDo(Location location, BLangStatement statement,
                                          BLangOnFailClause onFailClause) {
        BLangDo bLDo = (BLangDo) TreeBuilder.createDoNode();
        BLangBlockStmt doBlock = ASTBuilderUtil.createBlockStmt(location);
        doBlock.scope = new Scope(env.scope.owner);
        bLDo.body = doBlock;
        bLDo.pos = location;
        bLDo.onFailClause = onFailClause;
        bLDo.body.failureBreakMode = BLangBlockStmt.FailureBreakMode.BREAK_TO_OUTER_BLOCK;
        doBlock.stmts.add(statement);
        return bLDo;
    }

    @Override
    public void visit(BLangLock lockNode) {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        BLangOnFailClause currentOnFailClause = this.onFailClause;
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(lockNode.pos);
        if (lockNode.onFailClause != null) {
            blockStmt.failureBreakMode = BLangBlockStmt.FailureBreakMode.BREAK_TO_OUTER_BLOCK;
            rewrite(lockNode.onFailClause, env);
        }
        BLangLockStmt lockStmt = new BLangLockStmt(lockNode.pos);
        blockStmt.addStatement(lockStmt);

        enclLocks.push(lockStmt);

        BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(lockNode.pos, symTable.nilType, Names.NIL_VALUE);
        BType nillableError = BUnionType.create(null, symTable.errorType, symTable.nilType);
        BLangStatementExpression statementExpression = createStatementExpression(lockNode.body, nilLiteral);
        statementExpression.setBType(symTable.nilType);

        BLangTrapExpr trapExpr = (BLangTrapExpr) TreeBuilder.createTrapExpressionNode();
        trapExpr.setBType(nillableError);
        trapExpr.expr = statementExpression;
        BVarSymbol nillableErrorVarSymbol = new BVarSymbol(0, Names.fromString("$errorResult"),
                                                           this.env.scope.owner.pkgID, nillableError,
                                                           this.env.scope.owner, lockNode.pos, VIRTUAL);
        BLangSimpleVariable simpleVariable = ASTBuilderUtil.createVariable(lockNode.pos, "$errorResult",
                                                                           nillableError, trapExpr,
                                                                           nillableErrorVarSymbol);
        BLangSimpleVariableDef simpleVariableDef = ASTBuilderUtil.createVariableDef(lockNode.pos, simpleVariable);
        blockStmt.addStatement(simpleVariableDef);

        BLangUnLockStmt unLockStmt = new BLangUnLockStmt(lockNode.pos);
        unLockStmt.relatedLock = lockStmt; 
        blockStmt.addStatement(unLockStmt);
        BLangSimpleVarRef varRef = ASTBuilderUtil.createVariableRef(lockNode.pos, nillableErrorVarSymbol);

        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(lockNode.pos);
        BLangPanic panicNode = (BLangPanic) TreeBuilder.createPanicNode();
        panicNode.pos = lockNode.pos;
        panicNode.expr = types.addConversionExprIfRequired(varRef, symTable.errorType);
        ifBody.addStatement(panicNode);

        BLangTypeTestExpr isErrorTest =
                ASTBuilderUtil.createTypeTestExpr(lockNode.pos, varRef, getErrorTypeNode());
        isErrorTest.setBType(symTable.booleanType);

        BLangIf ifelse = ASTBuilderUtil.createIfElseStmt(lockNode.pos, isErrorTest, ifBody, null);
        blockStmt.addStatement(ifelse);
        result = rewrite(blockStmt, env);
        enclLocks.pop();
        this.onFailClause = currentOnFailClause;
    }

    @Override
    public void visit(BLangLockStmt lockStmt) {
        result = lockStmt;
    }

    @Override
    public void visit(BLangUnLockStmt unLockStmt) {
        result = unLockStmt;
    }


    private BLangOnFailClause createTrxInternalOnFail(Location pos, BLangSimpleVarRef shouldPanicRef,
                                                      BLangSimpleVarRef shouldRetryRef) {
        BLangOnFailClause trxOnFailClause = (BLangOnFailClause) TreeBuilder.createOnFailClauseNode();
        trxOnFailClause.pos = pos;
        trxOnFailClause.body = ASTBuilderUtil.createBlockStmt(pos);
        trxOnFailClause.body.scope = new Scope(env.scope.owner);
        trxOnFailClause.isInternal = true;

        
        
        BVarSymbol trxOnFailErrorSym = new BVarSymbol(0, Names.fromString("$trxError$"),
                env.scope.owner.pkgID, symTable.errorType, env.scope.owner, pos, VIRTUAL);
        BLangSimpleVariable trxOnFailError = ASTBuilderUtil.createVariable(pos,
                "$trxError$", symTable.errorType, null, trxOnFailErrorSym);
        trxOnFailClause.variableDefinitionNode = ASTBuilderUtil.createVariableDef(pos,
                trxOnFailError);
        trxOnFailClause.body.scope.define(trxOnFailErrorSym.name, trxOnFailErrorSym);

        
        
        
        
        transactionDesugar.createRollbackIfFailed(pos, trxOnFailClause.body, trxOnFailErrorSym,
                trxBlockId, shouldRetryRef);

        BLangGroupExpr shouldNotPanic = new BLangGroupExpr();
        shouldNotPanic.setBType(symTable.booleanType);
        shouldNotPanic.expression = createNotBinaryExpression(pos, shouldPanicRef);

        BLangSimpleVarRef caughtError =  ASTBuilderUtil.createVariableRef(pos, trxOnFailErrorSym);

        BLangBlockStmt failBlock = ASTBuilderUtil.createBlockStmt(pos);

        BLangPanic panicNode = (BLangPanic) TreeBuilder.createPanicNode();
        panicNode.pos = pos;
        panicNode.expr = caughtError;

        
        
        
        
        
        BLangIf exitIf = ASTBuilderUtil.createIfElseStmt(pos, shouldNotPanic, failBlock, panicNode);
        trxOnFailClause.body.stmts.add(exitIf);

        BLangFail failStmt = (BLangFail) TreeBuilder.createFailNode();
        failStmt.pos = pos;
        failStmt.expr = caughtError;
        failBlock.stmts.add(failStmt);
        trxOnFailClause.bodyContainsFail = true;

        
        
        
        
        
        
        
        
        
        
        
        
        return trxOnFailClause;
    }

    @Override
    public void visit(BLangTransaction transactionNode) {
        if (transactionNode.onFailClause != null) {
            
            BLangOnFailClause onFailClause = transactionNode.onFailClause;
            transactionNode.onFailClause = null;
            transactionNode.transactionBody.failureBreakMode = BLangBlockStmt.FailureBreakMode.NOT_BREAKABLE;
            BLangDo doStmt = wrapStatementWithinDo(transactionNode.pos, transactionNode, onFailClause);
            
            
            
            
            
            
            
            
            result = rewrite(doStmt, env);
        } else {
            BLangLiteral currentTrxBlockId = this.trxBlockId;
            String uniqueId = String.valueOf(++transactionBlockCount);
            this.trxBlockId = ASTBuilderUtil.createLiteral(transactionNode.pos, symTable.stringType, uniqueId);
            boolean currShouldReturnErrors = this.shouldReturnErrors;
            this.shouldReturnErrors = true;

            BLangOnFailClause currOnFailClause = this.onFailClause;

            
            BLangLiteral falseLiteral = ASTBuilderUtil.createLiteral(transactionNode.pos, symTable.booleanType, false);
            BVarSymbol shouldPanicVarSymbol = new BVarSymbol(0, Names.fromString("$shouldPanic$"),
                    env.scope.owner.pkgID, symTable.booleanType, this.env.scope.owner, transactionNode.pos, VIRTUAL);
            shouldPanicVarSymbol.closure = true;
            BLangSimpleVariable shouldPanicVariable = ASTBuilderUtil.createVariable(transactionNode.pos,
                    "$shouldPanic$", symTable.booleanType, falseLiteral, shouldPanicVarSymbol);

            BLangSimpleVariableDef shouldPanicDef = ASTBuilderUtil.createVariableDef(transactionNode.pos,
                    shouldPanicVariable);
            BLangSimpleVarRef shouldPanicRef = ASTBuilderUtil.createVariableRef(transactionNode.pos,
                    shouldPanicVarSymbol);

            
            
            
            
            
            
            
            
            
            
            
            BLangOnFailClause trxInternalOnFail = createTrxInternalOnFail(transactionNode.pos, shouldPanicRef,
                    this.shouldRetryRef);
            enclosingShouldPanic.put(trxInternalOnFail, shouldPanicRef);

            boolean userDefinedOnFailAvbl = this.onFailClause != null;
            analyzeOnFailClause(trxInternalOnFail, transactionNode.transactionBody);

            BLangBlockStmt transactionStmtBlock =
                    transactionDesugar.rewrite(transactionNode, trxBlockId, env, uniqueId);

            transactionStmtBlock.stmts.add(0, shouldPanicDef);
            transactionStmtBlock.scope.define(shouldPanicVarSymbol.name, shouldPanicVarSymbol);
            transactionStmtBlock.failureBreakMode = userDefinedOnFailAvbl ?
                    BLangBlockStmt.FailureBreakMode.NOT_BREAKABLE :
                    BLangBlockStmt.FailureBreakMode.BREAK_TO_OUTER_BLOCK;

            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            result = rewrite(transactionStmtBlock, this.env);

            this.shouldReturnErrors = currShouldReturnErrors;
            this.trxBlockId = currentTrxBlockId;
            swapAndResetEnclosingOnFail(currOnFailClause);
        }
    }

    @Override
    public void visit(BLangRollback rollbackNode) {
        BLangBlockStmt rollbackStmtExpr = transactionDesugar.desugar(rollbackNode, trxBlockId, this.shouldRetryRef);
        result = rewrite(rollbackStmtExpr, env);
    }

    private BLangOnFailClause createRetryInternalOnFail(Location pos,
                                                        BLangSimpleVarRef retryResultRef,
                                                        BLangSimpleVarRef retryManagerRef,
                                                        BLangSimpleVarRef shouldRetryRef,
                                                        BLangSimpleVarRef continueLoopRef,
                                                        BLangSimpleVarRef returnResult) {
        BLangOnFailClause internalOnFail = (BLangOnFailClause) TreeBuilder.createOnFailClauseNode();
        internalOnFail.pos = pos;
        internalOnFail.body = ASTBuilderUtil.createBlockStmt(pos);
        internalOnFail.body.scope = new Scope(env.scope.owner);

        BVarSymbol caughtErrorSym = new BVarSymbol(0, Names.fromString("$caughtError$"),
                env.scope.owner.pkgID, symTable.errorType, env.scope.owner, pos, VIRTUAL);
        BLangSimpleVariable caughtError = ASTBuilderUtil.createVariable(pos,
                "$caughtError$", symTable.errorType, null, caughtErrorSym);
        internalOnFail.variableDefinitionNode = ASTBuilderUtil.createVariableDef(pos,
                caughtError);
        env.scope.define(caughtErrorSym.name, caughtErrorSym);
        BLangSimpleVarRef caughtErrorRef = ASTBuilderUtil.createVariableRef(pos, caughtErrorSym);

        
        BLangAssignment errorAssignment = ASTBuilderUtil.createAssignmentStmt(pos, retryResultRef, caughtErrorRef);
        internalOnFail.body.stmts.add(errorAssignment);

        
        BLangAssignment continueLoopTrue = ASTBuilderUtil.createAssignmentStmt(pos, continueLoopRef,
                ASTBuilderUtil.createLiteral(pos, symTable.booleanType, true));
        internalOnFail.body.stmts.add(continueLoopTrue);

        
        BLangInvocation shouldRetryInvocation = createRetryManagerShouldRetryInvocation(pos,
                retryManagerRef, caughtErrorRef);
        BLangAssignment shouldRetryAssignment = ASTBuilderUtil.createAssignmentStmt(pos, shouldRetryRef,
                shouldRetryInvocation);
        internalOnFail.body.stmts.add(shouldRetryAssignment);

        BLangGroupExpr shouldNotRetryCheck = new BLangGroupExpr();
        shouldNotRetryCheck.setBType(symTable.booleanType);
        shouldNotRetryCheck.expression = createNotBinaryExpression(pos, shouldRetryRef);

        BLangGroupExpr exitCheck = new BLangGroupExpr();
        exitCheck.setBType(symTable.booleanType);
        exitCheck.expression = shouldNotRetryCheck;

        BLangBlockStmt exitLogicBlock = ASTBuilderUtil.createBlockStmt(pos);
        BLangIf exitIf = ASTBuilderUtil.createIfElseStmt(pos, exitCheck, exitLogicBlock, null);

        if (this.onFailClause != null) {
            
            
            BLangFail failStmt = (BLangFail) TreeBuilder.createFailNode();
            failStmt.pos = pos;
            failStmt.expr = retryResultRef;

            exitLogicBlock.stmts.add(failStmt);
            internalOnFail.bodyContainsFail = true;
            internalOnFail.body.stmts.add(exitIf);

            
            BLangContinue loopContinueStmt = (BLangContinue) TreeBuilder.createContinueNode();
            loopContinueStmt.pos = pos;
            internalOnFail.body.stmts.add(loopContinueStmt);

            
            
            
            
        } else {
            BLangAssignment returnErrorTrue = ASTBuilderUtil.createAssignmentStmt(pos, returnResult,
                    ASTBuilderUtil.createLiteral(pos, symTable.booleanType, true));
            exitLogicBlock.stmts.add(returnErrorTrue);
            internalOnFail.body.stmts.add(exitIf);
            
            
            
        }
        return internalOnFail;
    }

    BLangUnaryExpr createNotBinaryExpression(Location pos, BLangExpression expression) {
        List<BType> paramTypes = new ArrayList<>();
        paramTypes.add(symTable.booleanType);
        BInvokableType type = new BInvokableType(paramTypes, symTable.booleanType,
                null);
        BOperatorSymbol notOperatorSymbol = new BOperatorSymbol(
                Names.fromString(OperatorKind.NOT.value()), symTable.rootPkgSymbol.pkgID, type, symTable.rootPkgSymbol,
                symTable.builtinPos, VIRTUAL);
        return ASTBuilderUtil.createUnaryExpr(pos, expression, symTable.booleanType,
                OperatorKind.NOT, notOperatorSymbol);
    }

    BLangLambdaFunction createLambdaFunction(Location pos, String functionNamePrefix,
                                             List<BLangSimpleVariable> lambdaFunctionVariable,
                                             TypeNode returnType, BLangFunctionBody lambdaBody) {
        BLangLambdaFunction lambdaFunction = (BLangLambdaFunction) TreeBuilder.createLambdaFunctionNode();
        BLangFunction func =
                ASTBuilderUtil.createFunction(pos, functionNamePrefix + UNDERSCORE + lambdaFunctionCount++);
        lambdaFunction.function = func;
        func.requiredParams.addAll(lambdaFunctionVariable);
        func.setReturnTypeNode(returnType);
        func.desugaredReturnType = true;
        defineFunction(func, env.enclPkg);
        lambdaFunctionVariable = func.requiredParams;

        func.body = lambdaBody;
        func.desugared = false;
        lambdaFunction.pos = pos;
        List<BType> paramTypes = new ArrayList<>();
        lambdaFunctionVariable.forEach(variable -> paramTypes.add(variable.symbol.type));
        lambdaFunction.setBType(new BInvokableType(paramTypes, func.symbol.type.getReturnType(),
                                                   null));
        return lambdaFunction;
    }

    private void defineFunction(BLangFunction funcNode, BLangPackage targetPkg) {
        final BPackageSymbol packageSymbol = targetPkg.symbol;
        final SymbolEnv packageEnv = this.symTable.pkgEnvMap.get(packageSymbol);
        symbolEnter.defineNode(funcNode, packageEnv);
        packageEnv.enclPkg.functions.add(funcNode);
        packageEnv.enclPkg.topLevelNodes.add(funcNode);
    }

    @Override
    public void visit(BLangForkJoin forkJoin) {
         result = forkJoin;
    }

    

    @Override
    public void visit(BLangLiteral literalExpr) {
        int tag = Types.getImpliedType(literalExpr.getBType()).tag;
        if (tag == TypeTags.ARRAY || tag == TypeTags.TUPLE) {
            
            result = rewriteBlobLiteral(literalExpr);
            return;
        }
        result = literalExpr;
    }

    private BLangNode rewriteBlobLiteral(BLangLiteral literalExpr) {
        byte[] values = types.convertToByteArray((String) literalExpr.value);
        BLangArrayLiteral arrayLiteralNode = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode();
        arrayLiteralNode.setBType(literalExpr.getBType());
        arrayLiteralNode.pos = literalExpr.pos;
        arrayLiteralNode.exprs = new ArrayList<>();
        for (byte b : values) {
            arrayLiteralNode.exprs.add(createByteLiteral(literalExpr.pos, b));
        }
        return arrayLiteralNode;
    }

    @Override
    public void visit(BLangListConstructorSpreadOpExpr listConstructorSpreadOpExpr) {
        listConstructorSpreadOpExpr.expr = rewriteExpr(listConstructorSpreadOpExpr.expr);
        result = listConstructorSpreadOpExpr;
    }

    @Override
    public void visit(BLangListConstructorExpr listConstructor) {
        listConstructor.exprs = rewriteExprs(listConstructor.exprs);
        BLangExpression expr;
        BType listConstructorType = Types.getImpliedType(listConstructor.getBType());
        if (listConstructorType.tag == TypeTags.TUPLE) {
            expr = new BLangTupleLiteral(listConstructor.pos, listConstructor.exprs, listConstructor.getBType());
            result = rewriteExpr(expr);
        } else if (listConstructorType.tag == TypeTags.JSON) {
            expr = new BLangJSONArrayLiteral(listConstructor.exprs, new BArrayType(listConstructor.getBType()));
            result = rewriteExpr(expr);
        } else if (getElementType(listConstructorType).tag == TypeTags.JSON) {
            expr = new BLangJSONArrayLiteral(listConstructor.exprs, listConstructor.getBType());
            result = rewriteExpr(expr);
        } else if (listConstructorType.tag == TypeTags.TYPEDESC) {
            final BLangTypedescExpr typedescExpr = new BLangTypedescExpr();
            typedescExpr.resolvedType = listConstructor.typedescType;
            typedescExpr.setBType(symTable.typeDesc);
            result = rewriteExpr(typedescExpr);
        } else {
            expr = new BLangArrayLiteral(listConstructor.pos, listConstructor.exprs, listConstructor.getBType());
            result = rewriteExpr(expr);
        }
    }

    @Override
    public void visit(BLangTableConstructorExpr tableConstructorExpr) {
        rewriteExprs(tableConstructorExpr.recordLiteralList);
        result = tableConstructorExpr;
    }

    @Override
    public void visit(BLangArrayLiteral arrayLiteral) {
        arrayLiteral.exprs = rewriteExprs(arrayLiteral.exprs);
        BType arrayLiteralType = Types.getImpliedType(arrayLiteral.getBType());
        if (arrayLiteralType.tag == TypeTags.JSON) {
            result = new BLangJSONArrayLiteral(arrayLiteral.exprs, new BArrayType(arrayLiteral.getBType()));
            return;
        } else if (getElementType(arrayLiteralType).tag == TypeTags.JSON) {
            result = new BLangJSONArrayLiteral(arrayLiteral.exprs, arrayLiteral.getBType());
            return;
        }
        result = arrayLiteral;
    }

    @Override
    public void visit(BLangTupleLiteral tupleLiteral) {
        if (tupleLiteral.isTypedescExpr) {
            final BLangTypedescExpr typedescExpr = new BLangTypedescExpr();
            typedescExpr.resolvedType = tupleLiteral.typedescType;
            typedescExpr.setBType(symTable.typeDesc);
            result = rewriteExpr(typedescExpr);
            return;
        }
        List<BLangExpression> exprs = tupleLiteral.exprs;
        BTupleType tupleType = (BTupleType) Types.getImpliedType(tupleLiteral.getBType());
        List<BType> tupleMemberTypes = tupleType.getTupleTypes();
        int tupleMemberTypeSize = tupleMemberTypes.size();
        int tupleExprSize = exprs.size();

        boolean isInRestType = false;
        int i = 0;
        for (BLangExpression expr: exprs) {
            if (expr.getKind() == NodeKind.LIST_CONSTRUCTOR_SPREAD_OP) {
                BType spreadOpType = ((BLangListConstructorSpreadOpExpr) expr).expr.getBType();
                spreadOpType = Types.getImpliedType(spreadOpType);
                if (spreadOpType.tag == TypeTags.ARRAY) {
                    BArrayType spreadOpBArray = (BArrayType) spreadOpType;
                    if (spreadOpBArray.size >= 0) {
                        i += spreadOpBArray.size;
                        continue;
                    }
                } else {
                    BTupleType spreadOpTuple = spreadOpType.tag == TypeTags.INTERSECTION ?
                            (BTupleType) ((BIntersectionType) spreadOpType).effectiveType : (BTupleType) spreadOpType;
                    if (types.isFixedLengthTuple(spreadOpTuple)) {
                        i += spreadOpTuple.getMembers().size();
                        continue;
                    }
                }
                isInRestType = true;
                continue;
            }

            BType expType = expr.impConversionExpr == null ? expr.getBType() : expr.impConversionExpr.getBType();

            BType targetType = tupleType.restType;
            if (!isInRestType && i < tupleMemberTypeSize) {
                targetType = tupleMemberTypes.get(i);
            }

            types.setImplicitCastExpr(expr, expType, targetType);
            i++;
        }

        tupleLiteral.exprs = rewriteExprs(tupleLiteral.exprs);
        result = tupleLiteral;
    }

    @Override
    public void visit(BLangGroupExpr groupExpr) {
        result = rewriteExpr(groupExpr.expression);
    }

    @Override
    public void visit(BLangRecordLiteral recordLiteral) {
        List<RecordLiteralNode.RecordField> fields =  recordLiteral.fields;
        generateFieldsForUserUnspecifiedRecordFields(recordLiteral, fields);
        fields.sort((v1, v2) -> Boolean.compare(isComputedKey(v1), isComputedKey(v2)));
        result = rewriteExpr(rewriteMappingConstructor(recordLiteral));
    }

    private SymbolEnv findEnclosingInvokableEnv(SymbolEnv env, BLangInvokableNode encInvokable) {
        if (env.enclEnv.node != null && env.enclEnv.node.getKind() == NodeKind.ARROW_EXPR) {
            return env.enclEnv;
        }

        if (env.enclEnv.node != null && (env.enclEnv.node.getKind() == NodeKind.ON_FAIL)) {
            return env.enclEnv;
        }

        if (env.enclInvokable != null && env.enclInvokable == encInvokable) {
            return findEnclosingInvokableEnv(env.enclEnv, encInvokable);
        }
        return env;
    }

    private void updateClosureVariable(BVarSymbol varSymbol, BLangInvokableNode encInvokable, Location pos) {
        if (!varSymbol.closure) {
            SymbolEnv encInvokableEnv = findEnclosingInvokableEnv(env, encInvokable);
            BSymbol resolvedSymbol = symResolver.lookupClosureVarSymbol(encInvokableEnv, varSymbol);
            if (resolvedSymbol != symTable.notFoundSymbol) {
                varSymbol.closure = true;
                ((BLangFunction) encInvokable).closureVarSymbols.add(new ClosureVarSymbol(varSymbol, pos));
            }
        }
    }

    private List<String> getNamesOfUserSpecifiedRecordFields(List<RecordLiteralNode.RecordField> userSpecifiedFields) {
        List<String> fieldNames = new ArrayList<>();

        for (RecordLiteralNode.RecordField field : userSpecifiedFields) {
            if (field.isKeyValueField()) {
                BLangExpression key = ((BLangRecordLiteral.BLangRecordKeyValueField) field).key.expr;
                if (key.getKind() == NodeKind.LITERAL) {
                    fieldNames.add(Utils.unescapeBallerina(((BLangLiteral) key).value.toString()));
                } else if (key.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
                    fieldNames.add(Utils.unescapeBallerina(((BLangSimpleVarRef) key).variableName.value));
                }
            } else if (field.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
                fieldNames.add(Utils.unescapeBallerina(((BLangSimpleVarRef) field).variableName.value));
            } else {
                addRequiredFieldsFromSpreadOperator(field, fieldNames);
            }
        }

        return fieldNames;
    }

    private void addRequiredFieldsFromSpreadOperator(RecordLiteralNode.RecordField field, List<String> fieldNames) {
        BLangRecordLiteral.BLangRecordSpreadOperatorField spreadOpField =
                (BLangRecordLiteral.BLangRecordSpreadOperatorField) field;
        BType type = Types.getReferredType(spreadOpField.expr.getBType());
        if (type.tag != TypeTags.RECORD) {
            return;
        }
        for (BField bField : ((BRecordType) type).fields.values()) {
            fieldNames.add(Utils.unescapeBallerina(bField.name.value));
        }
    }

    private void generateFieldsForUserUnspecifiedRecordFields(List<RecordLiteralNode.RecordField> fields,
                                                              List<String> fieldNames,
                                                              Map<String, BInvokableSymbol> defaultValues,
                                                              Location pos, boolean isReadonly) {
        for (Map.Entry<String, BInvokableSymbol> entry : defaultValues.entrySet()) {
            String fieldName = entry.getKey();
            if (fieldNames.contains(fieldName)) {
                continue;
            }
            fieldNames.add(fieldName);
            BInvokableSymbol invokableSymbol = entry.getValue();
            BLangExpression expression = getFunctionPointerInvocation(invokableSymbol);

            if (isReadonly && !Symbols.isFlagOn(invokableSymbol.retType.flags, Flags.READONLY)) {
                expression = visitCloneReadonly(expression, invokableSymbol.retType);
            }
            if (env.enclInvokable != null) {
                BLangInvocation invocation = (BLangInvocation) expression;
                if (invocation.expr.getKind() == NodeKind.INVOCATION) {
                    updateClosureVariable((BVarSymbol) ((BLangInvocation) invocation.expr).symbol, env.enclInvokable,
                                                        pos);
                } else {
                    updateClosureVariable((BVarSymbol) invocation.symbol, env.enclInvokable, pos);
                }
            }
            BLangRecordLiteral.BLangRecordKeyValueField member = createRecordKeyValueField(pos, fieldName, expression);
            fields.add(member);
        }
    }

    private BLangRecordLiteral.BLangRecordKeyValueField createRecordKeyValueField(Location pos,
                                                                                  String fieldName,
                                                                                  BLangExpression expression) {
        BLangRecordLiteral.BLangRecordKeyValueField member = new BLangRecordLiteral.BLangRecordKeyValueField();
        member.key = new BLangRecordLiteral.BLangRecordKey(ASTBuilderUtil.createLiteral(pos, symTable.stringType,
                    Utils.unescapeJava(fieldName)));
        member.valueExpr = types.addConversionExprIfRequired(expression, expression.getBType());
        return member;
    }

    public void generateFieldsForUserUnspecifiedRecordFields(BLangRecordLiteral recordLiteral,
                                                             List<RecordLiteralNode.RecordField> userSpecifiedFields) {
        BType type = Types.getImpliedType(recordLiteral.getBType());
        if (type.getKind() != TypeKind.RECORD || isSpreadingAnOpenRecord(userSpecifiedFields)) {
            return;
        }
        List<String> fieldNames = getNamesOfUserSpecifiedRecordFields(userSpecifiedFields);
        Location pos = recordLiteral.pos;
        BRecordType recordType = (BRecordType) type;
        boolean isReadonly = Symbols.isFlagOn(recordType.flags, Flags.READONLY);
        generateFieldsForUserUnspecifiedRecordFields(recordType, userSpecifiedFields, fieldNames, pos, isReadonly);
    }

    private static boolean isSpreadingAnOpenRecord(List<RecordLiteralNode.RecordField> userSpecifiedFields) {
        for (RecordLiteralNode.RecordField field : userSpecifiedFields) {
            if (!(field instanceof BLangRecordLiteral.BLangRecordSpreadOperatorField spreadOperatorField)) {
                continue;
            }
            BType type = Types.getReferredType(spreadOperatorField.expr.getBType());
            if (!(type instanceof BRecordType recordType)) {
                return true;
            }
            if (recordType.restFieldType != null) {
                return true;
            }
        }
        return false;
    }

    private void generateFieldsForUserUnspecifiedRecordFields(BRecordType recordType,
                                                              List<RecordLiteralNode.RecordField> fields,
                                                              List<String> fieldNames, Location pos,
                                                              boolean isReadonly) {
        Map<String, BInvokableSymbol> defaultValues = ((BRecordTypeSymbol) recordType.tsymbol).defaultValues;
        generateFieldsForUserUnspecifiedRecordFields(fields, fieldNames, defaultValues, pos, isReadonly);
        List<BType> typeInclusions = recordType.typeInclusions;
        for (BType typeInclusion : typeInclusions) {
            generateFieldsForUserUnspecifiedRecordFields((BRecordType) Types.getImpliedType(typeInclusion), fields,
                                                         fieldNames, pos, isReadonly);
        }
    }

    @Override
    public void visit(BLangSimpleVarRef varRefExpr) {
        BLangSimpleVarRef genVarRefExpr = varRefExpr;

        
        if (varRefExpr.pkgSymbol != null && varRefExpr.pkgSymbol.tag == SymTag.XMLNS) {
            BLangXMLQName qnameExpr = new BLangXMLQName(varRefExpr.variableName);
            qnameExpr.nsSymbol = (BXMLNSSymbol) varRefExpr.pkgSymbol;
            qnameExpr.localname = varRefExpr.variableName;
            qnameExpr.prefix = varRefExpr.pkgAlias;
            qnameExpr.namespaceURI = qnameExpr.nsSymbol.namespaceURI;
            qnameExpr.isUsedInXML = false;
            qnameExpr.pos = varRefExpr.pos;
            qnameExpr.setBType(symTable.stringType);
            result = qnameExpr;
            return;
        }

        if (varRefExpr.symbol == null) {
            result = varRefExpr;
            return;
        }

        
        if ((varRefExpr.symbol.tag & SymTag.VARIABLE) == SymTag.VARIABLE) {
            BVarSymbol varSymbol = (BVarSymbol) varRefExpr.symbol;
            if (varSymbol.originalSymbol != null) {
                varRefExpr.symbol = varSymbol.originalSymbol;
                if (varSymbol.closure) {
                    varRefExpr.symbol.closure = true;
                }
            }
        }

        BType type = varRefExpr.getBType();

        BSymbol ownerSymbol = varRefExpr.symbol.owner;
        if ((varRefExpr.symbol.tag & SymTag.FUNCTION) == SymTag.FUNCTION &&
                Types.getImpliedType(varRefExpr.symbol.type).tag == TypeTags.INVOKABLE) {
            genVarRefExpr = new BLangFunctionVarRef((BVarSymbol) varRefExpr.symbol);
        } else if ((varRefExpr.symbol.tag & SymTag.TYPE) == SymTag.TYPE &&
                !((varRefExpr.symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT)) {
            genVarRefExpr = new BLangTypeLoad(varRefExpr.symbol);
        } else if ((ownerSymbol.tag & SymTag.INVOKABLE) == SymTag.INVOKABLE ||
                (ownerSymbol.tag & SymTag.FUNCTION_TYPE) == SymTag.FUNCTION_TYPE ||
                (ownerSymbol.tag & SymTag.LET) == SymTag.LET) {
            
            genVarRefExpr = new BLangLocalVarRef((BVarSymbol) varRefExpr.symbol);
        } else if ((ownerSymbol.tag & SymTag.STRUCT) == SymTag.STRUCT) {
            genVarRefExpr = new BLangFieldVarRef((BVarSymbol) varRefExpr.symbol);
        } else if ((ownerSymbol.tag & SymTag.PACKAGE) == SymTag.PACKAGE ||
                (ownerSymbol.tag & SymTag.SERVICE) == SymTag.SERVICE) {

            
            
            if ((varRefExpr.symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT) {
                BConstantSymbol constSymbol = (BConstantSymbol) varRefExpr.symbol;
                BType referredType = Types.getImpliedType(constSymbol.literalType);
                if (referredType.tag <= TypeTags.BOOLEAN || referredType.tag == TypeTags.NIL) {
                    BLangLiteral literal = ASTBuilderUtil.createLiteral(varRefExpr.pos, constSymbol.literalType,
                            constSymbol.value.value);
                    result = rewriteExpr(types.addConversionExprIfRequired(literal, varRefExpr.getBType()));
                    return;
                }
            }

            
            
            genVarRefExpr = new BLangPackageVarRef((BVarSymbol) varRefExpr.symbol);

            if (!enclLocks.isEmpty()) {
                BVarSymbol symbol = (BVarSymbol) varRefExpr.symbol;
                BLangLockStmt lockStmt = enclLocks.peek();
                lockStmt.addLockVariable(symbol);
                lockStmt.addLockVariable(this.globalVariablesDependsOn.getOrDefault(symbol, new HashSet<>()));
            }
        }

        genVarRefExpr.setBType(type);
        genVarRefExpr.pos = varRefExpr.pos;

        if ((varRefExpr.isLValue)
                || genVarRefExpr.symbol.name.equals(IGNORE)) { 
            genVarRefExpr.isLValue = varRefExpr.isLValue;
            genVarRefExpr.setBType(varRefExpr.symbol.type);
            result = genVarRefExpr;
            return;
        }

        
        
        genVarRefExpr.isLValue = varRefExpr.isLValue;
        BType targetType = genVarRefExpr.getBType();
        genVarRefExpr.setBType(genVarRefExpr.symbol.type);
        BLangExpression expression = types.addConversionExprIfRequired(genVarRefExpr, targetType);
        result = expression.impConversionExpr != null ? expression.impConversionExpr : expression;
    }

    @Override
    public void visit(BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess nsPrefixedFieldBasedAccess) {
        rewriteFieldBasedAccess(nsPrefixedFieldBasedAccess);
    }

    private void rewriteFieldBasedAccess(BLangFieldBasedAccess fieldAccessExpr) {
        if (safeNavigate(fieldAccessExpr)) {
            result = rewriteExpr(rewriteSafeNavigationExpr(fieldAccessExpr));
            return;
        }

        BLangAccessExpression targetVarRef = fieldAccessExpr;

        
        
        BType varRefType = types.getTypeWithEffectiveIntersectionTypes(fieldAccessExpr.expr.getBType());
        fieldAccessExpr.expr = rewriteExpr(fieldAccessExpr.expr);
        if (!types.isSameType(fieldAccessExpr.expr.getBType(), varRefType)) {
            fieldAccessExpr.expr = types.addConversionExprIfRequired(fieldAccessExpr.expr, varRefType);
        }

        BLangLiteral stringLit = createStringLiteral(fieldAccessExpr.field.pos,
                Utils.unescapeJava(fieldAccessExpr.field.value));
        BType refType = Types.getImpliedType(varRefType);
        int varRefTypeTag = refType.tag;
        if (varRefTypeTag == TypeTags.OBJECT ||
                (varRefTypeTag == TypeTags.UNION &&
                        Types.getImpliedType(
                                ((BUnionType) refType).getMemberTypes().iterator().next()).tag == TypeTags.OBJECT)) {
            if (fieldAccessExpr.symbol != null &&
                    Types.getImpliedType(fieldAccessExpr.symbol.type).tag == TypeTags.INVOKABLE &&
                    ((fieldAccessExpr.symbol.flags & Flags.ATTACHED) == Flags.ATTACHED)) {
                result = rewriteObjectMemberAccessAsField(fieldAccessExpr);
                return;
            } else {
                boolean isStoreOnCreation = fieldAccessExpr.isStoreOnCreation;

                if (!isStoreOnCreation && varRefTypeTag == TypeTags.OBJECT && env.enclInvokable != null) {
                    BInvokableSymbol originalFuncSymbol = ((BLangFunction) env.enclInvokable).originalFuncSymbol;
                    BObjectTypeSymbol objectTypeSymbol = (BObjectTypeSymbol) refType.tsymbol;
                    BAttachedFunction initializerFunc = objectTypeSymbol.initializerFunc;
                    BAttachedFunction generatedInitializerFunc = objectTypeSymbol.generatedInitializerFunc;

                    if ((generatedInitializerFunc != null && originalFuncSymbol == generatedInitializerFunc.symbol) ||
                            (initializerFunc != null && originalFuncSymbol == initializerFunc.symbol)) {
                        isStoreOnCreation = true;
                    }
                }

                targetVarRef = new BLangStructFieldAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit,
                        (BVarSymbol) fieldAccessExpr.symbol, false,
                        isStoreOnCreation);
                
            }
        } else if (varRefTypeTag == TypeTags.RECORD ||
                (varRefTypeTag == TypeTags.UNION &&
                        Types.getImpliedType(
                        ((BUnionType) refType).getMemberTypes().iterator().next()).tag == TypeTags.RECORD)) {
            if (fieldAccessExpr.symbol != null &&
                    Types.getImpliedType(fieldAccessExpr.symbol.type).tag == TypeTags.INVOKABLE
                    && ((fieldAccessExpr.symbol.flags & Flags.ATTACHED) == Flags.ATTACHED)) {
                targetVarRef = new BLangStructFunctionVarRef(fieldAccessExpr.expr, (BVarSymbol) fieldAccessExpr.symbol);
            } else {
                targetVarRef = new BLangStructFieldAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit,
                        (BVarSymbol) fieldAccessExpr.symbol, false, fieldAccessExpr.isStoreOnCreation);
            }
        } else if (types.isLaxFieldAccessAllowed(refType)) {
            if (!types.isAssignable(refType, symTable.xmlType)) {
                if (varRefTypeTag == TypeTags.MAP &&
                        TypeTags.isXMLTypeTag(Types.getImpliedType(((BMapType) refType).constraint).tag)) {
                    result = rewriteExpr(rewriteLaxMapAccess(fieldAccessExpr));
                    return;
                }
                
                
                fieldAccessExpr.expr = types.addConversionExprIfRequired(fieldAccessExpr.expr, symTable.jsonType);
                targetVarRef = new BLangJSONAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit);
            } else {
                fieldAccessExpr.expr = types.addConversionExprIfRequired(fieldAccessExpr.expr, symTable.xmlType);
                BLangInvocation xmlAccessInvocation = rewriteXMLAttributeOrElemNameAccess(fieldAccessExpr);
                xmlAccessInvocation.setBType(fieldAccessExpr.getBType());
                result = xmlAccessInvocation;
                return;
            }
        } else if (varRefTypeTag == TypeTags.MAP) {
            
            targetVarRef = new BLangMapAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit,
                    fieldAccessExpr.isStoreOnCreation);
        } else if (TypeTags.isXMLTypeTag(varRefTypeTag)) {
            targetVarRef = new BLangXMLAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit,
                    fieldAccessExpr.fieldKind);
        }

        targetVarRef.isLValue = fieldAccessExpr.isLValue;
        targetVarRef.setBType(fieldAccessExpr.getBType());
        targetVarRef.optionalFieldAccess = fieldAccessExpr.optionalFieldAccess;
        result = targetVarRef;
    }

    @Override
    public void visit(BLangFieldBasedAccess fieldAccessExpr) {
        rewriteFieldBasedAccess(fieldAccessExpr);
    }

    private BLangNode rewriteObjectMemberAccessAsField(BLangFieldBasedAccess fieldAccessExpr) {
        Location pos = fieldAccessExpr.pos;
        BInvokableSymbol originalMemberFuncSymbol = (BInvokableSymbol) fieldAccessExpr.symbol;
        
        BLangFunction func = (BLangFunction) TreeBuilder.createFunctionNode();
        String funcName = "$anon$method$delegate$" + originalMemberFuncSymbol.name.value + "$" + lambdaFunctionCount++;
        BInvokableSymbol funcSymbol = new BInvokableSymbol(SymTag.INVOKABLE, (Flags.ANONYMOUS | Flags.LAMBDA),
                                                           Names.fromString(funcName), env.enclPkg.packageID,
                                                           originalMemberFuncSymbol.type, env.scope.owner, pos,
                                                           VIRTUAL);
        funcSymbol.retType = originalMemberFuncSymbol.retType;
        funcSymbol.bodyExist = true;
        funcSymbol.params = new ArrayList<>();
        funcSymbol.scope = new Scope(funcSymbol);
        func.pos = pos;
        func.name = createIdentifier(pos, funcName);
        func.flagSet.add(Flag.LAMBDA);
        func.flagSet.add(Flag.ANONYMOUS);
        func.body = (BLangBlockFunctionBody) TreeBuilder.createBlockFunctionBodyNode();
        func.symbol = funcSymbol;
        func.setBType(funcSymbol.type);
        func.closureVarSymbols = new LinkedHashSet<>();
        
        BLangExpression receiver = fieldAccessExpr.expr;
        
        
        BLangSimpleVariableDef intermediateObjDef = null;
        if (receiver.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
            BSymbol receiverSymbol = ((BLangVariableReference) receiver).symbol;
            receiverSymbol.closure = true;
            func.closureVarSymbols.add(new ClosureVarSymbol(receiverSymbol, pos));
        } else {
            BLangSimpleVariableDef varDef = createVarDef("$$temp$obj$" + annonVarCount++, receiver.getBType(),
                                                         receiver, pos);
            intermediateObjDef = varDef;
            varDef.var.symbol.closure = true;
            env.scope.define(varDef.var.symbol.name, varDef.var.symbol);
            BLangSimpleVarRef variableRef = createVariableRef(pos, varDef.var.symbol);
            func.closureVarSymbols.add(new ClosureVarSymbol(varDef.var.symbol, pos));
            receiver = variableRef;
        }

        

        ArrayList<BLangExpression> requiredArgs = new ArrayList<>();
        for (BVarSymbol param : originalMemberFuncSymbol.params) {
            BLangSimpleVariable fParam = (BLangSimpleVariable) TreeBuilder.createSimpleVariableNode();
            fParam.symbol = new BVarSymbol(0, param.name, env.enclPkg.packageID, param.type,  funcSymbol, pos,
                    VIRTUAL);
            fParam.pos = pos;
            fParam.name = createIdentifier(pos, param.name.value);
            fParam.setBType(param.type);
            func.requiredParams.add(fParam);
            funcSymbol.params.add(fParam.symbol);
            funcSymbol.scope.define(fParam.symbol.name, fParam.symbol);

            BLangSimpleVarRef paramRef = createVariableRef(pos, fParam.symbol);
            requiredArgs.add(paramRef);
        }

        ArrayList<BLangExpression> restArgs = new ArrayList<>();
        if (originalMemberFuncSymbol.restParam != null) {
            BLangSimpleVariable restParam = (BLangSimpleVariable) TreeBuilder.createSimpleVariableNode();
            func.restParam = restParam;
            BVarSymbol restSym = originalMemberFuncSymbol.restParam;
            restParam.name = ASTBuilderUtil.createIdentifier(pos, restSym.name.value);
            restParam.symbol = new BVarSymbol(0, restSym.name, env.enclPkg.packageID, restSym.type, funcSymbol, pos,
                    VIRTUAL);
            restParam.pos = pos;
            restParam.setBType(restSym.type);
            funcSymbol.restParam = restParam.symbol;
            funcSymbol.scope.define(restParam.symbol.name, restParam.symbol);

            BLangSimpleVarRef restArg = createVariableRef(pos, restParam.symbol);
            restArgs.add(createRestArgsExpression(restArg));
        }

        BLangIdentifier field = fieldAccessExpr.field;
        BLangReturn retStmt = (BLangReturn) TreeBuilder.createReturnNode();
        retStmt.expr = createObjectMethodInvocation(
                receiver, field, fieldAccessExpr.symbol, requiredArgs, restArgs);
        ((BLangBlockFunctionBody) func.body).addStatement(retStmt);

        BLangLambdaFunction lambdaFunction = (BLangLambdaFunction) TreeBuilder.createLambdaFunctionNode();
        lambdaFunction.function = func;
        lambdaFunction.capturedClosureEnv = env;
        env.enclPkg.functions.add(func);
        env.enclPkg.topLevelNodes.add(func);
        
        lambdaFunction.parent = env.enclInvokable;
        lambdaFunction.setBType(func.getBType());

        if (intermediateObjDef == null) {
            return rewrite(lambdaFunction, env);
        } else {
            BLangStatementExpression expr = createStatementExpression(intermediateObjDef, rewrite(lambdaFunction, env));
            expr.setBType(lambdaFunction.getBType());
            return rewrite(expr, env);
        }
    }

    private BLangInvocation createObjectMethodInvocation(BLangExpression receiver, BLangIdentifier field,
                                                         BSymbol invocableSymbol,
                                                         List<BLangExpression> requiredArgs,
                                                         List<BLangExpression> restArgs) {
        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        invocationNode.name = field;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        invocationNode.expr = receiver;

        invocationNode.symbol = invocableSymbol;
        invocationNode.setBType(((BInvokableType) invocableSymbol.type).retType);
        invocationNode.requiredArgs = requiredArgs;
        invocationNode.restArgs = restArgs;
        return invocationNode;
    }

    private BLangStatementExpression rewriteLaxMapAccess(BLangFieldBasedAccess fieldAccessExpr) {
        BLangStatementExpression statementExpression = new BLangStatementExpression();
        BLangBlockStmt block = new BLangBlockStmt();
        statementExpression.stmt = block;
        BUnionType fieldAccessType = BUnionType.create(null, fieldAccessExpr.getBType(), symTable.errorType);
        Location pos = fieldAccessExpr.pos;
        BLangSimpleVariableDef result = createVarDef("$mapAccessResult$", fieldAccessType, null, pos);
        block.addStatement(result);
        BLangSimpleVarRef resultRef = ASTBuilderUtil.createVariableRef(pos, result.var.symbol);
        resultRef.setBType(fieldAccessType);
        statementExpression.setBType(fieldAccessType);


        
        
        BLangLiteral mapIndex = ASTBuilderUtil.createLiteral(
                        fieldAccessExpr.field.pos, symTable.stringType, fieldAccessExpr.field.value);
        BLangMapAccessExpr mapAccessExpr = new BLangMapAccessExpr(pos, fieldAccessExpr.expr, mapIndex);
        BUnionType xmlOrNil = BUnionType.create(null, fieldAccessExpr.getBType(), symTable.nilType);
        mapAccessExpr.setBType(xmlOrNil);
        BLangSimpleVariableDef mapResult = createVarDef("$mapAccess", xmlOrNil, mapAccessExpr, pos);
        BLangSimpleVarRef mapResultRef = ASTBuilderUtil.createVariableRef(pos, mapResult.var.symbol);
        block.addStatement(mapResult);

        BLangIf ifStmt = ASTBuilderUtil.createIfStmt(pos, block);

        BLangIsLikeExpr isLikeNilExpr = createIsLikeExpression(pos, mapResultRef, symTable.nilType);

        ifStmt.expr = isLikeNilExpr;
        BLangBlockStmt resultNilBody = new BLangBlockStmt();
        ifStmt.body = resultNilBody;
        BLangBlockStmt resultHasValueBody = new BLangBlockStmt();
        ifStmt.elseStmt = resultHasValueBody;

        BLangErrorConstructorExpr errorConstructorExpr =
                (BLangErrorConstructorExpr) TreeBuilder.createErrorConstructorExpressionNode();
        BSymbol symbol = symResolver.lookupMainSpaceSymbolInPackage(errorConstructorExpr.pos, env,
                Names.fromString(""), Names.fromString("error"));
        errorConstructorExpr.setBType(symbol.type);

        List<BLangExpression> positionalArgs = new ArrayList<>();
        List<BLangNamedArgsExpression> namedArgs = new ArrayList<>();
        positionalArgs.add(createStringLiteral(pos, "{" + RuntimeConstants.MAP_LANG_LIB + "}InvalidKey"));
        BLangNamedArgsExpression message = new BLangNamedArgsExpression();
        message.name = ASTBuilderUtil.createIdentifier(pos, "key");
        message.expr = createStringLiteral(pos, fieldAccessExpr.field.value);
        namedArgs.add(message);
        errorConstructorExpr.positionalArgs = positionalArgs;
        errorConstructorExpr.namedArgs = namedArgs;

        BLangSimpleVariableDef errorDef =
                createVarDef("$_invalid_key_error", symTable.errorType, errorConstructorExpr, pos);
        resultNilBody.addStatement(errorDef);

        BLangSimpleVarRef errorRef = ASTBuilderUtil.createVariableRef(pos, errorDef.var.symbol);

        BLangAssignment errorVarAssignment = ASTBuilderUtil.createAssignmentStmt(pos, resultNilBody);
        errorVarAssignment.varRef = resultRef;
        errorVarAssignment.expr = errorRef;

        BLangAssignment mapResultAssignment = ASTBuilderUtil.createAssignmentStmt(
                pos, resultHasValueBody);
        mapResultAssignment.varRef = resultRef;
        mapResultAssignment.expr = mapResultRef;

        statementExpression.expr = resultRef;
        return statementExpression;
    }

    private BLangInvocation rewriteXMLAttributeOrElemNameAccess(BLangFieldBasedAccess fieldAccessExpr) {
        ArrayList<BLangExpression> args = new ArrayList<>();

        String fieldName = fieldAccessExpr.field.value;
        if (fieldAccessExpr.fieldKind == FieldKind.WITH_NS) {
            BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess nsPrefixAccess =
                    (BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess) fieldAccessExpr;
            fieldName = createExpandedQName(nsPrefixAccess.nsSymbol.namespaceURI, fieldName);
        }

        
        if (fieldName.equals("_")) {
            return createLanglibXMLInvocation(fieldAccessExpr.pos, XML_INTERNAL_GET_ELEMENT_NAME_NIL_LIFTING,
                    fieldAccessExpr.expr, new ArrayList<>(), new ArrayList<>());
        }

        BLangLiteral attributeNameLiteral = createStringLiteral(fieldAccessExpr.field.pos, fieldName);
        args.add(attributeNameLiteral);
        args.add(isOptionalAccessToLiteral(fieldAccessExpr));

        return createLanglibXMLInvocation(fieldAccessExpr.pos, XML_INTERNAL_GET_ATTRIBUTE, fieldAccessExpr.expr, args,
                new ArrayList<>());
    }

    private BLangExpression isOptionalAccessToLiteral(BLangFieldBasedAccess fieldAccessExpr) {
        return rewrite(
                createLiteral(fieldAccessExpr.pos, symTable.booleanType, fieldAccessExpr.isOptionalFieldAccess()), env);
    }

    private String createExpandedQName(String nsURI, String localName) {
        return "{" + nsURI + "}" + localName;
    }

    @Override
    public void visit(BLangIndexBasedAccess indexAccessExpr) {
        if (safeNavigate(indexAccessExpr)) {
            result = rewriteExpr(rewriteSafeNavigationExpr(indexAccessExpr));
            return;
        }

        BLangIndexBasedAccess targetVarRef = indexAccessExpr;
        indexAccessExpr.indexExpr = rewriteExpr(indexAccessExpr.indexExpr);

        
        
        BType effectiveType = types.getTypeWithEffectiveIntersectionTypes(indexAccessExpr.expr.getBType());
        BType varRefType = Types.getImpliedType(effectiveType);
        indexAccessExpr.expr = rewriteExpr(indexAccessExpr.expr);
        if (!types.isSameType(indexAccessExpr.expr.getBType(), varRefType)) {
            indexAccessExpr.expr = types.addConversionExprIfRequired(indexAccessExpr.expr, varRefType);
        }

        if (varRefType.tag == TypeTags.MAP) {
            targetVarRef = new BLangMapAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                                                  indexAccessExpr.indexExpr, indexAccessExpr.isStoreOnCreation);
        } else if (types.isSubTypeOfMapping(types.getSafeType(varRefType, true, false))) {
            targetVarRef = new BLangStructFieldAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                                                          indexAccessExpr.indexExpr,
                                                          (BVarSymbol) indexAccessExpr.symbol, false);
        } else if (types.isSubTypeOfList(varRefType)) {
            targetVarRef = new BLangArrayAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                                                    indexAccessExpr.indexExpr);
        } else if (types.isAssignable(varRefType, symTable.xmlType)) {
            BLangExpression indexAccessExprExpr = indexAccessExpr.expr;
            
            if (Types.getImpliedType(indexAccessExprExpr.getBType()).tag == TypeTags.UNION) {
                indexAccessExprExpr = createTypeCastExpr(indexAccessExprExpr, symTable.xmlType);
            }
            targetVarRef = new BLangXMLAccessExpr(indexAccessExpr.pos, indexAccessExprExpr, indexAccessExpr.indexExpr);
        } else if (types.isAssignable(varRefType, symTable.stringType)) {
            indexAccessExpr.expr = types.addConversionExprIfRequired(indexAccessExpr.expr, symTable.stringType);
            targetVarRef = new BLangStringAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                                                     indexAccessExpr.indexExpr);
        } else if (varRefType.tag == TypeTags.TABLE) {
            targetVarRef = new BLangTableAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                    indexAccessExpr.indexExpr);
        }

        targetVarRef.isLValue = indexAccessExpr.isLValue;
        targetVarRef.setBType(indexAccessExpr.getBType());
        result = targetVarRef;
    }

    @Override
    public void visit(BLangInvocation iExpr) {
        BLangExpression invocation = rewriteInvocation(iExpr, false);
        if (invocation.getKind() == NodeKind.TYPE_CONVERSION_EXPR) {
            ((BLangTypeConversionExpr) invocation).expr =
                                        createStmtExpr((BLangInvocation) ((BLangTypeConversionExpr) invocation).expr);
            result = invocation;
        } else {
            result = createStmtExpr((BLangInvocation) invocation);
        }
    }

    
    @Override
    public void visit(BFunctionPointerInvocation invocation) {
        visitArgs(invocation);

        invocation.expr = rewriteExpr(invocation.expr);
        result = invocation;
    }

    private void visitArgs(BLangInvocation invocation) {
        
        reorderArguments(invocation);

        rewriteExprs(invocation.requiredArgs);
        fixStreamTypeCastsInInvocationParams(invocation);
        fixNonRestArgTypeCastInTypeParamInvocation(invocation);

        rewriteExprs(invocation.restArgs);
    }

    private BLangStatementExpression createStmtExpr(BLangInvocation invocation) {
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(invocation.pos);
        BType type = Types.getImpliedType(invocation.symbol.type);
        BInvokableTypeSymbol invokableTypeSymbol = (BInvokableTypeSymbol) type.tsymbol;

        if (invokableTypeSymbol == null) {
            BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, invocation);
            stmtExpr.setBType(invocation.getBType());
            return stmtExpr;
        }
        TreeMap<String, BLangExpression> arguments = new TreeMap<>();
        Map<String, BInvokableSymbol> defaultValues = invokableTypeSymbol.defaultValues;

        for (int i = 0; i < invokableTypeSymbol.params.size(); i++) {
            BLangExpression arg;
            BLangSimpleVariableDef variableDef;
            BLangSimpleVarRef simpleVarRef;
            if (invocation instanceof BLangInvocation.BLangAttachedFunctionInvocation) {
                arg = invocation.requiredArgs.get(i + 1);
            } else {
                arg = invocation.requiredArgs.get(i);
            }
            BVarSymbol param = invokableTypeSymbol.params.get(i);
            String paramName = param.name.value;
            if (arg.getKind() != NodeKind.IGNORE_EXPR) {
                if (invocation.expr == arg) {
                    arguments.put(paramName, arg);
                    continue;
                }
                if (arg.impConversionExpr != null) {
                    variableDef = createSimpleVarDef("$" + paramName + "$" + funcParamCount++, param.type, arg);
                } else {
                    variableDef = createSimpleVarDef("$" + paramName + "$" + funcParamCount++, arg.getBType(), arg);
                }
                simpleVarRef = ASTBuilderUtil.createVariableRef(invocation.pos, variableDef.var.symbol);
                simpleVarRef = rewrite(simpleVarRef, env);
                blockStmt.addStatement(variableDef);
                arguments.put(paramName, simpleVarRef);
                if (invocation instanceof BLangInvocation.BLangAttachedFunctionInvocation) {
                    invocation.requiredArgs.set(i + 1, simpleVarRef);
                } else {
                    invocation.requiredArgs.set(i, simpleVarRef);
                }
                continue;
            }

            BInvokableSymbol invokableSymbol = defaultValues.get(Utils.unescapeBallerina(paramName));
            BLangInvocation closureInvocation = getFunctionPointerInvocation(invokableSymbol);
            for (int m = 0; m < invokableSymbol.params.size(); m++) {
                String langLibFuncParam = invokableSymbol.params.get(m).name.value;
                closureInvocation.requiredArgs.add(arguments.get(langLibFuncParam));
            }
            variableDef = createVarDef("$" + paramName + "$" + funcParamCount++, closureInvocation.getBType(),
                                       closureInvocation, arg.pos);
            simpleVarRef = ASTBuilderUtil.createVariableRef(invocation.pos, variableDef.var.symbol);
            simpleVarRef = rewrite(simpleVarRef, env);
            blockStmt.addStatement(variableDef);
            arguments.put(paramName, simpleVarRef);
            if (invocation instanceof BLangInvocation.BLangAttachedFunctionInvocation) {
                invocation.requiredArgs.set(i + 1, simpleVarRef);
            } else {
                invocation.requiredArgs.set(i, simpleVarRef);
            }
        }
        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, invocation);
        stmtExpr.setBType(invocation.getBType());

        return stmtExpr;
    }

    private BLangInvocation getFunctionPointerInvocation(BInvokableSymbol symbol) {
        BLangInvocation funcInvocation = (BLangInvocation) TreeBuilder.createInvocationNode();
        funcInvocation.setBType(symbol.retType);
        funcInvocation.symbol = symbol;
        funcInvocation.name = ASTBuilderUtil.createIdentifier(symbol.pos, symbol.name.value);
        return visitFunctionPointerInvocation(funcInvocation);
    }

    @Override
    public void visit(BLangErrorConstructorExpr errorConstructorExpr) {
        if (errorConstructorExpr.positionalArgs.size() == 1) {
            errorConstructorExpr.positionalArgs.add(createNilLiteral());
        }
        errorConstructorExpr.positionalArgs.set(1,
                types.addConversionExprIfRequired(errorConstructorExpr.positionalArgs.get(1), symTable.errorType));
        rewriteExprs(errorConstructorExpr.positionalArgs);

        BLangExpression errorDetail;
        BLangRecordLiteral recordLiteral = ASTBuilderUtil.createEmptyRecordLiteral(errorConstructorExpr.pos,
                ((BErrorType) Types.getImpliedType(errorConstructorExpr.getBType())).detailType);
        if (errorConstructorExpr.namedArgs.isEmpty()) {
            errorDetail = visitCloneReadonly(rewriteExpr(recordLiteral), recordLiteral.getBType());
        } else {
            for (BLangNamedArgsExpression namedArg : errorConstructorExpr.namedArgs) {
                BLangRecordLiteral.BLangRecordKeyValueField member = new BLangRecordLiteral.BLangRecordKeyValueField();
                member.key = new BLangRecordLiteral.BLangRecordKey(ASTBuilderUtil.createLiteral(namedArg.name.pos,
                        symTable.stringType, Utils.unescapeJava(namedArg.name.value)));

                if (Types.getImpliedType(recordLiteral.getBType()).tag == TypeTags.RECORD) {
                    member.valueExpr = types.addConversionExprIfRequired(namedArg.expr, symTable.anyType);
                } else {
                    member.valueExpr = types.addConversionExprIfRequired(namedArg.expr, namedArg.expr.getBType());
                }
                recordLiteral.fields.add(member);
            }
            errorDetail = visitCloneReadonly(rewriteExpr(recordLiteral),
                    ((BErrorType) Types.getImpliedType(errorConstructorExpr.getBType())).detailType);
        }
        errorConstructorExpr.errorDetail = errorDetail;
        result = errorConstructorExpr;
    }

    @Override
    public void visit(BLangInvocation.BLangActionInvocation actionInvocation) {
        if (!actionInvocation.async && actionInvocation.invokedInsideTransaction) {
            transactionDesugar.startTransactionCoordinatorOnce(env, actionInvocation.pos);
        }

        
        if (!actionInvocation.functionPointerInvocation && actionInvocation.async &&
                Symbols.isFlagOn(actionInvocation.symbol.type.flags, Flags.ISOLATED)) {
            addStrandAnnotationWithThreadAny(actionInvocation.pos);
            actionInvocation.addAnnotationAttachment(this.strandAnnotAttachement);
            ((BInvokableSymbol) actionInvocation.symbol)
                    .addAnnotation(this.strandAnnotAttachement.annotationAttachmentSymbol);
        }

        BLangExpression invocation = rewriteInvocation(actionInvocation, actionInvocation.async);
        if (invocation.getKind() == NodeKind.TYPE_CONVERSION_EXPR) {
            ((BLangTypeConversionExpr) invocation).expr =
                                        createStmtExpr((BLangInvocation) ((BLangTypeConversionExpr) invocation).expr);
            result = invocation;
        } else {
            result = createStmtExpr((BLangInvocation) invocation);
        }
    }

    private void addStrandAnnotationWithThreadAny(Location pos) {
        if (this.strandAnnotAttachement == null) {
            
            this.strandAnnotAttachement = annotationDesugar.createStrandAnnotationWithThreadAny(pos, env);
        }
    }

    @Override
    public void visit(BLangInvocation.BLangResourceAccessInvocation resourceAccessInvocation) {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        if (resourceAccessInvocation.invokedInsideTransaction) {
            transactionDesugar.startTransactionCoordinatorOnce(env, resourceAccessInvocation.pos);
        }

        
        BLangInvocation pathParamInvocation = createInvocationForPathParams(resourceAccessInvocation);
        reorderArguments(pathParamInvocation);

        BResourceFunction targetResourceFunc = resourceAccessInvocation.targetResourceFunc;
        List<BResourcePathSegmentSymbol> pathSegmentSymbols = targetResourceFunc.pathSegmentSymbols;

        int pathParamInvocationRequiredArgCount = pathParamInvocation.requiredArgs.size();

        BLangInvocation bLangInvocation = new BLangInvocation();

        
        
        
        
        
        
        
        
        BLangStatementExpression firstRequiredArgFromRestArg = null;
        boolean isFirstRequiredArgFromRestArgIncluded = false;
        for (int i = 0; i < pathParamInvocationRequiredArgCount; i++) {
            BLangExpression requiredArg = pathParamInvocation.requiredArgs.get(i);
            
            Name resourcePathName = pathSegmentSymbols.get(i).name;
            if (firstRequiredArgFromRestArg == null && requiredArg.getKind() == NodeKind.STATEMENT_EXPRESSION) {
                firstRequiredArgFromRestArg = (BLangStatementExpression) requiredArg;
                if (resourcePathName.value.equals("^")) {
                    isFirstRequiredArgFromRestArgIncluded = true;
                    bLangInvocation.requiredArgs.add(requiredArg);
                    continue;
                }
            }

            if (resourcePathName.value.equals("^")) {
                if (firstRequiredArgFromRestArg != null && !isFirstRequiredArgFromRestArgIncluded) {
                    BLangStatementExpression statementExpression = new BLangStatementExpression();
                    statementExpression.expr = requiredArg;
                    statementExpression.stmt = firstRequiredArgFromRestArg.stmt;
                    statementExpression.setBType(requiredArg.getBType());
                    bLangInvocation.requiredArgs.add(statementExpression);
                    isFirstRequiredArgFromRestArgIncluded = true;
                } else {
                    bLangInvocation.requiredArgs.add(requiredArg);
                }
            }
        }

        Name lastResourcePathName = pathSegmentSymbols.get(pathSegmentSymbols.size() - 1).name;
        if (lastResourcePathName.value.equals("^^")) {
            
            for (BLangExpression restArg : pathParamInvocation.restArgs) {
                if (firstRequiredArgFromRestArg != null && !isFirstRequiredArgFromRestArgIncluded &&
                        restArg.getKind() == NodeKind.STATEMENT_EXPRESSION) {
                    BLangStatementExpression restArgStmtExpr = (BLangStatementExpression) restArg;
                    ((BLangBlockStmt) restArgStmtExpr.stmt).stmts.add(0,
                            ((BLangBlockStmt) firstRequiredArgFromRestArg.stmt).stmts.get(0));
                }
                bLangInvocation.requiredArgs.add(restArg);
            }
        }

        bLangInvocation.requiredArgs.addAll(resourceAccessInvocation.requiredArgs);
        bLangInvocation.pkgAlias = resourceAccessInvocation.pkgAlias;
        bLangInvocation.name = resourceAccessInvocation.name;
        bLangInvocation.expr = resourceAccessInvocation.expr;
        bLangInvocation.restArgs = resourceAccessInvocation.restArgs;
        bLangInvocation.symbol = resourceAccessInvocation.symbol;
        bLangInvocation.setBType(resourceAccessInvocation.getBType());
        bLangInvocation.parent = resourceAccessInvocation.parent;
        bLangInvocation.pos = resourceAccessInvocation.pos;
        result = rewriteExpr(bLangInvocation);
    }

    private BLangInvocation createInvocationForPathParams(
            BLangInvocation.BLangResourceAccessInvocation resourceAccessInvocation) {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        BLangInvocation bLangInvocation = new BLangInvocation();

        BInvokableSymbol invokableSymbol = new BInvokableSymbol(
                resourceAccessInvocation.symbol.tag,
                resourceAccessInvocation.symbol.flags,
                resourceAccessInvocation.symbol.name,
                resourceAccessInvocation.symbol.pkgID,
                resourceAccessInvocation.symbol.type,
                resourceAccessInvocation.symbol,
                resourceAccessInvocation.symbol.pos, VIRTUAL);

        BResourceFunction targetResourceFunc = resourceAccessInvocation.targetResourceFunc;
        List<BResourcePathSegmentSymbol> pathSegmentSymbols = targetResourceFunc.pathSegmentSymbols;
        List<BLangExpression> resourceAccessPathSegments = resourceAccessInvocation.resourceAccessPathSegments.exprs;

        List<BVarSymbol> invocationParams = new ArrayList<>(pathSegmentSymbols.size());

        int pathSegmentCount = pathSegmentSymbols.size();
        BResourcePathSegmentSymbol lastPathSegmentSym = pathSegmentSymbols.get(pathSegmentSymbols.size() - 1);
        if (lastPathSegmentSym.kind == SymbolKind.RESOURCE_PATH_REST_PARAM_SEGMENT) {
            invokableSymbol.restParam = new BVarSymbol(0, Names.EMPTY, this.env.scope.owner.pkgID,
                    new BArrayType(lastPathSegmentSym.type), this.env.scope.owner, lastPathSegmentSym.pos, VIRTUAL);
            pathSegmentCount--;
        }

        if (pathSegmentCount > 0 && lastPathSegmentSym.kind != SymbolKind.RESOURCE_ROOT_PATH_SEGMENT) {
            invocationParams.addAll(pathSegmentSymbols.subList(0, pathSegmentCount).stream()
                    .map(s -> new BVarSymbol(0, Names.EMPTY, this.env.scope.owner.pkgID, s.type,
                            this.env.scope.owner, s.pos, VIRTUAL)).toList());
        }

        invokableSymbol.params = invocationParams;

        bLangInvocation.symbol = invokableSymbol;

        for (int i = 0;  i < resourceAccessPathSegments.size(); i++) {
            BLangExpression resourceAccessPathSeg = resourceAccessPathSegments.get(i);
            if (resourceAccessPathSeg.getKind() == NodeKind.LIST_CONSTRUCTOR_SPREAD_OP) {
                bLangInvocation.restArgs.add(createRestArgsExpression(
                        ((BLangListConstructorSpreadOpExpr) resourceAccessPathSeg).expr));
            } else if (i > invocationParams.size() - 1) {
                bLangInvocation.restArgs.add(resourceAccessPathSeg);
            } else {
                bLangInvocation.requiredArgs.add(resourceAccessPathSeg);
            }
        }

        return bLangInvocation;
    }

    private BLangRestArgsExpression createRestArgsExpression(BLangExpression expr) {
        BLangRestArgsExpression bLangRestArgsExpression = new BLangRestArgsExpression();
        bLangRestArgsExpression.expr = expr;
        bLangRestArgsExpression.pos = expr.pos;
        bLangRestArgsExpression.setBType(expr.getBType());
        bLangRestArgsExpression.expectedType = bLangRestArgsExpression.getBType();
        return bLangRestArgsExpression;
    }

    private BLangExpression rewriteInvocation(BLangInvocation invocation, boolean async) {
        BLangInvocation invRef = invocation;

        if (!enclLocks.isEmpty()) {
            BLangLockStmt lock = enclLocks.peek();
            lock.lockVariables.addAll(((BInvokableSymbol) invocation.symbol).dependentGlobalVars);
        }

        visitArgs(invocation);

        annotationDesugar.defineStatementAnnotations(invocation.annAttachments, invocation.pos,
                                                     invocation.symbol.pkgID, invocation.symbol.owner, env);

        if (invocation.functionPointerInvocation) {
            return visitFunctionPointerInvocation(invocation);
        }
        result = invRef;

        BInvokableSymbol invSym = (BInvokableSymbol) invocation.symbol;
        if (Symbols.isFlagOn(invSym.retType.flags, Flags.PARAMETERIZED)) {
            BType retType = unifier.build(invSym.retType);
            invocation.setBType(invocation.async ? new BFutureType(TypeTags.FUTURE, retType, null) : retType);
        }

        if (invocation.expr == null) {
            BLangExpression expression = fixTypeCastInTypeParamInvocation(invocation, invRef);
            if (invocation.exprSymbol == null) {
                return expression;
            }
            invocation.expr = ASTBuilderUtil.createVariableRef(invocation.pos, invocation.exprSymbol);
            invocation.expr = rewriteExpr(invocation.expr);
        }
        switch (Types.getImpliedType(invocation.expr.getBType()).tag) {
            case TypeTags.OBJECT:
            case TypeTags.RECORD:
            case TypeTags.UNION:
                if (!invocation.langLibInvocation) {
                    invocation.expr = rewriteExpr(invocation.expr);
                    List<BLangExpression> argExprs = new ArrayList<>(invocation.requiredArgs);
                    argExprs.add(0, invocation.expr);
                    BLangAttachedFunctionInvocation attachedFunctionInvocation =
                            new BLangAttachedFunctionInvocation(invocation.pos, argExprs, invocation.restArgs,
                                                                invocation.symbol, invocation.getBType(),
                                                                invocation.expr, async);
                    attachedFunctionInvocation.name = invocation.name;
                    attachedFunctionInvocation.annAttachments = invocation.annAttachments;
                    invRef = attachedFunctionInvocation;
                }
                break;
        }
        populateOCEInvocation(invocation, invRef);
        return fixTypeCastInTypeParamInvocation(invocation, invRef);
    }

    private void populateOCEInvocation(BLangInvocation invocation,
                                       BLangInvocation invRef) {
        if (invocation.objectInitMethod && Symbols.isFlagOn(invocation.expr.getBType().flags, Flags.OBJECT_CTOR)) {
            BObjectType initializingObject = (BObjectType) invocation.expr.getBType();
            BLangClassDefinition classDef = initializingObject.classDef;
            if (classDef.hasClosureVars) {
                OCEDynamicEnvironmentData oceEnvData = initializingObject.classDef.oceEnvData;
                if (oceEnvData.attachedFunctionInvocation == null) {
                    oceEnvData.attachedFunctionInvocation = (BLangAttachedFunctionInvocation) invRef;
                }
            }
        }
    }

    private void fixNonRestArgTypeCastInTypeParamInvocation(BLangInvocation iExpr) {
        if (!iExpr.langLibInvocation) {
            return;
        }

        List<BLangExpression> requiredArgs = iExpr.requiredArgs;

        List<BVarSymbol> params = ((BInvokableSymbol) iExpr.symbol).params;

        for (int i = 0; i < requiredArgs.size(); i++) {
            requiredArgs.set(i, types.addConversionExprIfRequired(requiredArgs.get(i), params.get(i).type));
        }
    }

    /* This function is a workaround and need improvement
    *  Notes for improvement :
    *    1. Both arguments are same.
    *    2. Due to current type param logic we put type param flag on the original type.
    *    3. Error type having Cloneable type with type param flag, change expression type by this code.
    *    4. using error type is a problem as Cloneable type is an typeparm eg: ExprBodiedFunctionTest
    *       added never to CloneableType type param
    *       @typeParam type
    *       CloneableType Cloneable|never;
    *
    */
    private BLangExpression fixTypeCastInTypeParamInvocation(BLangInvocation iExpr, BLangInvocation genIExpr) {
        var returnTypeOfInvokable = ((BInvokableSymbol) iExpr.symbol).retType;
        if (!iExpr.langLibInvocation && !TypeParamAnalyzer.containsTypeParam(returnTypeOfInvokable)) {
            return genIExpr;
        }

        
        BType originalInvType = genIExpr.getBType();
        if (!genIExpr.async) {
            genIExpr.setBType(returnTypeOfInvokable);
        }
        return types.addConversionExprIfRequired(genIExpr, originalInvType);
    }

    private void fixStreamTypeCastsInInvocationParams(BLangInvocation iExpr) {
        List<BLangExpression> requiredArgs = iExpr.requiredArgs;
        List<BVarSymbol> params = ((BInvokableSymbol) iExpr.symbol).params;
        if (!params.isEmpty()) {
            for (int i = 0; i < requiredArgs.size(); i++) {
                BVarSymbol param = params.get(i);
                if (Types.getImpliedType(param.type).tag == TypeTags.STREAM) {
                    requiredArgs.set(i, types.addConversionExprIfRequired(requiredArgs.get(i), param.type));
                }
            }
        }
    }

    private BLangLiteral createNilLiteral() {
        BLangLiteral literal = (BLangLiteral) TreeBuilder.createLiteralExpression();
        literal.value = null;
        literal.setBType(symTable.nilType);
        return literal;
    }

    @Override
    public void visit(BLangTypeInit typeInitExpr) {
        if (Types.getImpliedType(typeInitExpr.getBType()).tag == TypeTags.STREAM) {
            result = rewriteExpr(desugarStreamTypeInit(typeInitExpr));
        } else {
            result = rewrite(desugarObjectTypeInit(typeInitExpr), env);
        }
    }

    private BLangStatementExpression desugarObjectTypeInit(BLangTypeInit typeInitExpr) {
        typeInitExpr.desugared = true;
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(typeInitExpr.pos);

        
        BLangInvocation initInvocation = (BLangInvocation) typeInitExpr.initInvocation;
        initInvocation.objectInitMethod = true;
        BType objType = getObjectType(typeInitExpr.getBType());
        BLangSimpleVariableDef objVarDef = createVarDef("$obj$", objType, typeInitExpr, typeInitExpr.pos);
        
        BLangSimpleVariableDef initInvRetValVarDef = createVarDef("$temp$", initInvocation.getBType(),
                                                                  initInvocation, initInvocation.pos);
        objVarDef.var.name.pos = symTable.builtinPos;
        BLangSimpleVarRef objVarRef = ASTBuilderUtil.createVariableRef(typeInitExpr.pos, objVarDef.var.symbol);
        BLangSimpleVarRef objInitVarRef = ASTBuilderUtil.createVariableRef(initInvocation.pos,
                                                                           initInvRetValVarDef.var.symbol);
        blockStmt.addStatement(objVarDef);
        blockStmt.addStatement(initInvRetValVarDef);
        initInvocation.exprSymbol = objVarDef.var.symbol;
        initInvocation.symbol =
                ((BObjectTypeSymbol) Types.getImpliedType(objType).tsymbol).generatedInitializerFunc.symbol;

        
        if (initInvocation.getBType().tag == TypeTags.NIL) {
            initInvocation.name.value = GENERATED_INIT_SUFFIX.value;
            BLangNode parent = initInvocation.parent;
            if (parent != null && parent.getKind() == NodeKind.OBJECT_CTOR_EXPRESSION) {
                BLangObjectConstructorExpression oceExpression = (BLangObjectConstructorExpression) parent;
                OCEDynamicEnvironmentData oceData = oceExpression.classNode.oceEnvData;
                oceData.initInvocation = typeInitExpr.initInvocation;
            }
            typeInitExpr.initInvocation = objInitVarRef;
            BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, objVarRef);
            stmtExpr.setBType(objVarRef.symbol.type);
            return stmtExpr;
        }

        
        BLangSimpleVariableDef resultVarDef = createVarDef("$result$", typeInitExpr.getBType(), null, typeInitExpr.pos);
        blockStmt.addStatement(resultVarDef);

        
        
        
        
        

        
        BLangSimpleVarRef initRetValVarRefInCondition =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, initInvRetValVarDef.var.symbol);
        BLangBlockStmt thenStmt = ASTBuilderUtil.createBlockStmt(symTable.builtinPos);
        BLangTypeTestExpr isErrorTest =
                ASTBuilderUtil.createTypeTestExpr(symTable.builtinPos, initRetValVarRefInCondition, getErrorTypeNode());
        isErrorTest.setBType(symTable.booleanType);

        
        BLangSimpleVarRef thenInitRetValVarRef =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, initInvRetValVarDef.var.symbol);
        BLangSimpleVarRef thenResultVarRef =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, resultVarDef.var.symbol);
        BLangAssignment errAssignment =
                ASTBuilderUtil.createAssignmentStmt(symTable.builtinPos, thenResultVarRef, thenInitRetValVarRef);
        thenStmt.addStatement(errAssignment);

        
        BLangSimpleVarRef elseResultVarRef =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, resultVarDef.var.symbol);
        BLangAssignment objAssignment =
                ASTBuilderUtil.createAssignmentStmt(symTable.builtinPos, elseResultVarRef, objVarRef);
        BLangBlockStmt elseStmt = ASTBuilderUtil.createBlockStmt(symTable.builtinPos);
        elseStmt.addStatement(objAssignment);

        BLangIf ifelse = ASTBuilderUtil.createIfElseStmt(symTable.builtinPos, isErrorTest, thenStmt, elseStmt);
        blockStmt.addStatement(ifelse);

        BLangSimpleVarRef resultVarRef =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, resultVarDef.var.symbol);
        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(resultVarRef.symbol.type);
        return stmtExpr;
    }

    private BLangInvocation desugarStreamTypeInit(BLangTypeInit typeInitExpr) {
        BInvokableSymbol symbol = (BInvokableSymbol) symTable.langInternalModuleSymbol.scope
                .lookup(Names.CONSTRUCT_STREAM).symbol;

        BStreamType referredStreamType = (BStreamType) Types.getImpliedType(typeInitExpr.getBType());
        BType constraintType = referredStreamType.constraint;
        BType constraintTdType = new BTypedescType(constraintType, symTable.typeDesc.tsymbol);
        BLangTypedescExpr constraintTdExpr = new BLangTypedescExpr();
        constraintTdExpr.resolvedType = constraintType;
        constraintTdExpr.setBType(constraintTdType);

        BType completionType = referredStreamType.completionType;
        BType completionTdType = new BTypedescType(completionType, symTable.typeDesc.tsymbol);
        BLangTypedescExpr completionTdExpr = new BLangTypedescExpr();
        completionTdExpr.resolvedType = completionType;
        completionTdExpr.setBType(completionTdType);

        List<BLangExpression> args = new ArrayList<>(Lists.of(constraintTdExpr, completionTdExpr));
        if (!typeInitExpr.argsExpr.isEmpty()) {
            args.add(typeInitExpr.argsExpr.get(0));
        }
        BLangInvocation streamConstructInvocation = ASTBuilderUtil.createInvocationExprForMethod(
                typeInitExpr.pos, symbol, args, symResolver);
        streamConstructInvocation.setBType(new BStreamType(TypeTags.STREAM, constraintType, completionType, null));
        return streamConstructInvocation;
    }

    private BLangSimpleVariableDef createSimpleVarDef(String name, BType type, BLangExpression expr) {
        BVarSymbol varSymbol = new BVarSymbol(0, Names.fromString(name), this.env.scope.owner.pkgID, type,
                                              this.env.scope.owner, expr.pos, VIRTUAL);
        BLangSimpleVariable simpleVariable = ASTBuilderUtil.createVariable(expr.pos, name, type, expr, varSymbol);
        BLangSimpleVariableDef simpleVariableDef = ASTBuilderUtil.createVariableDef(expr.pos);
        simpleVariableDef.var = simpleVariable;
        simpleVariableDef.setBType(simpleVariable.getBType());
        return simpleVariableDef;
    }

    private BLangSimpleVariableDef createVarDef(String name, BType type, BLangExpression expr, Location location) {
        BSymbol objSym = symResolver.lookupSymbolInMainSpace(env, Names.fromString(name));
        
        if (objSym == null || objSym == symTable.notFoundSymbol) {
            objSym = new BVarSymbol(0, Names.fromString(name), this.env.scope.owner.pkgID, type,
                                    this.env.scope.owner, location, VIRTUAL);
        }
        BLangSimpleVariable objVar = ASTBuilderUtil.createVariable(location, name, type, expr, (BVarSymbol) objSym);
        BLangSimpleVariableDef objVarDef = ASTBuilderUtil.createVariableDef(location);
        objVarDef.var = objVar;
        objVarDef.setBType(objVar.getBType());
        return objVarDef;
    }

    private BType getObjectType(BType bType) {
        BType type = Types.getImpliedType(bType);
        if (type.tag == TypeTags.OBJECT) {
            return bType;
        } else if (type.tag == TypeTags.UNION) {
            return ((BUnionType) type).getMemberTypes().stream()
                    .filter(t -> Types.getImpliedType(t).tag == TypeTags.OBJECT)
                    .findFirst()
                    .orElse(symTable.noType);
        }

        throw new IllegalStateException("None object type '" + type.toString() + "' found in object init context");
    }

    BLangErrorType getErrorTypeNode() {
        BLangErrorType errorTypeNode = (BLangErrorType) TreeBuilder.createErrorTypeNode();
        errorTypeNode.setBType(symTable.errorType);
        errorTypeNode.pos = symTable.builtinPos;
        return errorTypeNode;
    }

    BLangErrorType getErrorOrNillTypeNode() {
        BLangErrorType errorTypeNode = (BLangErrorType) TreeBuilder.createErrorTypeNode();
        errorTypeNode.setBType(symTable.errorOrNilType);
        return errorTypeNode;
    }

    @Override
    public void visit(BLangTernaryExpr ternaryExpr) {
        /*
         * First desugar to if-else:
         *
         * T $result$;
         * if () {
         *    $result$ = thenExpr;
         * } else {
         *    $result$ = elseExpr;
         * }
         *
         */
        BLangSimpleVariableDef resultVarDef =
                createVarDef("$ternary_result$", ternaryExpr.getBType(), null, ternaryExpr.pos);
        BLangBlockStmt thenBody = ASTBuilderUtil.createBlockStmt(ternaryExpr.pos);
        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(ternaryExpr.pos);

        
        BLangSimpleVarRef thenResultVarRef = ASTBuilderUtil.createVariableRef(ternaryExpr.pos, resultVarDef.var.symbol);
        BLangAssignment thenAssignment =
                ASTBuilderUtil.createAssignmentStmt(ternaryExpr.pos, thenResultVarRef, ternaryExpr.thenExpr);
        thenBody.addStatement(thenAssignment);

        
        BLangSimpleVarRef elseResultVarRef = ASTBuilderUtil.createVariableRef(ternaryExpr.pos, resultVarDef.var.symbol);
        BLangAssignment elseAssignment =
                ASTBuilderUtil.createAssignmentStmt(ternaryExpr.pos, elseResultVarRef, ternaryExpr.elseExpr);
        elseBody.addStatement(elseAssignment);

        
        BLangSimpleVarRef resultVarRef = ASTBuilderUtil.createVariableRef(ternaryExpr.pos, resultVarDef.var.symbol);
        BLangIf ifElse = ASTBuilderUtil.createIfElseStmt(ternaryExpr.pos, ternaryExpr.expr, thenBody, elseBody);

        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(ternaryExpr.pos, Lists.of(resultVarDef, ifElse));
        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(ternaryExpr.getBType());

        result = rewriteExpr(stmtExpr);
    }

    @Override
    public void visit(BLangWaitExpr waitExpr) {
        
        if (waitExpr.getExpression().getKind() == NodeKind.BINARY_EXPR) {
            waitExpr.exprList = collectAllBinaryExprs((BLangBinaryExpr) waitExpr.getExpression(), new ArrayList<>());
        } else { 
            waitExpr.exprList = Collections.singletonList(rewriteExpr(waitExpr.getExpression()));
        }
        result = waitExpr;
    }

    private List<BLangExpression> collectAllBinaryExprs(BLangBinaryExpr binaryExpr, List<BLangExpression> exprs) {
        visitBinaryExprOfWait(binaryExpr.lhsExpr, exprs);
        visitBinaryExprOfWait(binaryExpr.rhsExpr, exprs);
        return exprs;
    }

    private void visitBinaryExprOfWait(BLangExpression expr, List<BLangExpression> exprs) {
        if (expr.getKind() == NodeKind.BINARY_EXPR) {
            collectAllBinaryExprs((BLangBinaryExpr) expr, exprs);
        } else {
            expr = rewriteExpr(expr);
            exprs.add(expr);
        }
    }

    @Override
    public void visit(BLangWaitForAllExpr waitExpr) {
        waitExpr.keyValuePairs.forEach(keyValue -> {
            if (keyValue.valueExpr != null) {
                keyValue.valueExpr = rewriteExpr(keyValue.valueExpr);
            } else {
                keyValue.keyExpr = rewriteExpr(keyValue.keyExpr);
            }
        });
        BLangExpression expr = new BLangWaitForAllExpr.BLangWaitLiteral(waitExpr.keyValuePairs, waitExpr.getBType());
        expr.pos = waitExpr.pos;
        result = rewriteExpr(expr);
    }

    @Override
    public void visit(BLangTrapExpr trapExpr) {
        trapExpr.expr = rewriteExpr(trapExpr.expr);
        if (Types.getImpliedType(trapExpr.expr.getBType()).tag != TypeTags.NIL) {
            trapExpr.expr = types.addConversionExprIfRequired(trapExpr.expr, trapExpr.getBType());
        }
        result = trapExpr;
    }

    @Override
    public void visit(BLangBinaryExpr binaryExpr) {
        if (isNullableBinaryExpr(binaryExpr)) {
            BLangStatementExpression stmtExpr = createStmtExprForNullableBinaryExpr(binaryExpr);
            result = rewrite(stmtExpr, env);
            return;
        }

        if (binaryExpr.opKind == OperatorKind.HALF_OPEN_RANGE || binaryExpr.opKind == OperatorKind.CLOSED_RANGE) {
            BLangExpression lhsExpr = binaryExpr.lhsExpr;
            BLangExpression rhsExpr = binaryExpr.rhsExpr;

            
            lhsExpr = createTypeCastExpr(lhsExpr, symTable.intType);
            rhsExpr = createTypeCastExpr(rhsExpr, symTable.intType);

            if (binaryExpr.opKind == OperatorKind.HALF_OPEN_RANGE) {
                rhsExpr = getModifiedIntRangeEndExpr(rhsExpr);
            }

            result = rewriteExpr(replaceWithIntRange(binaryExpr.pos, lhsExpr, rhsExpr));
            return;
        }

        if (binaryExpr.opKind == OperatorKind.AND || binaryExpr.opKind == OperatorKind.OR) {
            visitBinaryLogicalExpr(binaryExpr);
            return;
        }

        OperatorKind binaryOpKind = binaryExpr.opKind;

        if (binaryOpKind == OperatorKind.ADD || binaryOpKind == OperatorKind.SUB ||
                binaryOpKind == OperatorKind.MUL || binaryOpKind == OperatorKind.DIV ||
                binaryOpKind == OperatorKind.MOD || binaryOpKind == OperatorKind.BITWISE_AND ||
                binaryOpKind == OperatorKind.BITWISE_OR || binaryOpKind == OperatorKind.BITWISE_XOR) {
            checkByteTypeIncompatibleOperations(binaryExpr);
        }

        binaryExpr.lhsExpr = rewriteExpr(binaryExpr.lhsExpr);
        binaryExpr.rhsExpr = rewriteExpr(binaryExpr.rhsExpr);
        result = binaryExpr;

        int rhsExprTypeTag = Types.getImpliedType(binaryExpr.rhsExpr.getBType()).tag;
        int lhsExprTypeTag = Types.getImpliedType(binaryExpr.lhsExpr.getBType()).tag;

        
        if (rhsExprTypeTag != lhsExprTypeTag && (binaryExpr.opKind == OperatorKind.EQUAL ||
                                                         binaryExpr.opKind == OperatorKind.NOT_EQUAL ||
                                                         binaryExpr.opKind == OperatorKind.REF_EQUAL ||
                                                         binaryExpr.opKind == OperatorKind.REF_NOT_EQUAL)) {
            if (TypeTags.isIntegerTypeTag(lhsExprTypeTag) && rhsExprTypeTag == TypeTags.BYTE) {
                binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.intType);
                return;
            }

            if (lhsExprTypeTag == TypeTags.BYTE && TypeTags.isIntegerTypeTag(rhsExprTypeTag)) {
                binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.intType);
                return;
            }
        }

        boolean isBinaryShiftOperator = symResolver.isBinaryShiftOperator(binaryOpKind);
        boolean isArithmeticOperator = symResolver.isArithmeticOperator(binaryOpKind);

        
        if (lhsExprTypeTag == rhsExprTypeTag) {
            if (!isBinaryShiftOperator && !isArithmeticOperator) {
                return;
            }
            if (types.isValueType(binaryExpr.lhsExpr.getBType())) {
                return;
            }
        }

        if (binaryExpr.opKind == OperatorKind.ADD && TypeTags.isStringTypeTag(lhsExprTypeTag) &&
                (rhsExprTypeTag == TypeTags.XML || rhsExprTypeTag == TypeTags.XML_TEXT)) {
            
            binaryExpr.lhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.lhsExpr,
                    binaryExpr.lhsExpr.pos, symTable.xmlType);
            return;
        }

        if (binaryExpr.opKind == OperatorKind.ADD && TypeTags.isStringTypeTag(rhsExprTypeTag) &&
                (lhsExprTypeTag == TypeTags.XML || lhsExprTypeTag == TypeTags.XML_TEXT)) {
            
            binaryExpr.rhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.rhsExpr,
                    binaryExpr.rhsExpr.pos, symTable.xmlType);
            return;
        }

        if (symResolver.isBinaryComparisonOperator(binaryOpKind)) {
            createTypeCastExprForRelationalExpr(binaryExpr, lhsExprTypeTag, rhsExprTypeTag);
            return;
        }

        if (lhsExprTypeTag == TypeTags.DECIMAL) {
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, binaryExpr.lhsExpr.getBType());
            return;
        }

        if (rhsExprTypeTag == TypeTags.DECIMAL) {
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, binaryExpr.rhsExpr.getBType());
            return;
        }

        if (lhsExprTypeTag == TypeTags.FLOAT) {
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, binaryExpr.lhsExpr.getBType());
            return;
        }

        if (rhsExprTypeTag == TypeTags.FLOAT) {
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, binaryExpr.rhsExpr.getBType());
            return;
        }

        if (isArithmeticOperator) {
            createTypeCastExprForArithmeticExpr(binaryExpr, lhsExprTypeTag, rhsExprTypeTag);
            return;
        }

        if (isBinaryShiftOperator) {
            createTypeCastExprForBinaryShiftExpr(binaryExpr, lhsExprTypeTag, rhsExprTypeTag);
            return;
        }
    }

    private BLangStatementExpression createStmtExprForNullableBinaryExpr(BLangBinaryExpr binaryExpr) {
        /*
         * int? x = 3;
         * int? y = 5;
         * int? z = x + y;
         * Above is desugared to
         * int? $result$;
         * 
         * int? $lhsExprVar$ = x;
         * int? $rhsExprVar$ = y;
         * if (lhsVar is () or rhsVar is ()) {
         *    $result$ = ();
         * } else {
         *    $result$ = $lhsExprVar$ + $rhsExprVar$;
         * }
         * int z = $result$;
         */
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);

        BUnionType exprBType = (BUnionType) binaryExpr.getBType();
        BType nonNilType = exprBType.getMemberTypes().iterator().next();

        BType rhsType;
        BType lhsType;
        if (symResolver.isArithmeticOperator(binaryExpr.opKind)) {
            rhsType = nonNilType;
            lhsType = nonNilType;
        } else {
            
            rhsType = getBinaryExprOperandNonNilType(binaryExpr.rhsExpr.getBType());
            lhsType = getBinaryExprOperandNonNilType(binaryExpr.lhsExpr.getBType());
        }

        if (binaryExpr.lhsExpr.getBType().isNullable()) {
            binaryExpr.lhsExpr = rewriteExpr(binaryExpr.lhsExpr);
        }

        BLangSimpleVariableDef tempVarDef = createVarDef("$result$",
                binaryExpr.getBType(), null, binaryExpr.pos);
        BLangSimpleVarRef tempVarRef = ASTBuilderUtil.createVariableRef(binaryExpr.pos, tempVarDef.var.symbol);
        blockStmt.addStatement(tempVarDef);

        BLangSimpleVariableDef lhsVarDef = createVarDef("$lhsExprVar$", binaryExpr.lhsExpr.getBType(),
                binaryExpr.lhsExpr, binaryExpr.pos);
        BLangSimpleVarRef lhsVarRef = ASTBuilderUtil.createVariableRef(binaryExpr.pos, lhsVarDef.var.symbol);
        blockStmt.addStatement(lhsVarDef);

        BLangSimpleVariableDef rhsVarDef = createVarDef("$rhsExprVar$", binaryExpr.rhsExpr.getBType(),
                binaryExpr.rhsExpr, binaryExpr.pos);
        BLangSimpleVarRef rhsVarRef = ASTBuilderUtil.createVariableRef(binaryExpr.pos, rhsVarDef.var.symbol);
        blockStmt.addStatement(rhsVarDef);

        BLangTypeTestExpr typeTestExprOne = getNilTypeTestExpr(binaryExpr.pos, lhsVarRef);
        BLangTypeTestExpr typeTestExprTwo = getNilTypeTestExpr(binaryExpr.pos, rhsVarRef);

        BLangBinaryExpr ifBlockCondition = ASTBuilderUtil.createBinaryExpr(binaryExpr.pos, typeTestExprOne,
                typeTestExprTwo, symTable.booleanType, OperatorKind.OR, binaryExpr.opSymbol);

        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);
        BLangAssignment bLangAssignmentIf = ASTBuilderUtil.createAssignmentStmt(binaryExpr.pos, ifBody);
        bLangAssignmentIf.varRef = tempVarRef;
        bLangAssignmentIf.expr = createNilLiteral();

        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);
        BLangAssignment bLangAssignmentElse = ASTBuilderUtil.createAssignmentStmt(binaryExpr.pos, elseBody);
        bLangAssignmentElse.varRef = tempVarRef;

        BLangBinaryExpr newBinaryExpr = ASTBuilderUtil.createBinaryExpr(binaryExpr.pos, lhsVarRef, rhsVarRef,
                nonNilType, binaryExpr.opKind, binaryExpr.opSymbol);
        newBinaryExpr.lhsExpr = createTypeCastExpr(lhsVarRef, lhsType);
        newBinaryExpr.rhsExpr = createTypeCastExpr(rhsVarRef, rhsType);
        bLangAssignmentElse.expr = newBinaryExpr;

        BLangIf ifStatement = ASTBuilderUtil.createIfStmt(binaryExpr.pos, blockStmt);
        ifStatement.expr = ifBlockCondition;
        ifStatement.body = ifBody;
        ifStatement.elseStmt = elseBody;

        BLangStatementExpression stmtExpr = ASTBuilderUtil.createStatementExpression(blockStmt, tempVarRef);
        stmtExpr.setBType(binaryExpr.getBType());

        return stmtExpr;
    }

    BLangTypeTestExpr getNilTypeTestExpr(Location pos, BLangExpression expr) {
        return createTypeCheckExpr(pos, expr, getNillTypeNode());
    }

    private BType getBinaryExprOperandNonNilType(BType operandType) {
        return operandType.isNullable() ? types.getSafeType(operandType, true, false) : operandType;
    }

    private boolean isNullableBinaryExpr(BLangBinaryExpr binaryExpr) {
        if ((binaryExpr.lhsExpr.getBType() != null && binaryExpr.rhsExpr.getBType() != null) &&
                (binaryExpr.rhsExpr.getBType().isNullable() ||
                        binaryExpr.lhsExpr.getBType().isNullable())) {
            switch (binaryExpr.getOperatorKind()) {
                case ADD:
                case SUB:
                case MUL:
                case DIV:
                case MOD:
                case BITWISE_LEFT_SHIFT:
                case BITWISE_RIGHT_SHIFT:
                case BITWISE_UNSIGNED_RIGHT_SHIFT:
                case BITWISE_AND:
                case BITWISE_OR:
                case BITWISE_XOR:
                    return true;
            }
        }
        return false;
    }

    private void createTypeCastExprForArithmeticExpr(BLangBinaryExpr binaryExpr, int lhsExprTypeTag,
                                                     int rhsExprTypeTag) {
        if ((TypeTags.isIntegerTypeTag(lhsExprTypeTag) && TypeTags.isIntegerTypeTag(rhsExprTypeTag)) ||
                (TypeTags.isStringTypeTag(lhsExprTypeTag) && TypeTags.isStringTypeTag(rhsExprTypeTag)) ||
                (TypeTags.isXMLTypeTag(lhsExprTypeTag) && TypeTags.isXMLTypeTag(rhsExprTypeTag))) {
            return;
        }
        if (TypeTags.isXMLTypeTag(lhsExprTypeTag) && !TypeTags.isXMLTypeTag(rhsExprTypeTag)) {
            if (types.checkTypeContainString(binaryExpr.rhsExpr.getBType())) {
                binaryExpr.rhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.rhsExpr,
                        binaryExpr.rhsExpr.pos, symTable.xmlType);
                return;
            }
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.xmlType);
            return;
        }
        if (TypeTags.isXMLTypeTag(rhsExprTypeTag) && !TypeTags.isXMLTypeTag(lhsExprTypeTag)) {
            if (types.checkTypeContainString(binaryExpr.lhsExpr.getBType())) {
                binaryExpr.lhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.lhsExpr,
                        binaryExpr.rhsExpr.pos, symTable.xmlType);
                return;
            }
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.xmlType);
            return;
        }
        binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, binaryExpr.getBType());
        binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, binaryExpr.getBType());
    }

    private void createTypeCastExprForBinaryShiftExpr(BLangBinaryExpr binaryExpr, int lhsExprTypeTag,
                                                      int rhsExprTypeTag) {
        boolean isLhsIntegerType = TypeTags.isIntegerTypeTag(lhsExprTypeTag);
        boolean isRhsIntegerType = TypeTags.isIntegerTypeTag(rhsExprTypeTag);
        if (isLhsIntegerType || lhsExprTypeTag == TypeTags.BYTE) {
            if (isRhsIntegerType || rhsExprTypeTag == TypeTags.BYTE) {
                return;
            }
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.intType);
            return;
        }

        if (isRhsIntegerType || rhsExprTypeTag == TypeTags.BYTE) {
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.intType);
            return;
        }

        binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.intType);
        binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.intType);
    }

    private void createTypeCastExprForRelationalExpr(BLangBinaryExpr binaryExpr, int lhsExprTypeTag,
                                                     int rhsExprTypeTag) {
        boolean isLhsIntegerType = TypeTags.isIntegerTypeTag(lhsExprTypeTag);
        boolean isRhsIntegerType = TypeTags.isIntegerTypeTag(rhsExprTypeTag);
        BType lhsExprType = binaryExpr.lhsExpr.getBType();
        BType rhsExprType = binaryExpr.rhsExpr.getBType();

        if ((isLhsIntegerType && isRhsIntegerType) || (lhsExprTypeTag == TypeTags.BYTE &&
                rhsExprTypeTag == TypeTags.BYTE)) {
            return;
        }

        if (lhsExprTypeTag == TypeTags.DECIMAL) {
            addTypeCastForBinaryExprB(binaryExpr, lhsExprType, rhsExprType);
            return;
        }

        if (rhsExprTypeTag == TypeTags.DECIMAL) {
            addTypeCastForBinaryExprA(binaryExpr, rhsExprType, lhsExprType);
            return;
        }

        if (lhsExprTypeTag == TypeTags.FLOAT) {
            addTypeCastForBinaryExprB(binaryExpr, lhsExprType, rhsExprType);
            return;
        }

        if (rhsExprTypeTag == TypeTags.FLOAT) {
            addTypeCastForBinaryExprA(binaryExpr, rhsExprType, lhsExprType);
            return;
        }

        if (isLhsIntegerType && !isRhsIntegerType) {
            addTypeCastForBinaryExprB(binaryExpr, symTable.intType, rhsExprType);
            return;
        }

        if (!isLhsIntegerType && isRhsIntegerType) {
            addTypeCastForBinaryExprA(binaryExpr, symTable.intType, lhsExprType);
            return;
        }

        if (lhsExprTypeTag == TypeTags.BYTE || rhsExprTypeTag == TypeTags.BYTE) {
            if ((lhsExprTypeTag == TypeTags.UNION && lhsExprType.isNullable()) ||
                    (rhsExprTypeTag == TypeTags.UNION && rhsExprType.isNullable())) {
                binaryExpr.lhsExpr = addNilType(symTable.intType, binaryExpr.lhsExpr);
                binaryExpr.rhsExpr = addNilType(symTable.intType, binaryExpr.rhsExpr);
                return;
            }
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.intType);
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.intType);
            return;
        }

        boolean isLhsStringType = TypeTags.isStringTypeTag(lhsExprTypeTag);
        boolean isRhsStringType = TypeTags.isStringTypeTag(rhsExprTypeTag);

        if (isLhsStringType && isRhsStringType) {
            return;
        }

        if (isLhsStringType && !isRhsStringType) {
            addTypeCastForBinaryExprB(binaryExpr, symTable.stringType, rhsExprType);
            return;
        }

        if (!isLhsStringType && isRhsStringType) {
            addTypeCastForBinaryExprA(binaryExpr, symTable.stringType, lhsExprType);
        }
    }

    private void addTypeCastForBinaryExprA(BLangBinaryExpr binaryExpr, BType rhsExprType, BType lhsExprType) {
        if (Types.getImpliedType(lhsExprType).tag == TypeTags.UNION && lhsExprType.isNullable()) {
            binaryExpr.rhsExpr = addNilType(rhsExprType, binaryExpr.rhsExpr);
        } else {
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, rhsExprType);
        }
    }

    private void addTypeCastForBinaryExprB(BLangBinaryExpr binaryExpr, BType lhsExprType, BType rhsExprType) {
        if (Types.getImpliedType(rhsExprType).tag == TypeTags.UNION && rhsExprType.isNullable()) {
            binaryExpr.lhsExpr = addNilType(lhsExprType, binaryExpr.lhsExpr);
        } else {
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, lhsExprType);
        }
    }

    private BLangExpression addNilType(BType exprType, BLangExpression expr) {
        LinkedHashSet<BType> members = new LinkedHashSet<>(2);
        members.add(exprType);
        members.add(symTable.nilType);
        BUnionType unionType = new BUnionType(null, members, true, false);
        return createTypeCastExpr(expr, unionType);
    }

    private BLangInvocation replaceWithIntRange(Location location, BLangExpression lhsExpr,
                                                BLangExpression rhsExpr) {
        BInvokableSymbol symbol = (BInvokableSymbol) symTable.langInternalModuleSymbol.scope
                .lookup(Names.CREATE_INT_RANGE).symbol;
        BLangInvocation createIntRangeInvocation = ASTBuilderUtil.createInvocationExprForMethod(location, symbol,
                new ArrayList<>(Lists.of(lhsExpr, rhsExpr)), symResolver);
        createIntRangeInvocation.setBType(symTable.intRangeType);
        return createIntRangeInvocation;
    }

    private void checkByteTypeIncompatibleOperations(BLangBinaryExpr binaryExpr) {
        if (binaryExpr.expectedType == null) {
            return;
        }

        int rhsExprTypeTag = Types.getImpliedType(binaryExpr.rhsExpr.getBType()).tag;
        int lhsExprTypeTag = Types.getImpliedType(binaryExpr.lhsExpr.getBType()).tag;
        if (rhsExprTypeTag != TypeTags.BYTE && lhsExprTypeTag != TypeTags.BYTE) {
            return;
        }

        int resultTypeTag = Types.getImpliedType(binaryExpr.expectedType).tag;
        if (resultTypeTag == TypeTags.INT) {
            if (rhsExprTypeTag == TypeTags.BYTE) {
                binaryExpr.rhsExpr = types.addConversionExprIfRequired(binaryExpr.rhsExpr, symTable.intType);
            }

            if (lhsExprTypeTag == TypeTags.BYTE) {
                binaryExpr.lhsExpr = types.addConversionExprIfRequired(binaryExpr.lhsExpr, symTable.intType);
            }
        }
    }

    /**
     * This method checks whether given binary expression is related to shift operation.
     * If its true, then both lhs and rhs of the binary expression will be converted to 'int' type.
     * <p>
     * byte a = 12;
     * byte b = 34;
     * int i = 234;
     * int j = -4;
     * <p>
     * true: where binary expression's expected type is 'int'
     * int i1 = a >> b;
     * int i2 = a << b;
     * int i3 = a >> i;
     * int i4 = a << i;
     * int i5 = i >> j;
     * int i6 = i << j;
     */
    private boolean isBitwiseShiftOperation(BLangBinaryExpr binaryExpr) {
        return binaryExpr.opKind == OperatorKind.BITWISE_LEFT_SHIFT ||
                binaryExpr.opKind == OperatorKind.BITWISE_RIGHT_SHIFT ||
                binaryExpr.opKind == OperatorKind.BITWISE_UNSIGNED_RIGHT_SHIFT;
    }

    @Override
    public void visit(BLangElvisExpr elvisExpr) {
        Location pos = elvisExpr.pos;
        String resultVarName = "_$result$_";
        BType resultType = elvisExpr.getBType();
        BLangSimpleVariable resultVar =
                ASTBuilderUtil.createVariable(pos, resultVarName, resultType, null,
                        new BVarSymbol(0, Names.fromString(resultVarName),
                                this.env.scope.owner.pkgID, resultType,
                                this.env.scope.owner, pos, VIRTUAL));
        BLangSimpleVariableDef resultVarDef = ASTBuilderUtil.createVariableDef(pos, resultVar);
        resultVarDef.desugared = true;
        BLangSimpleVarRef resultVarRef = ASTBuilderUtil.createVariableRef(pos, resultVar.symbol);

        String lhsResultVarName = GEN_VAR_PREFIX.value;
        BLangSimpleVariable lhsResultVar =
                ASTBuilderUtil.createVariable(pos, lhsResultVarName, elvisExpr.lhsExpr.getBType(), elvisExpr.lhsExpr,
                        new BVarSymbol(0, Names.fromString(lhsResultVarName),
                                this.env.scope.owner.pkgID, elvisExpr.lhsExpr.getBType(),
                                this.env.scope.owner, elvisExpr.pos, VIRTUAL));
        BLangSimpleVariableDef lhsResultVarDef = ASTBuilderUtil.createVariableDef(pos, lhsResultVar);
        BLangSimpleVarRef lhsResultVarRef = ASTBuilderUtil.createVariableRef(pos, lhsResultVar.symbol);

        BLangAssignment nilAssignment = ASTBuilderUtil.createAssignmentStmt(pos, resultVarRef, elvisExpr.rhsExpr);
        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(pos);
        ifBody.addStatement(nilAssignment);

        BLangAssignment notNilAssignment = ASTBuilderUtil.createAssignmentStmt(pos, resultVarRef,
                createTypeCastExpr(lhsResultVarRef, resultType));
        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(pos);
        elseBody.addStatement(notNilAssignment);

        BLangIf ifStmt = ASTBuilderUtil.createIfElseStmt(pos, getNilTypeTestExpr(pos, lhsResultVarRef),
                ifBody, elseBody);
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(pos, new ArrayList<>() {{
            add(resultVarDef);
            add(lhsResultVarDef);
            add(ifStmt);
        }});
        BLangStatementExpression stmtExpr = ASTBuilderUtil.createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(resultType);
        result = rewriteExpr(stmtExpr);
    }

    @Override
    public void visit(BLangUnaryExpr unaryExpr) {

        if (isNullableUnaryExpr(unaryExpr)) {
            BLangStatementExpression statementExpression = createStmtExprForNilableUnaryExpr(unaryExpr);
            result = rewrite(statementExpression, env);
            return;
        }

        if (OperatorKind.BITWISE_COMPLEMENT == unaryExpr.operator) {
            
            
            rewriteBitwiseComplementOperator(unaryExpr);
            return;
        }

        
        
        if (types.isExpressionInUnaryValid(unaryExpr.expr) &&
                Types.getImpliedType(unaryExpr.expectedType).tag == TypeTags.FINITE) {
            result = rewriteExpr(Types.constructNumericLiteralFromUnaryExpr(unaryExpr));
            return;
        }

        OperatorKind opKind = unaryExpr.operator;
        if (opKind == OperatorKind.ADD || opKind == OperatorKind.SUB) {
            createTypeCastExprForUnaryPlusAndMinus(unaryExpr);
        }
        unaryExpr.expr = rewriteExpr(unaryExpr.expr);
        result = unaryExpr;
    }

    private void createTypeCastExprForUnaryPlusAndMinus(BLangUnaryExpr unaryExpr) {
        BLangExpression expr = unaryExpr.expr;
        if (TypeTags.isIntegerTypeTag(Types.getImpliedType(expr.getBType()).tag)) {
            return;
        }
        unaryExpr.expr = createTypeCastExpr(expr, unaryExpr.getBType());
    }

    /**
     * This method desugar a bitwise complement (~) unary expressions into a bitwise xor binary expression as below.
     * Example : ~a  -> a ^ -1;
     * ~ 11110011 -> 00001100
     * 11110011 ^ 11111111 -> 00001100
     *
     * @param unaryExpr the bitwise complement expression
     */
    private void rewriteBitwiseComplementOperator(BLangUnaryExpr unaryExpr) {
        final Location pos = unaryExpr.pos;
        final BLangBinaryExpr binaryExpr = (BLangBinaryExpr) TreeBuilder.createBinaryExpressionNode();
        binaryExpr.pos = pos;
        binaryExpr.opKind = OperatorKind.BITWISE_XOR;
        binaryExpr.lhsExpr = unaryExpr.expr;
        if (TypeTags.BYTE == Types.getImpliedType(unaryExpr.getBType()).tag) {
            binaryExpr.setBType(symTable.byteType);
            binaryExpr.rhsExpr = ASTBuilderUtil.createLiteral(pos, symTable.byteType, 0xffL);
            binaryExpr.opSymbol = (BOperatorSymbol) symResolver.resolveBinaryOperator(OperatorKind.BITWISE_XOR,
                    symTable.byteType, symTable.byteType);
        } else {
            binaryExpr.setBType(symTable.intType);
            binaryExpr.rhsExpr = ASTBuilderUtil.createLiteral(pos, symTable.intType, -1L);
            binaryExpr.opSymbol = (BOperatorSymbol) symResolver.resolveBinaryOperator(OperatorKind.BITWISE_XOR,
                    symTable.intType, symTable.intType);
        }
        result = rewriteExpr(binaryExpr);
    }

    private BLangStatementExpression createStmtExprForNilableUnaryExpr(BLangUnaryExpr unaryExpr) {
        /*
         * int? x = 3;
         * int? y = +x;
         *
         *
         * Above is desugared to
         * int? $result$;
         * if (x is ()) {
         *    $result$ = ();
         * } else {
         *    $result$ = +x;
         * }
         * int y = $result$
         */
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(unaryExpr.pos);

        BUnionType exprBType = (BUnionType) unaryExpr.getBType();
        BType nilLiftType = exprBType.getMemberTypes().iterator().next();

        unaryExpr.expr = rewriteExpr(unaryExpr.expr);

        BLangSimpleVariableDef tempVarDef = createVarDef("$result",
                unaryExpr.getBType(), createNilLiteral(), unaryExpr.pos);
        BLangSimpleVarRef tempVarRef = ASTBuilderUtil.createVariableRef(unaryExpr.pos, tempVarDef.var.symbol);

        blockStmt.addStatement(tempVarDef);

        BLangTypeTestExpr typeTestExpr = getNilTypeTestExpr(unaryExpr.pos, unaryExpr.expr);

        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(unaryExpr.pos);
        BLangAssignment bLangAssignmentIf = ASTBuilderUtil.createAssignmentStmt(unaryExpr.pos, ifBody);
        bLangAssignmentIf.varRef = tempVarRef;
        bLangAssignmentIf.expr = createNilLiteral();

        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(unaryExpr.pos);
        BLangAssignment bLangAssignmentElse = ASTBuilderUtil.createAssignmentStmt(unaryExpr.pos, elseBody);
        bLangAssignmentElse.varRef = tempVarRef;

        BLangExpression expr = createTypeCastExpr(unaryExpr.expr, nilLiftType);
        bLangAssignmentElse.expr = ASTBuilderUtil.createUnaryExpr(unaryExpr.pos, expr,
                                                                  nilLiftType, unaryExpr.operator, unaryExpr.opSymbol);

        BLangIf ifStatement = ASTBuilderUtil.createIfStmt(unaryExpr.pos, blockStmt);
        ifStatement.expr = typeTestExpr;
        ifStatement.body = ifBody;
        ifStatement.elseStmt = elseBody;

        BLangStatementExpression stmtExpr = ASTBuilderUtil.createStatementExpression(blockStmt, tempVarRef);
        stmtExpr.setBType(unaryExpr.getBType());

        return stmtExpr;
    }

    private boolean isNullableUnaryExpr(BLangUnaryExpr unaryExpr) {
        if (unaryExpr.getBType() != null && unaryExpr.getBType().isNullable()) {
            switch (unaryExpr.operator) {
                case ADD:
                case SUB:
                case BITWISE_COMPLEMENT:
                    return true;
            }
        }
        return false;
    }

    @Override
    public void visit(BLangTypeConversionExpr conversionExpr) {
        
        
        
        

        
        if (conversionExpr.typeNode == null && !conversionExpr.annAttachments.isEmpty()) {
            result = rewriteExpr(conversionExpr.expr);
            return;
        }

        BType targetType = conversionExpr.targetType;
        conversionExpr.typeNode = rewrite(conversionExpr.typeNode, env);

        conversionExpr.expr = rewriteExpr(conversionExpr.expr);
        result = conversionExpr;
    }

    @Override
    public void visit(BLangLambdaFunction bLangLambdaFunction) {
        bLangLambdaFunction.function = rewrite(bLangLambdaFunction.function, bLangLambdaFunction.capturedClosureEnv);
        BLangFunction function = bLangLambdaFunction.function;
        
        if (function.flagSet.contains(Flag.WORKER) && Symbols.isFlagOn(function.symbol.type.flags, Flags.ISOLATED) &&
                Symbols.isFlagOn(env.enclInvokable.symbol.flags, Flags.ISOLATED)) {
            addStrandAnnotationWithThreadAny(function.pos);
            function.addAnnotationAttachment(this.strandAnnotAttachement);
            BInvokableSymbol funcSymbol = function.symbol;
            funcSymbol.addAnnotation(this.strandAnnotAttachement.annotationAttachmentSymbol);
            funcSymbol.schedulerPolicy = SchedulerPolicy.ANY;
        }
        bLangLambdaFunction.capturedClosureEnv = null;
        result = bLangLambdaFunction;
    }

    @Override
    public void visit(BLangArrowFunction bLangArrowFunction) {
        BLangFunction bLangFunction = (BLangFunction) TreeBuilder.createFunctionNode();
        bLangFunction.setName(bLangArrowFunction.functionName);

        BLangLambdaFunction lambdaFunction = (BLangLambdaFunction) TreeBuilder.createLambdaFunctionNode();
        lambdaFunction.pos = bLangArrowFunction.pos;
        bLangFunction.addFlag(Flag.LAMBDA);
        lambdaFunction.function = bLangFunction;

        
        BLangValueType returnType = (BLangValueType) TreeBuilder.createValueTypeNode();
        returnType.setBType(bLangArrowFunction.body.expr.getBType());
        bLangFunction.setReturnTypeNode(returnType);
        bLangFunction.setBody(populateArrowExprBodyBlock(bLangArrowFunction));

        bLangArrowFunction.params.forEach(bLangFunction::addParameter);
        lambdaFunction.parent = bLangArrowFunction.parent;
        lambdaFunction.setBType(bLangArrowFunction.funcType);

        
        BLangFunction funcNode = lambdaFunction.function;
        BInvokableSymbol funcSymbol = Symbols.createFunctionSymbol(Flags.asMask(funcNode.flagSet),
                                                                   new Name(funcNode.name.value),
                                                                   new Name(funcNode.name.originalValue),
                                                                   env.enclPkg.symbol.pkgID,
                                                                   bLangArrowFunction.funcType,
                                                                   env.enclEnv.enclVarSym, true,
                                                                   bLangArrowFunction.pos, VIRTUAL);

        funcSymbol.originalName = new Name(funcNode.name.originalValue);

        SymbolEnv invokableEnv = SymbolEnv.createFunctionEnv(funcNode, funcSymbol.scope, env);
        defineInvokableSymbol(funcNode, funcSymbol, invokableEnv);

        List<BVarSymbol> paramSymbols = funcNode.requiredParams.stream().peek(varNode -> {
            Scope enclScope = invokableEnv.scope;
            varNode.symbol.kind = SymbolKind.FUNCTION;
            varNode.symbol.owner = invokableEnv.scope.owner;
            enclScope.define(varNode.symbol.name, varNode.symbol);
        }).map(varNode -> varNode.symbol).collect(Collectors.toList());

        funcSymbol.params = paramSymbols;
        funcSymbol.restParam = getRestSymbol(funcNode);
        funcSymbol.retType = funcNode.returnTypeNode.getBType();
        
        List<BType> paramTypes = paramSymbols.stream().map(paramSym -> paramSym.type).collect(Collectors.toList());
        funcNode.setBType(new BInvokableType(paramTypes, getRestType(funcSymbol), funcNode.returnTypeNode.getBType(),
                          funcSymbol.type.tsymbol));

        lambdaFunction.function.pos = bLangArrowFunction.pos;
        lambdaFunction.function.body.pos = bLangArrowFunction.pos;
        
        lambdaFunction.capturedClosureEnv = env;
        env.enclPkg.addFunction(lambdaFunction.function);
        result = rewriteExpr(lambdaFunction);
    }

    private void defineInvokableSymbol(BLangInvokableNode invokableNode, BInvokableSymbol funcSymbol,
                                       SymbolEnv invokableEnv) {
        invokableNode.symbol = funcSymbol;
        funcSymbol.scope = new Scope(funcSymbol);
        invokableEnv.scope = funcSymbol.scope;
    }

    @Override
    public void visit(BLangXMLQName xmlQName) {
        result = xmlQName;
    }

    @Override
    public void visit(BLangXMLAttribute xmlAttribute) {
        xmlAttribute.name = rewriteExpr(xmlAttribute.name);
        xmlAttribute.value = rewriteExpr(xmlAttribute.value);
        result = xmlAttribute;
    }

    @Override
    public void visit(BLangXMLElementLiteral xmlElementLiteral) {
        xmlElementLiteral.attributes = rewriteExprs(xmlElementLiteral.attributes);

        
        Iterator<BLangXMLAttribute> attributesItr = xmlElementLiteral.attributes.iterator();
        while (attributesItr.hasNext()) {
            BLangXMLAttribute attribute = attributesItr.next();
            if (!attribute.isNamespaceDeclr) {
                continue;
            }

            
            BLangXMLNS xmlns;
            if ((xmlElementLiteral.scope.owner.tag & SymTag.PACKAGE) == SymTag.PACKAGE) {
                xmlns = new BLangPackageXMLNS();
            } else {
                xmlns = new BLangLocalXMLNS();
            }
            xmlns.namespaceURI = attribute.value.concatExpr;
            xmlns.prefix = ((BLangXMLQName) attribute.name).localname;
            xmlns.symbol = attribute.symbol;

            xmlElementLiteral.inlineNamespaces.add(xmlns);
        }

        List<BLangXMLNS> prevInlineNamespaces = this.inlineXMLNamespaces;
        if (isVisitingQuery && this.inlineXMLNamespaces != null) {
            
            xmlElementLiteral.inlineNamespaces.addAll(this.inlineXMLNamespaces);
        }
        this.inlineXMLNamespaces = xmlElementLiteral.inlineNamespaces;

        xmlElementLiteral.startTagName = rewriteExpr(xmlElementLiteral.startTagName);
        xmlElementLiteral.endTagName = rewriteExpr(xmlElementLiteral.endTagName);
        xmlElementLiteral.modifiedChildren = rewriteExprs(xmlElementLiteral.modifiedChildren);

        this.inlineXMLNamespaces = prevInlineNamespaces;
        result = xmlElementLiteral;
    }

    @Override
    public void visit(BLangXMLSequenceLiteral xmlSequenceLiteral) {
        for (BLangExpression xmlItem : xmlSequenceLiteral.xmlItems) {
            rewriteExpr(xmlItem);
        }
        result = xmlSequenceLiteral;
    }

    @Override
    public void visit(BLangXMLTextLiteral xmlTextLiteral) {
        xmlTextLiteral.concatExpr = rewriteExpr(constructStringTemplateConcatExpression(xmlTextLiteral.textFragments));
        result = xmlTextLiteral;
    }

    @Override
    public void visit(BLangXMLCommentLiteral xmlCommentLiteral) {
        xmlCommentLiteral.concatExpr = rewriteExpr(
                constructStringTemplateConcatExpression(xmlCommentLiteral.textFragments));
        result = xmlCommentLiteral;
    }

    @Override
    public void visit(BLangXMLProcInsLiteral xmlProcInsLiteral) {
        xmlProcInsLiteral.target = rewriteExpr(xmlProcInsLiteral.target);
        xmlProcInsLiteral.dataConcatExpr =
                rewriteExpr(constructStringTemplateConcatExpression(xmlProcInsLiteral.dataFragments));
        result = xmlProcInsLiteral;
    }

    @Override
    public void visit(BLangXMLQuotedString xmlQuotedString) {
        xmlQuotedString.concatExpr = rewriteExpr(
                constructStringTemplateConcatExpression(xmlQuotedString.textFragments));
        result = xmlQuotedString;
    }

    @Override
    public void visit(BLangStringTemplateLiteral stringTemplateLiteral) {
        result = rewriteExpr(constructStringTemplateConcatExpression(stringTemplateLiteral.exprs));
    }

    /**
     * The raw template literal gets desugared to a type init expression. For each literal, a new object class type
     * def is generated from the object type. The type init expression creates an instance of this generated object
     * type. For example, consider the following statements:
     *      string name = "Pubudu";
     *      'object:RawTemplate rt = `Hello ${name}!`;
     *
     * The raw template literal above is desugared to:
     *      type RawTemplate$Impl$0 object {
     *          public string[] strings = ["Hello ", "!"];
     *          public (any|error)[] insertions;
     *
     *          function init((any|error)[] insertions) {
     *              self.insertions = insertions;
     *          }
     *      };
     *
     *      
     *      'object:RawTemplate rt = new RawTemplate$Impl$0([name]);
     *
     * @param rawTemplateLiteral The raw template literal to be desugared.
     */
    @Override
    public void visit(BLangRawTemplateLiteral rawTemplateLiteral) {
        Location pos = rawTemplateLiteral.pos;
        BObjectType objType = (BObjectType) Types.getImpliedType(rawTemplateLiteral.getBType());
        BLangClassDefinition objClassDef =
                desugarTemplateLiteralObjectTypedef(rawTemplateLiteral.strings, objType, pos);
        BObjectType classObjType = (BObjectType) objClassDef.getBType();

        BVarSymbol insertionsSym = classObjType.fields.get("insertions").symbol;
        BLangListConstructorExpr insertionsList = ASTBuilderUtil.createListConstructorExpr(pos, insertionsSym.type);
        insertionsList.exprs.addAll(rawTemplateLiteral.insertions);
        insertionsList.expectedType = insertionsSym.type;

        
        BLangTypeInit typeNewExpr = ASTBuilderUtil.createEmptyTypeInit(pos, classObjType);
        typeNewExpr.argsExpr.add(insertionsList);
        BLangInvocation initInvocation = (BLangInvocation) typeNewExpr.initInvocation;
        initInvocation.argExprs.add(insertionsList);
        initInvocation.requiredArgs.add(insertionsList);

        result = rewriteExpr(typeNewExpr);
    }

    /**
     * This method desugars a raw template literal object class for the provided raw template object type as follows.
     * A literal defined as 'object:RawTemplate rt = `Hello ${name}!`;
     * is desugared to,
     *      type $anonType$0 object {
     *          public string[] strings = ["Hello ", "!"];
     *          public (any|error)[] insertions;
     *
     *          function init((any|error)[] insertions) {
     *              self.insertions = insertions;
     *          }
     *      };
     * @param strings    The string portions of the literal
     * @param objectType The abstract object type for which an object class needs to be generated
     * @param pos        The diagnostic position info for the type node
     * @return Returns the generated concrete object class def
     */
    private BLangClassDefinition desugarTemplateLiteralObjectTypedef(List<BLangLiteral> strings, BObjectType objectType,
                                                                    Location pos) {
        
        BObjectTypeSymbol tSymbol = (BObjectTypeSymbol) objectType.tsymbol;
        Name objectClassName = Names.fromString(
                anonModelHelper.getNextRawTemplateTypeKey(env.enclPkg.packageID, tSymbol.name));

        BObjectTypeSymbol classTSymbol = Symbols.createClassSymbol(tSymbol.flags, objectClassName,
                                                                   env.enclPkg.packageID, null, env.enclPkg.symbol,
                                                                   pos, VIRTUAL, false);
        classTSymbol.flags |= Flags.CLASS;

        
        BObjectType objectClassType = new BObjectType(classTSymbol, classTSymbol.flags);
        objectClassType.fields = objectType.fields;
        classTSymbol.type = objectClassType;
        objectClassType.typeIdSet.add(objectType.typeIdSet);

        



        BLangClassDefinition classDef = TypeDefBuilderHelper.createClassDef(pos, classTSymbol, env);
        classDef.name = ASTBuilderUtil.createIdentifier(pos, objectClassType.tsymbol.name.value);

        
        
        BType stringsType = objectClassType.fields.get("strings").symbol.type;
        BLangListConstructorExpr stringsList = ASTBuilderUtil.createListConstructorExpr(pos, stringsType);
        stringsList.exprs.addAll(strings);
        stringsList.expectedType = stringsType;
        classDef.fields.get(0).expr = stringsList;

        
        BLangFunction userDefinedInitFunction = createUserDefinedObjectInitFn(classDef, env);
        classDef.initFunction = userDefinedInitFunction;
        env.enclPkg.functions.add(userDefinedInitFunction);
        env.enclPkg.topLevelNodes.add(userDefinedInitFunction);

        
        BLangFunction tempGeneratedInitFunction = createGeneratedInitializerFunction(classDef, env);
        tempGeneratedInitFunction.clonedEnv = SymbolEnv.createFunctionEnv(tempGeneratedInitFunction,
                                                                          tempGeneratedInitFunction.symbol.scope, env);
        this.semanticAnalyzer.analyzeNode(tempGeneratedInitFunction, env);
        classDef.generatedInitFunction = tempGeneratedInitFunction;
        env.enclPkg.functions.add(classDef.generatedInitFunction);
        env.enclPkg.topLevelNodes.add(classDef.generatedInitFunction);

        return rewrite(classDef, env);
    }

    /**
     * Creates a user-defined init() method for the provided object type node. If there are fields without default
     * values specified in the type node, this will add parameters for those fields in the init() method and assign the
     * param values to the respective fields in the method body.
     *
     * @param classDefn The object type node for which the init() method is generated
     * @param env            The symbol env for the object type node
     * @return The generated init() method
     */
    private BLangFunction createUserDefinedObjectInitFn(BLangClassDefinition classDefn, SymbolEnv env) {
        BLangFunction initFunction =
                TypeDefBuilderHelper.createInitFunctionForStructureType(classDefn.symbol, env, names,
                        Names.USER_DEFINED_INIT_SUFFIX, symTable, classDefn.getBType());
        BObjectTypeSymbol typeSymbol = ((BObjectTypeSymbol) classDefn.getBType().tsymbol);
        typeSymbol.initializerFunc = new BAttachedFunction(Names.USER_DEFINED_INIT_SUFFIX, initFunction.symbol,
                                                           (BInvokableType) initFunction.getBType(), classDefn.pos);
        classDefn.initFunction = initFunction;
        initFunction.returnTypeNode.setBType(symTable.nilType);

        BLangBlockFunctionBody initFuncBody = (BLangBlockFunctionBody) initFunction.body;
        BInvokableType initFnType = (BInvokableType) initFunction.getBType();
        for (BLangSimpleVariable field : classDefn.fields) {
            if (field.expr != null) {
                continue;
            }
            BVarSymbol fieldSym = field.symbol;
            BVarSymbol paramSym = new BVarSymbol(Flags.FINAL, fieldSym.name, this.env.scope.owner.pkgID, fieldSym.type,
                                                 initFunction.symbol, classDefn.pos, VIRTUAL);
            BLangSimpleVariable param = ASTBuilderUtil.createVariable(classDefn.pos, fieldSym.name.value,
                                                                      fieldSym.type, null, paramSym);
            param.flagSet.add(Flag.FINAL);
            initFunction.symbol.scope.define(paramSym.name, paramSym);
            initFunction.symbol.params.add(paramSym);
            initFnType.paramTypes.add(param.getBType());
            initFunction.requiredParams.add(param);

            BLangSimpleVarRef paramRef = ASTBuilderUtil.createVariableRef(initFunction.pos, paramSym);
            BLangAssignment fieldInit = createStructFieldUpdate(initFunction, paramRef, fieldSym, field.getBType(),
                                                                initFunction.receiver.symbol, field.name);
            initFuncBody.addStatement(fieldInit);
        }

        return initFunction;
    }

    @Override
    public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) {
        asyncSendExpr.expr = visitCloneInvocation(rewriteExpr(asyncSendExpr.expr), asyncSendExpr.expr.getBType());
        this.channelsWithinIfStmt.add(asyncSendExpr.getChannel());
        result = asyncSendExpr;
    }

    @Override
    public void visit(BLangWorkerSyncSendExpr syncSendExpr) {
        syncSendExpr.expr = visitCloneInvocation(rewriteExpr(syncSendExpr.expr), syncSendExpr.expr.getBType());
        this.channelsWithinIfStmt.add(syncSendExpr.getChannel());
        result = syncSendExpr;
    }

    @Override
    public void visit(BLangAlternateWorkerReceive altWorkerReceive) {
        result = altWorkerReceive;
    }

    @Override
    public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) {
        result = multipleWorkerReceive;
    }

    @Override
    public void visit(BLangWorkerReceive workerReceiveNode) {
        result = workerReceiveNode;
    }

    @Override
    public void visit(BLangWorkerFlushExpr workerFlushExpr) {
        workerFlushExpr.workerIdentifierList = workerFlushExpr.cachedWorkerSendStmts
                .stream().map(send -> send.workerIdentifier).distinct().collect(Collectors.toList());
        result = workerFlushExpr;
    }

    @Override
    public void visit(BLangTransactionalExpr transactionalExpr) {
        BInvokableSymbol isTransactionalSymbol =
                (BInvokableSymbol) transactionDesugar.getInternalTransactionModuleInvokableSymbol(IS_TRANSACTIONAL);
        result = ASTBuilderUtil
                .createInvocationExprMethod(transactionalExpr.pos, isTransactionalSymbol, Collections.emptyList(),
                        Collections.emptyList(), symResolver);
    }

    @Override
    public void visit(BLangCommitExpr commitExpr) {
        BLangStatementExpression stmtExpr = transactionDesugar.desugar(commitExpr, env);
        result = rewriteExpr(stmtExpr);
    }

    @Override
    public void visit(BLangFail failNode) {
        if (this.onFailClause != null && !this.desugarToReturn) {
            if (this.onFailClause.bodyContainsFail) {
                result = rewriteNestedOnFail(this.onFailClause, failNode);
            } else {
                result = createOnFailInvocation(onFailClause, failNode);
            }
        } else {
            BLangReturn stmt = ASTBuilderUtil.createReturnStmt(failNode.pos, rewrite(failNode.expr, env));
            stmt.desugared = true;
            result = stmt;
        }
    }

    
    

    @Override
    public void visit(BLangLocalVarRef localVarRef) {
        result = localVarRef;
    }

    @Override
    public void visit(BLangFieldVarRef fieldVarRef) {
        result = fieldVarRef;
    }

    @Override
    public void visit(BLangPackageVarRef packageVarRef) {
        result = packageVarRef;
    }

    @Override
    public void visit(BLangFunctionVarRef functionVarRef) {
        result = functionVarRef;
    }

    @Override
    public void visit(BLangStructFieldAccessExpr fieldAccessExpr) {
        result = fieldAccessExpr;
    }

    @Override
    public void visit(BLangStructFunctionVarRef functionVarRef) {
        result = functionVarRef;
    }

    @Override
    public void visit(BLangMapAccessExpr mapKeyAccessExpr) {
        result = mapKeyAccessExpr;
    }

    @Override
    public void visit(BLangArrayAccessExpr arrayIndexAccessExpr) {
        result = arrayIndexAccessExpr;
    }

    @Override
    public void visit(BLangTupleAccessExpr arrayIndexAccessExpr) {
        result = arrayIndexAccessExpr;
    }

    @Override
    public void visit(BLangTableAccessExpr tableKeyAccessExpr) {
        result = tableKeyAccessExpr;
    }

    @Override
    public void visit(BLangMapLiteral mapLiteral) {
        result = mapLiteral;
    }

    @Override
    public void visit(BLangStructLiteral structLiteral) {
        result = structLiteral;
    }

    @Override
    public void visit(BLangWaitForAllExpr.BLangWaitLiteral waitLiteral) {
        result = waitLiteral;
    }

    @Override
    public void visit(BLangXMLElementAccess xmlElementAccess) {
        
        
        xmlElementAccess.expr = rewriteExpr(xmlElementAccess.expr);

        ArrayList<BLangExpression> filters = expandFilters(xmlElementAccess.filters);

        BLangInvocation invocationNode = createLanglibXMLInvocation(xmlElementAccess.pos, XML_INTERNAL_GET_ELEMENTS,
                xmlElementAccess.expr, new ArrayList<>(), filters);
        result = rewriteExpr(invocationNode);
    }

    private ArrayList<BLangExpression> expandFilters(List<BLangXMLElementFilter> filters) {
        ArrayList<BLangExpression> args = new ArrayList<>();
        for (BLangXMLElementFilter filter : filters) {
            BSymbol nsSymbol = filter.namespaceSymbol;
            String filterName = filter.name;
            if (nsSymbol != null &&
                    !(filter.namespace.equals(XMLConstants.DEFAULT_NS_PREFIX) && filterName.equals("*"))) {
                String expandedName = createExpandedQName(((BXMLNSSymbol) nsSymbol).namespaceURI, filterName);
                args.add(createStringLiteral(filter.elemNamePos, expandedName));
            } else {
                args.add(createStringLiteral(filter.elemNamePos, filterName));
            }
        }
        return args;
    }

    private BLangInvocation createLanglibXMLInvocation(Location pos, String functionName,
                                                       BLangExpression invokeOnExpr,
                                                       ArrayList<BLangExpression> args,
                                                       ArrayList<BLangExpression> restArgs) {
        invokeOnExpr = rewriteExpr(invokeOnExpr);

        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        invocationNode.pos = pos;
        BLangIdentifier name = (BLangIdentifier) TreeBuilder.createIdentifierNode();
        name.setLiteral(false);
        name.setValue(functionName);
        name.pos = pos;
        invocationNode.name = name;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        invocationNode.expr = invokeOnExpr;

        invocationNode.symbol = symResolver.lookupLangLibMethod(symTable.xmlType, Names.fromString(functionName), env);

        ArrayList<BLangExpression> requiredArgs = new ArrayList<>();
        requiredArgs.add(invokeOnExpr);
        requiredArgs.addAll(args);
        invocationNode.requiredArgs = requiredArgs;
        invocationNode.restArgs = rewriteExprs(restArgs);

        invocationNode.setBType(invocationNode.symbol.type.getReturnType());
        invocationNode.langLibInvocation = true;
        return invocationNode;
    }

    @Override
    public void visit(BLangXMLNavigationAccess xmlNavigation) {
        xmlNavigation.expr = rewriteExpr(xmlNavigation.expr);
        xmlNavigation.childIndex = rewriteExpr(xmlNavigation.childIndex);

        ArrayList<BLangExpression> filters = expandFilters(xmlNavigation.filters);

        
        if (xmlNavigation.navAccessType == XMLNavigationAccess.NavAccessType.DESCENDANTS) {
            BLangInvocation invocationNode = createLanglibXMLInvocation(xmlNavigation.pos,
                    XML_INTERNAL_SELECT_DESCENDANTS, xmlNavigation.expr, new ArrayList<>(), filters);
            result = rewriteExpr(invocationNode);
        } else if (xmlNavigation.navAccessType == XMLNavigationAccess.NavAccessType.CHILDREN) {
            
            BLangInvocation invocationNode = createLanglibXMLInvocation(xmlNavigation.pos, XML_INTERNAL_CHILDREN,
                    xmlNavigation.expr, new ArrayList<>(), new ArrayList<>());
            result = rewriteExpr(invocationNode);
        } else {
            BLangExpression childIndexExpr;
            
            if (xmlNavigation.childIndex == null) {
                childIndexExpr = new BLangLiteral(Long.valueOf(-1), symTable.intType);
            } else {
                
                childIndexExpr = xmlNavigation.childIndex;
            }
            ArrayList<BLangExpression> args = new ArrayList<>();
            args.add(rewriteExpr(childIndexExpr));

            BLangInvocation invocationNode = createLanglibXMLInvocation(xmlNavigation.pos,
                    XML_INTERNAL_GET_FILTERED_CHILDREN_FLAT, xmlNavigation.expr, args, filters);
            result = rewriteExpr(invocationNode);
        }
    }

    @Override
    public void visit(BLangIsAssignableExpr assignableExpr) {
        assignableExpr.lhsExpr = rewriteExpr(assignableExpr.lhsExpr);
        result = assignableExpr;
    }

    @Override
    public void visit(BLangTypedescExpr typedescExpr) {
        typedescExpr.typeNode = rewrite(typedescExpr.typeNode, env);
        result = typedescExpr;
    }

    @Override
    public void visit(BLangRestArgsExpression bLangVarArgsExpression) {
        result = rewriteExpr(bLangVarArgsExpression.expr);
    }

    @Override
    public void visit(BLangNamedArgsExpression bLangNamedArgsExpression) {
        bLangNamedArgsExpression.expr = rewriteExpr(bLangNamedArgsExpression.expr);
        result = bLangNamedArgsExpression.expr;
    }

    @Override
    public void visit(BLangCheckedExpr checkedExpr) {
        visitCheckAndCheckPanicExpr(checkedExpr, false);
    }

    @Override
    public void visit(BLangCheckPanickedExpr checkedExpr) {
        visitCheckAndCheckPanicExpr(checkedExpr, true);
    }

    private void visitCheckAndCheckPanicExpr(BLangCheckedExpr checkedExpr, boolean isCheckPanic) {
        
        if (checkedExpr.isRedundantChecking) {
            result = rewriteExpr(checkedExpr.expr);
            return;
        }

        
        
        
        
        
        
        
        
        
        
        
        

        Location pos = checkedExpr.pos;
        
        String resultVarName = "_$result$_";
        BType resultType = checkedExpr.getBType();
        BLangSimpleVariable resultVar =
                ASTBuilderUtil.createVariable(pos, resultVarName, resultType, null,
                                              new BVarSymbol(0, Names.fromString(resultVarName),
                                                             this.env.scope.owner.pkgID, resultType,
                                                             this.env.scope.owner, pos, VIRTUAL));
        BLangSimpleVariableDef resultVarDef = ASTBuilderUtil.createVariableDef(pos, resultVar);
        resultVarDef.desugared = true;
        BLangSimpleVarRef resultVarRef = ASTBuilderUtil.createVariableRef(pos, resultVar.symbol);

        String checkedExprVarName = GEN_VAR_PREFIX.value;
        BType checkedExprType = checkedExpr.expr.getBType();
        BLangSimpleVariable checkedExprVar =
                ASTBuilderUtil.createVariable(pos, checkedExprVarName, checkedExprType,
                        checkedExpr.expr, new BVarSymbol(0, Names.fromString(checkedExprVarName),
                                this.env.scope.owner.pkgID, checkedExprType,
                                this.env.scope.owner, pos, VIRTUAL));
        BLangSimpleVariableDef checkedExprVarDef = ASTBuilderUtil.createVariableDef(pos, checkedExprVar);
        BLangSimpleVarRef checkedExprVarRef = ASTBuilderUtil.createVariableRef(pos, checkedExprVar.symbol);

        BLangAssignment successAssignment = ASTBuilderUtil.createAssignmentStmt(pos, resultVarRef,
                createTypeCastExpr(checkedExprVarRef, resultType));
        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(pos);
        ifBody.addStatement(successAssignment);

        BLangBlockStmt elseBody = getSafeErrorAssignment(pos, checkedExprVarRef, this.env.enclInvokable.symbol,
                        checkedExpr.equivalentErrorTypeList, isCheckPanic);

        BLangValueType checkedExprTypeNode = (BLangValueType) TreeBuilder.createValueTypeNode();
        checkedExprTypeNode.setBType(resultType);
        checkedExprTypeNode.typeKind = resultType.getKind();
        BLangIf ifStmt = ASTBuilderUtil.createIfElseStmt(pos,
                createTypeCheckExpr(pos, checkedExprVarRef, checkedExprTypeNode), ifBody, elseBody);
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(pos, new ArrayList<>() {{
            add(resultVarDef);
            add(checkedExprVarDef);
            add(ifStmt);
        }});

        BLangStatementExpression stmtExpr = ASTBuilderUtil.createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(resultType);
        result = rewriteExpr(stmtExpr);
    }

    @Override
    public void visit(BLangServiceConstructorExpr serviceConstructorExpr) {
        final BLangTypeInit typeInit = ASTBuilderUtil.createEmptyTypeInit(serviceConstructorExpr.pos,
                serviceConstructorExpr.serviceNode.serviceClass.symbol.type);
        serviceConstructorExpr.serviceNode.annAttachments.forEach(attachment ->  rewrite(attachment, env));
        result = rewriteExpr(typeInit);
    }

    @Override
    public void visit(BLangObjectConstructorExpression bLangObjectConstructorExpression) {
        visit(bLangObjectConstructorExpression.classNode);
        bLangObjectConstructorExpression.classNode.annAttachments.forEach(attachment -> rewrite(attachment, env));
        result = rewriteExpr(bLangObjectConstructorExpression.typeInit);
    }

    @Override
    public void visit(BLangAnnotAccessExpr annotAccessExpr) {

        BLangBinaryExpr binaryExpr = (BLangBinaryExpr) TreeBuilder.createBinaryExpressionNode();
        binaryExpr.pos = annotAccessExpr.pos;
        binaryExpr.opKind = OperatorKind.ANNOT_ACCESS;
        binaryExpr.lhsExpr = annotAccessExpr.expr;
        binaryExpr.rhsExpr = ASTBuilderUtil.createLiteral(annotAccessExpr.pkgAlias.pos, symTable.stringType,
                annotAccessExpr.annotationSymbol.bvmAlias());
        binaryExpr.setBType(annotAccessExpr.getBType());
        binaryExpr.opSymbol = new BOperatorSymbol(Names.fromString(OperatorKind.ANNOT_ACCESS.value()), null,
                new BInvokableType(Lists.of(binaryExpr.lhsExpr.getBType(),
                        binaryExpr.rhsExpr.getBType()),
                        annotAccessExpr.getBType(), null), null,
                symTable.builtinPos, VIRTUAL);
        result = rewriteExpr(binaryExpr);
    }

    @Override
    public void visit(BLangTypeTestExpr typeTestExpr) {
        BLangExpression expr = typeTestExpr.expr;
        if (types.isValueType(expr.getBType())) {
            expr = types.addConversionExprIfRequired(expr, symTable.anyType);
        }
        if (typeTestExpr.isNegation) {
            BLangTypeTestExpr bLangTypeTestExpr = ASTBuilderUtil.createTypeTestExpr(typeTestExpr.pos,
                    typeTestExpr.expr, typeTestExpr.typeNode);
            BLangGroupExpr bLangGroupExpr = (BLangGroupExpr) TreeBuilder.createGroupExpressionNode();
            bLangGroupExpr.expression = bLangTypeTestExpr;
            bLangGroupExpr.setBType(typeTestExpr.getBType());
            BLangUnaryExpr unaryExpr = ASTBuilderUtil.createUnaryExpr(typeTestExpr.pos, bLangGroupExpr,
                    typeTestExpr.getBType(),
                    OperatorKind.NOT, null);
            result = rewriteExpr(unaryExpr);
            return;
        }
        typeTestExpr.expr = rewriteExpr(expr);
        typeTestExpr.typeNode = rewrite(typeTestExpr.typeNode, env);
        result = typeTestExpr;
    }

    @Override
    public void visit(BLangIsLikeExpr isLikeExpr) {
        isLikeExpr.expr = rewriteExpr(isLikeExpr.expr);
        result = isLikeExpr;
    }

    @Override
    public void visit(BLangStatementExpression bLangStatementExpression) {
        bLangStatementExpression.expr = rewriteExpr(bLangStatementExpression.expr);
        bLangStatementExpression.stmt = rewrite(bLangStatementExpression.stmt, env);
        result = bLangStatementExpression;
    }

    @Override
    public void visit(BLangQueryExpr queryExpr) {
        boolean prevIsVisitingQuery = this.isVisitingQuery;
        boolean prevDesugarToReturn = this.desugarToReturn;
        this.isVisitingQuery = true;
        this.desugarToReturn = true;
        BLangStatementExpression stmtExpr = queryDesugar.desugar(queryExpr, env, getVisibleXMLNSStmts(env));
        result = rewrite(stmtExpr, env);
        this.isVisitingQuery = prevIsVisitingQuery;
        this.desugarToReturn = prevDesugarToReturn;
    }

    List<BLangStatement> getVisibleXMLNSStmts(SymbolEnv env) {
        Map<Name, BXMLNSSymbol> nameBXMLNSSymbolMap = symResolver.resolveAllNamespaces(env);
        return nameBXMLNSSymbolMap.keySet().stream()
                .map(key -> this.stmtsToBePropagatedToQuery.get(key))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    @Override
    public void visit(BLangQueryAction queryAction) {
        boolean prevIsVisitingQuery = this.isVisitingQuery;
        boolean prevDesugarToReturn = this.desugarToReturn;
        this.desugarToReturn = true;
        this.isVisitingQuery = true;
        BLangStatementExpression stmtExpr = queryDesugar.desugar(queryAction, env, getVisibleXMLNSStmts(env));
        result = rewrite(stmtExpr, env);
        this.isVisitingQuery = prevIsVisitingQuery;
        this.desugarToReturn = prevDesugarToReturn;
    }

    @Override
    public void visit(BLangJSONArrayLiteral jsonArrayLiteral) {
        jsonArrayLiteral.exprs = rewriteExprs(jsonArrayLiteral.exprs);
        result = jsonArrayLiteral;
    }

    @Override
    public void visit(BLangConstant constant) {

        BConstantSymbol constSymbol = constant.symbol;
        BType refType = Types.getImpliedType(constSymbol.literalType);
        if (refType.tag <= TypeTags.BOOLEAN || refType.tag == TypeTags.NIL) {
            if (refType.tag != TypeTags.NIL && (constSymbol.value == null ||
                            constSymbol.value.value == null)) {
                throw new IllegalStateException();
            }
            BLangLiteral literal = ASTBuilderUtil.createLiteral(constant.expr.pos, constSymbol.literalType,
                    constSymbol.value.value);
            constant.expr = rewriteExpr(literal);
        } else {
            constant.expr = rewriteExpr(constant.expr);
        }
        constant.annAttachments.forEach(attachment ->  rewrite(attachment, env));
        result = constant;
    }

    @Override
    public void visit(BLangIgnoreExpr ignoreExpr) {
        result = ignoreExpr;
    }

    @Override
    public void visit(BLangDynamicArgExpr dynamicParamExpr) {
        dynamicParamExpr.conditionalArgument = rewriteExpr(dynamicParamExpr.conditionalArgument);
        dynamicParamExpr.condition = rewriteExpr(dynamicParamExpr.condition);
        result = dynamicParamExpr;
    }

    @Override
    public void visit(BLangConstRef constantRef) {
        result = ASTBuilderUtil.createLiteral(constantRef.pos, constantRef.getBType(), constantRef.value);
    }

    @Override
    public void visit(BLangRegExpTemplateLiteral regExpTemplateLiteral) {
        regExpTemplateLiteral.reDisjunction = rewriteExpr(regExpTemplateLiteral.reDisjunction);
        result = regExpTemplateLiteral;
    }

    @Override
    public void visit(BLangReDisjunction reDisjunction) {
        reDisjunction.sequenceList.forEach(this::rewriteExpr);
        result = reDisjunction;
    }

    @Override
    public void visit(BLangReSequence reSequence) {
        reSequence.termList.forEach(this::rewriteExpr);
        result = reSequence;
    }

    @Override
    public void visit(BLangReAssertion reAssertion) {
        reAssertion.assertion = rewriteExpr(reAssertion.assertion);
        result = reAssertion;
    }

    @Override
    public void visit(BLangReAtomQuantifier reAtomQuantifier) {
        BLangExpression reAtom = reAtomQuantifier.atom;
        if (symResolver.isReAtomNode(reAtom.getKind())) {
            reAtomQuantifier.atom = rewriteExpr(reAtom);
        } else {
            
            reAtomQuantifier.atom = rewriteExpr(getToStringInvocationOnExpr(reAtom));
        }

        
        if (reAtomQuantifier.quantifier == null) {
            reAtomQuantifier.quantifier = ASTBuilderUtil.createEmptyQuantifier(reAtomQuantifier.pos,
                    symTable.anydataType, symTable.stringType);
        }
        reAtomQuantifier.quantifier = rewriteExpr(reAtomQuantifier.quantifier);
        result = reAtomQuantifier;
    }

    @Override
    public void visit(BLangReAtomCharOrEscape reAtomCharOrEscape) {
        reAtomCharOrEscape.charOrEscape = rewriteExpr(reAtomCharOrEscape.charOrEscape);
        result = reAtomCharOrEscape;
    }

    @Override
    public void visit(BLangReQuantifier reQuantifier) {
        reQuantifier.quantifier = rewriteExpr(reQuantifier.quantifier);
        
        if (reQuantifier.nonGreedyChar == null) {
            reQuantifier.nonGreedyChar = ASTBuilderUtil.createLiteral(reQuantifier.pos,
                    symTable.stringType, "");
        }
        reQuantifier.nonGreedyChar = rewriteExpr(reQuantifier.nonGreedyChar);
        result = reQuantifier;
    }

    @Override
    public void visit(BLangReCharacterClass reCharacterClass) {
        reCharacterClass.characterClassStart = rewriteExpr(reCharacterClass.characterClassStart);
        
        if (reCharacterClass.negation == null) {
            reCharacterClass.negation = ASTBuilderUtil.createLiteral(reCharacterClass.pos,
                    symTable.stringType, "");
        }
        reCharacterClass.negation = rewriteExpr(reCharacterClass.negation);
        
        if (reCharacterClass.charSet == null) {
            reCharacterClass.charSet = ASTBuilderUtil.createEmptyCharSet(symTable.anydataType);
        }
        reCharacterClass.charSet = rewriteExpr(reCharacterClass.charSet);
        reCharacterClass.characterClassEnd = rewriteExpr(reCharacterClass.characterClassEnd);
        result = reCharacterClass;
    }

    @Override
    public void visit(BLangReCharSet reCharSet) {
        reCharSet.charSetAtoms.forEach(this::rewriteExpr);
        result = reCharSet;
    }

    @Override
    public void visit(BLangReCharSetRange reCharSetRange) {
        reCharSetRange.lhsCharSetAtom = rewriteExpr(reCharSetRange.lhsCharSetAtom);
        reCharSetRange.dash = rewriteExpr(reCharSetRange.dash);
        reCharSetRange.rhsCharSetAtom = rewriteExpr(reCharSetRange.rhsCharSetAtom);
        result = reCharSetRange;
    }

    @Override
    public void visit(BLangReCapturingGroups reCapturingGroups) {
        reCapturingGroups.openParen = rewriteExpr(reCapturingGroups.openParen);
        
        if (reCapturingGroups.flagExpr == null) {
            reCapturingGroups.flagExpr = ASTBuilderUtil.createEmptyFlagExpression(reCapturingGroups.pos,
                    symTable.anydataType, symTable.stringType);
        }
        reCapturingGroups.flagExpr = rewriteExpr(reCapturingGroups.flagExpr);
        reCapturingGroups.disjunction = rewriteExpr(reCapturingGroups.disjunction);
        reCapturingGroups.closeParen = rewriteExpr(reCapturingGroups.closeParen);
        result = reCapturingGroups;
    }

    @Override
    public void visit(BLangReFlagExpression reFlagExpression) {
        reFlagExpression.questionMark = rewriteExpr(reFlagExpression.questionMark);
        
        if (reFlagExpression.flagsOnOff == null) {
            reFlagExpression.flagsOnOff = ASTBuilderUtil.createEmptyFlagOnOff(reFlagExpression.pos,
                    symTable.anydataType, symTable.stringType);
        }
        reFlagExpression.flagsOnOff = rewriteExpr(reFlagExpression.flagsOnOff);
        reFlagExpression.colon = rewriteExpr(reFlagExpression.colon);
        result = reFlagExpression;
    }

    @Override
    public void visit(BLangReFlagsOnOff reFlagsOnOff) {
        reFlagsOnOff.flags = rewriteExpr(reFlagsOnOff.flags);
        result = reFlagsOnOff;
    }

    

    
    BLangSimpleVariableDef getIteratorVariableDefinition(Location pos, BVarSymbol collectionSymbol,
                                                         BInvokableSymbol iteratorInvokableSymbol,
                                                         boolean isIteratorFuncFromLangLib) {


        BLangSimpleVarRef dataReference = ASTBuilderUtil.createVariableRef(pos, collectionSymbol);
        BLangInvocation iteratorInvocation = (BLangInvocation) TreeBuilder.createInvocationNode();
        iteratorInvocation.pos = pos;
        iteratorInvocation.expr = dataReference;
        iteratorInvocation.symbol = iteratorInvokableSymbol;
        iteratorInvocation.setBType(iteratorInvokableSymbol.retType);
        iteratorInvocation.argExprs = Lists.of(dataReference);
        iteratorInvocation.requiredArgs = iteratorInvocation.argExprs;
        iteratorInvocation.langLibInvocation = isIteratorFuncFromLangLib;
        BVarSymbol iteratorSymbol = new BVarSymbol(0, Names.fromString("$iterator$"), this.env.scope.owner.pkgID,
                                                   iteratorInvokableSymbol.retType, this.env.scope.owner, pos, VIRTUAL);

        
        BLangSimpleVariable iteratorVariable = ASTBuilderUtil.createVariable(pos, "$iterator$",
                iteratorInvokableSymbol.retType, iteratorInvocation, iteratorSymbol);
        return ASTBuilderUtil.createVariableDef(pos, iteratorVariable);
    }

    
    BLangSimpleVariableDef getIteratorNextVariableDefinition(Location pos, BType nillableResultType,
                                                             BVarSymbol iteratorSymbol,
                                                             BVarSymbol resultSymbol) {
        BLangInvocation nextInvocation = createIteratorNextInvocation(pos, iteratorSymbol);
        BLangSimpleVariable resultVariable = ASTBuilderUtil.createVariable(pos, "$result$",
                nillableResultType, nextInvocation, resultSymbol);
        return ASTBuilderUtil.createVariableDef(pos, resultVariable);
    }

    BLangInvocation createIteratorNextInvocation(Location pos, BVarSymbol iteratorSymbol) {
        BLangIdentifier nextIdentifier = ASTBuilderUtil.createIdentifier(pos, "next");
        BLangSimpleVarRef iteratorReferenceInNext = ASTBuilderUtil.createVariableRef(pos, iteratorSymbol);
        BInvokableSymbol nextFuncSymbol =
                getNextFunc((BObjectType) Types.getImpliedType(iteratorSymbol.type)).symbol;
        BLangInvocation nextInvocation = (BLangInvocation) TreeBuilder.createInvocationNode();
        nextInvocation.pos = pos;
        nextInvocation.name = nextIdentifier;
        nextInvocation.expr = iteratorReferenceInNext;
        nextInvocation.requiredArgs = Lists.of(ASTBuilderUtil.createVariableRef(pos, iteratorSymbol));
        nextInvocation.argExprs = nextInvocation.requiredArgs;
        nextInvocation.symbol = nextFuncSymbol;
        nextInvocation.setBType(nextFuncSymbol.retType);
        return nextInvocation;
    }

    private BAttachedFunction getNextFunc(BObjectType iteratorType) {
        BObjectTypeSymbol iteratorSymbol = (BObjectTypeSymbol) iteratorType.tsymbol;
        for (BAttachedFunction bAttachedFunction : iteratorSymbol.attachedFuncs) {
            if (bAttachedFunction.funcName.value.equals("next")) {
                return bAttachedFunction;
            }
        }
        return null;
    }

    
    BLangFieldBasedAccess getValueAccessExpression(Location location, BType varType,
                                                   BVarSymbol resultSymbol) {
        return getFieldAccessExpression(location, "value", varType, resultSymbol);
    }

    BLangFieldBasedAccess getFieldAccessExpression(Location pos, String fieldName, BType varType,
                                                   BVarSymbol resultSymbol) {
        BLangSimpleVarRef resultReferenceInVariableDef = ASTBuilderUtil.createVariableRef(pos, resultSymbol);
        BLangIdentifier valueIdentifier = ASTBuilderUtil.createIdentifier(pos, fieldName);

        BLangFieldBasedAccess fieldBasedAccessExpression =
                ASTBuilderUtil.createFieldAccessExpr(resultReferenceInVariableDef, valueIdentifier);
        fieldBasedAccessExpression.pos = pos;
        fieldBasedAccessExpression.setBType(varType);
        fieldBasedAccessExpression.originalType = fieldBasedAccessExpression.getBType();
        return fieldBasedAccessExpression;
    }

    private BlockFunctionBodyNode populateArrowExprBodyBlock(BLangArrowFunction bLangArrowFunction) {
        BlockFunctionBodyNode blockNode = TreeBuilder.createBlockFunctionBodyNode();
        BLangReturn returnNode = (BLangReturn) TreeBuilder.createReturnNode();
        returnNode.pos = bLangArrowFunction.body.expr.pos;
        returnNode.setExpression(bLangArrowFunction.body.expr);
        blockNode.addStatement(returnNode);
        return blockNode;
    }

    protected BLangInvocation createInvocationNode(String functionName, List<BLangExpression> args, BType retType) {
        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        BLangIdentifier name = (BLangIdentifier) TreeBuilder.createIdentifierNode();
        name.setLiteral(false);
        name.setValue(functionName);
        invocationNode.name = name;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        
        invocationNode.symbol = symTable.rootScope.lookup(new Name(functionName)).symbol;
        invocationNode.setBType(retType);
        invocationNode.requiredArgs = args;
        return invocationNode;
    }

    protected BLangInvocation createLangLibInvocationNode(String functionName,
                                                        BLangExpression onExpr,
                                                        List<BLangExpression> args,
                                                        BType retType,
                                                        Location pos) {
        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        invocationNode.pos = pos;
        BLangIdentifier name = (BLangIdentifier) TreeBuilder.createIdentifierNode();
        name.setLiteral(false);
        name.setValue(functionName);
        name.pos = pos;
        invocationNode.name = name;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        invocationNode.expr = onExpr;
        invocationNode.symbol = symResolver.lookupLangLibMethod(onExpr.getBType(), Names.fromString(functionName), env);

        ArrayList<BLangExpression> requiredArgs = new ArrayList<>();
        requiredArgs.add(onExpr);
        requiredArgs.addAll(args);
        invocationNode.requiredArgs = requiredArgs;

        invocationNode.setBType(retType != null ? retType : ((BInvokableSymbol) invocationNode.symbol).retType);
        invocationNode.langLibInvocation = true;
        return invocationNode;
    }

    private BLangInvocation createLangLibInvocationNode(String functionName,
                                                        List<BLangExpression> requiredArgs,
                                                        BType retType,
                                                        Location pos) {
        return createLangLibInvocationNode(functionName, requiredArgs, new ArrayList<>(), retType, pos);
    }

    private BLangInvocation createLangLibInvocationNode(String functionName,
                                                        List<BLangExpression> requiredArgs,
                                                        List<BLangExpression> restArgs,
                                                        BType retType,
                                                        Location pos) {
        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        invocationNode.pos = pos;
        BLangIdentifier name = (BLangIdentifier) TreeBuilder.createIdentifierNode();
        name.setLiteral(false);
        name.setValue(functionName);
        name.pos = pos;
        invocationNode.name = name;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        invocationNode.symbol = symResolver.lookupMethodInModule(symTable.langInternalModuleSymbol,
                Names.fromString(functionName), env);

        invocationNode.requiredArgs = new ArrayList<>(requiredArgs);
        invocationNode.restArgs = new ArrayList<>(restArgs);

        invocationNode.setBType(retType != null ? retType : ((BInvokableSymbol) invocationNode.symbol).retType);
        invocationNode.langLibInvocation = true;
        return invocationNode;
    }

    private BLangArrayLiteral createArrayLiteralExprNode() {
        BLangArrayLiteral expr = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode();
        expr.exprs = new ArrayList<>();
        expr.setBType(new BArrayType(symTable.anyType));
        return expr;
    }

    private BFunctionPointerInvocation visitFunctionPointerInvocation(BLangInvocation iExpr) {
        BLangExpression rewritten = rewriteExpr(getFunctionPointerExpr(iExpr));
        return new BFunctionPointerInvocation(iExpr, rewritten);
    }

    protected BLangValueExpression getFunctionPointerExpr(BLangInvocation iExpr) {
        BLangValueExpression expr;
        if (iExpr.expr == null) {
            BLangSimpleVarRef varRef = new BLangSimpleVarRef();
            varRef.variableName = iExpr.name;
            expr = varRef;
        } else {
            BLangFieldBasedAccess fieldBasedAccess = new BLangFieldBasedAccess();
            fieldBasedAccess.expr = iExpr.expr;
            fieldBasedAccess.field = iExpr.name;
            expr = fieldBasedAccess;
        }
        expr.symbol = iExpr.symbol;
        expr.setBType(iExpr.symbol.type);
        return expr;
    }

    private BLangExpression visitCloneInvocation(BLangExpression expr, BType lhsType) {
        if (types.isValueType(expr.getBType())) {
            return expr;
        }
        if (Types.getImpliedType(expr.getBType()).tag == TypeTags.ERROR) {
            return expr;
        }
        BLangInvocation cloneInvok = createLangLibInvocationNode("clone", expr, new ArrayList<>(), null, expr.pos);
        return types.addConversionExprIfRequired(cloneInvok, lhsType);
    }

    private BLangExpression visitCloneReadonly(BLangExpression expr, BType lhsType) {
        if (types.isValueType(expr.getBType())) {
            return expr;
        }
        if (Types.getImpliedType(expr.getBType()).tag == TypeTags.ERROR) {
            return expr;
        }
        BLangInvocation cloneInvok = createLangLibInvocationNode("cloneReadOnly", expr, new ArrayList<>(),
                                                                 expr.getBType(),
                                                                 expr.pos);
        return types.addConversionExprIfRequired(cloneInvok, lhsType);
    }

    @SuppressWarnings("unchecked")
    <E extends BLangNode> E rewrite(E node, SymbolEnv env) {
        if (node == null) {
            return null;
        }

        if (node.desugared) {
            return node;
        }

        SymbolEnv previousEnv = this.env;
        this.env = env;

        node.accept(this);
        BLangNode resultNode = this.result;
        this.result = null;
        resultNode.desugared = true;

        this.env = previousEnv;
        return (E) resultNode;
    }

    @SuppressWarnings("unchecked")
    <E extends BLangExpression> E rewriteExpr(E node) {
        if (node == null) {
            return null;
        }

        if (node.desugared) {
            return node;
        }

        BLangExpression expr = node;
        if (node.impConversionExpr != null) {
            expr = node.impConversionExpr;
            node.impConversionExpr = null;
        }

        expr.accept(this);
        BLangNode resultNode = this.result;
        this.result = null;
        resultNode.desugared = true;

        return (E) resultNode;
    }

    @SuppressWarnings("unchecked")
    <E extends BLangStatement> E rewrite(E statement, SymbolEnv env) {
        if (statement == null) {
            return null;
        }
        BLangStatement stmt = (BLangStatement) rewrite((BLangNode) statement, env);
        return (E) stmt;
    }

    private <E extends BLangStatement> List<E> rewriteStmt(List<E> nodeList, SymbolEnv env) {
        for (int i = 0; i < nodeList.size(); i++) {
            nodeList.set(i, rewrite(nodeList.get(i), env));
        }
        return nodeList;
    }

    private <E extends BLangNode> List<E> rewrite(List<E> nodeList, SymbolEnv env) {
        for (int i = 0; i < nodeList.size(); i++) {
            nodeList.set(i, rewrite(nodeList.get(i), env));
        }
        return nodeList;
    }

    private <E extends BLangExpression> List<E> rewriteExprs(List<E> nodeList) {
        for (int i = 0; i < nodeList.size(); i++) {
            nodeList.set(i, rewriteExpr(nodeList.get(i)));
        }
        return nodeList;
    }

    private BLangLiteral createStringLiteral(Location pos, String value) {
        BLangLiteral stringLit = new BLangLiteral(value, symTable.stringType);
        stringLit.pos = pos;
        return stringLit;
    }

    private BLangLiteral createIntLiteral(long value) {
        BLangLiteral literal = (BLangLiteral) TreeBuilder.createLiteralExpression();
        literal.value = value;
        literal.setBType(symTable.intType);
        return literal;
    }

    private BLangLiteral createByteLiteral(Location pos, Byte value) {
        BLangLiteral byteLiteral = new BLangLiteral(Byte.toUnsignedInt(value), symTable.byteType);
        byteLiteral.pos = pos;
        return byteLiteral;
    }

    private BLangExpression createTypeCastExpr(BLangExpression expr, BType targetType) {
        if (types.isSameType(expr.getBType(), targetType)) {
            return expr;
        }

        BLangTypeConversionExpr conversionExpr = (BLangTypeConversionExpr) TreeBuilder.createTypeConversionNode();
        conversionExpr.pos = expr.pos;
        conversionExpr.expr = expr;
        conversionExpr.setBType(targetType);
        conversionExpr.targetType = targetType;
        conversionExpr.internal = true;
        return conversionExpr;
    }

    private BType getElementType(BType bType) {
        BType type = Types.getImpliedType(bType);
        if (type.tag != TypeTags.ARRAY) {
            return bType;
        }

        return getElementType(((BArrayType) type).getElementType());
    }

    
    private void addReturnIfNotPresent(BLangInvokableNode invokableNode) {
        if (Symbols.isNative(invokableNode.symbol) ||
                (invokableNode.hasBody() && invokableNode.body.getKind() != NodeKind.BLOCK_FUNCTION_BODY)) {
            return;
        }
        
        
        BLangBlockFunctionBody funcBody = (BLangBlockFunctionBody) invokableNode.body;
        if (invokableNode.symbol.type.getReturnType().isNullable() && (funcBody.stmts.isEmpty()
                || funcBody.stmts.get(funcBody.stmts.size() - 1).getKind() != NodeKind.RETURN)) {
            Location invPos = invokableNode.pos;
            Location returnStmtPos;
            if (invPos != null && !invokableNode.name.value.contains(GENERATED_INIT_SUFFIX.value)) {
                returnStmtPos = new BLangDiagnosticLocation(invPos.lineRange().fileName(),
                        invPos.lineRange().endLine().line(),
                        invPos.lineRange().endLine().line(),
                        invPos.lineRange().startLine().offset(),
                        invPos.lineRange().startLine().offset(), 0, 0);
            } else {
                returnStmtPos = null;
            }
            BLangReturn returnStmt = ASTBuilderUtil.createNilReturnStmt(returnStmtPos, symTable.nilType);
            funcBody.addStatement(returnStmt);
        }
    }

    /**
     * Reorder the invocation arguments to match the original function signature.
     *
     * @param iExpr Function invocation expressions to reorder the arguments
     */
    private void reorderArguments(BLangInvocation iExpr) {
        BSymbol symbol = iExpr.symbol;

        if (symbol == null || Types.getImpliedType(symbol.type).tag != TypeTags.INVOKABLE) {
            return;
        }

        BInvokableSymbol invokableSymbol = (BInvokableSymbol) symbol;

        List<BLangExpression> restArgs = iExpr.restArgs;
        int originalRequiredArgCount = iExpr.requiredArgs.size();

        
        BLangSimpleVarRef varargRef = null;
        BLangBlockStmt blockStmt = null;
        BType varargVarType = null;

        int restArgCount = restArgs.size();

        if (restArgCount > 0 &&
                restArgs.get(restArgCount - 1).getKind() == NodeKind.REST_ARGS_EXPR &&
                originalRequiredArgCount < invokableSymbol.params.size()) {
            
            
            
            BLangExpression expr = ((BLangRestArgsExpression) restArgs.get(restArgCount - 1)).expr;
            Location varargExpPos = expr.pos;
            varargVarType = expr.getBType();
            String varargVarName = DESUGARED_VARARG_KEY + UNDERSCORE + this.varargCount++;

            BVarSymbol varargVarSymbol = new BVarSymbol(0, Names.fromString(varargVarName), this.env.scope.owner.pkgID,
                                                        varargVarType, this.env.scope.owner, varargExpPos, VIRTUAL);
            varargRef = ASTBuilderUtil.createVariableRef(varargExpPos, varargVarSymbol);

            BLangSimpleVariable var = createVariable(varargExpPos, varargVarName, varargVarType, expr, varargVarSymbol);

            BLangSimpleVariableDef varDef = ASTBuilderUtil.createVariableDef(varargExpPos);
            varDef.var = var;
            varDef.setBType(varargVarType);

            blockStmt = createBlockStmt(varargExpPos);
            blockStmt.stmts.add(varDef);
        }

        if (!invokableSymbol.params.isEmpty()) {
            
            reorderNamedArgs(iExpr, invokableSymbol, varargRef);
        }

        
        if (restArgCount == 0 || restArgs.get(restArgCount - 1).getKind() != NodeKind.REST_ARGS_EXPR) {
            if (invokableSymbol.restParam == null) {
                return;
            }

            BLangArrayLiteral arrayLiteral = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode();
            List<BLangExpression> exprs = new ArrayList<>();

            BArrayType arrayType = (BArrayType) invokableSymbol.restParam.type;
            BType elemType = arrayType.eType;

            for (BLangExpression restArg : restArgs) {
                exprs.add(types.addConversionExprIfRequired(restArg, elemType));
            }

            arrayLiteral.exprs = exprs;
            arrayLiteral.setBType(arrayType);

            if (restArgCount != 0) {
                iExpr.restArgs = new ArrayList<>();
            }

            iExpr.restArgs.add(arrayLiteral);
            return;
        }

        
        if (restArgCount == 1 && restArgs.get(0).getKind() == NodeKind.REST_ARGS_EXPR) {

            
            
            if (iExpr.requiredArgs.size() == originalRequiredArgCount) {
                return;
            }

            
            
            
            
            BLangExpression firstNonRestArg = iExpr.requiredArgs.remove(0);
            BLangStatementExpression stmtExpression = createStatementExpression(blockStmt, firstNonRestArg);
            BType type = firstNonRestArg.impConversionExpr == null ?
                                              firstNonRestArg.getBType() : firstNonRestArg.impConversionExpr.targetType;
            stmtExpression.setBType(type);
            iExpr.requiredArgs.add(0, stmtExpression);

            
            if (invokableSymbol.restParam == null) {
                restArgs.remove(0);
                return;
            }

            
            
            
            
            BLangRestArgsExpression restArgsExpression = (BLangRestArgsExpression) restArgs.remove(0);
            BArrayType restParamType = (BArrayType) invokableSymbol.restParam.type;
            if (Types.getImpliedType(restArgsExpression.getBType()).tag == TypeTags.RECORD) {
                BLangExpression expr = ASTBuilderUtil.createEmptyArrayLiteral(invokableSymbol.pos, restParamType);
                restArgs.add(expr);
                return;
            }
            Location pos = restArgsExpression.pos;

            BLangArrayLiteral newArrayLiteral = createArrayLiteralExprNode();
            newArrayLiteral.setBType(restParamType);

            String name = DESUGARED_VARARG_KEY + UNDERSCORE + this.varargCount++;
            BVarSymbol varSymbol = new BVarSymbol(0, Names.fromString(name), this.env.scope.owner.pkgID,
                                                  restParamType, this.env.scope.owner, pos, VIRTUAL);
            BLangSimpleVarRef arrayVarRef = ASTBuilderUtil.createVariableRef(pos, varSymbol);

            BLangSimpleVariable var = createVariable(pos, name, restParamType, newArrayLiteral, varSymbol);
            BLangSimpleVariableDef varDef = ASTBuilderUtil.createVariableDef(pos);
            varDef.var = var;
            varDef.setBType(restParamType);

            BLangLiteral startIndex = createIntLiteral(invokableSymbol.params.size() - originalRequiredArgCount);
            BLangInvocation lengthInvocation = createLengthInvocation(pos, varargRef);
            BLangInvocation intRangeInvocation = replaceWithIntRange(pos, startIndex,
                                                                     getModifiedIntRangeEndExpr(lengthInvocation));

            BLangForeach foreach = (BLangForeach) TreeBuilder.createForeachNode();
            foreach.pos = pos;
            foreach.collection = intRangeInvocation;
            types.setForeachTypedBindingPatternType(foreach);

            final BLangSimpleVariable foreachVariable = ASTBuilderUtil.createVariable(pos, "$foreach$i",
                                                                                      foreach.varType);
            foreachVariable.symbol = new BVarSymbol(0, names.fromIdNode(foreachVariable.name),
                                                    this.env.scope.owner.pkgID, foreachVariable.getBType(),
                                                    this.env.scope.owner, pos, VIRTUAL);
            BLangSimpleVarRef foreachVarRef = ASTBuilderUtil.createVariableRef(pos, foreachVariable.symbol);
            foreach.variableDefinitionNode = ASTBuilderUtil.createVariableDef(pos, foreachVariable);
            foreach.isDeclaredWithVar = true;
            BLangBlockStmt foreachBody = ASTBuilderUtil.createBlockStmt(pos);

            BLangIndexBasedAccess valueExpr = ASTBuilderUtil.createIndexAccessExpr(varargRef, foreachVarRef);

            BType refType = Types.getImpliedType(varargVarType);
            if (refType.tag == TypeTags.ARRAY) {
                BArrayType arrayType = (BArrayType) refType;
                if (arrayType.state == BArrayState.CLOSED &&
                        arrayType.size == (iExpr.requiredArgs.size() - originalRequiredArgCount)) {
                    
                    
                    valueExpr.setBType(restParamType.eType);
                } else {
                    valueExpr.setBType(arrayType.eType);
                }
            } else {
                valueExpr.setBType(symTable.anyOrErrorType); 
            }

            BLangExpression pushExpr = types.addConversionExprIfRequired(valueExpr, restParamType.eType);
            BLangExpressionStmt expressionStmt = createExpressionStmt(pos, foreachBody);
            BLangInvocation pushInvocation = createLangLibInvocationNode(PUSH_LANGLIB_METHOD, arrayVarRef,
                                                                         List.of(pushExpr),
                                                                         restParamType, pos);
            pushInvocation.restArgs.add(pushInvocation.requiredArgs.remove(1));
            expressionStmt.expr = pushInvocation;
            foreach.body = foreachBody;
            BLangBlockStmt newArrayBlockStmt = createBlockStmt(pos);
            newArrayBlockStmt.addStatement(varDef);
            newArrayBlockStmt.addStatement(foreach);

            BLangStatementExpression newArrayStmtExpression = createStatementExpression(newArrayBlockStmt, arrayVarRef);
            newArrayStmtExpression.setBType(restParamType);

            restArgs.add(types.addConversionExprIfRequired(newArrayStmtExpression, restParamType));
            return;
        }

        
        
        BArrayType restParamType = (BArrayType) invokableSymbol.restParam.type;

        BLangArrayLiteral arrayLiteral = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode();
        arrayLiteral.setBType(restParamType);

        BType elemType = restParamType.eType;
        Location pos = restArgs.get(0).pos;

        List<BLangExpression> exprs = new ArrayList<>();

        for (int i = 0; i < restArgCount - 1; i++) {
            exprs.add(types.addConversionExprIfRequired(restArgs.get(i), elemType));
        }
        arrayLiteral.exprs = exprs;

        BLangRestArgsExpression pushRestArgsExpr = (BLangRestArgsExpression) TreeBuilder.createVarArgsNode();
        pushRestArgsExpr.pos = pos;
        pushRestArgsExpr.expr = restArgs.remove(restArgCount - 1);

        String name = DESUGARED_VARARG_KEY + UNDERSCORE + this.varargCount++;
        BVarSymbol varSymbol = new BVarSymbol(0, Names.fromString(name), this.env.scope.owner.pkgID, restParamType,
                                              this.env.scope.owner, pos, VIRTUAL);
        BLangSimpleVarRef arrayVarRef = ASTBuilderUtil.createVariableRef(pos, varSymbol);

        BLangSimpleVariable var = createVariable(pos, name, restParamType, arrayLiteral, varSymbol);
        BLangSimpleVariableDef varDef = ASTBuilderUtil.createVariableDef(pos);
        varDef.var = var;
        varDef.setBType(restParamType);

        BLangBlockStmt pushBlockStmt = createBlockStmt(pos);
        pushBlockStmt.stmts.add(varDef);

        BLangExpressionStmt expressionStmt = createExpressionStmt(pos, pushBlockStmt);
        BLangInvocation pushInvocation = createLangLibInvocationNode(PUSH_LANGLIB_METHOD, arrayVarRef,
                                                                     new ArrayList<BLangExpression>() {{
                                                                         add(pushRestArgsExpr);
                                                                     }}, restParamType, pos);
        pushInvocation.restArgs.add(pushInvocation.requiredArgs.remove(1));
        expressionStmt.expr = pushInvocation;

        BLangStatementExpression stmtExpression = createStatementExpression(pushBlockStmt, arrayVarRef);
        stmtExpression.setBType(restParamType);

        iExpr.restArgs = new ArrayList<BLangExpression>(1) {{ add(stmtExpression); }};
    }

    private void reorderNamedArgs(BLangInvocation iExpr, BInvokableSymbol invokableSymbol, BLangExpression varargRef) {
        List<BLangExpression> args = new ArrayList<>();
        Map<String, BLangExpression> namedArgs = new LinkedHashMap<>();
        iExpr.requiredArgs.stream()
                .filter(expr -> expr.getKind() == NodeKind.NAMED_ARGS_EXPR)
                .forEach(expr -> namedArgs.put(((NamedArgNode) expr).getName().value, expr));

        List<BVarSymbol> params = invokableSymbol.params;
        List<BLangRecordLiteral> incRecordLiterals = new ArrayList<>();
        BLangRecordLiteral incRecordParamAllowAdditionalFields = null;

        int varargIndex = 0;

        BType varargType = null;
        boolean tupleTypedVararg = false;

        if (varargRef != null) {
            varargType = Types.getImpliedType(varargRef.getBType());
            tupleTypedVararg = varargType.tag == TypeTags.TUPLE;
        }

        
        for (int i = 0; i < params.size(); i++) {
            BVarSymbol param = params.get(i);
            if (iExpr.requiredArgs.size() > i && iExpr.requiredArgs.get(i).getKind() != NodeKind.NAMED_ARGS_EXPR) {
                
                args.add(iExpr.requiredArgs.get(i));
            } else if (namedArgs.containsKey(param.name.value)) {
                
                args.add(namedArgs.remove(param.name.value));
            } else if (param.getFlags().contains(Flag.INCLUDED)) {
                BLangRecordLiteral recordLiteral = (BLangRecordLiteral) TreeBuilder.createRecordLiteralNode();
                BType paramType = param.type;
                recordLiteral.setBType(paramType);
                args.add(recordLiteral);
                incRecordLiterals.add(recordLiteral);
                if (((BRecordType) Types.getImpliedType(paramType)).restFieldType != symTable.noType) {
                    incRecordParamAllowAdditionalFields = recordLiteral;
                }
            } else if (varargRef == null) {
                
                BLangExpression expr = new BLangIgnoreExpr();
                expr.setBType(param.type);
                args.add(expr);
            } else {
                
                
                if (Types.getImpliedType(varargRef.getBType()).tag == TypeTags.RECORD) {
                    if (param.isDefaultable) {
                        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(varargRef.pos);
                        BLangInvocation hasKeyInvocation = createLangLibInvocationNode(HAS_KEY, varargRef,
                                List.of(createStringLiteral(param.pos, param.name.value)), null, varargRef.pos);
                        BLangSimpleVariableDef variableDef = createVarDef("$hasKey$", hasKeyInvocation.getBType(),
                                                                          hasKeyInvocation, hasKeyInvocation.pos);
                        blockStmt.stmts.add(variableDef);
                        BLangSimpleVarRef simpleVarRef = ASTBuilderUtil.createVariableRef(variableDef.pos,
                                                                                          variableDef.var.symbol);
                        BLangExpression indexExpr = rewriteExpr(createStringLiteral(param.pos, param.name.value));
                        BLangIndexBasedAccess memberAccessExpr =
                                ASTBuilderUtil.createMemberAccessExprNode(param.type, varargRef, indexExpr);
                        BLangExpression ignoreExpr = ASTBuilderUtil.createIgnoreExprNode(param.type);
                        BLangTernaryExpr ternaryExpr = ASTBuilderUtil.createTernaryExprNode(param.type, simpleVarRef,
                                                                                          memberAccessExpr, ignoreExpr);
                        BLangDynamicArgExpr dynamicArgExpr =
                                ASTBuilderUtil.createDynamicParamExpression(simpleVarRef, param, ternaryExpr);
                        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, dynamicArgExpr);
                        stmtExpr.setBType(dynamicArgExpr.getBType());
                        args.add(rewriteExpr(stmtExpr));
                    } else {
                        BLangFieldBasedAccess fieldBasedAccessExpression =
                                ASTBuilderUtil.createFieldAccessExpr(varargRef,
                                        ASTBuilderUtil.createIdentifier(param.pos, param.name.value));
                        fieldBasedAccessExpression.setBType(param.type);
                        args.add(fieldBasedAccessExpression);
                    }
                } else {
                    BLangExpression indexExpr = rewriteExpr(createIntLiteral(varargIndex));
                    BType memberAccessExprType = tupleTypedVararg ?
                            ((BTupleType) varargType).getTupleTypes().get(varargIndex)
                            : ((BArrayType) varargType).eType;
                    args.add(types.addConversionExprIfRequired(
                            ASTBuilderUtil.createMemberAccessExprNode(memberAccessExprType, varargRef, indexExpr),
                            param.type));
                    varargIndex++;
                }
            }
        }
        if (!namedArgs.isEmpty()) {
            setFieldsForIncRecordLiterals(namedArgs, incRecordLiterals, incRecordParamAllowAdditionalFields);
        }
        iExpr.requiredArgs = args;
    }

    private void setFieldsForIncRecordLiterals(Map<String, BLangExpression> namedArgs,
                                               List<BLangRecordLiteral> incRecordLiterals,
                                               BLangRecordLiteral incRecordParamAllowAdditionalFields) {
        for (String name : namedArgs.keySet()) {
            boolean isAdditionalField = true;
            BLangNamedArgsExpression expr = (BLangNamedArgsExpression) namedArgs.get(name);
            for (BLangRecordLiteral recordLiteral : incRecordLiterals) {
                LinkedHashMap<String, BField> fields =
                        ((BRecordType) Types.getImpliedType(recordLiteral.getBType())).fields;
                if (fields.containsKey(name) &&
                        Types.getImpliedType(fields.get(name).type).tag != TypeTags.NEVER) {
                    isAdditionalField = false;
                    createAndAddRecordFieldForIncRecordLiteral(recordLiteral, expr);
                    break;
                }
            }
            if (isAdditionalField) {
                createAndAddRecordFieldForIncRecordLiteral(incRecordParamAllowAdditionalFields, expr);
            }
        }
    }

    private void createAndAddRecordFieldForIncRecordLiteral(BLangRecordLiteral recordLiteral,
                                                            BLangNamedArgsExpression expr) {
        BLangSimpleVarRef varRef = new BLangSimpleVarRef();
        varRef.variableName = expr.name;
        BLangRecordLiteral.BLangRecordKeyValueField recordKeyValueField = ASTBuilderUtil.
                createBLangRecordKeyValue(varRef, expr.expr);
        recordLiteral.fields.add(recordKeyValueField);
    }

    private BLangBlockStmt getSafeErrorAssignment(Location location, BLangSimpleVarRef ref,
                                                  BSymbol invokableSymbol,
                                                  List<BType> equivalentErrorTypes,
                                                  boolean isCheckPanicExpr) {
        
        
        BType enclosingFuncReturnType = Types.getImpliedType(((BInvokableType) invokableSymbol.type).retType);
        Set<BType> returnTypeSet = enclosingFuncReturnType.tag == TypeTags.UNION ?
                ((BUnionType) enclosingFuncReturnType).getMemberTypes() :
                new LinkedHashSet<>() {{
                    add(enclosingFuncReturnType);
                }};

        
        boolean returnOnError = equivalentErrorTypes.stream()
                .allMatch(errorType -> returnTypeSet.stream()
                        .anyMatch(retType -> types.isAssignable(errorType, retType)));

        String patternFailureCaseVarName = GEN_VAR_PREFIX.value + "t_failure";
        BLangSimpleVariable errorVar =
                ASTBuilderUtil.createVariable(location, patternFailureCaseVarName, symTable.errorType,
                                                createTypeCastExpr(ref, symTable.errorType),
                                              new BVarSymbol(0, Names.fromString(patternFailureCaseVarName),
                                                             this.env.scope.owner.pkgID, symTable.errorType,
                                                             this.env.scope.owner, location, VIRTUAL));

        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(location);
        BLangSimpleVariableDef errorVarDef = ASTBuilderUtil.createVariableDef(location, errorVar);
        blockStmt.addStatement(errorVarDef);
        BLangVariableReference errorVarRef = ASTBuilderUtil.createVariableRef(location, errorVar.symbol);
        if (!isCheckPanicExpr && (returnOnError || this.onFailClause != null)) {
            
            BLangFail failStmt = (BLangFail) TreeBuilder.createFailNode();
            failStmt.pos = location;
            failStmt.expr = errorVarRef;
            blockStmt.addStatement(failStmt);
            if (returnOnError && this.shouldReturnErrors) {
                BLangReturn errorReturn = ASTBuilderUtil.createReturnStmt(location, rewrite(errorVarRef, env));
                errorReturn.desugared = true;
                failStmt.exprStmt = errorReturn;
            }
        } else {
            
            BLangPanic panicNode = (BLangPanic) TreeBuilder.createPanicNode();
            panicNode.pos = location;
            panicNode.expr = errorVarRef;
            blockStmt.addStatement(panicNode);
        }

        return blockStmt;
    }

    private BType getStructuredBindingPatternType(BLangVariable bindingPatternVariable) {
        if (NodeKind.TUPLE_VARIABLE == bindingPatternVariable.getKind()) {
            BLangTupleVariable tupleVariable = (BLangTupleVariable) bindingPatternVariable;
            List<BTupleMember> memberTypes = new ArrayList<>();
            for (int i = 0; i < tupleVariable.memberVariables.size(); i++) {
                BType member = getStructuredBindingPatternType(tupleVariable.memberVariables.get(i));
                BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(member);
                memberTypes.add(
                        new BTupleMember(member, varSymbol));
            }
            BTupleType tupleType = new BTupleType(memberTypes);
            if (tupleVariable.restVariable != null) {
                BArrayType restArrayType = (BArrayType) getStructuredBindingPatternType(tupleVariable.restVariable);
                tupleType.restType = restArrayType.eType;
            }
            return tupleType;
        }

        if (NodeKind.RECORD_VARIABLE == bindingPatternVariable.getKind()) {
            BLangRecordVariable recordVariable = (BLangRecordVariable) bindingPatternVariable;

            BRecordTypeSymbol recordSymbol =
                    Symbols.createRecordSymbol(0, Names.fromString("$anonRecordType$" + UNDERSCORE + recordCount++),
                                               env.enclPkg.symbol.pkgID, null, env.scope.owner, recordVariable.pos,
                                               VIRTUAL);
            recordSymbol.scope = new Scope(recordSymbol);

            LinkedHashMap<String, BField> fields = new LinkedHashMap<>();
            List<BLangSimpleVariable> typeDefFields = new ArrayList<>();

            for (int i = 0; i < recordVariable.variableList.size(); i++) {
                String fieldNameStr = recordVariable.variableList.get(i).key.value;
                Name fieldName = Names.fromString(fieldNameStr);
                BType fieldType = getStructuredBindingPatternType(
                        recordVariable.variableList.get(i).valueBindingPattern);
                BVarSymbol fieldSymbol = new BVarSymbol(Flags.REQUIRED, fieldName, env.enclPkg.symbol.pkgID, fieldType,
                                                        recordSymbol, bindingPatternVariable.pos, VIRTUAL);

                
                fields.put(fieldName.value, new BField(fieldName, bindingPatternVariable.pos, fieldSymbol));
                typeDefFields.add(ASTBuilderUtil.createVariable(null, fieldNameStr, fieldType, null, fieldSymbol));
                recordSymbol.scope.define(fieldName, fieldSymbol);
            }

            BRecordType recordVarType = new BRecordType(recordSymbol);
            recordVarType.fields = fields;

            
            recordVarType.restFieldType = recordVariable.restParam != null ?
                        ((BRecordType) recordVariable.restParam.getBType()).restFieldType :
                    symTable.anydataType;
            recordSymbol.type = recordVarType;
            recordVarType.tsymbol = recordSymbol;

            BLangRecordTypeNode recordTypeNode = TypeDefBuilderHelper.createRecordTypeNode(typeDefFields,
                                                                                           recordVarType,
                                                                                           bindingPatternVariable.pos);
            TypeDefBuilderHelper.createTypeDefinitionForTSymbol(recordVarType, recordSymbol, recordTypeNode, env);

            return recordVarType;
        }

        if (NodeKind.ERROR_VARIABLE == bindingPatternVariable.getKind()) {
            BLangErrorVariable errorVariable = (BLangErrorVariable) bindingPatternVariable;
            BErrorTypeSymbol errorTypeSymbol = new BErrorTypeSymbol(
                    SymTag.ERROR,
                    Flags.PUBLIC,
                    Names.fromString("$anonErrorType$" + UNDERSCORE + errorCount++),
                    env.enclPkg.symbol.pkgID,
                    null, null, errorVariable.pos, VIRTUAL);
            BType detailType;
            if ((errorVariable.detail == null || errorVariable.detail.isEmpty()) && errorVariable.restDetail != null) {
                detailType = symTable.detailType;
            } else {
                detailType = createDetailType(errorVariable.detail, errorVariable.restDetail, errorCount++,
                                              errorVariable.pos);

                BLangRecordTypeNode recordTypeNode = createRecordTypeNode(errorVariable, (BRecordType) detailType);
                TypeDefBuilderHelper.createTypeDefinitionForTSymbol(detailType, detailType.tsymbol,
                        recordTypeNode, env);
            }
            BErrorType errorType = new BErrorType(errorTypeSymbol, detailType);
            errorTypeSymbol.type = errorType;

            TypeDefBuilderHelper.createTypeDefinitionForTSymbol(errorType, errorTypeSymbol,
                    createErrorTypeNode(errorType), env);
            return errorType;
        }

        return bindingPatternVariable.getBType();
    }

    private BLangRecordTypeNode createRecordTypeNode(BLangErrorVariable errorVariable, BRecordType detailType) {
        List<BLangSimpleVariable> fieldList = new ArrayList<>();
        for (BLangErrorVariable.BLangErrorDetailEntry field : errorVariable.detail) {
            BVarSymbol symbol = field.valueBindingPattern.symbol;
            if (symbol == null) {
                symbol = new BVarSymbol(Flags.PUBLIC, Names.fromString(field.key.value + "$"),
                                        this.env.enclPkg.packageID, symTable.pureType, null,
                                        field.valueBindingPattern.pos, VIRTUAL);
            }
            BLangSimpleVariable fieldVar = ASTBuilderUtil.createVariable(
                    field.valueBindingPattern.pos,
                    symbol.name.value,
                    field.valueBindingPattern.getBType(),
                    field.valueBindingPattern.expr,
                    symbol);
            fieldList.add(fieldVar);
        }
        return TypeDefBuilderHelper.createRecordTypeNode(fieldList, detailType, errorVariable.pos);
    }

    private BType createDetailType(List<BLangErrorVariable.BLangErrorDetailEntry> detail,
                                   BLangSimpleVariable restDetail, int errorNo, Location pos) {
        BRecordType detailRecordType = createAnonRecordType(pos);

        if (restDetail == null) {
            detailRecordType.sealed = true;
        }

        for (BLangErrorVariable.BLangErrorDetailEntry detailEntry : detail) {
            Name fieldName = names.fromIdNode(detailEntry.key);
            BType fieldType = getStructuredBindingPatternType(detailEntry.valueBindingPattern);
            BVarSymbol fieldSym = new BVarSymbol(Flags.PUBLIC, fieldName, detailRecordType.tsymbol.pkgID, fieldType,
                    detailRecordType.tsymbol, detailEntry.key.pos, VIRTUAL);
            detailRecordType.fields.put(fieldName.value, new BField(fieldName, detailEntry.key.pos, fieldSym));
            detailRecordType.tsymbol.scope.define(fieldName, fieldSym);
        }

        return detailRecordType;
    }

    private BRecordType createAnonRecordType(Location pos) {
        BRecordTypeSymbol detailRecordTypeSymbol = new BRecordTypeSymbol(
                SymTag.RECORD,
                Flags.PUBLIC,
                Names.fromString(anonModelHelper.getNextRecordVarKey(env.enclPkg.packageID)),
                env.enclPkg.symbol.pkgID, null, null, pos, VIRTUAL);
        detailRecordTypeSymbol.scope = new Scope(detailRecordTypeSymbol);

        BRecordType detailRecordType = new BRecordType(detailRecordTypeSymbol);
        detailRecordType.restFieldType = symTable.anydataType;
        return detailRecordType;
    }

    BLangErrorType createErrorTypeNode(BErrorType errorType) {
        BLangErrorType errorTypeNode = (BLangErrorType) TreeBuilder.createErrorTypeNode();
        errorTypeNode.setBType(errorType);
        return errorTypeNode;
    }

    private BLangExpression createBinaryExpression(Location pos, BLangSimpleVarRef varRef,
                                                   BLangExpression expression) {

        BLangBinaryExpr binaryExpr;
        if (NodeKind.GROUP_EXPR == expression.getKind()) {
            return createBinaryExpression(pos, varRef, ((BLangGroupExpr) expression).expression);
        }

        if (NodeKind.BINARY_EXPR == expression.getKind()) {
            binaryExpr = (BLangBinaryExpr) expression;
            BLangExpression lhsExpr = createBinaryExpression(pos, varRef, binaryExpr.lhsExpr);
            BLangExpression rhsExpr = createBinaryExpression(pos, varRef, binaryExpr.rhsExpr);

            binaryExpr = ASTBuilderUtil.createBinaryExpr(pos, lhsExpr, rhsExpr, symTable.booleanType, OperatorKind.OR,
                    (BOperatorSymbol) symResolver
                            .resolveBinaryOperator(OperatorKind.OR, symTable.booleanType, symTable.booleanType));
        } else if (expression.getKind() == NodeKind.SIMPLE_VARIABLE_REF
                && ((BLangSimpleVarRef) expression).variableName.value.equals(IGNORE.value)) {
            BLangValueType anyType = (BLangValueType) TreeBuilder.createValueTypeNode();
            anyType.setBType(symTable.anyType);
            anyType.typeKind = TypeKind.ANY;
            return ASTBuilderUtil.createTypeTestExpr(pos, varRef, anyType);
        } else {
            binaryExpr = ASTBuilderUtil
                    .createBinaryExpr(pos, varRef, expression, symTable.booleanType, OperatorKind.EQUAL, null);
            BSymbol opSymbol = symResolver.resolveBinaryOperator(OperatorKind.EQUAL, varRef.getBType(),
                                                                 expression.getBType());
            if (opSymbol == symTable.notFoundSymbol) {
                opSymbol = symResolver
                        .getBinaryEqualityForTypeSets(OperatorKind.EQUAL, symTable.anydataType, expression.getBType(),
                                                      binaryExpr, env);
            }
            binaryExpr.opSymbol = (BOperatorSymbol) opSymbol;
        }
        return binaryExpr;
    }

    private BLangIsLikeExpr createIsLikeExpression(Location pos, BLangExpression expr, BType type) {
        return ASTBuilderUtil.createIsLikeExpr(pos, expr, ASTBuilderUtil.createTypeNode(type), symTable.booleanType);
    }

    private BLangAssignment createAssignmentStmt(BLangSimpleVariable variable) {
        BLangSimpleVarRef varRef = (BLangSimpleVarRef) TreeBuilder.createSimpleVariableReferenceNode();
        varRef.pos = variable.pos;
        varRef.variableName = variable.name;
        varRef.symbol = variable.symbol;
        varRef.setBType(variable.getBType());

        BLangAssignment assignmentStmt = (BLangAssignment) TreeBuilder.createAssignmentNode();
        assignmentStmt.expr = variable.expr;
        assignmentStmt.pos = variable.pos;
        assignmentStmt.setVariable(varRef);
        return assignmentStmt;
    }

    private BLangAssignment createStructFieldUpdate(BLangFunction function, BLangSimpleVariable variable,
                                                    BVarSymbol selfSymbol) {
        return createStructFieldUpdate(function, variable.expr, variable.symbol, variable.getBType(), selfSymbol,
                                       variable.name);
    }

    private BLangAssignment createStructFieldUpdate(BLangFunction function, BLangExpression expr,
                                                    BVarSymbol fieldSymbol, BType fieldType, BVarSymbol selfSymbol,
                                                    BLangIdentifier fieldName) {
        BLangSimpleVarRef selfVarRef = ASTBuilderUtil.createVariableRef(function.pos, selfSymbol);
        BLangFieldBasedAccess fieldAccess = ASTBuilderUtil.createFieldAccessExpr(selfVarRef, fieldName);
        fieldAccess.symbol = fieldSymbol;
        fieldAccess.setBType(fieldType);
        fieldAccess.isStoreOnCreation = true;

        BLangAssignment assignmentStmt = (BLangAssignment) TreeBuilder.createAssignmentNode();
        
        expr.pos = this.symTable.builtinPos;
        fieldName.pos =  this.symTable.builtinPos;
        fieldSymbol.pos =  this.symTable.builtinPos;
        assignmentStmt.expr = expr;
        assignmentStmt.pos = function.pos;
        assignmentStmt.setVariable(fieldAccess);

        SymbolEnv initFuncEnv = SymbolEnv.createFunctionEnv(function, function.symbol.scope, env);
        return rewrite(assignmentStmt, initFuncEnv);
    }

    private boolean safeNavigate(BLangAccessExpression accessExpr) {
        if (accessExpr.isLValue || accessExpr.expr == null) {
            return false;
        }

        if (accessExpr.errorSafeNavigation || accessExpr.nilSafeNavigation) {
            return true;
        }

        NodeKind kind = accessExpr.expr.getKind();
        if (kind == NodeKind.FIELD_BASED_ACCESS_EXPR ||
                kind == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            return safeNavigate((BLangAccessExpression) accessExpr.expr);
        }

        return false;
    }

    private BLangExpression rewriteSafeNavigationExpr(BLangAccessExpression accessExpr) {
        BType originalExprType = accessExpr.getBType();
        
        String matchTempResultVarName = GEN_VAR_PREFIX.value + "temp_result";
        BLangSimpleVariable tempResultVar =
                ASTBuilderUtil.createVariable(accessExpr.pos, matchTempResultVarName, accessExpr.getBType(), null,
                                              new BVarSymbol(0, Names.fromString(matchTempResultVarName),
                                                             this.env.scope.owner.pkgID, accessExpr.getBType(),
                                                             this.env.scope.owner, accessExpr.pos, VIRTUAL));
        BLangSimpleVariableDef tempResultVarDef = ASTBuilderUtil.createVariableDef(accessExpr.pos, tempResultVar);
        BLangVariableReference tempResultVarRef =
                ASTBuilderUtil.createVariableRef(accessExpr.pos, tempResultVar.symbol);

        
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(accessExpr.pos);
        blockStmt.stmts.add(tempResultVarDef);
        handleSafeNavigation(blockStmt, accessExpr, accessExpr.getBType(), tempResultVar);

        
        BLangMatchStatement matchStmt = this.matchStmtStack.firstElement();
        blockStmt.stmts.add(matchStmt);
        BLangStatementExpression stmtExpression = createStatementExpression(blockStmt, tempResultVarRef);
        stmtExpression.setBType(originalExprType);
        
        this.matchStmtStack = new Stack<>();
        this.accessExprStack = new Stack<>();
        this.successClause = null;
        this.safeNavigationAssignment = null;
        return stmtExpression;
    }

    private void handleSafeNavigation(BLangBlockStmt blockStmt, BLangAccessExpression accessExpr, BType type,
                                      BLangSimpleVariable tempResultVar) {
        if (accessExpr.expr == null) {
            return;
        }

        
        NodeKind kind = accessExpr.expr.getKind();
        if (kind == NodeKind.FIELD_BASED_ACCESS_EXPR || kind == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            handleSafeNavigation(blockStmt, (BLangAccessExpression) accessExpr.expr, type, tempResultVar);
        }

        if (!(accessExpr.errorSafeNavigation || accessExpr.nilSafeNavigation)) {
            BType originalType = Types.getImpliedType(accessExpr.originalType);
            if (isMapJson(originalType, false)) {
                accessExpr.setBType(BUnionType.create(null, originalType, symTable.errorType));
            } else {
                accessExpr.setBType(originalType);
            }
            if (this.safeNavigationAssignment != null) {
                this.safeNavigationAssignment.expr =
                                                types.addConversionExprIfRequired(accessExpr, tempResultVar.getBType());
            }
            return;
        }

        /*
         * If the field access is a safe navigation, create a match expression.
         * Then chain the current expression as the success-pattern of the parent
         * match expr, if available.
         * eg:
         * x but {              <--- parent match expr
         *   error e => e,
         *   T t => t.y but {   <--- current expr
         *      error e => e,
         *      R r => r.z
         *   }
         * }
         */

        BLangExpression matchExpr = accessExpr.expr;
        BLangSimpleVariableDef variableDef =
                                         createVarDef("$varDef$", matchExpr.getBType(), matchExpr, matchExpr.pos);
        BLangSimpleVarRef simpleVarRef = ASTBuilderUtil.createVariableRef(variableDef.pos, variableDef.var.symbol);
        accessExpr.expr = simpleVarRef;
        blockStmt.stmts.add(variableDef);
        Location pos = accessExpr.pos;
        BLangMatchStatement matchStmt = ASTBuilderUtil.createMatchStatement(simpleVarRef, pos);
        BType matchExprType = accessExpr.expr.getBType();

        boolean isAllTypesRecords = false;
        LinkedHashSet<BType> memTypes = new LinkedHashSet<>();
        BType referredType = Types.getImpliedType(matchExpr.getBType());
        if (referredType.tag == TypeTags.UNION) {
            memTypes = new LinkedHashSet<>(((BUnionType) referredType).getMemberTypes());
            isAllTypesRecords = isAllTypesAreRecordsInUnion(memTypes);
        }

        
        if (accessExpr.nilSafeNavigation) {
            matchStmt.addMatchClause(getMatchNullClause(matchExpr, tempResultVar));
            matchStmt.setBType(type);
            memTypes.remove(symTable.nilType);
        }

        
        if (accessExpr.errorSafeNavigation) {
            matchStmt.addMatchClause(getMatchErrorClause(matchExpr, tempResultVar));
            matchStmt.setBType(type);
            matchStmt.pos = pos;
            memTypes.remove(symTable.errorType);
        }

        BLangMatchClause successClause = null;
        Name field = getFieldName(accessExpr);
        if (field == Names.EMPTY) {
            successClause = getSuccessPatternClause(matchExprType, matchExpr, accessExpr, tempResultVar,
                    accessExpr.errorSafeNavigation);
            matchStmt.addMatchClause(successClause);
            pushToMatchStatementStack(matchStmt, successClause, pos);
            return;
        }

        if (isAllTypesRecords) {
            for (BType memberType : memTypes) {
                BRecordType recordType = (BRecordType) Types.getImpliedType(memberType);
                if (recordType.fields.containsKey(field.value) || !recordType.sealed) {
                    successClause = getSuccessPatternClause(memberType, matchExpr, accessExpr, tempResultVar,
                            accessExpr.errorSafeNavigation);
                    matchStmt.addMatchClause(successClause);
                }
            }
            matchStmt.addMatchClause(getMatchAllAndNilReturnClause(matchExpr, tempResultVar));
            pushToMatchStatementStack(matchStmt, successClause, pos);
            return;
        }

        
        successClause = getSuccessPatternClause(matchExprType, matchExpr, accessExpr, tempResultVar,
                accessExpr.errorSafeNavigation);
        matchStmt.addMatchClause(successClause);
        pushToMatchStatementStack(matchStmt, successClause, pos);
    }

    private boolean isMapJson(BType originalType, boolean fromMap) {
        originalType = Types.getImpliedType(originalType);
        return ((originalType.tag == TypeTags.MAP) && isMapJson(((BMapType) originalType).getConstraint(), true))
                || ((originalType.tag == TypeTags.JSON) && fromMap);
    }

    private void pushToMatchStatementStack(BLangMatchStatement matchStmt, BLangMatchClause successClause,
                                           Location pos) {
        this.matchStmtStack.push(matchStmt);
        if (this.successClause != null) {
            this.successClause.blockStmt = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(matchStmt));
        }
        this.successClause = successClause;
    }

    private Name getFieldName(BLangAccessExpression accessExpr) {
        Name field = Names.EMPTY;
        if (accessExpr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR) {
            field = new Name(((BLangFieldBasedAccess) accessExpr).field.value);
        } else if (accessExpr.getKind() == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            BLangExpression indexBasedExpression = ((BLangIndexBasedAccess) accessExpr).indexExpr;
            if (indexBasedExpression.getKind() == NodeKind.LITERAL) {
                field = new Name(((BLangLiteral) indexBasedExpression).value.toString());
            }
        }
        return field;
    }

    private boolean isAllTypesAreRecordsInUnion(LinkedHashSet<BType> memTypes) {
        for (BType memType : memTypes) {
            int typeTag = Types.getImpliedType(memType).tag;
            if (typeTag != TypeTags.RECORD && typeTag != TypeTags.ERROR && typeTag != TypeTags.NIL) {
                return false;
            }
        }
        return true;
    }

    private BLangMatchClause getMatchErrorClause(BLangExpression matchExpr, BLangSimpleVariable tempResultVar) {
        String errorPatternVarName = GEN_VAR_PREFIX.value + "t_match_error";
        Location pos = matchExpr.pos;
        BVarSymbol errorPatternVarSymbol = new BVarSymbol(0, Names.fromString(errorPatternVarName),
                this.env.scope.owner.pkgID, symTable.anyOrErrorType, this.env.scope.owner, pos, VIRTUAL);
        BLangCaptureBindingPattern captureBindingPattern =
                ASTBuilderUtil.createCaptureBindingPattern(errorPatternVarSymbol, errorPatternVarName);
        BLangVarBindingPatternMatchPattern varBindingPatternMatchPattern =
                ASTBuilderUtil.createVarBindingPatternMatchPattern(captureBindingPattern, matchExpr);

        BLangSimpleVarRef assignmentRhsExpr = ASTBuilderUtil.createVariableRef(pos, errorPatternVarSymbol);
        BLangVariableReference tempResultVarRef = ASTBuilderUtil.createVariableRef(pos, tempResultVar.symbol);
        BLangAssignment assignmentStmt =
                ASTBuilderUtil.createAssignmentStmt(pos, tempResultVarRef, assignmentRhsExpr);
        BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(assignmentStmt));
        BLangExpression matchGuard = ASTBuilderUtil.createTypeTestExpr(pos, assignmentRhsExpr, getErrorTypeNode());
        matchGuard.setBType(symTable.booleanType);

        return ASTBuilderUtil.createMatchClause(matchExpr, clauseBody, matchGuard, varBindingPatternMatchPattern);
    }

    private BLangMatchClause getMatchNullClause(BLangExpression matchExpr, BLangSimpleVariable tempResultVar) {
        String nullPatternVarName = GEN_VAR_PREFIX.value + "t_match_null";
        Location pos = matchExpr.pos;
        BVarSymbol nullPatternVarSymbol = new BVarSymbol(0, Names.fromString(nullPatternVarName),
                this.env.scope.owner.pkgID, symTable.anyOrErrorType, this.env.scope.owner, pos, VIRTUAL);
        BLangCaptureBindingPattern captureBindingPattern =
                ASTBuilderUtil.createCaptureBindingPattern(nullPatternVarSymbol, nullPatternVarName);
        BLangVarBindingPatternMatchPattern varBindingPatternMatchPattern =
                ASTBuilderUtil.createVarBindingPatternMatchPattern(captureBindingPattern, matchExpr);

        BLangSimpleVarRef assignmentRhsExpr = ASTBuilderUtil.createVariableRef(pos, nullPatternVarSymbol);
        BLangVariableReference tempResultVarRef = ASTBuilderUtil.createVariableRef(pos, tempResultVar.symbol);
        BLangAssignment assignmentStmt =
                ASTBuilderUtil.createAssignmentStmt(pos, tempResultVarRef, assignmentRhsExpr);
        BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(assignmentStmt));
        BLangExpression matchGuard = ASTBuilderUtil.createTypeTestExpr(pos, assignmentRhsExpr, getNillTypeNode());
        matchGuard.setBType(symTable.booleanType);

        return ASTBuilderUtil.createMatchClause(matchExpr, clauseBody, matchGuard, varBindingPatternMatchPattern);
    }

    private BLangMatchClause getMatchAllAndNilReturnClause(BLangExpression matchExpr,
                                                           BLangSimpleVariable tempResultVar) {
        Location pos = matchExpr.pos;
        BLangVariableReference tempResultVarRef = ASTBuilderUtil.createVariableRef(pos, tempResultVar.symbol);
        BLangAssignment assignmentStmt =
                ASTBuilderUtil.createAssignmentStmt(pos, tempResultVarRef, createLiteral(pos, symTable.nilType,
                        Names.NIL_VALUE));
        BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(assignmentStmt));

        BLangWildCardMatchPattern wildCardMatchPattern = ASTBuilderUtil.createWildCardMatchPattern(matchExpr);
        wildCardMatchPattern.setBType(symTable.anyType);
        return ASTBuilderUtil.createMatchClause(matchExpr, clauseBody, null, wildCardMatchPattern);
    }

    private BLangMatchClause getSuccessPatternClause(BType type, BLangExpression matchExpr,
                                                     BLangAccessExpression accessExpr,
                                                     BLangSimpleVariable tempResultVar, boolean liftError) {
        type = types.getSafeType(type, true, liftError);
        String successPatternVarName = GEN_VAR_PREFIX.value + "t_match_success";

        Location pos = accessExpr.pos;
        BVarSymbol  successPatternSymbol;
        if (Types.getImpliedType(type).tag == TypeTags.INVOKABLE) {
            successPatternSymbol = new BInvokableSymbol(SymTag.VARIABLE, 0, Names.fromString(successPatternVarName),
                    this.env.scope.owner.pkgID, symTable.anyOrErrorType, this.env.scope.owner, pos, VIRTUAL);
        } else {
            successPatternSymbol = new BVarSymbol(0, Names.fromString(successPatternVarName),
                    this.env.scope.owner.pkgID, symTable.anyOrErrorType, this.env.scope.owner, pos, VIRTUAL);
        }

        BLangSimpleVariable successPatternVar = ASTBuilderUtil.createVariable(accessExpr.pos, successPatternVarName,
                type, null, successPatternSymbol);
        BLangSimpleVarRef successPatternVarRef = ASTBuilderUtil.createVariableRef(accessExpr.pos,
                successPatternVar.symbol);

        BLangCaptureBindingPattern captureBindingPattern =
                ASTBuilderUtil.createCaptureBindingPattern(successPatternSymbol, successPatternVarName);
        BLangVarBindingPatternMatchPattern varBindingPatternMatchPattern =
                ASTBuilderUtil.createVarBindingPatternMatchPattern(captureBindingPattern, matchExpr);

        BLangAccessExpression tempAccessExpr = nodeCloner.cloneNode(accessExpr);
        if (accessExpr.getKind() == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            ((BLangIndexBasedAccess) tempAccessExpr).indexExpr = ((BLangIndexBasedAccess) accessExpr).indexExpr;
        }
        if (accessExpr instanceof BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess) {
            ((BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess) tempAccessExpr).nsSymbol =
                    ((BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess) accessExpr).nsSymbol;
        }

        tempAccessExpr.expr = types.addConversionExprIfRequired(successPatternVarRef, type);
        tempAccessExpr.errorSafeNavigation = false;
        tempAccessExpr.nilSafeNavigation = false;
        accessExpr.cloneRef = null;

        
        
        
        if (TypeTags.isXMLTypeTag(Types.getImpliedType(tempAccessExpr.expr.getBType()).tag)) {
            
            tempAccessExpr.setBType(BUnionType.create(null, accessExpr.originalType, symTable.errorType,
                    symTable.nilType));
        } else {
            tempAccessExpr.setBType(accessExpr.originalType);
        }
        tempAccessExpr.optionalFieldAccess = accessExpr.optionalFieldAccess;

        BLangVariableReference tempResultVarRef =
                ASTBuilderUtil.createVariableRef(accessExpr.pos, tempResultVar.symbol);

        BLangExpression assignmentRhsExpr =
                types.addConversionExprIfRequired(tempAccessExpr, tempResultVarRef.getBType());
        BLangAssignment assignmentStmt =
                ASTBuilderUtil.createAssignmentStmt(accessExpr.pos, tempResultVarRef, assignmentRhsExpr);
        BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(accessExpr.pos, this.env.scope,
                Lists.of(assignmentStmt));
        BLangExpression matchGuard = ASTBuilderUtil.createTypeTestExpr(pos, successPatternVarRef, createTypeNode(type));
        matchGuard.setBType(symTable.booleanType);

        return ASTBuilderUtil.createMatchClause(matchExpr, clauseBody, matchGuard, varBindingPatternMatchPattern);
    }

    BLangValueType getNillTypeNode() {
        BLangValueType nillTypeNode = (BLangValueType) TreeBuilder.createValueTypeNode();
        nillTypeNode.typeKind = TypeKind.NIL;
        nillTypeNode.setBType(symTable.nilType);
        return nillTypeNode;
    }

    BLangValueType createTypeNode(BType type) {
        BLangValueType typeNode = (BLangValueType) TreeBuilder.createValueTypeNode();
        typeNode.typeKind = type.getKind();
        typeNode.setBType(type);
        return typeNode;
    }

    private BLangValueExpression cloneExpression(BLangExpression expr) {
        switch (expr.getKind()) {
            case SIMPLE_VARIABLE_REF:
                return ASTBuilderUtil.createVariableRef(expr.pos, ((BLangSimpleVarRef) expr).symbol);
            case FIELD_BASED_ACCESS_EXPR:
            case INDEX_BASED_ACCESS_EXPR:
                return cloneAccessExpr((BLangAccessExpression) expr);
            default:
                throw new IllegalStateException();
        }
    }

    private BLangAccessExpression cloneAccessExpr(BLangAccessExpression originalAccessExpr) {
        if (originalAccessExpr.expr == null) {
            return originalAccessExpr;
        }

        BLangExpression varRef;
        NodeKind kind = originalAccessExpr.expr.getKind();
        if (kind == NodeKind.FIELD_BASED_ACCESS_EXPR || kind == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            varRef = cloneAccessExpr((BLangAccessExpression) originalAccessExpr.expr);
        } else {
            varRef = cloneExpression(originalAccessExpr.expr);
        }
        varRef.setBType(types.getSafeType(originalAccessExpr.expr.getBType(), true, false));

        BLangAccessExpression accessExpr;
        switch (originalAccessExpr.getKind()) {
            case FIELD_BASED_ACCESS_EXPR:
                accessExpr = ASTBuilderUtil.createFieldAccessExpr(varRef,
                        ((BLangFieldBasedAccess) originalAccessExpr).field);
                break;
            case INDEX_BASED_ACCESS_EXPR:
                accessExpr = ASTBuilderUtil.createIndexAccessExpr(varRef,
                        ((BLangIndexBasedAccess) originalAccessExpr).indexExpr);
                break;
            default:
                throw new IllegalStateException();
        }

        accessExpr.originalType = originalAccessExpr.originalType;
        accessExpr.pos = originalAccessExpr.pos;
        accessExpr.isLValue = originalAccessExpr.isLValue;
        accessExpr.symbol = originalAccessExpr.symbol;
        accessExpr.errorSafeNavigation = false;
        accessExpr.nilSafeNavigation = false;

        
        
        
        accessExpr.setBType(originalAccessExpr.originalType);
        return accessExpr;
    }

    private BLangBinaryExpr getModifiedIntRangeStartExpr(BLangExpression expr) {
        BLangLiteral constOneLiteral = ASTBuilderUtil.createLiteral(expr.pos, symTable.intType, 1L);
        return ASTBuilderUtil.createBinaryExpr(expr.pos, expr, constOneLiteral, symTable.intType, OperatorKind.ADD,
                (BOperatorSymbol) symResolver.resolveBinaryOperator(OperatorKind.ADD,
                        symTable.intType,
                        symTable.intType));
    }

    private BLangBinaryExpr getModifiedIntRangeEndExpr(BLangExpression expr) {
        BLangLiteral constOneLiteral = ASTBuilderUtil.createLiteral(expr.pos, symTable.intType, 1L);
        return ASTBuilderUtil.createBinaryExpr(expr.pos, expr, constOneLiteral, symTable.intType, OperatorKind.SUB,
                (BOperatorSymbol) symResolver.resolveBinaryOperator(OperatorKind.SUB,
                        symTable.intType,
                        symTable.intType));
    }

    BLangLiteral getBooleanLiteral(boolean value) {
        BLangLiteral literal = (BLangLiteral) TreeBuilder.createLiteralExpression();
        literal.value = value;
        literal.setBType(symTable.booleanType);
        literal.pos = symTable.builtinPos;
        return literal;
    }

    private BLangFunction createInitFunctionForClassDefn(BLangClassDefinition classDefinition, SymbolEnv env) {
        BType returnType = symTable.nilType;

        
        if (classDefinition.initFunction != null) {
            returnType = classDefinition.initFunction.getBType().getReturnType();
        }

        BLangFunction initFunction =
                TypeDefBuilderHelper.createInitFunctionForStructureType(classDefinition.symbol, env, names,
                        GENERATED_INIT_SUFFIX, classDefinition.getBType(), returnType);
        BObjectTypeSymbol typeSymbol = ((BObjectTypeSymbol) classDefinition.getBType().tsymbol);
        typeSymbol.generatedInitializerFunc = new BAttachedFunction(GENERATED_INIT_SUFFIX, initFunction.symbol,
                (BInvokableType) initFunction.getBType(), null);
        classDefinition.generatedInitFunction = initFunction;
        initFunction.returnTypeNode.setBType(returnType);
        return initFunction;
    }

    private void visitBinaryLogicalExpr(BLangBinaryExpr binaryExpr) {
        /*
         * Desugar (lhsExpr && rhsExpr) to following if-else:
         *
         * logical AND:
         * -------------
         * T $result$;
         * if (lhsExpr) {
         *    $result$ = rhsExpr;
         * } else {
         *    $result$ = false;
         * }
         *
         * logical OR:
         * -------------
         * T $result$;
         * if (lhsExpr) {
         *    $result$ = true;
         * } else {
         *    $result$ = rhsExpr;
         * }
         *
         */
        BLangSimpleVariableDef resultVarDef = createVarDef("$result$", binaryExpr.getBType(), null,
                                                           symTable.builtinPos);
        BLangBlockStmt thenBody = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);
        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);

        
        BLangSimpleVarRef thenResultVarRef = ASTBuilderUtil.createVariableRef(symTable.builtinPos,
                                                                              resultVarDef.var.symbol);
        BLangExpression thenResult;
        if (binaryExpr.opKind == OperatorKind.AND) {
            thenResult = binaryExpr.rhsExpr;
        } else {
            thenResult = getBooleanLiteral(true);
        }
        BLangAssignment thenAssignment =
                ASTBuilderUtil.createAssignmentStmt(binaryExpr.pos, thenResultVarRef, thenResult);
        thenBody.addStatement(thenAssignment);

        
        BLangExpression elseResult;
        BLangSimpleVarRef elseResultVarRef = ASTBuilderUtil.createVariableRef(symTable.builtinPos,
                                                                              resultVarDef.var.symbol);
        if (binaryExpr.opKind == OperatorKind.AND) {
            elseResult = getBooleanLiteral(false);
        } else {
            elseResult = binaryExpr.rhsExpr;
        }
        BLangAssignment elseAssignment =
                ASTBuilderUtil.createAssignmentStmt(binaryExpr.pos, elseResultVarRef, elseResult);
        elseBody.addStatement(elseAssignment);

        
        BLangSimpleVarRef resultVarRef = ASTBuilderUtil.createVariableRef(binaryExpr.pos, resultVarDef.var.symbol);
        BLangIf ifElse = ASTBuilderUtil.createIfElseStmt(binaryExpr.pos, binaryExpr.lhsExpr, thenBody, elseBody);

        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(binaryExpr.pos, Lists.of(resultVarDef, ifElse));
        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(binaryExpr.getBType());

        result = rewriteExpr(stmtExpr);
    }

    protected boolean isMappingOrObjectConstructorOrObjInit(BLangExpression expression) {
        switch (expression.getKind()) {
            case TYPE_INIT_EXPR:
            case RECORD_LITERAL_EXPR:
            case OBJECT_CTOR_EXPRESSION:
                return true;
            case CHECK_EXPR:
                return isMappingOrObjectConstructorOrObjInit(((BLangCheckedExpr) expression).expr);
            case TYPE_CONVERSION_EXPR:
                return isMappingOrObjectConstructorOrObjInit(((BLangTypeConversionExpr) expression).expr);
            default:
                return false;
        }
    }

    private BType getRestType(BInvokableSymbol invokableSymbol) {
        if (invokableSymbol != null && invokableSymbol.restParam != null) {
            return invokableSymbol.restParam.type;
        }
        return null;
    }

    private BType getRestType(BLangFunction function) {
        if (function != null && function.restParam != null) {
            return function.restParam.getBType();
        }
        return null;
    }

    private BVarSymbol getRestSymbol(BLangFunction function) {
        if (function != null && function.restParam != null) {
            return function.restParam.symbol;
        }
        return null;
    }

    private boolean isComputedKey(RecordLiteralNode.RecordField field) {
        if (!field.isKeyValueField()) {
            return false;
        }
        return ((BLangRecordLiteral.BLangRecordKeyValueField) field).key.computedKey;
    }

    private BLangRecordLiteral rewriteMappingConstructor(BLangRecordLiteral mappingConstructorExpr) {
        List<RecordLiteralNode.RecordField> fields = mappingConstructorExpr.fields;

        BType type = mappingConstructorExpr.getBType();
        Location pos = mappingConstructorExpr.pos;

        List<RecordLiteralNode.RecordField> rewrittenFields = new ArrayList<>(fields.size());

        for (RecordLiteralNode.RecordField field : fields) {
            if (field.isKeyValueField()) {
                BLangRecordLiteral.BLangRecordKeyValueField keyValueField =
                        (BLangRecordLiteral.BLangRecordKeyValueField) field;

                BLangRecordLiteral.BLangRecordKey key = keyValueField.key;
                BLangExpression origKey = key.expr;
                BLangExpression keyExpr;
                if (key.computedKey) {
                    keyExpr = origKey;
                } else {
                    keyExpr = origKey.getKind() == NodeKind.SIMPLE_VARIABLE_REF ? createStringLiteral(pos,
                            Utils.unescapeJava(((BLangSimpleVarRef) origKey).variableName.value)) :
                            ((BLangLiteral) origKey);
                }

                BLangRecordLiteral.BLangRecordKeyValueField rewrittenField =
                        ASTBuilderUtil.createBLangRecordKeyValue(rewriteExpr(keyExpr),
                                                                 rewriteExpr(keyValueField.valueExpr));
                rewrittenField.pos = keyValueField.pos;
                rewrittenField.key.pos = key.pos;
                rewrittenFields.add(rewrittenField);
            } else if (field.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
                BLangSimpleVarRef varRefField = (BLangSimpleVarRef) field;
                rewrittenFields.add(ASTBuilderUtil.createBLangRecordKeyValue(
                        rewriteExpr(createStringLiteral(pos, Utils.unescapeJava(varRefField.variableName.value))),
                        rewriteExpr(varRefField)));
            } else {
                BLangRecordLiteral.BLangRecordSpreadOperatorField spreadOpField =
                        (BLangRecordLiteral.BLangRecordSpreadOperatorField) field;
                spreadOpField.expr = rewriteExpr(spreadOpField.expr);
                rewrittenFields.add(spreadOpField);
            }
        }

        fields.clear();
        return new BLangMapLiteral(pos, type, rewrittenFields);
    }

    protected void addTransactionInternalModuleImport() {
        if (!env.enclPkg.packageID.equals(PackageID.TRANSACTION_INTERNAL)) {
            BLangImportPackage importDcl = (BLangImportPackage) TreeBuilder.createImportPackageNode();
            List<BLangIdentifier> pkgNameComps = new ArrayList<>();
            pkgNameComps.add(ASTBuilderUtil.createIdentifier(env.enclPkg.pos, Names.TRANSACTION.value));
            importDcl.pkgNameComps = pkgNameComps;
            importDcl.pos = env.enclPkg.symbol.pos;
            importDcl.orgName = ASTBuilderUtil.createIdentifier(env.enclPkg.pos, Names.BALLERINA_INTERNAL_ORG.value);
            importDcl.alias = ASTBuilderUtil.createIdentifier(env.enclPkg.pos, "trx");
            importDcl.version = ASTBuilderUtil.createIdentifier(env.enclPkg.pos, "");
            importDcl.symbol = symTable.internalTransactionModuleSymbol;
            env.enclPkg.imports.add(importDcl);
            env.enclPkg.symbol.imports.add(importDcl.symbol);
        }
    }
}

Line commented on: if (type.getKind() != TypeKind.RECORD || isSpreadingAnOpenRecord(userSpecifiedFields)) {

Revised code:
private BLangBlockStmt desugarForeachToWhileWithIterator(BLangForeach foreach, BLangSimpleVariableDef varDef) {

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        
        

        

        BVarSymbol iteratorSymbol = varDef.var.symbol;

        
        BVarSymbol resultSymbol = new BVarSymbol(0, Names.fromString("$result$"), this.env.scope.owner.pkgID,
                                                 foreach.nillableResultType, this.env.scope.owner, foreach.pos,
                                                 VIRTUAL);

        
        BLangSimpleVariableDef resultVariableDefinition = getIteratorNextVariableDefinition(foreach.pos,
                foreach.nillableResultType, iteratorSymbol, resultSymbol);

        
        BLangSimpleVarRef resultReferenceInWhile = ASTBuilderUtil.createVariableRef(foreach.pos, resultSymbol);
        BLangStatementExpression statementExpression = ASTBuilderUtil.createStatementExpression(
                resultVariableDefinition, resultReferenceInWhile);
        statementExpression.setBType(foreach.nillableResultType);

        
        BLangType userDefineType = getUserDefineTypeNode(foreach.resultType);
        BLangTypeTestExpr typeTestExpr = ASTBuilderUtil
                .createTypeTestExpr(foreach.pos, statementExpression, userDefineType);
        
        BLangWhile whileNode = (BLangWhile) TreeBuilder.createWhileNode();
        whileNode.pos = foreach.pos;
        whileNode.expr = typeTestExpr;
        whileNode.body = foreach.body;

        VariableDefinitionNode variableDefinitionNode = foreach.variableDefinitionNode;

        
        
        
        BLangFieldBasedAccess valueAccessExpr = getValueAccessExpression(foreach.pos, foreach.varType, resultSymbol);

        BLangExpression expr = valueAccessExpr.expr;
        
        
        valueAccessExpr.expr = types.addConversionExprIfRequired(expr, symTable.mapAllType);
        variableDefinitionNode.getVariable()
                .setInitialExpression(types.addConversionExprIfRequired(valueAccessExpr, foreach.varType));
        whileNode.body.stmts.add(0, (BLangStatement) variableDefinitionNode);

        
        BLangBlockStmt blockNode = ASTBuilderUtil.createBlockStmt(foreach.pos);

        
        blockNode.addStatement(varDef);

        
        blockNode.addStatement(whileNode);
        return blockNode;
    }

    private BLangType getUserDefineTypeNode(BType type) {
        BLangUserDefinedType recordType =
                new BLangUserDefinedType(ASTBuilderUtil.createIdentifier(null, ""),
                                         ASTBuilderUtil.createIdentifier(null, ""));
        recordType.setBType(type);
        return recordType;
    }

    @Override
    public void visit(BLangWhile whileNode) {
        if (whileNode.onFailClause != null) {
            BLangOnFailClause onFailClause = whileNode.onFailClause;
            whileNode.onFailClause = null;
            whileNode.body.failureBreakMode = BLangBlockStmt.FailureBreakMode.NOT_BREAKABLE;
            BLangDo doStmt = wrapStatementWithinDo(whileNode.pos, whileNode, onFailClause);
            result = rewrite(doStmt, env);
        } else {
            whileNode.expr = rewriteExpr(whileNode.expr);
            whileNode.body = rewrite(whileNode.body, env);
            result = whileNode;
        }
    }

    private BLangDo wrapStatementWithinDo(Location location, BLangStatement statement,
                                          BLangOnFailClause onFailClause) {
        BLangDo bLDo = (BLangDo) TreeBuilder.createDoNode();
        BLangBlockStmt doBlock = ASTBuilderUtil.createBlockStmt(location);
        doBlock.scope = new Scope(env.scope.owner);
        bLDo.body = doBlock;
        bLDo.pos = location;
        bLDo.onFailClause = onFailClause;
        bLDo.body.failureBreakMode = BLangBlockStmt.FailureBreakMode.BREAK_TO_OUTER_BLOCK;
        doBlock.stmts.add(statement);
        return bLDo;
    }

    @Override
    public void visit(BLangLock lockNode) {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        BLangOnFailClause currentOnFailClause = this.onFailClause;
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(lockNode.pos);
        if (lockNode.onFailClause != null) {
            blockStmt.failureBreakMode = BLangBlockStmt.FailureBreakMode.BREAK_TO_OUTER_BLOCK;
            rewrite(lockNode.onFailClause, env);
        }
        BLangLockStmt lockStmt = new BLangLockStmt(lockNode.pos);
        blockStmt.addStatement(lockStmt);

        enclLocks.push(lockStmt);

        BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(lockNode.pos, symTable.nilType, Names.NIL_VALUE);
        BType nillableError = BUnionType.create(null, symTable.errorType, symTable.nilType);
        BLangStatementExpression statementExpression = createStatementExpression(lockNode.body, nilLiteral);
        statementExpression.setBType(symTable.nilType);

        BLangTrapExpr trapExpr = (BLangTrapExpr) TreeBuilder.createTrapExpressionNode();
        trapExpr.setBType(nillableError);
        trapExpr.expr = statementExpression;
        BVarSymbol nillableErrorVarSymbol = new BVarSymbol(0, Names.fromString("$errorResult"),
                                                           this.env.scope.owner.pkgID, nillableError,
                                                           this.env.scope.owner, lockNode.pos, VIRTUAL);
        BLangSimpleVariable simpleVariable = ASTBuilderUtil.createVariable(lockNode.pos, "$errorResult",
                                                                           nillableError, trapExpr,
                                                                           nillableErrorVarSymbol);
        BLangSimpleVariableDef simpleVariableDef = ASTBuilderUtil.createVariableDef(lockNode.pos, simpleVariable);
        blockStmt.addStatement(simpleVariableDef);

        BLangUnLockStmt unLockStmt = new BLangUnLockStmt(lockNode.pos);
        unLockStmt.relatedLock = lockStmt; 
        blockStmt.addStatement(unLockStmt);
        BLangSimpleVarRef varRef = ASTBuilderUtil.createVariableRef(lockNode.pos, nillableErrorVarSymbol);

        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(lockNode.pos);
        BLangPanic panicNode = (BLangPanic) TreeBuilder.createPanicNode();
        panicNode.pos = lockNode.pos;
        panicNode.expr = types.addConversionExprIfRequired(varRef, symTable.errorType);
        ifBody.addStatement(panicNode);

        BLangTypeTestExpr isErrorTest =
                ASTBuilderUtil.createTypeTestExpr(lockNode.pos, varRef, getErrorTypeNode());
        isErrorTest.setBType(symTable.booleanType);

        BLangIf ifelse = ASTBuilderUtil.createIfElseStmt(lockNode.pos, isErrorTest, ifBody, null);
        blockStmt.addStatement(ifelse);
        result = rewrite(blockStmt, env);
        enclLocks.pop();
        this.onFailClause = currentOnFailClause;
    }

    @Override
    public void visit(BLangLockStmt lockStmt) {
        result = lockStmt;
    }

    @Override
    public void visit(BLangUnLockStmt unLockStmt) {
        result = unLockStmt;
    }


    private BLangOnFailClause createTrxInternalOnFail(Location pos, BLangSimpleVarRef shouldPanicRef,
                                                      BLangSimpleVarRef shouldRetryRef) {
        BLangOnFailClause trxOnFailClause = (BLangOnFailClause) TreeBuilder.createOnFailClauseNode();
        trxOnFailClause.pos = pos;
        trxOnFailClause.body = ASTBuilderUtil.createBlockStmt(pos);
        trxOnFailClause.body.scope = new Scope(env.scope.owner);
        trxOnFailClause.isInternal = true;

        
        
        BVarSymbol trxOnFailErrorSym = new BVarSymbol(0, Names.fromString("$trxError$"),
                env.scope.owner.pkgID, symTable.errorType, env.scope.owner, pos, VIRTUAL);
        BLangSimpleVariable trxOnFailError = ASTBuilderUtil.createVariable(pos,
                "$trxError$", symTable.errorType, null, trxOnFailErrorSym);
        trxOnFailClause.variableDefinitionNode = ASTBuilderUtil.createVariableDef(pos,
                trxOnFailError);
        trxOnFailClause.body.scope.define(trxOnFailErrorSym.name, trxOnFailErrorSym);

        
        
        
        
        transactionDesugar.createRollbackIfFailed(pos, trxOnFailClause.body, trxOnFailErrorSym,
                trxBlockId, shouldRetryRef);

        BLangGroupExpr shouldNotPanic = new BLangGroupExpr();
        shouldNotPanic.setBType(symTable.booleanType);
        shouldNotPanic.expression = createNotBinaryExpression(pos, shouldPanicRef);

        BLangSimpleVarRef caughtError =  ASTBuilderUtil.createVariableRef(pos, trxOnFailErrorSym);

        BLangBlockStmt failBlock = ASTBuilderUtil.createBlockStmt(pos);

        BLangPanic panicNode = (BLangPanic) TreeBuilder.createPanicNode();
        panicNode.pos = pos;
        panicNode.expr = caughtError;

        
        
        
        
        
        BLangIf exitIf = ASTBuilderUtil.createIfElseStmt(pos, shouldNotPanic, failBlock, panicNode);
        trxOnFailClause.body.stmts.add(exitIf);

        BLangFail failStmt = (BLangFail) TreeBuilder.createFailNode();
        failStmt.pos = pos;
        failStmt.expr = caughtError;
        failBlock.stmts.add(failStmt);
        trxOnFailClause.bodyContainsFail = true;

        
        
        
        
        
        
        
        
        
        
        
        
        return trxOnFailClause;
    }

    @Override
    public void visit(BLangTransaction transactionNode) {
        if (transactionNode.onFailClause != null) {
            
            BLangOnFailClause onFailClause = transactionNode.onFailClause;
            transactionNode.onFailClause = null;
            transactionNode.transactionBody.failureBreakMode = BLangBlockStmt.FailureBreakMode.NOT_BREAKABLE;
            BLangDo doStmt = wrapStatementWithinDo(transactionNode.pos, transactionNode, onFailClause);
            
            
            
            
            
            
            
            
            result = rewrite(doStmt, env);
        } else {
            BLangLiteral currentTrxBlockId = this.trxBlockId;
            String uniqueId = String.valueOf(++transactionBlockCount);
            this.trxBlockId = ASTBuilderUtil.createLiteral(transactionNode.pos, symTable.stringType, uniqueId);
            boolean currShouldReturnErrors = this.shouldReturnErrors;
            this.shouldReturnErrors = true;

            BLangOnFailClause currOnFailClause = this.onFailClause;

            
            BLangLiteral falseLiteral = ASTBuilderUtil.createLiteral(transactionNode.pos, symTable.booleanType, false);
            BVarSymbol shouldPanicVarSymbol = new BVarSymbol(0, Names.fromString("$shouldPanic$"),
                    env.scope.owner.pkgID, symTable.booleanType, this.env.scope.owner, transactionNode.pos, VIRTUAL);
            shouldPanicVarSymbol.closure = true;
            BLangSimpleVariable shouldPanicVariable = ASTBuilderUtil.createVariable(transactionNode.pos,
                    "$shouldPanic$", symTable.booleanType, falseLiteral, shouldPanicVarSymbol);

            BLangSimpleVariableDef shouldPanicDef = ASTBuilderUtil.createVariableDef(transactionNode.pos,
                    shouldPanicVariable);
            BLangSimpleVarRef shouldPanicRef = ASTBuilderUtil.createVariableRef(transactionNode.pos,
                    shouldPanicVarSymbol);

            
            
            
            
            
            
            
            
            
            
            
            BLangOnFailClause trxInternalOnFail = createTrxInternalOnFail(transactionNode.pos, shouldPanicRef,
                    this.shouldRetryRef);
            enclosingShouldPanic.put(trxInternalOnFail, shouldPanicRef);

            boolean userDefinedOnFailAvbl = this.onFailClause != null;
            analyzeOnFailClause(trxInternalOnFail, transactionNode.transactionBody);

            BLangBlockStmt transactionStmtBlock =
                    transactionDesugar.rewrite(transactionNode, trxBlockId, env, uniqueId);

            transactionStmtBlock.stmts.add(0, shouldPanicDef);
            transactionStmtBlock.scope.define(shouldPanicVarSymbol.name, shouldPanicVarSymbol);
            transactionStmtBlock.failureBreakMode = userDefinedOnFailAvbl ?
                    BLangBlockStmt.FailureBreakMode.NOT_BREAKABLE :
                    BLangBlockStmt.FailureBreakMode.BREAK_TO_OUTER_BLOCK;

            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            result = rewrite(transactionStmtBlock, this.env);

            this.shouldReturnErrors = currShouldReturnErrors;
            this.trxBlockId = currentTrxBlockId;
            swapAndResetEnclosingOnFail(currOnFailClause);
        }
    }

    @Override
    public void visit(BLangRollback rollbackNode) {
        BLangBlockStmt rollbackStmtExpr = transactionDesugar.desugar(rollbackNode, trxBlockId, this.shouldRetryRef);
        result = rewrite(rollbackStmtExpr, env);
    }

    private BLangOnFailClause createRetryInternalOnFail(Location pos,
                                                        BLangSimpleVarRef retryResultRef,
                                                        BLangSimpleVarRef retryManagerRef,
                                                        BLangSimpleVarRef shouldRetryRef,
                                                        BLangSimpleVarRef continueLoopRef,
                                                        BLangSimpleVarRef returnResult) {
        BLangOnFailClause internalOnFail = (BLangOnFailClause) TreeBuilder.createOnFailClauseNode();
        internalOnFail.pos = pos;
        internalOnFail.body = ASTBuilderUtil.createBlockStmt(pos);
        internalOnFail.body.scope = new Scope(env.scope.owner);

        BVarSymbol caughtErrorSym = new BVarSymbol(0, Names.fromString("$caughtError$"),
                env.scope.owner.pkgID, symTable.errorType, env.scope.owner, pos, VIRTUAL);
        BLangSimpleVariable caughtError = ASTBuilderUtil.createVariable(pos,
                "$caughtError$", symTable.errorType, null, caughtErrorSym);
        internalOnFail.variableDefinitionNode = ASTBuilderUtil.createVariableDef(pos,
                caughtError);
        env.scope.define(caughtErrorSym.name, caughtErrorSym);
        BLangSimpleVarRef caughtErrorRef = ASTBuilderUtil.createVariableRef(pos, caughtErrorSym);

        
        BLangAssignment errorAssignment = ASTBuilderUtil.createAssignmentStmt(pos, retryResultRef, caughtErrorRef);
        internalOnFail.body.stmts.add(errorAssignment);

        
        BLangAssignment continueLoopTrue = ASTBuilderUtil.createAssignmentStmt(pos, continueLoopRef,
                ASTBuilderUtil.createLiteral(pos, symTable.booleanType, true));
        internalOnFail.body.stmts.add(continueLoopTrue);

        
        BLangInvocation shouldRetryInvocation = createRetryManagerShouldRetryInvocation(pos,
                retryManagerRef, caughtErrorRef);
        BLangAssignment shouldRetryAssignment = ASTBuilderUtil.createAssignmentStmt(pos, shouldRetryRef,
                shouldRetryInvocation);
        internalOnFail.body.stmts.add(shouldRetryAssignment);

        BLangGroupExpr shouldNotRetryCheck = new BLangGroupExpr();
        shouldNotRetryCheck.setBType(symTable.booleanType);
        shouldNotRetryCheck.expression = createNotBinaryExpression(pos, shouldRetryRef);

        BLangGroupExpr exitCheck = new BLangGroupExpr();
        exitCheck.setBType(symTable.booleanType);
        exitCheck.expression = shouldNotRetryCheck;

        BLangBlockStmt exitLogicBlock = ASTBuilderUtil.createBlockStmt(pos);
        BLangIf exitIf = ASTBuilderUtil.createIfElseStmt(pos, exitCheck, exitLogicBlock, null);

        if (this.onFailClause != null) {
            
            
            BLangFail failStmt = (BLangFail) TreeBuilder.createFailNode();
            failStmt.pos = pos;
            failStmt.expr = retryResultRef;

            exitLogicBlock.stmts.add(failStmt);
            internalOnFail.bodyContainsFail = true;
            internalOnFail.body.stmts.add(exitIf);

            
            BLangContinue loopContinueStmt = (BLangContinue) TreeBuilder.createContinueNode();
            loopContinueStmt.pos = pos;
            internalOnFail.body.stmts.add(loopContinueStmt);

            
            
            
            
        } else {
            BLangAssignment returnErrorTrue = ASTBuilderUtil.createAssignmentStmt(pos, returnResult,
                    ASTBuilderUtil.createLiteral(pos, symTable.booleanType, true));
            exitLogicBlock.stmts.add(returnErrorTrue);
            internalOnFail.body.stmts.add(exitIf);
            
            
            
        }
        return internalOnFail;
    }

    BLangUnaryExpr createNotBinaryExpression(Location pos, BLangExpression expression) {
        List<BType> paramTypes = new ArrayList<>();
        paramTypes.add(symTable.booleanType);
        BInvokableType type = new BInvokableType(paramTypes, symTable.booleanType,
                null);
        BOperatorSymbol notOperatorSymbol = new BOperatorSymbol(
                Names.fromString(OperatorKind.NOT.value()), symTable.rootPkgSymbol.pkgID, type, symTable.rootPkgSymbol,
                symTable.builtinPos, VIRTUAL);
        return ASTBuilderUtil.createUnaryExpr(pos, expression, symTable.booleanType,
                OperatorKind.NOT, notOperatorSymbol);
    }

    BLangLambdaFunction createLambdaFunction(Location pos, String functionNamePrefix,
                                             List<BLangSimpleVariable> lambdaFunctionVariable,
                                             TypeNode returnType, BLangFunctionBody lambdaBody) {
        BLangLambdaFunction lambdaFunction = (BLangLambdaFunction) TreeBuilder.createLambdaFunctionNode();
        BLangFunction func =
                ASTBuilderUtil.createFunction(pos, functionNamePrefix + UNDERSCORE + lambdaFunctionCount++);
        lambdaFunction.function = func;
        func.requiredParams.addAll(lambdaFunctionVariable);
        func.setReturnTypeNode(returnType);
        func.desugaredReturnType = true;
        defineFunction(func, env.enclPkg);
        lambdaFunctionVariable = func.requiredParams;

        func.body = lambdaBody;
        func.desugared = false;
        lambdaFunction.pos = pos;
        List<BType> paramTypes = new ArrayList<>();
        lambdaFunctionVariable.forEach(variable -> paramTypes.add(variable.symbol.type));
        lambdaFunction.setBType(new BInvokableType(paramTypes, func.symbol.type.getReturnType(),
                                                   null));
        return lambdaFunction;
    }

    private void defineFunction(BLangFunction funcNode, BLangPackage targetPkg) {
        final BPackageSymbol packageSymbol = targetPkg.symbol;
        final SymbolEnv packageEnv = this.symTable.pkgEnvMap.get(packageSymbol);
        symbolEnter.defineNode(funcNode, packageEnv);
        packageEnv.enclPkg.functions.add(funcNode);
        packageEnv.enclPkg.topLevelNodes.add(funcNode);
    }

    @Override
    public void visit(BLangForkJoin forkJoin) {
         result = forkJoin;
    }

    

    @Override
    public void visit(BLangLiteral literalExpr) {
        int tag = Types.getImpliedType(literalExpr.getBType()).tag;
        if (tag == TypeTags.ARRAY || tag == TypeTags.TUPLE) {
            
            result = rewriteBlobLiteral(literalExpr);
            return;
        }
        result = literalExpr;
    }

    private BLangNode rewriteBlobLiteral(BLangLiteral literalExpr) {
        byte[] values = types.convertToByteArray((String) literalExpr.value);
        BLangArrayLiteral arrayLiteralNode = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode();
        arrayLiteralNode.setBType(literalExpr.getBType());
        arrayLiteralNode.pos = literalExpr.pos;
        arrayLiteralNode.exprs = new ArrayList<>();
        for (byte b : values) {
            arrayLiteralNode.exprs.add(createByteLiteral(literalExpr.pos, b));
        }
        return arrayLiteralNode;
    }

    @Override
    public void visit(BLangListConstructorSpreadOpExpr listConstructorSpreadOpExpr) {
        listConstructorSpreadOpExpr.expr = rewriteExpr(listConstructorSpreadOpExpr.expr);
        result = listConstructorSpreadOpExpr;
    }

    @Override
    public void visit(BLangListConstructorExpr listConstructor) {
        listConstructor.exprs = rewriteExprs(listConstructor.exprs);
        BLangExpression expr;
        BType listConstructorType = Types.getImpliedType(listConstructor.getBType());
        if (listConstructorType.tag == TypeTags.TUPLE) {
            expr = new BLangTupleLiteral(listConstructor.pos, listConstructor.exprs, listConstructor.getBType());
            result = rewriteExpr(expr);
        } else if (listConstructorType.tag == TypeTags.JSON) {
            expr = new BLangJSONArrayLiteral(listConstructor.exprs, new BArrayType(listConstructor.getBType()));
            result = rewriteExpr(expr);
        } else if (getElementType(listConstructorType).tag == TypeTags.JSON) {
            expr = new BLangJSONArrayLiteral(listConstructor.exprs, listConstructor.getBType());
            result = rewriteExpr(expr);
        } else if (listConstructorType.tag == TypeTags.TYPEDESC) {
            final BLangTypedescExpr typedescExpr = new BLangTypedescExpr();
            typedescExpr.resolvedType = listConstructor.typedescType;
            typedescExpr.setBType(symTable.typeDesc);
            result = rewriteExpr(typedescExpr);
        } else {
            expr = new BLangArrayLiteral(listConstructor.pos, listConstructor.exprs, listConstructor.getBType());
            result = rewriteExpr(expr);
        }
    }

    @Override
    public void visit(BLangTableConstructorExpr tableConstructorExpr) {
        rewriteExprs(tableConstructorExpr.recordLiteralList);
        result = tableConstructorExpr;
    }

    @Override
    public void visit(BLangArrayLiteral arrayLiteral) {
        arrayLiteral.exprs = rewriteExprs(arrayLiteral.exprs);
        BType arrayLiteralType = Types.getImpliedType(arrayLiteral.getBType());
        if (arrayLiteralType.tag == TypeTags.JSON) {
            result = new BLangJSONArrayLiteral(arrayLiteral.exprs, new BArrayType(arrayLiteral.getBType()));
            return;
        } else if (getElementType(arrayLiteralType).tag == TypeTags.JSON) {
            result = new BLangJSONArrayLiteral(arrayLiteral.exprs, arrayLiteral.getBType());
            return;
        }
        result = arrayLiteral;
    }

    @Override
    public void visit(BLangTupleLiteral tupleLiteral) {
        if (tupleLiteral.isTypedescExpr) {
            final BLangTypedescExpr typedescExpr = new BLangTypedescExpr();
            typedescExpr.resolvedType = tupleLiteral.typedescType;
            typedescExpr.setBType(symTable.typeDesc);
            result = rewriteExpr(typedescExpr);
            return;
        }
        List<BLangExpression> exprs = tupleLiteral.exprs;
        BTupleType tupleType = (BTupleType) Types.getImpliedType(tupleLiteral.getBType());
        List<BType> tupleMemberTypes = tupleType.getTupleTypes();
        int tupleMemberTypeSize = tupleMemberTypes.size();
        int tupleExprSize = exprs.size();

        boolean isInRestType = false;
        int i = 0;
        for (BLangExpression expr: exprs) {
            if (expr.getKind() == NodeKind.LIST_CONSTRUCTOR_SPREAD_OP) {
                BType spreadOpType = ((BLangListConstructorSpreadOpExpr) expr).expr.getBType();
                spreadOpType = Types.getImpliedType(spreadOpType);
                if (spreadOpType.tag == TypeTags.ARRAY) {
                    BArrayType spreadOpBArray = (BArrayType) spreadOpType;
                    if (spreadOpBArray.size >= 0) {
                        i += spreadOpBArray.size;
                        continue;
                    }
                } else {
                    BTupleType spreadOpTuple = spreadOpType.tag == TypeTags.INTERSECTION ?
                            (BTupleType) ((BIntersectionType) spreadOpType).effectiveType : (BTupleType) spreadOpType;
                    if (types.isFixedLengthTuple(spreadOpTuple)) {
                        i += spreadOpTuple.getMembers().size();
                        continue;
                    }
                }
                isInRestType = true;
                continue;
            }

            BType expType = expr.impConversionExpr == null ? expr.getBType() : expr.impConversionExpr.getBType();

            BType targetType = tupleType.restType;
            if (!isInRestType && i < tupleMemberTypeSize) {
                targetType = tupleMemberTypes.get(i);
            }

            types.setImplicitCastExpr(expr, expType, targetType);
            i++;
        }

        tupleLiteral.exprs = rewriteExprs(tupleLiteral.exprs);
        result = tupleLiteral;
    }

    @Override
    public void visit(BLangGroupExpr groupExpr) {
        result = rewriteExpr(groupExpr.expression);
    }

    @Override
    public void visit(BLangRecordLiteral recordLiteral) {
        List<RecordLiteralNode.RecordField> fields =  recordLiteral.fields;
        generateFieldsForUserUnspecifiedRecordFields(recordLiteral, fields);
        fields.sort((v1, v2) -> Boolean.compare(isComputedKey(v1), isComputedKey(v2)));
        result = rewriteExpr(rewriteMappingConstructor(recordLiteral));
    }

    private SymbolEnv findEnclosingInvokableEnv(SymbolEnv env, BLangInvokableNode encInvokable) {
        if (env.enclEnv.node != null && env.enclEnv.node.getKind() == NodeKind.ARROW_EXPR) {
            return env.enclEnv;
        }

        if (env.enclEnv.node != null && (env.enclEnv.node.getKind() == NodeKind.ON_FAIL)) {
            return env.enclEnv;
        }

        if (env.enclInvokable != null && env.enclInvokable == encInvokable) {
            return findEnclosingInvokableEnv(env.enclEnv, encInvokable);
        }
        return env;
    }

    private void updateClosureVariable(BVarSymbol varSymbol, BLangInvokableNode encInvokable, Location pos) {
        if (!varSymbol.closure) {
            SymbolEnv encInvokableEnv = findEnclosingInvokableEnv(env, encInvokable);
            BSymbol resolvedSymbol = symResolver.lookupClosureVarSymbol(encInvokableEnv, varSymbol);
            if (resolvedSymbol != symTable.notFoundSymbol) {
                varSymbol.closure = true;
                ((BLangFunction) encInvokable).closureVarSymbols.add(new ClosureVarSymbol(varSymbol, pos));
            }
        }
    }

    private List<String> getNamesOfUserSpecifiedRecordFields(List<RecordLiteralNode.RecordField> userSpecifiedFields) {
        List<String> fieldNames = new ArrayList<>();

        for (RecordLiteralNode.RecordField field : userSpecifiedFields) {
            if (field.isKeyValueField()) {
                BLangExpression key = ((BLangRecordLiteral.BLangRecordKeyValueField) field).key.expr;
                if (key.getKind() == NodeKind.LITERAL) {
                    fieldNames.add(Utils.unescapeBallerina(((BLangLiteral) key).value.toString()));
                } else if (key.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
                    fieldNames.add(Utils.unescapeBallerina(((BLangSimpleVarRef) key).variableName.value));
                }
            } else if (field.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
                fieldNames.add(Utils.unescapeBallerina(((BLangSimpleVarRef) field).variableName.value));
            } else {
                addRequiredFieldsFromSpreadOperator(field, fieldNames);
            }
        }

        return fieldNames;
    }

    private void addRequiredFieldsFromSpreadOperator(RecordLiteralNode.RecordField field, List<String> fieldNames) {
        BLangRecordLiteral.BLangRecordSpreadOperatorField spreadOpField =
                (BLangRecordLiteral.BLangRecordSpreadOperatorField) field;
        BType type = Types.getReferredType(spreadOpField.expr.getBType());
        if (type.tag != TypeTags.RECORD) {
            return;
        }
        for (BField bField : ((BRecordType) type).fields.values()) {
            fieldNames.add(Utils.unescapeBallerina(bField.name.value));
        }
    }

    private void generateFieldsForUserUnspecifiedRecordFields(List<RecordLiteralNode.RecordField> fields,
                                                              List<String> fieldNames,
                                                              Map<String, BInvokableSymbol> defaultValues,
                                                              Location pos, boolean isReadonly) {
        for (Map.Entry<String, BInvokableSymbol> entry : defaultValues.entrySet()) {
            String fieldName = entry.getKey();
            if (fieldNames.contains(fieldName)) {
                continue;
            }
            fieldNames.add(fieldName);
            BInvokableSymbol invokableSymbol = entry.getValue();
            BLangExpression expression = getFunctionPointerInvocation(invokableSymbol);

            if (isReadonly && !Symbols.isFlagOn(invokableSymbol.retType.flags, Flags.READONLY)) {
                expression = visitCloneReadonly(expression, invokableSymbol.retType);
            }
            if (env.enclInvokable != null) {
                BLangInvocation invocation = (BLangInvocation) expression;
                if (invocation.expr.getKind() == NodeKind.INVOCATION) {
                    updateClosureVariable((BVarSymbol) ((BLangInvocation) invocation.expr).symbol, env.enclInvokable,
                                                        pos);
                } else {
                    updateClosureVariable((BVarSymbol) invocation.symbol, env.enclInvokable, pos);
                }
            }
            BLangRecordLiteral.BLangRecordKeyValueField member = createRecordKeyValueField(pos, fieldName, expression);
            fields.add(member);
        }
    }

    private BLangRecordLiteral.BLangRecordKeyValueField createRecordKeyValueField(Location pos,
                                                                                  String fieldName,
                                                                                  BLangExpression expression) {
        BLangRecordLiteral.BLangRecordKeyValueField member = new BLangRecordLiteral.BLangRecordKeyValueField();
        member.key = new BLangRecordLiteral.BLangRecordKey(ASTBuilderUtil.createLiteral(pos, symTable.stringType,
                    Utils.unescapeJava(fieldName)));
        member.valueExpr = types.addConversionExprIfRequired(expression, expression.getBType());
        return member;
    }

    public void generateFieldsForUserUnspecifiedRecordFields(BLangRecordLiteral recordLiteral,
                                                             List<RecordLiteralNode.RecordField> userSpecifiedFields) {
        BType type = Types.getImpliedType(recordLiteral.getBType());
        
        
        
        if (type.getKind() != TypeKind.RECORD || isSpreadingAnOpenRecord(userSpecifiedFields)) {
            return;
        }
        List<String> fieldNames = getNamesOfUserSpecifiedRecordFields(userSpecifiedFields);
        Location pos = recordLiteral.pos;
        BRecordType recordType = (BRecordType) type;
        boolean isReadonly = Symbols.isFlagOn(recordType.flags, Flags.READONLY);
        generateFieldsForUserUnspecifiedRecordFields(recordType, userSpecifiedFields, fieldNames, pos, isReadonly);
    }

    private boolean isSpreadingAnOpenRecord(List<RecordLiteralNode.RecordField> userSpecifiedFields) {
        for (RecordLiteralNode.RecordField field : userSpecifiedFields) {
            if (!(field instanceof BLangRecordLiteral.BLangRecordSpreadOperatorField spreadOperatorField)) {
                continue;
            }
            BType type = Types.getReferredType(spreadOperatorField.expr.getBType());
            if (!(type instanceof BRecordType recordType)) {
                return true;
            }
            if (recordType.restFieldType != null &&
                    !types.isNeverTypeOrStructureTypeWithARequiredNeverMember(recordType.restFieldType)) {
                return true;
            }
        }
        return false;
    }

    private void generateFieldsForUserUnspecifiedRecordFields(BRecordType recordType,
                                                              List<RecordLiteralNode.RecordField> fields,
                                                              List<String> fieldNames, Location pos,
                                                              boolean isReadonly) {
        Map<String, BInvokableSymbol> defaultValues = ((BRecordTypeSymbol) recordType.tsymbol).defaultValues;
        generateFieldsForUserUnspecifiedRecordFields(fields, fieldNames, defaultValues, pos, isReadonly);
        List<BType> typeInclusions = recordType.typeInclusions;
        for (BType typeInclusion : typeInclusions) {
            generateFieldsForUserUnspecifiedRecordFields((BRecordType) Types.getImpliedType(typeInclusion), fields,
                                                         fieldNames, pos, isReadonly);
        }
    }

    @Override
    public void visit(BLangSimpleVarRef varRefExpr) {
        BLangSimpleVarRef genVarRefExpr = varRefExpr;

        
        if (varRefExpr.pkgSymbol != null && varRefExpr.pkgSymbol.tag == SymTag.XMLNS) {
            BLangXMLQName qnameExpr = new BLangXMLQName(varRefExpr.variableName);
            qnameExpr.nsSymbol = (BXMLNSSymbol) varRefExpr.pkgSymbol;
            qnameExpr.localname = varRefExpr.variableName;
            qnameExpr.prefix = varRefExpr.pkgAlias;
            qnameExpr.namespaceURI = qnameExpr.nsSymbol.namespaceURI;
            qnameExpr.isUsedInXML = false;
            qnameExpr.pos = varRefExpr.pos;
            qnameExpr.setBType(symTable.stringType);
            result = qnameExpr;
            return;
        }

        if (varRefExpr.symbol == null) {
            result = varRefExpr;
            return;
        }

        
        if ((varRefExpr.symbol.tag & SymTag.VARIABLE) == SymTag.VARIABLE) {
            BVarSymbol varSymbol = (BVarSymbol) varRefExpr.symbol;
            if (varSymbol.originalSymbol != null) {
                varRefExpr.symbol = varSymbol.originalSymbol;
                if (varSymbol.closure) {
                    varRefExpr.symbol.closure = true;
                }
            }
        }

        BType type = varRefExpr.getBType();

        BSymbol ownerSymbol = varRefExpr.symbol.owner;
        if ((varRefExpr.symbol.tag & SymTag.FUNCTION) == SymTag.FUNCTION &&
                Types.getImpliedType(varRefExpr.symbol.type).tag == TypeTags.INVOKABLE) {
            genVarRefExpr = new BLangFunctionVarRef((BVarSymbol) varRefExpr.symbol);
        } else if ((varRefExpr.symbol.tag & SymTag.TYPE) == SymTag.TYPE &&
                !((varRefExpr.symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT)) {
            genVarRefExpr = new BLangTypeLoad(varRefExpr.symbol);
        } else if ((ownerSymbol.tag & SymTag.INVOKABLE) == SymTag.INVOKABLE ||
                (ownerSymbol.tag & SymTag.FUNCTION_TYPE) == SymTag.FUNCTION_TYPE ||
                (ownerSymbol.tag & SymTag.LET) == SymTag.LET) {
            
            genVarRefExpr = new BLangLocalVarRef((BVarSymbol) varRefExpr.symbol);
        } else if ((ownerSymbol.tag & SymTag.STRUCT) == SymTag.STRUCT) {
            genVarRefExpr = new BLangFieldVarRef((BVarSymbol) varRefExpr.symbol);
        } else if ((ownerSymbol.tag & SymTag.PACKAGE) == SymTag.PACKAGE ||
                (ownerSymbol.tag & SymTag.SERVICE) == SymTag.SERVICE) {

            
            
            if ((varRefExpr.symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT) {
                BConstantSymbol constSymbol = (BConstantSymbol) varRefExpr.symbol;
                BType referredType = Types.getImpliedType(constSymbol.literalType);
                if (referredType.tag <= TypeTags.BOOLEAN || referredType.tag == TypeTags.NIL) {
                    BLangLiteral literal = ASTBuilderUtil.createLiteral(varRefExpr.pos, constSymbol.literalType,
                            constSymbol.value.value);
                    result = rewriteExpr(types.addConversionExprIfRequired(literal, varRefExpr.getBType()));
                    return;
                }
            }

            
            
            genVarRefExpr = new BLangPackageVarRef((BVarSymbol) varRefExpr.symbol);

            if (!enclLocks.isEmpty()) {
                BVarSymbol symbol = (BVarSymbol) varRefExpr.symbol;
                BLangLockStmt lockStmt = enclLocks.peek();
                lockStmt.addLockVariable(symbol);
                lockStmt.addLockVariable(this.globalVariablesDependsOn.getOrDefault(symbol, new HashSet<>()));
            }
        }

        genVarRefExpr.setBType(type);
        genVarRefExpr.pos = varRefExpr.pos;

        if ((varRefExpr.isLValue)
                || genVarRefExpr.symbol.name.equals(IGNORE)) { 
            genVarRefExpr.isLValue = varRefExpr.isLValue;
            genVarRefExpr.setBType(varRefExpr.symbol.type);
            result = genVarRefExpr;
            return;
        }

        
        
        genVarRefExpr.isLValue = varRefExpr.isLValue;
        BType targetType = genVarRefExpr.getBType();
        genVarRefExpr.setBType(genVarRefExpr.symbol.type);
        BLangExpression expression = types.addConversionExprIfRequired(genVarRefExpr, targetType);
        result = expression.impConversionExpr != null ? expression.impConversionExpr : expression;
    }

    @Override
    public void visit(BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess nsPrefixedFieldBasedAccess) {
        rewriteFieldBasedAccess(nsPrefixedFieldBasedAccess);
    }

    private void rewriteFieldBasedAccess(BLangFieldBasedAccess fieldAccessExpr) {
        if (safeNavigate(fieldAccessExpr)) {
            result = rewriteExpr(rewriteSafeNavigationExpr(fieldAccessExpr));
            return;
        }

        BLangAccessExpression targetVarRef = fieldAccessExpr;

        
        
        BType varRefType = types.getTypeWithEffectiveIntersectionTypes(fieldAccessExpr.expr.getBType());
        fieldAccessExpr.expr = rewriteExpr(fieldAccessExpr.expr);
        if (!types.isSameType(fieldAccessExpr.expr.getBType(), varRefType)) {
            fieldAccessExpr.expr = types.addConversionExprIfRequired(fieldAccessExpr.expr, varRefType);
        }

        BLangLiteral stringLit = createStringLiteral(fieldAccessExpr.field.pos,
                Utils.unescapeJava(fieldAccessExpr.field.value));
        BType refType = Types.getImpliedType(varRefType);
        int varRefTypeTag = refType.tag;
        if (varRefTypeTag == TypeTags.OBJECT ||
                (varRefTypeTag == TypeTags.UNION &&
                        Types.getImpliedType(
                                ((BUnionType) refType).getMemberTypes().iterator().next()).tag == TypeTags.OBJECT)) {
            if (fieldAccessExpr.symbol != null &&
                    Types.getImpliedType(fieldAccessExpr.symbol.type).tag == TypeTags.INVOKABLE &&
                    ((fieldAccessExpr.symbol.flags & Flags.ATTACHED) == Flags.ATTACHED)) {
                result = rewriteObjectMemberAccessAsField(fieldAccessExpr);
                return;
            } else {
                boolean isStoreOnCreation = fieldAccessExpr.isStoreOnCreation;

                if (!isStoreOnCreation && varRefTypeTag == TypeTags.OBJECT && env.enclInvokable != null) {
                    BInvokableSymbol originalFuncSymbol = ((BLangFunction) env.enclInvokable).originalFuncSymbol;
                    BObjectTypeSymbol objectTypeSymbol = (BObjectTypeSymbol) refType.tsymbol;
                    BAttachedFunction initializerFunc = objectTypeSymbol.initializerFunc;
                    BAttachedFunction generatedInitializerFunc = objectTypeSymbol.generatedInitializerFunc;

                    if ((generatedInitializerFunc != null && originalFuncSymbol == generatedInitializerFunc.symbol) ||
                            (initializerFunc != null && originalFuncSymbol == initializerFunc.symbol)) {
                        isStoreOnCreation = true;
                    }
                }

                targetVarRef = new BLangStructFieldAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit,
                        (BVarSymbol) fieldAccessExpr.symbol, false,
                        isStoreOnCreation);
                
            }
        } else if (varRefTypeTag == TypeTags.RECORD ||
                (varRefTypeTag == TypeTags.UNION &&
                        Types.getImpliedType(
                        ((BUnionType) refType).getMemberTypes().iterator().next()).tag == TypeTags.RECORD)) {
            if (fieldAccessExpr.symbol != null &&
                    Types.getImpliedType(fieldAccessExpr.symbol.type).tag == TypeTags.INVOKABLE
                    && ((fieldAccessExpr.symbol.flags & Flags.ATTACHED) == Flags.ATTACHED)) {
                targetVarRef = new BLangStructFunctionVarRef(fieldAccessExpr.expr, (BVarSymbol) fieldAccessExpr.symbol);
            } else {
                targetVarRef = new BLangStructFieldAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit,
                        (BVarSymbol) fieldAccessExpr.symbol, false, fieldAccessExpr.isStoreOnCreation);
            }
        } else if (types.isLaxFieldAccessAllowed(refType)) {
            if (!types.isAssignable(refType, symTable.xmlType)) {
                if (varRefTypeTag == TypeTags.MAP &&
                        TypeTags.isXMLTypeTag(Types.getImpliedType(((BMapType) refType).constraint).tag)) {
                    result = rewriteExpr(rewriteLaxMapAccess(fieldAccessExpr));
                    return;
                }
                
                
                fieldAccessExpr.expr = types.addConversionExprIfRequired(fieldAccessExpr.expr, symTable.jsonType);
                targetVarRef = new BLangJSONAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit);
            } else {
                fieldAccessExpr.expr = types.addConversionExprIfRequired(fieldAccessExpr.expr, symTable.xmlType);
                BLangInvocation xmlAccessInvocation = rewriteXMLAttributeOrElemNameAccess(fieldAccessExpr);
                xmlAccessInvocation.setBType(fieldAccessExpr.getBType());
                result = xmlAccessInvocation;
                return;
            }
        } else if (varRefTypeTag == TypeTags.MAP) {
            
            targetVarRef = new BLangMapAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit,
                    fieldAccessExpr.isStoreOnCreation);
        } else if (TypeTags.isXMLTypeTag(varRefTypeTag)) {
            targetVarRef = new BLangXMLAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit,
                    fieldAccessExpr.fieldKind);
        }

        targetVarRef.isLValue = fieldAccessExpr.isLValue;
        targetVarRef.setBType(fieldAccessExpr.getBType());
        targetVarRef.optionalFieldAccess = fieldAccessExpr.optionalFieldAccess;
        result = targetVarRef;
    }

    @Override
    public void visit(BLangFieldBasedAccess fieldAccessExpr) {
        rewriteFieldBasedAccess(fieldAccessExpr);
    }

    private BLangNode rewriteObjectMemberAccessAsField(BLangFieldBasedAccess fieldAccessExpr) {
        Location pos = fieldAccessExpr.pos;
        BInvokableSymbol originalMemberFuncSymbol = (BInvokableSymbol) fieldAccessExpr.symbol;
        
        BLangFunction func = (BLangFunction) TreeBuilder.createFunctionNode();
        String funcName = "$anon$method$delegate$" + originalMemberFuncSymbol.name.value + "$" + lambdaFunctionCount++;
        BInvokableSymbol funcSymbol = new BInvokableSymbol(SymTag.INVOKABLE, (Flags.ANONYMOUS | Flags.LAMBDA),
                                                           Names.fromString(funcName), env.enclPkg.packageID,
                                                           originalMemberFuncSymbol.type, env.scope.owner, pos,
                                                           VIRTUAL);
        funcSymbol.retType = originalMemberFuncSymbol.retType;
        funcSymbol.bodyExist = true;
        funcSymbol.params = new ArrayList<>();
        funcSymbol.scope = new Scope(funcSymbol);
        func.pos = pos;
        func.name = createIdentifier(pos, funcName);
        func.flagSet.add(Flag.LAMBDA);
        func.flagSet.add(Flag.ANONYMOUS);
        func.body = (BLangBlockFunctionBody) TreeBuilder.createBlockFunctionBodyNode();
        func.symbol = funcSymbol;
        func.setBType(funcSymbol.type);
        func.closureVarSymbols = new LinkedHashSet<>();
        
        BLangExpression receiver = fieldAccessExpr.expr;
        
        
        BLangSimpleVariableDef intermediateObjDef = null;
        if (receiver.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
            BSymbol receiverSymbol = ((BLangVariableReference) receiver).symbol;
            receiverSymbol.closure = true;
            func.closureVarSymbols.add(new ClosureVarSymbol(receiverSymbol, pos));
        } else {
            BLangSimpleVariableDef varDef = createVarDef("$$temp$obj$" + annonVarCount++, receiver.getBType(),
                                                         receiver, pos);
            intermediateObjDef = varDef;
            varDef.var.symbol.closure = true;
            env.scope.define(varDef.var.symbol.name, varDef.var.symbol);
            BLangSimpleVarRef variableRef = createVariableRef(pos, varDef.var.symbol);
            func.closureVarSymbols.add(new ClosureVarSymbol(varDef.var.symbol, pos));
            receiver = variableRef;
        }

        

        ArrayList<BLangExpression> requiredArgs = new ArrayList<>();
        for (BVarSymbol param : originalMemberFuncSymbol.params) {
            BLangSimpleVariable fParam = (BLangSimpleVariable) TreeBuilder.createSimpleVariableNode();
            fParam.symbol = new BVarSymbol(0, param.name, env.enclPkg.packageID, param.type,  funcSymbol, pos,
                    VIRTUAL);
            fParam.pos = pos;
            fParam.name = createIdentifier(pos, param.name.value);
            fParam.setBType(param.type);
            func.requiredParams.add(fParam);
            funcSymbol.params.add(fParam.symbol);
            funcSymbol.scope.define(fParam.symbol.name, fParam.symbol);

            BLangSimpleVarRef paramRef = createVariableRef(pos, fParam.symbol);
            requiredArgs.add(paramRef);
        }

        ArrayList<BLangExpression> restArgs = new ArrayList<>();
        if (originalMemberFuncSymbol.restParam != null) {
            BLangSimpleVariable restParam = (BLangSimpleVariable) TreeBuilder.createSimpleVariableNode();
            func.restParam = restParam;
            BVarSymbol restSym = originalMemberFuncSymbol.restParam;
            restParam.name = ASTBuilderUtil.createIdentifier(pos, restSym.name.value);
            restParam.symbol = new BVarSymbol(0, restSym.name, env.enclPkg.packageID, restSym.type, funcSymbol, pos,
                    VIRTUAL);
            restParam.pos = pos;
            restParam.setBType(restSym.type);
            funcSymbol.restParam = restParam.symbol;
            funcSymbol.scope.define(restParam.symbol.name, restParam.symbol);

            BLangSimpleVarRef restArg = createVariableRef(pos, restParam.symbol);
            restArgs.add(createRestArgsExpression(restArg));
        }

        BLangIdentifier field = fieldAccessExpr.field;
        BLangReturn retStmt = (BLangReturn) TreeBuilder.createReturnNode();
        retStmt.expr = createObjectMethodInvocation(
                receiver, field, fieldAccessExpr.symbol, requiredArgs, restArgs);
        ((BLangBlockFunctionBody) func.body).addStatement(retStmt);

        BLangLambdaFunction lambdaFunction = (BLangLambdaFunction) TreeBuilder.createLambdaFunctionNode();
        lambdaFunction.function = func;
        lambdaFunction.capturedClosureEnv = env;
        env.enclPkg.functions.add(func);
        env.enclPkg.topLevelNodes.add(func);
        
        lambdaFunction.parent = env.enclInvokable;
        lambdaFunction.setBType(func.getBType());

        if (intermediateObjDef == null) {
            return rewrite(lambdaFunction, env);
        } else {
            BLangStatementExpression expr = createStatementExpression(intermediateObjDef, rewrite(lambdaFunction, env));
            expr.setBType(lambdaFunction.getBType());
            return rewrite(expr, env);
        }
    }

    private BLangInvocation createObjectMethodInvocation(BLangExpression receiver, BLangIdentifier field,
                                                         BSymbol invocableSymbol,
                                                         List<BLangExpression> requiredArgs,
                                                         List<BLangExpression> restArgs) {
        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        invocationNode.name = field;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        invocationNode.expr = receiver;

        invocationNode.symbol = invocableSymbol;
        invocationNode.setBType(((BInvokableType) invocableSymbol.type).retType);
        invocationNode.requiredArgs = requiredArgs;
        invocationNode.restArgs = restArgs;
        return invocationNode;
    }

    private BLangStatementExpression rewriteLaxMapAccess(BLangFieldBasedAccess fieldAccessExpr) {
        BLangStatementExpression statementExpression = new BLangStatementExpression();
        BLangBlockStmt block = new BLangBlockStmt();
        statementExpression.stmt = block;
        BUnionType fieldAccessType = BUnionType.create(null, fieldAccessExpr.getBType(), symTable.errorType);
        Location pos = fieldAccessExpr.pos;
        BLangSimpleVariableDef result = createVarDef("$mapAccessResult$", fieldAccessType, null, pos);
        block.addStatement(result);
        BLangSimpleVarRef resultRef = ASTBuilderUtil.createVariableRef(pos, result.var.symbol);
        resultRef.setBType(fieldAccessType);
        statementExpression.setBType(fieldAccessType);


        
        
        BLangLiteral mapIndex = ASTBuilderUtil.createLiteral(
                        fieldAccessExpr.field.pos, symTable.stringType, fieldAccessExpr.field.value);
        BLangMapAccessExpr mapAccessExpr = new BLangMapAccessExpr(pos, fieldAccessExpr.expr, mapIndex);
        BUnionType xmlOrNil = BUnionType.create(null, fieldAccessExpr.getBType(), symTable.nilType);
        mapAccessExpr.setBType(xmlOrNil);
        BLangSimpleVariableDef mapResult = createVarDef("$mapAccess", xmlOrNil, mapAccessExpr, pos);
        BLangSimpleVarRef mapResultRef = ASTBuilderUtil.createVariableRef(pos, mapResult.var.symbol);
        block.addStatement(mapResult);

        BLangIf ifStmt = ASTBuilderUtil.createIfStmt(pos, block);

        BLangIsLikeExpr isLikeNilExpr = createIsLikeExpression(pos, mapResultRef, symTable.nilType);

        ifStmt.expr = isLikeNilExpr;
        BLangBlockStmt resultNilBody = new BLangBlockStmt();
        ifStmt.body = resultNilBody;
        BLangBlockStmt resultHasValueBody = new BLangBlockStmt();
        ifStmt.elseStmt = resultHasValueBody;

        BLangErrorConstructorExpr errorConstructorExpr =
                (BLangErrorConstructorExpr) TreeBuilder.createErrorConstructorExpressionNode();
        BSymbol symbol = symResolver.lookupMainSpaceSymbolInPackage(errorConstructorExpr.pos, env,
                Names.fromString(""), Names.fromString("error"));
        errorConstructorExpr.setBType(symbol.type);

        List<BLangExpression> positionalArgs = new ArrayList<>();
        List<BLangNamedArgsExpression> namedArgs = new ArrayList<>();
        positionalArgs.add(createStringLiteral(pos, "{" + RuntimeConstants.MAP_LANG_LIB + "}InvalidKey"));
        BLangNamedArgsExpression message = new BLangNamedArgsExpression();
        message.name = ASTBuilderUtil.createIdentifier(pos, "key");
        message.expr = createStringLiteral(pos, fieldAccessExpr.field.value);
        namedArgs.add(message);
        errorConstructorExpr.positionalArgs = positionalArgs;
        errorConstructorExpr.namedArgs = namedArgs;

        BLangSimpleVariableDef errorDef =
                createVarDef("$_invalid_key_error", symTable.errorType, errorConstructorExpr, pos);
        resultNilBody.addStatement(errorDef);

        BLangSimpleVarRef errorRef = ASTBuilderUtil.createVariableRef(pos, errorDef.var.symbol);

        BLangAssignment errorVarAssignment = ASTBuilderUtil.createAssignmentStmt(pos, resultNilBody);
        errorVarAssignment.varRef = resultRef;
        errorVarAssignment.expr = errorRef;

        BLangAssignment mapResultAssignment = ASTBuilderUtil.createAssignmentStmt(
                pos, resultHasValueBody);
        mapResultAssignment.varRef = resultRef;
        mapResultAssignment.expr = mapResultRef;

        statementExpression.expr = resultRef;
        return statementExpression;
    }

    private BLangInvocation rewriteXMLAttributeOrElemNameAccess(BLangFieldBasedAccess fieldAccessExpr) {
        ArrayList<BLangExpression> args = new ArrayList<>();

        String fieldName = fieldAccessExpr.field.value;
        if (fieldAccessExpr.fieldKind == FieldKind.WITH_NS) {
            BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess nsPrefixAccess =
                    (BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess) fieldAccessExpr;
            fieldName = createExpandedQName(nsPrefixAccess.nsSymbol.namespaceURI, fieldName);
        }

        
        if (fieldName.equals("_")) {
            return createLanglibXMLInvocation(fieldAccessExpr.pos, XML_INTERNAL_GET_ELEMENT_NAME_NIL_LIFTING,
                    fieldAccessExpr.expr, new ArrayList<>(), new ArrayList<>());
        }

        BLangLiteral attributeNameLiteral = createStringLiteral(fieldAccessExpr.field.pos, fieldName);
        args.add(attributeNameLiteral);
        args.add(isOptionalAccessToLiteral(fieldAccessExpr));

        return createLanglibXMLInvocation(fieldAccessExpr.pos, XML_INTERNAL_GET_ATTRIBUTE, fieldAccessExpr.expr, args,
                new ArrayList<>());
    }

    private BLangExpression isOptionalAccessToLiteral(BLangFieldBasedAccess fieldAccessExpr) {
        return rewrite(
                createLiteral(fieldAccessExpr.pos, symTable.booleanType, fieldAccessExpr.isOptionalFieldAccess()), env);
    }

    private String createExpandedQName(String nsURI, String localName) {
        return "{" + nsURI + "}" + localName;
    }

    @Override
    public void visit(BLangIndexBasedAccess indexAccessExpr) {
        if (safeNavigate(indexAccessExpr)) {
            result = rewriteExpr(rewriteSafeNavigationExpr(indexAccessExpr));
            return;
        }

        BLangIndexBasedAccess targetVarRef = indexAccessExpr;
        indexAccessExpr.indexExpr = rewriteExpr(indexAccessExpr.indexExpr);

        
        
        BType effectiveType = types.getTypeWithEffectiveIntersectionTypes(indexAccessExpr.expr.getBType());
        BType varRefType = Types.getImpliedType(effectiveType);
        indexAccessExpr.expr = rewriteExpr(indexAccessExpr.expr);
        if (!types.isSameType(indexAccessExpr.expr.getBType(), varRefType)) {
            indexAccessExpr.expr = types.addConversionExprIfRequired(indexAccessExpr.expr, varRefType);
        }

        if (varRefType.tag == TypeTags.MAP) {
            targetVarRef = new BLangMapAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                                                  indexAccessExpr.indexExpr, indexAccessExpr.isStoreOnCreation);
        } else if (types.isSubTypeOfMapping(types.getSafeType(varRefType, true, false))) {
            targetVarRef = new BLangStructFieldAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                                                          indexAccessExpr.indexExpr,
                                                          (BVarSymbol) indexAccessExpr.symbol, false);
        } else if (types.isSubTypeOfList(varRefType)) {
            targetVarRef = new BLangArrayAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                                                    indexAccessExpr.indexExpr);
        } else if (types.isAssignable(varRefType, symTable.xmlType)) {
            BLangExpression indexAccessExprExpr = indexAccessExpr.expr;
            
            if (Types.getImpliedType(indexAccessExprExpr.getBType()).tag == TypeTags.UNION) {
                indexAccessExprExpr = createTypeCastExpr(indexAccessExprExpr, symTable.xmlType);
            }
            targetVarRef = new BLangXMLAccessExpr(indexAccessExpr.pos, indexAccessExprExpr, indexAccessExpr.indexExpr);
        } else if (types.isAssignable(varRefType, symTable.stringType)) {
            indexAccessExpr.expr = types.addConversionExprIfRequired(indexAccessExpr.expr, symTable.stringType);
            targetVarRef = new BLangStringAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                                                     indexAccessExpr.indexExpr);
        } else if (varRefType.tag == TypeTags.TABLE) {
            targetVarRef = new BLangTableAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr,
                    indexAccessExpr.indexExpr);
        }

        targetVarRef.isLValue = indexAccessExpr.isLValue;
        targetVarRef.setBType(indexAccessExpr.getBType());
        result = targetVarRef;
    }

    @Override
    public void visit(BLangInvocation iExpr) {
        BLangExpression invocation = rewriteInvocation(iExpr, false);
        if (invocation.getKind() == NodeKind.TYPE_CONVERSION_EXPR) {
            ((BLangTypeConversionExpr) invocation).expr =
                                        createStmtExpr((BLangInvocation) ((BLangTypeConversionExpr) invocation).expr);
            result = invocation;
        } else {
            result = createStmtExpr((BLangInvocation) invocation);
        }
    }

    
    @Override
    public void visit(BFunctionPointerInvocation invocation) {
        visitArgs(invocation);

        invocation.expr = rewriteExpr(invocation.expr);
        result = invocation;
    }

    private void visitArgs(BLangInvocation invocation) {
        
        reorderArguments(invocation);

        rewriteExprs(invocation.requiredArgs);
        fixStreamTypeCastsInInvocationParams(invocation);
        fixNonRestArgTypeCastInTypeParamInvocation(invocation);

        rewriteExprs(invocation.restArgs);
    }

    private BLangStatementExpression createStmtExpr(BLangInvocation invocation) {
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(invocation.pos);
        BType type = Types.getImpliedType(invocation.symbol.type);
        BInvokableTypeSymbol invokableTypeSymbol = (BInvokableTypeSymbol) type.tsymbol;

        if (invokableTypeSymbol == null) {
            BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, invocation);
            stmtExpr.setBType(invocation.getBType());
            return stmtExpr;
        }
        TreeMap<String, BLangExpression> arguments = new TreeMap<>();
        Map<String, BInvokableSymbol> defaultValues = invokableTypeSymbol.defaultValues;

        for (int i = 0; i < invokableTypeSymbol.params.size(); i++) {
            BLangExpression arg;
            BLangSimpleVariableDef variableDef;
            BLangSimpleVarRef simpleVarRef;
            if (invocation instanceof BLangInvocation.BLangAttachedFunctionInvocation) {
                arg = invocation.requiredArgs.get(i + 1);
            } else {
                arg = invocation.requiredArgs.get(i);
            }
            BVarSymbol param = invokableTypeSymbol.params.get(i);
            String paramName = param.name.value;
            if (arg.getKind() != NodeKind.IGNORE_EXPR) {
                if (invocation.expr == arg) {
                    arguments.put(paramName, arg);
                    continue;
                }
                if (arg.impConversionExpr != null) {
                    variableDef = createSimpleVarDef("$" + paramName + "$" + funcParamCount++, param.type, arg);
                } else {
                    variableDef = createSimpleVarDef("$" + paramName + "$" + funcParamCount++, arg.getBType(), arg);
                }
                simpleVarRef = ASTBuilderUtil.createVariableRef(invocation.pos, variableDef.var.symbol);
                simpleVarRef = rewrite(simpleVarRef, env);
                blockStmt.addStatement(variableDef);
                arguments.put(paramName, simpleVarRef);
                if (invocation instanceof BLangInvocation.BLangAttachedFunctionInvocation) {
                    invocation.requiredArgs.set(i + 1, simpleVarRef);
                } else {
                    invocation.requiredArgs.set(i, simpleVarRef);
                }
                continue;
            }

            BInvokableSymbol invokableSymbol = defaultValues.get(Utils.unescapeBallerina(paramName));
            BLangInvocation closureInvocation = getFunctionPointerInvocation(invokableSymbol);
            for (int m = 0; m < invokableSymbol.params.size(); m++) {
                String langLibFuncParam = invokableSymbol.params.get(m).name.value;
                closureInvocation.requiredArgs.add(arguments.get(langLibFuncParam));
            }
            variableDef = createVarDef("$" + paramName + "$" + funcParamCount++, closureInvocation.getBType(),
                                       closureInvocation, arg.pos);
            simpleVarRef = ASTBuilderUtil.createVariableRef(invocation.pos, variableDef.var.symbol);
            simpleVarRef = rewrite(simpleVarRef, env);
            blockStmt.addStatement(variableDef);
            arguments.put(paramName, simpleVarRef);
            if (invocation instanceof BLangInvocation.BLangAttachedFunctionInvocation) {
                invocation.requiredArgs.set(i + 1, simpleVarRef);
            } else {
                invocation.requiredArgs.set(i, simpleVarRef);
            }
        }
        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, invocation);
        stmtExpr.setBType(invocation.getBType());

        return stmtExpr;
    }

    private BLangInvocation getFunctionPointerInvocation(BInvokableSymbol symbol) {
        BLangInvocation funcInvocation = (BLangInvocation) TreeBuilder.createInvocationNode();
        funcInvocation.setBType(symbol.retType);
        funcInvocation.symbol = symbol;
        funcInvocation.name = ASTBuilderUtil.createIdentifier(symbol.pos, symbol.name.value);
        return visitFunctionPointerInvocation(funcInvocation);
    }

    @Override
    public void visit(BLangErrorConstructorExpr errorConstructorExpr) {
        if (errorConstructorExpr.positionalArgs.size() == 1) {
            errorConstructorExpr.positionalArgs.add(createNilLiteral());
        }
        errorConstructorExpr.positionalArgs.set(1,
                types.addConversionExprIfRequired(errorConstructorExpr.positionalArgs.get(1), symTable.errorType));
        rewriteExprs(errorConstructorExpr.positionalArgs);

        BLangExpression errorDetail;
        BLangRecordLiteral recordLiteral = ASTBuilderUtil.createEmptyRecordLiteral(errorConstructorExpr.pos,
                ((BErrorType) Types.getImpliedType(errorConstructorExpr.getBType())).detailType);
        if (errorConstructorExpr.namedArgs.isEmpty()) {
            errorDetail = visitCloneReadonly(rewriteExpr(recordLiteral), recordLiteral.getBType());
        } else {
            for (BLangNamedArgsExpression namedArg : errorConstructorExpr.namedArgs) {
                BLangRecordLiteral.BLangRecordKeyValueField member = new BLangRecordLiteral.BLangRecordKeyValueField();
                member.key = new BLangRecordLiteral.BLangRecordKey(ASTBuilderUtil.createLiteral(namedArg.name.pos,
                        symTable.stringType, Utils.unescapeJava(namedArg.name.value)));

                if (Types.getImpliedType(recordLiteral.getBType()).tag == TypeTags.RECORD) {
                    member.valueExpr = types.addConversionExprIfRequired(namedArg.expr, symTable.anyType);
                } else {
                    member.valueExpr = types.addConversionExprIfRequired(namedArg.expr, namedArg.expr.getBType());
                }
                recordLiteral.fields.add(member);
            }
            errorDetail = visitCloneReadonly(rewriteExpr(recordLiteral),
                    ((BErrorType) Types.getImpliedType(errorConstructorExpr.getBType())).detailType);
        }
        errorConstructorExpr.errorDetail = errorDetail;
        result = errorConstructorExpr;
    }

    @Override
    public void visit(BLangInvocation.BLangActionInvocation actionInvocation) {
        if (!actionInvocation.async && actionInvocation.invokedInsideTransaction) {
            transactionDesugar.startTransactionCoordinatorOnce(env, actionInvocation.pos);
        }

        
        if (!actionInvocation.functionPointerInvocation && actionInvocation.async &&
                Symbols.isFlagOn(actionInvocation.symbol.type.flags, Flags.ISOLATED)) {
            addStrandAnnotationWithThreadAny(actionInvocation.pos);
            actionInvocation.addAnnotationAttachment(this.strandAnnotAttachement);
            ((BInvokableSymbol) actionInvocation.symbol)
                    .addAnnotation(this.strandAnnotAttachement.annotationAttachmentSymbol);
        }

        BLangExpression invocation = rewriteInvocation(actionInvocation, actionInvocation.async);
        if (invocation.getKind() == NodeKind.TYPE_CONVERSION_EXPR) {
            ((BLangTypeConversionExpr) invocation).expr =
                                        createStmtExpr((BLangInvocation) ((BLangTypeConversionExpr) invocation).expr);
            result = invocation;
        } else {
            result = createStmtExpr((BLangInvocation) invocation);
        }
    }

    private void addStrandAnnotationWithThreadAny(Location pos) {
        if (this.strandAnnotAttachement == null) {
            
            this.strandAnnotAttachement = annotationDesugar.createStrandAnnotationWithThreadAny(pos, env);
        }
    }

    @Override
    public void visit(BLangInvocation.BLangResourceAccessInvocation resourceAccessInvocation) {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        if (resourceAccessInvocation.invokedInsideTransaction) {
            transactionDesugar.startTransactionCoordinatorOnce(env, resourceAccessInvocation.pos);
        }

        
        BLangInvocation pathParamInvocation = createInvocationForPathParams(resourceAccessInvocation);
        reorderArguments(pathParamInvocation);

        BResourceFunction targetResourceFunc = resourceAccessInvocation.targetResourceFunc;
        List<BResourcePathSegmentSymbol> pathSegmentSymbols = targetResourceFunc.pathSegmentSymbols;

        int pathParamInvocationRequiredArgCount = pathParamInvocation.requiredArgs.size();

        BLangInvocation bLangInvocation = new BLangInvocation();

        
        
        
        
        
        
        
        
        BLangStatementExpression firstRequiredArgFromRestArg = null;
        boolean isFirstRequiredArgFromRestArgIncluded = false;
        for (int i = 0; i < pathParamInvocationRequiredArgCount; i++) {
            BLangExpression requiredArg = pathParamInvocation.requiredArgs.get(i);
            
            Name resourcePathName = pathSegmentSymbols.get(i).name;
            if (firstRequiredArgFromRestArg == null && requiredArg.getKind() == NodeKind.STATEMENT_EXPRESSION) {
                firstRequiredArgFromRestArg = (BLangStatementExpression) requiredArg;
                if (resourcePathName.value.equals("^")) {
                    isFirstRequiredArgFromRestArgIncluded = true;
                    bLangInvocation.requiredArgs.add(requiredArg);
                    continue;
                }
            }

            if (resourcePathName.value.equals("^")) {
                if (firstRequiredArgFromRestArg != null && !isFirstRequiredArgFromRestArgIncluded) {
                    BLangStatementExpression statementExpression = new BLangStatementExpression();
                    statementExpression.expr = requiredArg;
                    statementExpression.stmt = firstRequiredArgFromRestArg.stmt;
                    statementExpression.setBType(requiredArg.getBType());
                    bLangInvocation.requiredArgs.add(statementExpression);
                    isFirstRequiredArgFromRestArgIncluded = true;
                } else {
                    bLangInvocation.requiredArgs.add(requiredArg);
                }
            }
        }

        Name lastResourcePathName = pathSegmentSymbols.get(pathSegmentSymbols.size() - 1).name;
        if (lastResourcePathName.value.equals("^^")) {
            
            for (BLangExpression restArg : pathParamInvocation.restArgs) {
                if (firstRequiredArgFromRestArg != null && !isFirstRequiredArgFromRestArgIncluded &&
                        restArg.getKind() == NodeKind.STATEMENT_EXPRESSION) {
                    BLangStatementExpression restArgStmtExpr = (BLangStatementExpression) restArg;
                    ((BLangBlockStmt) restArgStmtExpr.stmt).stmts.add(0,
                            ((BLangBlockStmt) firstRequiredArgFromRestArg.stmt).stmts.get(0));
                }
                bLangInvocation.requiredArgs.add(restArg);
            }
        }

        bLangInvocation.requiredArgs.addAll(resourceAccessInvocation.requiredArgs);
        bLangInvocation.pkgAlias = resourceAccessInvocation.pkgAlias;
        bLangInvocation.name = resourceAccessInvocation.name;
        bLangInvocation.expr = resourceAccessInvocation.expr;
        bLangInvocation.restArgs = resourceAccessInvocation.restArgs;
        bLangInvocation.symbol = resourceAccessInvocation.symbol;
        bLangInvocation.setBType(resourceAccessInvocation.getBType());
        bLangInvocation.parent = resourceAccessInvocation.parent;
        bLangInvocation.pos = resourceAccessInvocation.pos;
        result = rewriteExpr(bLangInvocation);
    }

    private BLangInvocation createInvocationForPathParams(
            BLangInvocation.BLangResourceAccessInvocation resourceAccessInvocation) {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        BLangInvocation bLangInvocation = new BLangInvocation();

        BInvokableSymbol invokableSymbol = new BInvokableSymbol(
                resourceAccessInvocation.symbol.tag,
                resourceAccessInvocation.symbol.flags,
                resourceAccessInvocation.symbol.name,
                resourceAccessInvocation.symbol.pkgID,
                resourceAccessInvocation.symbol.type,
                resourceAccessInvocation.symbol,
                resourceAccessInvocation.symbol.pos, VIRTUAL);

        BResourceFunction targetResourceFunc = resourceAccessInvocation.targetResourceFunc;
        List<BResourcePathSegmentSymbol> pathSegmentSymbols = targetResourceFunc.pathSegmentSymbols;
        List<BLangExpression> resourceAccessPathSegments = resourceAccessInvocation.resourceAccessPathSegments.exprs;

        List<BVarSymbol> invocationParams = new ArrayList<>(pathSegmentSymbols.size());

        int pathSegmentCount = pathSegmentSymbols.size();
        BResourcePathSegmentSymbol lastPathSegmentSym = pathSegmentSymbols.get(pathSegmentSymbols.size() - 1);
        if (lastPathSegmentSym.kind == SymbolKind.RESOURCE_PATH_REST_PARAM_SEGMENT) {
            invokableSymbol.restParam = new BVarSymbol(0, Names.EMPTY, this.env.scope.owner.pkgID,
                    new BArrayType(lastPathSegmentSym.type), this.env.scope.owner, lastPathSegmentSym.pos, VIRTUAL);
            pathSegmentCount--;
        }

        if (pathSegmentCount > 0 && lastPathSegmentSym.kind != SymbolKind.RESOURCE_ROOT_PATH_SEGMENT) {
            invocationParams.addAll(pathSegmentSymbols.subList(0, pathSegmentCount).stream()
                    .map(s -> new BVarSymbol(0, Names.EMPTY, this.env.scope.owner.pkgID, s.type,
                            this.env.scope.owner, s.pos, VIRTUAL)).toList());
        }

        invokableSymbol.params = invocationParams;

        bLangInvocation.symbol = invokableSymbol;

        for (int i = 0;  i < resourceAccessPathSegments.size(); i++) {
            BLangExpression resourceAccessPathSeg = resourceAccessPathSegments.get(i);
            if (resourceAccessPathSeg.getKind() == NodeKind.LIST_CONSTRUCTOR_SPREAD_OP) {
                bLangInvocation.restArgs.add(createRestArgsExpression(
                        ((BLangListConstructorSpreadOpExpr) resourceAccessPathSeg).expr));
            } else if (i > invocationParams.size() - 1) {
                bLangInvocation.restArgs.add(resourceAccessPathSeg);
            } else {
                bLangInvocation.requiredArgs.add(resourceAccessPathSeg);
            }
        }

        return bLangInvocation;
    }

    private BLangRestArgsExpression createRestArgsExpression(BLangExpression expr) {
        BLangRestArgsExpression bLangRestArgsExpression = new BLangRestArgsExpression();
        bLangRestArgsExpression.expr = expr;
        bLangRestArgsExpression.pos = expr.pos;
        bLangRestArgsExpression.setBType(expr.getBType());
        bLangRestArgsExpression.expectedType = bLangRestArgsExpression.getBType();
        return bLangRestArgsExpression;
    }

    private BLangExpression rewriteInvocation(BLangInvocation invocation, boolean async) {
        BLangInvocation invRef = invocation;

        if (!enclLocks.isEmpty()) {
            BLangLockStmt lock = enclLocks.peek();
            lock.lockVariables.addAll(((BInvokableSymbol) invocation.symbol).dependentGlobalVars);
        }

        visitArgs(invocation);

        annotationDesugar.defineStatementAnnotations(invocation.annAttachments, invocation.pos,
                                                     invocation.symbol.pkgID, invocation.symbol.owner, env);

        if (invocation.functionPointerInvocation) {
            return visitFunctionPointerInvocation(invocation);
        }
        result = invRef;

        BInvokableSymbol invSym = (BInvokableSymbol) invocation.symbol;
        if (Symbols.isFlagOn(invSym.retType.flags, Flags.PARAMETERIZED)) {
            BType retType = unifier.build(invSym.retType);
            invocation.setBType(invocation.async ? new BFutureType(TypeTags.FUTURE, retType, null) : retType);
        }

        if (invocation.expr == null) {
            BLangExpression expression = fixTypeCastInTypeParamInvocation(invocation, invRef);
            if (invocation.exprSymbol == null) {
                return expression;
            }
            invocation.expr = ASTBuilderUtil.createVariableRef(invocation.pos, invocation.exprSymbol);
            invocation.expr = rewriteExpr(invocation.expr);
        }
        switch (Types.getImpliedType(invocation.expr.getBType()).tag) {
            case TypeTags.OBJECT:
            case TypeTags.RECORD:
            case TypeTags.UNION:
                if (!invocation.langLibInvocation) {
                    invocation.expr = rewriteExpr(invocation.expr);
                    List<BLangExpression> argExprs = new ArrayList<>(invocation.requiredArgs);
                    argExprs.add(0, invocation.expr);
                    BLangAttachedFunctionInvocation attachedFunctionInvocation =
                            new BLangAttachedFunctionInvocation(invocation.pos, argExprs, invocation.restArgs,
                                                                invocation.symbol, invocation.getBType(),
                                                                invocation.expr, async);
                    attachedFunctionInvocation.name = invocation.name;
                    attachedFunctionInvocation.annAttachments = invocation.annAttachments;
                    invRef = attachedFunctionInvocation;
                }
                break;
        }
        populateOCEInvocation(invocation, invRef);
        return fixTypeCastInTypeParamInvocation(invocation, invRef);
    }

    private void populateOCEInvocation(BLangInvocation invocation,
                                       BLangInvocation invRef) {
        if (invocation.objectInitMethod && Symbols.isFlagOn(invocation.expr.getBType().flags, Flags.OBJECT_CTOR)) {
            BObjectType initializingObject = (BObjectType) invocation.expr.getBType();
            BLangClassDefinition classDef = initializingObject.classDef;
            if (classDef.hasClosureVars) {
                OCEDynamicEnvironmentData oceEnvData = initializingObject.classDef.oceEnvData;
                if (oceEnvData.attachedFunctionInvocation == null) {
                    oceEnvData.attachedFunctionInvocation = (BLangAttachedFunctionInvocation) invRef;
                }
            }
        }
    }

    private void fixNonRestArgTypeCastInTypeParamInvocation(BLangInvocation iExpr) {
        if (!iExpr.langLibInvocation) {
            return;
        }

        List<BLangExpression> requiredArgs = iExpr.requiredArgs;

        List<BVarSymbol> params = ((BInvokableSymbol) iExpr.symbol).params;

        for (int i = 0; i < requiredArgs.size(); i++) {
            requiredArgs.set(i, types.addConversionExprIfRequired(requiredArgs.get(i), params.get(i).type));
        }
    }

    /* This function is a workaround and need improvement
    *  Notes for improvement :
    *    1. Both arguments are same.
    *    2. Due to current type param logic we put type param flag on the original type.
    *    3. Error type having Cloneable type with type param flag, change expression type by this code.
    *    4. using error type is a problem as Cloneable type is an typeparm eg: ExprBodiedFunctionTest
    *       added never to CloneableType type param
    *       @typeParam type
    *       CloneableType Cloneable|never;
    *
    */
    private BLangExpression fixTypeCastInTypeParamInvocation(BLangInvocation iExpr, BLangInvocation genIExpr) {
        var returnTypeOfInvokable = ((BInvokableSymbol) iExpr.symbol).retType;
        if (!iExpr.langLibInvocation && !TypeParamAnalyzer.containsTypeParam(returnTypeOfInvokable)) {
            return genIExpr;
        }

        
        BType originalInvType = genIExpr.getBType();
        if (!genIExpr.async) {
            genIExpr.setBType(returnTypeOfInvokable);
        }
        return types.addConversionExprIfRequired(genIExpr, originalInvType);
    }

    private void fixStreamTypeCastsInInvocationParams(BLangInvocation iExpr) {
        List<BLangExpression> requiredArgs = iExpr.requiredArgs;
        List<BVarSymbol> params = ((BInvokableSymbol) iExpr.symbol).params;
        if (!params.isEmpty()) {
            for (int i = 0; i < requiredArgs.size(); i++) {
                BVarSymbol param = params.get(i);
                if (Types.getImpliedType(param.type).tag == TypeTags.STREAM) {
                    requiredArgs.set(i, types.addConversionExprIfRequired(requiredArgs.get(i), param.type));
                }
            }
        }
    }

    private BLangLiteral createNilLiteral() {
        BLangLiteral literal = (BLangLiteral) TreeBuilder.createLiteralExpression();
        literal.value = null;
        literal.setBType(symTable.nilType);
        return literal;
    }

    @Override
    public void visit(BLangTypeInit typeInitExpr) {
        if (Types.getImpliedType(typeInitExpr.getBType()).tag == TypeTags.STREAM) {
            result = rewriteExpr(desugarStreamTypeInit(typeInitExpr));
        } else {
            result = rewrite(desugarObjectTypeInit(typeInitExpr), env);
        }
    }

    private BLangStatementExpression desugarObjectTypeInit(BLangTypeInit typeInitExpr) {
        typeInitExpr.desugared = true;
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(typeInitExpr.pos);

        
        BLangInvocation initInvocation = (BLangInvocation) typeInitExpr.initInvocation;
        initInvocation.objectInitMethod = true;
        BType objType = getObjectType(typeInitExpr.getBType());
        BLangSimpleVariableDef objVarDef = createVarDef("$obj$", objType, typeInitExpr, typeInitExpr.pos);
        
        BLangSimpleVariableDef initInvRetValVarDef = createVarDef("$temp$", initInvocation.getBType(),
                                                                  initInvocation, initInvocation.pos);
        objVarDef.var.name.pos = symTable.builtinPos;
        BLangSimpleVarRef objVarRef = ASTBuilderUtil.createVariableRef(typeInitExpr.pos, objVarDef.var.symbol);
        BLangSimpleVarRef objInitVarRef = ASTBuilderUtil.createVariableRef(initInvocation.pos,
                                                                           initInvRetValVarDef.var.symbol);
        blockStmt.addStatement(objVarDef);
        blockStmt.addStatement(initInvRetValVarDef);
        initInvocation.exprSymbol = objVarDef.var.symbol;
        initInvocation.symbol =
                ((BObjectTypeSymbol) Types.getImpliedType(objType).tsymbol).generatedInitializerFunc.symbol;

        
        if (initInvocation.getBType().tag == TypeTags.NIL) {
            initInvocation.name.value = GENERATED_INIT_SUFFIX.value;
            BLangNode parent = initInvocation.parent;
            if (parent != null && parent.getKind() == NodeKind.OBJECT_CTOR_EXPRESSION) {
                BLangObjectConstructorExpression oceExpression = (BLangObjectConstructorExpression) parent;
                OCEDynamicEnvironmentData oceData = oceExpression.classNode.oceEnvData;
                oceData.initInvocation = typeInitExpr.initInvocation;
            }
            typeInitExpr.initInvocation = objInitVarRef;
            BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, objVarRef);
            stmtExpr.setBType(objVarRef.symbol.type);
            return stmtExpr;
        }

        
        BLangSimpleVariableDef resultVarDef = createVarDef("$result$", typeInitExpr.getBType(), null, typeInitExpr.pos);
        blockStmt.addStatement(resultVarDef);

        
        
        
        
        

        
        BLangSimpleVarRef initRetValVarRefInCondition =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, initInvRetValVarDef.var.symbol);
        BLangBlockStmt thenStmt = ASTBuilderUtil.createBlockStmt(symTable.builtinPos);
        BLangTypeTestExpr isErrorTest =
                ASTBuilderUtil.createTypeTestExpr(symTable.builtinPos, initRetValVarRefInCondition, getErrorTypeNode());
        isErrorTest.setBType(symTable.booleanType);

        
        BLangSimpleVarRef thenInitRetValVarRef =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, initInvRetValVarDef.var.symbol);
        BLangSimpleVarRef thenResultVarRef =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, resultVarDef.var.symbol);
        BLangAssignment errAssignment =
                ASTBuilderUtil.createAssignmentStmt(symTable.builtinPos, thenResultVarRef, thenInitRetValVarRef);
        thenStmt.addStatement(errAssignment);

        
        BLangSimpleVarRef elseResultVarRef =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, resultVarDef.var.symbol);
        BLangAssignment objAssignment =
                ASTBuilderUtil.createAssignmentStmt(symTable.builtinPos, elseResultVarRef, objVarRef);
        BLangBlockStmt elseStmt = ASTBuilderUtil.createBlockStmt(symTable.builtinPos);
        elseStmt.addStatement(objAssignment);

        BLangIf ifelse = ASTBuilderUtil.createIfElseStmt(symTable.builtinPos, isErrorTest, thenStmt, elseStmt);
        blockStmt.addStatement(ifelse);

        BLangSimpleVarRef resultVarRef =
                ASTBuilderUtil.createVariableRef(symTable.builtinPos, resultVarDef.var.symbol);
        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(resultVarRef.symbol.type);
        return stmtExpr;
    }

    private BLangInvocation desugarStreamTypeInit(BLangTypeInit typeInitExpr) {
        BInvokableSymbol symbol = (BInvokableSymbol) symTable.langInternalModuleSymbol.scope
                .lookup(Names.CONSTRUCT_STREAM).symbol;

        BStreamType referredStreamType = (BStreamType) Types.getImpliedType(typeInitExpr.getBType());
        BType constraintType = referredStreamType.constraint;
        BType constraintTdType = new BTypedescType(constraintType, symTable.typeDesc.tsymbol);
        BLangTypedescExpr constraintTdExpr = new BLangTypedescExpr();
        constraintTdExpr.resolvedType = constraintType;
        constraintTdExpr.setBType(constraintTdType);

        BType completionType = referredStreamType.completionType;
        BType completionTdType = new BTypedescType(completionType, symTable.typeDesc.tsymbol);
        BLangTypedescExpr completionTdExpr = new BLangTypedescExpr();
        completionTdExpr.resolvedType = completionType;
        completionTdExpr.setBType(completionTdType);

        List<BLangExpression> args = new ArrayList<>(Lists.of(constraintTdExpr, completionTdExpr));
        if (!typeInitExpr.argsExpr.isEmpty()) {
            args.add(typeInitExpr.argsExpr.get(0));
        }
        BLangInvocation streamConstructInvocation = ASTBuilderUtil.createInvocationExprForMethod(
                typeInitExpr.pos, symbol, args, symResolver);
        streamConstructInvocation.setBType(new BStreamType(TypeTags.STREAM, constraintType, completionType, null));
        return streamConstructInvocation;
    }

    private BLangSimpleVariableDef createSimpleVarDef(String name, BType type, BLangExpression expr) {
        BVarSymbol varSymbol = new BVarSymbol(0, Names.fromString(name), this.env.scope.owner.pkgID, type,
                                              this.env.scope.owner, expr.pos, VIRTUAL);
        BLangSimpleVariable simpleVariable = ASTBuilderUtil.createVariable(expr.pos, name, type, expr, varSymbol);
        BLangSimpleVariableDef simpleVariableDef = ASTBuilderUtil.createVariableDef(expr.pos);
        simpleVariableDef.var = simpleVariable;
        simpleVariableDef.setBType(simpleVariable.getBType());
        return simpleVariableDef;
    }

    private BLangSimpleVariableDef createVarDef(String name, BType type, BLangExpression expr, Location location) {
        BSymbol objSym = symResolver.lookupSymbolInMainSpace(env, Names.fromString(name));
        
        if (objSym == null || objSym == symTable.notFoundSymbol) {
            objSym = new BVarSymbol(0, Names.fromString(name), this.env.scope.owner.pkgID, type,
                                    this.env.scope.owner, location, VIRTUAL);
        }
        BLangSimpleVariable objVar = ASTBuilderUtil.createVariable(location, name, type, expr, (BVarSymbol) objSym);
        BLangSimpleVariableDef objVarDef = ASTBuilderUtil.createVariableDef(location);
        objVarDef.var = objVar;
        objVarDef.setBType(objVar.getBType());
        return objVarDef;
    }

    private BType getObjectType(BType bType) {
        BType type = Types.getImpliedType(bType);
        if (type.tag == TypeTags.OBJECT) {
            return bType;
        } else if (type.tag == TypeTags.UNION) {
            return ((BUnionType) type).getMemberTypes().stream()
                    .filter(t -> Types.getImpliedType(t).tag == TypeTags.OBJECT)
                    .findFirst()
                    .orElse(symTable.noType);
        }

        throw new IllegalStateException("None object type '" + type.toString() + "' found in object init context");
    }

    BLangErrorType getErrorTypeNode() {
        BLangErrorType errorTypeNode = (BLangErrorType) TreeBuilder.createErrorTypeNode();
        errorTypeNode.setBType(symTable.errorType);
        errorTypeNode.pos = symTable.builtinPos;
        return errorTypeNode;
    }

    BLangErrorType getErrorOrNillTypeNode() {
        BLangErrorType errorTypeNode = (BLangErrorType) TreeBuilder.createErrorTypeNode();
        errorTypeNode.setBType(symTable.errorOrNilType);
        return errorTypeNode;
    }

    @Override
    public void visit(BLangTernaryExpr ternaryExpr) {
        /*
         * First desugar to if-else:
         *
         * T $result$;
         * if () {
         *    $result$ = thenExpr;
         * } else {
         *    $result$ = elseExpr;
         * }
         *
         */
        BLangSimpleVariableDef resultVarDef =
                createVarDef("$ternary_result$", ternaryExpr.getBType(), null, ternaryExpr.pos);
        BLangBlockStmt thenBody = ASTBuilderUtil.createBlockStmt(ternaryExpr.pos);
        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(ternaryExpr.pos);

        
        BLangSimpleVarRef thenResultVarRef = ASTBuilderUtil.createVariableRef(ternaryExpr.pos, resultVarDef.var.symbol);
        BLangAssignment thenAssignment =
                ASTBuilderUtil.createAssignmentStmt(ternaryExpr.pos, thenResultVarRef, ternaryExpr.thenExpr);
        thenBody.addStatement(thenAssignment);

        
        BLangSimpleVarRef elseResultVarRef = ASTBuilderUtil.createVariableRef(ternaryExpr.pos, resultVarDef.var.symbol);
        BLangAssignment elseAssignment =
                ASTBuilderUtil.createAssignmentStmt(ternaryExpr.pos, elseResultVarRef, ternaryExpr.elseExpr);
        elseBody.addStatement(elseAssignment);

        
        BLangSimpleVarRef resultVarRef = ASTBuilderUtil.createVariableRef(ternaryExpr.pos, resultVarDef.var.symbol);
        BLangIf ifElse = ASTBuilderUtil.createIfElseStmt(ternaryExpr.pos, ternaryExpr.expr, thenBody, elseBody);

        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(ternaryExpr.pos, Lists.of(resultVarDef, ifElse));
        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(ternaryExpr.getBType());

        result = rewriteExpr(stmtExpr);
    }

    @Override
    public void visit(BLangWaitExpr waitExpr) {
        
        if (waitExpr.getExpression().getKind() == NodeKind.BINARY_EXPR) {
            waitExpr.exprList = collectAllBinaryExprs((BLangBinaryExpr) waitExpr.getExpression(), new ArrayList<>());
        } else { 
            waitExpr.exprList = Collections.singletonList(rewriteExpr(waitExpr.getExpression()));
        }
        result = waitExpr;
    }

    private List<BLangExpression> collectAllBinaryExprs(BLangBinaryExpr binaryExpr, List<BLangExpression> exprs) {
        visitBinaryExprOfWait(binaryExpr.lhsExpr, exprs);
        visitBinaryExprOfWait(binaryExpr.rhsExpr, exprs);
        return exprs;
    }

    private void visitBinaryExprOfWait(BLangExpression expr, List<BLangExpression> exprs) {
        if (expr.getKind() == NodeKind.BINARY_EXPR) {
            collectAllBinaryExprs((BLangBinaryExpr) expr, exprs);
        } else {
            expr = rewriteExpr(expr);
            exprs.add(expr);
        }
    }

    @Override
    public void visit(BLangWaitForAllExpr waitExpr) {
        waitExpr.keyValuePairs.forEach(keyValue -> {
            if (keyValue.valueExpr != null) {
                keyValue.valueExpr = rewriteExpr(keyValue.valueExpr);
            } else {
                keyValue.keyExpr = rewriteExpr(keyValue.keyExpr);
            }
        });
        BLangExpression expr = new BLangWaitForAllExpr.BLangWaitLiteral(waitExpr.keyValuePairs, waitExpr.getBType());
        expr.pos = waitExpr.pos;
        result = rewriteExpr(expr);
    }

    @Override
    public void visit(BLangTrapExpr trapExpr) {
        trapExpr.expr = rewriteExpr(trapExpr.expr);
        if (Types.getImpliedType(trapExpr.expr.getBType()).tag != TypeTags.NIL) {
            trapExpr.expr = types.addConversionExprIfRequired(trapExpr.expr, trapExpr.getBType());
        }
        result = trapExpr;
    }

    @Override
    public void visit(BLangBinaryExpr binaryExpr) {
        if (isNullableBinaryExpr(binaryExpr)) {
            BLangStatementExpression stmtExpr = createStmtExprForNullableBinaryExpr(binaryExpr);
            result = rewrite(stmtExpr, env);
            return;
        }

        if (binaryExpr.opKind == OperatorKind.HALF_OPEN_RANGE || binaryExpr.opKind == OperatorKind.CLOSED_RANGE) {
            BLangExpression lhsExpr = binaryExpr.lhsExpr;
            BLangExpression rhsExpr = binaryExpr.rhsExpr;

            
            lhsExpr = createTypeCastExpr(lhsExpr, symTable.intType);
            rhsExpr = createTypeCastExpr(rhsExpr, symTable.intType);

            if (binaryExpr.opKind == OperatorKind.HALF_OPEN_RANGE) {
                rhsExpr = getModifiedIntRangeEndExpr(rhsExpr);
            }

            result = rewriteExpr(replaceWithIntRange(binaryExpr.pos, lhsExpr, rhsExpr));
            return;
        }

        if (binaryExpr.opKind == OperatorKind.AND || binaryExpr.opKind == OperatorKind.OR) {
            visitBinaryLogicalExpr(binaryExpr);
            return;
        }

        OperatorKind binaryOpKind = binaryExpr.opKind;

        if (binaryOpKind == OperatorKind.ADD || binaryOpKind == OperatorKind.SUB ||
                binaryOpKind == OperatorKind.MUL || binaryOpKind == OperatorKind.DIV ||
                binaryOpKind == OperatorKind.MOD || binaryOpKind == OperatorKind.BITWISE_AND ||
                binaryOpKind == OperatorKind.BITWISE_OR || binaryOpKind == OperatorKind.BITWISE_XOR) {
            checkByteTypeIncompatibleOperations(binaryExpr);
        }

        binaryExpr.lhsExpr = rewriteExpr(binaryExpr.lhsExpr);
        binaryExpr.rhsExpr = rewriteExpr(binaryExpr.rhsExpr);
        result = binaryExpr;

        int rhsExprTypeTag = Types.getImpliedType(binaryExpr.rhsExpr.getBType()).tag;
        int lhsExprTypeTag = Types.getImpliedType(binaryExpr.lhsExpr.getBType()).tag;

        
        if (rhsExprTypeTag != lhsExprTypeTag && (binaryExpr.opKind == OperatorKind.EQUAL ||
                                                         binaryExpr.opKind == OperatorKind.NOT_EQUAL ||
                                                         binaryExpr.opKind == OperatorKind.REF_EQUAL ||
                                                         binaryExpr.opKind == OperatorKind.REF_NOT_EQUAL)) {
            if (TypeTags.isIntegerTypeTag(lhsExprTypeTag) && rhsExprTypeTag == TypeTags.BYTE) {
                binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.intType);
                return;
            }

            if (lhsExprTypeTag == TypeTags.BYTE && TypeTags.isIntegerTypeTag(rhsExprTypeTag)) {
                binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.intType);
                return;
            }
        }

        boolean isBinaryShiftOperator = symResolver.isBinaryShiftOperator(binaryOpKind);
        boolean isArithmeticOperator = symResolver.isArithmeticOperator(binaryOpKind);

        
        if (lhsExprTypeTag == rhsExprTypeTag) {
            if (!isBinaryShiftOperator && !isArithmeticOperator) {
                return;
            }
            if (types.isValueType(binaryExpr.lhsExpr.getBType())) {
                return;
            }
        }

        if (binaryExpr.opKind == OperatorKind.ADD && TypeTags.isStringTypeTag(lhsExprTypeTag) &&
                (rhsExprTypeTag == TypeTags.XML || rhsExprTypeTag == TypeTags.XML_TEXT)) {
            
            binaryExpr.lhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.lhsExpr,
                    binaryExpr.lhsExpr.pos, symTable.xmlType);
            return;
        }

        if (binaryExpr.opKind == OperatorKind.ADD && TypeTags.isStringTypeTag(rhsExprTypeTag) &&
                (lhsExprTypeTag == TypeTags.XML || lhsExprTypeTag == TypeTags.XML_TEXT)) {
            
            binaryExpr.rhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.rhsExpr,
                    binaryExpr.rhsExpr.pos, symTable.xmlType);
            return;
        }

        if (symResolver.isBinaryComparisonOperator(binaryOpKind)) {
            createTypeCastExprForRelationalExpr(binaryExpr, lhsExprTypeTag, rhsExprTypeTag);
            return;
        }

        if (lhsExprTypeTag == TypeTags.DECIMAL) {
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, binaryExpr.lhsExpr.getBType());
            return;
        }

        if (rhsExprTypeTag == TypeTags.DECIMAL) {
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, binaryExpr.rhsExpr.getBType());
            return;
        }

        if (lhsExprTypeTag == TypeTags.FLOAT) {
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, binaryExpr.lhsExpr.getBType());
            return;
        }

        if (rhsExprTypeTag == TypeTags.FLOAT) {
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, binaryExpr.rhsExpr.getBType());
            return;
        }

        if (isArithmeticOperator) {
            createTypeCastExprForArithmeticExpr(binaryExpr, lhsExprTypeTag, rhsExprTypeTag);
            return;
        }

        if (isBinaryShiftOperator) {
            createTypeCastExprForBinaryShiftExpr(binaryExpr, lhsExprTypeTag, rhsExprTypeTag);
            return;
        }
    }

    private BLangStatementExpression createStmtExprForNullableBinaryExpr(BLangBinaryExpr binaryExpr) {
        /*
         * int? x = 3;
         * int? y = 5;
         * int? z = x + y;
         * Above is desugared to
         * int? $result$;
         * 
         * int? $lhsExprVar$ = x;
         * int? $rhsExprVar$ = y;
         * if (lhsVar is () or rhsVar is ()) {
         *    $result$ = ();
         * } else {
         *    $result$ = $lhsExprVar$ + $rhsExprVar$;
         * }
         * int z = $result$;
         */
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);

        BUnionType exprBType = (BUnionType) binaryExpr.getBType();
        BType nonNilType = exprBType.getMemberTypes().iterator().next();

        BType rhsType;
        BType lhsType;
        if (symResolver.isArithmeticOperator(binaryExpr.opKind)) {
            rhsType = nonNilType;
            lhsType = nonNilType;
        } else {
            
            rhsType = getBinaryExprOperandNonNilType(binaryExpr.rhsExpr.getBType());
            lhsType = getBinaryExprOperandNonNilType(binaryExpr.lhsExpr.getBType());
        }

        if (binaryExpr.lhsExpr.getBType().isNullable()) {
            binaryExpr.lhsExpr = rewriteExpr(binaryExpr.lhsExpr);
        }

        BLangSimpleVariableDef tempVarDef = createVarDef("$result$",
                binaryExpr.getBType(), null, binaryExpr.pos);
        BLangSimpleVarRef tempVarRef = ASTBuilderUtil.createVariableRef(binaryExpr.pos, tempVarDef.var.symbol);
        blockStmt.addStatement(tempVarDef);

        BLangSimpleVariableDef lhsVarDef = createVarDef("$lhsExprVar$", binaryExpr.lhsExpr.getBType(),
                binaryExpr.lhsExpr, binaryExpr.pos);
        BLangSimpleVarRef lhsVarRef = ASTBuilderUtil.createVariableRef(binaryExpr.pos, lhsVarDef.var.symbol);
        blockStmt.addStatement(lhsVarDef);

        BLangSimpleVariableDef rhsVarDef = createVarDef("$rhsExprVar$", binaryExpr.rhsExpr.getBType(),
                binaryExpr.rhsExpr, binaryExpr.pos);
        BLangSimpleVarRef rhsVarRef = ASTBuilderUtil.createVariableRef(binaryExpr.pos, rhsVarDef.var.symbol);
        blockStmt.addStatement(rhsVarDef);

        BLangTypeTestExpr typeTestExprOne = getNilTypeTestExpr(binaryExpr.pos, lhsVarRef);
        BLangTypeTestExpr typeTestExprTwo = getNilTypeTestExpr(binaryExpr.pos, rhsVarRef);

        BLangBinaryExpr ifBlockCondition = ASTBuilderUtil.createBinaryExpr(binaryExpr.pos, typeTestExprOne,
                typeTestExprTwo, symTable.booleanType, OperatorKind.OR, binaryExpr.opSymbol);

        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);
        BLangAssignment bLangAssignmentIf = ASTBuilderUtil.createAssignmentStmt(binaryExpr.pos, ifBody);
        bLangAssignmentIf.varRef = tempVarRef;
        bLangAssignmentIf.expr = createNilLiteral();

        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);
        BLangAssignment bLangAssignmentElse = ASTBuilderUtil.createAssignmentStmt(binaryExpr.pos, elseBody);
        bLangAssignmentElse.varRef = tempVarRef;

        BLangBinaryExpr newBinaryExpr = ASTBuilderUtil.createBinaryExpr(binaryExpr.pos, lhsVarRef, rhsVarRef,
                nonNilType, binaryExpr.opKind, binaryExpr.opSymbol);
        newBinaryExpr.lhsExpr = createTypeCastExpr(lhsVarRef, lhsType);
        newBinaryExpr.rhsExpr = createTypeCastExpr(rhsVarRef, rhsType);
        bLangAssignmentElse.expr = newBinaryExpr;

        BLangIf ifStatement = ASTBuilderUtil.createIfStmt(binaryExpr.pos, blockStmt);
        ifStatement.expr = ifBlockCondition;
        ifStatement.body = ifBody;
        ifStatement.elseStmt = elseBody;

        BLangStatementExpression stmtExpr = ASTBuilderUtil.createStatementExpression(blockStmt, tempVarRef);
        stmtExpr.setBType(binaryExpr.getBType());

        return stmtExpr;
    }

    BLangTypeTestExpr getNilTypeTestExpr(Location pos, BLangExpression expr) {
        return createTypeCheckExpr(pos, expr, getNillTypeNode());
    }

    private BType getBinaryExprOperandNonNilType(BType operandType) {
        return operandType.isNullable() ? types.getSafeType(operandType, true, false) : operandType;
    }

    private boolean isNullableBinaryExpr(BLangBinaryExpr binaryExpr) {
        if ((binaryExpr.lhsExpr.getBType() != null && binaryExpr.rhsExpr.getBType() != null) &&
                (binaryExpr.rhsExpr.getBType().isNullable() ||
                        binaryExpr.lhsExpr.getBType().isNullable())) {
            switch (binaryExpr.getOperatorKind()) {
                case ADD:
                case SUB:
                case MUL:
                case DIV:
                case MOD:
                case BITWISE_LEFT_SHIFT:
                case BITWISE_RIGHT_SHIFT:
                case BITWISE_UNSIGNED_RIGHT_SHIFT:
                case BITWISE_AND:
                case BITWISE_OR:
                case BITWISE_XOR:
                    return true;
            }
        }
        return false;
    }

    private void createTypeCastExprForArithmeticExpr(BLangBinaryExpr binaryExpr, int lhsExprTypeTag,
                                                     int rhsExprTypeTag) {
        if ((TypeTags.isIntegerTypeTag(lhsExprTypeTag) && TypeTags.isIntegerTypeTag(rhsExprTypeTag)) ||
                (TypeTags.isStringTypeTag(lhsExprTypeTag) && TypeTags.isStringTypeTag(rhsExprTypeTag)) ||
                (TypeTags.isXMLTypeTag(lhsExprTypeTag) && TypeTags.isXMLTypeTag(rhsExprTypeTag))) {
            return;
        }
        if (TypeTags.isXMLTypeTag(lhsExprTypeTag) && !TypeTags.isXMLTypeTag(rhsExprTypeTag)) {
            if (types.checkTypeContainString(binaryExpr.rhsExpr.getBType())) {
                binaryExpr.rhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.rhsExpr,
                        binaryExpr.rhsExpr.pos, symTable.xmlType);
                return;
            }
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.xmlType);
            return;
        }
        if (TypeTags.isXMLTypeTag(rhsExprTypeTag) && !TypeTags.isXMLTypeTag(lhsExprTypeTag)) {
            if (types.checkTypeContainString(binaryExpr.lhsExpr.getBType())) {
                binaryExpr.lhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.lhsExpr,
                        binaryExpr.rhsExpr.pos, symTable.xmlType);
                return;
            }
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.xmlType);
            return;
        }
        binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, binaryExpr.getBType());
        binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, binaryExpr.getBType());
    }

    private void createTypeCastExprForBinaryShiftExpr(BLangBinaryExpr binaryExpr, int lhsExprTypeTag,
                                                      int rhsExprTypeTag) {
        boolean isLhsIntegerType = TypeTags.isIntegerTypeTag(lhsExprTypeTag);
        boolean isRhsIntegerType = TypeTags.isIntegerTypeTag(rhsExprTypeTag);
        if (isLhsIntegerType || lhsExprTypeTag == TypeTags.BYTE) {
            if (isRhsIntegerType || rhsExprTypeTag == TypeTags.BYTE) {
                return;
            }
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.intType);
            return;
        }

        if (isRhsIntegerType || rhsExprTypeTag == TypeTags.BYTE) {
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.intType);
            return;
        }

        binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.intType);
        binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.intType);
    }

    private void createTypeCastExprForRelationalExpr(BLangBinaryExpr binaryExpr, int lhsExprTypeTag,
                                                     int rhsExprTypeTag) {
        boolean isLhsIntegerType = TypeTags.isIntegerTypeTag(lhsExprTypeTag);
        boolean isRhsIntegerType = TypeTags.isIntegerTypeTag(rhsExprTypeTag);
        BType lhsExprType = binaryExpr.lhsExpr.getBType();
        BType rhsExprType = binaryExpr.rhsExpr.getBType();

        if ((isLhsIntegerType && isRhsIntegerType) || (lhsExprTypeTag == TypeTags.BYTE &&
                rhsExprTypeTag == TypeTags.BYTE)) {
            return;
        }

        if (lhsExprTypeTag == TypeTags.DECIMAL) {
            addTypeCastForBinaryExprB(binaryExpr, lhsExprType, rhsExprType);
            return;
        }

        if (rhsExprTypeTag == TypeTags.DECIMAL) {
            addTypeCastForBinaryExprA(binaryExpr, rhsExprType, lhsExprType);
            return;
        }

        if (lhsExprTypeTag == TypeTags.FLOAT) {
            addTypeCastForBinaryExprB(binaryExpr, lhsExprType, rhsExprType);
            return;
        }

        if (rhsExprTypeTag == TypeTags.FLOAT) {
            addTypeCastForBinaryExprA(binaryExpr, rhsExprType, lhsExprType);
            return;
        }

        if (isLhsIntegerType && !isRhsIntegerType) {
            addTypeCastForBinaryExprB(binaryExpr, symTable.intType, rhsExprType);
            return;
        }

        if (!isLhsIntegerType && isRhsIntegerType) {
            addTypeCastForBinaryExprA(binaryExpr, symTable.intType, lhsExprType);
            return;
        }

        if (lhsExprTypeTag == TypeTags.BYTE || rhsExprTypeTag == TypeTags.BYTE) {
            if ((lhsExprTypeTag == TypeTags.UNION && lhsExprType.isNullable()) ||
                    (rhsExprTypeTag == TypeTags.UNION && rhsExprType.isNullable())) {
                binaryExpr.lhsExpr = addNilType(symTable.intType, binaryExpr.lhsExpr);
                binaryExpr.rhsExpr = addNilType(symTable.intType, binaryExpr.rhsExpr);
                return;
            }
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, symTable.intType);
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, symTable.intType);
            return;
        }

        boolean isLhsStringType = TypeTags.isStringTypeTag(lhsExprTypeTag);
        boolean isRhsStringType = TypeTags.isStringTypeTag(rhsExprTypeTag);

        if (isLhsStringType && isRhsStringType) {
            return;
        }

        if (isLhsStringType && !isRhsStringType) {
            addTypeCastForBinaryExprB(binaryExpr, symTable.stringType, rhsExprType);
            return;
        }

        if (!isLhsStringType && isRhsStringType) {
            addTypeCastForBinaryExprA(binaryExpr, symTable.stringType, lhsExprType);
        }
    }

    private void addTypeCastForBinaryExprA(BLangBinaryExpr binaryExpr, BType rhsExprType, BType lhsExprType) {
        if (Types.getImpliedType(lhsExprType).tag == TypeTags.UNION && lhsExprType.isNullable()) {
            binaryExpr.rhsExpr = addNilType(rhsExprType, binaryExpr.rhsExpr);
        } else {
            binaryExpr.lhsExpr = createTypeCastExpr(binaryExpr.lhsExpr, rhsExprType);
        }
    }

    private void addTypeCastForBinaryExprB(BLangBinaryExpr binaryExpr, BType lhsExprType, BType rhsExprType) {
        if (Types.getImpliedType(rhsExprType).tag == TypeTags.UNION && rhsExprType.isNullable()) {
            binaryExpr.lhsExpr = addNilType(lhsExprType, binaryExpr.lhsExpr);
        } else {
            binaryExpr.rhsExpr = createTypeCastExpr(binaryExpr.rhsExpr, lhsExprType);
        }
    }

    private BLangExpression addNilType(BType exprType, BLangExpression expr) {
        LinkedHashSet<BType> members = new LinkedHashSet<>(2);
        members.add(exprType);
        members.add(symTable.nilType);
        BUnionType unionType = new BUnionType(null, members, true, false);
        return createTypeCastExpr(expr, unionType);
    }

    private BLangInvocation replaceWithIntRange(Location location, BLangExpression lhsExpr,
                                                BLangExpression rhsExpr) {
        BInvokableSymbol symbol = (BInvokableSymbol) symTable.langInternalModuleSymbol.scope
                .lookup(Names.CREATE_INT_RANGE).symbol;
        BLangInvocation createIntRangeInvocation = ASTBuilderUtil.createInvocationExprForMethod(location, symbol,
                new ArrayList<>(Lists.of(lhsExpr, rhsExpr)), symResolver);
        createIntRangeInvocation.setBType(symTable.intRangeType);
        return createIntRangeInvocation;
    }

    private void checkByteTypeIncompatibleOperations(BLangBinaryExpr binaryExpr) {
        if (binaryExpr.expectedType == null) {
            return;
        }

        int rhsExprTypeTag = Types.getImpliedType(binaryExpr.rhsExpr.getBType()).tag;
        int lhsExprTypeTag = Types.getImpliedType(binaryExpr.lhsExpr.getBType()).tag;
        if (rhsExprTypeTag != TypeTags.BYTE && lhsExprTypeTag != TypeTags.BYTE) {
            return;
        }

        int resultTypeTag = Types.getImpliedType(binaryExpr.expectedType).tag;
        if (resultTypeTag == TypeTags.INT) {
            if (rhsExprTypeTag == TypeTags.BYTE) {
                binaryExpr.rhsExpr = types.addConversionExprIfRequired(binaryExpr.rhsExpr, symTable.intType);
            }

            if (lhsExprTypeTag == TypeTags.BYTE) {
                binaryExpr.lhsExpr = types.addConversionExprIfRequired(binaryExpr.lhsExpr, symTable.intType);
            }
        }
    }

    /**
     * This method checks whether given binary expression is related to shift operation.
     * If its true, then both lhs and rhs of the binary expression will be converted to 'int' type.
     * <p>
     * byte a = 12;
     * byte b = 34;
     * int i = 234;
     * int j = -4;
     * <p>
     * true: where binary expression's expected type is 'int'
     * int i1 = a >> b;
     * int i2 = a << b;
     * int i3 = a >> i;
     * int i4 = a << i;
     * int i5 = i >> j;
     * int i6 = i << j;
     */
    private boolean isBitwiseShiftOperation(BLangBinaryExpr binaryExpr) {
        return binaryExpr.opKind == OperatorKind.BITWISE_LEFT_SHIFT ||
                binaryExpr.opKind == OperatorKind.BITWISE_RIGHT_SHIFT ||
                binaryExpr.opKind == OperatorKind.BITWISE_UNSIGNED_RIGHT_SHIFT;
    }

    @Override
    public void visit(BLangElvisExpr elvisExpr) {
        Location pos = elvisExpr.pos;
        String resultVarName = "_$result$_";
        BType resultType = elvisExpr.getBType();
        BLangSimpleVariable resultVar =
                ASTBuilderUtil.createVariable(pos, resultVarName, resultType, null,
                        new BVarSymbol(0, Names.fromString(resultVarName),
                                this.env.scope.owner.pkgID, resultType,
                                this.env.scope.owner, pos, VIRTUAL));
        BLangSimpleVariableDef resultVarDef = ASTBuilderUtil.createVariableDef(pos, resultVar);
        resultVarDef.desugared = true;
        BLangSimpleVarRef resultVarRef = ASTBuilderUtil.createVariableRef(pos, resultVar.symbol);

        String lhsResultVarName = GEN_VAR_PREFIX.value;
        BLangSimpleVariable lhsResultVar =
                ASTBuilderUtil.createVariable(pos, lhsResultVarName, elvisExpr.lhsExpr.getBType(), elvisExpr.lhsExpr,
                        new BVarSymbol(0, Names.fromString(lhsResultVarName),
                                this.env.scope.owner.pkgID, elvisExpr.lhsExpr.getBType(),
                                this.env.scope.owner, elvisExpr.pos, VIRTUAL));
        BLangSimpleVariableDef lhsResultVarDef = ASTBuilderUtil.createVariableDef(pos, lhsResultVar);
        BLangSimpleVarRef lhsResultVarRef = ASTBuilderUtil.createVariableRef(pos, lhsResultVar.symbol);

        BLangAssignment nilAssignment = ASTBuilderUtil.createAssignmentStmt(pos, resultVarRef, elvisExpr.rhsExpr);
        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(pos);
        ifBody.addStatement(nilAssignment);

        BLangAssignment notNilAssignment = ASTBuilderUtil.createAssignmentStmt(pos, resultVarRef,
                createTypeCastExpr(lhsResultVarRef, resultType));
        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(pos);
        elseBody.addStatement(notNilAssignment);

        BLangIf ifStmt = ASTBuilderUtil.createIfElseStmt(pos, getNilTypeTestExpr(pos, lhsResultVarRef),
                ifBody, elseBody);
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(pos, new ArrayList<>() {{
            add(resultVarDef);
            add(lhsResultVarDef);
            add(ifStmt);
        }});
        BLangStatementExpression stmtExpr = ASTBuilderUtil.createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(resultType);
        result = rewriteExpr(stmtExpr);
    }

    @Override
    public void visit(BLangUnaryExpr unaryExpr) {

        if (isNullableUnaryExpr(unaryExpr)) {
            BLangStatementExpression statementExpression = createStmtExprForNilableUnaryExpr(unaryExpr);
            result = rewrite(statementExpression, env);
            return;
        }

        if (OperatorKind.BITWISE_COMPLEMENT == unaryExpr.operator) {
            
            
            rewriteBitwiseComplementOperator(unaryExpr);
            return;
        }

        
        
        if (types.isExpressionInUnaryValid(unaryExpr.expr) &&
                Types.getImpliedType(unaryExpr.expectedType).tag == TypeTags.FINITE) {
            result = rewriteExpr(Types.constructNumericLiteralFromUnaryExpr(unaryExpr));
            return;
        }

        OperatorKind opKind = unaryExpr.operator;
        if (opKind == OperatorKind.ADD || opKind == OperatorKind.SUB) {
            createTypeCastExprForUnaryPlusAndMinus(unaryExpr);
        }
        unaryExpr.expr = rewriteExpr(unaryExpr.expr);
        result = unaryExpr;
    }

    private void createTypeCastExprForUnaryPlusAndMinus(BLangUnaryExpr unaryExpr) {
        BLangExpression expr = unaryExpr.expr;
        if (TypeTags.isIntegerTypeTag(Types.getImpliedType(expr.getBType()).tag)) {
            return;
        }
        unaryExpr.expr = createTypeCastExpr(expr, unaryExpr.getBType());
    }

    /**
     * This method desugar a bitwise complement (~) unary expressions into a bitwise xor binary expression as below.
     * Example : ~a  -> a ^ -1;
     * ~ 11110011 -> 00001100
     * 11110011 ^ 11111111 -> 00001100
     *
     * @param unaryExpr the bitwise complement expression
     */
    private void rewriteBitwiseComplementOperator(BLangUnaryExpr unaryExpr) {
        final Location pos = unaryExpr.pos;
        final BLangBinaryExpr binaryExpr = (BLangBinaryExpr) TreeBuilder.createBinaryExpressionNode();
        binaryExpr.pos = pos;
        binaryExpr.opKind = OperatorKind.BITWISE_XOR;
        binaryExpr.lhsExpr = unaryExpr.expr;
        if (TypeTags.BYTE == Types.getImpliedType(unaryExpr.getBType()).tag) {
            binaryExpr.setBType(symTable.byteType);
            binaryExpr.rhsExpr = ASTBuilderUtil.createLiteral(pos, symTable.byteType, 0xffL);
            binaryExpr.opSymbol = (BOperatorSymbol) symResolver.resolveBinaryOperator(OperatorKind.BITWISE_XOR,
                    symTable.byteType, symTable.byteType);
        } else {
            binaryExpr.setBType(symTable.intType);
            binaryExpr.rhsExpr = ASTBuilderUtil.createLiteral(pos, symTable.intType, -1L);
            binaryExpr.opSymbol = (BOperatorSymbol) symResolver.resolveBinaryOperator(OperatorKind.BITWISE_XOR,
                    symTable.intType, symTable.intType);
        }
        result = rewriteExpr(binaryExpr);
    }

    private BLangStatementExpression createStmtExprForNilableUnaryExpr(BLangUnaryExpr unaryExpr) {
        /*
         * int? x = 3;
         * int? y = +x;
         *
         *
         * Above is desugared to
         * int? $result$;
         * if (x is ()) {
         *    $result$ = ();
         * } else {
         *    $result$ = +x;
         * }
         * int y = $result$
         */
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(unaryExpr.pos);

        BUnionType exprBType = (BUnionType) unaryExpr.getBType();
        BType nilLiftType = exprBType.getMemberTypes().iterator().next();

        unaryExpr.expr = rewriteExpr(unaryExpr.expr);

        BLangSimpleVariableDef tempVarDef = createVarDef("$result",
                unaryExpr.getBType(), createNilLiteral(), unaryExpr.pos);
        BLangSimpleVarRef tempVarRef = ASTBuilderUtil.createVariableRef(unaryExpr.pos, tempVarDef.var.symbol);

        blockStmt.addStatement(tempVarDef);

        BLangTypeTestExpr typeTestExpr = getNilTypeTestExpr(unaryExpr.pos, unaryExpr.expr);

        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(unaryExpr.pos);
        BLangAssignment bLangAssignmentIf = ASTBuilderUtil.createAssignmentStmt(unaryExpr.pos, ifBody);
        bLangAssignmentIf.varRef = tempVarRef;
        bLangAssignmentIf.expr = createNilLiteral();

        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(unaryExpr.pos);
        BLangAssignment bLangAssignmentElse = ASTBuilderUtil.createAssignmentStmt(unaryExpr.pos, elseBody);
        bLangAssignmentElse.varRef = tempVarRef;

        BLangExpression expr = createTypeCastExpr(unaryExpr.expr, nilLiftType);
        bLangAssignmentElse.expr = ASTBuilderUtil.createUnaryExpr(unaryExpr.pos, expr,
                                                                  nilLiftType, unaryExpr.operator, unaryExpr.opSymbol);

        BLangIf ifStatement = ASTBuilderUtil.createIfStmt(unaryExpr.pos, blockStmt);
        ifStatement.expr = typeTestExpr;
        ifStatement.body = ifBody;
        ifStatement.elseStmt = elseBody;

        BLangStatementExpression stmtExpr = ASTBuilderUtil.createStatementExpression(blockStmt, tempVarRef);
        stmtExpr.setBType(unaryExpr.getBType());

        return stmtExpr;
    }

    private boolean isNullableUnaryExpr(BLangUnaryExpr unaryExpr) {
        if (unaryExpr.getBType() != null && unaryExpr.getBType().isNullable()) {
            switch (unaryExpr.operator) {
                case ADD:
                case SUB:
                case BITWISE_COMPLEMENT:
                    return true;
            }
        }
        return false;
    }

    @Override
    public void visit(BLangTypeConversionExpr conversionExpr) {
        
        
        
        

        
        if (conversionExpr.typeNode == null && !conversionExpr.annAttachments.isEmpty()) {
            result = rewriteExpr(conversionExpr.expr);
            return;
        }

        BType targetType = conversionExpr.targetType;
        conversionExpr.typeNode = rewrite(conversionExpr.typeNode, env);

        conversionExpr.expr = rewriteExpr(conversionExpr.expr);
        result = conversionExpr;
    }

    @Override
    public void visit(BLangLambdaFunction bLangLambdaFunction) {
        bLangLambdaFunction.function = rewrite(bLangLambdaFunction.function, bLangLambdaFunction.capturedClosureEnv);
        BLangFunction function = bLangLambdaFunction.function;
        
        if (function.flagSet.contains(Flag.WORKER) && Symbols.isFlagOn(function.symbol.type.flags, Flags.ISOLATED) &&
                Symbols.isFlagOn(env.enclInvokable.symbol.flags, Flags.ISOLATED)) {
            addStrandAnnotationWithThreadAny(function.pos);
            function.addAnnotationAttachment(this.strandAnnotAttachement);
            BInvokableSymbol funcSymbol = function.symbol;
            funcSymbol.addAnnotation(this.strandAnnotAttachement.annotationAttachmentSymbol);
            funcSymbol.schedulerPolicy = SchedulerPolicy.ANY;
        }
        bLangLambdaFunction.capturedClosureEnv = null;
        result = bLangLambdaFunction;
    }

    @Override
    public void visit(BLangArrowFunction bLangArrowFunction) {
        BLangFunction bLangFunction = (BLangFunction) TreeBuilder.createFunctionNode();
        bLangFunction.setName(bLangArrowFunction.functionName);

        BLangLambdaFunction lambdaFunction = (BLangLambdaFunction) TreeBuilder.createLambdaFunctionNode();
        lambdaFunction.pos = bLangArrowFunction.pos;
        bLangFunction.addFlag(Flag.LAMBDA);
        lambdaFunction.function = bLangFunction;

        
        BLangValueType returnType = (BLangValueType) TreeBuilder.createValueTypeNode();
        returnType.setBType(bLangArrowFunction.body.expr.getBType());
        bLangFunction.setReturnTypeNode(returnType);
        bLangFunction.setBody(populateArrowExprBodyBlock(bLangArrowFunction));

        bLangArrowFunction.params.forEach(bLangFunction::addParameter);
        lambdaFunction.parent = bLangArrowFunction.parent;
        lambdaFunction.setBType(bLangArrowFunction.funcType);

        
        BLangFunction funcNode = lambdaFunction.function;
        BInvokableSymbol funcSymbol = Symbols.createFunctionSymbol(Flags.asMask(funcNode.flagSet),
                                                                   new Name(funcNode.name.value),
                                                                   new Name(funcNode.name.originalValue),
                                                                   env.enclPkg.symbol.pkgID,
                                                                   bLangArrowFunction.funcType,
                                                                   env.enclEnv.enclVarSym, true,
                                                                   bLangArrowFunction.pos, VIRTUAL);

        funcSymbol.originalName = new Name(funcNode.name.originalValue);

        SymbolEnv invokableEnv = SymbolEnv.createFunctionEnv(funcNode, funcSymbol.scope, env);
        defineInvokableSymbol(funcNode, funcSymbol, invokableEnv);

        List<BVarSymbol> paramSymbols = funcNode.requiredParams.stream().peek(varNode -> {
            Scope enclScope = invokableEnv.scope;
            varNode.symbol.kind = SymbolKind.FUNCTION;
            varNode.symbol.owner = invokableEnv.scope.owner;
            enclScope.define(varNode.symbol.name, varNode.symbol);
        }).map(varNode -> varNode.symbol).collect(Collectors.toList());

        funcSymbol.params = paramSymbols;
        funcSymbol.restParam = getRestSymbol(funcNode);
        funcSymbol.retType = funcNode.returnTypeNode.getBType();
        
        List<BType> paramTypes = paramSymbols.stream().map(paramSym -> paramSym.type).collect(Collectors.toList());
        funcNode.setBType(new BInvokableType(paramTypes, getRestType(funcSymbol), funcNode.returnTypeNode.getBType(),
                          funcSymbol.type.tsymbol));

        lambdaFunction.function.pos = bLangArrowFunction.pos;
        lambdaFunction.function.body.pos = bLangArrowFunction.pos;
        
        lambdaFunction.capturedClosureEnv = env;
        env.enclPkg.addFunction(lambdaFunction.function);
        result = rewriteExpr(lambdaFunction);
    }

    private void defineInvokableSymbol(BLangInvokableNode invokableNode, BInvokableSymbol funcSymbol,
                                       SymbolEnv invokableEnv) {
        invokableNode.symbol = funcSymbol;
        funcSymbol.scope = new Scope(funcSymbol);
        invokableEnv.scope = funcSymbol.scope;
    }

    @Override
    public void visit(BLangXMLQName xmlQName) {
        result = xmlQName;
    }

    @Override
    public void visit(BLangXMLAttribute xmlAttribute) {
        xmlAttribute.name = rewriteExpr(xmlAttribute.name);
        xmlAttribute.value = rewriteExpr(xmlAttribute.value);
        result = xmlAttribute;
    }

    @Override
    public void visit(BLangXMLElementLiteral xmlElementLiteral) {
        xmlElementLiteral.attributes = rewriteExprs(xmlElementLiteral.attributes);

        
        Iterator<BLangXMLAttribute> attributesItr = xmlElementLiteral.attributes.iterator();
        while (attributesItr.hasNext()) {
            BLangXMLAttribute attribute = attributesItr.next();
            if (!attribute.isNamespaceDeclr) {
                continue;
            }

            
            BLangXMLNS xmlns;
            if ((xmlElementLiteral.scope.owner.tag & SymTag.PACKAGE) == SymTag.PACKAGE) {
                xmlns = new BLangPackageXMLNS();
            } else {
                xmlns = new BLangLocalXMLNS();
            }
            xmlns.namespaceURI = attribute.value.concatExpr;
            xmlns.prefix = ((BLangXMLQName) attribute.name).localname;
            xmlns.symbol = attribute.symbol;

            xmlElementLiteral.inlineNamespaces.add(xmlns);
        }

        List<BLangXMLNS> prevInlineNamespaces = this.inlineXMLNamespaces;
        if (isVisitingQuery && this.inlineXMLNamespaces != null) {
            
            xmlElementLiteral.inlineNamespaces.addAll(this.inlineXMLNamespaces);
        }
        this.inlineXMLNamespaces = xmlElementLiteral.inlineNamespaces;

        xmlElementLiteral.startTagName = rewriteExpr(xmlElementLiteral.startTagName);
        xmlElementLiteral.endTagName = rewriteExpr(xmlElementLiteral.endTagName);
        xmlElementLiteral.modifiedChildren = rewriteExprs(xmlElementLiteral.modifiedChildren);

        this.inlineXMLNamespaces = prevInlineNamespaces;
        result = xmlElementLiteral;
    }

    @Override
    public void visit(BLangXMLSequenceLiteral xmlSequenceLiteral) {
        for (BLangExpression xmlItem : xmlSequenceLiteral.xmlItems) {
            rewriteExpr(xmlItem);
        }
        result = xmlSequenceLiteral;
    }

    @Override
    public void visit(BLangXMLTextLiteral xmlTextLiteral) {
        xmlTextLiteral.concatExpr = rewriteExpr(constructStringTemplateConcatExpression(xmlTextLiteral.textFragments));
        result = xmlTextLiteral;
    }

    @Override
    public void visit(BLangXMLCommentLiteral xmlCommentLiteral) {
        xmlCommentLiteral.concatExpr = rewriteExpr(
                constructStringTemplateConcatExpression(xmlCommentLiteral.textFragments));
        result = xmlCommentLiteral;
    }

    @Override
    public void visit(BLangXMLProcInsLiteral xmlProcInsLiteral) {
        xmlProcInsLiteral.target = rewriteExpr(xmlProcInsLiteral.target);
        xmlProcInsLiteral.dataConcatExpr =
                rewriteExpr(constructStringTemplateConcatExpression(xmlProcInsLiteral.dataFragments));
        result = xmlProcInsLiteral;
    }

    @Override
    public void visit(BLangXMLQuotedString xmlQuotedString) {
        xmlQuotedString.concatExpr = rewriteExpr(
                constructStringTemplateConcatExpression(xmlQuotedString.textFragments));
        result = xmlQuotedString;
    }

    @Override
    public void visit(BLangStringTemplateLiteral stringTemplateLiteral) {
        result = rewriteExpr(constructStringTemplateConcatExpression(stringTemplateLiteral.exprs));
    }

    /**
     * The raw template literal gets desugared to a type init expression. For each literal, a new object class type
     * def is generated from the object type. The type init expression creates an instance of this generated object
     * type. For example, consider the following statements:
     *      string name = "Pubudu";
     *      'object:RawTemplate rt = `Hello ${name}!`;
     *
     * The raw template literal above is desugared to:
     *      type RawTemplate$Impl$0 object {
     *          public string[] strings = ["Hello ", "!"];
     *          public (any|error)[] insertions;
     *
     *          function init((any|error)[] insertions) {
     *              self.insertions = insertions;
     *          }
     *      };
     *
     *      
     *      'object:RawTemplate rt = new RawTemplate$Impl$0([name]);
     *
     * @param rawTemplateLiteral The raw template literal to be desugared.
     */
    @Override
    public void visit(BLangRawTemplateLiteral rawTemplateLiteral) {
        Location pos = rawTemplateLiteral.pos;
        BObjectType objType = (BObjectType) Types.getImpliedType(rawTemplateLiteral.getBType());
        BLangClassDefinition objClassDef =
                desugarTemplateLiteralObjectTypedef(rawTemplateLiteral.strings, objType, pos);
        BObjectType classObjType = (BObjectType) objClassDef.getBType();

        BVarSymbol insertionsSym = classObjType.fields.get("insertions").symbol;
        BLangListConstructorExpr insertionsList = ASTBuilderUtil.createListConstructorExpr(pos, insertionsSym.type);
        insertionsList.exprs.addAll(rawTemplateLiteral.insertions);
        insertionsList.expectedType = insertionsSym.type;

        
        BLangTypeInit typeNewExpr = ASTBuilderUtil.createEmptyTypeInit(pos, classObjType);
        typeNewExpr.argsExpr.add(insertionsList);
        BLangInvocation initInvocation = (BLangInvocation) typeNewExpr.initInvocation;
        initInvocation.argExprs.add(insertionsList);
        initInvocation.requiredArgs.add(insertionsList);

        result = rewriteExpr(typeNewExpr);
    }

    /**
     * This method desugars a raw template literal object class for the provided raw template object type as follows.
     * A literal defined as 'object:RawTemplate rt = `Hello ${name}!`;
     * is desugared to,
     *      type $anonType$0 object {
     *          public string[] strings = ["Hello ", "!"];
     *          public (any|error)[] insertions;
     *
     *          function init((any|error)[] insertions) {
     *              self.insertions = insertions;
     *          }
     *      };
     * @param strings    The string portions of the literal
     * @param objectType The abstract object type for which an object class needs to be generated
     * @param pos        The diagnostic position info for the type node
     * @return Returns the generated concrete object class def
     */
    private BLangClassDefinition desugarTemplateLiteralObjectTypedef(List<BLangLiteral> strings, BObjectType objectType,
                                                                    Location pos) {
        
        BObjectTypeSymbol tSymbol = (BObjectTypeSymbol) objectType.tsymbol;
        Name objectClassName = Names.fromString(
                anonModelHelper.getNextRawTemplateTypeKey(env.enclPkg.packageID, tSymbol.name));

        BObjectTypeSymbol classTSymbol = Symbols.createClassSymbol(tSymbol.flags, objectClassName,
                                                                   env.enclPkg.packageID, null, env.enclPkg.symbol,
                                                                   pos, VIRTUAL, false);
        classTSymbol.flags |= Flags.CLASS;

        
        BObjectType objectClassType = new BObjectType(classTSymbol, classTSymbol.flags);
        objectClassType.fields = objectType.fields;
        classTSymbol.type = objectClassType;
        objectClassType.typeIdSet.add(objectType.typeIdSet);

        



        BLangClassDefinition classDef = TypeDefBuilderHelper.createClassDef(pos, classTSymbol, env);
        classDef.name = ASTBuilderUtil.createIdentifier(pos, objectClassType.tsymbol.name.value);

        
        
        BType stringsType = objectClassType.fields.get("strings").symbol.type;
        BLangListConstructorExpr stringsList = ASTBuilderUtil.createListConstructorExpr(pos, stringsType);
        stringsList.exprs.addAll(strings);
        stringsList.expectedType = stringsType;
        classDef.fields.get(0).expr = stringsList;

        
        BLangFunction userDefinedInitFunction = createUserDefinedObjectInitFn(classDef, env);
        classDef.initFunction = userDefinedInitFunction;
        env.enclPkg.functions.add(userDefinedInitFunction);
        env.enclPkg.topLevelNodes.add(userDefinedInitFunction);

        
        BLangFunction tempGeneratedInitFunction = createGeneratedInitializerFunction(classDef, env);
        tempGeneratedInitFunction.clonedEnv = SymbolEnv.createFunctionEnv(tempGeneratedInitFunction,
                                                                          tempGeneratedInitFunction.symbol.scope, env);
        this.semanticAnalyzer.analyzeNode(tempGeneratedInitFunction, env);
        classDef.generatedInitFunction = tempGeneratedInitFunction;
        env.enclPkg.functions.add(classDef.generatedInitFunction);
        env.enclPkg.topLevelNodes.add(classDef.generatedInitFunction);

        return rewrite(classDef, env);
    }

    /**
     * Creates a user-defined init() method for the provided object type node. If there are fields without default
     * values specified in the type node, this will add parameters for those fields in the init() method and assign the
     * param values to the respective fields in the method body.
     *
     * @param classDefn The object type node for which the init() method is generated
     * @param env            The symbol env for the object type node
     * @return The generated init() method
     */
    private BLangFunction createUserDefinedObjectInitFn(BLangClassDefinition classDefn, SymbolEnv env) {
        BLangFunction initFunction =
                TypeDefBuilderHelper.createInitFunctionForStructureType(classDefn.symbol, env, names,
                        Names.USER_DEFINED_INIT_SUFFIX, symTable, classDefn.getBType());
        BObjectTypeSymbol typeSymbol = ((BObjectTypeSymbol) classDefn.getBType().tsymbol);
        typeSymbol.initializerFunc = new BAttachedFunction(Names.USER_DEFINED_INIT_SUFFIX, initFunction.symbol,
                                                           (BInvokableType) initFunction.getBType(), classDefn.pos);
        classDefn.initFunction = initFunction;
        initFunction.returnTypeNode.setBType(symTable.nilType);

        BLangBlockFunctionBody initFuncBody = (BLangBlockFunctionBody) initFunction.body;
        BInvokableType initFnType = (BInvokableType) initFunction.getBType();
        for (BLangSimpleVariable field : classDefn.fields) {
            if (field.expr != null) {
                continue;
            }
            BVarSymbol fieldSym = field.symbol;
            BVarSymbol paramSym = new BVarSymbol(Flags.FINAL, fieldSym.name, this.env.scope.owner.pkgID, fieldSym.type,
                                                 initFunction.symbol, classDefn.pos, VIRTUAL);
            BLangSimpleVariable param = ASTBuilderUtil.createVariable(classDefn.pos, fieldSym.name.value,
                                                                      fieldSym.type, null, paramSym);
            param.flagSet.add(Flag.FINAL);
            initFunction.symbol.scope.define(paramSym.name, paramSym);
            initFunction.symbol.params.add(paramSym);
            initFnType.paramTypes.add(param.getBType());
            initFunction.requiredParams.add(param);

            BLangSimpleVarRef paramRef = ASTBuilderUtil.createVariableRef(initFunction.pos, paramSym);
            BLangAssignment fieldInit = createStructFieldUpdate(initFunction, paramRef, fieldSym, field.getBType(),
                                                                initFunction.receiver.symbol, field.name);
            initFuncBody.addStatement(fieldInit);
        }

        return initFunction;
    }

    @Override
    public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) {
        asyncSendExpr.expr = visitCloneInvocation(rewriteExpr(asyncSendExpr.expr), asyncSendExpr.expr.getBType());
        this.channelsWithinIfStmt.add(asyncSendExpr.getChannel());
        result = asyncSendExpr;
    }

    @Override
    public void visit(BLangWorkerSyncSendExpr syncSendExpr) {
        syncSendExpr.expr = visitCloneInvocation(rewriteExpr(syncSendExpr.expr), syncSendExpr.expr.getBType());
        this.channelsWithinIfStmt.add(syncSendExpr.getChannel());
        result = syncSendExpr;
    }

    @Override
    public void visit(BLangAlternateWorkerReceive altWorkerReceive) {
        result = altWorkerReceive;
    }

    @Override
    public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) {
        result = multipleWorkerReceive;
    }

    @Override
    public void visit(BLangWorkerReceive workerReceiveNode) {
        result = workerReceiveNode;
    }

    @Override
    public void visit(BLangWorkerFlushExpr workerFlushExpr) {
        workerFlushExpr.workerIdentifierList = workerFlushExpr.cachedWorkerSendStmts
                .stream().map(send -> send.workerIdentifier).distinct().collect(Collectors.toList());
        result = workerFlushExpr;
    }

    @Override
    public void visit(BLangTransactionalExpr transactionalExpr) {
        BInvokableSymbol isTransactionalSymbol =
                (BInvokableSymbol) transactionDesugar.getInternalTransactionModuleInvokableSymbol(IS_TRANSACTIONAL);
        result = ASTBuilderUtil
                .createInvocationExprMethod(transactionalExpr.pos, isTransactionalSymbol, Collections.emptyList(),
                        Collections.emptyList(), symResolver);
    }

    @Override
    public void visit(BLangCommitExpr commitExpr) {
        BLangStatementExpression stmtExpr = transactionDesugar.desugar(commitExpr, env);
        result = rewriteExpr(stmtExpr);
    }

    @Override
    public void visit(BLangFail failNode) {
        if (this.onFailClause != null && !this.desugarToReturn) {
            if (this.onFailClause.bodyContainsFail) {
                result = rewriteNestedOnFail(this.onFailClause, failNode);
            } else {
                result = createOnFailInvocation(onFailClause, failNode);
            }
        } else {
            BLangReturn stmt = ASTBuilderUtil.createReturnStmt(failNode.pos, rewrite(failNode.expr, env));
            stmt.desugared = true;
            result = stmt;
        }
    }

    
    

    @Override
    public void visit(BLangLocalVarRef localVarRef) {
        result = localVarRef;
    }

    @Override
    public void visit(BLangFieldVarRef fieldVarRef) {
        result = fieldVarRef;
    }

    @Override
    public void visit(BLangPackageVarRef packageVarRef) {
        result = packageVarRef;
    }

    @Override
    public void visit(BLangFunctionVarRef functionVarRef) {
        result = functionVarRef;
    }

    @Override
    public void visit(BLangStructFieldAccessExpr fieldAccessExpr) {
        result = fieldAccessExpr;
    }

    @Override
    public void visit(BLangStructFunctionVarRef functionVarRef) {
        result = functionVarRef;
    }

    @Override
    public void visit(BLangMapAccessExpr mapKeyAccessExpr) {
        result = mapKeyAccessExpr;
    }

    @Override
    public void visit(BLangArrayAccessExpr arrayIndexAccessExpr) {
        result = arrayIndexAccessExpr;
    }

    @Override
    public void visit(BLangTupleAccessExpr arrayIndexAccessExpr) {
        result = arrayIndexAccessExpr;
    }

    @Override
    public void visit(BLangTableAccessExpr tableKeyAccessExpr) {
        result = tableKeyAccessExpr;
    }

    @Override
    public void visit(BLangMapLiteral mapLiteral) {
        result = mapLiteral;
    }

    @Override
    public void visit(BLangStructLiteral structLiteral) {
        result = structLiteral;
    }

    @Override
    public void visit(BLangWaitForAllExpr.BLangWaitLiteral waitLiteral) {
        result = waitLiteral;
    }

    @Override
    public void visit(BLangXMLElementAccess xmlElementAccess) {
        
        
        xmlElementAccess.expr = rewriteExpr(xmlElementAccess.expr);

        ArrayList<BLangExpression> filters = expandFilters(xmlElementAccess.filters);

        BLangInvocation invocationNode = createLanglibXMLInvocation(xmlElementAccess.pos, XML_INTERNAL_GET_ELEMENTS,
                xmlElementAccess.expr, new ArrayList<>(), filters);
        result = rewriteExpr(invocationNode);
    }

    private ArrayList<BLangExpression> expandFilters(List<BLangXMLElementFilter> filters) {
        ArrayList<BLangExpression> args = new ArrayList<>();
        for (BLangXMLElementFilter filter : filters) {
            BSymbol nsSymbol = filter.namespaceSymbol;
            String filterName = filter.name;
            if (nsSymbol != null &&
                    !(filter.namespace.equals(XMLConstants.DEFAULT_NS_PREFIX) && filterName.equals("*"))) {
                String expandedName = createExpandedQName(((BXMLNSSymbol) nsSymbol).namespaceURI, filterName);
                args.add(createStringLiteral(filter.elemNamePos, expandedName));
            } else {
                args.add(createStringLiteral(filter.elemNamePos, filterName));
            }
        }
        return args;
    }

    private BLangInvocation createLanglibXMLInvocation(Location pos, String functionName,
                                                       BLangExpression invokeOnExpr,
                                                       ArrayList<BLangExpression> args,
                                                       ArrayList<BLangExpression> restArgs) {
        invokeOnExpr = rewriteExpr(invokeOnExpr);

        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        invocationNode.pos = pos;
        BLangIdentifier name = (BLangIdentifier) TreeBuilder.createIdentifierNode();
        name.setLiteral(false);
        name.setValue(functionName);
        name.pos = pos;
        invocationNode.name = name;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        invocationNode.expr = invokeOnExpr;

        invocationNode.symbol = symResolver.lookupLangLibMethod(symTable.xmlType, Names.fromString(functionName), env);

        ArrayList<BLangExpression> requiredArgs = new ArrayList<>();
        requiredArgs.add(invokeOnExpr);
        requiredArgs.addAll(args);
        invocationNode.requiredArgs = requiredArgs;
        invocationNode.restArgs = rewriteExprs(restArgs);

        invocationNode.setBType(invocationNode.symbol.type.getReturnType());
        invocationNode.langLibInvocation = true;
        return invocationNode;
    }

    @Override
    public void visit(BLangXMLNavigationAccess xmlNavigation) {
        xmlNavigation.expr = rewriteExpr(xmlNavigation.expr);
        xmlNavigation.childIndex = rewriteExpr(xmlNavigation.childIndex);

        ArrayList<BLangExpression> filters = expandFilters(xmlNavigation.filters);

        
        if (xmlNavigation.navAccessType == XMLNavigationAccess.NavAccessType.DESCENDANTS) {
            BLangInvocation invocationNode = createLanglibXMLInvocation(xmlNavigation.pos,
                    XML_INTERNAL_SELECT_DESCENDANTS, xmlNavigation.expr, new ArrayList<>(), filters);
            result = rewriteExpr(invocationNode);
        } else if (xmlNavigation.navAccessType == XMLNavigationAccess.NavAccessType.CHILDREN) {
            
            BLangInvocation invocationNode = createLanglibXMLInvocation(xmlNavigation.pos, XML_INTERNAL_CHILDREN,
                    xmlNavigation.expr, new ArrayList<>(), new ArrayList<>());
            result = rewriteExpr(invocationNode);
        } else {
            BLangExpression childIndexExpr;
            
            if (xmlNavigation.childIndex == null) {
                childIndexExpr = new BLangLiteral(Long.valueOf(-1), symTable.intType);
            } else {
                
                childIndexExpr = xmlNavigation.childIndex;
            }
            ArrayList<BLangExpression> args = new ArrayList<>();
            args.add(rewriteExpr(childIndexExpr));

            BLangInvocation invocationNode = createLanglibXMLInvocation(xmlNavigation.pos,
                    XML_INTERNAL_GET_FILTERED_CHILDREN_FLAT, xmlNavigation.expr, args, filters);
            result = rewriteExpr(invocationNode);
        }
    }

    @Override
    public void visit(BLangIsAssignableExpr assignableExpr) {
        assignableExpr.lhsExpr = rewriteExpr(assignableExpr.lhsExpr);
        result = assignableExpr;
    }

    @Override
    public void visit(BLangTypedescExpr typedescExpr) {
        typedescExpr.typeNode = rewrite(typedescExpr.typeNode, env);
        result = typedescExpr;
    }

    @Override
    public void visit(BLangRestArgsExpression bLangVarArgsExpression) {
        result = rewriteExpr(bLangVarArgsExpression.expr);
    }

    @Override
    public void visit(BLangNamedArgsExpression bLangNamedArgsExpression) {
        bLangNamedArgsExpression.expr = rewriteExpr(bLangNamedArgsExpression.expr);
        result = bLangNamedArgsExpression.expr;
    }

    @Override
    public void visit(BLangCheckedExpr checkedExpr) {
        visitCheckAndCheckPanicExpr(checkedExpr, false);
    }

    @Override
    public void visit(BLangCheckPanickedExpr checkedExpr) {
        visitCheckAndCheckPanicExpr(checkedExpr, true);
    }

    private void visitCheckAndCheckPanicExpr(BLangCheckedExpr checkedExpr, boolean isCheckPanic) {
        
        if (checkedExpr.isRedundantChecking) {
            result = rewriteExpr(checkedExpr.expr);
            return;
        }

        
        
        
        
        
        
        
        
        
        
        
        

        Location pos = checkedExpr.pos;
        
        String resultVarName = "_$result$_";
        BType resultType = checkedExpr.getBType();
        BLangSimpleVariable resultVar =
                ASTBuilderUtil.createVariable(pos, resultVarName, resultType, null,
                                              new BVarSymbol(0, Names.fromString(resultVarName),
                                                             this.env.scope.owner.pkgID, resultType,
                                                             this.env.scope.owner, pos, VIRTUAL));
        BLangSimpleVariableDef resultVarDef = ASTBuilderUtil.createVariableDef(pos, resultVar);
        resultVarDef.desugared = true;
        BLangSimpleVarRef resultVarRef = ASTBuilderUtil.createVariableRef(pos, resultVar.symbol);

        String checkedExprVarName = GEN_VAR_PREFIX.value;
        BType checkedExprType = checkedExpr.expr.getBType();
        BLangSimpleVariable checkedExprVar =
                ASTBuilderUtil.createVariable(pos, checkedExprVarName, checkedExprType,
                        checkedExpr.expr, new BVarSymbol(0, Names.fromString(checkedExprVarName),
                                this.env.scope.owner.pkgID, checkedExprType,
                                this.env.scope.owner, pos, VIRTUAL));
        BLangSimpleVariableDef checkedExprVarDef = ASTBuilderUtil.createVariableDef(pos, checkedExprVar);
        BLangSimpleVarRef checkedExprVarRef = ASTBuilderUtil.createVariableRef(pos, checkedExprVar.symbol);

        BLangAssignment successAssignment = ASTBuilderUtil.createAssignmentStmt(pos, resultVarRef,
                createTypeCastExpr(checkedExprVarRef, resultType));
        BLangBlockStmt ifBody = ASTBuilderUtil.createBlockStmt(pos);
        ifBody.addStatement(successAssignment);

        BLangBlockStmt elseBody = getSafeErrorAssignment(pos, checkedExprVarRef, this.env.enclInvokable.symbol,
                        checkedExpr.equivalentErrorTypeList, isCheckPanic);

        BLangValueType checkedExprTypeNode = (BLangValueType) TreeBuilder.createValueTypeNode();
        checkedExprTypeNode.setBType(resultType);
        checkedExprTypeNode.typeKind = resultType.getKind();
        BLangIf ifStmt = ASTBuilderUtil.createIfElseStmt(pos,
                createTypeCheckExpr(pos, checkedExprVarRef, checkedExprTypeNode), ifBody, elseBody);
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(pos, new ArrayList<>() {{
            add(resultVarDef);
            add(checkedExprVarDef);
            add(ifStmt);
        }});

        BLangStatementExpression stmtExpr = ASTBuilderUtil.createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(resultType);
        result = rewriteExpr(stmtExpr);
    }

    @Override
    public void visit(BLangServiceConstructorExpr serviceConstructorExpr) {
        final BLangTypeInit typeInit = ASTBuilderUtil.createEmptyTypeInit(serviceConstructorExpr.pos,
                serviceConstructorExpr.serviceNode.serviceClass.symbol.type);
        serviceConstructorExpr.serviceNode.annAttachments.forEach(attachment ->  rewrite(attachment, env));
        result = rewriteExpr(typeInit);
    }

    @Override
    public void visit(BLangObjectConstructorExpression bLangObjectConstructorExpression) {
        visit(bLangObjectConstructorExpression.classNode);
        bLangObjectConstructorExpression.classNode.annAttachments.forEach(attachment -> rewrite(attachment, env));
        result = rewriteExpr(bLangObjectConstructorExpression.typeInit);
    }

    @Override
    public void visit(BLangAnnotAccessExpr annotAccessExpr) {

        BLangBinaryExpr binaryExpr = (BLangBinaryExpr) TreeBuilder.createBinaryExpressionNode();
        binaryExpr.pos = annotAccessExpr.pos;
        binaryExpr.opKind = OperatorKind.ANNOT_ACCESS;
        binaryExpr.lhsExpr = annotAccessExpr.expr;
        binaryExpr.rhsExpr = ASTBuilderUtil.createLiteral(annotAccessExpr.pkgAlias.pos, symTable.stringType,
                annotAccessExpr.annotationSymbol.bvmAlias());
        binaryExpr.setBType(annotAccessExpr.getBType());
        binaryExpr.opSymbol = new BOperatorSymbol(Names.fromString(OperatorKind.ANNOT_ACCESS.value()), null,
                new BInvokableType(Lists.of(binaryExpr.lhsExpr.getBType(),
                        binaryExpr.rhsExpr.getBType()),
                        annotAccessExpr.getBType(), null), null,
                symTable.builtinPos, VIRTUAL);
        result = rewriteExpr(binaryExpr);
    }

    @Override
    public void visit(BLangTypeTestExpr typeTestExpr) {
        BLangExpression expr = typeTestExpr.expr;
        if (types.isValueType(expr.getBType())) {
            expr = types.addConversionExprIfRequired(expr, symTable.anyType);
        }
        if (typeTestExpr.isNegation) {
            BLangTypeTestExpr bLangTypeTestExpr = ASTBuilderUtil.createTypeTestExpr(typeTestExpr.pos,
                    typeTestExpr.expr, typeTestExpr.typeNode);
            BLangGroupExpr bLangGroupExpr = (BLangGroupExpr) TreeBuilder.createGroupExpressionNode();
            bLangGroupExpr.expression = bLangTypeTestExpr;
            bLangGroupExpr.setBType(typeTestExpr.getBType());
            BLangUnaryExpr unaryExpr = ASTBuilderUtil.createUnaryExpr(typeTestExpr.pos, bLangGroupExpr,
                    typeTestExpr.getBType(),
                    OperatorKind.NOT, null);
            result = rewriteExpr(unaryExpr);
            return;
        }
        typeTestExpr.expr = rewriteExpr(expr);
        typeTestExpr.typeNode = rewrite(typeTestExpr.typeNode, env);
        result = typeTestExpr;
    }

    @Override
    public void visit(BLangIsLikeExpr isLikeExpr) {
        isLikeExpr.expr = rewriteExpr(isLikeExpr.expr);
        result = isLikeExpr;
    }

    @Override
    public void visit(BLangStatementExpression bLangStatementExpression) {
        bLangStatementExpression.expr = rewriteExpr(bLangStatementExpression.expr);
        bLangStatementExpression.stmt = rewrite(bLangStatementExpression.stmt, env);
        result = bLangStatementExpression;
    }

    @Override
    public void visit(BLangQueryExpr queryExpr) {
        boolean prevIsVisitingQuery = this.isVisitingQuery;
        boolean prevDesugarToReturn = this.desugarToReturn;
        this.isVisitingQuery = true;
        this.desugarToReturn = true;
        BLangStatementExpression stmtExpr = queryDesugar.desugar(queryExpr, env, getVisibleXMLNSStmts(env));
        result = rewrite(stmtExpr, env);
        this.isVisitingQuery = prevIsVisitingQuery;
        this.desugarToReturn = prevDesugarToReturn;
    }

    List<BLangStatement> getVisibleXMLNSStmts(SymbolEnv env) {
        Map<Name, BXMLNSSymbol> nameBXMLNSSymbolMap = symResolver.resolveAllNamespaces(env);
        return nameBXMLNSSymbolMap.keySet().stream()
                .map(key -> this.stmtsToBePropagatedToQuery.get(key))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    @Override
    public void visit(BLangQueryAction queryAction) {
        boolean prevIsVisitingQuery = this.isVisitingQuery;
        boolean prevDesugarToReturn = this.desugarToReturn;
        this.desugarToReturn = true;
        this.isVisitingQuery = true;
        BLangStatementExpression stmtExpr = queryDesugar.desugar(queryAction, env, getVisibleXMLNSStmts(env));
        result = rewrite(stmtExpr, env);
        this.isVisitingQuery = prevIsVisitingQuery;
        this.desugarToReturn = prevDesugarToReturn;
    }

    @Override
    public void visit(BLangJSONArrayLiteral jsonArrayLiteral) {
        jsonArrayLiteral.exprs = rewriteExprs(jsonArrayLiteral.exprs);
        result = jsonArrayLiteral;
    }

    @Override
    public void visit(BLangConstant constant) {

        BConstantSymbol constSymbol = constant.symbol;
        BType refType = Types.getImpliedType(constSymbol.literalType);
        if (refType.tag <= TypeTags.BOOLEAN || refType.tag == TypeTags.NIL) {
            if (refType.tag != TypeTags.NIL && (constSymbol.value == null ||
                            constSymbol.value.value == null)) {
                throw new IllegalStateException();
            }
            BLangLiteral literal = ASTBuilderUtil.createLiteral(constant.expr.pos, constSymbol.literalType,
                    constSymbol.value.value);
            constant.expr = rewriteExpr(literal);
        } else {
            constant.expr = rewriteExpr(constant.expr);
        }
        constant.annAttachments.forEach(attachment ->  rewrite(attachment, env));
        result = constant;
    }

    @Override
    public void visit(BLangIgnoreExpr ignoreExpr) {
        result = ignoreExpr;
    }

    @Override
    public void visit(BLangDynamicArgExpr dynamicParamExpr) {
        dynamicParamExpr.conditionalArgument = rewriteExpr(dynamicParamExpr.conditionalArgument);
        dynamicParamExpr.condition = rewriteExpr(dynamicParamExpr.condition);
        result = dynamicParamExpr;
    }

    @Override
    public void visit(BLangConstRef constantRef) {
        result = ASTBuilderUtil.createLiteral(constantRef.pos, constantRef.getBType(), constantRef.value);
    }

    @Override
    public void visit(BLangRegExpTemplateLiteral regExpTemplateLiteral) {
        regExpTemplateLiteral.reDisjunction = rewriteExpr(regExpTemplateLiteral.reDisjunction);
        result = regExpTemplateLiteral;
    }

    @Override
    public void visit(BLangReDisjunction reDisjunction) {
        reDisjunction.sequenceList.forEach(this::rewriteExpr);
        result = reDisjunction;
    }

    @Override
    public void visit(BLangReSequence reSequence) {
        reSequence.termList.forEach(this::rewriteExpr);
        result = reSequence;
    }

    @Override
    public void visit(BLangReAssertion reAssertion) {
        reAssertion.assertion = rewriteExpr(reAssertion.assertion);
        result = reAssertion;
    }

    @Override
    public void visit(BLangReAtomQuantifier reAtomQuantifier) {
        BLangExpression reAtom = reAtomQuantifier.atom;
        if (symResolver.isReAtomNode(reAtom.getKind())) {
            reAtomQuantifier.atom = rewriteExpr(reAtom);
        } else {
            
            reAtomQuantifier.atom = rewriteExpr(getToStringInvocationOnExpr(reAtom));
        }

        
        if (reAtomQuantifier.quantifier == null) {
            reAtomQuantifier.quantifier = ASTBuilderUtil.createEmptyQuantifier(reAtomQuantifier.pos,
                    symTable.anydataType, symTable.stringType);
        }
        reAtomQuantifier.quantifier = rewriteExpr(reAtomQuantifier.quantifier);
        result = reAtomQuantifier;
    }

    @Override
    public void visit(BLangReAtomCharOrEscape reAtomCharOrEscape) {
        reAtomCharOrEscape.charOrEscape = rewriteExpr(reAtomCharOrEscape.charOrEscape);
        result = reAtomCharOrEscape;
    }

    @Override
    public void visit(BLangReQuantifier reQuantifier) {
        reQuantifier.quantifier = rewriteExpr(reQuantifier.quantifier);
        
        if (reQuantifier.nonGreedyChar == null) {
            reQuantifier.nonGreedyChar = ASTBuilderUtil.createLiteral(reQuantifier.pos,
                    symTable.stringType, "");
        }
        reQuantifier.nonGreedyChar = rewriteExpr(reQuantifier.nonGreedyChar);
        result = reQuantifier;
    }

    @Override
    public void visit(BLangReCharacterClass reCharacterClass) {
        reCharacterClass.characterClassStart = rewriteExpr(reCharacterClass.characterClassStart);
        
        if (reCharacterClass.negation == null) {
            reCharacterClass.negation = ASTBuilderUtil.createLiteral(reCharacterClass.pos,
                    symTable.stringType, "");
        }
        reCharacterClass.negation = rewriteExpr(reCharacterClass.negation);
        
        if (reCharacterClass.charSet == null) {
            reCharacterClass.charSet = ASTBuilderUtil.createEmptyCharSet(symTable.anydataType);
        }
        reCharacterClass.charSet = rewriteExpr(reCharacterClass.charSet);
        reCharacterClass.characterClassEnd = rewriteExpr(reCharacterClass.characterClassEnd);
        result = reCharacterClass;
    }

    @Override
    public void visit(BLangReCharSet reCharSet) {
        reCharSet.charSetAtoms.forEach(this::rewriteExpr);
        result = reCharSet;
    }

    @Override
    public void visit(BLangReCharSetRange reCharSetRange) {
        reCharSetRange.lhsCharSetAtom = rewriteExpr(reCharSetRange.lhsCharSetAtom);
        reCharSetRange.dash = rewriteExpr(reCharSetRange.dash);
        reCharSetRange.rhsCharSetAtom = rewriteExpr(reCharSetRange.rhsCharSetAtom);
        result = reCharSetRange;
    }

    @Override
    public void visit(BLangReCapturingGroups reCapturingGroups) {
        reCapturingGroups.openParen = rewriteExpr(reCapturingGroups.openParen);
        
        if (reCapturingGroups.flagExpr == null) {
            reCapturingGroups.flagExpr = ASTBuilderUtil.createEmptyFlagExpression(reCapturingGroups.pos,
                    symTable.anydataType, symTable.stringType);
        }
        reCapturingGroups.flagExpr = rewriteExpr(reCapturingGroups.flagExpr);
        reCapturingGroups.disjunction = rewriteExpr(reCapturingGroups.disjunction);
        reCapturingGroups.closeParen = rewriteExpr(reCapturingGroups.closeParen);
        result = reCapturingGroups;
    }

    @Override
    public void visit(BLangReFlagExpression reFlagExpression) {
        reFlagExpression.questionMark = rewriteExpr(reFlagExpression.questionMark);
        
        if (reFlagExpression.flagsOnOff == null) {
            reFlagExpression.flagsOnOff = ASTBuilderUtil.createEmptyFlagOnOff(reFlagExpression.pos,
                    symTable.anydataType, symTable.stringType);
        }
        reFlagExpression.flagsOnOff = rewriteExpr(reFlagExpression.flagsOnOff);
        reFlagExpression.colon = rewriteExpr(reFlagExpression.colon);
        result = reFlagExpression;
    }

    @Override
    public void visit(BLangReFlagsOnOff reFlagsOnOff) {
        reFlagsOnOff.flags = rewriteExpr(reFlagsOnOff.flags);
        result = reFlagsOnOff;
    }

    

    
    BLangSimpleVariableDef getIteratorVariableDefinition(Location pos, BVarSymbol collectionSymbol,
                                                         BInvokableSymbol iteratorInvokableSymbol,
                                                         boolean isIteratorFuncFromLangLib) {


        BLangSimpleVarRef dataReference = ASTBuilderUtil.createVariableRef(pos, collectionSymbol);
        BLangInvocation iteratorInvocation = (BLangInvocation) TreeBuilder.createInvocationNode();
        iteratorInvocation.pos = pos;
        iteratorInvocation.expr = dataReference;
        iteratorInvocation.symbol = iteratorInvokableSymbol;
        iteratorInvocation.setBType(iteratorInvokableSymbol.retType);
        iteratorInvocation.argExprs = Lists.of(dataReference);
        iteratorInvocation.requiredArgs = iteratorInvocation.argExprs;
        iteratorInvocation.langLibInvocation = isIteratorFuncFromLangLib;
        BVarSymbol iteratorSymbol = new BVarSymbol(0, Names.fromString("$iterator$"), this.env.scope.owner.pkgID,
                                                   iteratorInvokableSymbol.retType, this.env.scope.owner, pos, VIRTUAL);

        
        BLangSimpleVariable iteratorVariable = ASTBuilderUtil.createVariable(pos, "$iterator$",
                iteratorInvokableSymbol.retType, iteratorInvocation, iteratorSymbol);
        return ASTBuilderUtil.createVariableDef(pos, iteratorVariable);
    }

    
    BLangSimpleVariableDef getIteratorNextVariableDefinition(Location pos, BType nillableResultType,
                                                             BVarSymbol iteratorSymbol,
                                                             BVarSymbol resultSymbol) {
        BLangInvocation nextInvocation = createIteratorNextInvocation(pos, iteratorSymbol);
        BLangSimpleVariable resultVariable = ASTBuilderUtil.createVariable(pos, "$result$",
                nillableResultType, nextInvocation, resultSymbol);
        return ASTBuilderUtil.createVariableDef(pos, resultVariable);
    }

    BLangInvocation createIteratorNextInvocation(Location pos, BVarSymbol iteratorSymbol) {
        BLangIdentifier nextIdentifier = ASTBuilderUtil.createIdentifier(pos, "next");
        BLangSimpleVarRef iteratorReferenceInNext = ASTBuilderUtil.createVariableRef(pos, iteratorSymbol);
        BInvokableSymbol nextFuncSymbol =
                getNextFunc((BObjectType) Types.getImpliedType(iteratorSymbol.type)).symbol;
        BLangInvocation nextInvocation = (BLangInvocation) TreeBuilder.createInvocationNode();
        nextInvocation.pos = pos;
        nextInvocation.name = nextIdentifier;
        nextInvocation.expr = iteratorReferenceInNext;
        nextInvocation.requiredArgs = Lists.of(ASTBuilderUtil.createVariableRef(pos, iteratorSymbol));
        nextInvocation.argExprs = nextInvocation.requiredArgs;
        nextInvocation.symbol = nextFuncSymbol;
        nextInvocation.setBType(nextFuncSymbol.retType);
        return nextInvocation;
    }

    private BAttachedFunction getNextFunc(BObjectType iteratorType) {
        BObjectTypeSymbol iteratorSymbol = (BObjectTypeSymbol) iteratorType.tsymbol;
        for (BAttachedFunction bAttachedFunction : iteratorSymbol.attachedFuncs) {
            if (bAttachedFunction.funcName.value.equals("next")) {
                return bAttachedFunction;
            }
        }
        return null;
    }

    
    BLangFieldBasedAccess getValueAccessExpression(Location location, BType varType,
                                                   BVarSymbol resultSymbol) {
        return getFieldAccessExpression(location, "value", varType, resultSymbol);
    }

    BLangFieldBasedAccess getFieldAccessExpression(Location pos, String fieldName, BType varType,
                                                   BVarSymbol resultSymbol) {
        BLangSimpleVarRef resultReferenceInVariableDef = ASTBuilderUtil.createVariableRef(pos, resultSymbol);
        BLangIdentifier valueIdentifier = ASTBuilderUtil.createIdentifier(pos, fieldName);

        BLangFieldBasedAccess fieldBasedAccessExpression =
                ASTBuilderUtil.createFieldAccessExpr(resultReferenceInVariableDef, valueIdentifier);
        fieldBasedAccessExpression.pos = pos;
        fieldBasedAccessExpression.setBType(varType);
        fieldBasedAccessExpression.originalType = fieldBasedAccessExpression.getBType();
        return fieldBasedAccessExpression;
    }

    private BlockFunctionBodyNode populateArrowExprBodyBlock(BLangArrowFunction bLangArrowFunction) {
        BlockFunctionBodyNode blockNode = TreeBuilder.createBlockFunctionBodyNode();
        BLangReturn returnNode = (BLangReturn) TreeBuilder.createReturnNode();
        returnNode.pos = bLangArrowFunction.body.expr.pos;
        returnNode.setExpression(bLangArrowFunction.body.expr);
        blockNode.addStatement(returnNode);
        return blockNode;
    }

    protected BLangInvocation createInvocationNode(String functionName, List<BLangExpression> args, BType retType) {
        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        BLangIdentifier name = (BLangIdentifier) TreeBuilder.createIdentifierNode();
        name.setLiteral(false);
        name.setValue(functionName);
        invocationNode.name = name;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        
        invocationNode.symbol = symTable.rootScope.lookup(new Name(functionName)).symbol;
        invocationNode.setBType(retType);
        invocationNode.requiredArgs = args;
        return invocationNode;
    }

    protected BLangInvocation createLangLibInvocationNode(String functionName,
                                                        BLangExpression onExpr,
                                                        List<BLangExpression> args,
                                                        BType retType,
                                                        Location pos) {
        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        invocationNode.pos = pos;
        BLangIdentifier name = (BLangIdentifier) TreeBuilder.createIdentifierNode();
        name.setLiteral(false);
        name.setValue(functionName);
        name.pos = pos;
        invocationNode.name = name;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        invocationNode.expr = onExpr;
        invocationNode.symbol = symResolver.lookupLangLibMethod(onExpr.getBType(), Names.fromString(functionName), env);

        ArrayList<BLangExpression> requiredArgs = new ArrayList<>();
        requiredArgs.add(onExpr);
        requiredArgs.addAll(args);
        invocationNode.requiredArgs = requiredArgs;

        invocationNode.setBType(retType != null ? retType : ((BInvokableSymbol) invocationNode.symbol).retType);
        invocationNode.langLibInvocation = true;
        return invocationNode;
    }

    private BLangInvocation createLangLibInvocationNode(String functionName,
                                                        List<BLangExpression> requiredArgs,
                                                        BType retType,
                                                        Location pos) {
        return createLangLibInvocationNode(functionName, requiredArgs, new ArrayList<>(), retType, pos);
    }

    private BLangInvocation createLangLibInvocationNode(String functionName,
                                                        List<BLangExpression> requiredArgs,
                                                        List<BLangExpression> restArgs,
                                                        BType retType,
                                                        Location pos) {
        BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode();
        invocationNode.pos = pos;
        BLangIdentifier name = (BLangIdentifier) TreeBuilder.createIdentifierNode();
        name.setLiteral(false);
        name.setValue(functionName);
        name.pos = pos;
        invocationNode.name = name;
        invocationNode.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode();

        invocationNode.symbol = symResolver.lookupMethodInModule(symTable.langInternalModuleSymbol,
                Names.fromString(functionName), env);

        invocationNode.requiredArgs = new ArrayList<>(requiredArgs);
        invocationNode.restArgs = new ArrayList<>(restArgs);

        invocationNode.setBType(retType != null ? retType : ((BInvokableSymbol) invocationNode.symbol).retType);
        invocationNode.langLibInvocation = true;
        return invocationNode;
    }

    private BLangArrayLiteral createArrayLiteralExprNode() {
        BLangArrayLiteral expr = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode();
        expr.exprs = new ArrayList<>();
        expr.setBType(new BArrayType(symTable.anyType));
        return expr;
    }

    private BFunctionPointerInvocation visitFunctionPointerInvocation(BLangInvocation iExpr) {
        BLangExpression rewritten = rewriteExpr(getFunctionPointerExpr(iExpr));
        return new BFunctionPointerInvocation(iExpr, rewritten);
    }

    protected BLangValueExpression getFunctionPointerExpr(BLangInvocation iExpr) {
        BLangValueExpression expr;
        if (iExpr.expr == null) {
            BLangSimpleVarRef varRef = new BLangSimpleVarRef();
            varRef.variableName = iExpr.name;
            expr = varRef;
        } else {
            BLangFieldBasedAccess fieldBasedAccess = new BLangFieldBasedAccess();
            fieldBasedAccess.expr = iExpr.expr;
            fieldBasedAccess.field = iExpr.name;
            expr = fieldBasedAccess;
        }
        expr.symbol = iExpr.symbol;
        expr.setBType(iExpr.symbol.type);
        return expr;
    }

    private BLangExpression visitCloneInvocation(BLangExpression expr, BType lhsType) {
        if (types.isValueType(expr.getBType())) {
            return expr;
        }
        if (Types.getImpliedType(expr.getBType()).tag == TypeTags.ERROR) {
            return expr;
        }
        BLangInvocation cloneInvok = createLangLibInvocationNode("clone", expr, new ArrayList<>(), null, expr.pos);
        return types.addConversionExprIfRequired(cloneInvok, lhsType);
    }

    private BLangExpression visitCloneReadonly(BLangExpression expr, BType lhsType) {
        if (types.isValueType(expr.getBType())) {
            return expr;
        }
        if (Types.getImpliedType(expr.getBType()).tag == TypeTags.ERROR) {
            return expr;
        }
        BLangInvocation cloneInvok = createLangLibInvocationNode("cloneReadOnly", expr, new ArrayList<>(),
                                                                 expr.getBType(),
                                                                 expr.pos);
        return types.addConversionExprIfRequired(cloneInvok, lhsType);
    }

    @SuppressWarnings("unchecked")
    <E extends BLangNode> E rewrite(E node, SymbolEnv env) {
        if (node == null) {
            return null;
        }

        if (node.desugared) {
            return node;
        }

        SymbolEnv previousEnv = this.env;
        this.env = env;

        node.accept(this);
        BLangNode resultNode = this.result;
        this.result = null;
        resultNode.desugared = true;

        this.env = previousEnv;
        return (E) resultNode;
    }

    @SuppressWarnings("unchecked")
    <E extends BLangExpression> E rewriteExpr(E node) {
        if (node == null) {
            return null;
        }

        if (node.desugared) {
            return node;
        }

        BLangExpression expr = node;
        if (node.impConversionExpr != null) {
            expr = node.impConversionExpr;
            node.impConversionExpr = null;
        }

        expr.accept(this);
        BLangNode resultNode = this.result;
        this.result = null;
        resultNode.desugared = true;

        return (E) resultNode;
    }

    @SuppressWarnings("unchecked")
    <E extends BLangStatement> E rewrite(E statement, SymbolEnv env) {
        if (statement == null) {
            return null;
        }
        BLangStatement stmt = (BLangStatement) rewrite((BLangNode) statement, env);
        return (E) stmt;
    }

    private <E extends BLangStatement> List<E> rewriteStmt(List<E> nodeList, SymbolEnv env) {
        for (int i = 0; i < nodeList.size(); i++) {
            nodeList.set(i, rewrite(nodeList.get(i), env));
        }
        return nodeList;
    }

    private <E extends BLangNode> List<E> rewrite(List<E> nodeList, SymbolEnv env) {
        for (int i = 0; i < nodeList.size(); i++) {
            nodeList.set(i, rewrite(nodeList.get(i), env));
        }
        return nodeList;
    }

    private <E extends BLangExpression> List<E> rewriteExprs(List<E> nodeList) {
        for (int i = 0; i < nodeList.size(); i++) {
            nodeList.set(i, rewriteExpr(nodeList.get(i)));
        }
        return nodeList;
    }

    private BLangLiteral createStringLiteral(Location pos, String value) {
        BLangLiteral stringLit = new BLangLiteral(value, symTable.stringType);
        stringLit.pos = pos;
        return stringLit;
    }

    private BLangLiteral createIntLiteral(long value) {
        BLangLiteral literal = (BLangLiteral) TreeBuilder.createLiteralExpression();
        literal.value = value;
        literal.setBType(symTable.intType);
        return literal;
    }

    private BLangLiteral createByteLiteral(Location pos, Byte value) {
        BLangLiteral byteLiteral = new BLangLiteral(Byte.toUnsignedInt(value), symTable.byteType);
        byteLiteral.pos = pos;
        return byteLiteral;
    }

    private BLangExpression createTypeCastExpr(BLangExpression expr, BType targetType) {
        if (types.isSameType(expr.getBType(), targetType)) {
            return expr;
        }

        BLangTypeConversionExpr conversionExpr = (BLangTypeConversionExpr) TreeBuilder.createTypeConversionNode();
        conversionExpr.pos = expr.pos;
        conversionExpr.expr = expr;
        conversionExpr.setBType(targetType);
        conversionExpr.targetType = targetType;
        conversionExpr.internal = true;
        return conversionExpr;
    }

    private BType getElementType(BType bType) {
        BType type = Types.getImpliedType(bType);
        if (type.tag != TypeTags.ARRAY) {
            return bType;
        }

        return getElementType(((BArrayType) type).getElementType());
    }

    
    private void addReturnIfNotPresent(BLangInvokableNode invokableNode) {
        if (Symbols.isNative(invokableNode.symbol) ||
                (invokableNode.hasBody() && invokableNode.body.getKind() != NodeKind.BLOCK_FUNCTION_BODY)) {
            return;
        }
        
        
        BLangBlockFunctionBody funcBody = (BLangBlockFunctionBody) invokableNode.body;
        if (invokableNode.symbol.type.getReturnType().isNullable() && (funcBody.stmts.isEmpty()
                || funcBody.stmts.get(funcBody.stmts.size() - 1).getKind() != NodeKind.RETURN)) {
            Location invPos = invokableNode.pos;
            Location returnStmtPos;
            if (invPos != null && !invokableNode.name.value.contains(GENERATED_INIT_SUFFIX.value)) {
                returnStmtPos = new BLangDiagnosticLocation(invPos.lineRange().fileName(),
                        invPos.lineRange().endLine().line(),
                        invPos.lineRange().endLine().line(),
                        invPos.lineRange().startLine().offset(),
                        invPos.lineRange().startLine().offset(), 0, 0);
            } else {
                returnStmtPos = null;
            }
            BLangReturn returnStmt = ASTBuilderUtil.createNilReturnStmt(returnStmtPos, symTable.nilType);
            funcBody.addStatement(returnStmt);
        }
    }

    /**
     * Reorder the invocation arguments to match the original function signature.
     *
     * @param iExpr Function invocation expressions to reorder the arguments
     */
    private void reorderArguments(BLangInvocation iExpr) {
        BSymbol symbol = iExpr.symbol;

        if (symbol == null || Types.getImpliedType(symbol.type).tag != TypeTags.INVOKABLE) {
            return;
        }

        BInvokableSymbol invokableSymbol = (BInvokableSymbol) symbol;

        List<BLangExpression> restArgs = iExpr.restArgs;
        int originalRequiredArgCount = iExpr.requiredArgs.size();

        
        BLangSimpleVarRef varargRef = null;
        BLangBlockStmt blockStmt = null;
        BType varargVarType = null;

        int restArgCount = restArgs.size();

        if (restArgCount > 0 &&
                restArgs.get(restArgCount - 1).getKind() == NodeKind.REST_ARGS_EXPR &&
                originalRequiredArgCount < invokableSymbol.params.size()) {
            
            
            
            BLangExpression expr = ((BLangRestArgsExpression) restArgs.get(restArgCount - 1)).expr;
            Location varargExpPos = expr.pos;
            varargVarType = expr.getBType();
            String varargVarName = DESUGARED_VARARG_KEY + UNDERSCORE + this.varargCount++;

            BVarSymbol varargVarSymbol = new BVarSymbol(0, Names.fromString(varargVarName), this.env.scope.owner.pkgID,
                                                        varargVarType, this.env.scope.owner, varargExpPos, VIRTUAL);
            varargRef = ASTBuilderUtil.createVariableRef(varargExpPos, varargVarSymbol);

            BLangSimpleVariable var = createVariable(varargExpPos, varargVarName, varargVarType, expr, varargVarSymbol);

            BLangSimpleVariableDef varDef = ASTBuilderUtil.createVariableDef(varargExpPos);
            varDef.var = var;
            varDef.setBType(varargVarType);

            blockStmt = createBlockStmt(varargExpPos);
            blockStmt.stmts.add(varDef);
        }

        if (!invokableSymbol.params.isEmpty()) {
            
            reorderNamedArgs(iExpr, invokableSymbol, varargRef);
        }

        
        if (restArgCount == 0 || restArgs.get(restArgCount - 1).getKind() != NodeKind.REST_ARGS_EXPR) {
            if (invokableSymbol.restParam == null) {
                return;
            }

            BLangArrayLiteral arrayLiteral = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode();
            List<BLangExpression> exprs = new ArrayList<>();

            BArrayType arrayType = (BArrayType) invokableSymbol.restParam.type;
            BType elemType = arrayType.eType;

            for (BLangExpression restArg : restArgs) {
                exprs.add(types.addConversionExprIfRequired(restArg, elemType));
            }

            arrayLiteral.exprs = exprs;
            arrayLiteral.setBType(arrayType);

            if (restArgCount != 0) {
                iExpr.restArgs = new ArrayList<>();
            }

            iExpr.restArgs.add(arrayLiteral);
            return;
        }

        
        if (restArgCount == 1 && restArgs.get(0).getKind() == NodeKind.REST_ARGS_EXPR) {

            
            
            if (iExpr.requiredArgs.size() == originalRequiredArgCount) {
                return;
            }

            
            
            
            
            BLangExpression firstNonRestArg = iExpr.requiredArgs.remove(0);
            BLangStatementExpression stmtExpression = createStatementExpression(blockStmt, firstNonRestArg);
            BType type = firstNonRestArg.impConversionExpr == null ?
                                              firstNonRestArg.getBType() : firstNonRestArg.impConversionExpr.targetType;
            stmtExpression.setBType(type);
            iExpr.requiredArgs.add(0, stmtExpression);

            
            if (invokableSymbol.restParam == null) {
                restArgs.remove(0);
                return;
            }

            
            
            
            
            BLangRestArgsExpression restArgsExpression = (BLangRestArgsExpression) restArgs.remove(0);
            BArrayType restParamType = (BArrayType) invokableSymbol.restParam.type;
            if (Types.getImpliedType(restArgsExpression.getBType()).tag == TypeTags.RECORD) {
                BLangExpression expr = ASTBuilderUtil.createEmptyArrayLiteral(invokableSymbol.pos, restParamType);
                restArgs.add(expr);
                return;
            }
            Location pos = restArgsExpression.pos;

            BLangArrayLiteral newArrayLiteral = createArrayLiteralExprNode();
            newArrayLiteral.setBType(restParamType);

            String name = DESUGARED_VARARG_KEY + UNDERSCORE + this.varargCount++;
            BVarSymbol varSymbol = new BVarSymbol(0, Names.fromString(name), this.env.scope.owner.pkgID,
                                                  restParamType, this.env.scope.owner, pos, VIRTUAL);
            BLangSimpleVarRef arrayVarRef = ASTBuilderUtil.createVariableRef(pos, varSymbol);

            BLangSimpleVariable var = createVariable(pos, name, restParamType, newArrayLiteral, varSymbol);
            BLangSimpleVariableDef varDef = ASTBuilderUtil.createVariableDef(pos);
            varDef.var = var;
            varDef.setBType(restParamType);

            BLangLiteral startIndex = createIntLiteral(invokableSymbol.params.size() - originalRequiredArgCount);
            BLangInvocation lengthInvocation = createLengthInvocation(pos, varargRef);
            BLangInvocation intRangeInvocation = replaceWithIntRange(pos, startIndex,
                                                                     getModifiedIntRangeEndExpr(lengthInvocation));

            BLangForeach foreach = (BLangForeach) TreeBuilder.createForeachNode();
            foreach.pos = pos;
            foreach.collection = intRangeInvocation;
            types.setForeachTypedBindingPatternType(foreach);

            final BLangSimpleVariable foreachVariable = ASTBuilderUtil.createVariable(pos, "$foreach$i",
                                                                                      foreach.varType);
            foreachVariable.symbol = new BVarSymbol(0, names.fromIdNode(foreachVariable.name),
                                                    this.env.scope.owner.pkgID, foreachVariable.getBType(),
                                                    this.env.scope.owner, pos, VIRTUAL);
            BLangSimpleVarRef foreachVarRef = ASTBuilderUtil.createVariableRef(pos, foreachVariable.symbol);
            foreach.variableDefinitionNode = ASTBuilderUtil.createVariableDef(pos, foreachVariable);
            foreach.isDeclaredWithVar = true;
            BLangBlockStmt foreachBody = ASTBuilderUtil.createBlockStmt(pos);

            BLangIndexBasedAccess valueExpr = ASTBuilderUtil.createIndexAccessExpr(varargRef, foreachVarRef);

            BType refType = Types.getImpliedType(varargVarType);
            if (refType.tag == TypeTags.ARRAY) {
                BArrayType arrayType = (BArrayType) refType;
                if (arrayType.state == BArrayState.CLOSED &&
                        arrayType.size == (iExpr.requiredArgs.size() - originalRequiredArgCount)) {
                    
                    
                    valueExpr.setBType(restParamType.eType);
                } else {
                    valueExpr.setBType(arrayType.eType);
                }
            } else {
                valueExpr.setBType(symTable.anyOrErrorType); 
            }

            BLangExpression pushExpr = types.addConversionExprIfRequired(valueExpr, restParamType.eType);
            BLangExpressionStmt expressionStmt = createExpressionStmt(pos, foreachBody);
            BLangInvocation pushInvocation = createLangLibInvocationNode(PUSH_LANGLIB_METHOD, arrayVarRef,
                                                                         List.of(pushExpr),
                                                                         restParamType, pos);
            pushInvocation.restArgs.add(pushInvocation.requiredArgs.remove(1));
            expressionStmt.expr = pushInvocation;
            foreach.body = foreachBody;
            BLangBlockStmt newArrayBlockStmt = createBlockStmt(pos);
            newArrayBlockStmt.addStatement(varDef);
            newArrayBlockStmt.addStatement(foreach);

            BLangStatementExpression newArrayStmtExpression = createStatementExpression(newArrayBlockStmt, arrayVarRef);
            newArrayStmtExpression.setBType(restParamType);

            restArgs.add(types.addConversionExprIfRequired(newArrayStmtExpression, restParamType));
            return;
        }

        
        
        BArrayType restParamType = (BArrayType) invokableSymbol.restParam.type;

        BLangArrayLiteral arrayLiteral = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode();
        arrayLiteral.setBType(restParamType);

        BType elemType = restParamType.eType;
        Location pos = restArgs.get(0).pos;

        List<BLangExpression> exprs = new ArrayList<>();

        for (int i = 0; i < restArgCount - 1; i++) {
            exprs.add(types.addConversionExprIfRequired(restArgs.get(i), elemType));
        }
        arrayLiteral.exprs = exprs;

        BLangRestArgsExpression pushRestArgsExpr = (BLangRestArgsExpression) TreeBuilder.createVarArgsNode();
        pushRestArgsExpr.pos = pos;
        pushRestArgsExpr.expr = restArgs.remove(restArgCount - 1);

        String name = DESUGARED_VARARG_KEY + UNDERSCORE + this.varargCount++;
        BVarSymbol varSymbol = new BVarSymbol(0, Names.fromString(name), this.env.scope.owner.pkgID, restParamType,
                                              this.env.scope.owner, pos, VIRTUAL);
        BLangSimpleVarRef arrayVarRef = ASTBuilderUtil.createVariableRef(pos, varSymbol);

        BLangSimpleVariable var = createVariable(pos, name, restParamType, arrayLiteral, varSymbol);
        BLangSimpleVariableDef varDef = ASTBuilderUtil.createVariableDef(pos);
        varDef.var = var;
        varDef.setBType(restParamType);

        BLangBlockStmt pushBlockStmt = createBlockStmt(pos);
        pushBlockStmt.stmts.add(varDef);

        BLangExpressionStmt expressionStmt = createExpressionStmt(pos, pushBlockStmt);
        BLangInvocation pushInvocation = createLangLibInvocationNode(PUSH_LANGLIB_METHOD, arrayVarRef,
                                                                     new ArrayList<BLangExpression>() {{
                                                                         add(pushRestArgsExpr);
                                                                     }}, restParamType, pos);
        pushInvocation.restArgs.add(pushInvocation.requiredArgs.remove(1));
        expressionStmt.expr = pushInvocation;

        BLangStatementExpression stmtExpression = createStatementExpression(pushBlockStmt, arrayVarRef);
        stmtExpression.setBType(restParamType);

        iExpr.restArgs = new ArrayList<BLangExpression>(1) {{ add(stmtExpression); }};
    }

    private void reorderNamedArgs(BLangInvocation iExpr, BInvokableSymbol invokableSymbol, BLangExpression varargRef) {
        List<BLangExpression> args = new ArrayList<>();
        Map<String, BLangExpression> namedArgs = new LinkedHashMap<>();
        iExpr.requiredArgs.stream()
                .filter(expr -> expr.getKind() == NodeKind.NAMED_ARGS_EXPR)
                .forEach(expr -> namedArgs.put(((NamedArgNode) expr).getName().value, expr));

        List<BVarSymbol> params = invokableSymbol.params;
        List<BLangRecordLiteral> incRecordLiterals = new ArrayList<>();
        BLangRecordLiteral incRecordParamAllowAdditionalFields = null;

        int varargIndex = 0;

        BType varargType = null;
        boolean tupleTypedVararg = false;

        if (varargRef != null) {
            varargType = Types.getImpliedType(varargRef.getBType());
            tupleTypedVararg = varargType.tag == TypeTags.TUPLE;
        }

        
        for (int i = 0; i < params.size(); i++) {
            BVarSymbol param = params.get(i);
            if (iExpr.requiredArgs.size() > i && iExpr.requiredArgs.get(i).getKind() != NodeKind.NAMED_ARGS_EXPR) {
                
                args.add(iExpr.requiredArgs.get(i));
            } else if (namedArgs.containsKey(param.name.value)) {
                
                args.add(namedArgs.remove(param.name.value));
            } else if (param.getFlags().contains(Flag.INCLUDED)) {
                BLangRecordLiteral recordLiteral = (BLangRecordLiteral) TreeBuilder.createRecordLiteralNode();
                BType paramType = param.type;
                recordLiteral.setBType(paramType);
                args.add(recordLiteral);
                incRecordLiterals.add(recordLiteral);
                if (((BRecordType) Types.getImpliedType(paramType)).restFieldType != symTable.noType) {
                    incRecordParamAllowAdditionalFields = recordLiteral;
                }
            } else if (varargRef == null) {
                
                BLangExpression expr = new BLangIgnoreExpr();
                expr.setBType(param.type);
                args.add(expr);
            } else {
                
                
                if (Types.getImpliedType(varargRef.getBType()).tag == TypeTags.RECORD) {
                    if (param.isDefaultable) {
                        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(varargRef.pos);
                        BLangInvocation hasKeyInvocation = createLangLibInvocationNode(HAS_KEY, varargRef,
                                List.of(createStringLiteral(param.pos, param.name.value)), null, varargRef.pos);
                        BLangSimpleVariableDef variableDef = createVarDef("$hasKey$", hasKeyInvocation.getBType(),
                                                                          hasKeyInvocation, hasKeyInvocation.pos);
                        blockStmt.stmts.add(variableDef);
                        BLangSimpleVarRef simpleVarRef = ASTBuilderUtil.createVariableRef(variableDef.pos,
                                                                                          variableDef.var.symbol);
                        BLangExpression indexExpr = rewriteExpr(createStringLiteral(param.pos, param.name.value));
                        BLangIndexBasedAccess memberAccessExpr =
                                ASTBuilderUtil.createMemberAccessExprNode(param.type, varargRef, indexExpr);
                        BLangExpression ignoreExpr = ASTBuilderUtil.createIgnoreExprNode(param.type);
                        BLangTernaryExpr ternaryExpr = ASTBuilderUtil.createTernaryExprNode(param.type, simpleVarRef,
                                                                                          memberAccessExpr, ignoreExpr);
                        BLangDynamicArgExpr dynamicArgExpr =
                                ASTBuilderUtil.createDynamicParamExpression(simpleVarRef, param, ternaryExpr);
                        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, dynamicArgExpr);
                        stmtExpr.setBType(dynamicArgExpr.getBType());
                        args.add(rewriteExpr(stmtExpr));
                    } else {
                        BLangFieldBasedAccess fieldBasedAccessExpression =
                                ASTBuilderUtil.createFieldAccessExpr(varargRef,
                                        ASTBuilderUtil.createIdentifier(param.pos, param.name.value));
                        fieldBasedAccessExpression.setBType(param.type);
                        args.add(fieldBasedAccessExpression);
                    }
                } else {
                    BLangExpression indexExpr = rewriteExpr(createIntLiteral(varargIndex));
                    BType memberAccessExprType = tupleTypedVararg ?
                            ((BTupleType) varargType).getTupleTypes().get(varargIndex)
                            : ((BArrayType) varargType).eType;
                    args.add(types.addConversionExprIfRequired(
                            ASTBuilderUtil.createMemberAccessExprNode(memberAccessExprType, varargRef, indexExpr),
                            param.type));
                    varargIndex++;
                }
            }
        }
        if (!namedArgs.isEmpty()) {
            setFieldsForIncRecordLiterals(namedArgs, incRecordLiterals, incRecordParamAllowAdditionalFields);
        }
        iExpr.requiredArgs = args;
    }

    private void setFieldsForIncRecordLiterals(Map<String, BLangExpression> namedArgs,
                                               List<BLangRecordLiteral> incRecordLiterals,
                                               BLangRecordLiteral incRecordParamAllowAdditionalFields) {
        for (String name : namedArgs.keySet()) {
            boolean isAdditionalField = true;
            BLangNamedArgsExpression expr = (BLangNamedArgsExpression) namedArgs.get(name);
            for (BLangRecordLiteral recordLiteral : incRecordLiterals) {
                LinkedHashMap<String, BField> fields =
                        ((BRecordType) Types.getImpliedType(recordLiteral.getBType())).fields;
                if (fields.containsKey(name) &&
                        Types.getImpliedType(fields.get(name).type).tag != TypeTags.NEVER) {
                    isAdditionalField = false;
                    createAndAddRecordFieldForIncRecordLiteral(recordLiteral, expr);
                    break;
                }
            }
            if (isAdditionalField) {
                createAndAddRecordFieldForIncRecordLiteral(incRecordParamAllowAdditionalFields, expr);
            }
        }
    }

    private void createAndAddRecordFieldForIncRecordLiteral(BLangRecordLiteral recordLiteral,
                                                            BLangNamedArgsExpression expr) {
        BLangSimpleVarRef varRef = new BLangSimpleVarRef();
        varRef.variableName = expr.name;
        BLangRecordLiteral.BLangRecordKeyValueField recordKeyValueField = ASTBuilderUtil.
                createBLangRecordKeyValue(varRef, expr.expr);
        recordLiteral.fields.add(recordKeyValueField);
    }

    private BLangBlockStmt getSafeErrorAssignment(Location location, BLangSimpleVarRef ref,
                                                  BSymbol invokableSymbol,
                                                  List<BType> equivalentErrorTypes,
                                                  boolean isCheckPanicExpr) {
        
        
        BType enclosingFuncReturnType = Types.getImpliedType(((BInvokableType) invokableSymbol.type).retType);
        Set<BType> returnTypeSet = enclosingFuncReturnType.tag == TypeTags.UNION ?
                ((BUnionType) enclosingFuncReturnType).getMemberTypes() :
                new LinkedHashSet<>() {{
                    add(enclosingFuncReturnType);
                }};

        
        boolean returnOnError = equivalentErrorTypes.stream()
                .allMatch(errorType -> returnTypeSet.stream()
                        .anyMatch(retType -> types.isAssignable(errorType, retType)));

        String patternFailureCaseVarName = GEN_VAR_PREFIX.value + "t_failure";
        BLangSimpleVariable errorVar =
                ASTBuilderUtil.createVariable(location, patternFailureCaseVarName, symTable.errorType,
                                                createTypeCastExpr(ref, symTable.errorType),
                                              new BVarSymbol(0, Names.fromString(patternFailureCaseVarName),
                                                             this.env.scope.owner.pkgID, symTable.errorType,
                                                             this.env.scope.owner, location, VIRTUAL));

        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(location);
        BLangSimpleVariableDef errorVarDef = ASTBuilderUtil.createVariableDef(location, errorVar);
        blockStmt.addStatement(errorVarDef);
        BLangVariableReference errorVarRef = ASTBuilderUtil.createVariableRef(location, errorVar.symbol);
        if (!isCheckPanicExpr && (returnOnError || this.onFailClause != null)) {
            
            BLangFail failStmt = (BLangFail) TreeBuilder.createFailNode();
            failStmt.pos = location;
            failStmt.expr = errorVarRef;
            blockStmt.addStatement(failStmt);
            if (returnOnError && this.shouldReturnErrors) {
                BLangReturn errorReturn = ASTBuilderUtil.createReturnStmt(location, rewrite(errorVarRef, env));
                errorReturn.desugared = true;
                failStmt.exprStmt = errorReturn;
            }
        } else {
            
            BLangPanic panicNode = (BLangPanic) TreeBuilder.createPanicNode();
            panicNode.pos = location;
            panicNode.expr = errorVarRef;
            blockStmt.addStatement(panicNode);
        }

        return blockStmt;
    }

    private BType getStructuredBindingPatternType(BLangVariable bindingPatternVariable) {
        if (NodeKind.TUPLE_VARIABLE == bindingPatternVariable.getKind()) {
            BLangTupleVariable tupleVariable = (BLangTupleVariable) bindingPatternVariable;
            List<BTupleMember> memberTypes = new ArrayList<>();
            for (int i = 0; i < tupleVariable.memberVariables.size(); i++) {
                BType member = getStructuredBindingPatternType(tupleVariable.memberVariables.get(i));
                BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(member);
                memberTypes.add(
                        new BTupleMember(member, varSymbol));
            }
            BTupleType tupleType = new BTupleType(memberTypes);
            if (tupleVariable.restVariable != null) {
                BArrayType restArrayType = (BArrayType) getStructuredBindingPatternType(tupleVariable.restVariable);
                tupleType.restType = restArrayType.eType;
            }
            return tupleType;
        }

        if (NodeKind.RECORD_VARIABLE == bindingPatternVariable.getKind()) {
            BLangRecordVariable recordVariable = (BLangRecordVariable) bindingPatternVariable;

            BRecordTypeSymbol recordSymbol =
                    Symbols.createRecordSymbol(0, Names.fromString("$anonRecordType$" + UNDERSCORE + recordCount++),
                                               env.enclPkg.symbol.pkgID, null, env.scope.owner, recordVariable.pos,
                                               VIRTUAL);
            recordSymbol.scope = new Scope(recordSymbol);

            LinkedHashMap<String, BField> fields = new LinkedHashMap<>();
            List<BLangSimpleVariable> typeDefFields = new ArrayList<>();

            for (int i = 0; i < recordVariable.variableList.size(); i++) {
                String fieldNameStr = recordVariable.variableList.get(i).key.value;
                Name fieldName = Names.fromString(fieldNameStr);
                BType fieldType = getStructuredBindingPatternType(
                        recordVariable.variableList.get(i).valueBindingPattern);
                BVarSymbol fieldSymbol = new BVarSymbol(Flags.REQUIRED, fieldName, env.enclPkg.symbol.pkgID, fieldType,
                                                        recordSymbol, bindingPatternVariable.pos, VIRTUAL);

                
                fields.put(fieldName.value, new BField(fieldName, bindingPatternVariable.pos, fieldSymbol));
                typeDefFields.add(ASTBuilderUtil.createVariable(null, fieldNameStr, fieldType, null, fieldSymbol));
                recordSymbol.scope.define(fieldName, fieldSymbol);
            }

            BRecordType recordVarType = new BRecordType(recordSymbol);
            recordVarType.fields = fields;

            
            recordVarType.restFieldType = recordVariable.restParam != null ?
                        ((BRecordType) recordVariable.restParam.getBType()).restFieldType :
                    symTable.anydataType;
            recordSymbol.type = recordVarType;
            recordVarType.tsymbol = recordSymbol;

            BLangRecordTypeNode recordTypeNode = TypeDefBuilderHelper.createRecordTypeNode(typeDefFields,
                                                                                           recordVarType,
                                                                                           bindingPatternVariable.pos);
            TypeDefBuilderHelper.createTypeDefinitionForTSymbol(recordVarType, recordSymbol, recordTypeNode, env);

            return recordVarType;
        }

        if (NodeKind.ERROR_VARIABLE == bindingPatternVariable.getKind()) {
            BLangErrorVariable errorVariable = (BLangErrorVariable) bindingPatternVariable;
            BErrorTypeSymbol errorTypeSymbol = new BErrorTypeSymbol(
                    SymTag.ERROR,
                    Flags.PUBLIC,
                    Names.fromString("$anonErrorType$" + UNDERSCORE + errorCount++),
                    env.enclPkg.symbol.pkgID,
                    null, null, errorVariable.pos, VIRTUAL);
            BType detailType;
            if ((errorVariable.detail == null || errorVariable.detail.isEmpty()) && errorVariable.restDetail != null) {
                detailType = symTable.detailType;
            } else {
                detailType = createDetailType(errorVariable.detail, errorVariable.restDetail, errorCount++,
                                              errorVariable.pos);

                BLangRecordTypeNode recordTypeNode = createRecordTypeNode(errorVariable, (BRecordType) detailType);
                TypeDefBuilderHelper.createTypeDefinitionForTSymbol(detailType, detailType.tsymbol,
                        recordTypeNode, env);
            }
            BErrorType errorType = new BErrorType(errorTypeSymbol, detailType);
            errorTypeSymbol.type = errorType;

            TypeDefBuilderHelper.createTypeDefinitionForTSymbol(errorType, errorTypeSymbol,
                    createErrorTypeNode(errorType), env);
            return errorType;
        }

        return bindingPatternVariable.getBType();
    }

    private BLangRecordTypeNode createRecordTypeNode(BLangErrorVariable errorVariable, BRecordType detailType) {
        List<BLangSimpleVariable> fieldList = new ArrayList<>();
        for (BLangErrorVariable.BLangErrorDetailEntry field : errorVariable.detail) {
            BVarSymbol symbol = field.valueBindingPattern.symbol;
            if (symbol == null) {
                symbol = new BVarSymbol(Flags.PUBLIC, Names.fromString(field.key.value + "$"),
                                        this.env.enclPkg.packageID, symTable.pureType, null,
                                        field.valueBindingPattern.pos, VIRTUAL);
            }
            BLangSimpleVariable fieldVar = ASTBuilderUtil.createVariable(
                    field.valueBindingPattern.pos,
                    symbol.name.value,
                    field.valueBindingPattern.getBType(),
                    field.valueBindingPattern.expr,
                    symbol);
            fieldList.add(fieldVar);
        }
        return TypeDefBuilderHelper.createRecordTypeNode(fieldList, detailType, errorVariable.pos);
    }

    private BType createDetailType(List<BLangErrorVariable.BLangErrorDetailEntry> detail,
                                   BLangSimpleVariable restDetail, int errorNo, Location pos) {
        BRecordType detailRecordType = createAnonRecordType(pos);

        if (restDetail == null) {
            detailRecordType.sealed = true;
        }

        for (BLangErrorVariable.BLangErrorDetailEntry detailEntry : detail) {
            Name fieldName = names.fromIdNode(detailEntry.key);
            BType fieldType = getStructuredBindingPatternType(detailEntry.valueBindingPattern);
            BVarSymbol fieldSym = new BVarSymbol(Flags.PUBLIC, fieldName, detailRecordType.tsymbol.pkgID, fieldType,
                    detailRecordType.tsymbol, detailEntry.key.pos, VIRTUAL);
            detailRecordType.fields.put(fieldName.value, new BField(fieldName, detailEntry.key.pos, fieldSym));
            detailRecordType.tsymbol.scope.define(fieldName, fieldSym);
        }

        return detailRecordType;
    }

    private BRecordType createAnonRecordType(Location pos) {
        BRecordTypeSymbol detailRecordTypeSymbol = new BRecordTypeSymbol(
                SymTag.RECORD,
                Flags.PUBLIC,
                Names.fromString(anonModelHelper.getNextRecordVarKey(env.enclPkg.packageID)),
                env.enclPkg.symbol.pkgID, null, null, pos, VIRTUAL);
        detailRecordTypeSymbol.scope = new Scope(detailRecordTypeSymbol);

        BRecordType detailRecordType = new BRecordType(detailRecordTypeSymbol);
        detailRecordType.restFieldType = symTable.anydataType;
        return detailRecordType;
    }

    BLangErrorType createErrorTypeNode(BErrorType errorType) {
        BLangErrorType errorTypeNode = (BLangErrorType) TreeBuilder.createErrorTypeNode();
        errorTypeNode.setBType(errorType);
        return errorTypeNode;
    }

    private BLangExpression createBinaryExpression(Location pos, BLangSimpleVarRef varRef,
                                                   BLangExpression expression) {

        BLangBinaryExpr binaryExpr;
        if (NodeKind.GROUP_EXPR == expression.getKind()) {
            return createBinaryExpression(pos, varRef, ((BLangGroupExpr) expression).expression);
        }

        if (NodeKind.BINARY_EXPR == expression.getKind()) {
            binaryExpr = (BLangBinaryExpr) expression;
            BLangExpression lhsExpr = createBinaryExpression(pos, varRef, binaryExpr.lhsExpr);
            BLangExpression rhsExpr = createBinaryExpression(pos, varRef, binaryExpr.rhsExpr);

            binaryExpr = ASTBuilderUtil.createBinaryExpr(pos, lhsExpr, rhsExpr, symTable.booleanType, OperatorKind.OR,
                    (BOperatorSymbol) symResolver
                            .resolveBinaryOperator(OperatorKind.OR, symTable.booleanType, symTable.booleanType));
        } else if (expression.getKind() == NodeKind.SIMPLE_VARIABLE_REF
                && ((BLangSimpleVarRef) expression).variableName.value.equals(IGNORE.value)) {
            BLangValueType anyType = (BLangValueType) TreeBuilder.createValueTypeNode();
            anyType.setBType(symTable.anyType);
            anyType.typeKind = TypeKind.ANY;
            return ASTBuilderUtil.createTypeTestExpr(pos, varRef, anyType);
        } else {
            binaryExpr = ASTBuilderUtil
                    .createBinaryExpr(pos, varRef, expression, symTable.booleanType, OperatorKind.EQUAL, null);
            BSymbol opSymbol = symResolver.resolveBinaryOperator(OperatorKind.EQUAL, varRef.getBType(),
                                                                 expression.getBType());
            if (opSymbol == symTable.notFoundSymbol) {
                opSymbol = symResolver
                        .getBinaryEqualityForTypeSets(OperatorKind.EQUAL, symTable.anydataType, expression.getBType(),
                                                      binaryExpr, env);
            }
            binaryExpr.opSymbol = (BOperatorSymbol) opSymbol;
        }
        return binaryExpr;
    }

    private BLangIsLikeExpr createIsLikeExpression(Location pos, BLangExpression expr, BType type) {
        return ASTBuilderUtil.createIsLikeExpr(pos, expr, ASTBuilderUtil.createTypeNode(type), symTable.booleanType);
    }

    private BLangAssignment createAssignmentStmt(BLangSimpleVariable variable) {
        BLangSimpleVarRef varRef = (BLangSimpleVarRef) TreeBuilder.createSimpleVariableReferenceNode();
        varRef.pos = variable.pos;
        varRef.variableName = variable.name;
        varRef.symbol = variable.symbol;
        varRef.setBType(variable.getBType());

        BLangAssignment assignmentStmt = (BLangAssignment) TreeBuilder.createAssignmentNode();
        assignmentStmt.expr = variable.expr;
        assignmentStmt.pos = variable.pos;
        assignmentStmt.setVariable(varRef);
        return assignmentStmt;
    }

    private BLangAssignment createStructFieldUpdate(BLangFunction function, BLangSimpleVariable variable,
                                                    BVarSymbol selfSymbol) {
        return createStructFieldUpdate(function, variable.expr, variable.symbol, variable.getBType(), selfSymbol,
                                       variable.name);
    }

    private BLangAssignment createStructFieldUpdate(BLangFunction function, BLangExpression expr,
                                                    BVarSymbol fieldSymbol, BType fieldType, BVarSymbol selfSymbol,
                                                    BLangIdentifier fieldName) {
        BLangSimpleVarRef selfVarRef = ASTBuilderUtil.createVariableRef(function.pos, selfSymbol);
        BLangFieldBasedAccess fieldAccess = ASTBuilderUtil.createFieldAccessExpr(selfVarRef, fieldName);
        fieldAccess.symbol = fieldSymbol;
        fieldAccess.setBType(fieldType);
        fieldAccess.isStoreOnCreation = true;

        BLangAssignment assignmentStmt = (BLangAssignment) TreeBuilder.createAssignmentNode();
        
        expr.pos = this.symTable.builtinPos;
        fieldName.pos =  this.symTable.builtinPos;
        fieldSymbol.pos =  this.symTable.builtinPos;
        assignmentStmt.expr = expr;
        assignmentStmt.pos = function.pos;
        assignmentStmt.setVariable(fieldAccess);

        SymbolEnv initFuncEnv = SymbolEnv.createFunctionEnv(function, function.symbol.scope, env);
        return rewrite(assignmentStmt, initFuncEnv);
    }

    private boolean safeNavigate(BLangAccessExpression accessExpr) {
        if (accessExpr.isLValue || accessExpr.expr == null) {
            return false;
        }

        if (accessExpr.errorSafeNavigation || accessExpr.nilSafeNavigation) {
            return true;
        }

        NodeKind kind = accessExpr.expr.getKind();
        if (kind == NodeKind.FIELD_BASED_ACCESS_EXPR ||
                kind == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            return safeNavigate((BLangAccessExpression) accessExpr.expr);
        }

        return false;
    }

    private BLangExpression rewriteSafeNavigationExpr(BLangAccessExpression accessExpr) {
        BType originalExprType = accessExpr.getBType();
        
        String matchTempResultVarName = GEN_VAR_PREFIX.value + "temp_result";
        BLangSimpleVariable tempResultVar =
                ASTBuilderUtil.createVariable(accessExpr.pos, matchTempResultVarName, accessExpr.getBType(), null,
                                              new BVarSymbol(0, Names.fromString(matchTempResultVarName),
                                                             this.env.scope.owner.pkgID, accessExpr.getBType(),
                                                             this.env.scope.owner, accessExpr.pos, VIRTUAL));
        BLangSimpleVariableDef tempResultVarDef = ASTBuilderUtil.createVariableDef(accessExpr.pos, tempResultVar);
        BLangVariableReference tempResultVarRef =
                ASTBuilderUtil.createVariableRef(accessExpr.pos, tempResultVar.symbol);

        
        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(accessExpr.pos);
        blockStmt.stmts.add(tempResultVarDef);
        handleSafeNavigation(blockStmt, accessExpr, accessExpr.getBType(), tempResultVar);

        
        BLangMatchStatement matchStmt = this.matchStmtStack.firstElement();
        blockStmt.stmts.add(matchStmt);
        BLangStatementExpression stmtExpression = createStatementExpression(blockStmt, tempResultVarRef);
        stmtExpression.setBType(originalExprType);
        
        this.matchStmtStack = new Stack<>();
        this.accessExprStack = new Stack<>();
        this.successClause = null;
        this.safeNavigationAssignment = null;
        return stmtExpression;
    }

    private void handleSafeNavigation(BLangBlockStmt blockStmt, BLangAccessExpression accessExpr, BType type,
                                      BLangSimpleVariable tempResultVar) {
        if (accessExpr.expr == null) {
            return;
        }

        
        NodeKind kind = accessExpr.expr.getKind();
        if (kind == NodeKind.FIELD_BASED_ACCESS_EXPR || kind == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            handleSafeNavigation(blockStmt, (BLangAccessExpression) accessExpr.expr, type, tempResultVar);
        }

        if (!(accessExpr.errorSafeNavigation || accessExpr.nilSafeNavigation)) {
            BType originalType = Types.getImpliedType(accessExpr.originalType);
            if (isMapJson(originalType, false)) {
                accessExpr.setBType(BUnionType.create(null, originalType, symTable.errorType));
            } else {
                accessExpr.setBType(originalType);
            }
            if (this.safeNavigationAssignment != null) {
                this.safeNavigationAssignment.expr =
                                                types.addConversionExprIfRequired(accessExpr, tempResultVar.getBType());
            }
            return;
        }

        /*
         * If the field access is a safe navigation, create a match expression.
         * Then chain the current expression as the success-pattern of the parent
         * match expr, if available.
         * eg:
         * x but {              <--- parent match expr
         *   error e => e,
         *   T t => t.y but {   <--- current expr
         *      error e => e,
         *      R r => r.z
         *   }
         * }
         */

        BLangExpression matchExpr = accessExpr.expr;
        BLangSimpleVariableDef variableDef =
                                         createVarDef("$varDef$", matchExpr.getBType(), matchExpr, matchExpr.pos);
        BLangSimpleVarRef simpleVarRef = ASTBuilderUtil.createVariableRef(variableDef.pos, variableDef.var.symbol);
        accessExpr.expr = simpleVarRef;
        blockStmt.stmts.add(variableDef);
        Location pos = accessExpr.pos;
        BLangMatchStatement matchStmt = ASTBuilderUtil.createMatchStatement(simpleVarRef, pos);
        BType matchExprType = accessExpr.expr.getBType();

        boolean isAllTypesRecords = false;
        LinkedHashSet<BType> memTypes = new LinkedHashSet<>();
        BType referredType = Types.getImpliedType(matchExpr.getBType());
        if (referredType.tag == TypeTags.UNION) {
            memTypes = new LinkedHashSet<>(((BUnionType) referredType).getMemberTypes());
            isAllTypesRecords = isAllTypesAreRecordsInUnion(memTypes);
        }

        
        if (accessExpr.nilSafeNavigation) {
            matchStmt.addMatchClause(getMatchNullClause(matchExpr, tempResultVar));
            matchStmt.setBType(type);
            memTypes.remove(symTable.nilType);
        }

        
        if (accessExpr.errorSafeNavigation) {
            matchStmt.addMatchClause(getMatchErrorClause(matchExpr, tempResultVar));
            matchStmt.setBType(type);
            matchStmt.pos = pos;
            memTypes.remove(symTable.errorType);
        }

        BLangMatchClause successClause = null;
        Name field = getFieldName(accessExpr);
        if (field == Names.EMPTY) {
            successClause = getSuccessPatternClause(matchExprType, matchExpr, accessExpr, tempResultVar,
                    accessExpr.errorSafeNavigation);
            matchStmt.addMatchClause(successClause);
            pushToMatchStatementStack(matchStmt, successClause, pos);
            return;
        }

        if (isAllTypesRecords) {
            for (BType memberType : memTypes) {
                BRecordType recordType = (BRecordType) Types.getImpliedType(memberType);
                if (recordType.fields.containsKey(field.value) || !recordType.sealed) {
                    successClause = getSuccessPatternClause(memberType, matchExpr, accessExpr, tempResultVar,
                            accessExpr.errorSafeNavigation);
                    matchStmt.addMatchClause(successClause);
                }
            }
            matchStmt.addMatchClause(getMatchAllAndNilReturnClause(matchExpr, tempResultVar));
            pushToMatchStatementStack(matchStmt, successClause, pos);
            return;
        }

        
        successClause = getSuccessPatternClause(matchExprType, matchExpr, accessExpr, tempResultVar,
                accessExpr.errorSafeNavigation);
        matchStmt.addMatchClause(successClause);
        pushToMatchStatementStack(matchStmt, successClause, pos);
    }

    private boolean isMapJson(BType originalType, boolean fromMap) {
        originalType = Types.getImpliedType(originalType);
        return ((originalType.tag == TypeTags.MAP) && isMapJson(((BMapType) originalType).getConstraint(), true))
                || ((originalType.tag == TypeTags.JSON) && fromMap);
    }

    private void pushToMatchStatementStack(BLangMatchStatement matchStmt, BLangMatchClause successClause,
                                           Location pos) {
        this.matchStmtStack.push(matchStmt);
        if (this.successClause != null) {
            this.successClause.blockStmt = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(matchStmt));
        }
        this.successClause = successClause;
    }

    private Name getFieldName(BLangAccessExpression accessExpr) {
        Name field = Names.EMPTY;
        if (accessExpr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR) {
            field = new Name(((BLangFieldBasedAccess) accessExpr).field.value);
        } else if (accessExpr.getKind() == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            BLangExpression indexBasedExpression = ((BLangIndexBasedAccess) accessExpr).indexExpr;
            if (indexBasedExpression.getKind() == NodeKind.LITERAL) {
                field = new Name(((BLangLiteral) indexBasedExpression).value.toString());
            }
        }
        return field;
    }

    private boolean isAllTypesAreRecordsInUnion(LinkedHashSet<BType> memTypes) {
        for (BType memType : memTypes) {
            int typeTag = Types.getImpliedType(memType).tag;
            if (typeTag != TypeTags.RECORD && typeTag != TypeTags.ERROR && typeTag != TypeTags.NIL) {
                return false;
            }
        }
        return true;
    }

    private BLangMatchClause getMatchErrorClause(BLangExpression matchExpr, BLangSimpleVariable tempResultVar) {
        String errorPatternVarName = GEN_VAR_PREFIX.value + "t_match_error";
        Location pos = matchExpr.pos;
        BVarSymbol errorPatternVarSymbol = new BVarSymbol(0, Names.fromString(errorPatternVarName),
                this.env.scope.owner.pkgID, symTable.anyOrErrorType, this.env.scope.owner, pos, VIRTUAL);
        BLangCaptureBindingPattern captureBindingPattern =
                ASTBuilderUtil.createCaptureBindingPattern(errorPatternVarSymbol, errorPatternVarName);
        BLangVarBindingPatternMatchPattern varBindingPatternMatchPattern =
                ASTBuilderUtil.createVarBindingPatternMatchPattern(captureBindingPattern, matchExpr);

        BLangSimpleVarRef assignmentRhsExpr = ASTBuilderUtil.createVariableRef(pos, errorPatternVarSymbol);
        BLangVariableReference tempResultVarRef = ASTBuilderUtil.createVariableRef(pos, tempResultVar.symbol);
        BLangAssignment assignmentStmt =
                ASTBuilderUtil.createAssignmentStmt(pos, tempResultVarRef, assignmentRhsExpr);
        BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(assignmentStmt));
        BLangExpression matchGuard = ASTBuilderUtil.createTypeTestExpr(pos, assignmentRhsExpr, getErrorTypeNode());
        matchGuard.setBType(symTable.booleanType);

        return ASTBuilderUtil.createMatchClause(matchExpr, clauseBody, matchGuard, varBindingPatternMatchPattern);
    }

    private BLangMatchClause getMatchNullClause(BLangExpression matchExpr, BLangSimpleVariable tempResultVar) {
        String nullPatternVarName = GEN_VAR_PREFIX.value + "t_match_null";
        Location pos = matchExpr.pos;
        BVarSymbol nullPatternVarSymbol = new BVarSymbol(0, Names.fromString(nullPatternVarName),
                this.env.scope.owner.pkgID, symTable.anyOrErrorType, this.env.scope.owner, pos, VIRTUAL);
        BLangCaptureBindingPattern captureBindingPattern =
                ASTBuilderUtil.createCaptureBindingPattern(nullPatternVarSymbol, nullPatternVarName);
        BLangVarBindingPatternMatchPattern varBindingPatternMatchPattern =
                ASTBuilderUtil.createVarBindingPatternMatchPattern(captureBindingPattern, matchExpr);

        BLangSimpleVarRef assignmentRhsExpr = ASTBuilderUtil.createVariableRef(pos, nullPatternVarSymbol);
        BLangVariableReference tempResultVarRef = ASTBuilderUtil.createVariableRef(pos, tempResultVar.symbol);
        BLangAssignment assignmentStmt =
                ASTBuilderUtil.createAssignmentStmt(pos, tempResultVarRef, assignmentRhsExpr);
        BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(assignmentStmt));
        BLangExpression matchGuard = ASTBuilderUtil.createTypeTestExpr(pos, assignmentRhsExpr, getNillTypeNode());
        matchGuard.setBType(symTable.booleanType);

        return ASTBuilderUtil.createMatchClause(matchExpr, clauseBody, matchGuard, varBindingPatternMatchPattern);
    }

    private BLangMatchClause getMatchAllAndNilReturnClause(BLangExpression matchExpr,
                                                           BLangSimpleVariable tempResultVar) {
        Location pos = matchExpr.pos;
        BLangVariableReference tempResultVarRef = ASTBuilderUtil.createVariableRef(pos, tempResultVar.symbol);
        BLangAssignment assignmentStmt =
                ASTBuilderUtil.createAssignmentStmt(pos, tempResultVarRef, createLiteral(pos, symTable.nilType,
                        Names.NIL_VALUE));
        BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(assignmentStmt));

        BLangWildCardMatchPattern wildCardMatchPattern = ASTBuilderUtil.createWildCardMatchPattern(matchExpr);
        wildCardMatchPattern.setBType(symTable.anyType);
        return ASTBuilderUtil.createMatchClause(matchExpr, clauseBody, null, wildCardMatchPattern);
    }

    private BLangMatchClause getSuccessPatternClause(BType type, BLangExpression matchExpr,
                                                     BLangAccessExpression accessExpr,
                                                     BLangSimpleVariable tempResultVar, boolean liftError) {
        type = types.getSafeType(type, true, liftError);
        String successPatternVarName = GEN_VAR_PREFIX.value + "t_match_success";

        Location pos = accessExpr.pos;
        BVarSymbol  successPatternSymbol;
        if (Types.getImpliedType(type).tag == TypeTags.INVOKABLE) {
            successPatternSymbol = new BInvokableSymbol(SymTag.VARIABLE, 0, Names.fromString(successPatternVarName),
                    this.env.scope.owner.pkgID, symTable.anyOrErrorType, this.env.scope.owner, pos, VIRTUAL);
        } else {
            successPatternSymbol = new BVarSymbol(0, Names.fromString(successPatternVarName),
                    this.env.scope.owner.pkgID, symTable.anyOrErrorType, this.env.scope.owner, pos, VIRTUAL);
        }

        BLangSimpleVariable successPatternVar = ASTBuilderUtil.createVariable(accessExpr.pos, successPatternVarName,
                type, null, successPatternSymbol);
        BLangSimpleVarRef successPatternVarRef = ASTBuilderUtil.createVariableRef(accessExpr.pos,
                successPatternVar.symbol);

        BLangCaptureBindingPattern captureBindingPattern =
                ASTBuilderUtil.createCaptureBindingPattern(successPatternSymbol, successPatternVarName);
        BLangVarBindingPatternMatchPattern varBindingPatternMatchPattern =
                ASTBuilderUtil.createVarBindingPatternMatchPattern(captureBindingPattern, matchExpr);

        BLangAccessExpression tempAccessExpr = nodeCloner.cloneNode(accessExpr);
        if (accessExpr.getKind() == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            ((BLangIndexBasedAccess) tempAccessExpr).indexExpr = ((BLangIndexBasedAccess) accessExpr).indexExpr;
        }
        if (accessExpr instanceof BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess) {
            ((BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess) tempAccessExpr).nsSymbol =
                    ((BLangFieldBasedAccess.BLangNSPrefixedFieldBasedAccess) accessExpr).nsSymbol;
        }

        tempAccessExpr.expr = types.addConversionExprIfRequired(successPatternVarRef, type);
        tempAccessExpr.errorSafeNavigation = false;
        tempAccessExpr.nilSafeNavigation = false;
        accessExpr.cloneRef = null;

        
        
        
        if (TypeTags.isXMLTypeTag(Types.getImpliedType(tempAccessExpr.expr.getBType()).tag)) {
            
            tempAccessExpr.setBType(BUnionType.create(null, accessExpr.originalType, symTable.errorType,
                    symTable.nilType));
        } else {
            tempAccessExpr.setBType(accessExpr.originalType);
        }
        tempAccessExpr.optionalFieldAccess = accessExpr.optionalFieldAccess;

        BLangVariableReference tempResultVarRef =
                ASTBuilderUtil.createVariableRef(accessExpr.pos, tempResultVar.symbol);

        BLangExpression assignmentRhsExpr =
                types.addConversionExprIfRequired(tempAccessExpr, tempResultVarRef.getBType());
        BLangAssignment assignmentStmt =
                ASTBuilderUtil.createAssignmentStmt(accessExpr.pos, tempResultVarRef, assignmentRhsExpr);
        BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(accessExpr.pos, this.env.scope,
                Lists.of(assignmentStmt));
        BLangExpression matchGuard = ASTBuilderUtil.createTypeTestExpr(pos, successPatternVarRef, createTypeNode(type));
        matchGuard.setBType(symTable.booleanType);

        return ASTBuilderUtil.createMatchClause(matchExpr, clauseBody, matchGuard, varBindingPatternMatchPattern);
    }

    BLangValueType getNillTypeNode() {
        BLangValueType nillTypeNode = (BLangValueType) TreeBuilder.createValueTypeNode();
        nillTypeNode.typeKind = TypeKind.NIL;
        nillTypeNode.setBType(symTable.nilType);
        return nillTypeNode;
    }

    BLangValueType createTypeNode(BType type) {
        BLangValueType typeNode = (BLangValueType) TreeBuilder.createValueTypeNode();
        typeNode.typeKind = type.getKind();
        typeNode.setBType(type);
        return typeNode;
    }

    private BLangValueExpression cloneExpression(BLangExpression expr) {
        switch (expr.getKind()) {
            case SIMPLE_VARIABLE_REF:
                return ASTBuilderUtil.createVariableRef(expr.pos, ((BLangSimpleVarRef) expr).symbol);
            case FIELD_BASED_ACCESS_EXPR:
            case INDEX_BASED_ACCESS_EXPR:
                return cloneAccessExpr((BLangAccessExpression) expr);
            default:
                throw new IllegalStateException();
        }
    }

    private BLangAccessExpression cloneAccessExpr(BLangAccessExpression originalAccessExpr) {
        if (originalAccessExpr.expr == null) {
            return originalAccessExpr;
        }

        BLangExpression varRef;
        NodeKind kind = originalAccessExpr.expr.getKind();
        if (kind == NodeKind.FIELD_BASED_ACCESS_EXPR || kind == NodeKind.INDEX_BASED_ACCESS_EXPR) {
            varRef = cloneAccessExpr((BLangAccessExpression) originalAccessExpr.expr);
        } else {
            varRef = cloneExpression(originalAccessExpr.expr);
        }
        varRef.setBType(types.getSafeType(originalAccessExpr.expr.getBType(), true, false));

        BLangAccessExpression accessExpr;
        switch (originalAccessExpr.getKind()) {
            case FIELD_BASED_ACCESS_EXPR:
                accessExpr = ASTBuilderUtil.createFieldAccessExpr(varRef,
                        ((BLangFieldBasedAccess) originalAccessExpr).field);
                break;
            case INDEX_BASED_ACCESS_EXPR:
                accessExpr = ASTBuilderUtil.createIndexAccessExpr(varRef,
                        ((BLangIndexBasedAccess) originalAccessExpr).indexExpr);
                break;
            default:
                throw new IllegalStateException();
        }

        accessExpr.originalType = originalAccessExpr.originalType;
        accessExpr.pos = originalAccessExpr.pos;
        accessExpr.isLValue = originalAccessExpr.isLValue;
        accessExpr.symbol = originalAccessExpr.symbol;
        accessExpr.errorSafeNavigation = false;
        accessExpr.nilSafeNavigation = false;

        
        
        
        accessExpr.setBType(originalAccessExpr.originalType);
        return accessExpr;
    }

    private BLangBinaryExpr getModifiedIntRangeStartExpr(BLangExpression expr) {
        BLangLiteral constOneLiteral = ASTBuilderUtil.createLiteral(expr.pos, symTable.intType, 1L);
        return ASTBuilderUtil.createBinaryExpr(expr.pos, expr, constOneLiteral, symTable.intType, OperatorKind.ADD,
                (BOperatorSymbol) symResolver.resolveBinaryOperator(OperatorKind.ADD,
                        symTable.intType,
                        symTable.intType));
    }

    private BLangBinaryExpr getModifiedIntRangeEndExpr(BLangExpression expr) {
        BLangLiteral constOneLiteral = ASTBuilderUtil.createLiteral(expr.pos, symTable.intType, 1L);
        return ASTBuilderUtil.createBinaryExpr(expr.pos, expr, constOneLiteral, symTable.intType, OperatorKind.SUB,
                (BOperatorSymbol) symResolver.resolveBinaryOperator(OperatorKind.SUB,
                        symTable.intType,
                        symTable.intType));
    }

    BLangLiteral getBooleanLiteral(boolean value) {
        BLangLiteral literal = (BLangLiteral) TreeBuilder.createLiteralExpression();
        literal.value = value;
        literal.setBType(symTable.booleanType);
        literal.pos = symTable.builtinPos;
        return literal;
    }

    private BLangFunction createInitFunctionForClassDefn(BLangClassDefinition classDefinition, SymbolEnv env) {
        BType returnType = symTable.nilType;

        
        if (classDefinition.initFunction != null) {
            returnType = classDefinition.initFunction.getBType().getReturnType();
        }

        BLangFunction initFunction =
                TypeDefBuilderHelper.createInitFunctionForStructureType(classDefinition.symbol, env, names,
                        GENERATED_INIT_SUFFIX, classDefinition.getBType(), returnType);
        BObjectTypeSymbol typeSymbol = ((BObjectTypeSymbol) classDefinition.getBType().tsymbol);
        typeSymbol.generatedInitializerFunc = new BAttachedFunction(GENERATED_INIT_SUFFIX, initFunction.symbol,
                (BInvokableType) initFunction.getBType(), null);
        classDefinition.generatedInitFunction = initFunction;
        initFunction.returnTypeNode.setBType(returnType);
        return initFunction;
    }

    private void visitBinaryLogicalExpr(BLangBinaryExpr binaryExpr) {
        /*
         * Desugar (lhsExpr && rhsExpr) to following if-else:
         *
         * logical AND:
         * -------------
         * T $result$;
         * if (lhsExpr) {
         *    $result$ = rhsExpr;
         * } else {
         *    $result$ = false;
         * }
         *
         * logical OR:
         * -------------
         * T $result$;
         * if (lhsExpr) {
         *    $result$ = true;
         * } else {
         *    $result$ = rhsExpr;
         * }
         *
         */
        BLangSimpleVariableDef resultVarDef = createVarDef("$result$", binaryExpr.getBType(), null,
                                                           symTable.builtinPos);
        BLangBlockStmt thenBody = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);
        BLangBlockStmt elseBody = ASTBuilderUtil.createBlockStmt(binaryExpr.pos);

        
        BLangSimpleVarRef thenResultVarRef = ASTBuilderUtil.createVariableRef(symTable.builtinPos,
                                                                              resultVarDef.var.symbol);
        BLangExpression thenResult;
        if (binaryExpr.opKind == OperatorKind.AND) {
            thenResult = binaryExpr.rhsExpr;
        } else {
            thenResult = getBooleanLiteral(true);
        }
        BLangAssignment thenAssignment =
                ASTBuilderUtil.createAssignmentStmt(binaryExpr.pos, thenResultVarRef, thenResult);
        thenBody.addStatement(thenAssignment);

        
        BLangExpression elseResult;
        BLangSimpleVarRef elseResultVarRef = ASTBuilderUtil.createVariableRef(symTable.builtinPos,
                                                                              resultVarDef.var.symbol);
        if (binaryExpr.opKind == OperatorKind.AND) {
            elseResult = getBooleanLiteral(false);
        } else {
            elseResult = binaryExpr.rhsExpr;
        }
        BLangAssignment elseAssignment =
                ASTBuilderUtil.createAssignmentStmt(binaryExpr.pos, elseResultVarRef, elseResult);
        elseBody.addStatement(elseAssignment);

        
        BLangSimpleVarRef resultVarRef = ASTBuilderUtil.createVariableRef(binaryExpr.pos, resultVarDef.var.symbol);
        BLangIf ifElse = ASTBuilderUtil.createIfElseStmt(binaryExpr.pos, binaryExpr.lhsExpr, thenBody, elseBody);

        BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(binaryExpr.pos, Lists.of(resultVarDef, ifElse));
        BLangStatementExpression stmtExpr = createStatementExpression(blockStmt, resultVarRef);
        stmtExpr.setBType(binaryExpr.getBType());

        result = rewriteExpr(stmtExpr);
    }

    protected boolean isMappingOrObjectConstructorOrObjInit(BLangExpression expression) {
        switch (expression.getKind()) {
            case TYPE_INIT_EXPR:
            case RECORD_LITERAL_EXPR:
            case OBJECT_CTOR_EXPRESSION:
                return true;
            case CHECK_EXPR:
                return isMappingOrObjectConstructorOrObjInit(((BLangCheckedExpr) expression).expr);
            case TYPE_CONVERSION_EXPR:
                return isMappingOrObjectConstructorOrObjInit(((BLangTypeConversionExpr) expression).expr);
            default:
                return false;
        }
    }

    private BType getRestType(BInvokableSymbol invokableSymbol) {
        if (invokableSymbol != null && invokableSymbol.restParam != null) {
            return invokableSymbol.restParam.type;
        }
        return null;
    }

    private BType getRestType(BLangFunction function) {
        if (function != null && function.restParam != null) {
            return function.restParam.getBType();
        }
        return null;
    }

    private BVarSymbol getRestSymbol(BLangFunction function) {
        if (function != null && function.restParam != null) {
            return function.restParam.symbol;
        }
        return null;
    }

    private boolean isComputedKey(RecordLiteralNode.RecordField field) {
        if (!field.isKeyValueField()) {
            return false;
        }
        return ((BLangRecordLiteral.BLangRecordKeyValueField) field).key.computedKey;
    }

    private BLangRecordLiteral rewriteMappingConstructor(BLangRecordLiteral mappingConstructorExpr) {
        List<RecordLiteralNode.RecordField> fields = mappingConstructorExpr.fields;

        BType type = mappingConstructorExpr.getBType();
        Location pos = mappingConstructorExpr.pos;

        List<RecordLiteralNode.RecordField> rewrittenFields = new ArrayList<>(fields.size());

        for (RecordLiteralNode.RecordField field : fields) {
            if (field.isKeyValueField()) {
                BLangRecordLiteral.BLangRecordKeyValueField keyValueField =
                        (BLangRecordLiteral.BLangRecordKeyValueField) field;

                BLangRecordLiteral.BLangRecordKey key = keyValueField.key;
                BLangExpression origKey = key.expr;
                BLangExpression keyExpr;
                if (key.computedKey) {
                    keyExpr = origKey;
                } else {
                    keyExpr = origKey.getKind() == NodeKind.SIMPLE_VARIABLE_REF ? createStringLiteral(pos,
                            Utils.unescapeJava(((BLangSimpleVarRef) origKey).variableName.value)) :
                            ((BLangLiteral) origKey);
                }

                BLangRecordLiteral.BLangRecordKeyValueField rewrittenField =
                        ASTBuilderUtil.createBLangRecordKeyValue(rewriteExpr(keyExpr),
                                                                 rewriteExpr(keyValueField.valueExpr));
                rewrittenField.pos = keyValueField.pos;
                rewrittenField.key.pos = key.pos;
                rewrittenFields.add(rewrittenField);
            } else if (field.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
                BLangSimpleVarRef varRefField = (BLangSimpleVarRef) field;
                rewrittenFields.add(ASTBuilderUtil.createBLangRecordKeyValue(
                        rewriteExpr(createStringLiteral(pos, Utils.unescapeJava(varRefField.variableName.value))),
                        rewriteExpr(varRefField)));
            } else {
                BLangRecordLiteral.BLangRecordSpreadOperatorField spreadOpField =
                        (BLangRecordLiteral.BLangRecordSpreadOperatorField) field;
                spreadOpField.expr = rewriteExpr(spreadOpField.expr);
                rewrittenFields.add(spreadOpField);
            }
        }

        fields.clear();
        return new BLangMapLiteral(pos, type, rewrittenFields);
    }

    protected void addTransactionInternalModuleImport() {
        if (!env.enclPkg.packageID.equals(PackageID.TRANSACTION_INTERNAL)) {
            BLangImportPackage importDcl = (BLangImportPackage) TreeBuilder.createImportPackageNode();
            List<BLangIdentifier> pkgNameComps = new ArrayList<>();
            pkgNameComps.add(ASTBuilderUtil.createIdentifier(env.enclPkg.pos, Names.TRANSACTION.value));
            importDcl.pkgNameComps = pkgNameComps;
            importDcl.pos = env.enclPkg.symbol.pos;
            importDcl.orgName = ASTBuilderUtil.createIdentifier(env.enclPkg.pos, Names.BALLERINA_INTERNAL_ORG.value);
            importDcl.alias = ASTBuilderUtil.createIdentifier(env.enclPkg.pos, "trx");
            importDcl.version = ASTBuilderUtil.createIdentifier(env.enclPkg.pos, "");
            importDcl.symbol = symTable.internalTransactionModuleSymbol;
            env.enclPkg.imports.add(importDcl);
            env.enclPkg.symbol.imports.add(importDcl.symbol);
        }
    }
}