Skip to content

Commit 743be1e

Browse files
authored
Rollup merge of rust-lang#124715 - RalfJung:interpret-noreturn, r=compiler-errors
interpret, miri: uniform treatments of intrinsics/functions with and without return block A long time ago we didn't have a `dest: &MPlaceTy<'tcx, Self::Provenance>` for diverging functions, and since `dest` is used so often we special-cased these non-returning intrinsics and functions so that we'd have `dest` available everywhere else. But this has changed a while ago, now only the return block `ret` is optional, and there's a convenient `return_to_block` function for dealing with the `None` case. So there no longer is any reason to treat diverging intrinsics/functions any different from those that do return.
2 parents a5cc1f6 + 8e44664 commit 743be1e

File tree

24 files changed

+189
-237
lines changed

24 files changed

+189
-237
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

+2-14
Original file line numberDiff line numberDiff line change
@@ -467,19 +467,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
467467
let intrinsic_name = ecx.tcx.item_name(instance.def_id());
468468

469469
// CTFE-specific intrinsics.
470-
let Some(ret) = target else {
471-
// Handle diverging intrinsics. We can't handle any of them (that are not already
472-
// handled above), but check if there is a fallback body.
473-
if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
474-
throw_unsup_format!(
475-
"intrinsic `{intrinsic_name}` is not supported at compile-time"
476-
);
477-
}
478-
return Ok(Some(ty::Instance {
479-
def: ty::InstanceDef::Item(instance.def_id()),
480-
args: instance.args,
481-
}));
482-
};
483470
match intrinsic_name {
484471
sym::ptr_guaranteed_cmp => {
485472
let a = ecx.read_scalar(&args[0])?;
@@ -559,7 +546,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
559546
}
560547
}
561548

562-
ecx.go_to_block(ret);
549+
// Intrinsic is done, jump to next block.
550+
ecx.return_to_block(target)?;
563551
Ok(None)
564552
}
565553

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
113113
) -> InterpResult<'tcx, bool> {
114114
let instance_args = instance.args;
115115
let intrinsic_name = self.tcx.item_name(instance.def_id());
116-
let Some(ret) = ret else {
117-
// We don't support any intrinsic without return place.
118-
return Ok(false);
119-
};
120116

121117
match intrinsic_name {
122118
sym::caller_location => {
@@ -376,7 +372,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
376372
};
377373

378374
M::panic_nounwind(self, &msg)?;
379-
// Skip the `go_to_block` at the end.
375+
// Skip the `return_to_block` at the end (we panicked, we do not return).
380376
return Ok(true);
381377
}
382378
}
@@ -437,11 +433,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
437433
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
438434
}
439435

436+
// Unsupported intrinsic: skip the return_to_block below.
440437
_ => return Ok(false),
441438
}
442439

443440
trace!("{:?}", self.dump_place(&dest.clone().into()));
444-
self.go_to_block(ret);
441+
self.return_to_block(ret)?;
445442
Ok(true)
446443
}
447444

src/tools/miri/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub use rustc_const_eval::interpret::*;
9595
#[doc(no_inline)]
9696
pub use rustc_const_eval::interpret::{self, AllocMap, PlaceTy, Provenance as _};
9797

98+
pub use crate::shims::EmulateItemResult;
9899
pub use crate::shims::env::{EnvVars, EvalContextExt as _};
99100
pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
100101
pub use crate::shims::intrinsics::EvalContextExt as _;

src/tools/miri/src/shims/alloc.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use rustc_ast::expand::allocator::AllocatorKind;
44
use rustc_target::abi::{Align, Size};
55

66
use crate::*;
7-
use shims::foreign_items::EmulateForeignItemResult;
87

98
/// Check some basic requirements for this allocation request:
109
/// non-zero size, power-of-two alignment.
@@ -55,12 +54,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
5554
fn emulate_allocator(
5655
&mut self,
5756
default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>,
58-
) -> InterpResult<'tcx, EmulateForeignItemResult> {
57+
) -> InterpResult<'tcx, EmulateItemResult> {
5958
let this = self.eval_context_mut();
6059

6160
let Some(allocator_kind) = this.tcx.allocator_kind(()) else {
6261
// in real code, this symbol does not exist without an allocator
63-
return Ok(EmulateForeignItemResult::NotSupported);
62+
return Ok(EmulateItemResult::NotSupported);
6463
};
6564

6665
match allocator_kind {
@@ -70,11 +69,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
7069
// and not execute any Miri shim. Somewhat unintuitively doing so is done
7170
// by returning `NotSupported`, which triggers the `lookup_exported_symbol`
7271
// fallback case in `emulate_foreign_item`.
73-
return Ok(EmulateForeignItemResult::NotSupported);
72+
return Ok(EmulateItemResult::NotSupported);
7473
}
7574
AllocatorKind::Default => {
7675
default(this)?;
77-
Ok(EmulateForeignItemResult::NeedsJumping)
76+
Ok(EmulateItemResult::NeedsJumping)
7877
}
7978
}
8079
}

src/tools/miri/src/shims/foreign_items.rs

+69-97
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,6 @@ impl DynSym {
2828
}
2929
}
3030

31-
/// Returned by `emulate_foreign_item_inner`.
32-
pub enum EmulateForeignItemResult {
33-
/// The caller is expected to jump to the return block.
34-
NeedsJumping,
35-
/// Jumping has already been taken care of.
36-
AlreadyJumped,
37-
/// The item is not supported.
38-
NotSupported,
39-
}
40-
4131
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
4232
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
4333
/// Emulates calling a foreign item, failing if the item is not supported.
@@ -58,84 +48,47 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
5848
let this = self.eval_context_mut();
5949
let tcx = this.tcx.tcx;
6050

61-
// First: functions that diverge.
62-
let ret = match ret {
63-
None =>
64-
match link_name.as_str() {
65-
"miri_start_unwind" => {
66-
// `check_shim` happens inside `handle_miri_start_unwind`.
67-
this.handle_miri_start_unwind(abi, link_name, args, unwind)?;
68-
return Ok(None);
69-
}
70-
// This matches calls to the foreign item `panic_impl`.
71-
// The implementation is provided by the function with the `#[panic_handler]` attribute.
72-
"panic_impl" => {
73-
// We don't use `check_shim` here because we are just forwarding to the lang
74-
// item. Argument count checking will be performed when the returned `Body` is
75-
// called.
76-
this.check_abi_and_shim_symbol_clash(abi, Abi::Rust, link_name)?;
77-
let panic_impl_id = tcx.lang_items().panic_impl().unwrap();
78-
let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id);
79-
return Ok(Some((
80-
this.load_mir(panic_impl_instance.def, None)?,
81-
panic_impl_instance,
82-
)));
83-
}
84-
"__rust_alloc_error_handler" => {
85-
// Forward to the right symbol that implements this function.
86-
let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else {
87-
// in real code, this symbol does not exist without an allocator
88-
throw_unsup_format!(
89-
"`__rust_alloc_error_handler` cannot be called when no alloc error handler is set"
90-
);
91-
};
92-
let name = alloc_error_handler_name(handler_kind);
93-
let handler = this
94-
.lookup_exported_symbol(Symbol::intern(name))?
95-
.expect("missing alloc error handler symbol");
96-
return Ok(Some(handler));
97-
}
98-
#[rustfmt::skip]
99-
| "exit"
100-
| "ExitProcess"
101-
=> {
102-
let exp_abi = if link_name.as_str() == "exit" {
103-
Abi::C { unwind: false }
104-
} else {
105-
Abi::System { unwind: false }
106-
};
107-
let [code] = this.check_shim(abi, exp_abi, link_name, args)?;
108-
// it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway
109-
let code = this.read_scalar(code)?.to_i32()?;
110-
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
111-
}
112-
"abort" => {
113-
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
114-
throw_machine_stop!(TerminationInfo::Abort(
115-
"the program aborted execution".to_owned()
116-
))
117-
}
118-
_ => {
119-
if let Some(body) = this.lookup_exported_symbol(link_name)? {
120-
return Ok(Some(body));
121-
}
122-
this.handle_unsupported(format!(
123-
"can't call (diverging) foreign function: {link_name}"
124-
))?;
125-
return Ok(None);
126-
}
127-
},
128-
Some(p) => p,
129-
};
51+
// Some shims forward to other MIR bodies.
52+
match link_name.as_str() {
53+
// This matches calls to the foreign item `panic_impl`.
54+
// The implementation is provided by the function with the `#[panic_handler]` attribute.
55+
"panic_impl" => {
56+
// We don't use `check_shim` here because we are just forwarding to the lang
57+
// item. Argument count checking will be performed when the returned `Body` is
58+
// called.
59+
this.check_abi_and_shim_symbol_clash(abi, Abi::Rust, link_name)?;
60+
let panic_impl_id = tcx.lang_items().panic_impl().unwrap();
61+
let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id);
62+
return Ok(Some((
63+
this.load_mir(panic_impl_instance.def, None)?,
64+
panic_impl_instance,
65+
)));
66+
}
67+
"__rust_alloc_error_handler" => {
68+
// Forward to the right symbol that implements this function.
69+
let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else {
70+
// in real code, this symbol does not exist without an allocator
71+
throw_unsup_format!(
72+
"`__rust_alloc_error_handler` cannot be called when no alloc error handler is set"
73+
);
74+
};
75+
let name = alloc_error_handler_name(handler_kind);
76+
let handler = this
77+
.lookup_exported_symbol(Symbol::intern(name))?
78+
.expect("missing alloc error handler symbol");
79+
return Ok(Some(handler));
80+
}
81+
_ => {}
82+
}
13083

