From 38fc918a35eec579100fe0d3320338722aba31ab Mon Sep 17 00:00:00 2001 From: Petr Sumbera Date: Thu, 21 Nov 2024 13:28:34 +0100 Subject: [PATCH 1/8] Updates Solaris target information, adds Solaris maintainer --- .../src/spec/targets/sparcv9_sun_solaris.rs | 2 +- .../src/spec/targets/x86_64_pc_solaris.rs | 2 +- src/doc/rustc/src/SUMMARY.md | 2 ++ src/doc/rustc/src/platform-support.md | 4 +-- src/doc/rustc/src/platform-support/solaris.md | 32 +++++++++++++++++++ 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 src/doc/rustc/src/platform-support/solaris.md diff --git a/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs index fdc9628f78e8b..770da60da9e00 100644 --- a/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "sparcv9-sun-solaris".into(), metadata: crate::spec::TargetMetadata { - description: Some("SPARC Solaris 11, illumos".into()), + description: Some("SPARC Solaris 11.4".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs index 28dd3c426c7f7..843568a479273 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "x86_64-pc-solaris".into(), metadata: crate::spec::TargetMetadata { - description: Some("64-bit Solaris 11, illumos".into()), + description: Some("64-bit Solaris 11.4".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index f3d8a4edd6cd4..2341d25ff096c 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -75,6 +75,7 @@ - [s390x-unknown-linux-gnu](platform-support/s390x-unknown-linux-gnu.md) - [s390x-unknown-linux-musl](platform-support/s390x-unknown-linux-musl.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) + - [sparcv9-sun-solaris](platform-support/solaris.md) - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - [*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) @@ -93,6 +94,7 @@ - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md) - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) + - [x86_64-pc-solaris](platform-support/solaris.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 243cb3b2fc832..dda918e697281 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -180,7 +180,7 @@ target | std | notes `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA) `riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA) `sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4, glibc 2.23) -`sparcv9-sun-solaris` | ✓ | SPARC Solaris 11, illumos +[`sparcv9-sun-solaris`](platform-support/solaris.md) | ✓ | SPARC V9 Solaris 11.4 [`thumbv6m-none-eabi`](platform-support/thumbv6m-none-eabi.md) | * | Bare Armv6-M [`thumbv7em-none-eabi`](platform-support/thumbv7em-none-eabi.md) | * | Bare Armv7E-M [`thumbv7em-none-eabihf`](platform-support/thumbv7em-none-eabi.md) | * | Bare Armv7E-M, hardfloat @@ -201,7 +201,7 @@ target | std | notes [`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android -`x86_64-pc-solaris` | ✓ | 64-bit Solaris 11, illumos +[`x86_64-pc-solaris`](platform-support/solaris.md) | ✓ | 64-bit x86 Solaris 11.4 [`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 64-bit x86 MinGW (Windows 10+), LLVM ABI `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27) [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | x86_64 OpenHarmony diff --git a/src/doc/rustc/src/platform-support/solaris.md b/src/doc/rustc/src/platform-support/solaris.md new file mode 100644 index 0000000000000..1e0a241f8405a --- /dev/null +++ b/src/doc/rustc/src/platform-support/solaris.md @@ -0,0 +1,32 @@ +# sparcv9-sun-solaris +# x86_64-pc-solaris + +**Tier: 2** + +Rust for Solaris operating system. + +## Target maintainers + +- Petr Sumbera `sumbera@volny.cz`, https://github.com/psumbera + +## Requirements + +Binary built for this target is expected to run on sparcv9 or x86_64, and Solaris 11.4. + +## Testing + +For testing you can download Oracle Solaris 11.4 CBE release from: + + https://www.oracle.com/uk/solaris/solaris11/downloads/solaris-downloads.html + +Solaris CBE release is also available for GitHub CI: + + https://github.com/vmactions/solaris-vm + +Latest Solaris 11.4 SRU can be tested at Compile farm project: + + https://portal.cfarm.net/machines/list/ (cfarm215, cfarm215) + +There are no official Rust binaries for Solaris available for Rustup yet. But you can eventually download unofficial from: + + https://github.com/psumbera/solaris-rust From 25a7fdf5bc68758fb1a12e2f5ee7fbce0e6a70bb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Nov 2024 18:10:47 +0000 Subject: [PATCH 2/8] Dont use span as key when collecting missing associated types from dyn --- .../src/hir_ty_lowering/dyn_compatibility.rs | 41 ++- .../src/hir_ty_lowering/errors.rs | 242 +++++++++--------- 2 files changed, 134 insertions(+), 149 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index d3c86989440e6..6e5f241505777 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -1,9 +1,8 @@ -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; use rustc_middle::span_bug; use rustc_middle::ty::fold::BottomUpFolder; @@ -11,7 +10,7 @@ use rustc_middle::ty::{ self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations}; use rustc_type_ir::elaborate::ClauseWithSupertraitSpan; @@ -128,8 +127,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - let mut associated_types: FxIndexMap> = FxIndexMap::default(); + let mut needed_associated_types = FxIndexSet::default(); + let principal_span = regular_traits.first().map_or(DUMMY_SP, |info| info.bottom().1); let regular_traits_refs_spans = trait_bounds .into_iter() .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); @@ -146,11 +146,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { let pred = bound_predicate.rebind(pred); - associated_types.entry(original_span).or_default().extend( + needed_associated_types.extend( tcx.associated_items(pred.def_id()) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .filter(|item| !item.is_impl_trait_in_trait()) + // If the associated type has a `where Self: Sized` bound, + // we do not need to constrain the associated type. + .filter(|item| !tcx.generics_require_sized_self(item.def_id)) .map(|item| item.def_id), ); } @@ -201,26 +204,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a // corresponding `Projection` clause - for def_ids in associated_types.values_mut() { - for (projection_bound, span) in &projection_bounds { - let def_id = projection_bound.item_def_id(); - def_ids.swap_remove(&def_id); - if tcx.generics_require_sized_self(def_id) { - tcx.emit_node_span_lint( - UNUSED_ASSOCIATED_TYPE_BOUNDS, - hir_id, - *span, - crate::errors::UnusedAssociatedTypeBounds { span: *span }, - ); - } + for (projection_bound, span) in &projection_bounds { + let def_id = projection_bound.item_def_id(); + needed_associated_types.swap_remove(&def_id); + if tcx.generics_require_sized_self(def_id) { + tcx.emit_node_span_lint( + UNUSED_ASSOCIATED_TYPE_BOUNDS, + hir_id, + *span, + crate::errors::UnusedAssociatedTypeBounds { span: *span }, + ); } - // If the associated type has a `where Self: Sized` bound, we do not need to constrain the associated - // type in the `dyn Trait`. - def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id)); } if let Err(guar) = self.check_for_required_assoc_tys( - associated_types, + principal_span, + needed_associated_types, potential_assoc_types, hir_trait_bounds, ) { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 2e227ead14a93..3c40d33e8ca89 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -722,51 +722,40 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// emit a generic note suggesting using a `where` clause to constraint instead. pub(crate) fn check_for_required_assoc_tys( &self, - associated_types: FxIndexMap>, + principal_span: Span, + missing_assoc_types: FxIndexSet, potential_assoc_types: Vec, trait_bounds: &[hir::PolyTraitRef<'_>], ) -> Result<(), ErrorGuaranteed> { - if associated_types.values().all(|v| v.is_empty()) { + if missing_assoc_types.is_empty() { return Ok(()); } let tcx = self.tcx(); - // FIXME: Marked `mut` so that we can replace the spans further below with a more - // appropriate one, but this should be handled earlier in the span assignment. - let associated_types: FxIndexMap> = associated_types - .into_iter() - .map(|(span, def_ids)| { - (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) - }) - .collect(); + let missing_assoc_types: Vec<_> = + missing_assoc_types.into_iter().map(|def_id| tcx.associated_item(def_id)).collect(); let mut names: FxIndexMap> = Default::default(); let mut names_len = 0; // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and // `issue-22560.rs`. - let mut trait_bound_spans: Vec = vec![]; let mut dyn_compatibility_violations = Ok(()); - for (span, items) in &associated_types { - if !items.is_empty() { - trait_bound_spans.push(*span); - } - for assoc_item in items { - let trait_def_id = assoc_item.container_id(tcx); - names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name); - names_len += 1; - - let violations = - dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item); - if !violations.is_empty() { - dyn_compatibility_violations = Err(report_dyn_incompatibility( - tcx, - *span, - None, - trait_def_id, - &violations, - ) - .emit()); - } + for assoc_item in &missing_assoc_types { + let trait_def_id = assoc_item.container_id(tcx); + names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name); + names_len += 1; + + let violations = + dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item); + if !violations.is_empty() { + dyn_compatibility_violations = Err(report_dyn_incompatibility( + tcx, + principal_span, + None, + trait_def_id, + &violations, + ) + .emit()); } } @@ -827,10 +816,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { names.sort(); let names = names.join(", "); - trait_bound_spans.sort(); let mut err = struct_span_code_err!( self.dcx(), - trait_bound_spans, + principal_span, E0191, "the value of the associated type{} {} must be specified", pluralize!(names_len), @@ -840,81 +828,81 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut types_count = 0; let mut where_constraints = vec![]; let mut already_has_generics_args_suggestion = false; - for (span, assoc_items) in &associated_types { - let mut names: UnordMap<_, usize> = Default::default(); - for item in assoc_items { - types_count += 1; - *names.entry(item.name).or_insert(0) += 1; - } - let mut dupes = false; - let mut shadows = false; - for item in assoc_items { - let prefix = if names[&item.name] > 1 { - let trait_def_id = item.container_id(tcx); - dupes = true; - format!("{}::", tcx.def_path_str(trait_def_id)) - } else if bound_names.get(&item.name).is_some_and(|x| x != &item) { - let trait_def_id = item.container_id(tcx); - shadows = true; - format!("{}::", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - let mut is_shadowed = false; - - if let Some(assoc_item) = bound_names.get(&item.name) - && assoc_item != &item - { - is_shadowed = true; + let mut names: UnordMap<_, usize> = Default::default(); + for item in &missing_assoc_types { + types_count += 1; + *names.entry(item.name).or_insert(0) += 1; + } + let mut dupes = false; + let mut shadows = false; + for item in &missing_assoc_types { + let prefix = if names[&item.name] > 1 { + let trait_def_id = item.container_id(tcx); + dupes = true; + format!("{}::", tcx.def_path_str(trait_def_id)) + } else if bound_names.get(&item.name).is_some_and(|x| *x != item) { + let trait_def_id = item.container_id(tcx); + shadows = true; + format!("{}::", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; - let rename_message = - if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" }; - err.span_label( - tcx.def_span(assoc_item.def_id), - format!("`{}{}` shadowed here{}", prefix, item.name, rename_message), - ); - } + let mut is_shadowed = false; - let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; + if let Some(assoc_item) = bound_names.get(&item.name) + && *assoc_item != item + { + is_shadowed = true; - if let Some(sp) = tcx.hir().span_if_local(item.def_id) { - err.span_label( - sp, - format!("`{}{}` defined here{}", prefix, item.name, rename_message), - ); - } + let rename_message = + if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" }; + err.span_label( + tcx.def_span(assoc_item.def_id), + format!("`{}{}` shadowed here{}", prefix, item.name, rename_message), + ); } - if potential_assoc_types.len() == assoc_items.len() { - // When the amount of missing associated types equals the number of - // extra type arguments present. A suggesting to replace the generic args with - // associated types is already emitted. - already_has_generics_args_suggestion = true; - } else if let (Ok(snippet), false, false) = - (tcx.sess.source_map().span_to_snippet(*span), dupes, shadows) - { - let types: Vec<_> = - assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect(); - let code = if snippet.ends_with('>') { - // The user wrote `Trait<'a>` or similar and we don't have a type we can - // suggest, but at least we can clue them to the correct syntax - // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the - // suggestion. - format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) - } else if in_expr_or_pat { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator::`. - format!("{}::<{}>", snippet, types.join(", ")) - } else { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator`. - format!("{}<{}>", snippet, types.join(", ")) - }; - suggestions.push((*span, code)); - } else if dupes { - where_constraints.push(*span); + + let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; + + if let Some(sp) = tcx.hir().span_if_local(item.def_id) { + err.span_label( + sp, + format!("`{}{}` defined here{}", prefix, item.name, rename_message), + ); } } + if potential_assoc_types.len() == missing_assoc_types.len() { + // When the amount of missing associated types equals the number of + // extra type arguments present. A suggesting to replace the generic args with + // associated types is already emitted. + already_has_generics_args_suggestion = true; + } else if let (Ok(snippet), false, false) = + (tcx.sess.source_map().span_to_snippet(principal_span), dupes, shadows) + { + let types: Vec<_> = + missing_assoc_types.iter().map(|item| format!("{} = Type", item.name)).collect(); + let code = if snippet.ends_with('>') { + // The user wrote `Trait<'a>` or similar and we don't have a type we can + // suggest, but at least we can clue them to the correct syntax + // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the + // suggestion. + format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) + } else if in_expr_or_pat { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator::`. + format!("{}::<{}>", snippet, types.join(", ")) + } else { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator`. + format!("{}<{}>", snippet, types.join(", ")) + }; + suggestions.push((principal_span, code)); + } else if dupes { + where_constraints.push(principal_span); + } + let where_msg = "consider introducing a new type parameter, adding `where` constraints \ using the fully-qualified path to the associated types"; if !where_constraints.is_empty() && suggestions.is_empty() { @@ -925,32 +913,30 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if suggestions.len() != 1 || already_has_generics_args_suggestion { // We don't need this label if there's an inline suggestion, show otherwise. - for (span, assoc_items) in &associated_types { - let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); - for item in assoc_items { - types_count += 1; - *names.entry(item.name).or_insert(0) += 1; - } - let mut label = vec![]; - for item in assoc_items { - let postfix = if names[&item.name] > 1 { - let trait_def_id = item.container_id(tcx); - format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - label.push(format!("`{}`{}", item.name, postfix)); - } - if !label.is_empty() { - err.span_label( - *span, - format!( - "associated type{} {} must be specified", - pluralize!(label.len()), - label.join(", "), - ), - ); - } + let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); + for item in &missing_assoc_types { + types_count += 1; + *names.entry(item.name).or_insert(0) += 1; + } + let mut label = vec![]; + for item in &missing_assoc_types { + let postfix = if names[&item.name] > 1 { + let trait_def_id = item.container_id(tcx); + format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; + label.push(format!("`{}`{}", item.name, postfix)); + } + if !label.is_empty() { + err.span_label( + principal_span, + format!( + "associated type{} {} must be specified", + pluralize!(label.len()), + label.join(", "), + ), + ); } } suggestions.sort_by_key(|&(span, _)| span); From 43e2fd5086debfa74f33f6c323d21a756abb8236 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Nov 2024 18:46:50 +0000 Subject: [PATCH 3/8] Fix ICE when multiple supertrait substitutions need assoc but only one is provided --- .../src/hir_ty_lowering/dyn_compatibility.rs | 13 ++++-- .../src/hir_ty_lowering/errors.rs | 42 ++++++++++--------- .../missing-associated-types.stderr | 4 +- .../ui/dyn-compatibility/assoc_type_bounds.rs | 4 +- .../assoc_type_bounds.stderr | 4 +- .../dyn-compatibility/assoc_type_bounds2.rs | 4 +- .../assoc_type_bounds2.stderr | 4 +- .../require-assoc-for-all-super-substs.rs | 15 +++++++ .../require-assoc-for-all-super-substs.stderr | 12 ++++++ tests/ui/issues/issue-28344.stderr | 4 +- 10 files changed, 71 insertions(+), 35 deletions(-) create mode 100644 tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.rs create mode 100644 tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 6e5f241505777..71a5727ed6cdb 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -145,16 +145,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { - let pred = bound_predicate.rebind(pred); + // FIXME(negative_bounds): Handle this correctly... + let trait_ref = + tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref)); needed_associated_types.extend( - tcx.associated_items(pred.def_id()) + tcx.associated_items(trait_ref.def_id()) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .filter(|item| !item.is_impl_trait_in_trait()) // If the associated type has a `where Self: Sized` bound, // we do not need to constrain the associated type. .filter(|item| !tcx.generics_require_sized_self(item.def_id)) - .map(|item| item.def_id), + .map(|item| (item.def_id, trait_ref)), ); } ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { @@ -206,7 +208,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // corresponding `Projection` clause for (projection_bound, span) in &projection_bounds { let def_id = projection_bound.item_def_id(); - needed_associated_types.swap_remove(&def_id); + let trait_ref = tcx.anonymize_bound_vars( + projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)), + ); + needed_associated_types.swap_remove(&(def_id, trait_ref)); if tcx.generics_require_sized_self(def_id) { tcx.emit_node_span_lint( UNUSED_ASSOCIATED_TYPE_BOUNDS, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 3c40d33e8ca89..a66b0b322382b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -723,7 +723,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(crate) fn check_for_required_assoc_tys( &self, principal_span: Span, - missing_assoc_types: FxIndexSet, + missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>, potential_assoc_types: Vec, trait_bounds: &[hir::PolyTraitRef<'_>], ) -> Result<(), ErrorGuaranteed> { @@ -732,27 +732,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } let tcx = self.tcx(); - let missing_assoc_types: Vec<_> = - missing_assoc_types.into_iter().map(|def_id| tcx.associated_item(def_id)).collect(); - let mut names: FxIndexMap> = Default::default(); + // FIXME: This logic needs some more care w.r.t handling of conflicts + let missing_assoc_types: Vec<_> = missing_assoc_types + .into_iter() + .map(|(def_id, trait_ref)| (tcx.associated_item(def_id), trait_ref)) + .collect(); + let mut names: FxIndexMap<_, Vec> = Default::default(); let mut names_len = 0; // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and // `issue-22560.rs`. let mut dyn_compatibility_violations = Ok(()); - for assoc_item in &missing_assoc_types { - let trait_def_id = assoc_item.container_id(tcx); - names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name); + for (assoc_item, trait_ref) in &missing_assoc_types { + names.entry(trait_ref).or_default().push(assoc_item.name); names_len += 1; let violations = - dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item); + dyn_compatibility_violations_for_assoc_item(tcx, trait_ref.def_id(), *assoc_item); if !violations.is_empty() { dyn_compatibility_violations = Err(report_dyn_incompatibility( tcx, principal_span, None, - trait_def_id, + trait_ref.def_id(), &violations, ) .emit()); @@ -803,6 +805,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .into_iter() .map(|(trait_, mut assocs)| { assocs.sort(); + let trait_ = trait_.print_trait_sugared(); format!("{} in `{trait_}`", match &assocs[..] { [] => String::new(), [only] => format!("`{only}`"), @@ -830,19 +833,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut already_has_generics_args_suggestion = false; let mut names: UnordMap<_, usize> = Default::default(); - for item in &missing_assoc_types { + for (item, _) in &missing_assoc_types { types_count += 1; *names.entry(item.name).or_insert(0) += 1; } let mut dupes = false; let mut shadows = false; - for item in &missing_assoc_types { + for (item, trait_ref) in &missing_assoc_types { let prefix = if names[&item.name] > 1 { - let trait_def_id = item.container_id(tcx); + let trait_def_id = trait_ref.def_id(); dupes = true; format!("{}::", tcx.def_path_str(trait_def_id)) } else if bound_names.get(&item.name).is_some_and(|x| *x != item) { - let trait_def_id = item.container_id(tcx); + let trait_def_id = trait_ref.def_id(); shadows = true; format!("{}::", tcx.def_path_str(trait_def_id)) } else { @@ -881,8 +884,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else if let (Ok(snippet), false, false) = (tcx.sess.source_map().span_to_snippet(principal_span), dupes, shadows) { - let types: Vec<_> = - missing_assoc_types.iter().map(|item| format!("{} = Type", item.name)).collect(); + let types: Vec<_> = missing_assoc_types + .iter() + .map(|(item, _)| format!("{} = Type", item.name)) + .collect(); let code = if snippet.ends_with('>') { // The user wrote `Trait<'a>` or similar and we don't have a type we can // suggest, but at least we can clue them to the correct syntax @@ -914,15 +919,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if suggestions.len() != 1 || already_has_generics_args_suggestion { // We don't need this label if there's an inline suggestion, show otherwise. let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); - for item in &missing_assoc_types { + for (item, _) in &missing_assoc_types { types_count += 1; *names.entry(item.name).or_insert(0) += 1; } let mut label = vec![]; - for item in &missing_assoc_types { + for (item, trait_ref) in &missing_assoc_types { let postfix = if names[&item.name] > 1 { - let trait_def_id = item.container_id(tcx); - format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) + format!(" (from trait `{}`)", trait_ref.print_trait_sugared()) } else { String::new() }; diff --git a/tests/ui/associated-types/missing-associated-types.stderr b/tests/ui/associated-types/missing-associated-types.stderr index ce4b57e8af813..3a56c55896ebf 100644 --- a/tests/ui/associated-types/missing-associated-types.stderr +++ b/tests/ui/associated-types/missing-associated-types.stderr @@ -42,11 +42,11 @@ LL | type Bat = dyn Add + Sub + Fine; = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Add + Sub + Fine {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit -error[E0191]: the value of the associated types `Output` in `Div`, `Output` in `Mul` must be specified +error[E0191]: the value of the associated types `Output` in `Div`, `Output` in `Mul` must be specified --> $DIR/missing-associated-types.rs:20:21 | LL | type Bal = dyn X; - | ^^^^^^ associated types `Output` (from trait `Div`), `Output` (from trait `Mul`) must be specified + | ^^^^^^ associated types `Output` (from trait `Div`), `Output` (from trait `Mul`) must be specified | = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds.rs b/tests/ui/dyn-compatibility/assoc_type_bounds.rs index 8634ba626a18a..6e2076a482247 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds.rs +++ b/tests/ui/dyn-compatibility/assoc_type_bounds.rs @@ -7,7 +7,7 @@ trait Foo { trait Cake {} impl Cake for () {} -fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified -fn bar(_: &dyn Foo) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified +fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` in `Foo<()>` must be specified +fn bar(_: &dyn Foo) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified fn main() {} diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds.stderr b/tests/ui/dyn-compatibility/assoc_type_bounds.stderr index 3d5482625af3e..21ba903011739 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds.stderr +++ b/tests/ui/dyn-compatibility/assoc_type_bounds.stderr @@ -1,4 +1,4 @@ -error[E0191]: the value of the associated type `Bar` in `Foo` must be specified +error[E0191]: the value of the associated type `Bar` in `Foo<()>` must be specified --> $DIR/assoc_type_bounds.rs:10:16 | LL | type Bar @@ -7,7 +7,7 @@ LL | type Bar LL | fn foo(_: &dyn Foo<()>) {} | ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>` -error[E0191]: the value of the associated type `Bar` in `Foo` must be specified +error[E0191]: the value of the associated type `Bar` in `Foo` must be specified --> $DIR/assoc_type_bounds.rs:11:16 | LL | type Bar diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds2.rs b/tests/ui/dyn-compatibility/assoc_type_bounds2.rs index f7dc2fb88390f..2b35016d774ce 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds2.rs +++ b/tests/ui/dyn-compatibility/assoc_type_bounds2.rs @@ -7,7 +7,7 @@ trait Foo { trait Cake {} impl Cake for () {} -fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified -fn bar(_: &dyn Foo) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified +fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` in `Foo<()>` must be specified +fn bar(_: &dyn Foo) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified fn main() {} diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds2.stderr b/tests/ui/dyn-compatibility/assoc_type_bounds2.stderr index 815747436bf0d..5c4163b196936 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds2.stderr +++ b/tests/ui/dyn-compatibility/assoc_type_bounds2.stderr @@ -1,4 +1,4 @@ -error[E0191]: the value of the associated type `Bar` in `Foo` must be specified +error[E0191]: the value of the associated type `Bar` in `Foo<()>` must be specified --> $DIR/assoc_type_bounds2.rs:10:16 | LL | type Bar @@ -7,7 +7,7 @@ LL | type Bar LL | fn foo(_: &dyn Foo<()>) {} | ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>` -error[E0191]: the value of the associated type `Bar` in `Foo` must be specified +error[E0191]: the value of the associated type `Bar` in `Foo` must be specified --> $DIR/assoc_type_bounds2.rs:11:16 | LL | type Bar diff --git a/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.rs b/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.rs new file mode 100644 index 0000000000000..1f4e1bf653a3f --- /dev/null +++ b/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.rs @@ -0,0 +1,15 @@ +trait Sup { + type Assoc: Default; +} + +impl Sup for () { + type Assoc = T; +} +impl Dyn for () {} + +trait Dyn: Sup + Sup {} + +fn main() { + let q: as Sup>::Assoc = Default::default(); + //~^ ERROR the value of the associated type `Assoc` in `Sup` must be specified +} diff --git a/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.stderr b/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.stderr new file mode 100644 index 0000000000000..3d89b52d522db --- /dev/null +++ b/tests/ui/dyn-compatibility/require-assoc-for-all-super-substs.stderr @@ -0,0 +1,12 @@ +error[E0191]: the value of the associated type `Assoc` in `Sup` must be specified + --> $DIR/require-assoc-for-all-super-substs.rs:13:17 + | +LL | type Assoc: Default; + | ------------------- `Assoc` defined here +... +LL | let q: as Sup>::Assoc = Default::default(); + | ^^^^^^^^^^^^^ help: specify the associated type: `Dyn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0191`. diff --git a/tests/ui/issues/issue-28344.stderr b/tests/ui/issues/issue-28344.stderr index d8febe7165241..7bc965536e9a1 100644 --- a/tests/ui/issues/issue-28344.stderr +++ b/tests/ui/issues/issue-28344.stderr @@ -12,7 +12,7 @@ help: if this is a dyn-compatible trait, use `dyn` LL | let x: u8 = ::bitor(0 as u8, 0 as u8); | ++++ + -error[E0191]: the value of the associated type `Output` in `BitXor` must be specified +error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified --> $DIR/issue-28344.rs:4:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); @@ -31,7 +31,7 @@ help: if this is a dyn-compatible trait, use `dyn` LL | let g = ::bitor; | ++++ + -error[E0191]: the value of the associated type `Output` in `BitXor` must be specified +error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified --> $DIR/issue-28344.rs:9:13 | LL | let g = BitXor::bitor; From d82c520f1435e0fe646f749f9e71ceee825180ed Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 13 Dec 2024 15:10:58 +0000 Subject: [PATCH 4/8] Clean up infer_return_ty_for_fn_sig --- compiler/rustc_hir_analysis/src/collect.rs | 179 +++++++++++---------- 1 file changed, 94 insertions(+), 85 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 96d2714252af9..1f322b26af336 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1330,7 +1330,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn .. }) | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => { - infer_return_ty_for_fn_sig(sig, generics, def_id, &icx) + lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id) } ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => { @@ -1347,7 +1347,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn None, ) } else { - infer_return_ty_for_fn_sig(sig, generics, def_id, &icx) + lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id) } } @@ -1397,99 +1397,108 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn ty::EarlyBinder::bind(output) } -fn infer_return_ty_for_fn_sig<'tcx>( - sig: &hir::FnSig<'tcx>, - generics: &hir::Generics<'_>, +fn lower_fn_sig_recovering_infer_ret_ty<'tcx>( + icx: &ItemCtxt<'tcx>, + sig: &'tcx hir::FnSig<'tcx>, + generics: &'tcx hir::Generics<'tcx>, def_id: LocalDefId, +) -> ty::PolyFnSig<'tcx> { + if let Some(infer_ret_ty) = sig.decl.output.get_infer_ret_ty() { + return recover_infer_ret_ty(icx, infer_ret_ty, generics, def_id); + } + + icx.lowerer().lower_fn_ty( + icx.tcx().local_def_id_to_hir_id(def_id), + sig.header.safety, + sig.header.abi, + sig.decl, + Some(generics), + None, + ) +} + +fn recover_infer_ret_ty<'tcx>( icx: &ItemCtxt<'tcx>, + infer_ret_ty: &'tcx hir::Ty<'tcx>, + generics: &'tcx hir::Generics<'tcx>, + def_id: LocalDefId, ) -> ty::PolyFnSig<'tcx> { let tcx = icx.tcx; let hir_id = tcx.local_def_id_to_hir_id(def_id); - match sig.decl.output.get_infer_ret_ty() { - Some(ty) => { - let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; - // Typeck doesn't expect erased regions to be returned from `type_of`. - // This is a heuristic approach. If the scope has region parameters, - // we should change fn_sig's lifetime from `ReErased` to `ReError`, - // otherwise to `ReStatic`. - let has_region_params = generics.params.iter().any(|param| match param.kind { - GenericParamKind::Lifetime { .. } => true, - _ => false, - }); - let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r { - ty::ReErased => { - if has_region_params { - ty::Region::new_error_with_message( - tcx, - DUMMY_SP, - "erased region is not allowed here in return type", - ) - } else { - tcx.lifetimes.re_static - } - } - _ => r, - }); + let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; - let mut visitor = HirPlaceholderCollector::default(); - visitor.visit_ty(ty); - - let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type"); - let ret_ty = fn_sig.output(); - // Don't leak types into signatures unless they're nameable! - // For example, if a function returns itself, we don't want that - // recursive function definition to leak out into the fn sig. - let mut recovered_ret_ty = None; - - if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) { - diag.span_suggestion( - ty.span, - "replace with the correct return type", - suggestable_ret_ty, - Applicability::MachineApplicable, - ); - recovered_ret_ty = Some(suggestable_ret_ty); - } else if let Some(sugg) = suggest_impl_trait( - &tcx.infer_ctxt().build(TypingMode::non_body_analysis()), - tcx.param_env(def_id), - ret_ty, - ) { - diag.span_suggestion( - ty.span, - "replace with an appropriate return type", - sugg, - Applicability::MachineApplicable, - ); - } else if ret_ty.is_closure() { - diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); - } - // Also note how `Fn` traits work just in case! - if ret_ty.is_closure() { - diag.note( - "for more information on `Fn` traits and closure types, see \ - https://doc.rust-lang.org/book/ch13-01-closures.html", - ); + // Typeck doesn't expect erased regions to be returned from `type_of`. + // This is a heuristic approach. If the scope has region parameters, + // we should change fn_sig's lifetime from `ReErased` to `ReError`, + // otherwise to `ReStatic`. + let has_region_params = generics.params.iter().any(|param| match param.kind { + GenericParamKind::Lifetime { .. } => true, + _ => false, + }); + let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r { + ty::ReErased => { + if has_region_params { + ty::Region::new_error_with_message( + tcx, + DUMMY_SP, + "erased region is not allowed here in return type", + ) + } else { + tcx.lifetimes.re_static } - - let guar = diag.emit(); - ty::Binder::dummy(tcx.mk_fn_sig( - fn_sig.inputs().iter().copied(), - recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)), - fn_sig.c_variadic, - fn_sig.safety, - fn_sig.abi, - )) } - None => icx.lowerer().lower_fn_ty( - hir_id, - sig.header.safety, - sig.header.abi, - sig.decl, - Some(generics), - None, - ), + _ => r, + }); + + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_ty(infer_ret_ty); + + let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type"); + let ret_ty = fn_sig.output(); + + // Don't leak types into signatures unless they're nameable! + // For example, if a function returns itself, we don't want that + // recursive function definition to leak out into the fn sig. + let mut recovered_ret_ty = None; + if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) { + diag.span_suggestion( + infer_ret_ty.span, + "replace with the correct return type", + suggestable_ret_ty, + Applicability::MachineApplicable, + ); + recovered_ret_ty = Some(suggestable_ret_ty); + } else if let Some(sugg) = suggest_impl_trait( + &tcx.infer_ctxt().build(TypingMode::non_body_analysis()), + tcx.param_env(def_id), + ret_ty, + ) { + diag.span_suggestion( + infer_ret_ty.span, + "replace with an appropriate return type", + sugg, + Applicability::MachineApplicable, + ); + } else if ret_ty.is_closure() { + diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); + } + + // Also note how `Fn` traits work just in case! + if ret_ty.is_closure() { + diag.note( + "for more information on `Fn` traits and closure types, see \ + https://doc.rust-lang.org/book/ch13-01-closures.html", + ); } + let guar = diag.emit(); + ty::Binder::dummy(tcx.mk_fn_sig( + fn_sig.inputs().iter().copied(), + recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)), + fn_sig.c_variadic, + fn_sig.safety, + fn_sig.abi, + )) } pub fn suggest_impl_trait<'tcx>( From 831f4549cd1b23915729cbd2f1dd841621c4e8b8 Mon Sep 17 00:00:00 2001 From: uellenberg Date: Mon, 11 Nov 2024 19:20:59 -0800 Subject: [PATCH 5/8] Suggest using deref in patterns Fixes #132784 --- compiler/rustc_hir/src/hir.rs | 16 ++ .../src/fn_ctxt/suggestions.rs | 17 +- compiler/rustc_hir_typeck/src/pat.rs | 30 +- compiler/rustc_middle/src/traits/mod.rs | 23 +- .../src/error_reporting/infer/mod.rs | 115 ++++++-- .../src/error_reporting/infer/suggest.rs | 2 +- .../src/error_reporting/traits/suggestions.rs | 11 +- .../2229_closure_analysis/issue-118144.stderr | 5 + .../issue-57741.stderr | 36 +-- .../let-else/let-else-deref-coercion.stderr | 10 + .../suggest-deref-in-match-issue-132784.rs | 84 ++++++ ...suggest-deref-in-match-issue-132784.stderr | 259 ++++++++++++++++++ 12 files changed, 544 insertions(+), 64 deletions(-) create mode 100644 tests/ui/suggestions/suggest-deref-in-match-issue-132784.rs create mode 100644 tests/ui/suggestions/suggest-deref-in-match-issue-132784.stderr diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 8cf18c2b912eb..8225b1189a2c4 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2020,6 +2020,22 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { } } +/// Checks if the specified expression needs parentheses for prefix +/// or postfix suggestions to be valid. +/// For example, `a + b` requires parentheses to suggest `&(a + b)`, +/// but just `a` does not. +/// Similarly, `(a + b).c()` also requires parentheses. +/// This should not be used for other types of suggestions. +pub fn expr_needs_parens(expr: &Expr<'_>) -> bool { + match expr.kind { + // parenthesize if needed (Issue #46756) + ExprKind::Cast(_, _) | ExprKind::Binary(_, _, _) => true, + // parenthesize borrows of range literals (Issue #54505) + _ if is_range_literal(expr) => true, + _ => false, + } +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum ExprKind<'hir> { /// Allow anonymous constants from an inline `const` block diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ddcd90a2a9da6..21b1768ae6f4d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -10,7 +10,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, - Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, + Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, expr_needs_parens, }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -35,7 +35,6 @@ use tracing::{debug, instrument}; use super::FnCtxt; use crate::fn_ctxt::rustc_span::BytePos; -use crate::hir::is_range_literal; use crate::method::probe; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use crate::{errors, fluent_generated as fluent}; @@ -2648,7 +2647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| { - if self.needs_parentheses(expr) { + if expr_needs_parens(expr) { ( vec![ (span.shrink_to_lo(), format!("{prefix}{sugg}(")), @@ -2861,7 +2860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - if self.needs_parentheses(expr) { + if expr_needs_parens(expr) { return Some(( vec![ (span, format!("{suggestion}(")), @@ -2902,16 +2901,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn needs_parentheses(&self, expr: &hir::Expr<'_>) -> bool { - match expr.kind { - // parenthesize if needed (Issue #46756) - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(expr) => true, - _ => false, - } - } - pub(crate) fn suggest_cast( &self, err: &mut Diag<'_>, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 2a8ed26aa2bee..e7726845652f3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -10,8 +10,12 @@ use rustc_errors::{ }; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LangItem, Mutability, Pat, PatKind}; +use rustc_hir::{ + self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatKind, + expr_needs_parens, +}; use rustc_infer::infer; +use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; @@ -94,10 +98,32 @@ struct PatInfo<'a, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { + // If origin_expr exists, then expected represents the type of origin_expr. + // If span also exists, then span == origin_expr.span (although it doesn't need to exist). + // In that case, we can peel away references from both and treat them + // as the same. + let origin_expr_info = ti.origin_expr.map(|mut cur_expr| { + let mut count = 0; + + // cur_ty may have more layers of references than cur_expr. + // We can only make suggestions about cur_expr, however, so we'll + // use that as our condition for stopping. + while let ExprKind::AddrOf(.., inner) = &cur_expr.kind { + cur_expr = inner; + count += 1; + } + + PatternOriginExpr { + peeled_span: cur_expr.span, + peeled_count: count, + peeled_prefix_suggestion_parentheses: expr_needs_parens(cur_expr), + } + }); + let code = ObligationCauseCode::Pattern { span: ti.span, root_ty: ti.expected, - origin_expr: ti.origin_expr.is_some(), + origin_expr: origin_expr_info, }; self.cause(cause_span, code) } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 0a77c3bc42fb9..8c434265d2764 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -316,8 +316,8 @@ pub enum ObligationCauseCode<'tcx> { span: Option, /// The root expected type induced by a scrutinee or type expression. root_ty: Ty<'tcx>, - /// Whether the `Span` came from an expression or a type expression. - origin_expr: bool, + /// Information about the `Span`, if it came from an expression, otherwise `None`. + origin_expr: Option, }, /// Computing common supertype in an if expression @@ -530,6 +530,25 @@ pub struct MatchExpressionArmCause<'tcx> { pub tail_defines_return_position_impl_trait: Option, } +/// Information about the origin expression of a pattern, relevant to diagnostics. +/// Fields here refer to the scrutinee of a pattern. +/// If the scrutinee isn't given in the diagnostic, then this won't exist. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] +pub struct PatternOriginExpr { + /// A span representing the scrutinee expression, with all leading references + /// peeled from the expression. + /// Only references in the expression are peeled - if the expression refers to a variable + /// whose type is a reference, then that reference is kept because it wasn't created + /// in the expression. + pub peeled_span: Span, + /// The number of references that were peeled to produce `peeled_span`. + pub peeled_count: usize, + /// Does the peeled expression need to be wrapped in parentheses for + /// a prefix suggestion (i.e., dereference) to be valid. + pub peeled_prefix_suggestion_parentheses: bool, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] pub struct IfExpressionCause<'tcx> { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index f856a8d7abbe7..5a62a4c3bd5fe 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -63,10 +63,11 @@ use rustc_hir::{self as hir}; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; +use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, with_forced_trimmed_paths}; use rustc_middle::ty::{ - self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, + self, List, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; use rustc_span::def_id::LOCAL_CRATE; @@ -77,7 +78,7 @@ use crate::error_reporting::TypeErrCtxt; use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; use crate::infer::relate::{self, RelateResult, TypeRelation}; -use crate::infer::{InferCtxt, TypeTrace, ValuePairs}; +use crate::infer::{InferCtxt, InferCtxtExt as _, TypeTrace, ValuePairs}; use crate::solve::deeply_normalize_for_diagnostics; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -433,15 +434,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, exp_found: Option>>, terr: TypeError<'tcx>, + param_env: Option>, ) { match *cause.code() { - ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { - let ty = self.resolve_vars_if_possible(root_ty); - if !matches!(ty.kind(), ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_))) - { + ObligationCauseCode::Pattern { + origin_expr: Some(origin_expr), + span: Some(span), + root_ty, + } => { + let expected_ty = self.resolve_vars_if_possible(root_ty); + if !matches!( + expected_ty.kind(), + ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)) + ) { // don't show type `_` if span.desugaring_kind() == Some(DesugaringKind::ForLoop) - && let ty::Adt(def, args) = ty.kind() + && let ty::Adt(def, args) = expected_ty.kind() && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option) { err.span_label( @@ -449,22 +457,48 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { format!("this is an iterator with items of type `{}`", args.type_at(0)), ); } else { - err.span_label(span, format!("this expression has type `{ty}`")); + err.span_label(span, format!("this expression has type `{expected_ty}`")); } } if let Some(ty::error::ExpectedFound { found, .. }) = exp_found - && ty.boxed_ty() == Some(found) - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && let Ok(mut peeled_snippet) = + self.tcx.sess.source_map().span_to_snippet(origin_expr.peeled_span) { - err.span_suggestion( - span, - "consider dereferencing the boxed value", - format!("*{snippet}"), - Applicability::MachineApplicable, - ); + // Parentheses are needed for cases like as casts. + // We use the peeled_span for deref suggestions. + // It's also safe to use for box, since box only triggers if there + // wasn't a reference to begin with. + if origin_expr.peeled_prefix_suggestion_parentheses { + peeled_snippet = format!("({peeled_snippet})"); + } + + // Try giving a box suggestion first, as it is a special case of the + // deref suggestion. + if expected_ty.boxed_ty() == Some(found) { + err.span_suggestion_verbose( + span, + "consider dereferencing the boxed value", + format!("*{peeled_snippet}"), + Applicability::MachineApplicable, + ); + } else if let Some(param_env) = param_env + && let Some(prefix) = self.should_deref_suggestion_on_mismatch( + param_env, + found, + expected_ty, + origin_expr, + ) + { + err.span_suggestion_verbose( + span, + "consider dereferencing to access the inner value using the Deref trait", + format!("{prefix}{peeled_snippet}"), + Applicability::MaybeIncorrect, + ); + } } } - ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => { + ObligationCauseCode::Pattern { origin_expr: None, span: Some(span), .. } => { err.span_label(span, "expected due to this"); } ObligationCauseCode::BlockTailExpression( @@ -618,6 +652,45 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } + /// Determines whether deref_to == ::Target, and if so, + /// returns a prefix that should be added to deref_from as a suggestion. + fn should_deref_suggestion_on_mismatch( + &self, + param_env: ParamEnv<'tcx>, + deref_to: Ty<'tcx>, + deref_from: Ty<'tcx>, + origin_expr: PatternOriginExpr, + ) -> Option { + // origin_expr contains stripped away versions of our expression. + // We'll want to use that to avoid suggesting things like *&x. + // However, the type that we have access to hasn't been stripped away, + // so we need to ignore the first n dereferences, where n is the number + // that's been stripped away in origin_expr. + + // Find a way to autoderef from deref_from to deref_to. + let Some((num_derefs, (after_deref_ty, _))) = (self.autoderef_steps)(deref_from) + .into_iter() + .enumerate() + .find(|(_, (ty, _))| self.infcx.can_eq(param_env, *ty, deref_to)) + else { + return None; + }; + + if num_derefs <= origin_expr.peeled_count { + return None; + } + + let deref_part = "*".repeat(num_derefs - origin_expr.peeled_count); + + // If the user used a reference in the original expression, they probably + // want the suggestion to still give a reference. + if deref_from.is_ref() && !after_deref_ty.is_ref() { + Some(format!("&{deref_part}")) + } else { + Some(deref_part) + } + } + /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value` /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and /// populate `other_value` with `other_ty`. @@ -1406,8 +1479,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Variable(ty::error::ExpectedFound>), Fixed(&'static str), } - let (expected_found, exp_found, is_simple_error, values) = match values { - None => (None, Mismatch::Fixed("type"), false, None), + let (expected_found, exp_found, is_simple_error, values, param_env) = match values { + None => (None, Mismatch::Fixed("type"), false, None, None), Some(ty::ParamEnvAnd { param_env, value: values }) => { let mut values = self.resolve_vars_if_possible(values); if self.next_trait_solver() { @@ -1459,7 +1532,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { diag.downgrade_to_delayed_bug(); return; }; - (Some(vals), exp_found, is_simple_error, Some(values)) + (Some(vals), exp_found, is_simple_error, Some(values), Some(param_env)) } }; @@ -1791,7 +1864,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, cause, exp_found, terr); + self.note_error_origin(diag, cause, exp_found, terr, param_env); debug!(?diag); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index fc2d0ba36f04e..08775df5ac99a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -210,7 +210,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() { ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => { - origin_expr.then_some(ConsiderAddingAwait::FutureSugg { + origin_expr.is_some().then_some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi(), }) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 94682f501a8c4..cc8941b92242d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -20,7 +20,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, is_range_literal, + CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, expr_needs_parens, + is_range_literal, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::hir::map; @@ -1391,13 +1392,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let Some(expr) = expr_finder.result else { return false; }; - let needs_parens = match expr.kind { - // parenthesize if needed (Issue #46756) - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(expr) => true, - _ => false, - }; + let needs_parens = expr_needs_parens(expr); let span = if needs_parens { span } else { span.shrink_to_lo() }; let suggestions = if !needs_parens { diff --git a/tests/ui/closures/2229_closure_analysis/issue-118144.stderr b/tests/ui/closures/2229_closure_analysis/issue-118144.stderr index 85cb5adc07e90..87084e6023729 100644 --- a/tests/ui/closures/2229_closure_analysis/issue-118144.stderr +++ b/tests/ui/closures/2229_closure_analysis/issue-118144.stderr @@ -5,6 +5,11 @@ LL | V(x) = func_arg; | ^^^^ -------- this expression has type `&mut V` | | | expected `&mut V`, found `V` + | +help: consider dereferencing to access the inner value using the Deref trait + | +LL | V(x) = &*func_arg; + | ~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr b/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr index 38014ecce7574..3c19b68cffbb3 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr +++ b/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr @@ -2,57 +2,61 @@ error[E0308]: mismatched types --> $DIR/issue-57741.rs:20:9 | LL | let y = match x { - | - - | | - | this expression has type `Box` - | help: consider dereferencing the boxed value: `*x` + | - this expression has type `Box` LL | T::A(a) | T::B(a) => a, | ^^^^^^^ expected `Box`, found `T` | = note: expected struct `Box` found enum `T` +help: consider dereferencing the boxed value + | +LL | let y = match *x { + | ~~ error[E0308]: mismatched types --> $DIR/issue-57741.rs:20:19 | LL | let y = match x { - | - - | | - | this expression has type `Box` - | help: consider dereferencing the boxed value: `*x` + | - this expression has type `Box` LL | T::A(a) | T::B(a) => a, | ^^^^^^^ expected `Box`, found `T` | = note: expected struct `Box` found enum `T` +help: consider dereferencing the boxed value + | +LL | let y = match *x { + | ~~ error[E0308]: mismatched types --> $DIR/issue-57741.rs:27:9 | LL | let y = match x { - | - - | | - | this expression has type `Box` - | help: consider dereferencing the boxed value: `*x` + | - this expression has type `Box` LL | S::A { a } | S::B { b: a } => a, | ^^^^^^^^^^ expected `Box`, found `S` | = note: expected struct `Box` found enum `S` +help: consider dereferencing the boxed value + | +LL | let y = match *x { + | ~~ error[E0308]: mismatched types --> $DIR/issue-57741.rs:27:22 | LL | let y = match x { - | - - | | - | this expression has type `Box` - | help: consider dereferencing the boxed value: `*x` + | - this expression has type `Box` LL | S::A { a } | S::B { b: a } => a, | ^^^^^^^^^^^^^ expected `Box`, found `S` | = note: expected struct `Box` found enum `S` +help: consider dereferencing the boxed value + | +LL | let y = match *x { + | ~~ error: aborting due to 4 previous errors diff --git a/tests/ui/let-else/let-else-deref-coercion.stderr b/tests/ui/let-else/let-else-deref-coercion.stderr index 143b838bac500..da8b1f4c48e97 100644 --- a/tests/ui/let-else/let-else-deref-coercion.stderr +++ b/tests/ui/let-else/let-else-deref-coercion.stderr @@ -5,6 +5,11 @@ LL | let Bar::Present(z) = self else { | ^^^^^^^^^^^^^^^ ---- this expression has type `&mut Foo` | | | expected `Foo`, found `Bar` + | +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Bar::Present(z) = &**self else { + | ~~~~~~~ error[E0308]: mismatched types --> $DIR/let-else-deref-coercion.rs:68:13 @@ -13,6 +18,11 @@ LL | let Bar(z) = x; | ^^^^^^ - this expression has type `&mut irrefutable::Foo` | | | expected `Foo`, found `Bar` + | +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Bar(z) = &**x; + | ~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/suggest-deref-in-match-issue-132784.rs b/tests/ui/suggestions/suggest-deref-in-match-issue-132784.rs new file mode 100644 index 0000000000000..205e57f4a9ff5 --- /dev/null +++ b/tests/ui/suggestions/suggest-deref-in-match-issue-132784.rs @@ -0,0 +1,84 @@ +use std::sync::Arc; +fn main() { + let mut x = Arc::new(Some(1)); + match x { + //~^ HELP consider dereferencing to access the inner value using the Deref trait + //~| HELP consider dereferencing to access the inner value using the Deref trait + Some(_) => {} + //~^ ERROR mismatched types + None => {} + //~^ ERROR mismatched types + } + + match &x { + //~^ HELP consider dereferencing to access the inner value using the Deref trait + //~| HELP consider dereferencing to access the inner value using the Deref trait + Some(_) => {} + //~^ ERROR mismatched types + None => {} + //~^ ERROR mismatched types + } + + let mut y = Box::new(Some(1)); + match y { + //~^ HELP consider dereferencing to access the inner value using the Deref trait + //~| HELP consider dereferencing to access the inner value using the Deref trait + Some(_) => {} + //~^ ERROR mismatched types + None => {} + //~^ ERROR mismatched types + } + + let mut z = Arc::new(Some(1)); + match z as Arc> { + //~^ HELP consider dereferencing to access the inner value using the Deref trait + //~| HELP consider dereferencing to access the inner value using the Deref trait + Some(_) => {} + //~^ ERROR mismatched types + None => {} + //~^ ERROR mismatched types + } + + let z_const: &Arc> = &z; + match z_const { + //~^ HELP consider dereferencing to access the inner value using the Deref trait + //~| HELP consider dereferencing to access the inner value using the Deref trait + Some(_) => {} + //~^ ERROR mismatched types + None => {} + //~^ ERROR mismatched types + } + + // Normal reference because Arc doesn't implement DerefMut. + let z_mut: &mut Arc> = &mut z; + match z_mut { + //~^ HELP consider dereferencing to access the inner value using the Deref trait + //~| HELP consider dereferencing to access the inner value using the Deref trait + Some(_) => {} + //~^ ERROR mismatched types + None => {} + //~^ ERROR mismatched types + } + + // Mutable reference because Box does implement DerefMut. + let y_mut: &mut Box> = &mut y; + match y_mut { + //~^ HELP consider dereferencing to access the inner value using the Deref trait + //~| HELP consider dereferencing to access the inner value using the Deref trait + Some(_) => {} + //~^ ERROR mismatched types + None => {} + //~^ ERROR mismatched types + } + + // Difficult expression. + let difficult = Arc::new(Some(1)); + match (& (&difficult) ) { + //~^ HELP consider dereferencing to access the inner value using the Deref trait + //~| HELP consider dereferencing to access the inner value using the Deref trait + Some(_) => {} + //~^ ERROR mismatched types + None => {} + //~^ ERROR mismatched types + } +} diff --git a/tests/ui/suggestions/suggest-deref-in-match-issue-132784.stderr b/tests/ui/suggestions/suggest-deref-in-match-issue-132784.stderr new file mode 100644 index 0000000000000..9338ef1908943 --- /dev/null +++ b/tests/ui/suggestions/suggest-deref-in-match-issue-132784.stderr @@ -0,0 +1,259 @@ +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:7:9 + | +LL | match x { + | - this expression has type `Arc>` +... +LL | Some(_) => {} + | ^^^^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *x { + | ~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:9:9 + | +LL | match x { + | - this expression has type `Arc>` +... +LL | None => {} + | ^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *x { + | ~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:16:9 + | +LL | match &x { + | -- this expression has type `&Arc>` +... +LL | Some(_) => {} + | ^^^^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &*x { + | ~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:18:9 + | +LL | match &x { + | -- this expression has type `&Arc>` +... +LL | None => {} + | ^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &*x { + | ~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:26:9 + | +LL | match y { + | - this expression has type `Box>` +... +LL | Some(_) => {} + | ^^^^^^^ expected `Box>`, found `Option<_>` + | + = note: expected struct `Box>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *y { + | ~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:28:9 + | +LL | match y { + | - this expression has type `Box>` +... +LL | None => {} + | ^^^^ expected `Box>`, found `Option<_>` + | + = note: expected struct `Box>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *y { + | ~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:36:9 + | +LL | match z as Arc> { + | --------------------- this expression has type `Arc>` +... +LL | Some(_) => {} + | ^^^^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *(z as Arc>) { + | ~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:38:9 + | +LL | match z as Arc> { + | --------------------- this expression has type `Arc>` +... +LL | None => {} + | ^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *(z as Arc>) { + | ~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:46:9 + | +LL | match z_const { + | ------- this expression has type `&Arc>` +... +LL | Some(_) => {} + | ^^^^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &**z_const { + | ~~~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:48:9 + | +LL | match z_const { + | ------- this expression has type `&Arc>` +... +LL | None => {} + | ^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &**z_const { + | ~~~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:57:9 + | +LL | match z_mut { + | ----- this expression has type `&mut Arc>` +... +LL | Some(_) => {} + | ^^^^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &**z_mut { + | ~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:59:9 + | +LL | match z_mut { + | ----- this expression has type `&mut Arc>` +... +LL | None => {} + | ^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &**z_mut { + | ~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:68:9 + | +LL | match y_mut { + | ----- this expression has type `&mut Box>` +... +LL | Some(_) => {} + | ^^^^^^^ expected `Box>`, found `Option<_>` + | + = note: expected struct `Box>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &**y_mut { + | ~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:70:9 + | +LL | match y_mut { + | ----- this expression has type `&mut Box>` +... +LL | None => {} + | ^^^^ expected `Box>`, found `Option<_>` + | + = note: expected struct `Box>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &**y_mut { + | ~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:79:9 + | +LL | match (& (&difficult) ) { + | ------------------ this expression has type `&&Arc>` +... +LL | Some(_) => {} + | ^^^^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &*difficult { + | ~~~~~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/suggest-deref-in-match-issue-132784.rs:81:9 + | +LL | match (& (&difficult) ) { + | ------------------ this expression has type `&&Arc>` +... +LL | None => {} + | ^^^^ expected `Arc>`, found `Option<_>` + | + = note: expected struct `Arc>` + found enum `Option<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match &*difficult { + | ~~~~~~~~~~~ + +error: aborting due to 16 previous errors + +For more information about this error, try `rustc --explain E0308`. From fb6a19bc67bcc757a671592de3706390a414395b Mon Sep 17 00:00:00 2001 From: Andrew Bond Date: Fri, 6 Dec 2024 11:09:11 -0700 Subject: [PATCH 6/8] Add documentation for anonymous pipe module --- library/std/src/pipe.rs | 140 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 5 deletions(-) diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs index 891032e94a669..06f3fd9fdffe8 100644 --- a/library/std/src/pipe.rs +++ b/library/std/src/pipe.rs @@ -1,20 +1,66 @@ -//! Module for anonymous pipe +//! A cross-platform anonymous pipe. //! -//! ``` -//! #![feature(anonymous_pipe)] +//! This module provides support for anonymous OS pipes, like [pipe] on Linux or [CreatePipe] on +//! Windows. +//! +//! # Behavior +//! +//! A pipe is a synchronous, unidirectional data channel between two or more processes, like an +//! interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular: +//! +//! * A read on a [`PipeReader`] blocks until the pipe is non-empty. +//! * A write on a [`PipeWriter`] blocks when the pipe is full. +//! * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] +//! returns EOF. +//! * [`PipeReader`] can be shared, but only one process will consume the data in the pipe. +//! +//! # Capacity +//! +//! Pipe capacity is platform dependent. To quote the Linux [man page]: +//! +//! > Different implementations have different limits for the pipe capacity. Applications should +//! > not rely on a particular capacity: an application should be designed so that a reading process +//! > consumes data as soon as it is available, so that a writing process does not remain blocked. //! +//! # Examples +//! +//! ```no_run +//! #![feature(anonymous_pipe)] //! # #[cfg(miri)] fn main() {} //! # #[cfg(not(miri))] //! # fn main() -> std::io::Result<()> { -//! let (reader, writer) = std::pipe::pipe()?; +//! # use std::process::Command; +//! # use std::io::{Read, Write}; +//! let (ping_rx, mut ping_tx) = std::pipe::pipe()?; +//! let (mut pong_rx, pong_tx) = std::pipe::pipe()?; +//! +//! // Spawn a process that echoes its input. +//! let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; +//! +//! ping_tx.write_all(b"hello")?; +//! // Close to unblock echo_server's reader. +//! drop(ping_tx); +//! +//! let mut buf = String::new(); +//! // Block until echo_server's writer is closed. +//! pong_rx.read_to_string(&mut buf)?; +//! assert_eq!(&buf, "hello"); +//! +//! echo_server.wait()?; //! # Ok(()) //! # } //! ``` - +//! [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html +//! [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe +//! [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html use crate::io; use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; /// Create anonymous pipe that is close-on-exec and blocking. +/// +/// # Examples +/// +/// See the [module-level](crate::pipe) documentation for examples. #[unstable(feature = "anonymous_pipe", issue = "127154")] #[inline] pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { @@ -33,6 +79,58 @@ pub struct PipeWriter(pub(crate) AnonPipe); impl PipeReader { /// Create a new [`PipeReader`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// # use std::fs; + /// # use std::io::Write; + /// # use std::process::Command; + /// const NUM_SLOT: u8 = 2; + /// const NUM_PROC: u8 = 5; + /// const OUTPUT: &str = "work.txt"; + /// + /// let mut jobs = vec![]; + /// let (reader, mut writer) = std::pipe::pipe()?; + /// + /// // Write NUM_SLOT characters the the pipe. + /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; + /// + /// // Spawn several processes that read a character from the pipe, do some work, then + /// // write back to the pipe. When the pipe is empty, the processes block, so only + /// // NUM_SLOT processes can be working at any given time. + /// for _ in 0..NUM_PROC { + /// jobs.push( + /// Command::new("bash") + /// .args(["-c", + /// &format!( + /// "read -n 1\n\ + /// echo -n 'x' >> '{OUTPUT}'\n\ + /// echo -n '|'", + /// ), + /// ]) + /// .stdin(reader.try_clone()?) + /// .stdout(writer.try_clone()?) + /// .spawn()?, + /// ); + /// } + /// + /// // Wait for all jobs to finish. + /// for mut job in jobs { + /// job.wait()?; + /// } + /// + /// // Check our work and clean up. + /// let xs = fs::read_to_string(OUTPUT)?; + /// fs::remove_file(OUTPUT)?; + /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); + /// # Ok(()) + /// # } + /// ``` #[unstable(feature = "anonymous_pipe", issue = "127154")] pub fn try_clone(&self) -> io::Result { self.0.try_clone().map(Self) @@ -41,6 +139,38 @@ impl PipeReader { impl PipeWriter { /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// # use std::process::Command; + /// # use std::io::Read; + /// let (mut reader, writer) = std::pipe::pipe()?; + /// + /// // Spawn a process that writes to stdout and stderr. + /// let mut peer = Command::new("bash") + /// .args([ + /// "-c", + /// "echo -n foo\n\ + /// echo -n bar >&2" + /// ]) + /// .stdout(writer.try_clone()?) + /// .stderr(writer) + /// .spawn()?; + /// + /// // Read and check the result. + /// let mut msg = String::new(); + /// reader.read_to_string(&mut msg)?; + /// assert_eq!(&msg, "foobar"); + /// + /// peer.wait()?; + /// # Ok(()) + /// # } + /// ``` #[unstable(feature = "anonymous_pipe", issue = "127154")] pub fn try_clone(&self) -> io::Result { self.0.try_clone().map(Self) From b27817c8c605499dc8269c5d1ad34527be8c52f2 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 2 Dec 2024 15:02:59 +0000 Subject: [PATCH 7/8] Arbitrary self types v2: Weak, NonNull hints Various types can be used as method receivers, such as Rc<>, Box<> and Arc<>. The arbitrary self types v2 work allows further types to be made method receivers by implementing the Receiver trait. With that in mind, it may come as a surprise to people when certain common types do not implement Receiver and thus cannot be used as a method receiver. The RFC for arbitrary self types v2 therefore proposes emitting specific lint hints for these cases: * NonNull * Weak * Raw pointers The code already emits a hint for this third case, in that it advises folks that the `arbitrary_self_types_pointers` feature may meet their need. This PR adds diagnostic hints for the Weak and NonNull cases. --- compiler/rustc_hir_analysis/messages.ftl | 6 +++++ .../rustc_hir_analysis/src/check/wfcheck.rs | 14 ++++++++++- compiler/rustc_hir_analysis/src/errors.rs | 10 ++++++++ tests/ui/self/arbitrary_self_types_nonnull.rs | 13 +++++++++++ .../self/arbitrary_self_types_nonnull.stderr | 23 +++++++++++++++++++ tests/ui/self/arbitrary_self_types_weak.rs | 13 +++++++++++ .../ui/self/arbitrary_self_types_weak.stderr | 23 +++++++++++++++++++ 7 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tests/ui/self/arbitrary_self_types_nonnull.rs create mode 100644 tests/ui/self/arbitrary_self_types_nonnull.stderr create mode 100644 tests/ui/self/arbitrary_self_types_weak.rs create mode 100644 tests/ui/self/arbitrary_self_types_weak.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index a2df0ba265c0e..0c3ed9b5c609b 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -249,6 +249,12 @@ hir_analysis_invalid_receiver_ty_help = hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types = 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`) +hir_analysis_invalid_receiver_ty_help_nonnull_note = + `NonNull` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver` + +hir_analysis_invalid_receiver_ty_help_weak_note = + `Weak` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `Weak` in a newtype wrapper for which you implement `Receiver` + hir_analysis_invalid_receiver_ty_no_arbitrary_self_types = invalid `self` parameter type: `{$receiver_ty}` .note = type of `self` must be `Self` or a type that dereferences to it diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e6ef29de965a9..95ad8225f61bb 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -44,6 +44,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use crate::autoderef::Autoderef; use crate::collect::CollectItemTypesVisitor; use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params}; +use crate::errors::InvalidReceiverTyHint; use crate::{errors, fluent_generated as fluent}; pub(super) struct WfCheckingCtxt<'a, 'tcx> { @@ -1749,7 +1750,18 @@ fn check_method_receiver<'tcx>( { match receiver_validity_err { ReceiverValidityError::DoesNotDeref if arbitrary_self_types_level.is_some() => { - tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty }) + let hint = match receiver_ty + .builtin_deref(false) + .unwrap_or(receiver_ty) + .ty_adt_def() + .and_then(|adt_def| tcx.get_diagnostic_name(adt_def.did())) + { + Some(sym::RcWeak | sym::ArcWeak) => Some(InvalidReceiverTyHint::Weak), + Some(sym::NonNull) => Some(InvalidReceiverTyHint::NonNull), + _ => None, + }; + + tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty, hint }) } ReceiverValidityError::DoesNotDeref => { tcx.dcx().emit_err(errors::InvalidReceiverTyNoArbitrarySelfTypes { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 5ab6faf3b7cea..d46f60b16f5d1 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1655,6 +1655,14 @@ pub(crate) struct NonConstRange { pub span: Span, } +#[derive(Subdiagnostic)] +pub(crate) enum InvalidReceiverTyHint { + #[note(hir_analysis_invalid_receiver_ty_help_weak_note)] + Weak, + #[note(hir_analysis_invalid_receiver_ty_help_nonnull_note)] + NonNull, +} + #[derive(Diagnostic)] #[diag(hir_analysis_invalid_receiver_ty_no_arbitrary_self_types, code = E0307)] #[note] @@ -1673,6 +1681,8 @@ pub(crate) struct InvalidReceiverTy<'tcx> { #[primary_span] pub span: Span, pub receiver_ty: Ty<'tcx>, + #[subdiagnostic] + pub hint: Option, } #[derive(Diagnostic)] diff --git a/tests/ui/self/arbitrary_self_types_nonnull.rs b/tests/ui/self/arbitrary_self_types_nonnull.rs new file mode 100644 index 0000000000000..ab1589b956e5b --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_nonnull.rs @@ -0,0 +1,13 @@ +#![feature(arbitrary_self_types)] + +struct A; + +impl A { + fn m(self: std::ptr::NonNull) {} + //~^ ERROR: invalid `self` parameter type + fn n(self: &std::ptr::NonNull) {} + //~^ ERROR: invalid `self` parameter type +} + +fn main() { +} diff --git a/tests/ui/self/arbitrary_self_types_nonnull.stderr b/tests/ui/self/arbitrary_self_types_nonnull.stderr new file mode 100644 index 0000000000000..5ebe58b4e48ec --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_nonnull.stderr @@ -0,0 +1,23 @@ +error[E0307]: invalid `self` parameter type: `NonNull` + --> $DIR/arbitrary_self_types_nonnull.rs:6:16 + | +LL | fn m(self: std::ptr::NonNull) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + = note: `NonNull` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver` + +error[E0307]: invalid `self` parameter type: `&NonNull` + --> $DIR/arbitrary_self_types_nonnull.rs:8:16 + | +LL | fn n(self: &std::ptr::NonNull) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + = note: `NonNull` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/self/arbitrary_self_types_weak.rs b/tests/ui/self/arbitrary_self_types_weak.rs new file mode 100644 index 0000000000000..91f2b9e0a95f5 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_weak.rs @@ -0,0 +1,13 @@ +#![feature(arbitrary_self_types)] + +struct A; + +impl A { + fn m(self: std::rc::Weak) {} + //~^ ERROR: invalid `self` parameter type + fn n(self: std::sync::Weak) {} + //~^ ERROR: invalid `self` parameter type +} + +fn main() { +} diff --git a/tests/ui/self/arbitrary_self_types_weak.stderr b/tests/ui/self/arbitrary_self_types_weak.stderr new file mode 100644 index 0000000000000..42d239419ad01 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_weak.stderr @@ -0,0 +1,23 @@ +error[E0307]: invalid `self` parameter type: `std::rc::Weak` + --> $DIR/arbitrary_self_types_weak.rs:6:16 + | +LL | fn m(self: std::rc::Weak) {} + | ^^^^^^^^^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + = note: `Weak` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `Weak` in a newtype wrapper for which you implement `Receiver` + +error[E0307]: invalid `self` parameter type: `std::sync::Weak` + --> $DIR/arbitrary_self_types_weak.rs:8:16 + | +LL | fn n(self: std::sync::Weak) {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + = note: `Weak` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `Weak` in a newtype wrapper for which you implement `Receiver` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0307`. From 7717df22865391ee406847e75afe736bd736df9a Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Sun, 8 Dec 2024 08:14:05 +0100 Subject: [PATCH 8/8] Correct spelling of CURRENT_RUSTC_VERSION I mixed it up with RUSTC_CURRENT_VERSION unfortunately. Also improve the formatting of the macro invocation slightly. --- library/core/src/iter/traits/collect.rs | 63 ++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index c3c7288e38990..8ab1c26f95e32 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -493,15 +493,64 @@ impl Extend<()> for () { } macro_rules! spec_tuple_impl { - ( ($ty_name:ident, $var_name:ident, $extend_ty_name: ident, $trait_name:ident, $default_fn_name:ident, $cnt:tt), ) => { - spec_tuple_impl!($trait_name, $default_fn_name, #[doc(fake_variadic)] #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in RUSTC_CURRENT_VERSION."] => ($ty_name, $var_name, $extend_ty_name, $cnt),); + ( + ( + $ty_name:ident, $var_name:ident, $extend_ty_name: ident, + $trait_name:ident, $default_fn_name:ident, $cnt:tt + ), + ) => { + spec_tuple_impl!( + $trait_name, + $default_fn_name, + #[doc(fake_variadic)] + #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for \ + 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in \ + CURRENT_RUSTC_VERSION."] + => ($ty_name, $var_name, $extend_ty_name, $cnt), + ); }; - ( ($ty_name:ident, $var_name:ident, $extend_ty_name: ident, $trait_name:ident, $default_fn_name:ident, $cnt:tt), $(($ty_names:ident, $var_names:ident, $extend_ty_names:ident, $trait_names:ident, $default_fn_names:ident, $cnts:tt),)*) => { - - spec_tuple_impl!($(($ty_names, $var_names, $extend_ty_names, $trait_names, $default_fn_names, $cnts),)*); - spec_tuple_impl!($trait_name, $default_fn_name, #[doc(hidden)] => ($ty_name, $var_name, $extend_ty_name, $cnt), $(($ty_names, $var_names, $extend_ty_names, $cnts),)*); + ( + ( + $ty_name:ident, $var_name:ident, $extend_ty_name: ident, + $trait_name:ident, $default_fn_name:ident, $cnt:tt + ), + $( + ( + $ty_names:ident, $var_names:ident, $extend_ty_names:ident, + $trait_names:ident, $default_fn_names:ident, $cnts:tt + ), + )* + ) => { + spec_tuple_impl!( + $( + ( + $ty_names, $var_names, $extend_ty_names, + $trait_names, $default_fn_names, $cnts + ), + )* + ); + spec_tuple_impl!( + $trait_name, + $default_fn_name, + #[doc(hidden)] + => ( + $ty_name, $var_name, $extend_ty_name, $cnt + ), + $( + ( + $ty_names, $var_names, $extend_ty_names, $cnts + ), + )* + ); }; - ($trait_name:ident, $default_fn_name:ident, #[$meta:meta] $(#[$doctext:meta])? => $(($ty_names:ident, $var_names:ident, $extend_ty_names:ident, $cnts:tt),)*) => { + ( + $trait_name:ident, $default_fn_name:ident, #[$meta:meta] + $(#[$doctext:meta])? => $( + ( + $ty_names:ident, $var_names:ident, $extend_ty_names:ident, $cnts:tt + ), + )* + ) => { #[$meta] $(#[$doctext])? #[stable(feature = "extend_for_tuple", since = "1.56.0")]