mentioned_items: record all callee and coerced closure types, whether they are FnDef/Closure or not
They may become FnDef during monomorphization!
This commit is contained in:
parent
f1ec494c32
commit
0d6a16ac4b
@ -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> {
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -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::<MentionedItem<'tcx>>::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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||
--> $DIR/collect-in-dead-closure.rs:23:33
|
||||
|
|
||||
LL | let _closure: fn() = || not_called::<T>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -0,0 +1,20 @@
|
||||
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||
--> $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`.
|
@ -0,0 +1,20 @@
|
||||
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||
--> $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`.
|
@ -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>(T);
|
||||
impl<T> Fail<T> {
|
||||
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
|
||||
}
|
||||
|
||||
fn not_called<T>() {
|
||||
if false {
|
||||
let _ = Fail::<T>::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::<i32>;
|
||||
}
|
||||
|
||||
fn reveal<T: Hideaway>() {
|
||||
if false {
|
||||
callit_not(T::C);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if false {
|
||||
reveal::<()>()
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||
--> $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`.
|
@ -0,0 +1,20 @@
|
||||
error[E0080]: evaluation of `Fail::<i32>::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::<T>::C;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||
--> $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`.
|
@ -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>(T);
|
||||
impl<T> Fail<T> {
|
||||
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
|
||||
}
|
||||
|
||||
fn not_called<T>() {
|
||||
if false {
|
||||
let _ = Fail::<T>::C;
|
||||
}
|
||||
}
|
||||
|
||||
fn callit_not(f: impl Fn()) {
|
||||
if false {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if false {
|
||||
callit_not(not_called::<i32>)
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
error[E0080]: evaluation of `m::Fail::<i32>::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::<T>::C;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
note: the above error was encountered while instantiating `fn m::not_called::<i32>`
|
||||
--> $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`.
|
@ -0,0 +1,20 @@
|
||||
error[E0080]: evaluation of `m::Fail::<i32>::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::<T>::C;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
note: the above error was encountered while instantiating `fn m::not_called::<i32>`
|
||||
--> $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`.
|
@ -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>(T);
|
||||
impl<T> Fail<T> {
|
||||
const C: () = panic!(); //~ERROR evaluation of `m::Fail::<i32>::C` failed
|
||||
}
|
||||
|
||||
pub type NotCalledFn = impl Fn();
|
||||
|
||||
fn not_called<T>() {
|
||||
if false {
|
||||
let _ = Fail::<T>::C;
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_not_called() -> NotCalledFn {
|
||||
not_called::<i32>
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ note: the above error was encountered while instantiating `fn not_called::<i32>`
|
||||
--> $DIR/collect-in-dead-fn.rs:25:9
|
||||
|
|
||||
LL | not_called::<T>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user