From 9f5a933f00e0aba526edac2cf1804b4360059e55 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 9 Jan 2023 09:31:34 +0100 Subject: [PATCH 01/12] Remove backwards compat for LLVM 12 coverage format --- .../src/coverageinfo/mapgen.rs | 45 +++++++------------ compiler/rustc_codegen_llvm/src/errors.rs | 4 -- .../locales/en-US/codegen_llvm.ftl | 3 -- 3 files changed, 17 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 393bf30e9f8..ca49c862324 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,6 +1,5 @@ use crate::common::CodegenCx; use crate::coverageinfo; -use crate::errors::InstrumentCoverageRequiresLLVM12; use crate::llvm; use llvm::coverageinfo::CounterMappingRegion; @@ -19,8 +18,8 @@ use std::ffi::CString; /// Generates and exports the Coverage Map. /// -/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions -/// 5 (LLVM 12, only) and 6 (zero-based encoded as 4 and 5, respectively), as defined at +/// Rust Coverage Map generation supports LLVM Coverage Mapping Format version +/// 6 (zero-based encoded as 5), as defined at /// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format). /// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`) /// bundled with Rust's fork of LLVM. @@ -33,13 +32,10 @@ use std::ffi::CString; pub fn finalize(cx: &CodegenCx<'_, '_>) { let tcx = cx.tcx; - // Ensure the installed version of LLVM supports at least Coverage Map - // Version 5 (encoded as a zero-based value: 4), which was introduced with - // LLVM 12. + // Ensure the installed version of LLVM supports Coverage Map Version 6 + // (encoded as a zero-based value: 5), which was introduced with LLVM 13. let version = coverageinfo::mapping_version(); - if version < 4 { - tcx.sess.emit_fatal(InstrumentCoverageRequiresLLVM12); - } + assert_eq!(version, 5, "The `CoverageMappingVersion` exposed by `llvm-wrapper` is out of sync"); debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); @@ -61,7 +57,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { return; } - let mut mapgen = CoverageMapGenerator::new(tcx, version); + let mut mapgen = CoverageMapGenerator::new(tcx); // Encode coverage mappings and generate function records let mut function_data = Vec::new(); @@ -124,25 +120,18 @@ struct CoverageMapGenerator { } impl CoverageMapGenerator { - fn new(tcx: TyCtxt<'_>, version: u32) -> Self { + fn new(tcx: TyCtxt<'_>) -> Self { let mut filenames = FxIndexSet::default(); - if version >= 5 { - // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5) - // requires setting the first filename to the compilation directory. - // Since rustc generates coverage maps with relative paths, the - // compilation directory can be combined with the relative paths - // to get absolute paths, if needed. - let working_dir = tcx - .sess - .opts - .working_dir - .remapped_path_if_available() - .to_string_lossy() - .to_string(); - let c_filename = - CString::new(working_dir).expect("null error converting filename to C string"); - filenames.insert(c_filename); - } + // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5) + // requires setting the first filename to the compilation directory. + // Since rustc generates coverage maps with relative paths, the + // compilation directory can be combined with the relative paths + // to get absolute paths, if needed. + let working_dir = + tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy().to_string(); + let c_filename = + CString::new(working_dir).expect("null error converting filename to C string"); + filenames.insert(c_filename); Self { filenames } } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index b4620997242..001d1ce93d8 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -39,10 +39,6 @@ pub(crate) struct ErrorCreatingImportLibrary<'a> { pub error: String, } -#[derive(Diagnostic)] -#[diag(codegen_llvm_instrument_coverage_requires_llvm_12)] -pub(crate) struct InstrumentCoverageRequiresLLVM12; - #[derive(Diagnostic)] #[diag(codegen_llvm_symbol_already_defined)] pub(crate) struct SymbolAlreadyDefined<'a> { diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl index 860212b051f..b82c903290b 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl @@ -11,9 +11,6 @@ codegen_llvm_unknown_ctarget_feature_prefix = codegen_llvm_error_creating_import_library = Error creating import library for {$lib_name}: {$error} -codegen_llvm_instrument_coverage_requires_llvm_12 = - rustc option `-C instrument-coverage` requires LLVM 12 or higher. - codegen_llvm_symbol_already_defined = symbol `{$symbol_name}` is already defined From 6155b9a772c944702f931ce5940d6c19cb7ced07 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Thu, 12 Jan 2023 18:47:05 +0000 Subject: [PATCH 02/12] Avoid __cxa_thread_atexit_impl on Emscripten - Fixes https://github.com/rust-lang/rust/issues/91628. - Fixes https://github.com/emscripten-core/emscripten/issues/15722. See discussion in both issues. The TL;DR is that weak linkage causes LLVM to produce broken Wasm, presumably due to pointer mismatch. The code is casting a void pointer to a function pointer with specific signature, but Wasm is very strict about function pointer compatibility, so the resulting code is invalid. Ideally LLVM should catch this earlier in the process rather than emit invalid Wasm, but it currently doesn't and this is an easy and valid fix, given that Emcripten doesn't have `__cxa_thread_atexit_impl` these days anyway. Unfortunately, I can't add a regression test as even after looking into this issue for a long time, I couldn't reproduce it with any minimal Rust example, only with extracted LLVM IR or on a large project involving Rust + C++. r? @alexcrichton --- library/std/src/sys/unix/thread_local_dtor.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index d7fd2130f7c..aa4df000632 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -11,13 +11,7 @@ // Note, however, that we run on lots older linuxes, as well as cross // compiling from a newer linux to an older linux, so we also have a // fallback implementation to use as well. -#[cfg(any( - target_os = "linux", - target_os = "fuchsia", - target_os = "redox", - target_os = "emscripten" -))] -#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten) +#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox"))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::mem; use crate::sys_common::thread_local_dtor::register_dtor_fallback; @@ -94,7 +88,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { } } -#[cfg(any(target_os = "vxworks", target_os = "horizon"))] +#[cfg(any(target_os = "vxworks", target_os = "horizon", target_os = "emscripten"))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::sys_common::thread_local_dtor::register_dtor_fallback; register_dtor_fallback(t, dtor); From ab20f8d5ba164510a4a782bcf305b7005bc88b15 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Sat, 14 Jan 2023 00:29:48 -0500 Subject: [PATCH 03/12] remove optimistic spinning from `mpsc::SyncSender` --- library/std/src/sync/mpmc/array.rs | 18 +++++------------- library/std/src/sync/mpmc/utils.rs | 12 ++---------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs index c1e3e48b044..c6bb09b0417 100644 --- a/library/std/src/sync/mpmc/array.rs +++ b/library/std/src/sync/mpmc/array.rs @@ -319,19 +319,10 @@ impl Channel { ) -> Result<(), SendTimeoutError> { let token = &mut Token::default(); loop { - // Try sending a message several times. - let backoff = Backoff::new(); - loop { - if self.start_send(token) { - let res = unsafe { self.write(token, msg) }; - return res.map_err(SendTimeoutError::Disconnected); - } - - if backoff.is_completed() { - break; - } else { - backoff.spin_light(); - } + // Try sending a message. + if self.start_send(token) { + let res = unsafe { self.write(token, msg) }; + return res.map_err(SendTimeoutError::Disconnected); } if let Some(d) = deadline { @@ -379,6 +370,7 @@ impl Channel { pub(crate) fn recv(&self, deadline: Option) -> Result { let token = &mut Token::default(); loop { + // Try receiving a message. if self.start_recv(token) { let res = unsafe { self.read(token) }; return res.map_err(|_| RecvTimeoutError::Disconnected); diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index cfe42750d52..d053d69e26e 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -105,10 +105,8 @@ impl Backoff { /// Backs off using lightweight spinning. /// - /// This method should be used for: - /// - Retrying an operation because another thread made progress. i.e. on CAS failure. - /// - Waiting for an operation to complete by spinning optimistically for a few iterations - /// before falling back to parking the thread (see `Backoff::is_completed`). + /// This method should be used for retrying an operation because another thread made + /// progress. i.e. on CAS failure. #[inline] pub fn spin_light(&self) { let step = self.step.get().min(SPIN_LIMIT); @@ -134,10 +132,4 @@ impl Backoff { self.step.set(self.step.get() + 1); } - - /// Returns `true` if quadratic backoff has completed and parking the thread is advised. - #[inline] - pub fn is_completed(&self) -> bool { - self.step.get() > SPIN_LIMIT - } } From a41c5f9c381aeb6cb29afd1a0ca79f31d257cd6c Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Sat, 14 Jan 2023 12:29:41 +0000 Subject: [PATCH 04/12] Re-add #[allow(unused)] attr --- library/std/src/sys/unix/thread_local_dtor.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index aa4df000632..6810e197df8 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -89,6 +89,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { } #[cfg(any(target_os = "vxworks", target_os = "horizon", target_os = "emscripten"))] +#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten) pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::sys_common::thread_local_dtor::register_dtor_fallback; register_dtor_fallback(t, dtor); From 25f0147db2ca7538ab9324d63bc1e0060f67f9c8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 16 Jan 2023 11:41:19 -0800 Subject: [PATCH 05/12] implement Hash for proc_macro::LineColumn --- library/proc_macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index f0e4f5d8a80..1699f0d55bc 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -581,7 +581,7 @@ impl fmt::Debug for Span { /// A line-column pair representing the start or end of a `Span`. #[unstable(feature = "proc_macro_span", issue = "54725")] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct LineColumn { /// The 1-indexed line in the source file on which the span starts or ends (inclusive). #[unstable(feature = "proc_macro_span", issue = "54725")] From 783caf3702fa28aeeaebd413eb9072399506eab1 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 13 Jan 2023 06:29:46 -0800 Subject: [PATCH 06/12] Append .dwp to the binary filename instead of replacing the existing extension. gdb et al. expect to find the dwp file at .dwp, even if already has an extension (e.g. libfoo.so's dwp is expected to be at libfoo.so.dwp). --- compiler/rustc_codegen_ssa/src/back/link.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8ca7103ed48..06dbeac2850 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -599,7 +599,8 @@ fn link_dwarf_object<'a>( cg_results: &CodegenResults, executable_out_filename: &Path, ) { - let dwp_out_filename = executable_out_filename.with_extension("dwp"); + let mut dwp_out_filename = executable_out_filename.to_path_buf().into_os_string(); + dwp_out_filename.push(".dwp"); debug!(?dwp_out_filename, ?executable_out_filename); #[derive(Default)] From 0accf08e6b2b7a59147e9ca1c80ed969e82781fc Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 19 Jan 2023 16:06:25 +0100 Subject: [PATCH 07/12] remove unnecessary check for opaque types --- compiler/rustc_const_eval/src/transform/validate.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index dd168a9ac3c..3f83d40755a 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::{ ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, }; -use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; @@ -230,11 +230,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Equal types, all is good. return true; } - // Normalization reveals opaque types, but we may be validating MIR while computing - // said opaque types, causing cycles. - if (src, dest).has_opaque_types() { - return true; - } crate::util::is_subtype(self.tcx, self.param_env, src, dest) } From a2d1cb2c22016ae003ab51d9654746cc4fc5200a Mon Sep 17 00:00:00 2001 From: dimi Date: Tue, 24 May 2022 23:56:19 +0200 Subject: [PATCH 08/12] impl DispatchFromDyn for Cell and UnsafeCell --- library/core/src/cell.rs | 33 ++++++++++++++++++- ...itrary_self_types_pointers_and_wrappers.rs | 22 +++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 129213fde74..7f109491350 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -196,7 +196,7 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; use crate::mem; -use crate::ops::{CoerceUnsized, Deref, DerefMut}; +use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn}; use crate::ptr::{self, NonNull}; mod lazy; @@ -571,6 +571,16 @@ impl Cell { #[unstable(feature = "coerce_unsized", issue = "18598")] impl, U> CoerceUnsized> for Cell {} +// Allow types that wrap `Cell` to also implement `DispatchFromDyn` +// and become object safe method receivers. +// Note that currently `Cell` itself cannot be a method receiver +// because it does not implement Deref. +// In other words: +// `self: Cell<&Self>` won't work +// `self: CellWrapper` becomes possible +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U> DispatchFromDyn> for Cell {} + impl Cell<[T]> { /// Returns a `&[Cell]` from a `&Cell<[T]>` /// @@ -2078,6 +2088,16 @@ impl const From for UnsafeCell { #[unstable(feature = "coerce_unsized", issue = "18598")] impl, U> CoerceUnsized> for UnsafeCell {} +// Allow types that wrap `UnsafeCell` to also implement `DispatchFromDyn` +// and become object safe method receivers. +// Note that currently `UnsafeCell` itself cannot be a method receiver +// because it does not implement Deref. +// In other words: +// `self: UnsafeCell<&Self>` won't work +// `self: UnsafeCellWrapper` becomes possible +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U> DispatchFromDyn> for UnsafeCell {} + /// [`UnsafeCell`], but [`Sync`]. /// /// This is just an `UnsafeCell`, except it implements `Sync` @@ -2169,6 +2189,17 @@ impl const From for SyncUnsafeCell { //#[unstable(feature = "sync_unsafe_cell", issue = "95439")] impl, U> CoerceUnsized> for SyncUnsafeCell {} +// Allow types that wrap `SyncUnsafeCell` to also implement `DispatchFromDyn` +// and become object safe method receivers. +// Note that currently `SyncUnsafeCell` itself cannot be a method receiver +// because it does not implement Deref. +// In other words: +// `self: SyncUnsafeCell<&Self>` won't work +// `self: SyncUnsafeCellWrapper` becomes possible +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +//#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +impl, U> DispatchFromDyn> for SyncUnsafeCell {} + #[allow(unused)] fn assert_coerce_unsized( a: UnsafeCell<&i32>, diff --git a/tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs b/tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs index 65fec3becac..91aacedfc57 100644 --- a/tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs +++ b/tests/ui/self/arbitrary_self_types_pointers_and_wrappers.rs @@ -3,6 +3,7 @@ #![feature(rustc_attrs)] use std::{ + cell::Cell, ops::{Deref, CoerceUnsized, DispatchFromDyn}, marker::Unsize, }; @@ -20,6 +21,20 @@ impl Deref for Ptr { impl + ?Sized, U: ?Sized> CoerceUnsized> for Ptr {} impl + ?Sized, U: ?Sized> DispatchFromDyn> for Ptr {} + +struct CellPtr<'a, T: ?Sized>(Cell<&'a T>); + +impl<'a, T: ?Sized> Deref for CellPtr<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + self.0.get() + } +} + +impl<'a, T: Unsize + ?Sized, U: ?Sized> CoerceUnsized> for CellPtr<'a, T> {} +impl<'a, T: Unsize + ?Sized, U: ?Sized> DispatchFromDyn> for CellPtr<'a, T> {} + struct Wrapper(T); impl Deref for Wrapper { @@ -42,6 +57,7 @@ trait Trait { fn ptr_wrapper(self: Ptr>) -> i32; fn wrapper_ptr(self: Wrapper>) -> i32; fn wrapper_ptr_wrapper(self: Wrapper>>) -> i32; + fn cell(self: CellPtr) -> i32; } impl Trait for i32 { @@ -54,6 +70,9 @@ impl Trait for i32 { fn wrapper_ptr_wrapper(self: Wrapper>>) -> i32 { ***self } + fn cell(self: CellPtr) -> i32 { + *self + } } fn main() { @@ -65,4 +84,7 @@ fn main() { let wpw = Wrapper(Ptr(Box::new(Wrapper(7)))) as Wrapper>>; assert_eq!(wpw.wrapper_ptr_wrapper(), 7); + + let c = CellPtr(Cell::new(&8)) as CellPtr; + assert_eq!(c.cell(), 8); } From af58854168f2ae6b0af89d7945584cb78e276193 Mon Sep 17 00:00:00 2001 From: dimi Date: Wed, 26 Oct 2022 14:40:03 +0200 Subject: [PATCH 09/12] add feature gate tests for DispatchFromDyn --- .../feature-gate-dispatch-from-dyn-cell.rs | 9 ++++ ...feature-gate-dispatch-from-dyn-cell.stderr | 12 +++++ ...ure-gate-dispatch-from-dyn-missing-impl.rs | 35 +++++++++++++++ ...gate-dispatch-from-dyn-missing-impl.stderr | 45 +++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 tests/ui/feature-gates/feature-gate-dispatch-from-dyn-cell.rs create mode 100644 tests/ui/feature-gates/feature-gate-dispatch-from-dyn-cell.stderr create mode 100644 tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.rs create mode 100644 tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr diff --git a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-cell.rs b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-cell.rs new file mode 100644 index 00000000000..83366ea02b0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-cell.rs @@ -0,0 +1,9 @@ +// Check that even though Cell: DispatchFromDyn it remains an invalid self parameter type + +use std::cell::Cell; + +trait Trait{ + fn cell(self: Cell<&Self>); //~ ERROR invalid `self` parameter type: Cell<&Self> +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-cell.stderr b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-cell.stderr new file mode 100644 index 00000000000..ce06ce916a7 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-cell.stderr @@ -0,0 +1,12 @@ +error[E0307]: invalid `self` parameter type: Cell<&Self> + --> $DIR/feature-gate-dispatch-from-dyn-cell.rs:6:19 + | +LL | fn cell(self: Cell<&Self>); + | ^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.rs b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.rs new file mode 100644 index 00000000000..23857cbaca8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.rs @@ -0,0 +1,35 @@ +// Check that a self parameter type requires a DispatchFromDyn impl to be object safe + +#![feature(arbitrary_self_types, unsize, coerce_unsized)] + +use std::{ + marker::Unsize, + ops::{CoerceUnsized, Deref}, +}; + +struct Ptr(Box); + +impl Deref for Ptr { + type Target = T; + + fn deref(&self) -> &T { + &*self.0 + } +} + +impl + ?Sized, U: ?Sized> CoerceUnsized> for Ptr {} +// Because this impl is missing the coercion below fails. +// impl + ?Sized, U: ?Sized> DispatchFromDyn> for Ptr {} + +trait Trait { + fn ptr(self: Ptr); +} +impl Trait for i32 { + fn ptr(self: Ptr) {} +} + +fn main() { + Ptr(Box::new(4)) as Ptr; + //~^ ERROR the trait `Trait` cannot be made into an object + //~^^ ERROR the trait `Trait` cannot be made into an object +} diff --git a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr new file mode 100644 index 00000000000..d81eade8e9b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr @@ -0,0 +1,45 @@ +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:32:25 + | +LL | fn ptr(self: Ptr); + | --------- help: consider changing method `ptr`'s `self` parameter to be `&self`: `&Self` +... +LL | Ptr(Box::new(4)) as Ptr; + | ^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18 + | +LL | trait Trait { + | ----- this trait cannot be made into an object... +LL | fn ptr(self: Ptr); + | ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:32:5 + | +LL | fn ptr(self: Ptr); + | --------- help: consider changing method `ptr`'s `self` parameter to be `&self`: `&Self` +... +LL | Ptr(Box::new(4)) as Ptr; + | ^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18 + | +LL | trait Trait { + | ----- this trait cannot be made into an object... +LL | fn ptr(self: Ptr); + | ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on +note: required for `Ptr<{integer}>` to implement `CoerceUnsized>` + --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:20:40 + | +LL | impl + ?Sized, U: ?Sized> CoerceUnsized> for Ptr {} + | --------- ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ + | | + | unsatisfied trait bound introduced here + = note: required by cast to type `Ptr` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0038`. From b222f2e2660f8d81dc30061c918d774147fcf1a6 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 25 Jan 2023 19:53:03 +0100 Subject: [PATCH 10/12] Move `note_and_explain_type_err` from `rustc_middle` to `rustc_infer` This way we can properly deal with the types. --- .../src/infer/error_reporting/mod.rs | 5 +- .../infer/error_reporting/note_and_explain.rs | 647 ++++++++++++++++++ compiler/rustc_middle/src/ty/assoc.rs | 2 +- compiler/rustc_middle/src/ty/error.rs | 637 +---------------- 4 files changed, 657 insertions(+), 634 deletions(-) create mode 100644 compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 79704b6adf7..faba723acda 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -79,6 +79,7 @@ use std::path::PathBuf; use std::{cmp, fmt, iter}; mod note; +mod note_and_explain; mod suggest; pub(crate) mod need_type_info; @@ -1846,7 +1847,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } self.check_and_note_conflicting_crates(diag, terr); - self.tcx.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); + + self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); + if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values && let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs new file mode 100644 index 00000000000..6e781a041e0 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -0,0 +1,647 @@ +use super::TypeErrCtxt; +use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; +use rustc_errors::{pluralize, Diagnostic, MultiSpan}; +use rustc_hir::{self as hir, def::DefKind}; +use rustc_middle::traits::ObligationCauseCode; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::print::Printer; +use rustc_middle::{ + traits::ObligationCause, + ty::{self, error::TypeError, print::FmtPrinter, suggest_constraining_type_param, Ty}, +}; +use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol}; + +impl<'tcx> TypeErrCtxt<'_, 'tcx> { + pub fn note_and_explain_type_err( + &self, + diag: &mut Diagnostic, + err: TypeError<'tcx>, + cause: &ObligationCause<'tcx>, + sp: Span, + body_owner_def_id: DefId, + ) { + use ty::error::TypeError::*; + debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); + + let tcx = self.tcx; + + match err { + ArgumentSorts(values, _) | Sorts(values) => { + match (values.expected.kind(), values.found.kind()) { + (ty::Closure(..), ty::Closure(..)) => { + diag.note("no two closures, even if identical, have the same type"); + diag.help("consider boxing your closure and/or using it as a trait object"); + } + (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => { + // Issue #63167 + diag.note("distinct uses of `impl Trait` result in different opaque types"); + } + (ty::Float(_), ty::Infer(ty::IntVar(_))) + if let Ok( + // Issue #53280 + snippet, + ) = tcx.sess.source_map().span_to_snippet(sp) => + { + if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { + diag.span_suggestion( + sp, + "use a float literal", + format!("{}.0", snippet), + MachineApplicable, + ); + } + } + (ty::Param(expected), ty::Param(found)) => { + let generics = tcx.generics_of(body_owner_def_id); + let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id); + if !sp.contains(e_span) { + diag.span_label(e_span, "expected type parameter"); + } + let f_span = tcx.def_span(generics.type_param(found, tcx).def_id); + if !sp.contains(f_span) { + diag.span_label(f_span, "found type parameter"); + } + diag.note( + "a type parameter was expected, but a different one was found; \ + you might be missing a type parameter or trait bound", + ); + diag.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", + ); + } + (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => { + diag.note("an associated type was expected, but a different one was found"); + } + (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p)) + if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder => + { + let generics = tcx.generics_of(body_owner_def_id); + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "this type parameter"); + } + let hir = tcx.hir(); + let mut note = true; + if let Some(generics) = generics + .type_param(p, tcx) + .def_id + .as_local() + .map(|id| hir.local_def_id_to_hir_id(id)) + .and_then(|id| tcx.hir().find_parent(id)) + .as_ref() + .and_then(|node| node.generics()) + { + // Synthesize the associated type restriction `Add`. + // FIXME: extract this logic for use in other diagnostics. + let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx); + let path = + tcx.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); + let item_name = tcx.item_name(proj.def_id); + let item_args = self.format_generic_args(assoc_substs); + + let path = if path.ends_with('>') { + format!( + "{}, {}{} = {}>", + &path[..path.len() - 1], + item_name, + item_args, + p + ) + } else { + format!("{}<{}{} = {}>", path, item_name, item_args, p) + }; + note = !suggest_constraining_type_param( + tcx, + generics, + diag, + &format!("{}", proj.self_ty()), + &path, + None, + ); + } + if note { + diag.note("you might be missing a type parameter or trait bound"); + } + } + (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..)) + | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => { + let generics = tcx.generics_of(body_owner_def_id); + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "this type parameter"); + } + diag.help("type parameters must be constrained to match other types"); + if tcx.sess.teach(&diag.get_code().unwrap()) { + diag.help( + "given a type parameter `T` and a method `foo`: +``` +trait Trait { fn foo(&tcx) -> T; } +``` +the only ways to implement method `foo` are: +- constrain `T` with an explicit type: +``` +impl Trait for X { + fn foo(&tcx) -> String { String::new() } +} +``` +- add a trait bound to `T` and call a method on that trait that returns `Self`: +``` +impl Trait for X { + fn foo(&tcx) -> T { ::default() } +} +``` +- change `foo` to return an argument of type `T`: +``` +impl Trait for X { + fn foo(&tcx, x: T) -> T { x } +} +```", + ); + } + diag.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", + ); + } + (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => { + let generics = tcx.generics_of(body_owner_def_id); + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "this type parameter"); + } + diag.help(&format!( + "every closure has a distinct type and so could not always match the \ + caller-chosen type of parameter `{}`", + p + )); + } + (ty::Param(p), _) | (_, ty::Param(p)) => { + let generics = tcx.generics_of(body_owner_def_id); + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "this type parameter"); + } + } + (ty::Alias(ty::Projection, proj_ty), _) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => { + self.expected_projection( + diag, + proj_ty, + values, + body_owner_def_id, + cause.code(), + ); + } + (_, ty::Alias(ty::Projection, proj_ty)) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => { + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.found, values.expected, + ); + if !(self.suggest_constraining_opaque_associated_type( + diag, + &msg, + proj_ty, + values.expected, + ) || self.suggest_constraint( + diag, + &msg, + body_owner_def_id, + proj_ty, + values.expected, + )) { + diag.help(&msg); + diag.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + } + _ => {} + } + debug!( + "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", + values.expected, + values.expected.kind(), + values.found, + values.found.kind(), + ); + } + CyclicTy(ty) => { + // Watch out for various cases of cyclic types and try to explain. + if ty.is_closure() || ty.is_generator() { + diag.note( + "closures cannot capture themselves or take themselves as argument;\n\ + this error may be the result of a recent compiler bug-fix,\n\ + see issue #46062 \n\ + for more information", + ); + } + } + TargetFeatureCast(def_id) => { + let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span); + diag.note( + "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" + ); + diag.span_labels(target_spans, "`#[target_feature]` added here"); + } + _ => {} + } + } + + fn suggest_constraint( + &self, + diag: &mut Diagnostic, + msg: &str, + body_owner_def_id: DefId, + proj_ty: &ty::AliasTy<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + let tcx = self.tcx; + let assoc = tcx.associated_item(proj_ty.def_id); + let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx); + if let Some(item) = tcx.hir().get_if_local(body_owner_def_id) { + if let Some(hir_generics) = item.generics() { + // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. + // This will also work for `impl Trait`. + let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { + let generics = tcx.generics_of(body_owner_def_id); + generics.type_param(param_ty, tcx).def_id + } else { + return false; + }; + let Some(def_id) = def_id.as_local() else { + return false; + }; + + // First look in the `where` clause, as this might be + // `fn foo(x: T) where T: Trait`. + for pred in hir_generics.bounds_for_param(def_id) { + if self.constrain_generic_bound_associated_type_structured_suggestion( + diag, + &trait_ref, + pred.bounds, + &assoc, + assoc_substs, + ty, + msg, + false, + ) { + return true; + } + } + } + } + false + } + + /// An associated type was expected and a different type was found. + /// + /// We perform a few different checks to see what we can suggest: + /// + /// - In the current item, look for associated functions that return the expected type and + /// suggest calling them. (Not a structured suggestion.) + /// - If any of the item's generic bounds can be constrained, we suggest constraining the + /// associated type to the found type. + /// - If the associated type has a default type and was expected inside of a `trait`, we + /// mention that this is disallowed. + /// - If all other things fail, and the error is not because of a mismatch between the `trait` + /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc + /// fn that returns the type. + fn expected_projection( + &self, + diag: &mut Diagnostic, + proj_ty: &ty::AliasTy<'tcx>, + values: ExpectedFound>, + body_owner_def_id: DefId, + cause_code: &ObligationCauseCode<'_>, + ) { + let tcx = self.tcx; + + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.expected, values.found + ); + let body_owner = tcx.hir().get_if_local(body_owner_def_id); + let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); + + // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. + let callable_scope = matches!( + body_owner, + Some( + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) + | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), + ) + ); + let impl_comparison = + matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. }); + let assoc = tcx.associated_item(proj_ty.def_id); + if !callable_scope || impl_comparison { + // We do not want to suggest calling functions when the reason of the + // type error is a comparison of an `impl` with its `trait` or when the + // scope is outside of a `Body`. + } else { + // If we find a suitable associated function that returns the expected type, we don't + // want the more general suggestion later in this method about "consider constraining + // the associated type or calling a method that returns the associated type". + let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( + diag, + assoc.container_id(tcx), + current_method_ident, + proj_ty.def_id, + values.expected, + ); + // Possibly suggest constraining the associated type to conform to the + // found type. + if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found) + || point_at_assoc_fn + { + return; + } + } + + self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found); + + if self.point_at_associated_type(diag, body_owner_def_id, values.found) { + return; + } + + if !impl_comparison { + // Generic suggestion when we can't be more specific. + if callable_scope { + diag.help(&format!( + "{} or calling a method that returns `{}`", + msg, values.expected + )); + } else { + diag.help(&msg); + } + diag.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + if tcx.sess.teach(&diag.get_code().unwrap()) { + diag.help( + "given an associated type `T` and a method `foo`: +``` +trait Trait { +type T; +fn foo(&tcx) -> Self::T; +} +``` +the only way of implementing method `foo` is to constrain `T` with an explicit associated type: +``` +impl Trait for X { +type T = String; +fn foo(&tcx) -> Self::T { String::new() } +} +```", + ); + } + } + + /// When the expected `impl Trait` is not defined in the current item, it will come from + /// a return type. This can occur when dealing with `TryStream` (#71035). + fn suggest_constraining_opaque_associated_type( + &self, + diag: &mut Diagnostic, + msg: &str, + proj_ty: &ty::AliasTy<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + let tcx = self.tcx; + + let assoc = tcx.associated_item(proj_ty.def_id); + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() { + let opaque_local_def_id = def_id.as_local(); + let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id { + match &tcx.hir().expect_item(opaque_local_def_id).kind { + hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty, + _ => bug!("The HirId comes from a `ty::Opaque`"), + } + } else { + return false; + }; + + let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx); + + self.constrain_generic_bound_associated_type_structured_suggestion( + diag, + &trait_ref, + opaque_hir_ty.bounds, + assoc, + assoc_substs, + ty, + msg, + true, + ) + } else { + false + } + } + + fn point_at_methods_that_satisfy_associated_type( + &self, + diag: &mut Diagnostic, + assoc_container_id: DefId, + current_method_ident: Option, + proj_ty_item_def_id: DefId, + expected: Ty<'tcx>, + ) -> bool { + let tcx = self.tcx; + + let items = tcx.associated_items(assoc_container_id); + // Find all the methods in the trait that could be called to construct the + // expected associated type. + // FIXME: consider suggesting the use of associated `const`s. + let methods: Vec<(Span, String)> = items + .in_definition_order() + .filter(|item| { + ty::AssocKind::Fn == item.kind && Some(item.name) != current_method_ident + }) + .filter_map(|item| { + let method = tcx.fn_sig(item.def_id); + match *method.output().skip_binder().kind() { + ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. }) + if item_def_id == proj_ty_item_def_id => + { + Some(( + tcx.def_span(item.def_id), + format!("consider calling `{}`", tcx.def_path_str(item.def_id)), + )) + } + _ => None, + } + }) + .collect(); + if !methods.is_empty() { + // Use a single `help:` to show all the methods in the trait that can + // be used to construct the expected associated type. + let mut span: MultiSpan = + methods.iter().map(|(sp, _)| *sp).collect::>().into(); + let msg = format!( + "{some} method{s} {are} available that return{r} `{ty}`", + some = if methods.len() == 1 { "a" } else { "some" }, + s = pluralize!(methods.len()), + are = pluralize!("is", methods.len()), + r = if methods.len() == 1 { "s" } else { "" }, + ty = expected + ); + for (sp, label) in methods.into_iter() { + span.push_span_label(sp, label); + } + diag.span_help(span, &msg); + return true; + } + false + } + + fn point_at_associated_type( + &self, + diag: &mut Diagnostic, + body_owner_def_id: DefId, + found: Ty<'tcx>, + ) -> bool { + let tcx = self.tcx; + + let Some(hir_id) = body_owner_def_id.as_local() else { + return false; + }; + let hir_id = tcx.hir().local_def_id_to_hir_id(hir_id); + // When `body_owner` is an `impl` or `trait` item, look in its associated types for + // `expected` and point at it. + let parent_id = tcx.hir().get_parent_item(hir_id); + let item = tcx.hir().find_by_def_id(parent_id.def_id); + debug!("expected_projection parent item {:?}", item); + match item { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { + // FIXME: account for `#![feature(specialization)]` + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type => { + // FIXME: account for returning some type in a trait fn impl that has + // an assoc type as a return type (#72076). + if let hir::Defaultness::Default { has_value: true } = + tcx.impl_defaultness(item.id.owner_id) + { + if tcx.type_of(item.id.owner_id) == found { + diag.span_label( + item.span, + "associated type defaults can't be assumed inside the \ + trait defining them", + ); + return true; + } + } + } + _ => {} + } + } + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { items, .. }), + .. + })) => { + for item in &items[..] { + if let hir::AssocItemKind::Type = item.kind { + if tcx.type_of(item.id.owner_id) == found { + diag.span_label(item.span, "expected this associated type"); + return true; + } + } + } + } + _ => {} + } + false + } + + /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` + /// requirement, provide a structured suggestion to constrain it to a given type `ty`. + /// + /// `is_bound_surely_present` indicates whether we know the bound we're looking for is + /// inside `bounds`. If that's the case then we can consider `bounds` containing only one + /// trait bound as the one we're looking for. This can help in cases where the associated + /// type is defined on a supertrait of the one present in the bounds. + fn constrain_generic_bound_associated_type_structured_suggestion( + &self, + diag: &mut Diagnostic, + trait_ref: &ty::TraitRef<'tcx>, + bounds: hir::GenericBounds<'_>, + assoc: &ty::AssocItem, + assoc_substs: &[ty::GenericArg<'tcx>], + ty: Ty<'tcx>, + msg: &str, + is_bound_surely_present: bool, + ) -> bool { + // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. + + let trait_bounds = bounds.iter().filter_map(|bound| match bound { + hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr), + _ => None, + }); + + let matching_trait_bounds = trait_bounds + .clone() + .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)) + .collect::>(); + + let span = match &matching_trait_bounds[..] { + &[ptr] => ptr.span, + &[] if is_bound_surely_present => match &trait_bounds.collect::>()[..] { + &[ptr] => ptr.span, + _ => return false, + }, + _ => return false, + }; + + self.constrain_associated_type_structured_suggestion( + diag, + span, + assoc, + assoc_substs, + ty, + msg, + ) + } + + /// Given a span corresponding to a bound, provide a structured suggestion to set an + /// associated type to a given type `ty`. + fn constrain_associated_type_structured_suggestion( + &self, + diag: &mut Diagnostic, + span: Span, + assoc: &ty::AssocItem, + assoc_substs: &[ty::GenericArg<'tcx>], + ty: Ty<'tcx>, + msg: &str, + ) -> bool { + let tcx = self.tcx; + + if let Ok(has_params) = + tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) + { + let (span, sugg) = if has_params { + let pos = span.hi() - BytePos(1); + let span = Span::new(pos, pos, span.ctxt(), span.parent()); + (span, format!(", {} = {}", assoc.ident(tcx), ty)) + } else { + let item_args = self.format_generic_args(assoc_substs); + (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty)) + }; + diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); + return true; + } + false + } + + pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String { + FmtPrinter::new(self.tcx, hir::def::Namespace::TypeNS) + .path_generic_args(Ok, args) + .expect("could not write to `String`.") + .into_buffer() + } +} diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index bb7fba3ee71..47091ca1d69 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -130,7 +130,7 @@ impl std::fmt::Display for AssocKind { /// done only on items with the same name. #[derive(Debug, Clone, PartialEq, HashStable)] pub struct AssocItems<'tcx> { - pub(super) items: SortedIndexMultiMap, + items: SortedIndexMultiMap, } impl<'tcx> AssocItems<'tcx> { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 5d394f71f0d..c8a700c4e28 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -1,24 +1,18 @@ -use crate::traits::{ObligationCause, ObligationCauseCode}; -use crate::ty::diagnostics::suggest_constraining_type_param; -use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, Printer}; +use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, PrettyPrinter}; use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; -use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; -use rustc_errors::{pluralize, Diagnostic, MultiSpan}; +use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::def_id::DefId; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{BytePos, Span}; +use rustc_span::symbol::Symbol; use rustc_target::spec::abi; - use std::borrow::Cow; use std::collections::hash_map::DefaultHasher; use std::fmt; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; +use std::hash::Hasher; use std::path::PathBuf; -use super::print::PrettyPrinter; - #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable, Lift)] pub struct ExpectedFound { pub expected: T, @@ -391,620 +385,6 @@ impl<'tcx> Ty<'tcx> { } impl<'tcx> TyCtxt<'tcx> { - pub fn note_and_explain_type_err( - self, - diag: &mut Diagnostic, - err: TypeError<'tcx>, - cause: &ObligationCause<'tcx>, - sp: Span, - body_owner_def_id: DefId, - ) { - use self::TypeError::*; - debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); - match err { - ArgumentSorts(values, _) | Sorts(values) => { - match (values.expected.kind(), values.found.kind()) { - (ty::Closure(..), ty::Closure(..)) => { - diag.note("no two closures, even if identical, have the same type"); - diag.help("consider boxing your closure and/or using it as a trait object"); - } - (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => { - // Issue #63167 - diag.note("distinct uses of `impl Trait` result in different opaque types"); - } - (ty::Float(_), ty::Infer(ty::IntVar(_))) - if let Ok( - // Issue #53280 - snippet, - ) = self.sess.source_map().span_to_snippet(sp) => - { - if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { - diag.span_suggestion( - sp, - "use a float literal", - format!("{}.0", snippet), - MachineApplicable, - ); - } - } - (ty::Param(expected), ty::Param(found)) => { - let generics = self.generics_of(body_owner_def_id); - let e_span = self.def_span(generics.type_param(expected, self).def_id); - if !sp.contains(e_span) { - diag.span_label(e_span, "expected type parameter"); - } - let f_span = self.def_span(generics.type_param(found, self).def_id); - if !sp.contains(f_span) { - diag.span_label(f_span, "found type parameter"); - } - diag.note( - "a type parameter was expected, but a different one was found; \ - you might be missing a type parameter or trait bound", - ); - diag.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", - ); - } - (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => { - diag.note("an associated type was expected, but a different one was found"); - } - (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p)) - if self.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder => - { - let generics = self.generics_of(body_owner_def_id); - let p_span = self.def_span(generics.type_param(p, self).def_id); - if !sp.contains(p_span) { - diag.span_label(p_span, "this type parameter"); - } - let hir = self.hir(); - let mut note = true; - if let Some(generics) = generics - .type_param(p, self) - .def_id - .as_local() - .map(|id| hir.local_def_id_to_hir_id(id)) - .and_then(|id| self.hir().find_parent(id)) - .as_ref() - .and_then(|node| node.generics()) - { - // Synthesize the associated type restriction `Add`. - // FIXME: extract this logic for use in other diagnostics. - let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(self); - let path = - self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); - let item_name = self.item_name(proj.def_id); - let item_args = self.format_generic_args(assoc_substs); - - let path = if path.ends_with('>') { - format!( - "{}, {}{} = {}>", - &path[..path.len() - 1], - item_name, - item_args, - p - ) - } else { - format!("{}<{}{} = {}>", path, item_name, item_args, p) - }; - note = !suggest_constraining_type_param( - self, - generics, - diag, - &format!("{}", proj.self_ty()), - &path, - None, - ); - } - if note { - diag.note("you might be missing a type parameter or trait bound"); - } - } - (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..)) - | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => { - let generics = self.generics_of(body_owner_def_id); - let p_span = self.def_span(generics.type_param(p, self).def_id); - if !sp.contains(p_span) { - diag.span_label(p_span, "this type parameter"); - } - diag.help("type parameters must be constrained to match other types"); - if self.sess.teach(&diag.get_code().unwrap()) { - diag.help( - "given a type parameter `T` and a method `foo`: -``` -trait Trait { fn foo(&self) -> T; } -``` -the only ways to implement method `foo` are: -- constrain `T` with an explicit type: -``` -impl Trait for X { - fn foo(&self) -> String { String::new() } -} -``` -- add a trait bound to `T` and call a method on that trait that returns `Self`: -``` -impl Trait for X { - fn foo(&self) -> T { ::default() } -} -``` -- change `foo` to return an argument of type `T`: -``` -impl Trait for X { - fn foo(&self, x: T) -> T { x } -} -```", - ); - } - diag.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", - ); - } - (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => { - let generics = self.generics_of(body_owner_def_id); - let p_span = self.def_span(generics.type_param(p, self).def_id); - if !sp.contains(p_span) { - diag.span_label(p_span, "this type parameter"); - } - diag.help(&format!( - "every closure has a distinct type and so could not always match the \ - caller-chosen type of parameter `{}`", - p - )); - } - (ty::Param(p), _) | (_, ty::Param(p)) => { - let generics = self.generics_of(body_owner_def_id); - let p_span = self.def_span(generics.type_param(p, self).def_id); - if !sp.contains(p_span) { - diag.span_label(p_span, "this type parameter"); - } - } - (ty::Alias(ty::Projection, proj_ty), _) if self.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => { - self.expected_projection( - diag, - proj_ty, - values, - body_owner_def_id, - cause.code(), - ); - } - (_, ty::Alias(ty::Projection, proj_ty)) if self.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => { - let msg = format!( - "consider constraining the associated type `{}` to `{}`", - values.found, values.expected, - ); - if !(self.suggest_constraining_opaque_associated_type( - diag, - &msg, - proj_ty, - values.expected, - ) || self.suggest_constraint( - diag, - &msg, - body_owner_def_id, - proj_ty, - values.expected, - )) { - diag.help(&msg); - diag.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", - ); - } - } - _ => {} - } - debug!( - "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", - values.expected, - values.expected.kind(), - values.found, - values.found.kind(), - ); - } - CyclicTy(ty) => { - // Watch out for various cases of cyclic types and try to explain. - if ty.is_closure() || ty.is_generator() { - diag.note( - "closures cannot capture themselves or take themselves as argument;\n\ - this error may be the result of a recent compiler bug-fix,\n\ - see issue #46062 \n\ - for more information", - ); - } - } - TargetFeatureCast(def_id) => { - let target_spans = - self.get_attrs(def_id, sym::target_feature).map(|attr| attr.span); - diag.note( - "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" - ); - diag.span_labels(target_spans, "`#[target_feature]` added here"); - } - _ => {} - } - } - - fn suggest_constraint( - self, - diag: &mut Diagnostic, - msg: &str, - body_owner_def_id: DefId, - proj_ty: &ty::AliasTy<'tcx>, - ty: Ty<'tcx>, - ) -> bool { - let assoc = self.associated_item(proj_ty.def_id); - let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self); - if let Some(item) = self.hir().get_if_local(body_owner_def_id) { - if let Some(hir_generics) = item.generics() { - // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. - // This will also work for `impl Trait`. - let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { - let generics = self.generics_of(body_owner_def_id); - generics.type_param(param_ty, self).def_id - } else { - return false; - }; - let Some(def_id) = def_id.as_local() else { - return false; - }; - - // First look in the `where` clause, as this might be - // `fn foo(x: T) where T: Trait`. - for pred in hir_generics.bounds_for_param(def_id) { - if self.constrain_generic_bound_associated_type_structured_suggestion( - diag, - &trait_ref, - pred.bounds, - &assoc, - assoc_substs, - ty, - msg, - false, - ) { - return true; - } - } - } - } - false - } - - /// An associated type was expected and a different type was found. - /// - /// We perform a few different checks to see what we can suggest: - /// - /// - In the current item, look for associated functions that return the expected type and - /// suggest calling them. (Not a structured suggestion.) - /// - If any of the item's generic bounds can be constrained, we suggest constraining the - /// associated type to the found type. - /// - If the associated type has a default type and was expected inside of a `trait`, we - /// mention that this is disallowed. - /// - If all other things fail, and the error is not because of a mismatch between the `trait` - /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc - /// fn that returns the type. - fn expected_projection( - self, - diag: &mut Diagnostic, - proj_ty: &ty::AliasTy<'tcx>, - values: ExpectedFound>, - body_owner_def_id: DefId, - cause_code: &ObligationCauseCode<'_>, - ) { - let msg = format!( - "consider constraining the associated type `{}` to `{}`", - values.expected, values.found - ); - let body_owner = self.hir().get_if_local(body_owner_def_id); - let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); - - // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. - let callable_scope = matches!( - body_owner, - Some( - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) - | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) - | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), - ) - ); - let impl_comparison = - matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. }); - let assoc = self.associated_item(proj_ty.def_id); - if !callable_scope || impl_comparison { - // We do not want to suggest calling functions when the reason of the - // type error is a comparison of an `impl` with its `trait` or when the - // scope is outside of a `Body`. - } else { - // If we find a suitable associated function that returns the expected type, we don't - // want the more general suggestion later in this method about "consider constraining - // the associated type or calling a method that returns the associated type". - let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( - diag, - assoc.container_id(self), - current_method_ident, - proj_ty.def_id, - values.expected, - ); - // Possibly suggest constraining the associated type to conform to the - // found type. - if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found) - || point_at_assoc_fn - { - return; - } - } - - self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found); - - if self.point_at_associated_type(diag, body_owner_def_id, values.found) { - return; - } - - if !impl_comparison { - // Generic suggestion when we can't be more specific. - if callable_scope { - diag.help(&format!( - "{} or calling a method that returns `{}`", - msg, values.expected - )); - } else { - diag.help(&msg); - } - diag.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", - ); - } - if self.sess.teach(&diag.get_code().unwrap()) { - diag.help( - "given an associated type `T` and a method `foo`: -``` -trait Trait { -type T; -fn foo(&self) -> Self::T; -} -``` -the only way of implementing method `foo` is to constrain `T` with an explicit associated type: -``` -impl Trait for X { -type T = String; -fn foo(&self) -> Self::T { String::new() } -} -```", - ); - } - } - - /// When the expected `impl Trait` is not defined in the current item, it will come from - /// a return type. This can occur when dealing with `TryStream` (#71035). - fn suggest_constraining_opaque_associated_type( - self, - diag: &mut Diagnostic, - msg: &str, - proj_ty: &ty::AliasTy<'tcx>, - ty: Ty<'tcx>, - ) -> bool { - let assoc = self.associated_item(proj_ty.def_id); - if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() { - let opaque_local_def_id = def_id.as_local(); - let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id { - match &self.hir().expect_item(opaque_local_def_id).kind { - hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty, - _ => bug!("The HirId comes from a `ty::Opaque`"), - } - } else { - return false; - }; - - let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self); - - self.constrain_generic_bound_associated_type_structured_suggestion( - diag, - &trait_ref, - opaque_hir_ty.bounds, - assoc, - assoc_substs, - ty, - msg, - true, - ) - } else { - false - } - } - - fn point_at_methods_that_satisfy_associated_type( - self, - diag: &mut Diagnostic, - assoc_container_id: DefId, - current_method_ident: Option, - proj_ty_item_def_id: DefId, - expected: Ty<'tcx>, - ) -> bool { - let items = self.associated_items(assoc_container_id); - // Find all the methods in the trait that could be called to construct the - // expected associated type. - // FIXME: consider suggesting the use of associated `const`s. - let methods: Vec<(Span, String)> = items - .items - .iter() - .filter(|(name, item)| { - ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident - }) - .filter_map(|(_, item)| { - let method = self.fn_sig(item.def_id); - match *method.output().skip_binder().kind() { - ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. }) - if item_def_id == proj_ty_item_def_id => - { - Some(( - self.def_span(item.def_id), - format!("consider calling `{}`", self.def_path_str(item.def_id)), - )) - } - _ => None, - } - }) - .collect(); - if !methods.is_empty() { - // Use a single `help:` to show all the methods in the trait that can - // be used to construct the expected associated type. - let mut span: MultiSpan = - methods.iter().map(|(sp, _)| *sp).collect::>().into(); - let msg = format!( - "{some} method{s} {are} available that return{r} `{ty}`", - some = if methods.len() == 1 { "a" } else { "some" }, - s = pluralize!(methods.len()), - are = pluralize!("is", methods.len()), - r = if methods.len() == 1 { "s" } else { "" }, - ty = expected - ); - for (sp, label) in methods.into_iter() { - span.push_span_label(sp, label); - } - diag.span_help(span, &msg); - return true; - } - false - } - - fn point_at_associated_type( - self, - diag: &mut Diagnostic, - body_owner_def_id: DefId, - found: Ty<'tcx>, - ) -> bool { - let Some(hir_id) = body_owner_def_id.as_local() else { - return false; - }; - let hir_id = self.hir().local_def_id_to_hir_id(hir_id); - // When `body_owner` is an `impl` or `trait` item, look in its associated types for - // `expected` and point at it. - let parent_id = self.hir().get_parent_item(hir_id); - let item = self.hir().find_by_def_id(parent_id.def_id); - debug!("expected_projection parent item {:?}", item); - match item { - Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { - // FIXME: account for `#![feature(specialization)]` - for item in &items[..] { - match item.kind { - hir::AssocItemKind::Type => { - // FIXME: account for returning some type in a trait fn impl that has - // an assoc type as a return type (#72076). - if let hir::Defaultness::Default { has_value: true } = - self.impl_defaultness(item.id.owner_id) - { - if self.type_of(item.id.owner_id) == found { - diag.span_label( - item.span, - "associated type defaults can't be assumed inside the \ - trait defining them", - ); - return true; - } - } - } - _ => {} - } - } - } - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { items, .. }), - .. - })) => { - for item in &items[..] { - if let hir::AssocItemKind::Type = item.kind { - if self.type_of(item.id.owner_id) == found { - diag.span_label(item.span, "expected this associated type"); - return true; - } - } - } - } - _ => {} - } - false - } - - /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` - /// requirement, provide a structured suggestion to constrain it to a given type `ty`. - /// - /// `is_bound_surely_present` indicates whether we know the bound we're looking for is - /// inside `bounds`. If that's the case then we can consider `bounds` containing only one - /// trait bound as the one we're looking for. This can help in cases where the associated - /// type is defined on a supertrait of the one present in the bounds. - fn constrain_generic_bound_associated_type_structured_suggestion( - self, - diag: &mut Diagnostic, - trait_ref: &ty::TraitRef<'tcx>, - bounds: hir::GenericBounds<'_>, - assoc: &ty::AssocItem, - assoc_substs: &[ty::GenericArg<'tcx>], - ty: Ty<'tcx>, - msg: &str, - is_bound_surely_present: bool, - ) -> bool { - // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. - - let trait_bounds = bounds.iter().filter_map(|bound| match bound { - hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr), - _ => None, - }); - - let matching_trait_bounds = trait_bounds - .clone() - .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)) - .collect::>(); - - let span = match &matching_trait_bounds[..] { - &[ptr] => ptr.span, - &[] if is_bound_surely_present => match &trait_bounds.collect::>()[..] { - &[ptr] => ptr.span, - _ => return false, - }, - _ => return false, - }; - - self.constrain_associated_type_structured_suggestion( - diag, - span, - assoc, - assoc_substs, - ty, - msg, - ) - } - - /// Given a span corresponding to a bound, provide a structured suggestion to set an - /// associated type to a given type `ty`. - fn constrain_associated_type_structured_suggestion( - self, - diag: &mut Diagnostic, - span: Span, - assoc: &ty::AssocItem, - assoc_substs: &[ty::GenericArg<'tcx>], - ty: Ty<'tcx>, - msg: &str, - ) -> bool { - if let Ok(has_params) = - self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) - { - let (span, sugg) = if has_params { - let pos = span.hi() - BytePos(1); - let span = Span::new(pos, pos, span.ctxt(), span.parent()); - (span, format!(", {} = {}", assoc.ident(self), ty)) - } else { - let item_args = self.format_generic_args(assoc_substs); - (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(self), item_args, ty)) - }; - diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); - return true; - } - false - } - pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option) { let width = self.sess.diagnostic_width(); let length_limit = width.saturating_sub(30); @@ -1047,11 +427,4 @@ fn foo(&self) -> Self::T { String::new() } Err(_) => (regular, None), } } - - fn format_generic_args(self, args: &[ty::GenericArg<'tcx>]) -> String { - FmtPrinter::new(self, hir::def::Namespace::TypeNS) - .path_generic_args(Ok, args) - .expect("could not write to `String`.") - .into_buffer() - } } From 943000fdcf9eac6c77b44923f551409aa06a46b5 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 25 Jan 2023 20:11:05 +0100 Subject: [PATCH 11/12] Use `can_eq` to compare types for default assoc type error This works correctly with inference variables. --- .../src/infer/error_reporting/mod.rs | 1 - .../infer/error_reporting/note_and_explain.rs | 11 +++++++++-- compiler/rustc_middle/src/ty/query.rs | 8 ++++++++ compiler/rustc_middle/src/ty/util.rs | 11 ----------- .../defaults-in-other-trait-items.rs | 14 ++++++++++++++ .../defaults-in-other-trait-items.stderr | 17 ++++++++++++++++- tests/ui/associated-types/issue-26681.stderr | 4 ++-- 7 files changed, 49 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index faba723acda..d19a0007f08 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1850,7 +1850,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); - if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values && let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() && let Some(def_id) = def_id.as_local() diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 6e781a041e0..425cde3302d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -515,7 +515,11 @@ fn foo(&tcx) -> Self::T { String::new() } // `expected` and point at it. let parent_id = tcx.hir().get_parent_item(hir_id); let item = tcx.hir().find_by_def_id(parent_id.def_id); + debug!("expected_projection parent item {:?}", item); + + let param_env = tcx.param_env(body_owner_def_id); + match item { Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { // FIXME: account for `#![feature(specialization)]` @@ -527,7 +531,8 @@ fn foo(&tcx) -> Self::T { String::new() } if let hir::Defaultness::Default { has_value: true } = tcx.impl_defaultness(item.id.owner_id) { - if tcx.type_of(item.id.owner_id) == found { + let assoc_ty = tcx.bound_type_of(item.id.owner_id).subst_identity(); + if self.infcx.can_eq(param_env, assoc_ty, found).is_ok() { diag.span_label( item.span, "associated type defaults can't be assumed inside the \ @@ -547,7 +552,9 @@ fn foo(&tcx) -> Self::T { String::new() } })) => { for item in &items[..] { if let hir::AssocItemKind::Type = item.kind { - if tcx.type_of(item.id.owner_id) == found { + let assoc_ty = tcx.bound_type_of(item.id.owner_id).subst_identity(); + + if self.infcx.can_eq(param_env, assoc_ty, found).is_ok() { diag.span_label(item.span, "expected this associated type"); return true; } diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 9d4ee22a727..28b9bdf5660 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -441,6 +441,10 @@ impl<'tcx> TyCtxt<'tcx> { self.opt_def_kind(def_id) .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id)) } + + pub fn bound_type_of(self, def_id: impl IntoQueryParam) -> ty::EarlyBinder> { + ty::EarlyBinder(self.type_of(def_id)) + } } impl<'tcx> TyCtxtAt<'tcx> { @@ -449,4 +453,8 @@ impl<'tcx> TyCtxtAt<'tcx> { self.opt_def_kind(def_id) .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id)) } + + pub fn bound_type_of(self, def_id: impl IntoQueryParam) -> ty::EarlyBinder> { + ty::EarlyBinder(self.type_of(def_id)) + } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 60076c8cb5f..95abbb50380 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -3,7 +3,6 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; use crate::ty::layout::IntegerExt; -use crate::ty::query::TyCtxtAt; use crate::ty::{ self, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, @@ -637,10 +636,6 @@ impl<'tcx> TyCtxt<'tcx> { if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) } } - pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder> { - ty::EarlyBinder(self.type_of(def_id)) - } - pub fn bound_return_position_impl_trait_in_trait_tys( self, def_id: DefId, @@ -738,12 +733,6 @@ impl<'tcx> TyCtxt<'tcx> { } } -impl<'tcx> TyCtxtAt<'tcx> { - pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder> { - ty::EarlyBinder(self.type_of(def_id)) - } -} - struct OpaqueTypeExpander<'tcx> { // Contains the DefIds of the opaque types that are currently being // expanded. When we expand an opaque type we insert the DefId of diff --git a/tests/ui/associated-types/defaults-in-other-trait-items.rs b/tests/ui/associated-types/defaults-in-other-trait-items.rs index 505751969b6..f263809552f 100644 --- a/tests/ui/associated-types/defaults-in-other-trait-items.rs +++ b/tests/ui/associated-types/defaults-in-other-trait-items.rs @@ -44,4 +44,18 @@ impl AssocConst for () { const C: Self::Ty = 0u8; } +pub trait Trait { + type Res = isize; //~ NOTE associated type defaults can't be assumed inside the trait defining them + + fn infer_me_correctly() -> Self::Res { + //~^ NOTE expected `::Res` because of return type + + // {integer} == isize + 2 + //~^ ERROR mismatched types + //~| NOTE expected associated type, found integer + //~| NOTE expected associated type `::Res` + } +} + fn main() {} diff --git a/tests/ui/associated-types/defaults-in-other-trait-items.stderr b/tests/ui/associated-types/defaults-in-other-trait-items.stderr index 71d421926e7..bdcfadd3955 100644 --- a/tests/ui/associated-types/defaults-in-other-trait-items.stderr +++ b/tests/ui/associated-types/defaults-in-other-trait-items.stderr @@ -24,6 +24,21 @@ LL | const C: Self::Ty = 0u8; = note: expected associated type `::Ty` found type `u8` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/defaults-in-other-trait-items.rs:54:9 + | +LL | type Res = isize; + | ----------------- associated type defaults can't be assumed inside the trait defining them +LL | +LL | fn infer_me_correctly() -> Self::Res { + | --------- expected `::Res` because of return type +... +LL | 2 + | ^ expected associated type, found integer + | + = note: expected associated type `::Res` + found type `{integer}` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-types/issue-26681.stderr b/tests/ui/associated-types/issue-26681.stderr index 74411008c9d..977620d9052 100644 --- a/tests/ui/associated-types/issue-26681.stderr +++ b/tests/ui/associated-types/issue-26681.stderr @@ -1,13 +1,13 @@ error[E0308]: mismatched types --> $DIR/issue-26681.rs:17:39 | +LL | type Fv: Foo = u8; + | ------------------ associated type defaults can't be assumed inside the trait defining them LL | const C: ::Bar = 6665; | ^^^^ expected associated type, found integer | = note: expected associated type `<::Fv as Foo>::Bar` found type `{integer}` - = help: consider constraining the associated type `<::Fv as Foo>::Bar` to `{integer}` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to previous error From 3016f55579bfb3a6a130eb75ddfcc699a64f0477 Mon Sep 17 00:00:00 2001 From: Matthew J Perez Date: Wed, 25 Jan 2023 17:10:26 +0000 Subject: [PATCH 12/12] improve fn pointer notes - add note and suggestion for casting both expected and found fn items to fn pointers - add note for casting expected fn item to fn pointer --- .../src/infer/error_reporting/suggest.rs | 52 ++++++++++++++++--- tests/ui/fn/fn-compare-mismatch.stderr | 1 + tests/ui/fn/fn-item-type.stderr | 6 ++- tests/ui/fn/fn-pointer-mismatch.stderr | 3 ++ 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index eb7bd7256c6..768cef89f3c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -380,7 +380,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return; } - let (msg, sugg) = match (expected.is_ref(), found.is_ref()) { + let (msg, sug) = match (expected.is_ref(), found.is_ref()) { (true, false) => { let msg = "consider using a reference"; let sug = format!("&{fn_name}"); @@ -404,7 +404,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (msg, sug) } }; - diag.span_suggestion(span, msg, &sugg, Applicability::MaybeIncorrect); + diag.span_suggestion(span, msg, sug, Applicability::MaybeIncorrect); } (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { let expected_sig = @@ -412,14 +412,50 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let found_sig = &(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2)); - if self.same_type_modulo_infer(*found_sig, *expected_sig) { - diag.note( - "different fn items have unique types, even if their signatures are the same", - ); + if self.same_type_modulo_infer(*expected_sig, *found_sig) { + diag.note("different fn items have unique types, even if their signatures are the same"); } + + if !self.same_type_modulo_infer(*found_sig, *expected_sig) + || !found_sig.is_suggestable(self.tcx, true) + || !expected_sig.is_suggestable(self.tcx, true) + || ty::util::is_intrinsic(self.tcx, *did1) + || ty::util::is_intrinsic(self.tcx, *did2) + { + return; + } + + let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2); + let sug = if found.is_ref() { + format!("&({fn_name} as {found_sig})") + } else { + format!("{fn_name} as {found_sig}") + }; + + let msg = format!( + "consider casting both fn items to fn pointers using `as {expected_sig}`" + ); + + diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect); } - (ty::FnDef(_, _), ty::FnPtr(_)) => { - diag.note("fn items are distinct from fn pointers"); + (ty::FnDef(did, substs), ty::FnPtr(sig)) => { + let expected_sig = + &(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did).subst(self.tcx, substs)); + let found_sig = &(self.normalize_fn_sig)(*sig); + + if !self.same_type_modulo_infer(*found_sig, *expected_sig) { + return; + } + + let fn_name = self.tcx.def_path_str_with_substs(*did, substs); + + let casting = if expected.is_ref() { + format!("&({fn_name} as {found_sig})") + } else { + format!("{fn_name} as {found_sig}") + }; + + diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting)); } _ => { return; diff --git a/tests/ui/fn/fn-compare-mismatch.stderr b/tests/ui/fn/fn-compare-mismatch.stderr index f247ff6cf3f..b4e71e75fdb 100644 --- a/tests/ui/fn/fn-compare-mismatch.stderr +++ b/tests/ui/fn/fn-compare-mismatch.stderr @@ -20,6 +20,7 @@ LL | let x = f == g; = note: expected fn item `fn() {f}` found fn item `fn() {g}` = note: different fn items have unique types, even if their signatures are the same + = help: consider casting both fn items to fn pointers using `as fn()` error: aborting due to 2 previous errors diff --git a/tests/ui/fn/fn-item-type.stderr b/tests/ui/fn/fn-item-type.stderr index cb1b88c7ab8..9d41243ef11 100644 --- a/tests/ui/fn/fn-item-type.stderr +++ b/tests/ui/fn/fn-item-type.stderr @@ -14,6 +14,7 @@ note: function defined here | LL | fn eq(x: T, y: T) {} | ^^ ---- + = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:29:19 @@ -31,6 +32,7 @@ note: function defined here | LL | fn eq(x: T, y: T) {} | ^^ ---- + = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:34:23 @@ -48,6 +50,7 @@ note: function defined here | LL | fn eq(x: T, y: T) {} | ^^ ---- + = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:41:26 @@ -65,6 +68,7 @@ note: function defined here | LL | fn eq(x: T, y: T) {} | ^^ ---- + = help: consider casting both fn items to fn pointers using `as fn()` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:46:19 @@ -76,7 +80,7 @@ LL | eq(foo::, bar:: as fn(isize) -> isize); | = note: expected fn item `fn(_) -> _ {foo::}` found fn pointer `fn(_) -> _` - = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo:: as fn(isize) -> isize` note: function defined here --> $DIR/fn-item-type.rs:11:4 | diff --git a/tests/ui/fn/fn-pointer-mismatch.stderr b/tests/ui/fn/fn-pointer-mismatch.stderr index 2dc0710e27e..e0bd60fbc0b 100644 --- a/tests/ui/fn/fn-pointer-mismatch.stderr +++ b/tests/ui/fn/fn-pointer-mismatch.stderr @@ -9,6 +9,7 @@ LL | let g = if n % 2 == 0 { &foo } else { &bar }; = note: expected reference `&fn(u32) -> u32 {foo}` found reference `&fn(u32) -> u32 {bar}` = note: different fn items have unique types, even if their signatures are the same + = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32` error[E0308]: mismatched types --> $DIR/fn-pointer-mismatch.rs:23:9 @@ -21,6 +22,7 @@ LL | a = bar; = note: expected fn item `fn(_) -> _ {foo}` found fn item `fn(_) -> _ {bar}` = note: different fn items have unique types, even if their signatures are the same + = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32` error[E0308]: mismatched types --> $DIR/fn-pointer-mismatch.rs:31:18 @@ -35,6 +37,7 @@ LL | b = Box::new(bar); = note: different fn items have unique types, even if their signatures are the same note: associated function defined here --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32` error[E0308]: mismatched types --> $DIR/fn-pointer-mismatch.rs:36:29