Skip to content
13 changes: 13 additions & 0 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,14 @@ impl<'tcx> CoroutineInfo<'tcx> {
}
}

/// Some item that needs to monomorphize successfully for a MIR body to be considered well-formed.
#[derive(Copy, Clone, PartialEq, Debug, HashStable, TyEncodable, TyDecodable)]
#[derive(TypeFoldable, TypeVisitable)]
pub enum RequiredItem<'tcx> {
Fn(DefId, GenericArgsRef<'tcx>),
Drop(Ty<'tcx>),
}

/// The lowered representation of a single function.
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
pub struct Body<'tcx> {
Expand Down Expand Up @@ -375,6 +383,9 @@ pub struct Body<'tcx> {
/// We hold in this field all the constants we are not able to evaluate yet.
pub required_consts: Vec<ConstOperand<'tcx>>,

/// Further items that need to monomorphize successfully for this MIR to be well-formed.
pub required_items: Vec<RequiredItem<'tcx>>,

/// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
///
/// Note that this does not actually mean that this body is not computable right now.
Expand Down Expand Up @@ -445,6 +456,7 @@ impl<'tcx> Body<'tcx> {
var_debug_info,
span,
required_consts: Vec::new(),
required_items: Vec::new(),
is_polymorphic: false,
injection_phase: None,
tainted_by_errors,
Expand Down Expand Up @@ -473,6 +485,7 @@ impl<'tcx> Body<'tcx> {
spread_arg: None,
span: DUMMY_SP,
required_consts: Vec::new(),
required_items: Vec::new(),
var_debug_info: Vec::new(),
is_polymorphic: false,
injection_phase: None,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/build/custom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub(super) fn build_custom_mir<'tcx>(
var_debug_info: Vec::new(),
span,
required_consts: Vec::new(),
required_items: Vec::new(),
is_polymorphic: false,
tainted_by_errors: None,
injection_phase: None,
Expand Down
15 changes: 14 additions & 1 deletion compiler/rustc_mir_transform/src/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,8 @@ impl<'tcx> Inliner<'tcx> {
mut callee_body: Body<'tcx>,
) {
let terminator = caller_body[callsite.block].terminator.take().unwrap();
let TerminatorKind::Call { args, destination, unwind, target, .. } = terminator.kind else {
let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind
else {
bug!("unexpected terminator kind {:?}", terminator.kind);
};

Expand Down Expand Up @@ -717,6 +718,18 @@ impl<'tcx> Inliner<'tcx> {
Const::Val(..) | Const::Unevaluated(..) => true,
},
));
// Now that we incorporated the callee's `required_consts`, we can remove the callee from
// `required_items` -- but we have to take their `required_consts` in return.
let callee_item = {
// We need to reconstruct the `required_item` for the callee.
let func_ty = func.ty(caller_body, self.tcx);
match func_ty.kind() {
ty::FnDef(def_id, args) => RequiredItem::Fn(*def_id, args),
_ => bug!(),
}
};
caller_body.required_items.retain(|item| *item != callee_item);
caller_body.required_items.extend(callee_body.required_items);
}

fn make_call_args(
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,11 +344,14 @@ fn mir_promoted(
}

let mut required_consts = Vec::new();
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
let mut required_items = Vec::new();
let mut required_consts_visitor =
RequiredConstsVisitor::new(tcx, &body, &mut required_consts, &mut required_items);
for (bb, bb_data) in traversal::reverse_postorder(&body) {
required_consts_visitor.visit_basic_block_data(bb, bb_data);
}
body.required_consts = required_consts;
body.required_items = required_items;

// What we need to run borrowck etc.
let promote_pass = promote_consts::PromoteTemps::default();
Expand Down
39 changes: 34 additions & 5 deletions compiler/rustc_mir_transform/src/required_consts.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{Const, ConstOperand, Location};
use rustc_middle::ty::ConstKind;
use rustc_middle::mir::{self, Const, ConstOperand, Location, RequiredItem};
use rustc_middle::ty::{self, ConstKind, TyCtxt};

#[allow(unused)]
pub struct RequiredConstsVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
required_items: &'a mut Vec<RequiredItem<'tcx>>,
}

impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
pub fn new(required_consts: &'a mut Vec<ConstOperand<'tcx>>) -> Self {
RequiredConstsVisitor { required_consts }
pub fn new(
tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
required_items: &'a mut Vec<RequiredItem<'tcx>>,
) -> Self {
RequiredConstsVisitor { tcx, body, required_consts, required_items }
}
}

Expand All @@ -21,7 +30,27 @@ impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
_ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c),
},
Const::Unevaluated(..) => self.required_consts.push(*constant),
Const::Val(..) => {}
Const::Val(_val, ty) => {
// This is how function items get referenced: via zero-sized constants of `FnDef` type
if let ty::FnDef(def_id, args) = ty.kind() {
debug!("adding to required_items: {def_id:?}");
self.required_items.push(RequiredItem::Fn(*def_id, args));
}
}
}
}

