Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: monomorphize things from dead code, too #122258

Closed
wants to merge 11 commits into from
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
Loading