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

Can't construct struct with named field #400

Closed
JohnnnyWang opened this issue Jun 2, 2022 · 2 comments · Fixed by #589
Closed

Can't construct struct with named field #400

JohnnnyWang opened this issue Jun 2, 2022 · 2 comments · Fixed by #589
Labels
enhancement New feature or request

Comments

@JohnnnyWang
Copy link

Hi,
I can't construct struct like this:

let action = SendAction {
                   seq: 1,
                   target_id: "123",
                   headers: [],
                   body_bytes: Bytes::new(),
                   wait_strategy: RuneWaitStrategy::Ignore
              };

its will cause an error:

 missing runtime information for variant with hash 0xe49bb8eb2c9a12e7 (at inst 40) 

I know I must be doing something wrong, but I don't know how to debug and fix it.

Here is my rune code

#[cfg(test)]
mod test {
    // rune 0.11.0 from git master
    // rune-modules 0.11.0
    // rust version: 1.61.0
    // windows

    use std::sync::Arc;
    use std::time::Duration;

    use rune::runtime::{SyncFunction, VmError};
    use rune::termcolor::{ColorChoice, StandardStream};
    use rune::{Context, ContextError, Diagnostics, FromValue, Module, Vm};

    #[derive(Debug, Clone, rune::Any)]
    pub enum RuneWaitStrategy {
        #[rune(constructor)]
        Ignore,
        #[rune(constructor)]
        Must(#[rune(get)] Dur),
        #[rune(constructor)]
        AsyncOnError,
        #[rune(constructor)]
        AsyncAlways,
    }

    #[derive(Debug, Clone, rune::Any)]
    pub struct SendAction {
        #[rune(get, set)]
        pub seq: u64,
        #[rune(get, set)]
        pub target_id: String,
        #[rune(get, set)]
        pub headers: Vec<(String, String)>,
        #[rune(get, set)]
        pub body_bytes: rune::runtime::Bytes,
        #[rune(get, set)]
        pub wait_strategy: RuneWaitStrategy,
    }

    #[derive(Debug, Clone, rune::Any)]
    pub struct Dur {
        pub inner: Duration,
    }

    fn module() -> Result<Module, ContextError> {
        let mut module = Module::with_crate("m_action");
        module.ty::<SendAction>()?;
        module.ty::<RuneWaitStrategy>()?;
        module.ty::<Dur>()?;

        Ok(module)
    }

    #[test]
    pub fn test() {
        let mut sources = rune::sources! {
            entry => {
                use m_action::*;
                use std::bytes::*;
                fn next(seq, events) {
                   let action = SendAction{
                        seq: 1,
                        target_id: "123",
                        headers: [],
                        body_bytes: Bytes::new(),
                        wait_strategy: RuneWaitStrategy::Ignore

                    };

                    // but this works
                    //let action = SendAction::new(...)

                    action
                }
                pub fn main() {
                    next
                }
            }
        };

        let m = module().unwrap();
        let mut context = Context::with_default_modules().unwrap();

        context.install(&m).unwrap();
        let runtime = Arc::new(context.runtime());
        let mut diagnostics = Diagnostics::new();
        let result = rune::prepare(&mut sources)
            .with_context(&context)
            .with_diagnostics(&mut diagnostics)
            .build();

        if !diagnostics.is_empty() {
            let mut writer = StandardStream::stderr(ColorChoice::Always);
            diagnostics.emit(&mut writer, &sources).unwrap();
        }

        let unit = result.unwrap();

        let mut vm = Vm::new(runtime, Arc::new(unit));
        let controller_f = SyncFunction::from_value(vm.call(&["main"], ()).unwrap()).unwrap();

        // error here: MissingRtti hash: xxx
        let mut action: SendAction = controller_f.call((1, 2)).unwarp();
    }
}
@udoprog
Copy link
Collaborator

udoprog commented Jun 3, 2022

Thanks for the report!

@jbdutton
Copy link
Contributor

jbdutton commented Jul 26, 2023

So I think the issue lies in try_lookup_meta. It forgets to add the meta to the UnitBuilder, so ultimately the compiled Unit is missing the run-time type information (RTTI) when the VM tries to construct the object later on.

All that's needed is to include that extra line, like we do in the index_meta call:

self.unit.insert_meta(location.as_spanned(), &meta, self.pool, self.inner)?;
self.insert_meta(meta.clone()).with_span(location.as_spanned())?;

Then something like this will work fine:

 pub fn main() {
     let external = External {
         first: 1,
         second: "two",
     };

     external
}

But this fails if you want to extract the value from the VM:

let output = vm.call(["main"], ())?;
let output: External = rune::from_value(output)?;
println!("{:?}", output);
Error: Expected `Any` type, but found `External`

Because the from_value implementation is expecting a Value::Any, when it's actually a Value::Struct. That means the VM is actually treating the External Rust struct as a Rune struct, just reusing the field names. You can debug the Value and see the correct result, or coerce it into a Shared<Struct> and interact with it as an object using .get("first").

To get an actual External type from the VM, I think we'd need to push an instance of External onto the stack as an Value::Any, which would require associating a constructor function with the type like enum variants have now. See my draft PR (#589) for an attempt at that.

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

Successfully merging a pull request may close this issue.

3 participants