diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index f7451e4c916..8e9b095028c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -324,7 +324,8 @@ pub enum MentionedItem<'tcx> { source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, }, - // FIXME: do we have to add closures? + /// A closure that is coerced to a function pointer. + Closure(DefId, GenericArgsRef<'tcx>), } /// The lowered representation of a single function. diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index 0e0114c9d2c..63f898630ab 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -74,6 +74,22 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { span, }); } + // Similarly, record closures that are turned into function pointers. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), + ref operand, + _, + ) => { + let span = self.body.source_info(location).span; + let source_ty = operand.ty(self.body, self.tcx); + match *source_ty.kind() { + ty::Closure(def_id, args) => { + self.mentioned_items + .push(Spanned { node: MentionedItem::Closure(def_id, args), span }); + } + _ => bug!(), + } + } // Function pointer casts are already handled by `visit_constant` above. _ => {} } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 005a2233507..a5ab4ab3d18 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1727,6 +1727,12 @@ fn visit_mentioned_item<'tcx>( create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, span, output); } } + MentionedItem::Closure(def_id, args) => { + let instance = Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(tcx, &instance) { + output.push(create_fn_mono_item(tcx, instance, span)); + } + } } } diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr new file mode 100644 index 00000000000..40cecaab241 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-closure.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.rs:8:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-closure.rs:16:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-in-dead-closure.rs:23:33 + | +LL | let _closure: fn() = || not_called::(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr new file mode 100644 index 00000000000..d6298132e1b --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-closure.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.rs:8:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-closure.rs:16:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-in-dead-closure.rs:23:33 + | +LL | let _closure: fn() = || not_called::(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.rs b/tests/ui/consts/required-consts/collect-in-dead-closure.rs new file mode 100644 index 00000000000..91f52827af4 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.rs @@ -0,0 +1,29 @@ +//@revisions: noopt opt +//@ build-fail +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but it is mentioned in a closure that is coerced to a +// function pointer in dead code in a function that is called. Make sure we still find this error. +#[inline(never)] +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called() { + if false { + let _closure: fn() = || not_called::(); + } +} + +pub fn main() { + called::(); +}