Skip to content

Commit 870d450

Browse files
committed
Auto merge of #122258 - RalfJung:required-fns, r=<try>
Draft: monomorphize things from dead code, too This is another attempt at fixing #107503. The previous attempt at #112879 seems stuck in figuring out where the perf regression comes from. So here I want to take baby steps to see the impact of each step. r? `@ghost`
2 parents 094a620 + 6be68ae commit 870d450

21 files changed

+251
-70
lines changed

compiler/rustc_middle/src/mir/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,13 @@ impl<'tcx> CoroutineInfo<'tcx> {
310310
}
311311
}
312312

313+
/// Some item that needs to monomorphize successfully for a MIR body to be considered well-formed.
314+
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
315+
pub enum RequiredItem<'tcx> {
316+
Fn(DefId, GenericArgsRef<'tcx>),
317+
Drop(Ty<'tcx>),
318+
}
319+
313320
/// The lowered representation of a single function.
314321
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
315322
pub struct Body<'tcx> {
@@ -375,6 +382,9 @@ pub struct Body<'tcx> {
375382
/// We hold in this field all the constants we are not able to evaluate yet.
376383
pub required_consts: Vec<ConstOperand<'tcx>>,
377384

385+
/// Further items that need to monomorphize successfully for this MIR to be well-formed.
386+
pub required_items: Vec<RequiredItem<'tcx>>,
387+
378388
/// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
379389
///
380390
/// Note that this does not actually mean that this body is not computable right now.
@@ -445,6 +455,7 @@ impl<'tcx> Body<'tcx> {
445455
var_debug_info,
446456
span,
447457
required_consts: Vec::new(),
458+
required_items: Vec::new(),
448459
is_polymorphic: false,
449460
injection_phase: None,
450461
tainted_by_errors,
@@ -473,6 +484,7 @@ impl<'tcx> Body<'tcx> {
473484
spread_arg: None,
474485
span: DUMMY_SP,
475486
required_consts: Vec::new(),
487+
required_items: Vec::new(),
476488
var_debug_info: Vec::new(),
477489
is_polymorphic: false,
478490
injection_phase: None,

compiler/rustc_mir_build/src/build/custom/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub(super) fn build_custom_mir<'tcx>(
5656
var_debug_info: Vec::new(),
5757
span,
5858
required_consts: Vec::new(),
59+
required_items: Vec::new(),
5960
is_polymorphic: false,
6061
tainted_by_errors: None,
6162
injection_phase: None,

compiler/rustc_mir_transform/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,14 @@ fn mir_promoted(
344344
}
345345

346346
let mut required_consts = Vec::new();
347-
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
347+
let mut required_items = Vec::new();
348+
let mut required_consts_visitor =
349+
RequiredConstsVisitor::new(tcx, &body, &mut required_consts, &mut required_items);
348350
for (bb, bb_data) in traversal::reverse_postorder(&body) {
349351
required_consts_visitor.visit_basic_block_data(bb, bb_data);
350352
}
351353
body.required_consts = required_consts;
354+
body.required_items = required_items;
352355

353356
// What we need to run borrowck etc.
354357
let promote_pass = promote_consts::PromoteTemps::default();
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
use rustc_middle::mir::visit::Visitor;
2-
use rustc_middle::mir::{Const, ConstOperand, Location};
3-
use rustc_middle::ty::ConstKind;
2+
use rustc_middle::mir::{self, Const, ConstOperand, Location, RequiredItem};
3+
use rustc_middle::ty::{self, ConstKind, TyCtxt};
44

55
pub struct RequiredConstsVisitor<'a, 'tcx> {
6+
tcx: TyCtxt<'tcx>,
7+
body: &'a mir::Body<'tcx>,
68
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
9+
required_items: &'a mut Vec<RequiredItem<'tcx>>,
710
}
811

912
impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
10-
pub fn new(required_consts: &'a mut Vec<ConstOperand<'tcx>>) -> Self {
11-
RequiredConstsVisitor { required_consts }
13+
pub fn new(
14+
tcx: TyCtxt<'tcx>,
15+
body: &'a mir::Body<'tcx>,
16+
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
17+
required_items: &'a mut Vec<RequiredItem<'tcx>>,
18+
) -> Self {
19+
RequiredConstsVisitor { tcx, body, required_consts, required_items }
1220
}
1321
}
1422

@@ -21,7 +29,27 @@ impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
2129
_ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c),
2230
},
2331
Const::Unevaluated(..) => self.required_consts.push(*constant),
24-
Const::Val(..) => {}
32+
Const::Val(_val, ty) => {
33+
// This is how function items get referenced: via zero-sized constants of `FnDef` type
34+
if let ty::FnDef(def_id, args) = ty.kind() {
35+
debug!("adding to required_items: {def_id:?}");
36+
self.required_items.push(RequiredItem::Fn(*def_id, args));
37+
}
38+
}
39+
}
40+
}
41+
42+
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
43+
self.super_terminator(terminator, location);
44+
45+
match terminator.kind {
46+
// We don't need to handle `Call` as we already handled all function type operands in
47+
// `visit_constant`. But we do need to handle `Drop`.
48+
mir::TerminatorKind::Drop { place, .. } => {
49+
let ty = place.ty(self.body, self.tcx).ty;
50+
self.required_items.push(RequiredItem::Drop(ty));
51+
}
52+
_ => {}
2553
}
2654
}
2755
}

compiler/rustc_monomorphize/src/collector.rs

+30
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,17 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
734734
}
735735

