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

crucible-mir crashes due to improperly constructing vtable shim types #1222

Open
Torrencem opened this issue Jul 16, 2024 · 6 comments
Open
Labels
bug crucible MIR Issues relating to Rust/MIR support

Comments

@Torrencem
Copy link

I've installed saw and mir-json on my system, and I think I have the right version of everything set up.

$ saw --version
1.1
$ saw-rustc --version
test build - extract output path - ["rustc", "--version", "--test", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs"]
rustc 1.69.0-nightly (5e37043d6 2023-01-22)
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/bin/mir-json-rustc-wrapper.rs:86:27
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

(kind of weird version is panicking but you can see the rustc version is correct in there)

When I try to compile the following:

fn add(x: u32, y: u32) -> u32 {
x + y
}

it works fine, I'm able to handle it with the following saw script:

enable_experimental;

m <- mir_load_module "hello.linked-mir.json";

then I run saw-rustc, which succeeds

$ saw-rustc hello.rs
test build - extract output path - ["rustc", "hello.rs", "--test", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs"]
test build - ["rustc", "hello.rs", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--cfg", "crux_top_level", "--crate-type", "rlib"]
warning: function `add` is never used
 --> hello.rs:1:4
  |
1 | fn add(x: u32, y: u32) -> u32 {
  |    ^^^
  |
  = note: `#[warn(dead_code)]` on by default

note: Emitting MIR for hello/a2764e5c::add

warning: 1 warning emitted

linking 19 mir files into hello.linked-mir.json
  inputs: libhello.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libstd.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcore.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcompiler_builtins.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/liballoc.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcrucible.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/liblibc.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/librustc_std_workspace_core.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libunwind.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcfg_if.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libminiz_oxide.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libadler.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libhashbrown.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libstd_detect.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/librustc_demangle.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libaddr2line.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libgimli.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libobject.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libmemchr.mir
generated test script hello

when I run saw on this, it succeeds, telling me my installation is at least somewhat working:

$ saw hello.saw
[17:24:19.173] Loading file "/Users/sarah.spall/projects/provers/aes_rust_colin/src/hello.saw"

however, if I then try a more complicated function that uses IO, like the following:

fn main() {
println!("hello world");
}

I can compile this with saw-rustc:

test build - extract output path - ["rustc", "hello.rs", "--test", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs"]
test build - ["rustc", "hello.rs", "--target", "aarch64-apple-darwin", "--cfg", "crux", "-L", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--sysroot", "/Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs", "--cfg", "crux_top_level", "--crate-type", "rlib"]
warning: function `main` is never used
 --> hello.rs:1:4
  |
1 | fn main() {
  |    ^^^^
  |
  = note: `#[warn(dead_code)]` on by default

note: Emitting MIR for hello/7d20a575::main

note: Emitting MIR for hello/7d20a575::main::{{promoted}}[0]

note: Emitting MIR for hello/7d20a575::main::{{promoted}}[1]

note: Emitting ADT definition for core/0a2bb85a::fmt::Arguments::_adtbd21306cbe4f0b9b[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::ArgumentV1::_adtbd21306cbe4f0b9b[0]

note: Emitting MIR for core/0a2bb85a::fmt::{impl#4}::new_v1::_instbd21306cbe4f0b9b[0]

note: Emitting MIR for core/0a2bb85a::fmt::{impl#4}::new_v1::_instbd21306cbe4f0b9b[0]::{{promoted}}[0]

note: Emitting MIR for core/0a2bb85a::fmt::{impl#4}::new_v1::_instbd21306cbe4f0b9b[0]::{{promoted}}[1]

note: Emitting ADT definition for core/0a2bb85a::option::Option::_adt9c34ccba92936212[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::rt::v1::Argument::_adtb7803c2264daf0ec[0]

note: Emitting ADT definition for core/0a2bb85a::result::Result::_adtb3f4e538b15dbdda[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::Formatter::_adtbd21306cbe4f0b9b[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::Error::_adtb7803c2264daf0ec[0]

note: Emitting ADT definition for core/0a2bb85a::marker::PhantomData::_adta595c6a450ad127c[0]

note: Emitting ADT definition for core/0a2bb85a::crucible::any::Any::_adtb7803c2264daf0ec[0]

note: Emitting trait def for Some(dyn std::fmt::Write)

note: Emitting ADT definition for core/0a2bb85a::fmt::rt::v1::FormatSpec::_adtb7803c2264daf0ec[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::rt::v1::Alignment::_adtb7803c2264daf0ec[0]

note: Emitting ADT definition for core/0a2bb85a::option::Option::_adtaffa7a8b1157c078[0]

note: Emitting ADT definition for core/0a2bb85a::fmt::rt::v1::Count::_adtb7803c2264daf0ec[0]

warning: 1 warning emitted

linking 19 mir files into hello.linked-mir.json
  inputs: libhello.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libstd.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcore.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcompiler_builtins.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/liballoc.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcrucible.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/liblibc.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/librustc_std_workspace_core.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libunwind.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libcfg_if.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libminiz_oxide.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libadler.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libhashbrown.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libstd_detect.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/librustc_demangle.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libaddr2line.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libgimli.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libobject.mir /Users/sarah.spall/projects/provers/crucible/crux-mir/rlibs_real/lib/rustlib/aarch64-apple-darwin/lib/libmemchr.mir
generated test script hello

(and I can provide this mir file if it might be useful). When I run saw hello.saw (same saw file) again for this example I get

[17:26:51.993] Loading file "/Users/sarah.spall/projects/provers/aes_rust_colin/src/hello.saw"
[17:26:54.234] vtable signature mismatch for vtable core/0a2bb85a::ops[0]::function[0]::FnMut[0]::_vtblb704da9d5ff2e622[0] of trait core/0a2bb85a::ops[0]::function[0]::FnMut[0]::_trait1f2906ad28e660ae[0] : StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (IntrinsicRepr MirReference [AnyRepr])]] UnitRepr, FunctionHandleRepr [AnyRepr, IntrinsicRepr MirReference [AnyRepr]] UnitRepr] != StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (IntrinsicRepr MirReference [AnyRepr])]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (IntrinsicRepr MirReference [AnyRepr])]] UnitRepr]
CallStack (from HasCallStack):
  error, called at src/Mir/Trans.hs:980:20 in crucible-mir-0.2-inplace:Mir.Trans
  mkTraitObject, called at src/Mir/Trans.hs:823:11 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast', called at src/Mir/Trans.hs:954:5 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast, called at src/Mir/Trans.hs:1011:30 in crucible-mir-0.2-inplace:Mir.Trans
  evalRval, called at src/Mir/Trans.hs:1246:11 in crucible-mir-0.2-inplace:Mir.Trans
  transStatement, called at src/Mir/Trans.hs:1712:68 in crucible-mir-0.2-inplace:Mir.Trans
  translateBlockBody, called at src/Mir/Trans.hs:1721:28 in crucible-mir-0.2-inplace:Mir.Trans
  registerBlock, called at src/Mir/Trans.hs:1761:10 in crucible-mir-0.2-inplace:Mir.Trans
  genFn, called at src/Mir/Trans.hs:1807:24 in crucible-mir-0.2-inplace:Mir.Trans
  transDefine, called at src/Mir/Trans.hs:2264:30 in crucible-mir-0.2-inplace:Mir.Trans
  transCollection, called at src/Mir/ParseTranslate.hs:76:26 in crucible-mir-0.2-inplace:Mir.ParseTranslate
  translateMIR, called at src/SAWScript/Crucible/MIR/Builtins.hs:528:11 in saw-script-1.1-inplace:SAWScript.Crucible.MIR.Builtins

Any troubleshooting tips / suggestions? is my installation borked or is something else wrong with the way I'm doing things

@RyanGlScott
Copy link
Contributor

Interestingly, I am not able to reproduce this locally on my x86_64-unknown-linux-gnu machine, which leads me to wonder if this issue is platform-specific. (I note that you're using aarch64-apple-darwin.) Would you be able to upload your hello.linked-mir.json file somewhere?

@Torrencem
Copy link
Author

Here's the mir json (fair warning, it's 11mb): https://gist.github.com/Torrencem/2da86154e1a6ae7304bd6bc90ad92e53

@RyanGlScott
Copy link
Contributor

@mccleeary-galois (who also uses aarch64-apple-darwin) is also able to reproduce this issue.

@Torrencem
Copy link
Author

It's funny, as we were discussing this issue, the first thing we said was "this better not be an aarch64-apple-darwin issue again"

@RyanGlScott
Copy link
Contributor

Thank you for uploading your MIR JSON file! This was invaluable in figuring out what is going on.

First, a general disclaimer: I doubt that SAW will be able to verify code that contains calls to println!, even if we fixed this bug. As such, I'd recommend removing calls to println! in any Rust code that you want to verify (at least, for the time being).

That being said, you're not actually verifying the main() function here—you're just loading it into SAW. Loading Rust code into SAW should never crash, so we should strive to make at least this much work. Here are some takeaways:

Your installation isn't borked.

Thankfully, nothing's wrong on your end. This is a bug in how SAW ingests MIR JSON files.

There's nothing inherently macOS-specific about this bug.

The fact that this bug seemingly only arose on aarch64-apple-darwin is a bit of a red herring. That's simply because there's some code in the std crate which happens to use different implementations depending on what operating system is used, and it just so happens that the macOS implementation triggers this bug, but the Linux implementation doesn't.

Thankfully, it proves relatively straightforward to extract the code which triggers the bug into a minimal form that crashes on all operating systems:

// test.rs
pub fn foo(f: &dyn Fn(i32)) {
    f(42)
}

pub fn bar() {
    foo(&|_| ())
}
// test.saw
enable_experimental;
m <- mir_load_module "test.linked-mir.json";
$ saw-rustc test.rs && ~/Software/saw-1.1/bin/saw test.saw
test build - extract output path - ["rustc", "test.rs", "--test", "--target", "x86_64-unknown-linux-gnu", "--cfg", "crux"]
test build - ["rustc", "test.rs", "--target", "x86_64-unknown-linux-gnu", "--cfg", "crux", "--cfg", "crux_top_level", "--crate-type", "rlib"]
note: Emitting MIR for test/801036f2::bar

note: Emitting MIR for test/801036f2::bar::{{promoted}}[0]

note: Emitting MIR for test/801036f2::foo

note: Emitting trait def for Some(dyn std::ops::Fn(i32))

note: Emitting MIR for test/801036f2::bar::{closure#0}::_insta56a8199f5bc040a[0]

note: Emitting MIR for core/73237d41::ops::function::FnOnce::call_once::_callonce194a5c55a7e36159[0]

linking 1 mir files into test.linked-mir.json
  inputs: libtest.mir
generated test script test
[13:10:27.920] Loading file "/home/ryanscott/Documents/Hacking/Haskell/sandbox/saw-script/test.saw"
[13:10:27.922] vtable signature mismatch for vtable core/73237d41::ops[0]::function[0]::Fn[0]::_vtbl87c30a95762061ee[0] of trait core/73237d41::ops[0]::function[0]::Fn[0]::_traitb9ec75e8d3618d41[0] : StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, BVRepr 32] UnitRepr, FunctionHandleRepr [AnyRepr, BVRepr 32] UnitRepr] != StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr]
CallStack (from HasCallStack):
  error, called at src/Mir/Trans.hs:980:20 in crucible-mir-0.2-inplace:Mir.Trans
  mkTraitObject, called at src/Mir/Trans.hs:823:11 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast', called at src/Mir/Trans.hs:954:5 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast, called at src/Mir/Trans.hs:1011:30 in crucible-mir-0.2-inplace:Mir.Trans
  evalRval, called at src/Mir/Trans.hs:1246:11 in crucible-mir-0.2-inplace:Mir.Trans
  transStatement, called at src/Mir/Trans.hs:1712:68 in crucible-mir-0.2-inplace:Mir.Trans
  translateBlockBody, called at src/Mir/Trans.hs:1721:28 in crucible-mir-0.2-inplace:Mir.Trans
  registerBlock, called at src/Mir/Trans.hs:1761:10 in crucible-mir-0.2-inplace:Mir.Trans
  genFn, called at src/Mir/Trans.hs:1807:24 in crucible-mir-0.2-inplace:Mir.Trans
  transDefine, called at src/Mir/Trans.hs:2264:30 in crucible-mir-0.2-inplace:Mir.Trans
  transCollection, called at src/Mir/ParseTranslate.hs:76:26 in crucible-mir-0.2-inplace:Mir.ParseTranslate
  translateMIR, called at src/SAWScript/Crucible/MIR/Builtins.hs:528:11 in saw-script-1.1-inplace:SAWScript.Crucible.MIR.Builtins

What is actually going on here?

I haven't made it as far as identifying the actual cause of the underlying bug, but it appears that crucible-mir is improperly constructing the types of vtable shims. Note that you can also reproduce this bug using crux-mir (after annotating bar as a crux-mir test entrypoint):

// test.rs
pub fn foo(f: &dyn Fn(i32)) {
    f(42)
}

#[crux::test]
pub fn bar() {
    foo(&|_| ())
}
$ crux-mir test.rs 
vtable signature mismatch for vtable core/092bc89a::ops[0]::function[0]::Fn[0]::_vtbl437dbfe18b7afdbd[0] of trait core/092bc89a::ops[0]::function[0]::Fn[0]::_trait82b5b5831af5fcf4[0] : StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, BVRepr 32] UnitRepr, FunctionHandleRepr [AnyRepr, BVRepr 32] UnitRepr] != StructRepr [FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr, FunctionHandleRepr [AnyRepr, StructRepr [MaybeRepr (BVRepr 32)]] UnitRepr]
CallStack (from HasCallStack):
  error, called at src/Mir/Trans.hs:980:20 in crucible-mir-0.2-inplace:Mir.Trans
  mkTraitObject, called at src/Mir/Trans.hs:823:11 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast', called at src/Mir/Trans.hs:954:5 in crucible-mir-0.2-inplace:Mir.Trans
  evalCast, called at src/Mir/Trans.hs:1011:30 in crucible-mir-0.2-inplace:Mir.Trans
  evalRval, called at src/Mir/Trans.hs:1246:11 in crucible-mir-0.2-inplace:Mir.Trans
  transStatement, called at src/Mir/Trans.hs:1712:68 in crucible-mir-0.2-inplace:Mir.Trans
  translateBlockBody, called at src/Mir/Trans.hs:1721:28 in crucible-mir-0.2-inplace:Mir.Trans
  registerBlock, called at src/Mir/Trans.hs:1761:10 in crucible-mir-0.2-inplace:Mir.Trans
  genFn, called at src/Mir/Trans.hs:1807:24 in crucible-mir-0.2-inplace:Mir.Trans
  transDefine, called at src/Mir/Trans.hs:2264:30 in crucible-mir-0.2-inplace:Mir.Trans
  transCollection, called at src/Mir/ParseTranslate.hs:76:26 in crucible-mir-0.2-inplace:Mir.ParseTranslate
  translateMIR, called at src/Mir/Language.hs:256:16 in crux-mir-0.8.0.99-inplace:Mir.Language

As such, this is really more of a crucible-mir issue than a SAW one. I'll move this issue over to the Crucible repo and retitle it accordingly.

@RyanGlScott RyanGlScott transferred this issue from GaloisInc/saw-script Jul 17, 2024
@RyanGlScott RyanGlScott changed the title saw MIR hello world script error crucible-mir crashes due to improperly constructing vtable shim types Jul 17, 2024
@RyanGlScott RyanGlScott added bug crucible MIR Issues relating to Rust/MIR support labels Jul 17, 2024
@RyanGlScott
Copy link
Contributor

@spernsteiner reminded me that the culprit is the spread_arg hack:

-- TODO: current handling of spread_arg is a hack.
--
-- Correct behavior would be (1) always pass args tupled for rust-call abi
-- (in other words, translate the MIR as-is, with no special case for calls
-- to rust-call fns), and (2) in rust-call functions, if `spread_arg` is
-- null, adjust the sig (by tupling up the argument types) and explicitly
-- untuple the values on entry to the function. Current behavior is (2)
-- translate rust-call function bodies as-is, and (1) tuple argument values
-- at the call site if the target has rust-call abi and spread_arg is null.
-- However, on the rust side, the value of spread_arg is part of the
-- mir::Body, not the signature, which means this design is broken in the
-- presence of function pointers.
--
-- Anyway, that's why this weird `fsspreadarg` field is here. The sig of a
-- function definition will include the value of `spread_arg` from the
-- `mir::Body`, and that will be visible at *direct* calls (not via fn ptr)
-- of the function, to make decisions about whether to untuple the args.
, _fsspreadarg :: Maybe Int

More specifically, suppose we have this program:

// test.rs
pub fn foo(f: &mut dyn FnMut(i16, i32)) {
    f(27, 42)
}

#[crux::test]
pub fn bar() {
    foo(&mut |_, _| ())
}

If I dump out the internal representation of the FnMut instantiation in the program above (using mir-json), I get:

  "traits": [
    {
      "items": [
        {
          "item_id": "core/092bc89a::ops::function::FnOnce::call_once",
          "kind": "Method",
          "signature": {
            "abi": {
              "kind": "RustCall"
            },
            "inputs": [
              "ty::Dynamic::985de022e3d83671",
              "ty::Tuple::05f82569127efd62"
            ],
            "output": "ty::Tuple::e93222e871854c41"
          }
        },
        {
          "item_id": "core/092bc89a::ops::function::FnMut::call_mut",
          "kind": "Method",
          "signature": {
            "abi": {
              "kind": "RustCall"
            },
            "inputs": [
              "ty::Ref::0751798588f5ec27",
              "ty::Tuple::05f82569127efd62"
            ],
            "output": "ty::Tuple::e93222e871854c41"
          }
        }
      ],
      "name": "core/092bc89a::ops::function::FnMut::_traite54ca09db4b501ca[0]"
    }
  ]

Here, we see that both call_once and call_mut expect a Tuple argument, which corresponds to the pair (i16, i32). So far, so good.

If I look up the vtable used in this program, I see this:

  "vtables": [
    {
      "items": [
        {
          "def_id": "core/092bc89a::ops::function::FnOnce::call_once::_callonce95ae10fa377a8068[0]",
          "item_id": "core/092bc89a::ops::function::FnOnce::call_once"
        },
        {
          "def_id": "test/7578cddf::bar::{closure#0}::_inst4da6783bb0bc5290[0]",
          "item_id": "test/7578cddf::bar::{closure#0}"
        }
      ],
      "name": "core/092bc89a::ops::function::FnMut::_vtbl421e8f554162ba11[0]",
      "trait_id": "core/092bc89a::ops::function::FnMut::_traite54ca09db4b501ca[0]"
    }
  ]

What is peculiar is the type of test/7578cddf::bar::{closure#0}::_inst4da6783bb0bc5290[0] used as the second item in the vtable:

    {
      ...,
      "args": [
        {
          "is_zst": false,
          "mut": {
            "kind": "Mut"
          },
          "name": "_1",
          "ty": "ty::Ref::fcca228e69df3496"
        },
        {
          "is_zst": false,
          "mut": {
            "kind": "Mut"
          },
          "name": "_2",
          "ty": "ty::i16"
        },
        {
          "is_zst": false,
          "mut": {
            "kind": "Mut"
          },
          "name": "_3",
          "ty": "ty::i32"
        }
      ],
      ...,
      "name": "test/7578cddf::bar::{closure#0}::_inst4da6783bb0bc5290[0]",
      "return_ty": "ty::Tuple::e93222e871854c41",
      "spread_arg": null
    }

Unlike call_mut above, this doesn't take a Tuple as an argument. Instead it "unpacks" the tuple, having separate i16 and i32 arguments. This is the root cause of the vtable signature mismatch error reported earlier.

The unpacking (or lack thereof) is related to crucible-mir's hacky treatment of spread_arg in MIR. As the comment above indicates, the current hack works for direct calls to Fn-like things, but not function pointers. In order to make this work, we will likely need to remove the hack and do the right thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug crucible MIR Issues relating to Rust/MIR support
Projects
None yet
Development

No branches or pull requests

2 participants