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

unstable fingerprints error with mockall 0.10 #112430

Closed
matt-phylum opened this issue Jun 8, 2023 · 6 comments
Closed

unstable fingerprints error with mockall 0.10 #112430

matt-phylum opened this issue Jun 8, 2023 · 6 comments
Labels
A-incr-comp Area: Incremental compilation C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@matt-phylum
Copy link

I think this problem is related to asomers/mockall#333. Upgrading to Mockall 0.11.4 is one of the things that prevents it from happening.

I'm not seeing the crash on beta 1.71.0-beta.2, but I can't see a bug for this so I don't know if that's intentional. When I upgraded to 1.70 it seemed like almost every other build I would get a crash. Coworkers downgraded to 1.69 because of it, so to me it seems like a good candidate for backporting if it's not already planned.

Code

[package]
name = "api"
version = "0.1.0"
edition = "2021"

[dependencies]
async-trait = "0.1.56"
mockall = "0.10.2"
paste = "1.0"
use paste::paste;

// This constant allows us to rebuild without changing anything in this file.
#[allow(unused)]
const RANDOM: &str = include_str!("rand.txt");

pub type Callback<T> = Box<dyn for<'a> FnOnce(&'a mut T) + 'static>;

// This trait doesn't seem like it should do anything, but removing it stops the crash.
#[mockall::automock]
pub trait DaoA {}

// Normally, you put these attributes in the opposite order.
// Doing it this way allows the mock callback to return a future for tests that involve synchronization.
#[async_trait::async_trait]
#[mockall::automock]
pub trait DaoB {
    async fn method(&self, param: &usize);
}

// Removing this send constraint stops the crash?
struct Wrapper<M>(Callback<M>)
where
    M: Send;

// Expanding this macro by hand stops the crash?
macro_rules! builder_struct {
    ($($dao_trait:ty),* $(,)?) => {
        paste! {
            pub struct Builder{
                $(
                    [< $dao_trait:snake >]: Wrapper<[<Mock $dao_trait>]>,
                )*
            }

            #[allow(unused)]
            impl Builder {
                $(
                    pub fn [< with_mock_ $dao_trait:snake>] (
                        mut self,
                        config_callback: impl for<'a> FnOnce(&'a mut [<Mock $dao_trait>]) + 'static,
                    ) -> Self {
                        let boxed: Callback<[<Mock $dao_trait>]> = Box::new(config_callback);
                        self.[< $dao_trait:snake >] = Wrapper(boxed);
                        self
                    }
                )*
            }
        }
    }
}

builder_struct!(DaoA, DaoB);

Meta

rustc --version --verbose:

rustc 1.70.0 (90c541806 2023-05-31)
binary: rustc
commit-hash: 90c541806f23a127002de5b4038be731ba1458ca
commit-date: 2023-05-31
host: aarch64-apple-darwin
release: 1.70.0
LLVM version: 16.0.2

Error output