736736
impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
737+
fn visit_body(&mut self, body: &mir::Body<'tcx>) {
738+
for item in &body.required_items {
739+
// All these also need monomorphization to ensure that if that leads to error, we find
740+
// those errors.
741+
let item = self.monomorphize(*item);
742+
visit_required_item(self.tcx, item, self.output);
743+
}
744+
745+
self.super_body(body);
746+
}
747+
737748
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
738749
debug!("visiting rvalue {:?}", *rvalue);
739750

@@ -915,6 +926,25 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
915926
}
916927
}
917928

929+
fn visit_required_item<'tcx>(
930+
tcx: TyCtxt<'tcx>,
931+
item: mir::RequiredItem<'tcx>,
932+
output: &mut MonoItems<'tcx>,
933+
) {
934+
let instance = match item {
935+
mir::RequiredItem::Fn(def_id, args) => {
936+
Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
937+
}
938+
mir::RequiredItem::Drop(ty) => Instance::resolve_drop_in_place(tcx, ty),
939+
};
940+
// We pretend this is a direct call, just to make sure this is visited at all.
941+
// "indirect" would mean we also generate some shims, but we don't care about the
942+
// generated code, just about the side-effect of code generation causing errors, so we
943+
// can skip the shims.
944+
// FIXME: track the span so that we can show it here.
945+
visit_instance_use(tcx, instance, /*is_direct_call*/ true, DUMMY_SP, output);
946+
}
947+
918948
fn visit_drop_use<'tcx>(
919949
tcx: TyCtxt<'tcx>,
920950
ty: Ty<'tcx>,

tests/ui/consts/const-eval/erroneous-const.stderr

-15
This file was deleted.

tests/ui/consts/const-eval/erroneous-const2.stderr

-15
This file was deleted.

tests/ui/consts/const-eval/unused-broken-const-late.stderr

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/dead-code-in-called-fn.rs:9:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-called-fn.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: the above error was encountered while instantiating `fn called::<i32>`
10+
11+
error: aborting due to 1 previous error
12+
13+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/dead-code-in-called-fn.rs:9:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-called-fn.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: the above error was encountered while instantiating `fn called::<i32>`
10+
11+
error: aborting due to 1 previous error
12+
13+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1+
//@revisions: opt no-opt
12
//@ build-fail
2-
//@ compile-flags: -O
3+
//@[opt] compile-flags: -O
34
//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is
45
//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090)
56
6-
struct PrintName<T>(T);
7-
impl<T> PrintName<T> {
8-
const VOID: () = panic!(); //~ERROR evaluation of `PrintName::<i32>::VOID` failed
7+
struct Fail<T>(T);
8+
impl<T> Fail<T> {
9+
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
910
}
1011

11-
fn no_codegen<T>() {
12+
#[inline(never)]
13+
fn called<T>() {
1214
// Any function that is called is guaranteed to have all consts that syntactically
1315
// appear in its body evaluated, even if they only appear in dead code.
1416
if false {
15-
let _ = PrintName::<T>::VOID;
17+
let _ = Fail::<T>::C;
1618
}
1719
}
20+
1821
pub fn main() {
19-
no_codegen::<i32>();
22+
called::<i32>();
2023
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/dead-code-in-const-called-fn.rs:8:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-const-called-fn.rs:8: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/dead-code-in-const-called-fn.rs:16:9
11+
|
12+
LL | Fail::<T>::C;
13+
| ^^^^^^^^^^^^
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/dead-code-in-const-called-fn.rs:8:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-const-called-fn.rs:8: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/dead-code-in-const-called-fn.rs:16:9
11+
|
12+
LL | Fail::<T>::C;
13+
| ^^^^^^^^^^^^
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0080`.

tests/ui/consts/const-eval/erroneous-const.rs tests/ui/consts/monomorphization/dead-code-in-const-called-fn.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1+
//@revisions: opt no-opt
2+
//@[opt] compile-flags: -O
13
//! Make sure we error on erroneous consts even if they are unused.
24
#![allow(unconditional_panic)]
35

4-
struct PrintName<T>(T);
5-
impl<T> PrintName<T> {
6-
const VOID: () = [()][2]; //~ERROR evaluation of `PrintName::<i32>::VOID` failed
6+
struct Fail<T>(T);
7+
impl<T> Fail<T> {
8+
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
79
}
810

11+
#[inline(never)]
912
const fn no_codegen<T>() {
1013
if false {
1114
// This bad constant is only used in dead code in a no-codegen function... and yet we still
1215
// must make sure that the build fails.
13-
PrintName::<T>::VOID; //~ constant
16+
Fail::<T>::C; //~ constant
1417
}
1518
}
1619

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/dead-code-in-dead-fn.rs:9:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-dead-fn.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: the above error was encountered while instantiating `fn not_called::<i32>`
10+
11+
error: aborting due to 1 previous error
12+
13+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/dead-code-in-dead-fn.rs:9:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-dead-fn.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: the above error was encountered while instantiating `fn not_called::<i32>`
10+
11+
error: aborting due to 1 previous error
12+
13+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@revisions: opt no-opt
2+
//@ build-fail
3+
//@[opt] compile-flags: -O
4+
//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is
5+
//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090)
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+
// This function is not actually called, but it is mentioned in a function that is called.
13+
// Make sure we still find this error.
14+
#[inline(never)]
15+
fn not_called<T>() {
16+
let _ = Fail::<T>::C;
17+
}
18+
19+
#[inline(never)]
20+
fn called<T>() {
21+
if false {
22+
not_called::<T>();
23+
}
24+
}
25+
26+
pub fn main() {
27+
called::<i32>();
28+
}

0 commit comments

Comments
 (0)