ballerina-platform / ballerina-lang

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

[Bug]: `effectiveType` of `immutableType`s of anonymous record types are not set properly #42110

Open Nadeeshan96 opened 8 months ago

Nadeeshan96 commented 8 months ago

Description

$title

To create a record value using the ValueCreator runtime API, I passed the correct target type and observed the created record value. Though the created record value is created , its type is not initialized properly. e.g. effectiveType of immutableTypes of anonymous record types are not set properly. Because of this freezeDirect() is not working properly.

Note that the below code snippet is working with Ballerina 2201.8.4 (Swan Lake Update 8). It is only not working with the current master branch. - May be due to record init runtime changes.

Steps to Reproduce

public function main() {
    testCloneWithTypeTableToAnydata();
}

type ReadOnlyAnydata anydata & readonly;

function testCloneWithTypeTableToAnydata() {
    table<record {readonly int 'key; string value;}> key('key) tableVal = table key('key) [
        {'key: 1, value: "foo"}
        ];

    anydata|(anydata & readonly) tableValCloned = checkpanic tableVal.cloneWithType(anydata);

    anydata|(anydata & readonly) tableValCloned2 = checkpanic tableVal.cloneWithType(ReadOnlyAnydata);

    table<record {int 'key; readonly string value;}> key(value) tableVal2 = checkpanic tableValCloned.cloneWithType();

    table<record {int 'key; readonly string value;}> key(value) tableVal3 = checkpanic tableValCloned2.cloneWithType();

    json vvvv = [{"key": 1, "value": "foo"}];
    table<record {int 'key; readonly string value;}> key(value) & readonly tableVal4 =
        checkpanic vvvv.cloneWithType();

    table<record {int 'key; readonly string value;}> key(value) & readonly tableVal5 =
        checkpanic tableValCloned2.cloneWithType();
}

gives

Running executable

error: {ballerina/lang.value}ConversionError {"message":"value type 'record {| int key; readonly string value; anydata...; |} & readonly' inconsistent with the inherent table type 'table<(record {| int key; readonly string value; anydata...; |} & readonly)> key(value) & readonly'"}
        at ballerina.lang.value.0:cloneWithType(value.bal:114)
           immutable:testCloneWithTypeTableToAnydata(immutable.bal:22)
           immutable:main(immutable.bal:2

Affected Version(s)

master branch

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

Simplified code to reproduce this.


public function main() {
    anydata tableValCloned = table key('key) [{'key: 1, value: "foo"}];
    table<record {int 'key; readonly string value;}> key(value) tableVal2 = checkpanic tableValCloned.cloneWithType();
    // If I remove the above lines the error disappears
    json vvvv = [{"key": 1, "value": "foo"}];
    table<record {int 'key; readonly string value;}> key(value) & readonly tableVal4 = checkpanic vvvv.cloneWithType();
}
HindujaB commented 2 months ago

The issue seems to be due to the anonymous type names that are passed from compilation. In the above code, there two inline record types are used with same definition record {| int key; readonly string value; anydata...; |} during the cloning.

  1. For tableVal2 creation
  2. For tableVal4 creation - as the target type is an immutable type and source value is mutable, the value converter uses the mutable type of the target type (record {| int key; readonly string value; anydata...; |}) and use readonly cloning afterwards.

We use a type name based switch case at the runtime value creation where both the types have the same type name record {| int key; readonly string value; anydata...; |}. Therefore, the value creation returns a wrong value (of tableVal2 typed) which causes the type checker failure. We need to pass a unique name for each anon types to handle cases like this.

Screenshot 2024-08-12 at 15 52 46

As per the offline discussion with @chiranSachintha, this need to be handled from the compilation. Because both the switch case and record type name passing are generated from the compiled package, it should be consistent to handle runtime value creator API usages.