Skip to content

Commit f716599

Browse files
committed
mentioned_items: record all callee types, whether they are FnDef or not
They may become FnDef during monomorphization!
1 parent de0feb3 commit f716599

9 files changed

+148
-54
lines changed

compiler/rustc_middle/src/mir/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -318,13 +318,13 @@ impl<'tcx> CoroutineInfo<'tcx> {
318318
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable, TyEncodable, TyDecodable)]
319319
#[derive(TypeFoldable, TypeVisitable)]
320320
pub enum MentionedItem<'tcx> {
321-
Fn(DefId, GenericArgsRef<'tcx>),
321+
/// A function that gets called. We don't necessarily know its precise type yet, since it can be
322+
/// hidden behind a generic.
323+
Fn(Ty<'tcx>),
324+
/// A type that has its drop shim called.
322325
Drop(Ty<'tcx>),
323326
/// Unsizing casts might require vtables, so we have to record them.
324-
UnsizeCast {
325-
source_ty: Ty<'tcx>,
326-
target_ty: Ty<'tcx>,
327-
},
327+
UnsizeCast { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> },
328328
/// A closure that is coerced to a function pointer.
329329
Closure(DefId, GenericArgsRef<'tcx>),
330330
}

compiler/rustc_mir_transform/src/inline.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -723,15 +723,9 @@ impl<'tcx> Inliner<'tcx> {
723723
// some extra work here to save the monomorphization collector work later. It helps a lot,
724724
// since monomorphization can avoid a lot of work when the "mentioned items" are similar to
725725
// the actually used items. By doing this we can entirely avoid visiting the callee!
726-
let callee_item = {
727-
// We need to reconstruct the `required_item` for the callee so that we can find and
728-
// remove it.
729-
let func_ty = func.ty(caller_body, self.tcx);
730-
match func_ty.kind() {
731-
ty::FnDef(def_id, args) => MentionedItem::Fn(*def_id, args),
732-
_ => bug!(),
733-
}
734-
};
726+
// We need to reconstruct the `required_item` for the callee so that we can find and
727+
// remove it.
728+
let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx));
735729
if let Some(idx) =
736730
caller_body.mentioned_items.iter().position(|item| item.node == callee_item)
737731
{

compiler/rustc_mir_transform/src/mentioned_items.rs

+40-22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use rustc_middle::mir::visit::Visitor;
22
use rustc_middle::mir::{self, Location, MentionedItem, MirPass};
3-
use rustc_middle::ty::Ty;
43
use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt};
54
use rustc_session::Session;
65
use rustc_span::source_map::Spanned;
@@ -30,33 +29,44 @@ impl<'tcx> MirPass<'tcx> for MentionedItems {
3029
}
3130
}
3231

32+
// This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are
33+
// visiting the exact same places but then instead of monomorphizing and creating `MonoItems`, we
34+
// have to remain generic and just recording the relevant information in `mentioned_items`, where it
35+
// will then be monomorphized later during "mentioned items" collection.
3336
impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
34-
fn visit_ty(&mut self, ty: Ty<'tcx>, ty_context: mir::visit::TyContext) {
35-
if let ty::FnDef(def_id, args) = ty.kind() {
36-
debug!("adding to required_items: {def_id:?}");
37-
self.mentioned_items.push(Spanned {
38-
node: MentionedItem::Fn(*def_id, args),
39-
span: self.body.span_for_ty_context(ty_context),
40-
});
41-
}
42-
}
43-
4437
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
4538
self.super_terminator(terminator, location);
46-
match terminator.kind {
47-
// We don't need to handle `Call` as we already handled all function type operands in
48-
// `visit_ty`. But we do need to handle `Drop`.
39+
let span = || self.body.source_info(location).span;
40+
match &terminator.kind {
41+
mir::TerminatorKind::Call { func, .. } => {
42+
let callee_ty = func.ty(self.body, self.tcx);
43+
self.mentioned_items
44+
.push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() });
45+
}
4946
mir::TerminatorKind::Drop { place, .. } => {
5047
let ty = place.ty(self.body, self.tcx).ty;
51-
let span = self.body.source_info(location).span;
52-
self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span });
48+
self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span: span() });
49+
}
50+
mir::TerminatorKind::InlineAsm { ref operands, .. } => {
51+
for op in operands {
52+
match *op {
53+
mir::InlineAsmOperand::SymFn { ref value } => {
54+
self.mentioned_items.push(Spanned {
55+
node: MentionedItem::Fn(value.const_.ty()),
56+
span: span(),
57+
});
58+
}
59+
_ => {}
60+
}
61+
}
5362
}
5463
_ => {}
5564
}
5665
}
5766

5867
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
5968
self.super_rvalue(rvalue, location);
69+
let span = || self.body.source_info(location).span;
6070
match *rvalue {
6171
// We need to detect unsizing casts that required vtables.
6272
mir::Rvalue::Cast(
@@ -65,13 +75,12 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
6575
target_ty,
6676
)
6777
| mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => {
68-
let span = self.body.source_info(location).span;
6978
self.mentioned_items.push(Spanned {
7079
node: MentionedItem::UnsizeCast {
7180
source_ty: operand.ty(self.body, self.tcx),
7281
target_ty,
7382
},
74-
span,
83+
span: span(),
7584
});
7685
}
7786
// Similarly, record closures that are turned into function pointers.
@@ -80,17 +89,26 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
8089
ref operand,
8190
_,
8291
) => {
83-
let span = self.body.source_info(location).span;
8492
let source_ty = operand.ty(self.body, self.tcx);
8593
match *source_ty.kind() {
8694
ty::Closure(def_id, args) => {
87-
self.mentioned_items
88-
.push(Spanned { node: MentionedItem::Closure(def_id, args), span });
95+
self.mentioned_items.push(Spanned {
96+
node: MentionedItem::Closure(def_id, args),
97+
span: span(),
98+
});
8999
}
90100
_ => bug!(),
91101
}
92102
}
93-
// Function pointer casts are already handled by `visit_ty` above.
103+
// And finally, function pointer reification casts.
104+
mir::Rvalue::Cast(
105+
mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer),
106+
ref operand,
107+
_,
108+
) => {
109+
let fn_ty = operand.ty(self.body, self.tcx);
110+
self.mentioned_items.push(Spanned { node: MentionedItem::Fn(fn_ty), span: span() });
111+
}
94112
_ => {}
95113
}
96114
}

