ballerina-platform / ballerina-lang

The Ballerina Programming Language
https://ballerina.io/
Apache License 2.0
3.68k stars 753 forks source link

Runtime crashes for Query expression #37481

Open malinthar opened 2 years ago

malinthar commented 2 years ago

Description: Consider the following program

import ballerina/time;
import ballerina/io;

type ExchangeRate record {|
    string 'from;
    string to;
    string date;
    decimal rate;
|};

type Response record {
    boolean success;
    decimal result;
};

public type Subscriber record {|
    readonly string mobileNumber;
    readonly Conversion[] conversions;
|};

type Conversion record {|
    string to;
    string 'from;
|};

class Job {
    private map<Subscriber> subscribers;
    private map<string[]> conversions;

    isolated function init() {
        self.subscribers = {};
        self.conversions = {};
    }

    function getRatesAndSendSms(string date) returns error? {
        map<ExchangeRate[]> currentRates = {};
        check from Subscriber subscriber in self.subscribers
            do {
                ExchangeRate[] subscribedRates = [];
                check from Conversion conversion in subscriber.conversions
                    do {
                        if currentRates.hasKey(conversion.'from) {
                            ExchangeRate rate = currentRates.get(conversion.'from)
                            .filter(exchageRate => exchageRate.to.equalsIgnoreCaseAscii(conversion.to)).pop();
                            subscribedRates.push(rate);
                        }
                    };
            };
    }

}

public function main() {
    string date = string:substring(time:utcToString(time:utcNow()), 0, 10);
    Job job = new();
    error? ratesAndSendSms = job.getRatesAndSendSms(date);
    if ratesAndSendSms is error {
            io:print(ratesAndSendSms.message());
    }
}

I get the following stack trace when trying to run the following program.

Compiling source
        malintha/test_crash:0.1.0

Running executable

[2022-08-23 07:00:38,098] SEVERE {b7a.log.crash} - class io.ballerina.runtime.internal.values.BmpStringValue cannot be cast to class io.ballerina.runtime.internal.values.MapValue (io.ballerina.runtime.internal.values.BmpStringValue and io.ballerina.runtime.internal.values.MapValue are in unnamed module of loader 'app') 
java.lang.ClassCastException: class io.ballerina.runtime.internal.values.BmpStringValue cannot be cast to class io.ballerina.runtime.internal.values.MapValue (io.ballerina.runtime.internal.values.BmpStringValue and io.ballerina.runtime.internal.values.MapValue are in unnamed module of loader 'app')
        at malintha.test_crash.0.$value$Job.call(main.bal)
        at malintha.test_crash.0.main.main(main.bal:56)
        at malintha.test_crash.0.$_init.$lambda$main$(test_crash)
        at io.ballerina.runtime.internal.scheduling.SchedulerItem.execute(Scheduler.java:569)
        at io.ballerina.runtime.internal.scheduling.Scheduler.run(Scheduler.java:303)
        at io.ballerina.runtime.internal.scheduling.Scheduler.runSafely(Scheduler.java:270)
        at java.base/java.lang.Thread.run(Thread.java:829)

The issue seems to be with the query expression in the getRatesAndSendSms method. When I remove it the program runs without an issue.

Steps to reproduce: Run the above program

Affected Versions: 2201.2.0-rc2

pcnfernando commented 2 years ago

Seems to be an issue related to closure resolving related to https://github.com/ballerina-platform/ballerina-lang/issues/32710 Please try out the work-around below

import ballerina/time;
import ballerina/io;

type ExchangeRate record {|
    string 'from;
    string to;
    string date;
    decimal rate;
|};

type Response record {
    boolean success;
    decimal result;
};

public type Subscriber record {|
    readonly string mobileNumber;
    readonly Conversion[] conversions;
|};

type Conversion record {|
    string to;
    string 'from;
|};

class Job {
    private map<Subscriber> subscribers;
    private map<string[]> conversions;

    isolated function init() {
        self.subscribers = {};
        self.conversions = {};
    }

    function getRatesAndSendSms(string date) returns error? {
        map<ExchangeRate[]> currentRates = {};
        check from Subscriber subscriber in self.subscribers
            do {
                ExchangeRate[] subscribedRates = [];
                check from Conversion conversion in subscriber.conversions
                    let string conversionToVal = conversion.to //binding the erroneous closure symbol to a temp variable 
                    do {
                        if currentRates.hasKey(conversion.'from) {
                            ExchangeRate rate = currentRates.get(conversion.'from)
                            .filter(exchageRate => exchageRate.to.equalsIgnoreCaseAscii(conversionToVal)).pop();
                            subscribedRates.push(rate);
                        }
                    };
            };
    }

}

public function main() {
    string date = string:substring(time:utcToString(time:utcNow()), 0, 10);
    Job job = new ();
    error? ratesAndSendSms = job.getRatesAndSendSms(date);
    if ratesAndSendSms is error {
        io:print(ratesAndSendSms.message());
    }
}