fzyzcjy / flutter_rust_bridge

Flutter/Dart <-> Rust binding generator, feature-rich, but seamless and simple.
https://fzyzcjy.github.io/flutter_rust_bridge/
MIT License
4.12k stars 283 forks source link

[Bug] Windows is hitting unreachable!() on enum invariant #923

Closed tmpfs closed 1 year ago

tmpfs commented 1 year ago

Describe the bug

On Windows I am hitting an unreachable!() on an enum invariant in the generated rust bindings.

The error message in the unreachable is this:

Invalid variant for GeneratorType: 16

On MacOS, iOS and Android the code works as expected.

The generated Rust code that throws the error is:

impl Wire2Api<GeneratorType> for i32 {
    fn wire2api(self) -> GeneratorType {
        match self {
            0 => GeneratorType::LettersNumbers,
            1 => GeneratorType::Numbers,
            2 => GeneratorType::Random,
            _ => unreachable!("Invalid variant for GeneratorType: {}", self),
        }
    }
}

The type definitions for the relevant types are:

/// Enumeration of the types of password generation.
pub enum GeneratorType {
    /// Use letters and numbers.
    LettersNumbers,
    /// Use numbers only.
    Numbers,
    /// Use letters, numbers and punctuation.
    Random,
}

/// Options for password generation.
pub struct PasswordGenerator {
    /// Number of passwords to generate.
    pub count: usize,
    /// Generator type.
    pub generator: GeneratorType,
    /// Length of each generated password.
    pub length: usize,
}

The binding function I am calling looks like this:

/// Generate a list of passwords.
pub fn generate_passwords(
    options: PasswordGenerator,
) -> Result<Vec<GeneratedPassword>> {
    let passgen = match options.generator {
        GeneratorType::LettersNumbers => {
            PasswordGen::new_alpha_numeric(options.length)
        }
        GeneratorType::Numbers => PasswordGen::new_numeric(options.length),
        GeneratorType::Random => {
            PasswordGen::new_ascii_printable(options.length)
        }
    };

    let passwords = generate_password(passgen, options.count)?;
    Ok(passwords
        .into_iter()
        .map(|(p, s)| GeneratedPassword {
            value: p.expose_secret().to_owned(),
            score: s,
        })
        .collect())
}

And the dart code that calls out looks like this:

  void _setup() async {
    final options =
        PasswordGenerator(count: 5, length: _amount, generator: _generatorType);
    final passwords = await api.generatePasswords(options: options);
    _selectedSuggestion = passwords[0];
    suggestions.add(passwords);
    widget.onChanged(_selectedSuggestion!.value);
  }

The Wire2Api implementation seems fine:

impl Wire2Api<PasswordGenerator> for wire_PasswordGenerator {
    fn wire2api(self) -> PasswordGenerator {
        PasswordGenerator {
            count: self.count.wire2api(),
            generator: self.generator.wire2api(),
            length: self.length.wire2api(),
        }
    }
}

The very interesting thing is that the default value for length (_amount in the dart code) is 16 which appears in the invariant error message.

So it appears that only on Windows fields are getting mixed up, any ideas?

Codegen logs with RUST_LOG=debug environment variable

N/A - cannot include otherwise Github says the comment is too long.

To Reproduce

No response

Expected behavior

No response

Generated binding code

No response

OS

Windows

Version of flutter_rust_bridge_codegen

1.57

Flutter info

No response

Version of clang++

No response

Version of ffigen

6.1.2

Additional context

No response

fzyzcjy commented 1 year ago

Weird... Could you please make a (more) minimal reproducible sample, so the problem can be spotted more easily?

tmpfs commented 1 year ago

could you please modify it manually (as a hack) e.g.

impl Wire2Api<GeneratorType> for i32 {
    fn wire2api(self) -> GeneratorType {
        println!("hack! {}", self); <------
        match self {
            0 => GeneratorType::LettersNumbers,
            1 => GeneratorType::Numbers,
            2 => GeneratorType::Random,
            _ => unreachable!("Invalid variant for GeneratorType: {}", self),
        }
    }
}

and see the value

This just prints 16 as expected.

tmpfs commented 1 year ago

Weird... Could you please make a (more) minimal reproducible sample, so the problem can be spotted more easily?

Sure, I will try, it will take a while though as it is quite time consuming to set all this up for a new project.

I will try to debug the generated bindings a bit first on Windows to see if I can determine the cause of the problem.

fzyzcjy commented 1 year ago

This just prints 16 as expected.

Yes I realized that later

tmpfs commented 1 year ago

FYI, this has existed for a while. I was using 1.46 and upgraded to 1.57 so see if a fix was present.

tmpfs commented 1 year ago

Just upgraded ffigen from 6.1.2 to 7.2.4 and this part of the diff seems interesting:

 class wire_PasswordGenerator extends ffi.Struct {
-  @uintptr_t()
+  @ffi.UintPtr()
   external int count;

   @ffi.Int32()
   external int generator;

-  @uintptr_t()
+  @ffi.UintPtr()
   external int length;
 }

Just ran it on Windows and the problem has disappeared. What luck! 🥳

fzyzcjy commented 1 year ago

Happy to hear that!

github-actions[bot] commented 1 year ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new issue.