From 0d6a16ac4b0905df17d93ed015ecb0a9a89c3484 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 18 Mar 2024 11:36:53 +0100 Subject: [PATCH] mentioned_items: record all callee and coerced closure types, whether they are FnDef/Closure or not They may become FnDef during monomorphization! --- compiler/rustc_middle/src/mir/mod.rs | 24 +++-- compiler/rustc_mir_transform/src/inline.rs | 12 +-- .../src/mentioned_items.rs | 71 ++++++++------ compiler/rustc_monomorphize/src/collector.rs | 93 +++++++++++++------ .../collect-in-dead-closure.opt.stderr | 2 +- ...-in-dead-fn-behind-assoc-type.noopt.stderr | 20 ++++ ...ct-in-dead-fn-behind-assoc-type.opt.stderr | 20 ++++ .../collect-in-dead-fn-behind-assoc-type.rs | 45 +++++++++ ...ect-in-dead-fn-behind-generic.noopt.stderr | 20 ++++ ...llect-in-dead-fn-behind-generic.opt.stderr | 20 ++++ .../collect-in-dead-fn-behind-generic.rs | 27 ++++++ ...in-dead-fn-behind-opaque-type.noopt.stderr | 20 ++++ ...t-in-dead-fn-behind-opaque-type.opt.stderr | 20 ++++ .../collect-in-dead-fn-behind-opaque-type.rs | 35 +++++++ .../collect-in-dead-fn.opt.stderr | 2 +- 15 files changed, 355 insertions(+), 76 deletions(-) create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 8e9b095028c..db40cb29082 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -45,6 +45,7 @@ use std::{iter, mem}; pub use self::query::*; +use self::visit::TyContext; pub use basic_blocks::BasicBlocks; mod basic_blocks; @@ -317,15 +318,15 @@ pub fn initial( #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable, TyEncodable, TyDecodable)] #[derive(TypeFoldable, TypeVisitable)] pub enum MentionedItem<'tcx> { - Fn(DefId, GenericArgsRef<'tcx>), + /// A function that gets called. We don't necessarily know its precise type yet, since it can be + /// hidden behind a generic. + Fn(Ty<'tcx>), + /// A type that has its drop shim called. Drop(Ty<'tcx>), /// Unsizing casts might require vtables, so we have to record them. - UnsizeCast { - source_ty: Ty<'tcx>, - target_ty: Ty<'tcx>, - }, + UnsizeCast { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> }, /// A closure that is coerced to a function pointer. - Closure(DefId, GenericArgsRef<'tcx>), + Closure(Ty<'tcx>), } /// The lowered representation of a single function. @@ -610,6 +611,17 @@ pub fn source_info(&self, location: Location) -> &SourceInfo { } } + pub fn span_for_ty_context(&self, ty_context: TyContext) -> Span { + match ty_context { + TyContext::UserTy(span) => span, + TyContext::ReturnTy(source_info) + | TyContext::LocalDecl { source_info, .. } + | TyContext::YieldTy(source_info) + | TyContext::ResumeTy(source_info) => source_info.span, + TyContext::Location(loc) => self.source_info(loc).span, + } + } + /// Returns the return type; it always return first element from `local_decls` array. #[inline] pub fn return_ty(&self) -> Ty<'tcx> { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 0f6a7ae982c..4ec76eec3a9 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -723,15 +723,9 @@ fn dest_needs_borrow(place: Place<'_>) -> bool { // some extra work here to save the monomorphization collector work later. It helps a lot, // since monomorphization can avoid a lot of work when the "mentioned items" are similar to // the actually used items. By doing this we can entirely avoid visiting the callee! - let callee_item = { - // We need to reconstruct the `required_item` for the callee so that we can find and - // remove it. - let func_ty = func.ty(caller_body, self.tcx); - match func_ty.kind() { - ty::FnDef(def_id, args) => MentionedItem::Fn(*def_id, args), - _ => bug!(), - } - }; + // We need to reconstruct the `required_item` for the callee so that we can find and + // remove it. + let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx)); if let Some(idx) = caller_body.mentioned_items.iter().position(|item| item.node == callee_item) { diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index 63f898630ab..765269a7c80 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, ConstOperand, Location, MentionedItem, MirPass}; -use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt}; +use rustc_middle::mir::{self, Location, MentionedItem, MirPass}; +use rustc_middle::ty::{adjustment::PointerCoercion, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::Spanned; @@ -29,27 +29,36 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { } } +// This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are +// visiting the exact same places but then instead of monomorphizing and creating `MonoItems`, we +// have to remain generic and just recording the relevant information in `mentioned_items`, where it +// will then be monomorphized later during "mentioned items" collection. impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { - fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) { - let const_ = constant.const_; - // This is how function items get referenced: via constants of `FnDef` type. This handles - // both functions that are called and those that are just turned to function pointers. - if let ty::FnDef(def_id, args) = const_.ty().kind() { - debug!("adding to required_items: {def_id:?}"); - self.mentioned_items - .push(Spanned { node: MentionedItem::Fn(*def_id, args), span: constant.span }); - } - } - fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { self.super_terminator(terminator, location); - match terminator.kind { - // We don't need to handle `Call` as we already handled all function type operands in - // `visit_constant`. But we do need to handle `Drop`. + let span = || self.body.source_info(location).span; + match &terminator.kind { + mir::TerminatorKind::Call { func, .. } => { + let callee_ty = func.ty(self.body, self.tcx); + self.mentioned_items + .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() }); + } mir::TerminatorKind::Drop { place, .. } => { let ty = place.ty(self.body, self.tcx).ty; - let span = self.body.source_info(location).span; - self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span }); + self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span: span() }); + } + mir::TerminatorKind::InlineAsm { ref operands, .. } => { + for op in operands { + match *op { + mir::InlineAsmOperand::SymFn { ref value } => { + self.mentioned_items.push(Spanned { + node: MentionedItem::Fn(value.const_.ty()), + span: span(), + }); + } + _ => {} + } + } } _ => {} } @@ -57,6 +66,7 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Loc fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { self.super_rvalue(rvalue, location); + let span = || self.body.source_info(location).span; match *rvalue { // We need to detect unsizing casts that required vtables. mir::Rvalue::Cast( @@ -65,13 +75,14 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { target_ty, ) | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { - let span = self.body.source_info(location).span; + // This isn't monomorphized yet so we can't tell what the actual types are -- just + // add everything. self.mentioned_items.push(Spanned { node: MentionedItem::UnsizeCast { source_ty: operand.ty(self.body, self.tcx), target_ty, }, - span, + span: span(), }); } // Similarly, record closures that are turned into function pointers. @@ -80,17 +91,19 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { 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!(), - } + self.mentioned_items + .push(Spanned { node: MentionedItem::Closure(source_ty), span: span() }); + } + // And finally, function pointer reification casts. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), + ref operand, + _, + ) => { + let fn_ty = operand.ty(self.body, self.tcx); + self.mentioned_items.push(Spanned { node: MentionedItem::Fn(fn_ty), span: span() }); } - // 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 a5ab4ab3d18..44950151203 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -173,6 +173,26 @@ //! do not use a failing constant. This is reflected via the [`CollectionMode`], which determines //! whether we are visiting a used item or merely a mentioned item. //! +//! The collector and "mentioned items" gathering (which lives in `rustc_mir_transform::mentioned_items`) +//! need to stay in sync in the following sense: +//! +//! - For every item that the collector gather that could eventually lead to build failure (most +//! likely due to containing a constant that fails to evaluate), a corresponding mentioned item +//! must be added. This should use the exact same strategy as the ecollector to make sure they are +//! in sync. However, while the collector works on monomorphized types, mentioned items are +//! collected on generic MIR -- so any time the collector checks for a particular type (such as +//! `ty::FnDef`), we have to just onconditionally add this as a mentioned item. +//! - In `visit_mentioned_item`, we then do with that mentioned item exactly what the collector +//! would have done during regular MIR visiting. Basically you can think of the collector having +//! two stages, a pre-monomorphization stage and a post-monomorphization stage (usually quite +//! literally separated by a call to `self.monomorphize`); the pre-monomorphizationn stage is +//! duplicated in mentioned items gathering and the post-monomorphization stage is duplicated in +//! `visit_mentioned_item`. +//! - Finally, as a performance optimization, the collector should fill `used_mentioned_item` during +//! its MIR traversal with exactly what mentioned item gathering would have added in the same +//! situation. This detects mentioned items that have *not* been optimized away and hence don't +//! need a dedicated traversal. +//! //! Open Issues //! ----------- //! Some things are not yet fully implemented in the current version of this @@ -904,8 +924,11 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { target_ty, ) | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { - let target_ty = self.monomorphize(target_ty); let source_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items + .insert(MentionedItem::UnsizeCast { source_ty, target_ty }); + let target_ty = self.monomorphize(target_ty); let source_ty = self.monomorphize(source_ty); let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.tcx.at(span), source_ty, target_ty); @@ -930,6 +953,8 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { _, ) => { let fn_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(fn_ty)); let fn_ty = self.monomorphize(fn_ty); visit_fn_use(self.tcx, fn_ty, false, span, self.used_items); } @@ -939,20 +964,17 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { _, ) => { let source_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Closure(source_ty)); let source_ty = self.monomorphize(source_ty); - match *source_ty.kind() { - ty::Closure(def_id, args) => { - let instance = Instance::resolve_closure( - self.tcx, - def_id, - args, - ty::ClosureKind::FnOnce, - ); - if should_codegen_locally(self.tcx, &instance) { - self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); - } + if let ty::Closure(def_id, args) = *source_ty.kind() { + let instance = + Instance::resolve_closure(self.tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(self.tcx, &instance) { + self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); } - _ => bug!(), + } else { + bug!() } } mir::Rvalue::ThreadLocalRef(def_id) => { @@ -994,9 +1016,7 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Loc mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { let callee_ty = func.ty(self.body, tcx); // *Before* monomorphizing, record that we already handled this mention. - if let ty::FnDef(def_id, args) = callee_ty.kind() { - self.used_mentioned_items.insert(MentionedItem::Fn(*def_id, args)); - } + self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); let callee_ty = self.monomorphize(callee_ty); self.check_fn_args_move_size(callee_ty, args, *fn_span, location); visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) @@ -1012,7 +1032,10 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Loc for op in operands { match *op { mir::InlineAsmOperand::SymFn { ref value } => { - let fn_ty = self.monomorphize(value.const_.ty()); + let fn_ty = value.const_.ty(); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(fn_ty)); + let fn_ty = self.monomorphize(fn_ty); visit_fn_use(self.tcx, fn_ty, false, source, self.used_items); } mir::InlineAsmOperand::SymStatic { def_id } => { @@ -1076,6 +1099,8 @@ fn visit_drop_use<'tcx>( visit_instance_use(tcx, instance, is_direct_call, source, output); } +/// For every call of this function in the visitor, make sure there is a matching call in the +/// `mentioned_items` pass! fn visit_fn_use<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, @@ -1653,13 +1678,13 @@ fn collect_items_of_instance<'tcx>( // Naively, in "used" collection mode, all functions get added to *both* `used_items` and // `mentioned_items`. Mentioned items processing will then notice that they have already been // visited, but at that point each mentioned item has been monomorphized, added to the - // `mentioned_items` worklist, and checked in the global set of visited items. To removes that + // `mentioned_items` worklist, and checked in the global set of visited items. To remove that // overhead, we have a special optimization that avoids adding items to `mentioned_items` when // they are already added in `used_items`. We could just scan `used_items`, but that's a linear // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already // added to `used_items` in a hash set, which can efficiently query in the - // `body.mentioned_items` loop below. + // `body.mentioned_items` loop below without even having to monomorphize the item. let mut used_mentioned_items = FxHashSet::>::default(); let mut collector = MirUsedCollector { tcx, @@ -1704,13 +1729,16 @@ fn visit_mentioned_item<'tcx>( output: &mut MonoItems<'tcx>, ) { match *item { - MentionedItem::Fn(def_id, args) => { - let instance = Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); - // `visit_instance_use` was written for "used" item collection but works just as well - // for "mentioned" item collection. - // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway - // can't have their own failing constants. - visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); + MentionedItem::Fn(ty) => { + if let ty::FnDef(def_id, args) = *ty.kind() { + let instance = + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); + // `visit_instance_use` was written for "used" item collection but works just as well + // for "mentioned" item collection. + // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway + // can't have their own failing constants. + visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); + } } MentionedItem::Drop(ty) => { visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output); @@ -1727,10 +1755,15 @@ 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)); + MentionedItem::Closure(source_ty) => { + if let ty::Closure(def_id, args) = *source_ty.kind() { + 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)); + } + } else { + bug!() } } } 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 index d6298132e1b..40cecaab241 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr @@ -16,7 +16,7 @@ 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 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr new file mode 100644 index 00000000000..ec9031440eb --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:9: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-fn-behind-assoc-type.rs:14:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +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-fn-behind-assoc-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr new file mode 100644 index 00000000000..ec9031440eb --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:9: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-fn-behind-assoc-type.rs:14:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +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-fn-behind-assoc-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs new file mode 100644 index 00000000000..a738f550f16 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs @@ -0,0 +1,45 @@ +#![feature(impl_trait_in_assoc_type)] +//@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 +} + +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +fn callit_not(f: impl Fn()) { + if false { + f(); + } +} + +// Using `Fn` here is important; with `FnOnce` another shim gets involved which somehow makes this +// easier to collect properly. +trait Hideaway { + type T: Fn(); + const C: Self::T; +} +impl Hideaway for () { + type T = impl Fn(); + const C: Self::T = not_called::; +} + +fn reveal() { + if false { + callit_not(T::C); + } +} + +fn main() { + if false { + reveal::<()>() + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr new file mode 100644 index 00000000000..51d8d6f35b4 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-generic.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.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-fn-behind-generic.rs:13:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +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-fn-behind-generic.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr new file mode 100644 index 00000000000..51d8d6f35b4 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-generic.rs:8:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.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-fn-behind-generic.rs:13:17 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +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-fn-behind-generic.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs new file mode 100644 index 00000000000..ac07af38c1d --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs @@ -0,0 +1,27 @@ +//@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 +} + +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +fn callit_not(f: impl Fn()) { + if false { + f(); + } +} + +fn main() { + if false { + callit_not(not_called::) + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr new file mode 100644 index 00000000000..fb0be0905c2 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `m::Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | + = 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-fn-behind-opaque-type.rs:17:21 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn m::not_called::` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +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-fn-behind-opaque-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr new file mode 100644 index 00000000000..fb0be0905c2 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `m::Fail::::C` failed + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:10:23 + | + = 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-fn-behind-opaque-type.rs:17:21 + | +LL | let _ = Fail::::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn m::not_called::` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +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-fn-behind-opaque-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs new file mode 100644 index 00000000000..245400f16a5 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs @@ -0,0 +1,35 @@ +//@revisions: noopt opt +//@ build-fail +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. +#![feature(type_alias_impl_trait)] + +mod m { + struct Fail(T); + impl Fail { + const C: () = panic!(); //~ERROR evaluation of `m::Fail::::C` failed + } + + pub type NotCalledFn = impl Fn(); + + fn not_called() { + if false { + let _ = Fail::::C; + } + } + + fn mk_not_called() -> NotCalledFn { + not_called:: + } +} + +fn main() { + // This does not involve a constant of `FnDef` type, it generates the value via unsafe + // shenanigans instead. This ensures that we check all `FnDef` types that occur in a function, + // not just those of constants. Furthermore the `FnDef` is behind an opaque type which bust be + // normalized away to reveal the function type. + if false { + let x: m::NotCalledFn = unsafe { std::mem::transmute(()) }; + x(); + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr index 25c9bb0f8c6..f0b7984d2fa 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr @@ -16,7 +16,7 @@ note: the above error was encountered while instantiating `fn not_called::` --> $DIR/collect-in-dead-fn.rs:25:9 | LL | not_called::(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error