Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Cannot cast spread record to record #43199

Closed
Shadow-Devil opened this issue Jul 31, 2024 · 5 comments
Closed

[Bug]: Cannot cast spread record to record #43199

Shadow-Devil opened this issue Jul 31, 2024 · 5 comments
Labels
Reason/Invalid Issue is invalid. Team/CompilerFE All issues related to Language implementation and Compiler, this exclude run times. Type/Bug userCategory/Compilation

Comments

@Shadow-Devil
Copy link
Contributor

Description

I don't quite understand why this cast is not allowed. Is this intended or a bug?

type A record {
    string a;
};

public function main() returns error? {
    record {} x = {a: "a"};
    A a = <A>{...x}; // missing non-defaultable required record field 'a'(BCE2520)
    A b = check {...x}.ensureType(); // works
}

Steps to Reproduce

No response

Affected Version(s)

Swanlake Update 9.2

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

@ballerina-bot ballerina-bot added needTriage The issue has to be inspected and labeled manually userCategory/Compilation labels Jul 31, 2024
@MaryamZi MaryamZi added Team/CompilerFE All issues related to Language implementation and Compiler, this exclude run times. and removed needTriage The issue has to be inspected and labeled manually labels Jul 31, 2024
@MaryamZi
Copy link
Member

When you use a cast with a (mapping) constructor expression, the type becomes the contextually-expected type for the (mapping) constructor. So it is effectively the same as

A a = {...x};

With the spread field, we look at the type of the expression used with spread - which is record {} in this case. To create a value of type A, a string field a is mandatory, and since we cannot guarantee that x has that field, these result in compile-time errors.

To do purely a cast, you can do

record {} x = {"a": "a"};
record {} y = {...x};
A a = <A>y;

But this will panic at runtime, since a is not a required field in record {} (which is the inherent type of the value created), and therefore, it can be modified to no longer have the a field. The value doesn't belong to A, and therefore, the cast panics.

For the same reason, ensureType() will also return an error at runtime.

A b = check {...x}.ensureType(); // allowed at compile-time, but returns an error at runtime

@Shadow-Devil
Copy link
Contributor Author

Oh that is a bit weird to me that the check {...x}.ensureType() does not work.
I came across this using the salesforce:Client, where there is currently a bug that the query returns a stream of specific records and not the correct type that is given in the type parameter (ballerina-platform/ballerina-library#6727).

I used the following code that was suggested in the issue to convert to a custom record type:

import ballerinax/salesforce;
type SalesforceCase record {|
    string Id;
|};

configurable salesforce:ConnectionConfig salesforceConfig = ?;

public function main() returns error? {
    salesforce:Client salesforce = check new (config = salesforceConfig);
    stream<record {}, error?> caseResponse = check salesforce->query(string `
        SELECT Id
        FROM Case
    `);
    final SalesforceCase[] cases = check from record {} entry in caseResponse
        select check {...entry}.ensureType();
}

Is this somehow different because it is in a query expression or is this due to what salesforce->query returns as its stream elements?

@Shadow-Devil
Copy link
Contributor Author

Shadow-Devil commented Jul 31, 2024

All different examples that I'm trying always end up with the runtime error that you described.
I'm really curious why it still works with the salesforce response stream.

It is probably because of some "native magic" that is done here

@MaryamZi
Copy link
Member

It probably works in the query case since the type of the constructed value comes from the select clause (related to #43006, ballerina-platform/ballerina-spec#1309).

You don't even need ensureType() there, right?

final SalesforceCase[] cases = check from SalesforceCase entry in caseResponse
    select {...entry};

This also works. Had to change SalesforceCase to be an open record either way.

Copy link

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.

@MaryamZi MaryamZi added the Reason/Invalid Issue is invalid. label Jul 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Reason/Invalid Issue is invalid. Team/CompilerFE All issues related to Language implementation and Compiler, this exclude run times. Type/Bug userCategory/Compilation
Projects
None yet
Development

No branches or pull requests

3 participants