From dab77c50daeb563a05b20b31527f731b683cded5 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Thu, 27 Jun 2024 18:31:29 +0200 Subject: [PATCH 01/11] fix least significant digits of f128 associated constants While the numbers are parsed to the correct value, the decimal numbers in the source were rounded to zero instead of to the nearest, making the literals different from the values shown in the documentation. --- library/core/src/num/f128.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 58ed98c888cc6..048e3cca93d21 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -167,7 +167,7 @@ impl f128 { /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon /// [`MANTISSA_DIGITS`]: f128::MANTISSA_DIGITS #[unstable(feature = "f128", issue = "116909")] - pub const EPSILON: f128 = 1.92592994438723585305597794258492731e-34_f128; + pub const EPSILON: f128 = 1.92592994438723585305597794258492732e-34_f128; /// Smallest finite `f128` value. /// @@ -175,7 +175,7 @@ impl f128 { /// /// [`MAX`]: f128::MAX #[unstable(feature = "f128", issue = "116909")] - pub const MIN: f128 = -1.18973149535723176508575932662800701e+4932_f128; + pub const MIN: f128 = -1.18973149535723176508575932662800702e+4932_f128; /// Smallest positive normal `f128` value. /// /// Equal to 2[`MIN_EXP`] − 1. @@ -191,7 +191,7 @@ impl f128 { /// [`MANTISSA_DIGITS`]: f128::MANTISSA_DIGITS /// [`MAX_EXP`]: f128::MAX_EXP #[unstable(feature = "f128", issue = "116909")] - pub const MAX: f128 = 1.18973149535723176508575932662800701e+4932_f128; + pub const MAX: f128 = 1.18973149535723176508575932662800702e+4932_f128; /// One greater than the minimum possible normal power of 2 exponent. /// From 9568e120f10e35f748cb50bf90f4216ae6d27de4 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Tue, 18 Jun 2024 14:04:28 +0300 Subject: [PATCH 02/11] Delegation: support coercion for target expression --- compiler/rustc_ast_lowering/src/delegation.rs | 77 +++++++++++++++---- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 1 + .../src/fn_ctxt/suggestions.rs | 1 + compiler/rustc_hir_typeck/src/method/mod.rs | 15 +++- compiler/rustc_hir_typeck/src/method/probe.rs | 34 ++++++++ .../rustc_hir_typeck/src/method/suggest.rs | 2 + compiler/rustc_resolve/src/late.rs | 22 +++++- tests/ui/delegation/bad-resolve.rs | 3 + tests/ui/delegation/bad-resolve.stderr | 24 +++++- tests/ui/delegation/explicit-paths-pass.rs | 4 +- tests/ui/delegation/explicit-paths.rs | 4 +- tests/ui/delegation/explicit-paths.stderr | 9 ++- tests/ui/delegation/ice-issue-122550.stderr | 25 +++--- tests/ui/delegation/method-call-choice.rs | 25 ++++++ tests/ui/delegation/method-call-choice.stderr | 21 +++++ tests/ui/delegation/method-call-priority.rs | 34 ++++++++ tests/ui/delegation/self-coercion.rs | 26 +++++++ 17 files changed, 291 insertions(+), 36 deletions(-) create mode 100644 tests/ui/delegation/method-call-choice.rs create mode 100644 tests/ui/delegation/method-call-choice.stderr create mode 100644 tests/ui/delegation/method-call-priority.rs create mode 100644 tests/ui/delegation/self-coercion.rs diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 678cac210f413..75b9c2e0de18b 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -38,7 +38,7 @@ use crate::{ImplTraitPosition, ResolverAstLoweringExt}; -use super::{ImplTraitContext, LoweringContext, ParamMode}; +use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; use ast::visit::Visitor; use hir::def::{DefKind, PartialRes, Res}; @@ -259,8 +259,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self_param_id: pat_node_id, }; self_resolver.visit_block(block); - let block = this.lower_block(block, false); - this.mk_expr(hir::ExprKind::Block(block, None), block.span) + this.lower_target_expr(&block) } else { let pat_hir_id = this.lower_node_id(pat_node_id); this.generate_arg(pat_hir_id, span) @@ -273,26 +272,74 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - // Generates fully qualified call for the resulting body. + // FIXME(fn_delegation): Alternatives for target expression lowering: + // https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600. + fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> { + if block.stmts.len() == 1 + && let StmtKind::Expr(expr) = &block.stmts[0].kind + { + return self.lower_expr_mut(expr); + } + + let block = self.lower_block(block, false); + self.mk_expr(hir::ExprKind::Block(block, None), block.span) + } + + // Generates expression for the resulting body. If possible, `MethodCall` is used + // instead of fully qualified call for the self type coercion. fn finalize_body_lowering( &mut self, delegation: &Delegation, args: Vec>, span: Span, ) -> hir::Expr<'hir> { - let path = self.lower_qpath( - delegation.id, - &delegation.qself, - &delegation.path, - ParamMode::Optional, - ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, - ); - let args = self.arena.alloc_from_iter(args); - let path_expr = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span)); - let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(path_expr, args), span)); + let has_generic_args = + delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some()); + + let call = if self + .get_resolution_id(delegation.id, span) + .and_then(|def_id| Ok(self.has_self(def_id, span))) + .unwrap_or_default() + && delegation.qself.is_none() + && !has_generic_args + { + let ast_segment = delegation.path.segments.last().unwrap(); + let segment = self.lower_path_segment( + delegation.path.span, + ast_segment, + ParamMode::Optional, + ParenthesizedGenericArgs::Err, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + None, + ); + let segment = self.arena.alloc(segment); + + let method_call_id = self.next_id(); + if let Some(traits) = self.resolver.trait_map.remove(&delegation.id) { + self.trait_map.insert(method_call_id.local_id, traits.into_boxed_slice()); + } + + self.arena.alloc(hir::Expr { + hir_id: method_call_id, + kind: hir::ExprKind::MethodCall(segment, &args[0], &args[1..], span), + span, + }) + } else { + let path = self.lower_qpath( + delegation.id, + &delegation.qself, + &delegation.path, + ParamMode::Optional, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + + let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span)); + self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span)) + }; let block = self.arena.alloc(hir::Block { stmts: &[], expr: Some(call), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 1138642c56d61..af9a8946f232c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -570,6 +570,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), callee_ty.peel_refs(), callee_expr.unwrap().hir_id, + None, TraitsInScope, |mut ctxt| ctxt.probe_for_similar_candidate(), ) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 8d380caf91628..66aee13fffbc8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1588,6 +1588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, expr.hir_id, + None, ProbeScope::TraitsInScope, ) { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index e1223307b53e3..450fa60d03ffb 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -104,6 +104,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, call_expr_id, + None, ProbeScope::TraitsInScope, ) { Ok(pick) => { @@ -182,8 +183,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_expr: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], ) -> Result, MethodError<'tcx>> { - let pick = - self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; + let pick = self.lookup_probe( + segment.ident, + self_ty, + call_expr, + segment.res.opt_def_id(), + ProbeScope::TraitsInScope, + )?; self.lint_edition_dependent_dot_call( self_ty, segment, span, call_expr, self_expr, &pick, args, @@ -208,6 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segment.ident, trait_type, call_expr, + None, ProbeScope::TraitsInScope, ) { Ok(ref new_pick) if pick.differs_from(new_pick) => { @@ -276,6 +283,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method_name: Ident, self_ty: Ty<'tcx>, call_expr: &hir::Expr<'_>, + expected_def_id: Option, scope: ProbeScope, ) -> probe::PickResult<'tcx> { let pick = self.probe_for_name( @@ -285,6 +293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(false), self_ty, call_expr.hir_id, + expected_def_id, scope, )?; pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id); @@ -306,6 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, call_expr.hir_id, + None, scope, )?; Ok(pick) @@ -516,6 +526,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(false), self_ty, expr_id, + None, ProbeScope::TraitsInScope, ); let pick = match (pick, struct_variant) { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 9747a91ccbfad..f519366e3803f 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -96,6 +96,27 @@ pub(crate) struct ProbeContext<'a, 'tcx> { scope_expr_id: HirId, + /// Delegation item can be expanded into method calls or fully qualified calls + /// depending on the callee's signature. Method calls are used to allow + /// autoref/autoderef for target expression. For example in: + /// + /// ```ignore (illustrative) + /// trait Trait : Sized { + /// fn by_value(self) -> i32 { 1 } + /// fn by_mut_ref(&mut self) -> i32 { 2 } + /// fn by_ref(&self) -> i32 { 3 } + /// } + /// + /// struct NewType(SomeType); + /// impl Trait for NewType { + /// reuse Trait::* { self.0 } + /// } + /// ``` + /// + /// `self.0` will automatically coerce. The difference with existing method lookup + /// is that methods in delegation items are pre-resolved by callee path (`Trait::*`). + expected_def_id: Option, + /// Is this probe being done for a diagnostic? This will skip some error reporting /// machinery, since we don't particularly care about, for example, similarly named /// candidates if we're *reporting* similarly named candidates. @@ -248,6 +269,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, scope_expr_id, + None, ProbeScope::AllTraits, |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)), ) @@ -263,6 +285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, scope_expr_id, + None, ProbeScope::AllTraits, |probe_cx| probe_cx.pick(), ) @@ -281,6 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: HirId, + expected_def_id: Option, scope: ProbeScope, ) -> PickResult<'tcx> { self.probe_op( @@ -291,6 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion, self_ty, scope_expr_id, + expected_def_id, scope, |probe_cx| probe_cx.pick(), ) @@ -315,6 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion, self_ty, scope_expr_id, + None, scope, |probe_cx| { Ok(probe_cx @@ -335,6 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: HirId, + expected_def_id: Option, scope: ProbeScope, op: OP, ) -> Result> @@ -476,6 +503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &orig_values, steps.steps, scope_expr_id, + expected_def_id, is_suggestion, ); @@ -571,6 +599,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { orig_steps_var_values: &'a OriginalQueryValues<'tcx>, steps: &'tcx [CandidateStep<'tcx>], scope_expr_id: HirId, + expected_def_id: Option, is_suggestion: IsSuggestion, ) -> ProbeContext<'a, 'tcx> { ProbeContext { @@ -590,6 +619,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { static_candidates: RefCell::new(Vec::new()), unsatisfied_predicates: RefCell::new(Vec::new()), scope_expr_id, + expected_def_id, is_suggestion, } } @@ -1220,6 +1250,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ) -> Option> { let mut applicable_candidates: Vec<_> = candidates .iter() + .filter(|candidate| { + !matches!(self.expected_def_id, Some(def_id) if def_id != candidate.item.def_id) + }) .map(|probe| { (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) }) @@ -1677,6 +1710,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.orig_steps_var_values, self.steps, self.scope_expr_id, + None, IsSuggestion(true), ); pcx.allow_similar_names = true; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a385bc70e359b..afdd5b2384d60 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2034,6 +2034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), rcvr_ty, expr_id, + None, ProbeScope::TraitsInScope, ) .is_ok() @@ -3095,6 +3096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), deref_ty, ty.hir_id, + None, ProbeScope::TraitsInScope, ) { if deref_ty.is_suggestable(self.tcx, true) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 66a1c05289b7d..515ce266fd22a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3316,14 +3316,32 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.visit_ty(&qself.ty); } self.visit_path(&delegation.path, delegation.id); + let last_ident = delegation.path.segments.last().unwrap().ident; + + // Saving traits for a `MethodCall` that has not yet been generated. + // Traits found in the path are also considered visible: + // + // impl Trait for Type { + // reuse inner::TraitFoo::*; // OK, even `TraitFoo` is not in scope. + // } + let mut traits = self.traits_in_scope(last_ident, ValueNS); + for segment in &delegation.path.segments { + if let Some(partial_res) = self.r.partial_res_map.get(&segment.id) + && let Some(def_id) = partial_res.full_res().and_then(|res| res.opt_def_id()) + && self.r.tcx.def_kind(def_id) == DefKind::Trait + { + traits.push(TraitCandidate { def_id, import_ids: smallvec![] }); + } + } + self.r.trait_map.insert(delegation.id, traits); + if let Some(body) = &delegation.body { self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { // `PatBoundCtx` is not necessary in this context let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - let span = delegation.path.segments.last().unwrap().ident.span; this.fresh_binding( - Ident::new(kw::SelfLower, span), + Ident::new(kw::SelfLower, last_ident.span), delegation.id, PatternSource::FnParam, &mut bindings, diff --git a/tests/ui/delegation/bad-resolve.rs b/tests/ui/delegation/bad-resolve.rs index f378e05304b29..f15e6aa81afb0 100644 --- a/tests/ui/delegation/bad-resolve.rs +++ b/tests/ui/delegation/bad-resolve.rs @@ -34,6 +34,9 @@ impl Trait for S { reuse foo { &self.0 } //~^ ERROR cannot find function `foo` in this scope + reuse Trait::foo2 { self.0 } + //~^ ERROR cannot find function `foo2` in trait `Trait` + //~| ERROR method `foo2` is not a member of trait `Trait` } mod prefix {} diff --git a/tests/ui/delegation/bad-resolve.stderr b/tests/ui/delegation/bad-resolve.stderr index 883ff523bcfea..32d2f3b26cb03 100644 --- a/tests/ui/delegation/bad-resolve.stderr +++ b/tests/ui/delegation/bad-resolve.stderr @@ -25,6 +25,15 @@ LL | reuse ::baz; | | help: there is an associated function with a similar name: `bar` | not a member of trait `Trait` +error[E0407]: method `foo2` is not a member of trait `Trait` + --> $DIR/bad-resolve.rs:37:5 + | +LL | reuse Trait::foo2 { self.0 } + | ^^^^^^^^^^^^^----^^^^^^^^^^^ + | | | + | | help: there is an associated function with a similar name: `foo` + | not a member of trait `Trait` + error[E0423]: expected function, found associated constant `Trait::C` --> $DIR/bad-resolve.rs:24:11 | @@ -54,6 +63,15 @@ error[E0425]: cannot find function `foo` in this scope LL | reuse foo { &self.0 } | ^^^ not found in this scope +error[E0425]: cannot find function `foo2` in trait `Trait` + --> $DIR/bad-resolve.rs:37:18 + | +LL | fn foo(&self, x: i32) -> i32 { x } + | ---------------------------- similarly named associated function `foo` defined here +... +LL | reuse Trait::foo2 { self.0 } + | ^^^^ help: an associated function with a similar name exists: `foo` + error[E0046]: not all trait items implemented, missing: `Type` --> $DIR/bad-resolve.rs:22:1 | @@ -64,18 +82,18 @@ LL | impl Trait for S { | ^^^^^^^^^^^^^^^^ missing `Type` in implementation error[E0433]: failed to resolve: use of undeclared crate or module `unresolved_prefix` - --> $DIR/bad-resolve.rs:40:7 + --> $DIR/bad-resolve.rs:43:7 | LL | reuse unresolved_prefix::{a, b, c}; | ^^^^^^^^^^^^^^^^^ use of undeclared crate or module `unresolved_prefix` error[E0433]: failed to resolve: `crate` in paths can only be used in start position - --> $DIR/bad-resolve.rs:41:29 + --> $DIR/bad-resolve.rs:44:29 | LL | reuse prefix::{self, super, crate}; | ^^^^^ `crate` in paths can only be used in start position -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0046, E0324, E0407, E0423, E0425, E0433, E0575, E0576. For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/delegation/explicit-paths-pass.rs b/tests/ui/delegation/explicit-paths-pass.rs index fada793bd118c..dd0ee2c732f59 100644 --- a/tests/ui/delegation/explicit-paths-pass.rs +++ b/tests/ui/delegation/explicit-paths-pass.rs @@ -24,8 +24,8 @@ reuse to_reuse::zero_args { self } struct S(F); impl Trait for S { - reuse Trait::bar { &self.0 } - reuse Trait::description { &self.0 } + reuse Trait::bar { self.0 } + reuse Trait::description { self.0 } reuse ::static_method; reuse ::static_method2 { S::static_method(self) } } diff --git a/tests/ui/delegation/explicit-paths.rs b/tests/ui/delegation/explicit-paths.rs index a91ca4cb931ed..d42e305b252f0 100644 --- a/tests/ui/delegation/explicit-paths.rs +++ b/tests/ui/delegation/explicit-paths.rs @@ -34,7 +34,7 @@ mod inherent_impl_assoc_fn_to_other { use crate::*; impl S { - reuse Trait::foo1 { &self.0 } + reuse Trait::foo1 { self.0 } reuse ::foo2; reuse to_reuse::foo3; reuse F::foo4 { &self.0 } @@ -46,7 +46,7 @@ mod trait_impl_assoc_fn_to_other { use crate::*; impl Trait for S { - reuse Trait::foo1 { &self.0 } + reuse Trait::foo1 { self.0 } reuse ::foo2; reuse to_reuse::foo3; //~^ ERROR method `foo3` is not a member of trait `Trait` diff --git a/tests/ui/delegation/explicit-paths.stderr b/tests/ui/delegation/explicit-paths.stderr index d33c5da4377b1..b5afe19f87897 100644 --- a/tests/ui/delegation/explicit-paths.stderr +++ b/tests/ui/delegation/explicit-paths.stderr @@ -91,10 +91,17 @@ error[E0308]: mismatched types LL | trait Trait2 : Trait { | -------------------- found this type parameter LL | reuse ::foo1 { self } - | ^^^^ expected `&F`, found `&Self` + | ---- ^^^^ expected `&F`, found `&Self` + | | + | arguments to this function are incorrect | = note: expected reference `&F` found reference `&Self` +note: method defined here + --> $DIR/explicit-paths.rs:5:8 + | +LL | fn foo1(&self, x: i32) -> i32 { x } + | ^^^^ ----- error[E0277]: the trait bound `S2: Trait` is not satisfied --> $DIR/explicit-paths.rs:78:16 diff --git a/tests/ui/delegation/ice-issue-122550.stderr b/tests/ui/delegation/ice-issue-122550.stderr index c92170644e78f..1a01bee3e1e27 100644 --- a/tests/ui/delegation/ice-issue-122550.stderr +++ b/tests/ui/delegation/ice-issue-122550.stderr @@ -4,15 +4,6 @@ error[E0308]: mismatched types LL | fn description(&self) -> &str {} | ^^ expected `&str`, found `()` -error[E0308]: mismatched types - --> $DIR/ice-issue-122550.rs:13:39 - | -LL | reuse ::description { &self.0 } - | ^^^^^^^ expected `&S`, found `&F` - | - = note: expected reference `&S` - found reference `&F` - error[E0277]: the trait bound `S: Trait` is not satisfied --> $DIR/ice-issue-122550.rs:13:12 | @@ -25,6 +16,22 @@ help: this trait has no implementations, consider adding one LL | trait Trait { | ^^^^^^^^^^^ +error[E0308]: mismatched types + --> $DIR/ice-issue-122550.rs:13:39 + | +LL | reuse ::description { &self.0 } + | ----------- ^^^^^^^ expected `&S`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected reference `&S` + found reference `&F` +note: method defined here + --> $DIR/ice-issue-122550.rs:5:8 + | +LL | fn description(&self) -> &str {} + | ^^^^^^^^^^^ ----- + error: aborting due to 3 previous errors Some errors have detailed explanations: E0277, E0308. diff --git a/tests/ui/delegation/method-call-choice.rs b/tests/ui/delegation/method-call-choice.rs new file mode 100644 index 0000000000000..8d53d8bfdb72d --- /dev/null +++ b/tests/ui/delegation/method-call-choice.rs @@ -0,0 +1,25 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) {} +} + +struct F; +impl Trait for F {} +struct S(F); + +pub mod to_reuse { + use crate::F; + + pub fn foo(_: &F) {} +} + +impl Trait for S { + // Make sure that the method call is not generated if the path resolution + // does not have a `self` parameter. + reuse to_reuse::foo { self.0 } + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/delegation/method-call-choice.stderr b/tests/ui/delegation/method-call-choice.stderr new file mode 100644 index 0000000000000..6757af20a6b3a --- /dev/null +++ b/tests/ui/delegation/method-call-choice.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/method-call-choice.rs:21:27 + | +LL | reuse to_reuse::foo { self.0 } + | --- ^^^^^^ expected `&F`, found `F` + | | + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/method-call-choice.rs:15:12 + | +LL | pub fn foo(_: &F) {} + | ^^^ ----- +help: consider borrowing here + | +LL | reuse to_reuse::foo { &self.0 } + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/delegation/method-call-priority.rs b/tests/ui/delegation/method-call-priority.rs new file mode 100644 index 0000000000000..8d68740d181a8 --- /dev/null +++ b/tests/ui/delegation/method-call-priority.rs @@ -0,0 +1,34 @@ +//@ run-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![allow(dead_code)] + +trait Trait1 { + fn foo(&self) -> i32 { 1 } +} + +trait Trait2 { + fn foo(&self) -> i32 { 2 } +} + +struct F; +impl Trait1 for F {} +impl Trait2 for F {} + +impl F { + fn foo(&self) -> i32 { 3 } +} + +struct S(F); + +impl Trait1 for S { + // Make sure that the generated `self.0.foo()` does not turn into the inherent method `F::foo` + // that has a higher priority than methods from traits. + reuse Trait1::foo { self.0 } +} + +fn main() { + let s = S(F); + assert_eq!(s.foo(), 1); +} diff --git a/tests/ui/delegation/self-coercion.rs b/tests/ui/delegation/self-coercion.rs new file mode 100644 index 0000000000000..96c1f1b140b14 --- /dev/null +++ b/tests/ui/delegation/self-coercion.rs @@ -0,0 +1,26 @@ +//@ run-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait : Sized { + fn by_value(self) -> i32 { 1 } + fn by_mut_ref(&mut self) -> i32 { 2 } + fn by_ref(&self) -> i32 { 3 } +} + +struct F; +impl Trait for F {} + +struct S(F); + +impl Trait for S { + reuse Trait::{by_value, by_mut_ref, by_ref} { self.0 } +} + +fn main() { + let mut s = S(F); + assert_eq!(s.by_ref(), 3); + assert_eq!(s.by_mut_ref(), 2); + assert_eq!(s.by_value(), 1); +} From abeb7203e4c979b2446bf0cca0155427f0c46c18 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Thu, 11 Jul 2024 22:36:45 +0000 Subject: [PATCH 03/11] Add regression test for issue 127545 --- .../transforming-option-ref-issue-127545.rs | 8 ++++ ...ransforming-option-ref-issue-127545.stderr | 40 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs index 5ba58e7427581..9125ffeeb066c 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs @@ -4,3 +4,11 @@ pub fn foo(arg: Option<&Vec>) -> Option<&[i32]> { arg //~ ERROR 5:5: 5:8: mismatched types [E0308] } + +pub fn bar(arg: Option<&Vec>) -> &[i32] { + arg.unwrap_or(&[]) //~ ERROR 9:19: 9:22: mismatched types [E0308] +} + +pub fn barzz<'a>(arg: Option<&'a Vec>, v: &'a [i32]) -> &'a [i32] { + arg.unwrap_or(v) //~ ERROR 13:19: 13:20: mismatched types [E0308] +} diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr index b7c7202113a16..2d4f26c388dbe 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr @@ -14,5 +14,45 @@ LL | arg.map(|v| &**v) | ++++++++++++++ error: aborting due to 1 previous error + --> $DIR/transforming-option-ref-issue-127545.rs:9:19 + | +LL | arg.unwrap_or(&[]) + | --------- ^^^ expected `&Vec`, found `&[_; 0]` + | | + | arguments to this method are incorrect + | + = note: expected reference `&Vec` + found reference `&[_; 0]` +help: the return type of this call is `&[_; 0]` due to the type of the argument passed + --> $DIR/transforming-option-ref-issue-127545.rs:9:5 + | +LL | arg.unwrap_or(&[]) + | ^^^^^^^^^^^^^^---^ + | | + | this argument influences the return type of `unwrap_or` +note: method defined here + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0308]: mismatched types + --> $DIR/transforming-option-ref-issue-127545.rs:13:19 + | +LL | arg.unwrap_or(v) + | --------- ^ expected `&Vec`, found `&[i32]` + | | + | arguments to this method are incorrect + | + = note: expected reference `&Vec` + found reference `&'a [i32]` +help: the return type of this call is `&'a [i32]` due to the type of the argument passed + --> $DIR/transforming-option-ref-issue-127545.rs:13:5 + | +LL | arg.unwrap_or(v) + | ^^^^^^^^^^^^^^-^ + | | + | this argument influences the return type of `unwrap_or` +note: method defined here + --> $SRC_DIR/core/src/option.rs:LL:COL + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. From 5e1cabe98271fc79cd8ebf8e9722df781cad7aa8 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Fri, 12 Jul 2024 08:43:28 +0000 Subject: [PATCH 04/11] Add suggestion to use Option::map_or over erroneous Option::unwrap_or --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 11 ++++ .../src/fn_ctxt/suggestions.rs | 65 +++++++++++++++++++ ...ransforming-option-ref-issue-127545.stderr | 10 ++- 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index ab0f356ce91f1..6fdf616f50e09 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -948,6 +948,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut err, ); + self.suggest_deref_unwrap_or( + &mut err, + error_span, + callee_ty, + call_ident, + expected_ty, + provided_ty, + provided_args[*provided_idx], + is_method, + ); + // Call out where the function is defined self.label_fn_like( &mut err, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 5975c52ca46e8..15a1a18daa70a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1429,6 +1429,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } + // Suggest to change `Option<&Vec>::unwrap_or(&[])` to `Option::map_or(&[], |v| v)`. + #[instrument(level = "trace", skip(self, err, provided_expr))] + pub(crate) fn suggest_deref_unwrap_or( + &self, + err: &mut Diag<'_>, + error_span: Span, + callee_ty: Option>, + call_ident: Option, + expected_ty: Ty<'tcx>, + provided_ty: Ty<'tcx>, + provided_expr: &Expr<'tcx>, + is_method: bool, + ) { + if !is_method { + return; + } + let Some(callee_ty) = callee_ty else { + return; + }; + let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else { + return; + }; + if !self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) { + return; + } + + if call_ident.map_or(true, |ident| ident.name != sym::unwrap_or) { + return; + } + + let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else { + return; + }; + + // NOTE: Can we reuse `suggest_deref_or_ref`? + + // Create an dummy type `&[_]` so that both &[] and `&Vec` can coerce to it. + let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind() + && let ty::Infer(_) = elem_ty.kind() + && size.try_eval_target_usize(self.tcx, self.param_env) == Some(0) + { + let slice = Ty::new_slice(self.tcx, *elem_ty); + Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice) + } else { + provided_ty + }; + + if !self.can_coerce(expected_ty, dummy_ty) { + return; + } + let (provided_snip, applicability) = + match self.tcx.sess.source_map().span_to_snippet(provided_expr.span) { + Ok(snip) => (snip, Applicability::MachineApplicable), + Err(_) => ("/* _ */".to_owned(), Applicability::MaybeIncorrect), + }; + let sugg = &format!("map_or({provided_snip}, |v| v)"); + err.span_suggestion_verbose( + error_span, + "use `Option::map_or` to deref inner value of `Option`", + sugg, + applicability, + ); + return; + } + /// Suggest wrapping the block in square brackets instead of curly braces /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. pub(crate) fn suggest_block_to_brackets( diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr index 2d4f26c388dbe..90a7cfdb44962 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr @@ -13,7 +13,7 @@ help: try using `.map(|v| &**v)` to convert `Option<&Vec>` to `Option<&[i32 LL | arg.map(|v| &**v) | ++++++++++++++ -error: aborting due to 1 previous error +error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:9:19 | LL | arg.unwrap_or(&[]) @@ -32,6 +32,10 @@ LL | arg.unwrap_or(&[]) | this argument influences the return type of `unwrap_or` note: method defined here --> $SRC_DIR/core/src/option.rs:LL:COL +help: use `Option::map_or` to deref inner value of `Option` + | +LL | arg.map_or(&[], |v| v) + | ~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:13:19 @@ -52,6 +56,10 @@ LL | arg.unwrap_or(v) | this argument influences the return type of `unwrap_or` note: method defined here --> $SRC_DIR/core/src/option.rs:LL:COL +help: use `Option::map_or` to deref inner value of `Option` + | +LL | arg.map_or(v, |v| v) + | ~~~~~~~~~~~~~~~~ error: aborting due to 3 previous errors From 9c1a9e03d5e001543a21eaff5d616de8cb4e2220 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 13 Jul 2024 00:54:00 +0000 Subject: [PATCH 05/11] add test for Result<&T, _> where T; Deref --- .../transforming-option-ref-issue-127545.rs | 4 ++++ ...ransforming-option-ref-issue-127545.stderr | 22 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs index 9125ffeeb066c..f589e88f68e44 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs @@ -12,3 +12,7 @@ pub fn bar(arg: Option<&Vec>) -> &[i32] { pub fn barzz<'a>(arg: Option<&'a Vec>, v: &'a [i32]) -> &'a [i32] { arg.unwrap_or(v) //~ ERROR 13:19: 13:20: mismatched types [E0308] } + +pub fn convert_result(arg: Result<&Vec, ()>) -> &[i32] { + arg.unwrap_or(&[]) //~ ERROR 17:19: 17:22: mismatched types [E0308] +} diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr index 90a7cfdb44962..0a6d47339d8e8 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr @@ -61,6 +61,26 @@ help: use `Option::map_or` to deref inner value of `Option` LL | arg.map_or(v, |v| v) | ~~~~~~~~~~~~~~~~ -error: aborting due to 3 previous errors +error[E0308]: mismatched types + --> $DIR/transforming-option-ref-issue-127545.rs:17:19 + | +LL | arg.unwrap_or(&[]) + | --------- ^^^ expected `&Vec`, found `&[_; 0]` + | | + | arguments to this method are incorrect + | + = note: expected reference `&Vec` + found reference `&[_; 0]` +help: the return type of this call is `&[_; 0]` due to the type of the argument passed + --> $DIR/transforming-option-ref-issue-127545.rs:17:5 + | +LL | arg.unwrap_or(&[]) + | ^^^^^^^^^^^^^^---^ + | | + | this argument influences the return type of `unwrap_or` +note: method defined here + --> $SRC_DIR/core/src/result.rs:LL:COL + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. From e595f3d13f0491186577d7b6e0290f31791f5059 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Fri, 12 Jul 2024 13:08:05 +0800 Subject: [PATCH 06/11] Add cache for `allocate_str` --- .../rustc_const_eval/src/interpret/place.rs | 14 +++++++++++++- compiler/rustc_middle/src/mir/interpret/mod.rs | 17 +++++++++++------ compiler/rustc_middle/src/ty/context.rs | 5 +++-- .../src/build/expr/as_constant.rs | 2 +- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index baaee67e7871c..33c25b746ccc6 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -995,13 +995,25 @@ where } /// Returns a wide MPlace of type `str` to a new 1-aligned allocation. + /// Immutable strings are deduplicated and stored in global memory. pub fn allocate_str( &mut self, str: &str, kind: MemoryKind, mutbl: Mutability, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?; + let tcx = self.tcx.tcx; + + // Use cache for immutable strings. + let ptr = if mutbl.is_not() { + // Use dedup'd allocation function. + let id = tcx.allocate_bytes_dedup(str.as_bytes()); + + // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation. + M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))? + } else { + self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)? + }; let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self); let layout = self.layout_of(self.tcx.types.str_).unwrap(); Ok(self.ptr_with_meta_to_mplace(ptr.into(), MemPlaceMeta::Meta(meta), layout)) diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 4e95e600b5ab9..bdd1eb11a38e4 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -393,7 +393,6 @@ pub(crate) struct AllocMap<'tcx> { alloc_map: FxHashMap>, /// Used to ensure that statics and functions only get one associated `AllocId`. - /// Should never contain a `GlobalAlloc::Memory`! // // FIXME: Should we just have two separate dedup maps for statics and functions each? dedup: FxHashMap, AllocId>, @@ -433,13 +432,13 @@ impl<'tcx> TyCtxt<'tcx> { } /// Reserves a new ID *if* this allocation has not been dedup-reserved before. - /// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we - /// don't want to dedup IDs for "real" memory! + /// Should not be used for mutable memory. fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId { let mut alloc_map = self.alloc_map.lock(); - match alloc { - GlobalAlloc::Function { .. } | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {} - GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), + if let GlobalAlloc::Memory(mem) = alloc { + if mem.inner().mutability.is_mut() { + bug!("trying to dedup-reserve mutable memory"); + } } if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { return alloc_id; @@ -451,6 +450,12 @@ impl<'tcx> TyCtxt<'tcx> { id } + /// Generates an `AllocId` for a memory allocation. If the exact same memory has been + /// allocated before, this will return the same `AllocId`. + pub fn reserve_and_set_memory_dedup(self, mem: ConstAllocation<'tcx>) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::Memory(mem)) + } + /// Generates an `AllocId` for a static or return a cached one in case this function has been /// called on the same static before. pub fn reserve_and_set_static_alloc(self, static_id: DefId) -> AllocId { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9e24ea485b26e..25070e6b042c9 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1442,11 +1442,12 @@ impl<'tcx> TyCtxt<'tcx> { } /// Allocates a read-only byte or string literal for `mir::interpret`. - pub fn allocate_bytes(self, bytes: &[u8]) -> interpret::AllocId { + /// Returns the same `AllocId` if called again with the same bytes. + pub fn allocate_bytes_dedup(self, bytes: &[u8]) -> interpret::AllocId { // Create an allocation that just contains these bytes. let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes); let alloc = self.mk_const_alloc(alloc); - self.reserve_and_set_memory_alloc(alloc) + self.reserve_and_set_memory_dedup(alloc) } /// Returns a range of the start/end indices specified with the diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 3b69058d3cb4a..be62a3d373656 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -140,7 +140,7 @@ fn lit_to_mir_constant<'tcx>( ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } } (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { - let id = tcx.allocate_bytes(data); + let id = tcx.allocate_bytes_dedup(data); ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) } (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => From 9c3c278b545d9e03939e3bfcbebb256758b9a8b9 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 13 Jul 2024 01:06:40 +0000 Subject: [PATCH 07/11] Add support for `Result<&T, _>' --- .../src/fn_ctxt/suggestions.rs | 19 +++++++++---------- ...ransforming-option-ref-issue-127545.stderr | 4 ++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 15a1a18daa70a..faa6d06ccaf2a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1451,9 +1451,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else { return; }; - if !self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) { + let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) { + "Option" + } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) { + "Result" + } else { return; - } + }; if call_ident.map_or(true, |ident| ident.name != sym::unwrap_or) { return; @@ -1484,14 +1488,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(snip) => (snip, Applicability::MachineApplicable), Err(_) => ("/* _ */".to_owned(), Applicability::MaybeIncorrect), }; - let sugg = &format!("map_or({provided_snip}, |v| v)"); - err.span_suggestion_verbose( - error_span, - "use `Option::map_or` to deref inner value of `Option`", - sugg, - applicability, - ); - return; + let sugg = format!("map_or({provided_snip}, |v| v)"); + let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`"); + err.span_suggestion_verbose(error_span, msg, sugg, applicability); } /// Suggest wrapping the block in square brackets instead of curly braces diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr index 0a6d47339d8e8..1790fc1249ab7 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr @@ -80,6 +80,10 @@ LL | arg.unwrap_or(&[]) | this argument influences the return type of `unwrap_or` note: method defined here --> $SRC_DIR/core/src/result.rs:LL:COL +help: use `Result::map_or` to deref inner value of `Result` + | +LL | arg.map_or(&[], |v| v) + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 4 previous errors From bdc9df247841e259c24e95fd81754fb61e332c65 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 13 Jul 2024 01:18:21 +0000 Subject: [PATCH 08/11] Use multipart_suggestion to avoid place holder in span_to_snippet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CO-AUTHORED-BY: Esteban Küber --- .../src/fn_ctxt/suggestions.rs | 20 +++++++++++-------- ...ransforming-option-ref-issue-127545.stderr | 6 +++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index faa6d06ccaf2a..b3b4c5a56fbd3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1459,7 +1459,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; }; - if call_ident.map_or(true, |ident| ident.name != sym::unwrap_or) { + let Some(call_ident) = call_ident else { + return; + }; + if call_ident.name != sym::unwrap_or { return; } @@ -1483,14 +1486,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !self.can_coerce(expected_ty, dummy_ty) { return; } - let (provided_snip, applicability) = - match self.tcx.sess.source_map().span_to_snippet(provided_expr.span) { - Ok(snip) => (snip, Applicability::MachineApplicable), - Err(_) => ("/* _ */".to_owned(), Applicability::MaybeIncorrect), - }; - let sugg = format!("map_or({provided_snip}, |v| v)"); let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`"); - err.span_suggestion_verbose(error_span, msg, sugg, applicability); + err.multipart_suggestion_verbose( + msg, + vec![ + (call_ident.span, "map_or".to_owned()), + (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()), + ], + Applicability::MachineApplicable, + ); } /// Suggest wrapping the block in square brackets instead of curly braces diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr index 1790fc1249ab7..ad423f86ef9e3 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr @@ -35,7 +35,7 @@ note: method defined here help: use `Option::map_or` to deref inner value of `Option` | LL | arg.map_or(&[], |v| v) - | ~~~~~~~~~~~~~~~~~~ + | ~~~~~~ +++++++ error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:13:19 @@ -59,7 +59,7 @@ note: method defined here help: use `Option::map_or` to deref inner value of `Option` | LL | arg.map_or(v, |v| v) - | ~~~~~~~~~~~~~~~~ + | ~~~~~~ +++++++ error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:17:19 @@ -83,7 +83,7 @@ note: method defined here help: use `Result::map_or` to deref inner value of `Result` | LL | arg.map_or(&[], |v| v) - | ~~~~~~~~~~~~~~~~~~ + | ~~~~~~ +++++++ error: aborting due to 4 previous errors From ee86e2d624a61b634c14dd931b7ae2532a6634ff Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:00:42 -0400 Subject: [PATCH 09/11] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/embedded-book | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- src/doc/rustc-dev-guide | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/book b/src/doc/book index f1e49bf7a8ea6..67fa536768013 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit f1e49bf7a8ea6c31ce016a52b8a4f6e1ffcfbc64 +Subproject commit 67fa536768013d9d5a13f3a06790521d511ef711 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 941db8b3df45f..5454de3d12b9c 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 941db8b3df45fd46cd87b50a5c86714b91dcde9c +Subproject commit 5454de3d12b9ccc6375b629cf7ccda8264640aac diff --git a/src/doc/embedded-book b/src/doc/embedded-book index b10c6acaf0f43..019f3928d8b93 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit b10c6acaf0f43481f6600e95d4b5013446e29f7a +Subproject commit 019f3928d8b939ec71b63722dcc2e46330156441 diff --git a/src/doc/reference b/src/doc/reference index 1ae3deebc3ac1..2a7931476643e 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 1ae3deebc3ac16e276b6558e01420f8e605def08 +Subproject commit 2a7931476643e2a07bf9796e14bbeee3c9e589b8 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 658c6c27cb975..89aecb6951b77 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 658c6c27cb975b92227936024816986c2d3716fb +Subproject commit 89aecb6951b77bc746da73df8c9f2b2ceaad494a diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index d6e3a32a557db..0c4d55cb59fe4 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit d6e3a32a557db5902e714604def8015d6bb7e0f7 +Subproject commit 0c4d55cb59fe440d1a630e4e5774d043968edb3f From 3051436a3c5e7717c840bf04f6f147223739566b Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 15 Jul 2024 10:58:58 -0700 Subject: [PATCH 10/11] Update reference to fix toolstate --- src/doc/reference | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/reference b/src/doc/reference index 2a7931476643e..e2f0bdc403186 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 2a7931476643e2a07bf9796e14bbeee3c9e589b8 +Subproject commit e2f0bdc4031866734661dcdb548184bde1450baf From 317952677ad6a80b3751a075f42ffd2a8d142a84 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 13 Feb 2024 05:24:32 +0800 Subject: [PATCH 11/11] Suggest a borrow when using dbg --- .../src/diagnostics/conflict_errors.rs | 65 +++++++++- tests/ui/borrowck/dbg-issue-120327.rs | 68 ++++++++++ tests/ui/borrowck/dbg-issue-120327.stderr | 117 ++++++++++++++++++ .../dbg-macro-move-semantics.stderr | 4 + 4 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 tests/ui/borrowck/dbg-issue-120327.rs create mode 100644 tests/ui/borrowck/dbg-issue-120327.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 1cb74849017ab..c7f6840e401c6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -4,7 +4,7 @@ #![allow(rustc::untranslatable_diagnostic)] use either::Either; -use hir::ClosureKind; +use hir::{ClosureKind, Path}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan}; @@ -16,6 +16,7 @@ use rustc_hir::{CoroutineKind, CoroutineSource, LangItem}; use rustc_middle::bug; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::VarDebugInfoContents; use rustc_middle::mir::{ self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory, FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind, @@ -546,7 +547,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { self.suggest_cloning(err, ty, expr, None, Some(move_spans)); } } - if let Some(pat) = finder.pat { + + self.suggest_ref_for_dbg_args(expr, place, move_span, err); + + // it's useless to suggest inserting `ref` when the span don't comes from local code + if let Some(pat) = finder.pat + && !move_span.is_dummy() + && !self.infcx.tcx.sess.source_map().is_imported(move_span) + { *in_pattern = true; let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())]; if let Some(pat) = finder.parent_pat { @@ -561,6 +569,59 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { } } + // for dbg!(x) which may take ownership, suggest dbg!(&x) instead + // but here we actually do not check whether the macro name is `dbg!` + // so that we may extend the scope a bit larger to cover more cases + fn suggest_ref_for_dbg_args( + &self, + body: &hir::Expr<'_>, + place: &Place<'tcx>, + move_span: Span, + err: &mut Diag<'infcx>, + ) { + let var_info = self.body.var_debug_info.iter().find(|info| match info.value { + VarDebugInfoContents::Place(ref p) => p == place, + _ => false, + }); + let arg_name = if let Some(var_info) = var_info { + var_info.name + } else { + return; + }; + struct MatchArgFinder { + expr_span: Span, + match_arg_span: Option, + arg_name: Symbol, + } + impl Visitor<'_> for MatchArgFinder { + fn visit_expr(&mut self, e: &hir::Expr<'_>) { + // dbg! is expanded into a match pattern, we need to find the right argument span + if let hir::ExprKind::Match(expr, ..) = &e.kind + && let hir::ExprKind::Path(hir::QPath::Resolved( + _, + path @ Path { segments: [seg], .. }, + )) = &expr.kind + && seg.ident.name == self.arg_name + && self.expr_span.source_callsite().contains(expr.span) + { + self.match_arg_span = Some(path.span); + } + hir::intravisit::walk_expr(self, e); + } + } + + let mut finder = MatchArgFinder { expr_span: move_span, match_arg_span: None, arg_name }; + finder.visit_expr(body); + if let Some(macro_arg_span) = finder.match_arg_span { + err.span_suggestion_verbose( + macro_arg_span.shrink_to_lo(), + "consider borrowing instead of transferring ownership", + "&", + Applicability::MachineApplicable, + ); + } + } + fn report_use_of_uninitialized( &self, mpi: MovePathIndex, diff --git a/tests/ui/borrowck/dbg-issue-120327.rs b/tests/ui/borrowck/dbg-issue-120327.rs new file mode 100644 index 0000000000000..2de43f634877a --- /dev/null +++ b/tests/ui/borrowck/dbg-issue-120327.rs @@ -0,0 +1,68 @@ +fn s() -> String { + let a = String::new(); + dbg!(a); + return a; //~ ERROR use of moved value: +} + +fn m() -> String { + let a = String::new(); + dbg!(1, 2, a, 1, 2); + return a; //~ ERROR use of moved value: +} + +fn t(a: String) -> String { + let b: String = "".to_string(); + dbg!(a, b); + return b; //~ ERROR use of moved value: +} + +fn x(a: String) -> String { + let b: String = "".to_string(); + dbg!(a, b); + return a; //~ ERROR use of moved value: +} + +macro_rules! my_dbg { + () => { + eprintln!("[{}:{}:{}]", file!(), line!(), column!()) + }; + ($val:expr $(,)?) => { + match $val { + tmp => { + eprintln!("[{}:{}:{}] {} = {:#?}", + file!(), line!(), column!(), stringify!($val), &tmp); + tmp + } + } + }; + ($($val:expr),+ $(,)?) => { + ($(my_dbg!($val)),+,) + }; +} + +fn test_my_dbg() -> String { + let b: String = "".to_string(); + my_dbg!(b, 1); + return b; //~ ERROR use of moved value: +} + +fn test_not_macro() -> String { + let a = String::new(); + let _b = match a { + tmp => { + eprintln!("dbg: {}", tmp); + tmp + } + }; + return a; //~ ERROR use of moved value: +} + +fn get_expr(_s: String) {} + +fn test() { + let a: String = "".to_string(); + let _res = get_expr(dbg!(a)); + let _l = a.len(); //~ ERROR borrow of moved value +} + +fn main() {} diff --git a/tests/ui/borrowck/dbg-issue-120327.stderr b/tests/ui/borrowck/dbg-issue-120327.stderr new file mode 100644 index 0000000000000..efacc0c3f1341 --- /dev/null +++ b/tests/ui/borrowck/dbg-issue-120327.stderr @@ -0,0 +1,117 @@ +error[E0382]: use of moved value: `a` + --> $DIR/dbg-issue-120327.rs:4:12 + | +LL | let a = String::new(); + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | dbg!(a); + | ------- value moved here +LL | return a; + | ^ value used here after move + | +help: consider borrowing instead of transferring ownership + | +LL | dbg!(&a); + | + + +error[E0382]: use of moved value: `a` + --> $DIR/dbg-issue-120327.rs:10:12 + | +LL | let a = String::new(); + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | dbg!(1, 2, a, 1, 2); + | ------------------- value moved here +LL | return a; + | ^ value used here after move + | +help: consider borrowing instead of transferring ownership + | +LL | dbg!(1, 2, &a, 1, 2); + | + + +error[E0382]: use of moved value: `b` + --> $DIR/dbg-issue-120327.rs:16:12 + | +LL | let b: String = "".to_string(); + | - move occurs because `b` has type `String`, which does not implement the `Copy` trait +LL | dbg!(a, b); + | ---------- value moved here +LL | return b; + | ^ value used here after move + | +help: consider borrowing instead of transferring ownership + | +LL | dbg!(a, &b); + | + + +error[E0382]: use of moved value: `a` + --> $DIR/dbg-issue-120327.rs:22:12 + | +LL | fn x(a: String) -> String { + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | let b: String = "".to_string(); +LL | dbg!(a, b); + | ---------- value moved here +LL | return a; + | ^ value used here after move + | +help: consider borrowing instead of transferring ownership + | +LL | dbg!(&a, b); + | + + +error[E0382]: use of moved value: `b` + --> $DIR/dbg-issue-120327.rs:46:12 + | +LL | tmp => { + | --- value moved here +... +LL | let b: String = "".to_string(); + | - move occurs because `b` has type `String`, which does not implement the `Copy` trait +LL | my_dbg!(b, 1); +LL | return b; + | ^ value used here after move + | +help: consider borrowing instead of transferring ownership + | +LL | my_dbg!(&b, 1); + | + +help: borrow this binding in the pattern to avoid moving the value + | +LL | ref tmp => { + | +++ + +error[E0382]: use of moved value: `a` + --> $DIR/dbg-issue-120327.rs:57:12 + | +LL | let a = String::new(); + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | let _b = match a { +LL | tmp => { + | --- value moved here +... +LL | return a; + | ^ value used here after move + | +help: borrow this binding in the pattern to avoid moving the value + | +LL | ref tmp => { + | +++ + +error[E0382]: borrow of moved value: `a` + --> $DIR/dbg-issue-120327.rs:65:14 + | +LL | let a: String = "".to_string(); + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | let _res = get_expr(dbg!(a)); + | ------- value moved here +LL | let _l = a.len(); + | ^ value borrowed here after move + | +help: consider borrowing instead of transferring ownership + | +LL | let _res = get_expr(dbg!(&a)); + | + + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr index c2b9899e20db3..f515cb62c7cde 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr @@ -9,6 +9,10 @@ LL | let _ = dbg!(a); | ^^^^^^^ value used here after move | = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider borrowing instead of transferring ownership + | +LL | let _ = dbg!(&a); + | + error: aborting due to 1 previous error