thread 'rustc' panicked at 'Found unstable fingerprints for mir_built(ab11575bca0c211c-f2e0d84c65314716): Steal { value: RwLock(RefCell { value: Some(Body { basic_blocks: BasicBlocks { basic_blocks: [BasicBlockData { statements: [StorageLive(_3), StorageLive(_4), StorageLive(_5), _5 = move _2], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:43:68: 43:93 (#68), scope: scope[0] }, kind: _4 = Box::<impl for<'a> FnOnce(&'a mut MockDaoB) + 'static>::new(move _5) -> [return: bb1, unwind: bb14] }), is_cleanup: false }, BasicBlockData { statements: [_3 = move _4 as std::boxed::Box<dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB)> (Pointer(Unsize))], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:43:92: 43:93 (#68), scope: scope[0] }, kind: drop(_4) -> [return: bb2, unwind: bb14] }), is_cleanup: false }, BasicBlockData { statements: [StorageDead(_5), StorageDead(_4), FakeRead(ForLet(None), _3), AscribeUserType(_3, o, UserTypeProjection { base: UserType(2), projs: [] }), StorageLive(_6), StorageLive(_7), StorageLive(_8), _8 = move _3, _7 = move _8 as std::boxed::Box<dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB)> (Pointer(Unsize))], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:44:67: 44:68 (#68), scope: scope[1] }, kind: drop(_8) -> [return: bb3, unwind: bb12] }), is_cleanup: false }, BasicBlockData { statements: [StorageDead(_8), _6 = Wrapper::<MockDaoB>(move _7)], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:44:68: 44:69 (#68), scope: scope[1] }, kind: drop(_7) -> [return: bb4, unwind: bb11] }), is_cleanup: false }, BasicBlockData { statements: [StorageDead(_7)], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:44:25: 44:52 (#127), scope: scope[1] }, kind: drop((_1.1: Wrapper<MockDaoB>)) -> [return: bb5, unwind: bb6] }), is_cleanup: false }, BasicBlockData { statements: [(_1.1: Wrapper<MockDaoB>) = move _6], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:44:68: 44:69 (#68), scope: scope[1] }, kind: drop(_6) -> [return: bb7, unwind: bb13] }), is_cleanup: false }, BasicBlockData { statements: [(_1.1: Wrapper<MockDaoB>) = move _6], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:44:25: 44:52 (#127), scope: scope[1] }, kind: goto -> bb11 }), is_cleanup: true }, BasicBlockData { statements: [StorageDead(_6), _0 = move _1], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:46:21: 46:22 (#68), scope: scope[0] }, kind: drop(_3) -> [return: bb8, unwind: bb15] }), is_cleanup: false }, BasicBlockData { statements: [StorageDead(_3)], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:46:21: 46:22 (#68), scope: scope[0] }, kind: drop(_2) -> [return: bb9, unwind: bb16] }), is_cleanup: false }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:46:21: 46:22 (#68), scope: scope[0] }, kind: drop(_1) -> [return: bb10, unwind: bb17] }), is_cleanup: false }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:46:22: 46:22 (#68), scope: scope[0] }, kind: return }), is_cleanup: false }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:44:68: 44:69 (#68), scope: scope[1] }, kind: drop(_6) -> [return: bb13, unwind terminate] }), is_cleanup: true }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:44:68: 44:69 (#68), scope: scope[1] }, kind: drop(_7) -> [return: bb13, unwind terminate] }), is_cleanup: true }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:46:21: 46:22 (#68), scope: scope[0] }, kind: drop(_3) -> [return: bb15, unwind terminate] }), is_cleanup: true }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:43:92: 43:93 (#68), scope: scope[0] }, kind: drop(_5) -> [return: bb15, unwind terminate] }), is_cleanup: true }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:46:21: 46:22 (#68), scope: scope[0] }, kind: drop(_2) -> [return: bb16, unwind terminate] }), is_cleanup: true }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:46:21: 46:22 (#68), scope: scope[0] }, kind: drop(_1) -> [return: bb17, unwind terminate] }), is_cleanup: true }, BasicBlockData { statements: [], terminator: Some(Terminator { source_info: SourceInfo { span: src/lib.rs:39:21: 46:22 (#68), scope: scope[0] }, kind: resume }), is_cleanup: true }], cache: Cache { predecessors: OnceCell(Uninit), switch_sources: OnceCell(Uninit), is_cyclic: OnceCell(Uninit), postorder: OnceCell(Uninit) } }, phase: Built, pass_count: 0, source: MirSource { instance: Item(WithOptConstParam { did: DefId(0:258 ~ api[b95e]::{impl#14}::with_mock_dao_b), const_param_did: None }), promoted: None }, source_scopes: [SourceScopeData { span: src/lib.rs:39:21: 46:22 (#68), parent_scope: None, inlined: None, inlined_parent_scope: None, local_data: Set(SourceScopeLocalData { lint_root: HirId(DefId(0:258 ~ api[b95e]::{impl#14}::with_mock_dao_b).0), safety: Safe }) }, SourceScopeData { span: src/lib.rs:43:25: 46:22 (#68), parent_scope: Some(scope[0]), inlined: None, inlined_parent_scope: None, local_data: Set(SourceScopeLocalData { lint_root: HirId(DefId(0:258 ~ api[b95e]::{impl#14}::with_mock_dao_b).0), safety: Safe }) }], generator: None, local_decls: [LocalDecl { mutability: Mut, local_info: Set(Boring), internal: false, ty: Builder, user_ty: None, source_info: SourceInfo { span: src/lib.rs:42:26: 42:30 (#68), scope: scope[0] } }, LocalDecl { mutability: Mut, local_info: Set(User(ImplicitSelf(Mut))), internal: false, ty: Builder, user_ty: None, source_info: SourceInfo { span: src/lib.rs:40:25: 40:33 (#68), scope: scope[0] } }, LocalDecl { mutability: Not, local_info: Set(User(Var(VarBindingForm { binding_mode: BindByValue(Not), opt_ty_info: Some(src/lib.rs:41:42: 41:100 (#68)), opt_match_place: Some((None, src/lib.rs:41:25: 41:40 (#68))), pat_span: src/lib.rs:41:25: 41:40 (#68) }))), internal: false, ty: impl for<'a> FnOnce(&'a mut MockDaoB) + 'static, user_ty: None, source_info: SourceInfo { span: src/lib.rs:41:25: 41:40 (#68), scope: scope[0] } }, LocalDecl { mutability: Not, local_info: Set(User(Var(VarBindingForm { binding_mode: BindByValue(Not), opt_ty_info: None, opt_match_place: Some((None, src/lib.rs:43:68: 43:93 (#68))), pat_span: src/lib.rs:43:29: 43:34 (#68) }))), internal: false, ty: std::boxed::Box<dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB)>, user_ty: Some(UserTypeProjections { contents: [(UserTypeProjection { base: UserType(0), projs: [] }, src/lib.rs:43:36: 43:65 (#68))] }), source_info: SourceInfo { span: src/lib.rs:43:29: 43:34 (#68), scope: scope[0] } }, LocalDecl { mutability: Mut, local_info: Set(Boring), internal: false, ty: std::boxed::Box<impl for<'a> FnOnce(&'a mut MockDaoB) + 'static>, user_ty: None, source_info: SourceInfo { span: src/lib.rs:43:68: 43:93 (#68), scope: scope[0] } }, LocalDecl { mutability: Mut, local_info: Set(Boring), internal: false, ty: impl for<'a> FnOnce(&'a mut MockDaoB) + 'static, user_ty: None, source_info: SourceInfo { span: src/lib.rs:43:77: 43:92 (#68), scope: scope[0] } }, LocalDecl { mutability: Mut, local_info: Set(Boring), internal: false, ty: Wrapper<MockDaoB>, user_ty: None, source_info: SourceInfo { span: src/lib.rs:44:55: 44:69 (#68), scope: scope[0] } }, LocalDecl { mutability: Mut, local_info: Set(AggregateTemp), internal: false, ty: std::boxed::Box<dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB)>, user_ty: None, source_info: SourceInfo { span: src/lib.rs:44:63: 44:68 (#68), scope: scope[0] } }, LocalDecl { mutability: Mut, local_info: Set(Boring), internal: false, ty: std::boxed::Box<dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB)>, user_ty: None, source_info: SourceInfo { span: src/lib.rs:44:63: 44:68 (#68), scope: scope[0] } }], user_type_annotations: [CanonicalUserTypeAnnotation { user_ty: Canonical { value: Ty(std::boxed::Box<(dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB) + 'static)>), max_universe: U0, variables: [] }, span: src/lib.rs:43:36: 43:65 (#68), inferred_ty: std::boxed::Box<dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB)> }, CanonicalUserTypeAnnotation { user_ty: Canonical { value: TypeOf(DefId(5:285 ~ alloc[c023]::boxed::{impl#0}::new), UserSubsts { substs: [^0], user_self_ty: Some(UserSelfTy { impl_def_id: DefId(5:283 ~ alloc[c023]::boxed::{impl#0}), self_ty: std::boxed::Box<^1, ^2> }) }), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }, CanonicalVarInfo { kind: Ty(General(U0)) }, CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: src/lib.rs:43:68: 43:76 (#68), inferred_ty: fn(impl for<'a> FnOnce(&'a mut MockDaoB) + 'static) -> std::boxed::Box<impl for<'a> FnOnce(&'a mut MockDaoB) + 'static> {std::boxed::Box::<impl for<'a> FnOnce(&'a mut MockDaoB) + 'static>::new} }, CanonicalUserTypeAnnotation { user_ty: Canonical { value: Ty(std::boxed::Box<(dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB) + 'static)>), max_universe: U0, variables: [] }, span: src/lib.rs:43:36: 43:65 (#68), inferred_ty: std::boxed::Box<dyn for<'a> std::ops::FnOnce(&'a mut MockDaoB)> }], arg_count: 2, spread_arg: None, var_debug_info: [VarDebugInfo { name: "self", source_info: SourceInfo { span: src/lib.rs:40:25: 40:33 (#68), scope: scope[0] }, value: _1, argument_index: Some(1) }, VarDebugInfo { name: "config_callback", source_info: SourceInfo { span: src/lib.rs:41:25: 41:40 (#68), scope: scope[0] }, value: _2, argument_index: Some(2) }, VarDebugInfo { name: "boxed", source_info: SourceInfo { span: src/lib.rs:43:29: 43:34 (#68), scope: scope[1] }, value: _3, argument_index: None }], span: src/lib.rs:39:21: 46:22 (#68), required_consts: [], is_polymorphic: true, injection_phase: None, tainted_by_errors: None }) }) }', /rustc/90c541806f23a127002de5b4038be731ba1458ca/compiler/rustc_query_system/src/query/plumbing.rs:715:9
Backtrace

stack backtrace:
   0: _rust_begin_unwind
   1: core::panicking::panic_fmt
   2: rustc_query_system::query::plumbing::incremental_verify_ich_failed::<rustc_middle::ty::context::TyCtxt>
   3: <std::thread::local::LocalKey<core::cell::Cell<*const ()>>>::with::<rustc_middle::ty::context::tls::enter_context<rustc_query_system::query::plumbing::execute_job_incr<rustc_query_impl::queries::mir_built, rustc_query_impl::plumbing::QueryCtxt>::{closure#1}, core::option::Option<(rustc_middle::query::erase::Erased<[u8; 8]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>>::{closure#0}, core::option::Option<(rustc_middle::query::erase::Erased<[u8; 8]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>>
   4: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::queries::mir_built, rustc_query_impl::plumbing::QueryCtxt>
   5: <rustc_query_impl::Queries as rustc_middle::ty::query::QueryEngine>::mir_built
   6: rustc_mir_transform::mir_const
   7: <std::thread::local::LocalKey<core::cell::Cell<*const ()>>>::with::<rustc_middle::ty::context::tls::enter_context<rustc_query_system::query::plumbing::execute_job_incr<rustc_query_impl::queries::mir_const, rustc_query_impl::plumbing::QueryCtxt>::{closure#1}, core::option::Option<(rustc_middle::query::erase::Erased<[u8; 8]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>>::{closure#0}, core::option::Option<(rustc_middle::query::erase::Erased<[u8; 8]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>>
   8: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::queries::mir_const, rustc_query_impl::plumbing::QueryCtxt>
   9: <rustc_query_impl::Queries as rustc_middle::ty::query::QueryEngine>::mir_const
  10: rustc_mir_transform::mir_promoted
  11: <std::thread::local::LocalKey<core::cell::Cell<*const ()>>>::with::<rustc_middle::ty::context::tls::enter_context<rustc_query_system::query::plumbing::execute_job_incr<rustc_query_impl::queries::mir_promoted, rustc_query_impl::plumbing::QueryCtxt>::{closure#1}, core::option::Option<(rustc_middle::query::erase::Erased<[u8; 16]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>>::{closure#0}, core::option::Option<(rustc_middle::query::erase::Erased<[u8; 16]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>>
  12: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::queries::mir_promoted, rustc_query_impl::plumbing::QueryCtxt>
  13: <rustc_query_impl::Queries as rustc_middle::ty::query::QueryEngine>::mir_promoted
  14: rustc_borrowck::mir_borrowck
  15: <rustc_borrowck::provide::{closure#0} as core::ops::function::FnOnce<(rustc_middle::ty::context::TyCtxt, rustc_span::def_id::LocalDefId)>>::call_once
  16: <std::thread::local::LocalKey<core::cell::Cell<*const ()>>>::with::<rustc_middle::ty::context::tls::enter_context<rustc_query_system::query::plumbing::execute_job_incr<rustc_query_impl::queries::mir_borrowck, rustc_query_impl::plumbing::QueryCtxt>::{closure#2}, (rustc_middle::query::erase::Erased<[u8; 8]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>::{closure#0}, (rustc_middle::query::erase::Erased<[u8; 8]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>
  17: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::queries::mir_borrowck, rustc_query_impl::plumbing::QueryCtxt>
  18: <rustc_query_impl::Queries as rustc_middle::ty::query::QueryEngine>::mir_borrowck
  19: <core::panic::unwind_safe::AssertUnwindSafe<rustc_data_structures::sync::par_for_each_in<&[rustc_span::def_id::LocalDefId], <rustc_middle::hir::map::Map>::par_body_owners<rustc_interface::passes::analysis::{closure#2}::{closure#0}>::{closure#0}>::{closure#0}::{closure#0}> as core::ops::function::FnOnce<()>>::call_once
  20: rustc_data_structures::sync::par_for_each_in::<&[rustc_span::def_id::LocalDefId], <rustc_middle::hir::map::Map>::par_body_owners<rustc_interface::passes::analysis::{closure#2}::{closure#0}>::{closure#0}>
  21: <rustc_session::session::Session>::time::<(), rustc_interface::passes::analysis::{closure#2}>
  22: rustc_interface::passes::analysis
  23: <std::thread::local::LocalKey<core::cell::Cell<*const ()>>>::with::<rustc_middle::ty::context::tls::enter_context<rustc_query_system::query::plumbing::execute_job_incr<rustc_query_impl::queries::analysis, rustc_query_impl::plumbing::QueryCtxt>::{closure#2}, (rustc_middle::query::erase::Erased<[u8; 1]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>::{closure#0}, (rustc_middle::query::erase::Erased<[u8; 1]>, rustc_query_system::dep_graph::graph::DepNodeIndex)>
  24: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::queries::analysis, rustc_query_impl::plumbing::QueryCtxt>
  25: <rustc_query_impl::Queries as rustc_middle::ty::query::QueryEngine>::analysis
  26: <std::thread::local::LocalKey<core::cell::Cell<*const ()>>>::with::<rustc_middle::ty::context::tls::enter_context<<rustc_middle::ty::context::GlobalCtxt>::enter<rustc_driver_impl::run_compiler::{closure#1}::{closure#2}::{closure#4}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>
  27: <rustc_interface::queries::QueryResult<&rustc_middle::ty::context::GlobalCtxt>>::enter::<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#1}::{closure#2}::{closure#4}>
  28: <rustc_interface::interface::Compiler>::enter::<rustc_driver_impl::run_compiler::{closure#1}::{closure#2}, core::result::Result<core::option::Option<rustc_interface::queries::Linker>, rustc_span::ErrorGuaranteed>>
  29: rustc_span::set_source_map::<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_interface::interface::run_compiler<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#1}>::{closure#0}::{closure#0}>

@matt-phylum matt-phylum added C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 8, 2023
@Noratrieb
Copy link
Member

First, unstable proc macros should very much not cause incremental problems, proc macros run before incremental kicks in. It does make it way harder to debug though.

Do you have a simple way to reproduce it?
I suspect that this is one of the cases that is fixed by #111952, which is in 1.71. that would explain what you're seeing in beta.

@Noratrieb Noratrieb added the A-incr-comp Area: Incremental compilation label Jun 9, 2023
@matt-phylum
Copy link
Author

Maybe it shouldn't, but if I use cargo expand on the automock macro and then unexpand the panic! unreachable! format! macros to make rustc happy, the crashes stop. I don't know if that's because the expanded code is stable or because the expanded code is no longer coming from a macro.

Interestingly, expanding the paste! macro also causes the crashes to stop.

I managed to remove a few more things. This version doesn't use async-trait. Trying to change the parameters or lifetimes of DaoB::method stops the crashes.

Crashing code:

use paste::paste;

// This constant allows us to rebuild without changing anything in this file.
#[allow(unused)]
const RANDOM: &str = include_str!("rand.txt");

type Callback<T> = Box<dyn FnOnce(&mut T) + 'static>;

// This struct doesn't seem like it should do anything, but removing it stops the crash.
struct MockDaoA;

#[mockall::automock]
trait DaoB {
    fn method<'life0, 'life1>(&'life0 self, param: &'life1 usize);
}

// Removing this send constraint stops the crash.
struct Wrapper<M>(Callback<M>)
where
    M: Send;

// Expanding this macro by hand stops the crash?
macro_rules! builder_struct {
    ($($dao_impl:ty),* $(,)?) => {
        paste! {
            struct Builder{
                $(
                    [< $dao_impl:snake >]: Wrapper<$dao_impl>,
                )*
            }

            #[allow(unused)]
            impl Builder {
                $(
                    fn [< with_mock_ $dao_impl:snake>] (
                        mut self,
                        config_callback: Callback<$dao_impl>,
                    ) {
                        self.[< $dao_impl:snake >] = Wrapper(config_callback);
                    }
                )*
            }
        }
    }
}

builder_struct!(MockDaoA, MockDaoB);

Non crashing code:

// This constant allows us to rebuild without changing anything in this file.
#[allow(unused)]
const RANDOM: &str = include_str!("rand.txt");

type Callback<T> = Box<dyn FnOnce(&mut T) + 'static>;

// This struct doesn't seem like it should do anything, but removing it stops the crash.
struct MockDaoA;

#[mockall::automock]
trait DaoB {
    fn method<'life0, 'life1>(&'life0 self, param: &'life1 usize);
}

// Removing this send constraint stops the crash.
struct Wrapper<M>(Callback<M>)
where
    M: Send;

struct Builder {
    mock_dao_a: Wrapper<MockDaoA>,
    mock_dao_b: Wrapper<MockDaoB>,
}
#[allow(unused)]
impl Builder {
    fn with_mock_mock_dao_a(mut self, config_callback: Callback<MockDaoA>) {
        self.mock_dao_a = Wrapper(config_callback);
    }
    fn with_mock_mock_dao_b(mut self, config_callback: Callback<MockDaoB>) {
        self.mock_dao_b = Wrapper(config_callback);
    }
}

That paste macro isn't very complicated and has stable output.

@matt-phylum
Copy link
Author

It's definitely related to asomers/mockall#333. I pulled down the mockall code and bisected. asomers/mockall@e2f5285 is the first commit where it doesn't crash.

@Noratrieb
Copy link
Member

Noratrieb commented Jun 9, 2023

the nondeterminism probably exhibits a specific pattern that makes rustc crash. I don't think this is worth your time debugging if it doesn't ICE on beta anymore. It's unlikely that you'll reveal new things about it. it was probably fixed by #11195.
Since you already have a workaround (updating mockall), it shouldn't be blocking for you. Thank you for the report anyways.

@matt-phylum
Copy link
Author

I built my own Rust 1.70 where I could reproduce the problem and cherry-picking ddb5424 (#111952) does seem to have fixed it.

@Noratrieb
Copy link
Member

awesome, thanks! closing it as completed then

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-incr-comp Area: Incremental compilation C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

2 participants