/*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`.
mir::TerminatorKind::Drop { place, .. } => {
let ty = place.ty(self.body, self.tcx).ty;
self.required_items.push(RequiredItem::Drop(ty));
}
_ => {}
}
}*/
}
36 changes: 34 additions & 2 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ fn collect_items_rec<'tcx>(
return;
}

let mut used_items = Vec::new();
let mut used_items = MonoItems::new();
let recursion_depth_reset;

// Post-monomorphization errors MVP
Expand Down Expand Up @@ -734,6 +734,19 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
}

impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
fn visit_body(&mut self, body: &mir::Body<'tcx>) {
self.super_body(body);

// Go over `required_items` *after* body, so if an item appears in both we reach it the
// "normal" way, via the body.
for item in &body.required_items {
// All these also need monomorphization to ensure that if that leads to error, we find
// those errors.
let item = self.monomorphize(*item);
visit_required_item(self.tcx, item, self.output);
}
}

fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
debug!("visiting rvalue {:?}", *rvalue);

Expand Down Expand Up @@ -915,6 +928,25 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
}
}

fn visit_required_item<'tcx>(
tcx: TyCtxt<'tcx>,
item: mir::RequiredItem<'tcx>,
output: &mut MonoItems<'tcx>,
) {
let instance = match item {
mir::RequiredItem::Fn(def_id, args) => {
Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
}
mir::RequiredItem::Drop(ty) => Instance::resolve_drop_in_place(tcx, ty),
};
// We pretend this is a direct call, just to make sure this is visited at all.
// "indirect" would mean we also generate some shims, but we don't care about the
// generated code, just about the side-effect of code generation causing errors, so we
// can skip the shims.
// FIXME: track the span so that we can show it here.
visit_instance_use(tcx, instance, /*is_direct_call*/ true, DUMMY_SP, output);
}

fn visit_drop_use<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
Expand Down Expand Up @@ -953,7 +985,7 @@ fn visit_instance_use<'tcx>(
source: Span,
output: &mut MonoItems<'tcx>,
) {
debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call);
debug!("visit_instance_use({:?}, is_direct_call={:?})", instance, is_direct_call);
if !should_codegen_locally(tcx, &instance) {
return;
}
Expand Down
24 changes: 12 additions & 12 deletions tests/coverage/async.cov-map
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,21 @@ Number of expressions: 0
Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 17, 20) to (start + 0, 25)

Function name: async::e (unused)
Raw bytes (9): 0x[01, 01, 00, 01, 00, 13, 01, 00, 14]
Function name: async::e
Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 01, 00, 14]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Zero) at (prev + 19, 1) to (start + 0, 20)
- Code(Counter(0)) at (prev + 19, 1) to (start + 0, 20)

Function name: async::e::{closure#0} (unused)
Raw bytes (9): 0x[01, 01, 00, 01, 00, 13, 14, 00, 19]
Function name: async::e::{closure#0}
Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 14, 00, 19]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Zero) at (prev + 19, 20) to (start + 0, 25)
- Code(Counter(0)) at (prev + 19, 20) to (start + 0, 25)

