Inline FnOnce once again

This commit is contained in:
Michael Goulet 2025-03-03 02:12:28 +00:00
parent e081b7b77e
commit d33946c3ab
23 changed files with 391 additions and 116 deletions

View file

@ -625,13 +625,6 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
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),
@ -641,18 +634,23 @@ 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 ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else {
bug!("Closure arguments are not passed as a tuple");
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) {
trace!(?arg_ty, ?input_type);
debug!("failed to normalize tuple argument type");
return Err("implementation limitation -- arg mismatch");
return Err("implementation limitation");
}
}
} else {
@ -1059,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: {