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

Generated code from Enum in Rust has errors #60

Open
itrumper opened this issue Dec 15, 2023 · 5 comments · May be fixed by #95
Open

Generated code from Enum in Rust has errors #60

itrumper opened this issue Dec 15, 2023 · 5 comments · May be fixed by #95
Labels
bug Something isn't working

Comments

@itrumper
Copy link
Contributor

Hello, and thank you very much for this crate! It is a big help for my project.

I have the following Rust code:

#[derive(uniffi::Enum)]
pub enum Shape {
    Rectangle { s: Rectangle },
    Ellipse { s: Ellipse },
}

#[derive(uniffi::Record)]
pub struct Rectangle {
    width: f64,
    height: f64,
}

#[derive(uniffi::Record)]
pub struct Ellipse {
    x_radius: f64,
    y_radius: f64,
}

which generates the following C# code when using --library mode:

public record Shape {
    
    public record Rectangle (
        Rectangle @s
    ) : Shape {}
    
    public record Ellipse (
        Ellipse @s
    ) : Shape {}

}

The C# Rectangle and Ellipse records both contain the error: "The primary constructor conflicts with the synthesized copy constructor." Which is documented by Microsoft.

There is also the following generated code:

class FfiConverterTypeShape : FfiConverterRustBuffer<Shape>{
    public static FfiConverterRustBuffer<Shape> INSTANCE = new FfiConverterTypeShape();

    public override Shape Read(BigEndianStream stream) {
        var value = stream.ReadInt();
        switch (value) {
            case 1:
                return new Shape.Rectangle(
                    FfiConverterTypeRectangle.INSTANCE.Read(stream)
                );
            case 2:
                return new Shape.Ellipse(
                    FfiConverterTypeEllipse.INSTANCE.Read(stream)
                );
            default:
                throw new InternalException(String.Format("invalid enum value '{0}' in FfiConverterTypeShape.Read()", value));
        }
    }

    public override int AllocationSize(Shape value) {
        switch (value) {
            case Shape.Rectangle variant_value:
                return 4
                    + FfiConverterTypeRectangle.INSTANCE.AllocationSize(variant_value.@s);
            case Shape.Ellipse variant_value:
                return 4
                    + FfiConverterTypeEllipse.INSTANCE.AllocationSize(variant_value.@s);
            default:
                throw new InternalException(String.Format("invalid enum value '{0}' in FfiConverterTypeShape.AllocationSize()", value));
        }
    }

    public override void Write(Shape value, BigEndianStream stream) {
        switch (value) {
            case Shape.Rectangle variant_value:
                stream.WriteInt(1);
                FfiConverterTypeRectangle.INSTANCE.Write(variant_value.@s, stream);
                break;
            case Shape.Ellipse variant_value:
                stream.WriteInt(2);
                FfiConverterTypeEllipse.INSTANCE.Write(variant_value.@s, stream);
                break;
            default:
                throw new InternalException(String.Format("invalid enum value '{0}' in FfiConverterTypeShape.Write()", value));
        }
    }
}

which has the error "Cannot convert from 'uniffi.uniffi_lib.Rectangle' to 'uniffi.uniffi_lib.Shape.Rectangle" for all cases. The same in reverse conversion too. Have you seen this error before? Do you have suggestions to help remedy this issue? I have not seen any limitations on support for Enums in this crate, just external types.

Thank you!

@arg0d arg0d added the bug Something isn't working label Dec 15, 2023
@arg0d
Copy link
Collaborator

arg0d commented Dec 15, 2023

Hey, thanks for reporting this! I'm not exactly sure why this error happens. Need to look into this deeper, but don't have time right now. It seems like using the same identifier for both the struct and enum variant causes this issue.

@arg0d
Copy link
Collaborator

arg0d commented Dec 15, 2023

Oh, I think I understand. The inner record constructor Shape.Rectangle is confused about which Rectangle its supposed to refer to. I'm guessing Rectangle @s parameter is referring to itself, not the Rectangle defined in top level scope.

public record Rectangle(double @width, double @height) { }
public record Shape
{
                     ____________
                     ∨          ∧
    public record Rectangle(Rectangle @s) : Shape { }
    public record Ellipse(Ellipse @s) : Shape { }
}

@itrumper
Copy link
Contributor Author

Thank you for the help! You are correct, the records sharing the same name was causing the error. As a work-around, I re-named my Rust variants by appending an E (for Enum):

#[derive(uniffi::Enum)]
pub enum Shape {
    RectangleE { s: Rectangle },
    EllipseE { s: Ellipse },
}

#[derive(uniffi::Record)]
pub struct Rectangle {
    width: f64,
    height: f64,
}

#[derive(uniffi::Record)]
pub struct Ellipse {
    x_radius: f64,
    y_radius: f64,
}

This resolves the C# issues. Is this behavior that you expect to change? I don't see a good way to handle this conversion automatically, as it involves changing the names of the records. In either case, I think this should be a documented behavior, alongside the unsupported features. Personally, I am going to append E to all my enum variants as that is easy for me to work around this behavior, so this issue can be closed on my end, but feel free to leave it open if that is how you would like to track it.

@itrumper
Copy link
Contributor Author

My proposal for documenting this behavior is in #61

@arg0d
Copy link
Collaborator

arg0d commented Dec 22, 2023

Gonna keep this issue open. I want to experiment with prefixing enum constructor parameters with global:: when I have more time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
2 participants