131-
// Second: functions that return immediately.
132-
match this.emulate_foreign_item_inner(link_name, abi, args, dest)? {
133-
EmulateForeignItemResult::NeedsJumping => {
84+
// The rest either implements the logic, or falls back to `lookup_exported_symbol`.
85+
match this.emulate_foreign_item_inner(link_name, abi, args, dest, unwind)? {
86+
EmulateItemResult::NeedsJumping => {
13487
trace!("{:?}", this.dump_place(&dest.clone().into()));
135-
this.go_to_block(ret);
88+
this.return_to_block(ret)?;
13689
}
137-
EmulateForeignItemResult::AlreadyJumped => (),
138-
EmulateForeignItemResult::NotSupported => {
90+
EmulateItemResult::AlreadyJumped => (),
91+
EmulateItemResult::NotSupported => {
13992
if let Some(body) = this.lookup_exported_symbol(link_name)? {
14093
return Ok(Some(body));
14194
}
@@ -243,7 +196,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
243196
abi: Abi,
244197
args: &[OpTy<'tcx, Provenance>],
245198
dest: &MPlaceTy<'tcx, Provenance>,
246-
) -> InterpResult<'tcx, EmulateForeignItemResult> {
199+
unwind: mir::UnwindAction,
200+
) -> InterpResult<'tcx, EmulateItemResult> {
247201
let this = self.eval_context_mut();
248202

249203
// First deal with any external C functions in linked .so file.
@@ -254,7 +208,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
254208
// by the specified `.so` file; we should continue and check if it corresponds to
255209
// a provided shim.
256210
if this.call_external_c_fct(link_name, dest, args)? {
257-
return Ok(EmulateForeignItemResult::NeedsJumping);
211+
return Ok(EmulateItemResult::NeedsJumping);
258212
}
259213
}
260214

@@ -298,6 +252,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
298252
// shim, add it to the corresponding submodule.
299253
match link_name.as_str() {
300254
// Miri-specific extern functions
255+
"miri_start_unwind" => {
256+
// `check_shim` happens inside `handle_miri_start_unwind`.
257+
this.handle_miri_start_unwind(abi, link_name, args, unwind)?;
258+
return Ok(EmulateItemResult::AlreadyJumped);
259+
}
301260
"miri_run_provenance_gc" => {
302261
let [] = this.check_shim(abi, Abi::Rust, link_name, args)?;
303262
this.run_provenance_gc();
@@ -362,29 +321,24 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
362321
// Return value: 0 on success, otherwise the size it would have needed.
363322
this.write_int(if success { 0 } else { needed_size }, dest)?;
364323
}
365-
366324
// Obtains the size of a Miri backtrace. See the README for details.
367325
"miri_backtrace_size" => {
368326
this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
369327
}
370-
371328
// Obtains a Miri backtrace. See the README for details.
372329
"miri_get_backtrace" => {
373330
// `check_shim` happens inside `handle_miri_get_backtrace`.
374331
this.handle_miri_get_backtrace(abi, link_name, args, dest)?;
375332
}
376-
377333
// Resolves a Miri backtrace frame. See the README for details.
378334
"miri_resolve_frame" => {
379335
// `check_shim` happens inside `handle_miri_resolve_frame`.
380336
this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
381337
}
382-
383338
// Writes the function and file names of a Miri backtrace frame into a user provided buffer. See the README for details.
384339
"miri_resolve_frame_names" => {
385340
this.handle_miri_resolve_frame_names(abi, link_name, args)?;
386341
}
387-
388342
// Writes some bytes to the interpreter's stdout/stderr. See the
389343
// README for details.
390344
"miri_write_to_stdout" | "miri_write_to_stderr" => {
@@ -398,7 +352,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
398352
_ => unreachable!(),
399353
};
400354
}
401-
402355
// Promises that a pointer has a given symbolic alignment.
403356
"miri_promise_symbolic_alignment" => {
404357
use rustc_target::abi::AlignFromBytesError;
@@ -442,6 +395,25 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
442395
}
443396
}
444397

398+
// Aborting the process.
399+
"exit" | "ExitProcess" => {
400+
let exp_abi = if link_name.as_str() == "exit" {
401+
Abi::C { unwind: false }
402+
} else {
403+
Abi::System { unwind: false }
404+
};
405+
let [code] = this.check_shim(abi, exp_abi, link_name, args)?;
406+
// it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway
407+
let code = this.read_scalar(code)?.to_i32()?;
408+
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
409+
}
410+
"abort" => {
411+
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
412+
throw_machine_stop!(TerminationInfo::Abort(
413+
"the program aborted execution".to_owned()
414+
))
415+
}
416+
445417
// Standard C allocation
446418
"malloc" => {
447419
let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@@ -504,7 +476,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
504476
"__rust_alloc" => return this.emulate_allocator(default),
505477
"miri_alloc" => {
506478
default(this)?;
507-
return Ok(EmulateForeignItemResult::NeedsJumping);
479+
return Ok(EmulateItemResult::NeedsJumping);
508480
}
509481
_ => unreachable!(),
510482
}
@@ -564,7 +536,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
564536
}
565537
"miri_dealloc" => {
566538
default(this)?;
567-
return Ok(EmulateForeignItemResult::NeedsJumping);
539+
return Ok(EmulateItemResult::NeedsJumping);
568540
}
569541
_ => unreachable!(),
570542
}
@@ -966,11 +938,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
966938
shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
967939
this, link_name, abi, args, dest,
968940
),
969-
_ => Ok(EmulateForeignItemResult::NotSupported),
941+
_ => Ok(EmulateItemResult::NotSupported),
970942
},
971943
};
972944
// We only fall through to here if we did *not* hit the `_` arm above,
973945
// i.e., if we actually emulated the function with one of the shims.
974-
Ok(EmulateForeignItemResult::NeedsJumping)
946+
Ok(EmulateItemResult::NeedsJumping)
975947
}
976948
}

src/tools/miri/src/shims/intrinsics/atomic.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
2020
intrinsic_name: &str,
2121
args: &[OpTy<'tcx, Provenance>],
2222
dest: &MPlaceTy<'tcx, Provenance>,
23-
) -> InterpResult<'tcx, bool> {
23+
) -> InterpResult<'tcx, EmulateItemResult> {
2424
let this = self.eval_context_mut();
2525

2626
let intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect();
@@ -114,9 +114,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
114114
this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?;
115115
}
116116

117-
_ => return Ok(false),
117+
_ => return Ok(EmulateItemResult::NotSupported),
118118
}
119-
Ok(true)
119+
Ok(EmulateItemResult::NeedsJumping)
120120
}
121121
}
122122

0 commit comments

Comments
 (0)