ballerina-platform / ballerina-lang

The Ballerina Programming Language

Home Page:https://ballerina.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Bug]: Annotation values with mapping values panic at runtime for const annotations

MaryamZi opened this issue · comments

Description

$title.

Steps to Reproduce

public type AnnotationRecord record {|
    string summary?;
    Examples examples?;
|};

public type Examples record {|
    map<ExampleItem> response;
|};

public type ExampleItem record {
    map<string> headers?;
};

const annotation AnnotationRecord annot on type;

@annot {
    examples: {
        response: {}
    }
}
type Employee record {|
    int id;
    string name;
|};
error: {ballerina/lang.map}InherentTypeViolation {"message":"invalid value for record field 'examples': expected value of type 'record {| readonly record {| |} & readonly response; |} & readonly', found 'Examples'"}
        at x:$annot_func$_0(demo.bal:16)

The value seems to be created as a mutable value.

Works when Examples is updated to be an immutable record (since the value is then created as an immutable value due to Examples' type).

public type Examples readonly & record {|
    map<ExampleItem> response;
|};

Affected Version(s)

2201.9.1 and older versions

OS, DB, other environment details and versions

No response

Related area

-> Compilation

Related issue(s) (optional)

No response

Suggested label(s) (optional)

No response

Suggested assignee(s) (optional)

No response

When creating the annotation value, we make it immutable by invoking cloneReadonly. The problem occurs when populating the initial values of the record.
In the above example,

public type AnnotationRecord record {|
    string summary?;
    Examples examples?;
|};

When creating the AnnotationRecord value, we use the type of the field as Examples, and do a istypecheck with the type of the field. That is immutable.

We can fix this by making the field read-only as well.

public type AnnotationRecord record {|
    string summary?;
    Examples & readonly examples?;
|};

public type Examples record {|
    map<ExampleItem> response;
|};

public type ExampleItem record {
    map<string> headers?;
};

const annotation AnnotationRecord annot on type;

@annot {
    examples: {
        response: {}
    }
}
type Employee record {|
    int id;
    string name;
|};

The problem occurs, we call cloneReadonly on {examples: {response: {}}} and set the type correctly. However, the type of {response: {}} is set to Examples, and we do not create cloneReadonly for this.

As a workaround for this issue, we can define the Examples type as read-only.

This issue is NOT closed with a proper Reason/ label. Make sure to add proper reason label before closing. Please add or leave a comment with the proper reason label now.

      - Reason/EngineeringMistake - The issue occurred due to a mistake made in the past.
      - Reason/Regression - The issue has introduced a regression.
      - Reason/MultipleComponentInteraction - Issue occured due to interactions in multiple components.
      - Reason/Complex - Issue occurred due to complex scenario.
      - Reason/Invalid - Issue is invalid.
      - Reason/Other - None of the above cases.