Skip to content

Commit de9441d

Browse files
committed
Fix regression: Passing a closure with no captures is now a constant with zero size.
This case must be handled separately with a new utility function `extract_closure`. Keep the original `extract_nth_argument` for all the other cases. Update the README accordingly. All tests are green again.
1 parent a5fdfcc commit de9441d

File tree

4 files changed

+88
-11
lines changed

4 files changed

+88
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ This proves extremely useful to get feedback on the types, compiler errors, etc.
4848

4949
As time goes on and the compiler internals change, the code will inevitably need changes to work again.
5050

51-
**The current state of the repository compiled without warnings and with all tests passing with** `rustc 1.70.0-nightly (39f2657d1 2023-03-09)`
51+
**The current state of the repository compiled without warnings and with all tests passing with** `rustc 1.70.0-nightly (4a04d086c 2023-03-18)`
5252

5353
### Installation
5454

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// These crates are only available when using the nightly toolchain.
2121
// It suffices to declare them once to use their types and methods in the whole crate.
2222
extern crate rustc_ast_pretty;
23+
extern crate rustc_const_eval;
2324
extern crate rustc_driver;
2425
extern crate rustc_error_codes;
2526
extern crate rustc_errors;

src/translator/sync/thread_manager.rs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use crate::data_structures::petri_net_interface::TransitionRef;
1111
use crate::translator::mir_function::{
1212
CondvarEntries, JoinHandleEntries, LockGuardEntries, Memory, MutexEntries,
1313
};
14-
use crate::utils::{extract_def_id_of_called_function_from_operand, extract_nth_argument};
14+
use crate::utils::{
15+
extract_closure, extract_def_id_of_called_function_from_operand, extract_nth_argument,
16+
};
1517
use log::{debug, info};
1618
use std::collections::VecDeque;
1719

@@ -52,16 +54,13 @@ impl ThreadManager {
5254
tcx,
5355
);
5456

55-
let closure_for_spawn = extract_nth_argument(args, 0);
56-
let mutexes = memory.find_mutexes_linked_to_place(closure_for_spawn);
57-
let lock_guards = memory.find_lock_guards_linked_to_place(closure_for_spawn);
58-
let join_handles = memory.find_join_handles_linked_to_place(closure_for_spawn);
59-
let condvars = memory.find_condvars_linked_to_place(closure_for_spawn);
57+
let closure_for_spawn = extract_closure(args);
58+
let memory_entries = Self::find_sync_variables(closure_for_spawn, memory);
6059

6160
let thread_ref = self.add_thread(
6261
transition_function_call,
6362
thread_function_def_id,
64-
(mutexes, lock_guards, join_handles, condvars),
63+
memory_entries,
6564
);
6665
// The return value contains a new join handle. Link the local variable to it.
6766
memory.link_place_to_join_handle(return_value, thread_ref);
@@ -128,4 +127,36 @@ impl ThreadManager {
128127
pub fn pop_thread(&mut self) -> Option<Thread> {
129128
self.threads.pop_front()
130129
}
130+
131+
/// Finds sync variables captured by the closure for a new thread.
132+
/// Returns the memory entries for each sync variable type that should be re-mapped in the new thread's memory.
133+
///
134+
/// If the closure is `None` (no variables were captured, it is a `ZeroSizedType`),
135+
/// then return empty vectors for the memory entries.
136+
fn find_sync_variables<'tcx>(
137+
closure: Option<rustc_middle::mir::Place<'tcx>>,
138+
memory: &mut Memory<'tcx>,
139+
) -> (
140+
MutexEntries,
141+
LockGuardEntries,
142+
JoinHandleEntries,
143+
CondvarEntries,
144+
) {
145+
closure.map_or_else(
146+
|| {
147+
let mutexes = vec![];
148+
let lock_guards = vec![];
149+
let join_handles = vec![];
150+
let condvars = vec![];
151+
(mutexes, lock_guards, join_handles, condvars)
152+
},
153+
|place| {
154+
let mutexes = memory.find_mutexes_linked_to_place(place);
155+
let lock_guards = memory.find_lock_guards_linked_to_place(place);
156+
let join_handles = memory.find_join_handles_linked_to_place(place);
157+
let condvars = memory.find_condvars_linked_to_place(place);
158+
(mutexes, lock_guards, join_handles, condvars)
159+
},
160+
)
161+
}
131162
}

src/utils.rs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ pub fn extract_def_id_of_called_function_from_operand<'tcx>(
4949
/// This is also useful for obtaining the self reference for method calls.
5050
/// For example: The call `mutex.lock()` desugars to `std::sync::Mutex::lock(&mutex)`
5151
/// where `&self` is the first argument.
52+
///
53+
/// # Panics
54+
///
55+
/// If the argument passed to the function is a constant, then the function panics.
5256
pub fn extract_nth_argument<'tcx>(
5357
args: &[rustc_middle::mir::Operand<'tcx>],
5458
index: usize,
@@ -60,9 +64,50 @@ pub fn extract_nth_argument<'tcx>(
6064
match operand {
6165
rustc_middle::mir::Operand::Move(place) | rustc_middle::mir::Operand::Copy(place) => *place,
6266
rustc_middle::mir::Operand::Constant(_) => {
63-
unimplemented!(
64-
"Passing an operand of type Operand::Constant to a function is not implemented yet"
65-
);
67+
panic!("BUG: Function should not receive arguments passed as constants");
68+
}
69+
}
70+
}
71+
72+
/// Extracts the closure passed as the 0-th argument to `std::thread::spawn`.
73+
/// Returns the place corresponding to that argument.
74+
///
75+
/// If a valid place cannot be found, then the operand was passed as a constant.
76+
/// If it is a `rustc_middle::mir::interpret::value::ConstValue::ZeroSized` return `None`.
77+
///
78+
/// # Panics
79+
///
80+
/// If the operand was passed a constant with user-defined type,
81+
/// a type constant (i.e. `T`) or an unevaluated constant, then the functions panics.
82+
pub fn extract_closure<'tcx>(
83+
args: &[rustc_middle::mir::Operand<'tcx>],
84+
) -> Option<rustc_middle::mir::Place<'tcx>> {
85+
let operand = args
86+
.get(0)
87+
.expect("BUG: `std::thread::spawn` should receive at least one argument");
88+
89+
match operand {
90+
rustc_middle::mir::Operand::Move(place) | rustc_middle::mir::Operand::Copy(place) => {
91+
Some(*place)
92+
}
93+
rustc_middle::mir::Operand::Constant(boxed_const) => {
94+
let unboxed_const = **boxed_const;
95+
assert!(unboxed_const.user_ty.is_none(), "BUG: The closure passed to `std::thread::spawn` should not be of type `Operand::Constant` with user-defined type");
96+
let constant_kind = unboxed_const.literal;
97+
match constant_kind {
98+
rustc_middle::mir::ConstantKind::Ty(_) => {
99+
panic!("BUG: The closure passed to `std::thread::spawn` should not be a constant containing a type");
100+
}
101+
rustc_middle::mir::ConstantKind::Unevaluated(_, _) => {
102+
panic!("BUG: The closure passed to `std::thread::spawn` should not be a unevaluated constant");
103+
}
104+
rustc_middle::mir::ConstantKind::Val(value, _ty) => {
105+
if value == rustc_const_eval::interpret::ConstValue::ZeroSized {
106+
return None;
107+
}
108+
panic!("BUG: The closure passed to `std::thread::spawn` should not be a constant whose value is not a zero-sized type");
109+
}
110+
}
66111
}
67112
}
68113
}

0 commit comments

Comments
 (0)