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

Enum impl fn gives name conflict error #1715

Open
cretz opened this issue Aug 8, 2019 · 8 comments
Open

Enum impl fn gives name conflict error #1715

cretz opened this issue Aug 8, 2019 · 8 comments
Labels

Comments

@cretz
Copy link

cretz commented Aug 8, 2019

Based on the comment at #1496 (comment), this gives an error:

#[wasm_bindgen]
#[derive(Copy, Clone)]
pub enum ImageFormat {
    PNG,
    JPEG,
    GIF,
}

#[wasm_bindgen]
impl ImageFormat {
    #[wasm_bindgen]
    pub fn from_str(s: &str) -> Result<ImageFormat, JsValue> {
        match s {
            "PNG" => Ok(ImageFormat::PNG),
            "JPEG" => Ok(ImageFormat::JPEG),
            "GIF" => Ok(ImageFormat::GIF),
            _ => Err(JsValue::from(js_sys::Error::new(&format!("Unknown format: {}", s)))),
        }
    }
}

The error is:

error: cannot shadow already defined class `ImageFormat`

The wasm-bindgen version in my lock file is 0.2.48.

@cretz cretz added the bug label Aug 8, 2019
@GeorgeScrivener
Copy link

GeorgeScrivener commented Apr 2, 2020

I'm running in to this as well using version 0.2.60 , is there a workaround for it?
I'm happy to help track it down but would need some pointers as I'm unfamiliar with the codebase. @alexcrichton where's the best place to start?

@alexcrichton
Copy link
Contributor

The code to fix here is likely around crates/cli-support/src/js/* and this may just be a bug in generation or it may be something where we need to generate some bindings before others. I'm not entirely sure where the bug lies here myself, unfortunately.

@GeorgeScrivener
Copy link

GeorgeScrivener commented Apr 11, 2020

The problem seems to be that the class gets generated twice here:

    pub fn generate(&mut self) -> Result<(), Error> {
        for (id, adapter) in crate::sorted_iter(&self.wit.adapters) {
            let instrs = match &adapter.kind {
                AdapterKind::Import { .. } => continue,
                AdapterKind::Local { instructions } => instructions,
            };
            self.generate_adapter(*id, adapter, instrs)?; // << First here as a Method
        }

        ...

        for e in self.aux.enums.iter() {
            self.generate_enum(e)?; // << Then again here
        }
        ...

Enum's are currently generated as JS objects like this:
export const EnumName = Object.freeze({ A:0,B:1,... });

What is the JS we expect to generate for methods on enums?

@alexcrichton
Copy link
Contributor

I don't know what the expected JS for this is, it may be the case that JS doesn't support methods-on-enums.

@Pauan
Copy link
Contributor

Pauan commented Apr 13, 2020

@alexcrichton Enums don't exist in JavaScript at all. They do exist in TypeScript, but they do not support methods at all, they are just simple C-style enums.

For example, this...

enum Foo {
    Bar,
    Qux,
    Corge,
}

...gets compiled by TypeScript into this:

(function (Foo) {
    Foo[Foo["Bar"] = 0] = "Bar";
    Foo[Foo["Qux"] = 1] = "Qux";
    Foo[Foo["Corge"] = 2] = "Corge";
})(Foo || (Foo = {}));

This is very similar to what wasm-bindgen currently generates.

TypeScript also supports "ADT-style" enums, which look like this:

type Foo
    = { type: "bar", value: number }
    | { type: "qux" }
    | { type: "corge", first: string, second: string };

These are translated into normal JS objects (e.g. { type: "corge", first: "yes", second: "no" }), and they also do not support methods at all, they are just raw data.

@alexcrichton
Copy link
Contributor

I think this is perhaps an issue we can't fix then? We can likely provide a better error message but methods-on-enums may just not be supported.

@Pauan
Copy link
Contributor

Pauan commented Apr 13, 2020

Yeah, I don't see any good ways to support methods on enums:

  1. We could translate them into regular JS functions (not methods). That seems... surprising. And it would cause name conflicts.

  2. We could translate enums into classes, but that would break conventions with TypeScript, which also seems bad. And that won't work with ADT-style enums (if we ever support those in the future).

We definitely should have a better error message though.

@Pauan
Copy link
Contributor

Pauan commented Apr 13, 2020

As far as users are concerned, the solution for them is to export regular functions (not methods), like this:

#[wasm_bindgen]
#[derive(Copy, Clone)]
pub enum ImageFormat {
    PNG,
    JPEG,
    GIF,
}

#[wasm_bindgen]
pub fn from_str(s: &str) -> Result<ImageFormat, JsValue> {
    match s {
        "PNG" => Ok(ImageFormat::PNG),
        "JPEG" => Ok(ImageFormat::JPEG),
        "GIF" => Ok(ImageFormat::GIF),
        _ => Err(JsValue::from(js_sys::Error::new(&format!("Unknown format: {}", s)))),
    }
}

#[wasm_bindgen]
pub fn foo(x: ImageFormat) {
    // ...
}

Now everything works fine, both in the code generation and the TypeScript types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants