1
Fork 0

Auto merge of #137907 - compiler-errors:inline-fnonce, r=saethlin

Inline `FnOnce`/`FnMut`/`Fn` shims once again

This PR fixes the argument checking for `extern "rust-call"` ABI functions with a spread arg, which do no expect their arguments to be exploded from a tuple like closures do.

Secondly, it removes the hack that prevented them from being inlined. This results in more work done by the compiler, but it does end up allowing us to inline functions we didn't before.

Fixes #137901
This commit is contained in:
bors 2025-03-05 18:39:17 +00:00
commit 30f168ef81
23 changed files with 397 additions and 123 deletions

View file

@ -606,14 +606,14 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
ty::EarlyBinder::bind(callee_body.clone()),
) else {
debug!("failed to normalize callee body");
return Err("implementation limitation");
return Err("implementation limitation -- could not normalize callee body");
};
// Normally, this shouldn't be required, but trait normalization failure can create a
// validation ICE.
if !validate_types(tcx, inliner.typing_env(), &callee_body, &caller_body).is_empty() {
debug!("failed to validate callee body");
return Err("implementation limitation");
return Err("implementation limitation -- callee body failed validation");
}
// Check call signature compatibility.
@ -622,17 +622,9 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
let output_type = callee_body.return_ty();
if !util::sub_types(tcx, inliner.typing_env(), output_type, destination_ty) {
trace!(?output_type, ?destination_ty);
debug!("failed to normalize return type");
return Err("implementation limitation");
return Err("implementation limitation -- return type mismatch");
}
if callsite.fn_sig.abi() == ExternAbi::RustCall {
// FIXME: Don't inline user-written `extern "rust-call"` functions,
// since this is generally perf-negative on rustc, and we hope that
// LLVM will inline these functions instead.
if callee_body.spread_arg.is_some() {
return Err("user-written rust-call functions");
}
let (self_arg, arg_tuple) = match &args[..] {
[arg_tuple] => (None, arg_tuple),
[self_arg, arg_tuple] => (Some(self_arg), arg_tuple),
@ -642,12 +634,17 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
let self_arg_ty = self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, tcx));
let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, tcx);
let arg_tys = if callee_body.spread_arg.is_some() {
std::slice::from_ref(&arg_tuple_ty)
} else {
let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else {
bug!("Closure arguments are not passed as a tuple");
};
arg_tuple_tys.as_slice()
};
for (arg_ty, input) in
self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter())
self_arg_ty.into_iter().chain(arg_tys.iter().copied()).zip(callee_body.args_iter())
{
let input_type = callee_body.local_decls[input].ty;
if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) {
@ -663,7 +660,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) {
trace!(?arg_ty, ?input_type);
debug!("failed to normalize argument type");
return Err("implementation limitation");
return Err("implementation limitation -- arg mismatch");
}
}
}
@ -693,13 +690,13 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>(
// won't cause cycles on this.
if !inliner.tcx().is_mir_available(callee_def_id) {
debug!("item MIR unavailable");
return Err("implementation limitation");
return Err("implementation limitation -- MIR unavailable");
}
}
// These have no own callable MIR.
InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => {
debug!("instance without MIR (intrinsic / virtual)");
return Err("implementation limitation");
return Err("implementation limitation -- cannot inline intrinsic");
}
// FIXME(#127030): `ConstParamHasTy` has bad interactions with
@ -709,7 +706,7 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>(
// substituted.
InstanceKind::DropGlue(_, Some(ty)) if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => {
debug!("still needs substitution");
return Err("implementation limitation");
return Err("implementation limitation -- HACK for dropping polymorphic type");
}
// This cannot result in an immediate cycle since the callee MIR is a shim, which does
@ -1060,8 +1057,7 @@ fn make_call_args<'tcx, I: Inliner<'tcx>>(
closure_ref_arg.chain(tuple_tmp_args).collect()
} else {
// FIXME(edition_2024): switch back to a normal method call.
<_>::into_iter(args)
args.into_iter()
.map(|a| create_temp_if_necessary(inliner, a.node, callsite, caller_body, return_block))
.collect()
}

View file

@ -7,23 +7,42 @@
let mut _0: ();
let mut _3: &mut std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>;
let mut _4: I;
+ scope 1 (inlined <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut) {
+ let mut _5: &mut dyn std::ops::FnMut<I, Output = ()>;
+ let mut _6: std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>;
+ let mut _7: *const dyn std::ops::FnMut<I, Output = ()>;
+ }
bb0: {
StorageLive(_3);
_3 = &mut _1;
StorageLive(_4);
_4 = move _2;
_0 = <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut(move _3, move _4) -> [return: bb1, unwind unreachable];
- _0 = <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut(move _3, move _4) -> [return: bb1, unwind unreachable];
+ StorageLive(_6);
+ StorageLive(_7);
+ StorageLive(_5);
+ _6 = copy (*_3);
+ _7 = copy ((_6.0: std::ptr::Unique<dyn std::ops::FnMut<I, Output = ()>>).0: std::ptr::NonNull<dyn std::ops::FnMut<I, Output = ()>>) as *const dyn std::ops::FnMut<I, Output = ()> (Transmute);
+ _5 = &mut (*_7);
+ _0 = <dyn FnMut<I, Output = ()> as FnMut<I>>::call_mut(move _5, move _4) -> [return: bb2, unwind unreachable];
}
bb1: {
StorageDead(_4);
StorageDead(_3);
drop(_1) -> [return: bb2, unwind unreachable];
- StorageDead(_4);
- StorageDead(_3);
- drop(_1) -> [return: bb2, unwind unreachable];
+ return;
}
bb2: {
return;
- return;
+ StorageDead(_5);
+ StorageDead(_7);
+ StorageDead(_6);
+ StorageDead(_4);
+ StorageDead(_3);
+ drop(_1) -> [return: bb1, unwind unreachable];
}
}

View file

@ -7,31 +7,54 @@
let mut _0: ();
let mut _3: &mut std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>;
let mut _4: I;
+ scope 1 (inlined <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut) {
+ let mut _5: &mut dyn std::ops::FnMut<I, Output = ()>;
+ let mut _6: std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>;
+ let mut _7: *const dyn std::ops::FnMut<I, Output = ()>;
+ }
bb0: {
StorageLive(_3);
_3 = &mut _1;
StorageLive(_4);
_4 = move _2;
_0 = <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut(move _3, move _4) -> [return: bb1, unwind: bb3];
- _0 = <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut(move _3, move _4) -> [return: bb1, unwind: bb3];
+ StorageLive(_6);
+ StorageLive(_7);
+ StorageLive(_5);
+ _6 = copy (*_3);
+ _7 = copy ((_6.0: std::ptr::Unique<dyn std::ops::FnMut<I, Output = ()>>).0: std::ptr::NonNull<dyn std::ops::FnMut<I, Output = ()>>) as *const dyn std::ops::FnMut<I, Output = ()> (Transmute);
+ _5 = &mut (*_7);
+ _0 = <dyn FnMut<I, Output = ()> as FnMut<I>>::call_mut(move _5, move _4) -> [return: bb4, unwind: bb2];
}
bb1: {
StorageDead(_4);
StorageDead(_3);
drop(_1) -> [return: bb2, unwind: bb4];
- StorageDead(_4);
- StorageDead(_3);
- drop(_1) -> [return: bb2, unwind: bb4];
+ return;
}
bb2: {
return;
- bb2: {
- return;
+ bb2 (cleanup): {
+ drop(_1) -> [return: bb3, unwind terminate(cleanup)];
}
bb3 (cleanup): {
drop(_1) -> [return: bb4, unwind terminate(cleanup)];
- drop(_1) -> [return: bb4, unwind terminate(cleanup)];
+ resume;
}
bb4 (cleanup): {
resume;
- bb4 (cleanup): {
- resume;
+ bb4: {
+ StorageDead(_5);
+ StorageDead(_7);
+ StorageDead(_6);
+ StorageDead(_4);
+ StorageDead(_3);
+ drop(_1) -> [return: bb1, unwind: bb3];
}
}

View file

@ -8,6 +8,6 @@ use std::marker::Tuple;
// EMIT_MIR dont_ice_on_generic_rust_call.call.Inline.diff
pub fn call<I: Tuple>(mut mock: Box<dyn FnMut<I, Output = ()>>, input: I) {
// CHECK-LABEL: fn call(
// CHECK-NOT: inlined
// CHECK: (inlined <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut)
mock.call_mut(input)
}

View file

@ -7,6 +7,11 @@
let _2: ();
let mut _3: &std::boxed::Box<dyn std::ops::Fn(i32)>;
let mut _4: (i32,);
+ scope 1 (inlined <Box<dyn Fn(i32)> as Fn<(i32,)>>::call) {
+ let mut _5: &dyn std::ops::Fn(i32);
+ let mut _6: std::boxed::Box<dyn std::ops::Fn(i32)>;
+ let mut _7: *const dyn std::ops::Fn(i32);
+ }
bb0: {
StorageLive(_2);
@ -14,19 +19,34 @@
_3 = &_1;
StorageLive(_4);
_4 = (const 1_i32,);
_2 = <Box<dyn Fn(i32)> as Fn<(i32,)>>::call(move _3, move _4) -> [return: bb1, unwind unreachable];
- _2 = <Box<dyn Fn(i32)> as Fn<(i32,)>>::call(move _3, move _4) -> [return: bb1, unwind unreachable];
+ StorageLive(_6);
+ StorageLive(_7);
+ StorageLive(_5);
+ _6 = copy (*_3);
+ _7 = copy ((_6.0: std::ptr::Unique<dyn std::ops::Fn(i32)>).0: std::ptr::NonNull<dyn std::ops::Fn(i32)>) as *const dyn std::ops::Fn(i32) (Transmute);
+ _5 = &(*_7);
+ _2 = <dyn Fn(i32) as Fn<(i32,)>>::call(move _5, move _4) -> [return: bb2, unwind unreachable];
}
bb1: {
+ return;
+ }
+
+ bb2: {
+ StorageDead(_5);
+ StorageDead(_7);
+ StorageDead(_6);
StorageDead(_4);
StorageDead(_3);
StorageDead(_2);
_0 = const ();
drop(_1) -> [return: bb2, unwind unreachable];
}
bb2: {
return;
- drop(_1) -> [return: bb2, unwind unreachable];
- }
-
- bb2: {
- return;
+ drop(_1) -> [return: bb1, unwind unreachable];
}
}

View file

@ -7,6 +7,11 @@
let _2: ();
let mut _3: &std::boxed::Box<dyn std::ops::Fn(i32)>;
let mut _4: (i32,);
+ scope 1 (inlined <Box<dyn Fn(i32)> as Fn<(i32,)>>::call) {
+ let mut _5: &dyn std::ops::Fn(i32);
+ let mut _6: std::boxed::Box<dyn std::ops::Fn(i32)>;
+ let mut _7: *const dyn std::ops::Fn(i32);
+ }
bb0: {
StorageLive(_2);
@ -14,27 +19,47 @@
_3 = &_1;
StorageLive(_4);
_4 = (const 1_i32,);
_2 = <Box<dyn Fn(i32)> as Fn<(i32,)>>::call(move _3, move _4) -> [return: bb1, unwind: bb3];
- _2 = <Box<dyn Fn(i32)> as Fn<(i32,)>>::call(move _3, move _4) -> [return: bb1, unwind: bb3];
+ StorageLive(_6);
+ StorageLive(_7);
+ StorageLive(_5);
+ _6 = copy (*_3);
+ _7 = copy ((_6.0: std::ptr::Unique<dyn std::ops::Fn(i32)>).0: std::ptr::NonNull<dyn std::ops::Fn(i32)>) as *const dyn std::ops::Fn(i32) (Transmute);
+ _5 = &(*_7);
+ _2 = <dyn Fn(i32) as Fn<(i32,)>>::call(move _5, move _4) -> [return: bb4, unwind: bb2];
}
bb1: {
StorageDead(_4);
StorageDead(_3);
StorageDead(_2);
_0 = const ();
drop(_1) -> [return: bb2, unwind: bb4];
- StorageDead(_4);
- StorageDead(_3);
- StorageDead(_2);
- _0 = const ();
- drop(_1) -> [return: bb2, unwind: bb4];
+ return;
}
bb2: {
return;
- bb2: {
- return;
+ bb2 (cleanup): {
+ drop(_1) -> [return: bb3, unwind terminate(cleanup)];
}
bb3 (cleanup): {
drop(_1) -> [return: bb4, unwind terminate(cleanup)];
- drop(_1) -> [return: bb4, unwind terminate(cleanup)];
+ resume;
}
bb4 (cleanup): {
resume;
- bb4 (cleanup): {
- resume;
+ bb4: {
+ StorageDead(_5);
+ StorageDead(_7);
+ StorageDead(_6);
+ StorageDead(_4);
+ StorageDead(_3);
+ StorageDead(_2);
+ _0 = const ();
+ drop(_1) -> [return: bb1, unwind: bb3];
}
}

View file

@ -5,6 +5,6 @@
// EMIT_MIR inline_box_fn.call.Inline.diff
fn call(x: Box<dyn Fn(i32)>) {
// CHECK-LABEL: fn call(
// CHECK-NOT: inlined
// CHECK: (inlined <Box<dyn Fn(i32)> as Fn<(i32,)>>::call)
x(1);
}

View file

@ -5,9 +5,15 @@
let mut _0: ();
let _1: ();
+ let mut _2: fn() {f};
+ let mut _4: ();
+ scope 1 (inlined call::<fn() {f}>) {
+ debug f => _2;
+ let _3: ();
+ scope 2 (inlined <fn() {f} as FnOnce<()>>::call_once - shim(fn() {f})) {
+ scope 3 (inlined f) {
+ let _5: ();
+ }
+ }
+ }
bb0: {
@ -16,10 +22,15 @@
+ StorageLive(_2);
+ _2 = f;
+ StorageLive(_3);
+ _3 = <fn() {f} as FnOnce<()>>::call_once(move _2, const ()) -> [return: bb1, unwind unreachable];
+ StorageLive(_4);
+ _4 = const ();
+ StorageLive(_5);
+ _5 = call::<fn() {f}>(f) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
+ StorageDead(_4);
+ StorageDead(_3);
+ StorageDead(_2);
StorageDead(_1);

View file

@ -5,9 +5,15 @@
let mut _0: ();
let _1: ();
+ let mut _2: fn() {f};
+ let mut _4: ();
+ scope 1 (inlined call::<fn() {f}>) {
+ debug f => _2;
+ let _3: ();
+ scope 2 (inlined <fn() {f} as FnOnce<()>>::call_once - shim(fn() {f})) {
+ scope 3 (inlined f) {
+ let _5: ();
+ }
+ }
+ }
bb0: {
@ -16,10 +22,15 @@
+ StorageLive(_2);
+ _2 = f;
+ StorageLive(_3);
+ _3 = <fn() {f} as FnOnce<()>>::call_once(move _2, const ()) -> [return: bb1, unwind continue];
+ StorageLive(_4);
+ _4 = const ();
+ StorageLive(_5);
+ _5 = call::<fn() {f}>(f) -> [return: bb1, unwind continue];
}
bb1: {
+ StorageDead(_5);
+ StorageDead(_4);
+ StorageDead(_3);
+ StorageDead(_2);
StorageDead(_1);

View file

@ -5,6 +5,7 @@
let mut _0: ();
let _1: (!, !);
+ let mut _2: fn() -> ! {sleep};
+ let mut _7: ();
+ scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) {
+ debug f => _2;
+ let mut _3: &fn() -> ! {sleep};
@ -17,6 +18,10 @@
+ debug b => _6;
+ }
+ }
+ scope 4 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) {
+ scope 5 (inlined sleep) {
+ }
+ }
+ }
bb0: {
@ -28,24 +33,13 @@
+ StorageLive(_6);
+ StorageLive(_3);
+ _3 = &_2;
+ _4 = <fn() -> ! {sleep} as Fn<()>>::call(move _3, const ()) -> [return: bb1, unwind unreachable];
+ StorageLive(_7);
+ _7 = const ();
+ goto -> bb1;
+ }
+
+ bb1: {
+ StorageDead(_3);
+ StorageLive(_5);
+ _5 = &_2;
+ _6 = <fn() -> ! {sleep} as Fn<()>>::call(move _5, const ()) -> [return: bb2, unwind unreachable];
+ }
+
+ bb2: {
+ StorageDead(_5);
+ _1 = (copy _4, copy _6);
+ drop(_2) -> [return: bb3, unwind unreachable];
+ }
+
+ bb3: {
+ unreachable;
+ goto -> bb1;
}
}

View file

@ -5,6 +5,7 @@
let mut _0: ();
let _1: (!, !);
+ let mut _2: fn() -> ! {sleep};
+ let mut _8: ();
+ scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) {
+ debug f => _2;
+ let mut _3: &fn() -> ! {sleep};
@ -18,6 +19,10 @@
+ debug b => _6;
+ }
+ }
+ scope 4 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) {
+ scope 5 (inlined sleep) {
+ }
+ }
+ }
bb0: {
@ -29,40 +34,13 @@
+ StorageLive(_4);
+ StorageLive(_3);
+ _3 = &_2;
+ _4 = <fn() -> ! {sleep} as Fn<()>>::call(move _3, const ()) -> [return: bb1, unwind: bb5];
+ StorageLive(_8);
+ _8 = const ();
+ goto -> bb1;
+ }
+
+ bb1: {
+ StorageDead(_3);
+ StorageLive(_5);
+ _5 = &_2;
+ _6 = <fn() -> ! {sleep} as Fn<()>>::call(move _5, const ()) -> [return: bb2, unwind: bb4];
+ }
+
+ bb2: {
+ StorageDead(_5);
+ StorageLive(_7);
+ _7 = move _4;
+ _1 = (move _7, copy _6);
+ StorageDead(_7);
+ StorageDead(_4);
+ drop(_2) -> [return: bb3, unwind continue];
+ }
+
+ bb3: {
+ unreachable;
+ }
+
+ bb4 (cleanup): {
+ drop(_4) -> [return: bb5, unwind terminate(cleanup)];
+ }
+
+ bb5 (cleanup): {
+ drop(_2) -> [return: bb6, unwind terminate(cleanup)];
+ }
+
+ bb6 (cleanup): {
+ resume;
+ goto -> bb1;
}
}

View file

@ -26,7 +26,8 @@ pub fn g(i: i32) -> u32 {
pub fn h() {
// CHECK-LABEL: fn h(
// CHECK: (inlined call_twice::<!, fn() -> ! {sleep}>)
// CHECK-NOT: inlined
// CHECK: (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep}))
// CHECK: (inlined sleep)
call_twice(sleep);
}

View file

@ -9,6 +9,10 @@
let _4: fn() {foo};
let mut _5: ();
+ scope 1 (inlined hide_foo) {
+ }
+ scope 2 (inlined <fn() {foo} as Fn<()>>::call - shim(fn() {foo})) {
+ scope 3 (inlined foo) {
+ }
+ }
bb0: {
@ -23,22 +27,20 @@
StorageLive(_5);
_5 = ();
- _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind unreachable];
+ _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb1, unwind unreachable];
}
- }
-
- bb2: {
+ bb1: {
StorageDead(_5);
StorageDead(_3);
StorageDead(_4);
StorageDead(_2);
_0 = const ();
- drop(_1) -> [return: bb3, unwind unreachable];
+ drop(_1) -> [return: bb2, unwind unreachable];
+ drop(_1) -> [return: bb1, unwind unreachable];
}
- bb3: {
+ bb2: {
+ bb1: {
return;
}
}

View file

@ -9,6 +9,10 @@
let _4: fn() {foo};
let mut _5: ();
+ scope 1 (inlined hide_foo) {
+ }
+ scope 2 (inlined <fn() {foo} as Fn<()>>::call - shim(fn() {foo})) {
+ scope 3 (inlined foo) {
+ }
+ }
bb0: {
@ -23,33 +27,29 @@
StorageLive(_5);
_5 = ();
- _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind: bb4];
+ _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb1, unwind: bb3];
}
- }
-
- bb2: {
+ bb1: {
StorageDead(_5);
StorageDead(_3);
StorageDead(_4);
StorageDead(_2);
_0 = const ();
- drop(_1) -> [return: bb3, unwind: bb5];
+ drop(_1) -> [return: bb2, unwind: bb4];
+ drop(_1) -> [return: bb1, unwind: bb2];
}
- bb3: {
+ bb2: {
+ bb1: {
return;
}
- bb4 (cleanup): {
- drop(_1) -> [return: bb5, unwind terminate(cleanup)];
+ bb3 (cleanup): {
+ drop(_1) -> [return: bb4, unwind terminate(cleanup)];
}
- }
-
- bb5 (cleanup): {
+ bb4 (cleanup): {
+ bb2 (cleanup): {
resume;
}
}

View file

@ -9,10 +9,9 @@ pub fn bar<P>(
_baz: P,
) {
// CHECK-LABEL: fn bar(
// CHECK: let mut {{.*}}: &fn() {foo};
// CHECK: let {{.*}}: fn() {foo};
// CHECK: (inlined hide_foo)
// CHECK-NOT: inlined
// CHECK: (inlined <fn() {foo} as Fn<()>>::call - shim(fn() {foo}))
// CHECK: (inlined foo)
hide_foo()();
}

View file

@ -0,0 +1,19 @@
//@ test-mir-pass: Inline
//@ compile-flags: --crate-type=lib -C panic=abort
// EMIT_MIR inline_fn_call_for_fn_def.test.Inline.diff
fn inline_fn(x: impl FnOnce() -> i32) -> i32 {
x()
}
fn yield_number() -> i32 {
64
}
fn test() -> i32 {
// CHECK: (inlined inline_fn::<fn() -> i32 {yield_number}>)
// CHECK: (inlined <fn() -> i32 {yield_number} as FnOnce<()>>::call_once - shim(fn() -> i32 {yield_number}))
// CHECK: (inlined yield_number)
inline_fn(yield_number)
}

View file

@ -0,0 +1,34 @@
- // MIR for `test` before Inline
+ // MIR for `test` after Inline
fn test() -> i32 {
let mut _0: i32;
-
- bb0: {
- _0 = inline_fn::<fn() -> i32 {yield_number}>(yield_number) -> [return: bb1, unwind unreachable];
+ let mut _1: fn() -> i32 {yield_number};
+ scope 1 (inlined inline_fn::<fn() -> i32 {yield_number}>) {
+ let mut _2: fn() -> i32 {yield_number};
+ let mut _3: ();
+ scope 2 (inlined <fn() -> i32 {yield_number} as FnOnce<()>>::call_once - shim(fn() -> i32 {yield_number})) {
+ scope 3 (inlined yield_number) {
+ }
+ }
}
- bb1: {
+ bb0: {
+ StorageLive(_1);
+ _1 = yield_number;
+ StorageLive(_2);
+ _2 = move _1;
+ StorageLive(_3);
+ _3 = ();
+ _0 = const 64_i32;
+ StorageDead(_3);
+ StorageDead(_2);
+ StorageDead(_1);
return;
}
}

View file

@ -0,0 +1,68 @@
// MIR for `num_to_digit` after PreCodegen
fn num_to_digit(_1: char) -> u32 {
debug num => _1;
let mut _0: u32;
let mut _4: std::option::Option<u32>;
scope 1 (inlined char::methods::<impl char>::is_digit) {
let _2: std::option::Option<u32>;
scope 2 (inlined Option::<u32>::is_some) {
let mut _3: isize;
}
}
scope 3 (inlined #[track_caller] Option::<u32>::unwrap) {
let mut _5: isize;
let mut _6: !;
scope 4 {
}
}
bb0: {
StorageLive(_2);
_2 = char::methods::<impl char>::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageLive(_3);
_3 = discriminant(_2);
StorageDead(_2);
switchInt(move _3) -> [1: bb2, otherwise: bb7];
}
bb2: {
StorageDead(_3);
StorageLive(_4);
_4 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageLive(_5);
_5 = discriminant(_4);
switchInt(move _5) -> [0: bb4, 1: bb5, otherwise: bb6];
}
bb4: {
_6 = option::unwrap_failed() -> unwind unreachable;
}
bb5: {
_0 = move ((_4 as Some).0: u32);
StorageDead(_5);
StorageDead(_4);
goto -> bb8;
}
bb6: {
unreachable;
}
bb7: {
StorageDead(_3);
_0 = const 0_u32;
goto -> bb8;
}
bb8: {
return;
}
}

View file

@ -0,0 +1,68 @@
// MIR for `num_to_digit` after PreCodegen
fn num_to_digit(_1: char) -> u32 {
debug num => _1;
let mut _0: u32;
let mut _4: std::option::Option<u32>;
scope 1 (inlined char::methods::<impl char>::is_digit) {
let _2: std::option::Option<u32>;
scope 2 (inlined Option::<u32>::is_some) {
let mut _3: isize;
}
}
scope 3 (inlined #[track_caller] Option::<u32>::unwrap) {
let mut _5: isize;
let mut _6: !;
scope 4 {
}
}
bb0: {
StorageLive(_2);
_2 = char::methods::<impl char>::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue];
}
bb1: {
StorageLive(_3);
_3 = discriminant(_2);
StorageDead(_2);
switchInt(move _3) -> [1: bb2, otherwise: bb7];
}
bb2: {
StorageDead(_3);
StorageLive(_4);
_4 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind continue];
}
bb3: {
StorageLive(_5);
_5 = discriminant(_4);
switchInt(move _5) -> [0: bb4, 1: bb5, otherwise: bb6];
}
bb4: {
_6 = option::unwrap_failed() -> unwind continue;
}
bb5: {
_0 = move ((_4 as Some).0: u32);
StorageDead(_5);
StorageDead(_4);
goto -> bb8;
}
bb6: {
unreachable;
}
bb7: {
StorageDead(_3);
_0 = const 0_u32;
goto -> bb8;
}
bb8: {
return;
}
}

View file

@ -1,4 +1,6 @@
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// EMIT_MIR_FOR_EACH_BIT_WIDTH
// This test is a mirror of codegen/issue-59352.rs.
// The LLVM inliner doesn't inline `char::method::is_digit()` and so it doesn't recognize this case
// as effectively `if x.is_some() { x.unwrap() } else { 0 }`.

View file

@ -29,6 +29,10 @@ fn mapped(_1: impl Iterator<Item = T>, _2: impl Fn(T) -> U) -> () {
let mut _12: U;
scope 6 {
debug x => _10;
scope 7 (inlined ops::function::impls::<impl FnOnce<(T,)> for &mut impl Fn(T) -> U>::call_once) {
debug self => _8;
debug args => _11;
}
}
}
}
@ -85,7 +89,7 @@ fn mapped(_1: impl Iterator<Item = T>, _2: impl Fn(T) -> U) -> () {
StorageLive(_12);
StorageLive(_11);
_11 = (copy _10,);
_12 = <&mut impl Fn(T) -> U as FnOnce<(T,)>>::call_once(move _8, move _11) -> [return: bb7, unwind: bb10];
_12 = <impl Fn(T) -> U as FnMut<(T,)>>::call_mut(move _8, move _11) -> [return: bb7, unwind: bb10];
}
bb7: {