Function name: async::f
Raw bytes (9): 0x[01, 01, 00, 01, 01, 15, 01, 00, 14]
Expand All @@ -69,21 +69,21 @@ Number of expressions: 0
Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 21, 20) to (start + 0, 25)

Function name: async::foo (unused)
Raw bytes (9): 0x[01, 01, 00, 01, 00, 17, 01, 00, 1e]
Function name: async::foo
Raw bytes (9): 0x[01, 01, 00, 01, 01, 17, 01, 00, 1e]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Zero) at (prev + 23, 1) to (start + 0, 30)
- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 30)

Function name: async::foo::{closure#0} (unused)
Raw bytes (9): 0x[01, 01, 00, 01, 00, 17, 1e, 00, 2d]
Function name: async::foo::{closure#0}
Raw bytes (9): 0x[01, 01, 00, 01, 01, 17, 1e, 00, 2d]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Zero) at (prev + 23, 30) to (start + 0, 45)
- Code(Counter(0)) at (prev + 23, 30) to (start + 0, 45)

Function name: async::g
Raw bytes (9): 0x[01, 01, 00, 01, 01, 19, 01, 00, 17]
Expand Down
18 changes: 11 additions & 7 deletions tests/coverage/closure.cov-map
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,20 @@ Number of file 0 mappings: 24
- Code(Expression(1, Add)) at (prev + 1, 5) to (start + 3, 2)
= (c1 + (c0 - c1))

Function name: closure::main::{closure#0} (unused)
Raw bytes (24): 0x[01, 01, 00, 04, 00, 28, 05, 02, 14, 00, 02, 15, 02, 0a, 00, 02, 0a, 00, 0b, 00, 01, 09, 01, 06]
Function name: closure::main::{closure#0}
Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 28, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 07, 01, 09, 01, 06]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of expressions: 2
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
Number of file 0 mappings: 4
- Code(Zero) at (prev + 40, 5) to (start + 2, 20)
- Code(Zero) at (prev + 2, 21) to (start + 2, 10)
- Code(Zero) at (prev + 2, 10) to (start + 0, 11)
- Code(Zero) at (prev + 1, 9) to (start + 1, 6)
- Code(Counter(0)) at (prev + 40, 5) to (start + 2, 20)
- Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10)
- Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11)
= (c0 - c1)
- Code(Expression(1, Add)) at (prev + 1, 9) to (start + 1, 6)
= (c1 + (c0 - c1))

Function name: closure::main::{closure#10} (unused)
Raw bytes (10): 0x[01, 01, 00, 01, 00, 9b, 01, 07, 00, 21]
Expand Down
15 changes: 0 additions & 15 deletions tests/ui/consts/const-eval/erroneous-const.stderr

This file was deleted.

15 changes: 0 additions & 15 deletions tests/ui/consts/const-eval/erroneous-const2.stderr

This file was deleted.

11 changes: 0 additions & 11 deletions tests/ui/consts/const-eval/unused-broken-const-late.stderr

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0080]: evaluation of `Fail::<i32>::C` failed
--> $DIR/dead-code-in-called-fn.rs:9:19
|
LL | const C: () = panic!();
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-called-fn.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: the above error was encountered while instantiating `fn called::<i32>`
--> $DIR/dead-code-in-called-fn.rs:23:5
|
LL | called::<i32>();
| ^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.
17 changes: 17 additions & 0 deletions tests/ui/consts/required-consts/dead-code-in-called-fn.opt.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0080]: evaluation of `Fail::<i32>::C` failed
--> $DIR/dead-code-in-called-fn.rs:9:19
|
LL | const C: () = panic!();
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/dead-code-in-called-fn.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: the above error was encountered while instantiating `fn called::<i32>`
--> $DIR/dead-code-in-called-fn.rs:23:5
|
LL | called::<i32>();
| ^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.
Loading