ballerina-platform / ballerina-lang

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

[Bug]: Invalid type set for value created by `lang.array:iterator` #43301

Open MaryamZi opened 2 months ago

MaryamZi commented 2 months ago

Description

$title, uses lang.array:ArrayIterator rather than a specific type. Identified while looking into an issue raised by @DimuthuMadushan.

This may have been intentional at the time, to avoid creating multiple types.

Steps to Reproduce

import ballerina/io;

public function main() {
    int[] x = [];

    object {public isolated function next() returns record {|int value;|}?;} iterator = x.iterator();

    io:println(iterator is object {public isolated function next() returns record {|int value;|}?;});
}

Gives the always true warning, but prints false.

Affected Version(s)

2201.10.0-RC2

OS, DB, other environment details and versions

No response

Related area

-> Runtime

Related issue(s) (optional)

No response

Suggested label(s) (optional)

No response

Suggested assignee(s) (optional)

No response

HindujaB commented 2 months ago

This is happening for map:iterator() calls as well. This is because the runtime TypeChecker returns false for the return type checking for the next function. In the array iterator implementation, we use a generic return type Type for the array element type which is referred to any|error. Though the LHS type expected to return in the next function is int which is derived from the array variable x. Runtime typechecker fails because the expected type is int and the source type is any|error that cannot be directly assignable. I tried a similar implementation in a user defined bal code.

import ballerina/io;
import ballerina/lang.array;

type Type any|error;

public function main() {
    int[] x = [];
    object {
        public isolated function next() returns record {|int value;|}?;
    } iterator1 = iterator(x);
    io:println("array local - ", iterator1 is object {
                public isolated function next() returns record {|int value;|}?;
            });
}

public isolated function iterator(Type[] arr) returns object {
    public isolated function next() returns record {|
        Type value;
    |}?;
} {
    ArrayIterator arrIterator = new(arr);
    return arrIterator;
}

class ArrayIterator {
    private Type[] m;
    public isolated function init(Type[] m) {
        self.m = m;
    }
    # Return next member or nil if end of iteration is reached.
    # + return - iterator result
    public isolated function next() returns record {| Type value; |}? {
        // return externNext(self);
    }
}

This code fails with compilation error incompatible types: expected 'object { public isolated function next () returns (record {| int value; |}?); }', found 'object { public isolated function next () returns (record {| hinduja/test_listener:0.1.0:Type value; |}?); }' for the same reason runtime type-checker fails.

At compilation, it seems we special case these scenarios using TYPE_PARAM flags only for langlib methods that returns new resolved types without failing at type checking.

Should we do the same with the runtime type checking? @MaryamZi

In that case, we need to add additional validation to identify negative cases as we don't have direct access to the respective element type (of the x).

iterator is object { public isolated function next() returns record {|int value;|}?; } // should return true
iterator is object { public isolated function next() returns record {|string value;|}?; } // should return false
MaryamZi commented 2 months ago

I think this needs to be fixed to use the proper type. More a compile-time change, rather than a runtime change. This could result in repeatedly creating a lot of types, but we could avoid that to some extent by having some iterators predefined for builtin types.

cc @gimantha