diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index cfaa43f397095..3726115357436 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -149,7 +149,12 @@ To learn more about a subcommand, run `./x.py -h`", "N", ); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); - opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS"); + let j_msg = format!( + "number of jobs to run in parallel; \ + defaults to {} (this host's logical CPU count)", + num_cpus::get() + ); + opts.optopt("j", "jobs", &j_msg, "JOBS"); opts.optflag("h", "help", "print this help message"); opts.optopt( "", diff --git a/src/libcore/convert/mod.rs b/src/libcore/convert/mod.rs index eef9ee7cb0093..8ff1ced53b071 100644 --- a/src/libcore/convert/mod.rs +++ b/src/libcore/convert/mod.rs @@ -374,6 +374,7 @@ pub trait Into: Sized { /// [`Into`]: trait.Into.html /// [`from`]: trait.From.html#tymethod.from /// [book]: ../../book/ch09-00-error-handling.html +#[rustc_diagnostic_item = "from_trait"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented(on( all(_Self = "&str", T = "std::string::String"), diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 85076a573b528..3806d3ae25487 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1941,6 +1941,13 @@ extern "rust-intrinsic" { /// /// Perma-unstable: do not use. pub fn miri_start_panic(payload: *mut u8) -> !; + + /// Internal placeholder for injecting code coverage counters when the "instrument-coverage" + /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code + /// generation. + #[cfg(not(bootstrap))] + #[lang = "count_code_region"] + pub fn count_code_region(_index: u32); } // Some functions are defined here because they accidentally got made diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index d1f5cb44913db..8bce980cadd1e 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -129,7 +129,7 @@ pub use crate::intrinsics::transmute; /// erring on the side of (double-)dropping. /// /// Also, `ManuallyDrop` prevents us from having to "touch" `v` after transferring the -/// ownership to `s` - the final step of interacting with `v` to dispoe of it without +/// ownership to `s` — the final step of interacting with `v` to dispose of it without /// running its destructor is entirely avoided. /// /// [drop]: fn.drop.html diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index 1ffd493d8130b..c7009b76e8148 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -39,8 +39,7 @@ pub struct PanicInfo<'a> { impl<'a> PanicInfo<'a> { #[unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] @@ -55,8 +54,7 @@ impl<'a> PanicInfo<'a> { #[unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] @@ -244,8 +242,7 @@ impl<'a> Location<'a> { impl<'a> Location<'a> { #![unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 3ed5e65e11c62..766c69a5f9420 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -22,8 +22,7 @@ #![allow(dead_code, missing_docs)] #![unstable( feature = "core_panic", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index c2a816f0a7d90..4bc44e98fc802 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs @@ -241,3 +241,52 @@ fn iterator_drops() { } assert_eq!(i.get(), 5); } + +// This test does not work on targets without panic=unwind support. +// To work around this problem, test is marked is should_panic, so it will +// be automagically skipped on unsuitable targets, such as +// wasm32-unknown-unkown. +// +// It means that we use panic for indicating success. +#[test] +#[should_panic(expected = "test succeeded")] +fn array_default_impl_avoids_leaks_on_panic() { + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + static COUNTER: AtomicUsize = AtomicUsize::new(0); + #[derive(Debug)] + struct Bomb(usize); + + impl Default for Bomb { + fn default() -> Bomb { + if COUNTER.load(Relaxed) == 3 { + panic!("bomb limit exceeded"); + } + + COUNTER.fetch_add(1, Relaxed); + Bomb(COUNTER.load(Relaxed)) + } + } + + impl Drop for Bomb { + fn drop(&mut self) { + COUNTER.fetch_sub(1, Relaxed); + } + } + + let res = std::panic::catch_unwind(|| <[Bomb; 5]>::default()); + let panic_msg = match res { + Ok(_) => unreachable!(), + Err(p) => p.downcast::<&'static str>().unwrap(), + }; + assert_eq!(*panic_msg, "bomb limit exceeded"); + // check that all bombs are successfully dropped + assert_eq!(COUNTER.load(Relaxed), 0); + panic!("test succeeded") +} + +#[test] +fn empty_array_is_always_default() { + struct DoesNotImplDefault; + + let _arr = <[DoesNotImplDefault; 0]>::default(); +} diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index f5ae9824df894..ba285b5ef38d1 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -997,6 +997,33 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); } + fn instrprof_increment( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + num_counters: &'ll Value, + index: &'ll Value, + ) -> &'ll Value { + debug!( + "instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})", + fn_name, hash, num_counters, index + ); + + let llfn = unsafe { llvm::LLVMRustGetInstrprofIncrementIntrinsic(self.cx().llmod) }; + let args = &[fn_name, hash, num_counters, index]; + let args = self.check_call("call", llfn, args); + + unsafe { + llvm::LLVMRustBuildCall( + self.llbuilder, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + None, + ) + } + } + fn call( &mut self, llfn: &'ll Value, diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 4c810a37d4180..7ff5ac5cbdc10 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -749,6 +749,8 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void); ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void); + ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void); + ifn!("llvm.expect.i1", fn(i1, i1) -> i1); ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); ifn!("llvm.localescape", fn(...) -> void); diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 1e6d2e3dbb74e..95465939070a0 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -7,6 +7,8 @@ use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; use crate::value::Value; +use log::debug; + use rustc_ast::ast; use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh}; use rustc_codegen_ssa::common::span_invalid_monomorphization_error; @@ -21,6 +23,7 @@ use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; use rustc_middle::ty::{self, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use rustc_span::Symbol; use rustc_target::abi::{self, HasDataLayout, LayoutOf, Primitive}; use rustc_target::spec::PanicStrategy; @@ -86,6 +89,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { args: &[OperandRef<'tcx, &'ll Value>], llresult: &'ll Value, span: Span, + caller_instance: ty::Instance<'tcx>, ) { let tcx = self.tcx; let callee_ty = instance.monomorphic_ty(tcx); @@ -136,6 +140,28 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { let llfn = self.get_intrinsic(&("llvm.debugtrap")); self.call(llfn, &[], None) } + "count_code_region" => { + if let ty::InstanceDef::Item(fn_def_id) = caller_instance.def { + let caller_fn_path = tcx.def_path_str(fn_def_id); + debug!( + "count_code_region to llvm.instrprof.increment(fn_name={})", + caller_fn_path + ); + + // FIXME(richkadel): (1) Replace raw function name with mangled function name; + // (2) Replace hardcoded `1234` in `hash` with a computed hash (as discussed in) + // the MCP (compiler-team/issues/278); and replace the hardcoded `1` for + // `num_counters` with the actual number of counters per function (when the + // changes are made to inject more than one counter per function). + let (fn_name, _len_val) = self.const_str(Symbol::intern(&caller_fn_path)); + let index = args[0].immediate(); + let hash = self.const_u64(1234); + let num_counters = self.const_u32(1); + self.instrprof_increment(fn_name, hash, num_counters, index) + } else { + bug!("intrinsic count_code_region: no src.instance"); + } + } "va_start" => self.va_start(args[0].immediate()), "va_end" => self.va_end(args[0].immediate()), "va_copy" => { diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 54cf99e1c6d6c..372fb17573a4b 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -1360,6 +1360,7 @@ extern "C" { // Miscellaneous instructions pub fn LLVMBuildPhi(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; + pub fn LLVMRustGetInstrprofIncrementIntrinsic(M: &Module) -> &'a Value; pub fn LLVMRustBuildCall( B: &Builder<'a>, Fn: &'a Value, diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index c118e5ebdb72d..49054765b9dae 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -175,6 +175,12 @@ impl ModuleConfig { if sess.opts.debugging_opts.profile && !is_compiler_builtins { passes.push("insert-gcov-profiling".to_owned()); } + + // The rustc option `-Zinstrument_coverage` injects intrinsic calls to + // `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass. + if sess.opts.debugging_opts.instrument_coverage { + passes.push("instrprof".to_owned()); + } passes }, vec![] diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index ef59ad486eefe..665ef77090987 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -693,6 +693,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &args, dest, terminator.source_info.span, + self.instance, ); if let ReturnDest::IndirectOperand(dst, _) = ret_dest { diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index caba7ebef593b..7ffc9f15bffdc 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs @@ -260,6 +260,14 @@ pub trait BuilderMethods<'a, 'tcx>: /// Called for `StorageDead` fn lifetime_end(&mut self, ptr: Self::Value, size: Size); + fn instrprof_increment( + &mut self, + fn_name: Self::Value, + hash: Self::Value, + num_counters: Self::Value, + index: Self::Value, + ) -> Self::Value; + fn call( &mut self, llfn: Self::Value, diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs index 9d48e233de655..f62019498511c 100644 --- a/src/librustc_codegen_ssa/traits/intrinsic.rs +++ b/src/librustc_codegen_ssa/traits/intrinsic.rs @@ -15,6 +15,7 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes { args: &[OperandRef<'tcx, Self::Value>], llresult: Self::Value, span: Span, + caller_instance: ty::Instance<'tcx>, ); fn abort(&mut self); diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 83bada4041963..091ded6d74d0f 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -242,6 +242,8 @@ language_item_table! { StartFnLangItem, "start", start_fn, Target::Fn; + CountCodeRegionFnLangItem, "count_code_region", count_code_region_fn, Target::Fn; + EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn; EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static; diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 87647f3b0b017..c2a7d1a4a6102 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -548,6 +548,7 @@ fn test_debugging_options_tracking_hash() { tracked!(human_readable_cgu_names, true); tracked!(inline_in_all_cgus, Some(true)); tracked!(insert_sideeffect, true); + tracked!(instrument_coverage, true); tracked!(instrument_mcount, true); tracked!(link_only, true); tracked!(merge_functions, Some(MergeFunctions::Disabled)); diff --git a/src/librustc_middle/macros.rs b/src/librustc_middle/macros.rs index 88ddd96eec8f5..a5482b7bdcfeb 100644 --- a/src/librustc_middle/macros.rs +++ b/src/librustc_middle/macros.rs @@ -1,16 +1,20 @@ #[macro_export] macro_rules! bug { - () => ( bug!("impossible case reached") ); - ($($message:tt)*) => ({ - $crate::util::bug::bug_fmt(file!(), line!(), format_args!($($message)*)) - }) + () => ( $crate::bug!("impossible case reached") ); + ($msg:expr) => ({ $crate::util::bug::bug_fmt(::std::format_args!($msg)) }); + ($msg:expr,) => ({ $crate::bug!($msg) }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::util::bug::bug_fmt(::std::format_args!($fmt, $($arg)+)) + }); } #[macro_export] macro_rules! span_bug { - ($span:expr, $($message:tt)*) => ({ - $crate::util::bug::span_bug_fmt(file!(), line!(), $span, format_args!($($message)*)) - }) + ($span:expr, $msg:expr) => ({ $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) }); + ($span:expr, $msg:expr,) => ({ $crate::span_bug!($span, $msg) }); + ($span:expr, $fmt:expr, $($arg:tt)+) => ({ + $crate::util::bug::span_bug_fmt($span, ::std::format_args!($fmt, $($arg)+)) + }); } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs index 27848684706d6..11ae2cf72c462 100644 --- a/src/librustc_middle/mir/mod.rs +++ b/src/librustc_middle/mir/mod.rs @@ -29,6 +29,7 @@ use rustc_macros::HashStable; use rustc_serialize::{Decodable, Encodable}; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi; use rustc_target::asm::InlineAsmRegOrRegClass; use std::borrow::Cow; use std::fmt::{self, Debug, Display, Formatter, Write}; @@ -2218,6 +2219,33 @@ impl<'tcx> Operand<'tcx> { }) } + /// Convenience helper to make a literal-like constant from a given scalar value. + /// Since this is used to synthesize MIR, assumes `user_ty` is None. + pub fn const_from_scalar( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + val: Scalar, + span: Span, + ) -> Operand<'tcx> { + debug_assert!({ + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let type_size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + let scalar_size = abi::Size::from_bytes(match val { + Scalar::Raw { size, .. } => size, + _ => panic!("Invalid scalar type {:?}", val), + }); + scalar_size == type_size + }); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::from_scalar(tcx, val, ty), + }) + } + pub fn to_copy(&self) -> Self { match *self { Operand::Copy(_) | Operand::Constant(_) => self.clone(), diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs index d5be3508d2d80..de2e1046f1cbb 100644 --- a/src/librustc_middle/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -189,8 +189,7 @@ fn validate_hir_id_for_typeck_tables( if hir_id.owner != hir_owner { ty::tls::with(|tcx| { bug!( - "node {} with HirId::owner {:?} cannot be placed in \ - TypeckTables with hir_owner {:?}", + "node {} with HirId::owner {:?} cannot be placed in TypeckTables with hir_owner {:?}", tcx.hir().node_to_string(hir_id), hir_id.owner, hir_owner diff --git a/src/librustc_middle/util/bug.rs b/src/librustc_middle/util/bug.rs index 54cd8a29f9474..9c3a97d8332f1 100644 --- a/src/librustc_middle/util/bug.rs +++ b/src/librustc_middle/util/bug.rs @@ -3,34 +3,31 @@ use crate::ty::{tls, TyCtxt}; use rustc_span::{MultiSpan, Span}; use std::fmt; +use std::panic::Location; #[cold] #[inline(never)] -pub fn bug_fmt(file: &'static str, line: u32, args: fmt::Arguments<'_>) -> ! { +#[track_caller] +pub fn bug_fmt(args: fmt::Arguments<'_>) -> ! { // this wrapper mostly exists so I don't have to write a fully // qualified path of None:: inside the bug!() macro definition - opt_span_bug_fmt(file, line, None::, args); + opt_span_bug_fmt(None::, args, Location::caller()); } #[cold] #[inline(never)] -pub fn span_bug_fmt>( - file: &'static str, - line: u32, - span: S, - args: fmt::Arguments<'_>, -) -> ! { - opt_span_bug_fmt(file, line, Some(span), args); +#[track_caller] +pub fn span_bug_fmt>(span: S, args: fmt::Arguments<'_>) -> ! { + opt_span_bug_fmt(Some(span), args, Location::caller()); } fn opt_span_bug_fmt>( - file: &'static str, - line: u32, span: Option, args: fmt::Arguments<'_>, + location: &Location<'_>, ) -> ! { tls::with_opt(move |tcx| { - let msg = format!("{}:{}: {}", file, line, args); + let msg = format!("{}: {}", location, args); match (tcx, span) { (Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, &msg), (Some(tcx), None) => tcx.sess.diagnostic().bug(&msg), diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 47e5b8b4fcec4..ac28ccd181520 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -389,6 +389,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); self.copy_op(self.operand_index(args[0], index)?, dest)?; } + // FIXME(#73156): Handle source code coverage in const eval + sym::count_code_region => (), _ => return Ok(false), } diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs new file mode 100644 index 0000000000000..c36614938e10f --- /dev/null +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -0,0 +1,92 @@ +use crate::transform::{MirPass, MirSource}; +use crate::util::patch::MirPatch; +use rustc_hir::lang_items; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::*; +use rustc_middle::ty; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with +/// the intrinsic llvm.instrprof.increment. +pub struct InstrumentCoverage; + +impl<'tcx> MirPass<'tcx> for InstrumentCoverage { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.opts.debugging_opts.instrument_coverage { + debug!("instrumenting {:?}", src.def_id()); + instrument_coverage(tcx, body); + } + } +} + +// The first counter (start of the function) is index zero. +const INIT_FUNCTION_COUNTER: u32 = 0; + +/// Injects calls to placeholder function `count_code_region()`. +// FIXME(richkadel): As a first step, counters are only injected at the top of each function. +// The complete solution will inject counters at each conditional code branch. +pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let span = body.span.shrink_to_lo(); + + let count_code_region_fn = function_handle( + tcx, + tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None), + span, + ); + let counter_index = Operand::const_from_scalar( + tcx, + tcx.types.u32, + Scalar::from_u32(INIT_FUNCTION_COUNTER), + span, + ); + + let mut patch = MirPatch::new(body); + + let new_block = patch.new_block(placeholder_block(SourceInfo::outermost(body.span))); + let next_block = START_BLOCK; + + let temp = patch.new_temp(tcx.mk_unit(), body.span); + patch.patch_terminator( + new_block, + TerminatorKind::Call { + func: count_code_region_fn, + args: vec![counter_index], + // new_block will swapped with the next_block, after applying patch + destination: Some((Place::from(temp), new_block)), + cleanup: None, + from_hir_call: false, + fn_span: span, + }, + ); + + patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp)); + patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp)); + + patch.apply(body); + + // To insert the `new_block` in front of the first block in the counted branch (for example, + // the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the + // graph unchanged. + body.basic_blocks_mut().swap(next_block, new_block); +} + +fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> { + let ret_ty = tcx.fn_sig(fn_def_id).output(); + let ret_ty = ret_ty.no_bound_vars().unwrap(); + let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); + Operand::function_handle(tcx, fn_def_id, substs, span) +} + +fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> { + BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info, + // this gets overwritten by the counter Call + kind: TerminatorKind::Unreachable, + }), + is_cleanup: false, + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 4240b528a6124..846ed1f86d8d6 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -28,6 +28,7 @@ pub mod elaborate_drops; pub mod generator; pub mod inline; pub mod instcombine; +pub mod instrument_coverage; pub mod no_landing_pads; pub mod nrvo; pub mod promote_consts; @@ -288,6 +289,10 @@ fn mir_validated( // What we need to run borrowck etc. &promote_pass, &simplify::SimplifyCfg::new("qualify-consts"), + // If the `instrument-coverage` option is enabled, analyze the CFG, identify each + // conditional branch, construct a coverage map to be passed to LLVM, and inject counters + // where needed. + &instrument_coverage::InstrumentCoverage, ]], ); diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index 8792605c08d38..fc9ffc3092447 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -961,7 +961,7 @@ impl<'a> Parser<'a> { self.bump(); let sp = self.prev_token.span; self.struct_span_err(sp, &msg) - .span_suggestion(sp, "change this to `;`", ";".to_string(), appl) + .span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl) .emit(); return Ok(()); } else if self.look_ahead(0, |t| { diff --git a/src/librustc_passes/weak_lang_items.rs b/src/librustc_passes/weak_lang_items.rs index 96ec23692df51..f2f07b5d4fb26 100644 --- a/src/librustc_passes/weak_lang_items.rs +++ b/src/librustc_passes/weak_lang_items.rs @@ -5,10 +5,12 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::lang_items; +use rustc_hir::lang_items::ITEM_REFS; use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS; use rustc_middle::middle::lang_items::whitelisted; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; +use rustc_span::symbol::sym; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -70,11 +72,21 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { } impl<'a, 'tcx> Context<'a, 'tcx> { - fn register(&mut self, name: Symbol, span: Span) { + fn register(&mut self, name: Symbol, span: Span, hir_id: hir::HirId) { if let Some(&item) = WEAK_ITEMS_REFS.get(&name) { if self.items.require(item).is_err() { self.items.missing.push(item); } + } else if name == sym::count_code_region { + // `core::intrinsics::code_count_region()` is (currently) the only `extern` lang item + // that is never actually linked. It is not a `weak_lang_item` that can be registered + // when used, and should be registered here instead. + if let Some((item_index, _)) = ITEM_REFS.get(&*name.as_str()).cloned() { + if self.items.items[item_index].is_none() { + let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id(); + self.items.items[item_index] = Some(item_def_id); + } + } } else { struct_span_err!(self.tcx.sess, span, E0264, "unknown external lang item: `{}`", name) .emit(); @@ -91,7 +103,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) { if let Some((lang_item, _)) = hir::lang_items::extract(&i.attrs) { - self.register(lang_item, i.span); + self.register(lang_item, i.span, i.hir_id); } intravisit::walk_foreign_item(self, i) } diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index d22c6ec9d7d01..2d231359057fd 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -876,6 +876,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "fix undefined behavior when a thread doesn't eventually make progress \ (such as entering an empty infinite loop) by inserting llvm.sideeffect \ (default: no)"), + instrument_coverage: bool = (false, parse_bool, [TRACKED], + "instrument the generated code with LLVM code region counters to \ + (in the future) generate coverage reports (experimental; default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index fdeb58b7b7a31..970a26325926c 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -240,6 +240,7 @@ symbols! { copy_closures, core, core_intrinsics, + count_code_region, crate_id, crate_in_paths, crate_local, @@ -345,6 +346,7 @@ symbols! { from_method, from_ok, from_usize, + from_trait, fundamental, future, Future, diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 46d6706cbf429..bad009f4039af 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -43,6 +43,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; use rustc_session::lint; use rustc_session::Session; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; @@ -333,10 +334,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { "only `u8` can be cast as `char`, not `{}`", self.expr_ty ) + .span_label(self.span, "invalid cast") .emit(); } CastError::NonScalar => { - type_error_struct!( + let mut err = type_error_struct!( fcx.tcx.sess, self.span, self.expr_ty, @@ -344,12 +346,75 @@ impl<'a, 'tcx> CastCheck<'tcx> { "non-primitive cast: `{}` as `{}`", self.expr_ty, fcx.ty_to_string(self.cast_ty) - ) - .note( - "an `as` expression can only be used to convert between \ - primitive types. Consider using the `From` trait", - ) - .emit(); + ); + let mut sugg = None; + if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind { + if fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg = Some(format!("&{}", mutbl.prefix_str())); + } + } + if let Some(sugg) = sugg { + err.span_label(self.span, "invalid cast"); + err.span_suggestion_verbose( + self.expr.span.shrink_to_lo(), + "borrow the value for the cast to be valid", + sugg, + Applicability::MachineApplicable, + ); + } else if !matches!( + self.cast_ty.kind, + ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) + ) { + let mut label = true; + // Check `impl From for self.cast_ty {}` for accurate suggestion: + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) { + let ty = fcx.resolve_vars_if_possible(&self.cast_ty); + // Erase regions to avoid panic in `prove_value` when calling + // `type_implements_trait`. + let ty = fcx.tcx.erase_regions(&ty); + let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty); + let expr_ty = fcx.tcx.erase_regions(&expr_ty); + let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); + // Check for infer types because cases like `Option<{integer}>` would + // panic otherwise. + if !expr_ty.has_infer_types() + && fcx.tcx.type_implements_trait(( + from_trait, + ty, + ty_params, + fcx.param_env, + )) + { + label = false; + err.span_suggestion( + self.span, + "consider using the `From` trait instead", + format!("{}::from({})", self.cast_ty, snippet), + Applicability::MaybeIncorrect, + ); + } + } + } + let msg = "an `as` expression can only be used to convert between primitive \ + types or to coerce to a specific trait object"; + if label { + err.span_label(self.span, msg); + } else { + err.note(msg); + } + } else { + err.span_label(self.span, "invalid cast"); + } + err.emit(); } CastError::SizedUnsizedCast => { use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; @@ -370,21 +435,22 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; let mut err = struct_span_err!( fcx.tcx.sess, - self.span, + if unknown_cast_to { self.cast_span } else { self.span }, E0641, "cannot cast {} a pointer of an unknown kind", if unknown_cast_to { "to" } else { "from" } ); - err.note( - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); if unknown_cast_to { - err.span_suggestion_short( - self.cast_span, - "consider giving more type information", - String::new(), - Applicability::Unspecified, + err.span_label(self.cast_span, "needs more type information"); + err.note( + "the type information given here is insufficient to check whether \ + the pointer cast is valid", + ); + } else { + err.span_label( + self.span, + "the type information given here is insufficient to check whether \ + the pointer cast is valid", ); } err.emit(); @@ -438,13 +504,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { Ok(s) => { err.span_suggestion( self.cast_span, - "try casting to a `Box` instead", + "you can cast to a `Box` instead", format!("Box<{}>", s), Applicability::MachineApplicable, ); } Err(_) => { - err.span_help(self.cast_span, &format!("did you mean `Box<{}>`?", tstr)); + err.span_help( + self.cast_span, + &format!("you might have meant `Box<{}>`", tstr), + ); } } } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index bded2c695c9db..3ec6973a17d56 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -347,6 +347,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { return; } + "count_code_region" => (0, vec![tcx.types.u32], tcx.mk_unit()), + ref other => { struct_span_err!( tcx.sess, diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 6a120f930ab15..d22ac1d538584 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -201,8 +201,7 @@ fn default_hook(info: &PanicInfo<'_>) { if FIRST_PANIC.swap(false, Ordering::SeqCst) { let _ = writeln!( err, - "note: run with `RUST_BACKTRACE=1` \ - environment variable to display a backtrace" + "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace" ); } } @@ -454,10 +453,7 @@ fn rust_panic_with_hook( // process real quickly as we don't want to try calling it again as it'll // probably just panic again. if panics > 2 { - util::dumb_print(format_args!( - "thread panicked while processing \ - panic. aborting.\n" - )); + util::dumb_print(format_args!("thread panicked while processing panic. aborting.\n")); intrinsics::abort() } @@ -489,10 +485,7 @@ fn rust_panic_with_hook( // have limited options. Currently our preference is to // just abort. In the future we may consider resuming // unwinding or otherwise exiting the thread cleanly. - util::dumb_print(format_args!( - "thread panicked while panicking. \ - aborting.\n" - )); + util::dumb_print(format_args!("thread panicked while panicking. aborting.\n")); intrinsics::abort() } diff --git a/src/libstd/time.rs b/src/libstd/time.rs index bc3bfde6d7559..295ebcbb72939 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -60,6 +60,21 @@ pub use core::time::Duration; /// } /// ``` /// +/// # OS-specific behaviors +/// +/// An `Instant` is a wrapper around system-specific types and it may behave +/// differently depending on the underlying operating system. For example, +/// the following snippet is fine on Linux but panics on macOS: +/// +/// ```no_run +/// use std::time::{Instant, Duration}; +/// +/// let now = Instant::now(); +/// let max_nanoseconds = u64::MAX / 1_000_000_000; +/// let duration = Duration::new(max_nanoseconds, 0); +/// println!("{:?}", now + duration); +/// ``` +/// /// # Underlying System calls /// Currently, the following system calls are being used to get the current time using `now()`: /// diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 4704622922af0..cdb3a157eab97 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -5,6 +5,7 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Bitcode/BitcodeWriterPass.h" @@ -1364,6 +1365,11 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles)); } +extern "C" LLVMValueRef LLVMRustGetInstrprofIncrementIntrinsic(LLVMModuleRef M) { + return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), + (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); +} + extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, diff --git a/src/test/mir-opt/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs new file mode 100644 index 0000000000000..e8c723b528a1a --- /dev/null +++ b/src/test/mir-opt/instrument_coverage.rs @@ -0,0 +1,19 @@ +// Test that the initial version of Rust coverage injects count_code_region() placeholder calls, +// at the top of each function. The placeholders are later converted into LLVM instrprof.increment +// intrinsics, during codegen. + +// compile-flags: -Zinstrument-coverage +// EMIT_MIR rustc.main.InstrumentCoverage.diff +// EMIT_MIR rustc.bar.InstrumentCoverage.diff +fn main() { + loop { + if bar() { + break; + } + } +} + +#[inline(never)] +fn bar() -> bool { + true +} diff --git a/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff new file mode 100644 index 0000000000000..d23bb93d951dc --- /dev/null +++ b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff @@ -0,0 +1,41 @@ +- // MIR for `bar` before InstrumentCoverage ++ // MIR for `bar` after InstrumentCoverage + + fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:17:13: 17:17 ++ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 + + bb0: { ++ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ _1 = const std::intrinsics::count_code_region(const 0u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ // ty::Const ++ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:17:1: 17:1 ++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:17:1: 17:1 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } ++ } ++ ++ bb1 (cleanup): { ++ resume; // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ } ++ ++ bb2: { ++ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:18:5: 18:9 + _0 = const true; // scope 0 at $DIR/instrument_coverage.rs:18:5: 18:9 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:18:5: 18:9 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + return; // scope 0 at $DIR/instrument_coverage.rs:19:2: 19:2 + } + } + diff --git a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff new file mode 100644 index 0000000000000..095246580409e --- /dev/null +++ b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:8:11: 8:11 + let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:10:18: 12:10 ++ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + + bb0: { +- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 ++ _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 ++ // ty::Const ++ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:8:1: 8:1 ++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:8:1: 8:1 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + } + + bb1: { + StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + // ty::Const + // + ty: fn() -> bool {bar} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:10:12: 10:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + } + + bb3: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + } + + bb4: { + falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + } + + bb5: { + _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:10:9: 12:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 + goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + } + + bb6: { + _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:13: 11:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:11:13: 11:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 + return; // scope 0 at $DIR/instrument_coverage.rs:14:2: 14:2 ++ } ++ ++ bb7: { ++ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + } + } + diff --git a/src/test/ui/cast/cast-from-nil.stderr b/src/test/ui/cast/cast-from-nil.stderr index c8e3628a7ded8..dab133cfb4b67 100644 --- a/src/test/ui/cast/cast-from-nil.stderr +++ b/src/test/ui/cast/cast-from-nil.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `u32` --> $DIR/cast-from-nil.rs:2:21 | LL | fn main() { let u = (assert!(true) as u32); } - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/cast/cast-to-bare-fn.stderr b/src/test/ui/cast/cast-to-bare-fn.stderr index 84933dca929a4..d97b0c5f8aadc 100644 --- a/src/test/ui/cast/cast-to-bare-fn.stderr +++ b/src/test/ui/cast/cast-to-bare-fn.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `fn(isize) {foo}` as `extern "C" fn() -> isize --> $DIR/cast-to-bare-fn.rs:5:13 | LL | let x = foo as extern "C" fn() -> isize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)` --> $DIR/cast-to-bare-fn.rs:7:13 | LL | let y = v as extern "Rust" fn(isize) -> (isize, isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to 2 previous errors diff --git a/src/test/ui/cast/cast-to-nil.stderr b/src/test/ui/cast/cast-to-nil.stderr index 478f6b69dafc8..29a9baffd71d7 100644 --- a/src/test/ui/cast/cast-to-nil.stderr +++ b/src/test/ui/cast/cast-to-nil.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `u32` as `()` --> $DIR/cast-to-nil.rs:2:21 | LL | fn main() { let u = 0u32 as (); } - | ^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr index ffa02533d8b66..9b86f8d4def86 100644 --- a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr +++ b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr @@ -12,7 +12,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box<{integer}>` as `dyn std::ma LL | Box::new(1) as dyn Send; | ^^^^^^^^^^^^^^^-------- | | - | help: try casting to a `Box` instead: `Box` + | help: you can cast to a `Box` instead: `Box` error: aborting due to 2 previous errors diff --git a/src/test/ui/closures/closure-no-fn-3.stderr b/src/test/ui/closures/closure-no-fn-3.stderr index ab6056b65473e..4b3b4be798fc1 100644 --- a/src/test/ui/closures/closure-no-fn-3.stderr +++ b/src/test/ui/closures/closure-no-fn-3.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:27: 6:37 b --> $DIR/closure-no-fn-3.rs:6:27 | LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to previous error diff --git a/src/test/ui/coercion/coerce-to-bang-cast.stderr b/src/test/ui/coercion/coerce-to-bang-cast.stderr index ff30ebc09c63a..d3adbd5158dbb 100644 --- a/src/test/ui/coercion/coerce-to-bang-cast.stderr +++ b/src/test/ui/coercion/coerce-to-bang-cast.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `i32` as `!` --> $DIR/coerce-to-bang-cast.rs:6:13 | LL | let y = {return; 22} as !; - | ^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `i32` as `!` --> $DIR/coerce-to-bang-cast.rs:11:13 | LL | let y = 22 as !; - | ^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr index 5b2c4116c4b1d..e4d256c0ad192 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr @@ -16,7 +16,7 @@ error[E0604]: only `u8` can be cast as `char`, not `i8` --> $DIR/const-eval-overflow-4b.rs:25:13 | LL | : [u32; 5i8 as char as usize] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ invalid cast error: aborting due to 3 previous errors diff --git a/src/test/ui/error-codes/E0604.stderr b/src/test/ui/error-codes/E0604.stderr index 5861bdcb7a953..18835310bd5e8 100644 --- a/src/test/ui/error-codes/E0604.stderr +++ b/src/test/ui/error-codes/E0604.stderr @@ -2,7 +2,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/E0604.rs:2:5 | LL | 1u32 as char; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ invalid cast error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0605.stderr b/src/test/ui/error-codes/E0605.stderr index 95e899db8b7e9..f23d2008e0b5f 100644 --- a/src/test/ui/error-codes/E0605.stderr +++ b/src/test/ui/error-codes/E0605.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` --> $DIR/E0605.rs:3:5 | LL | x as Vec; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/E0605.rs:6:5 | LL | v as &u8; - | ^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/error-festival.stderr b/src/test/ui/error-festival.stderr index 7f524230ef006..905195d4ad963 100644 --- a/src/test/ui/error-festival.stderr +++ b/src/test/ui/error-festival.stderr @@ -42,15 +42,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/error-festival.rs:25:5 | LL | 0u32 as char; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` --> $DIR/error-festival.rs:29:5 | LL | x as Vec; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0054]: cannot cast as `bool` --> $DIR/error-festival.rs:33:24 diff --git a/src/test/ui/fat-ptr-cast.stderr b/src/test/ui/fat-ptr-cast.stderr index 93e1471838f72..56d5a26beb04e 100644 --- a/src/test/ui/fat-ptr-cast.stderr +++ b/src/test/ui/fat-ptr-cast.stderr @@ -34,9 +34,7 @@ error[E0605]: non-primitive cast: `std::boxed::Box<[i32]>` as `usize` --> $DIR/fat-ptr-cast.rs:14:5 | LL | b as usize; - | ^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const [i32]` as `usize` is invalid --> $DIR/fat-ptr-cast.rs:15:5 diff --git a/src/test/ui/issues/issue-10991.stderr b/src/test/ui/issues/issue-10991.stderr index f12539b47cf44..5b8a182338693 100644 --- a/src/test/ui/issues/issue-10991.stderr +++ b/src/test/ui/issues/issue-10991.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `usize` --> $DIR/issue-10991.rs:3:14 | LL | let _t = nil as usize; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/issues/issue-16048.rs b/src/test/ui/issues/issue-16048.rs index 7d24f3a40a742..eaf6acff26bf3 100644 --- a/src/test/ui/issues/issue-16048.rs +++ b/src/test/ui/issues/issue-16048.rs @@ -18,12 +18,12 @@ impl<'a> Test<'a> for Foo<'a> { } impl<'a> NoLifetime for Foo<'a> { - fn get<'p, T : Test<'a>>(&self) -> T { + fn get<'p, T: Test<'a> + From>>(&self) -> T { //~^ ERROR E0195 //~| NOTE lifetimes do not match method in trait return *self as T; //~^ ERROR non-primitive cast: `Foo<'a>` as `T` - //~| NOTE an `as` expression can only be used to convert between primitive types. + //~| NOTE an `as` expression can only be used to convert between primitive types } } diff --git a/src/test/ui/issues/issue-16048.stderr b/src/test/ui/issues/issue-16048.stderr index a137bcdf1915e..73610942d7a7e 100644 --- a/src/test/ui/issues/issue-16048.stderr +++ b/src/test/ui/issues/issue-16048.stderr @@ -4,16 +4,16 @@ error[E0195]: lifetime parameters or bounds on method `get` do not match the tra LL | fn get<'p, T : Test<'p>>(&self) -> T; | ------------------ lifetimes in impl do not match this method in trait ... -LL | fn get<'p, T : Test<'a>>(&self) -> T { - | ^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait +LL | fn get<'p, T: Test<'a> + From>>(&self) -> T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait error[E0605]: non-primitive cast: `Foo<'a>` as `T` --> $DIR/issue-16048.rs:24:16 | LL | return *self as T; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using the `From` trait instead: `T::from(*self)` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-17441.stderr b/src/test/ui/issues/issue-17441.stderr index 0ab035515a051..b63a3995d255d 100644 --- a/src/test/ui/issues/issue-17441.stderr +++ b/src/test/ui/issues/issue-17441.stderr @@ -16,7 +16,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box` as `dyn std::fmt::D LL | let _bar = Box::new(1_usize) as dyn std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^------------------- | | - | help: try casting to a `Box` instead: `Box` + | help: you can cast to a `Box` instead: `Box` error[E0620]: cast to unsized type: `usize` as `dyn std::fmt::Debug` --> $DIR/issue-17441.rs:8:16 diff --git a/src/test/ui/issues/issue-22289.stderr b/src/test/ui/issues/issue-22289.stderr index cc7ace30cabef..4c35deb1fbe4e 100644 --- a/src/test/ui/issues/issue-22289.stderr +++ b/src/test/ui/issues/issue-22289.stderr @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `i32` as `&(dyn std::any::Any + 'static)` --> $DIR/issue-22289.rs:2:5 | LL | 0 as &dyn std::any::Any; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait +help: borrow the value for the cast to be valid + | +LL | &0 as &dyn std::any::Any; + | ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-22312.rs b/src/test/ui/issues/issue-22312.rs index 250fec2588702..4e359b3412a71 100644 --- a/src/test/ui/issues/issue-22312.rs +++ b/src/test/ui/issues/issue-22312.rs @@ -1,6 +1,6 @@ use std::ops::Index; -pub trait Array2D: Index { +pub trait Array2D: Index + Sized { fn rows(&self) -> usize; fn columns(&self) -> usize; fn get<'a>(&'a self, y: usize, x: usize) -> Option<&'a >::Output> { diff --git a/src/test/ui/issues/issue-22312.stderr b/src/test/ui/issues/issue-22312.stderr index fc32fd376b75a..28564b074633b 100644 --- a/src/test/ui/issues/issue-22312.stderr +++ b/src/test/ui/issues/issue-22312.stderr @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `Self` as `&dyn std::ops::Index $DIR/issue-22312.rs:11:24 | LL | let indexer = &(*self as &dyn Index>::Output>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait +help: borrow the value for the cast to be valid + | +LL | let indexer = &(&*self as &dyn Index>::Output>); + | ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-2995.stderr b/src/test/ui/issues/issue-2995.stderr index c316780d5f6a5..9f5968399a37d 100644 --- a/src/test/ui/issues/issue-2995.stderr +++ b/src/test/ui/issues/issue-2995.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `*const isize` as `&isize` --> $DIR/issue-2995.rs:2:22 | LL | let _q: &isize = p as &isize; - | ^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/issues/issue-45730.stderr b/src/test/ui/issues/issue-45730.stderr index d4ddba52df14a..d00f3d91b49da 100644 --- a/src/test/ui/issues/issue-45730.stderr +++ b/src/test/ui/issues/issue-45730.stderr @@ -1,30 +1,24 @@ error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:3:23 + --> $DIR/issue-45730.rs:3:28 | LL | let x: *const _ = 0 as _; - | ^^^^^- - | | - | help: consider giving more type information + | ^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:5:23 + --> $DIR/issue-45730.rs:5:28 | LL | let x: *const _ = 0 as *const _; - | ^^^^^-------- - | | - | help: consider giving more type information + | ^^^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:8:13 + --> $DIR/issue-45730.rs:8:44 | LL | let x = 0 as *const i32 as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------ - | | - | help: consider giving more type information + | ^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index f94dfd100a6f4..95936de218b8f 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -24,41 +24,31 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/cast-rfc0401.rs:29:13 | LL | let _ = v as &u8; - | ^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `E` --> $DIR/cast-rfc0401.rs:30:13 | LL | let _ = v as E; - | ^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `fn()` --> $DIR/cast-rfc0401.rs:31:13 | LL | let _ = v as fn(); - | ^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `*const u8` as `(u32,)` --> $DIR/cast-rfc0401.rs:32:13 | LL | let _ = v as (u32,); - | ^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `std::option::Option<&*const u8>` as `*const u8` --> $DIR/cast-rfc0401.rs:33:13 | LL | let _ = Some(&v) as *const u8; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const u8` as `f32` is invalid --> $DIR/cast-rfc0401.rs:35:13 @@ -102,7 +92,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/cast-rfc0401.rs:41:13 | LL | let _ = 0x61u32 as char; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ invalid cast error[E0606]: casting `bool` as `f32` is invalid --> $DIR/cast-rfc0401.rs:43:13 diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr index 69a9d03e474ba..d39b0a3207763 100644 --- a/src/test/ui/mismatched_types/issue-26480.stderr +++ b/src/test/ui/mismatched_types/issue-26480.stderr @@ -17,12 +17,11 @@ error[E0605]: non-primitive cast: `{integer}` as `()` --> $DIR/issue-26480.rs:22:19 | LL | ($x:expr) => ($x as ()) - | ^^^^^^^^ + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object ... LL | cast!(2); | --------- in this macro invocation | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/nonscalar-cast.fixed b/src/test/ui/nonscalar-cast.fixed new file mode 100644 index 0000000000000..0a4b98469b2b6 --- /dev/null +++ b/src/test/ui/nonscalar-cast.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +#[derive(Debug)] +struct Foo { + x: isize +} + +impl From for isize { + fn from(val: Foo) -> isize { + val.x + } +} + +fn main() { + println!("{}", isize::from(Foo { x: 1 })); //~ non-primitive cast: `Foo` as `isize` [E0605] +} diff --git a/src/test/ui/nonscalar-cast.rs b/src/test/ui/nonscalar-cast.rs index 7e6f1fd038fb7..59fcf09666b24 100644 --- a/src/test/ui/nonscalar-cast.rs +++ b/src/test/ui/nonscalar-cast.rs @@ -1,8 +1,16 @@ +// run-rustfix + #[derive(Debug)] struct Foo { x: isize } +impl From for isize { + fn from(val: Foo) -> isize { + val.x + } +} + fn main() { println!("{}", Foo { x: 1 } as isize); //~ non-primitive cast: `Foo` as `isize` [E0605] } diff --git a/src/test/ui/nonscalar-cast.stderr b/src/test/ui/nonscalar-cast.stderr index 9338688b037ff..2a7037121876d 100644 --- a/src/test/ui/nonscalar-cast.stderr +++ b/src/test/ui/nonscalar-cast.stderr @@ -1,10 +1,10 @@ error[E0605]: non-primitive cast: `Foo` as `isize` - --> $DIR/nonscalar-cast.rs:7:20 + --> $DIR/nonscalar-cast.rs:15:20 | LL | println!("{}", Foo { x: 1 } as isize); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(Foo { x: 1 })` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/order-dependent-cast-inference.stderr b/src/test/ui/order-dependent-cast-inference.stderr index ad50b415869dd..9f4ac0fea36ef 100644 --- a/src/test/ui/order-dependent-cast-inference.stderr +++ b/src/test/ui/order-dependent-cast-inference.stderr @@ -1,10 +1,8 @@ error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/order-dependent-cast-inference.rs:5:17 + --> $DIR/order-dependent-cast-inference.rs:5:22 | LL | let mut y = 0 as *const _; - | ^^^^^-------- - | | - | help: consider giving more type information + | ^^^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid diff --git a/src/test/ui/tag-variant-cast-non-nullary.fixed b/src/test/ui/tag-variant-cast-non-nullary.fixed new file mode 100644 index 0000000000000..53e68c2ac6af6 --- /dev/null +++ b/src/test/ui/tag-variant-cast-non-nullary.fixed @@ -0,0 +1,20 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] +enum NonNullary { + Nullary, + Other(isize), +} + +impl From for isize { + fn from(val: NonNullary) -> isize { + match val { + NonNullary::Nullary => 0, + NonNullary::Other(i) => i, + } + } +} + +fn main() { + let v = NonNullary::Nullary; + let val = isize::from(v); //~ ERROR non-primitive cast: `NonNullary` as `isize` [E0605] +} diff --git a/src/test/ui/tag-variant-cast-non-nullary.rs b/src/test/ui/tag-variant-cast-non-nullary.rs index bb34e82cdca37..0d0c6188ad114 100644 --- a/src/test/ui/tag-variant-cast-non-nullary.rs +++ b/src/test/ui/tag-variant-cast-non-nullary.rs @@ -1,8 +1,19 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] enum NonNullary { Nullary, Other(isize), } +impl From for isize { + fn from(val: NonNullary) -> isize { + match val { + NonNullary::Nullary => 0, + NonNullary::Other(i) => i, + } + } +} + fn main() { let v = NonNullary::Nullary; let val = v as isize; //~ ERROR non-primitive cast: `NonNullary` as `isize` [E0605] diff --git a/src/test/ui/tag-variant-cast-non-nullary.stderr b/src/test/ui/tag-variant-cast-non-nullary.stderr index 87ec20f20d789..ae2f5a7aead55 100644 --- a/src/test/ui/tag-variant-cast-non-nullary.stderr +++ b/src/test/ui/tag-variant-cast-non-nullary.stderr @@ -1,10 +1,10 @@ error[E0605]: non-primitive cast: `NonNullary` as `isize` - --> $DIR/tag-variant-cast-non-nullary.rs:8:15 + --> $DIR/tag-variant-cast-non-nullary.rs:19:15 | LL | let val = v as isize; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(v)` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr index 4fbbf34752803..a6b7e35b488b1 100644 --- a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr +++ b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr @@ -16,9 +16,7 @@ error[E0605]: non-primitive cast: `impl std::fmt::Debug` as `&'static str` --> $DIR/never_reveal_concrete_type.rs:14:13 | LL | let _ = x as &'static str; - | ^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/uninhabited/uninhabited-enum-cast.stderr b/src/test/ui/uninhabited/uninhabited-enum-cast.stderr index a39af7832f8c9..a9f10dfec994a 100644 --- a/src/test/ui/uninhabited/uninhabited-enum-cast.stderr +++ b/src/test/ui/uninhabited/uninhabited-enum-cast.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `E` as `isize` --> $DIR/uninhabited-enum-cast.rs:4:20 | LL | println!("{}", (e as isize).to_string()); - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error