Skip to content

Commit 56a2b28

Browse files
committed
Improvement: Support all combinations of target and unwind action for function calls.
A specific case that was not supported was the function `std::process::exit`, which in some cases generates a cleanup block but no return block. Besides, the new `match` considers all possibilities and handles the `Unreacheable` case where it was simply ignored before. The new `match` statement cannot be moved to a separate function because of the divergent function case.
1 parent 85b2dcd commit 56a2b28

File tree

4 files changed

+76
-24
lines changed

4 files changed

+76
-24
lines changed

examples/results/function_call/greet/net.dot

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ digraph petrinet {
7474
std_ops_Index_index_0_CALL_UNWIND [shape="box" xlabel="" label="std_ops_Index_index_0_CALL_UNWIND"];
7575
std_ops_Index_index_1_CALL [shape="box" xlabel="" label="std_ops_Index_index_1_CALL"];
7676
std_ops_Index_index_1_CALL_UNWIND [shape="box" xlabel="" label="std_ops_Index_index_1_CALL_UNWIND"];
77-
std_process_exit_DIVERGING_CALL [shape="box" xlabel="" label="std_process_exit_DIVERGING_CALL"];
77+
std_process_exit_0_CALL [shape="box" xlabel="" label="std_process_exit_0_CALL"];
7878
std_vec_Vec_T_A_len_0_CALL [shape="box" xlabel="" label="std_vec_Vec_T_A_len_0_CALL"];
7979
std_vec_Vec_T_A_len_0_CALL_UNWIND [shape="box" xlabel="" label="std_vec_Vec_T_A_len_0_CALL_UNWIND"];
8080
PROGRAM_START -> std_env_args_0_CALL;
@@ -116,7 +116,7 @@ digraph petrinet {
116116
main_BB6_STMT1_END -> main_BB6_STMT2;
117117
main_BB7 -> std_io__eprint_0_CALL;
118118
main_BB7 -> std_io__eprint_0_CALL_UNWIND;
119-
main_BB8 -> std_process_exit_DIVERGING_CALL;
119+
main_BB8 -> std_process_exit_0_CALL;
120120
main_BB9 -> main_BB9_STMT0;
121121
main_BB9_END_PLACE -> std_ops_Index_index_1_CALL;
122122
main_BB9_END_PLACE -> std_ops_Index_index_1_CALL_UNWIND;
@@ -160,6 +160,7 @@ digraph petrinet {
160160
std_ops_Index_index_0_CALL_UNWIND -> main_BB15;
161161
std_ops_Index_index_1_CALL -> main_BB10;
162162
std_ops_Index_index_1_CALL_UNWIND -> main_BB15;
163+
std_process_exit_0_CALL -> main_BB15;
163164
std_vec_Vec_T_A_len_0_CALL -> main_BB3;
164165
std_vec_Vec_T_A_len_0_CALL_UNWIND -> main_BB15;
165166
}

examples/results/function_call/greet/net.lola

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,11 @@ TRANSITION std_ops_Index_index_1_CALL_UNWIND
237237
main_BB9_END_PLACE : 1;
238238
PRODUCE
239239
main_BB15 : 1;
240-
TRANSITION std_process_exit_DIVERGING_CALL
240+
TRANSITION std_process_exit_0_CALL
241241
CONSUME
242242
main_BB8 : 1;
243+
PRODUCE
244+
main_BB15 : 1;
243245
TRANSITION std_vec_Vec_T_A_len_0_CALL
244246
CONSUME
245247
main_BB2_END_PLACE : 1;

examples/results/function_call/greet/net.pnml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,9 @@
380380
<text>std_ops_Index_index_1_CALL_UNWIND</text>
381381
</name>
382382
</transition>
383-
<transition id="std_process_exit_DIVERGING_CALL">
383+
<transition id="std_process_exit_0_CALL">
384384
<name>
385-
<text>std_process_exit_DIVERGING_CALL</text>
385+
<text>std_process_exit_0_CALL</text>
386386
</name>
387387
</transition>
388388
<transition id="std_vec_Vec_T_A_len_0_CALL">
@@ -707,9 +707,9 @@
707707
<text>1</text>
708708
</inscription>
709709
</arc>
710-
<arc source="main_BB8" target="std_process_exit_DIVERGING_CALL" id="(main_BB8, std_process_exit_DIVERGING_CALL)">
710+
<arc source="main_BB8" target="std_process_exit_0_CALL" id="(main_BB8, std_process_exit_0_CALL)">
711711
<name>
712-
<text>(main_BB8, std_process_exit_DIVERGING_CALL)</text>
712+
<text>(main_BB8, std_process_exit_0_CALL)</text>
713713
</name>
714714
<inscription>
715715
<text>1</text>
@@ -1059,6 +1059,14 @@
10591059
<text>1</text>
10601060
</inscription>
10611061
</arc>
1062+
<arc source="std_process_exit_0_CALL" target="main_BB15" id="(std_process_exit_0_CALL, main_BB15)">
1063+
<name>
1064+
<text>(std_process_exit_0_CALL, main_BB15)</text>
1065+
</name>
1066+
<inscription>
1067+
<text>1</text>
1068+
</inscription>
1069+
</arc>
10621070
<arc source="std_vec_Vec_T_A_len_0_CALL" target="main_BB3" id="(std_vec_Vec_T_A_len_0_CALL, main_BB3)">
10631071
<name>
10641072
<text>(std_vec_Vec_T_A_len_0_CALL, main_BB3)</text>

src/translator.rs

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,12 @@ impl<'tcx> Translator<'tcx> {
186186
let function_def_id =
187187
extract_def_id_of_called_function_from_operand(func, current_function.def_id, self.tcx);
188188
let function_name = self.tcx.def_path_str(function_def_id);
189+
let start_place = current_function.get_start_place_for_function_call();
189190
info!("Encountered function call: {function_name}");
190191

191192
if is_panic_function(&function_name) {
192193
// Function call which starts an abnormal termination of the program.
193194
// Non-recursive call for the translation process.
194-
let start_place = current_function.get_start_place_for_function_call();
195195
call_panic_function(
196196
&start_place,
197197
&self.program_panic,
@@ -201,22 +201,63 @@ impl<'tcx> Translator<'tcx> {
201201
return;
202202
}
203203

204-
let Some(return_block) = target else {
205-
// Call to a function which does not return (Return type: -> !).
206-
// Non-recursive call for the translation process.
207-
let start_place = current_function.get_start_place_for_function_call();
208-
call_diverging_function(&start_place, &function_name, &mut self.net);
209-
return;
210-
};
211-
212-
let start_place = current_function.get_start_place_for_function_call();
213-
let end_place =
214-
current_function.get_end_place_for_function_call(return_block, &mut self.net);
215-
let cleanup_place = if let rustc_middle::mir::UnwindAction::Cleanup(cleanup_block) = unwind
216-
{
217-
Some(current_function.get_end_place_for_function_call(cleanup_block, &mut self.net))
218-
} else {
219-
None
204+
// Depending on whether a return or a unwind for the function are present,
205+
// we have different possibilities for the function call end place and the (optional) cleanup place.
206+
let (end_place, cleanup_place) = match (target, unwind) {
207+
(Some(return_block), rustc_middle::mir::UnwindAction::Continue) => {
208+
// MIR function or foreign function calls without a cleanup block.
209+
let end_place =
210+
current_function.get_end_place_for_function_call(return_block, &mut self.net);
211+
(end_place, None)
212+
}
213+
(Some(return_block), rustc_middle::mir::UnwindAction::Cleanup(cleanup_block)) => {
214+
// The usual foreign function call case.
215+
let end_place =
216+
current_function.get_end_place_for_function_call(return_block, &mut self.net);
217+
let cleanup_place =
218+
current_function.get_end_place_for_function_call(cleanup_block, &mut self.net);
219+
(end_place, Some(cleanup_place))
220+
}
221+
(Some(return_block), rustc_middle::mir::UnwindAction::Terminate) => {
222+
// Specific foreign function calls that terminate the program (abort).
223+
let end_place =
224+
current_function.get_end_place_for_function_call(return_block, &mut self.net);
225+
// Connect cleanup to panic place
226+
(end_place, Some(self.program_panic.clone()))
227+
}
228+
(None, rustc_middle::mir::UnwindAction::Terminate) => {
229+
// Foreign function calls that simply terminate the program.
230+
(self.program_panic.clone(), None)
231+
}
232+
(None, rustc_middle::mir::UnwindAction::Cleanup(cleanup_block)) => {
233+
// A very special case seen in functions like `std::process::exit`
234+
// where the return block is actually expressed as a cleanup.
235+
// This needs to be modelled differently than a diverging function.
236+
let end_place =
237+
current_function.get_end_place_for_function_call(cleanup_block, &mut self.net);
238+
(end_place, None)
239+
}
240+
(None, rustc_middle::mir::UnwindAction::Continue) => {
241+
// Call to a function which does not return (Return type: -> !).
242+
// Non-recursive call for the translation process.
243+
call_diverging_function(&start_place, &function_name, &mut self.net);
244+
return;
245+
}
246+
(Some(return_block), rustc_middle::mir::UnwindAction::Unreachable) => {
247+
// Support the unreachable case simply by matching the cleanup place to the program end place.
248+
// This is a compromise solution to avoid polluting the panic state with these extraneous states
249+
// that are actually not reachable during execution.
250+
let end_place =
251+
current_function.get_end_place_for_function_call(return_block, &mut self.net);
252+
// Connect cleanup to program end place.
253+
(end_place, Some(self.program_end.clone()))
254+
}
255+
(None, rustc_middle::mir::UnwindAction::Unreachable) => {
256+
// Support the unreachable case simply by matching the cleanup place to the program end place.
257+
// This is a compromise solution to avoid polluting the panic state with these extraneous states
258+
// that are actually not reachable during execution.
259+
(self.program_end.clone(), None)
260+
}
220261
};
221262

222263
let function_call = FunctionCall::new(function_def_id, self.tcx);

0 commit comments

Comments
 (0)