compiler/rustc_monomorphize/src/collector.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -994,9 +994,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
994994
mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => {
995995
let callee_ty = func.ty(self.body, tcx);
996996
// *Before* monomorphizing, record that we already handled this mention.
997-
if let ty::FnDef(def_id, args) = callee_ty.kind() {
998-
self.used_mentioned_items.insert(MentionedItem::Fn(*def_id, args));
999-
}
997+
self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty));
1000998
let callee_ty = self.monomorphize(callee_ty);
1001999
self.check_fn_args_move_size(callee_ty, args, *fn_span, location);
10021000
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items)
@@ -1076,6 +1074,8 @@ fn visit_drop_use<'tcx>(
10761074
visit_instance_use(tcx, instance, is_direct_call, source, output);
10771075
}
10781076

1077+
/// For every call of this function in the visitor, make sure there is a matching call in the
1078+
/// `mentioned_items` pass!
10791079
fn visit_fn_use<'tcx>(
10801080
tcx: TyCtxt<'tcx>,
10811081
ty: Ty<'tcx>,
@@ -1700,13 +1700,16 @@ fn visit_mentioned_item<'tcx>(
17001700
output: &mut MonoItems<'tcx>,
17011701
) {
17021702
match *item {
1703-
MentionedItem::Fn(def_id, args) => {
1704-
let instance = Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args);
1705-
// `visit_instance_use` was written for "used" item collection but works just as well
1706-
// for "mentioned" item collection.
1707-
// We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway
1708-
// can't have their own failing constants.
1709-
visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output);
1703+
MentionedItem::Fn(ty) => {
1704+
if let ty::FnDef(def_id, args) = *ty.kind() {
1705+
let instance =
1706+
Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args);
1707+
// `visit_instance_use` was written for "used" item collection but works just as well
1708+
// for "mentioned" item collection.
1709+
// We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway
1710+
// can't have their own failing constants.
1711+
visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output);
1712+
}
17101713
}
17111714
MentionedItem::Drop(ty) => {
17121715
visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19
6+
|
7+
= 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)
8+
9+
note: erroneous constant encountered
10+
--> $DIR/collect-in-dead-fn-behind-assoc-type.rs:14:17
11+
|
12+
LL | let _ = Fail::<T>::C;
13+
| ^^^^^^^^^^^^
14+
15+
note: the above error was encountered while instantiating `fn not_called::<i32>`
16+
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:9:19
6+
|
7+
= 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)
8+
9+
note: erroneous constant encountered
10+
--> $DIR/collect-in-dead-fn-behind-assoc-type.rs:14:17
11+
|
12+
LL | let _ = Fail::<T>::C;
13+
| ^^^^^^^^^^^^
14+
15+
note: the above error was encountered while instantiating `fn not_called::<i32>`
16+
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![feature(impl_trait_in_assoc_type)]
2+
//@revisions: noopt opt
3+
//@ build-fail
4+
//@[opt] compile-flags: -O
5+
//! This fails without optimizations, so it should also fail with optimizations.
6+
7+
struct Fail<T>(T);
8+
impl<T> Fail<T> {
9+
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
10+
}
11+
12+
fn not_called<T>() {
13+
if false {
14+
let _ = Fail::<T>::C;
15+
}
16+
}
17+
18+
fn callit_not(f: impl Fn()) {
19+
if false {
20+
f();
21+
}
22+
}
23+
24+
// Using `Fn` here is important; with `FnOnce` another shim gets involved which somehow makes this
25+
// easier to collect properly.
26+
trait Hideaway {
27+
type T: Fn();
28+
const C: Self::T;
29+
}
30+
impl Hideaway for () {
31+
type T = impl Fn();
32+
const C: Self::T = not_called::<i32>;
33+
}
34+
35+
fn reveal<T: Hideaway>() {
36+
if false {
37+
callit_not(T::C);
38+
}
39+
}
40+
41+
fn main() {
42+
if false {
43+
reveal::<()>()
44+
}
45+
}

tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ LL | let _ = Fail::<T>::C;
1313
| ^^^^^^^^^^^^
1414

1515
note: the above error was encountered while instantiating `fn not_called::<i32>`
16-
--> $DIR/collect-in-dead-fn-behind-generic.rs:25:9
17-
|
18-
LL | callit_not(not_called::<i32>)
19-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
2017

2118
error: aborting due to 1 previous error
2219

tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ LL | let _ = Fail::<T>::C;
1313
| ^^^^^^^^^^^^
1414

1515
note: the above error was encountered while instantiating `fn m::not_called::<i32>`
16-
--> $DIR/collect-in-dead-fn-behind-opaque-type.rs:32:42
17-
|
18-
LL | let x: m::NotCalledFn = unsafe { std::mem::transmute(()) };
19-
| ^^^^^^^^^^^^^^^^^^^^^^^
16+
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
2017

2118
error: aborting due to 1 previous error
2219

0 commit comments

Comments
 (0)