diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 6d95fbcfad00d..68dba86029126 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2362,11 +2362,7 @@ impl FileWithAnnotatedLines { let label = label.as_ref().map(|m| { normalize_whitespace( - &emitter - .translate_message(m, &args) - .map_err(Report::new) - .unwrap() - .to_string(), + &emitter.translate_message(m, &args).map_err(Report::new).unwrap(), ) }); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index bb21b027cc9fb..9f439a2b32aaf 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -13,7 +13,7 @@ use crate::errors::{ YieldExprOutsideOfCoroutine, }; use crate::fatally_break_rust; -use crate::method::{MethodCallComponents, SelfSource}; +use crate::method::SelfSource; use crate::type_error_struct; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; use crate::{ @@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let tcx = self.tcx; let (res, opt_ty, segs) = - self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); + self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span, Some(args)); let ty = match res { Res::Err => { self.suggest_assoc_method_call(segs); @@ -1332,7 +1332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segment.ident, SelfSource::MethodCall(rcvr), error, - Some(MethodCallComponents { receiver: rcvr, args, full_expr: expr }), + Some(args), expected, false, ) { @@ -1551,21 +1551,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => {} } - // If someone calls a const fn, they can extract that call out into a separate constant (or a const - // block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck. - let is_const_fn = match element.kind { + // If someone calls a const fn or constructs a const value, they can extract that + // out into a separate constant (or a const block in the future), so we check that + // to tell them that in the diagnostic. Does not affect typeck. + let is_constable = match element.kind { hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { - ty::FnDef(def_id, _) => tcx.is_const_fn(def_id), - _ => false, + ty::FnDef(def_id, _) if tcx.is_const_fn(def_id) => traits::IsConstable::Fn, + _ => traits::IsConstable::No, }, - _ => false, + hir::ExprKind::Path(qpath) => { + match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) { + Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor, + _ => traits::IsConstable::No, + } + } + _ => traits::IsConstable::No, }; // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we // don't copy that one element, we move it. Only check for Copy if the length is larger. if count.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) { let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); - let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn }; + let code = traits::ObligationCauseCode::RepeatElementCopy { + is_constable, + elt_type: element_ty, + elt_span: element.span, + elt_stmt_span: self + .tcx + .hir() + .parent_iter(element.hir_id) + .find_map(|(_, node)| match node { + hir::Node::Item(it) => Some(it.span), + hir::Node::Stmt(stmt) => Some(stmt.span), + _ => None, + }) + .expect("array repeat expressions must be inside an item or statement"), + }; self.require_type_meets(element_ty, element.span, code, lang_item); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index b5a07f0d3e9ec..750ed2c34913f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -797,6 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &'tcx QPath<'tcx>, hir_id: hir::HirId, span: Span, + args: Option<&'tcx [hir::Expr<'tcx>]>, ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { debug!( "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}", @@ -898,7 +899,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name, SelfSource::QPath(qself), error, - None, + args, Expectation::NoExpectation, trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021 ) { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 86a0e95de1dab..d69d2529b18d8 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -7,7 +7,7 @@ mod prelude2021; pub mod probe; mod suggest; -pub use self::suggest::{MethodCallComponents, SelfSource}; +pub use self::suggest::SelfSource; pub use self::MethodError::*; use crate::errors::OpMethodGenericParams; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 87dfcf09cdd3e..b1a2df8ace40a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -34,7 +34,7 @@ use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::DefIdSet; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; -use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span}; +use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; @@ -50,15 +50,6 @@ use rustc_hir::intravisit::Visitor; use std::cmp::{self, Ordering}; use std::iter; -/// After identifying that `full_expr` is a method call, we use this type to keep the expression's -/// components readily available to us to point at the right place in diagnostics. -#[derive(Debug, Clone, Copy)] -pub struct MethodCallComponents<'tcx> { - pub receiver: &'tcx hir::Expr<'tcx>, - pub args: &'tcx [hir::Expr<'tcx>], - pub full_expr: &'tcx hir::Expr<'tcx>, -} - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { let tcx = self.tcx; @@ -124,7 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name: Ident, source: SelfSource<'tcx>, error: MethodError<'tcx>, - args: Option>, + args: Option<&'tcx [hir::Expr<'tcx>]>, expected: Expectation<'tcx>, trait_missing_method: bool, ) -> Option> { @@ -167,6 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_candidates_on_method_error( rcvr_ty, item_name, + source, args, span, &mut err, @@ -266,23 +258,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_missing_writer( &self, rcvr_ty: Ty<'tcx>, - args: MethodCallComponents<'tcx>, + rcvr_expr: &hir::Expr<'tcx>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty); let mut err = struct_span_err!( self.tcx.sess, - args.receiver.span, + rcvr_expr.span, E0599, "cannot write into `{}`", ty_str ); err.span_note( - args.receiver.span, + rcvr_expr.span, "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method", ); - if let ExprKind::Lit(_) = args.receiver.kind { + if let ExprKind::Lit(_) = rcvr_expr.kind { err.span_help( - args.receiver.span.shrink_to_lo(), + rcvr_expr.span.shrink_to_lo(), "a writer is needed before this format string", ); }; @@ -296,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, item_name: Ident, source: SelfSource<'tcx>, - args: Option>, + args: Option<&'tcx [hir::Expr<'tcx>]>, sugg_span: Span, no_match_data: &mut NoMatchData<'tcx>, expected: Expectation<'tcx>, @@ -377,23 +369,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) }) && item_name.name == Symbol::intern("write_fmt"); - let mut err = if is_write && let Some(args) = args { - self.suggest_missing_writer(rcvr_ty, args) - } else { - tcx.sess.create_err(NoAssociatedItem { - span, - item_kind, - item_name, - ty_prefix: if trait_missing_method { - // FIXME(mu001999) E0599 maybe not suitable here because it is for types - Cow::from("trait") - } else { - rcvr_ty.prefix_string(self.tcx) - }, - ty_str: ty_str_reported, - trait_missing_method, - }) - }; + let mut err = + if is_write && let SelfSource::MethodCall(rcvr_expr) = source + { + self.suggest_missing_writer(rcvr_ty, rcvr_expr) + } else { + tcx.sess.create_err(NoAssociatedItem { + span, + item_kind, + item_name, + ty_prefix: if trait_missing_method { + // FIXME(mu001999) E0599 maybe not suitable here because it is for types + Cow::from("trait") + } else { + rcvr_ty.prefix_string(self.tcx) + }, + ty_str: ty_str_reported, + trait_missing_method, + }) + }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } @@ -409,7 +403,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.downgrade_to_delayed_bug(); } - self.find_builder_fn(&mut err, rcvr_ty, source); + if matches!(source, SelfSource::QPath(_)) && args.is_some() { + self.find_builder_fn(&mut err, rcvr_ty); + } + if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { err.help(format!( "method `poll` found on `Pin<&mut {ty_str}>`, \ @@ -523,6 +520,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_candidates_on_method_error( rcvr_ty, item_name, + source, args, span, &mut err, @@ -533,6 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_candidates_on_method_error( rcvr_ty, item_name, + source, args, span, &mut err, @@ -976,7 +975,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unsatisfied_bounds = true; } } else if let ty::Adt(def, targs) = rcvr_ty.kind() - && let Some(args) = args + && let SelfSource::MethodCall(rcvr_expr) = source { // This is useful for methods on arbitrary self types that might have a simple // mutability difference, like calling a method on `Pin<&mut Self>` that is on @@ -999,8 +998,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty, &item_segment, span, - args.full_expr, - args.receiver, + tcx.hir().get_parent(rcvr_expr.hir_id).expect_expr(), + rcvr_expr, ) { err.span_note( tcx.def_span(method.def_id), @@ -1169,7 +1168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, rcvr_ty, item_name, - args.map(|MethodCallComponents { args, .. }| args.len() + 1), + args.map(|args| args.len() + 1), source, no_match_data.out_of_scope_traits.clone(), &unsatisfied_predicates, @@ -1250,7 +1249,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, rcvr_ty: Ty<'tcx>, item_name: Ident, - args: Option>, + self_source: SelfSource<'tcx>, + args: Option<&'tcx [hir::Expr<'tcx>]>, span: Span, err: &mut Diagnostic, sources: &mut Vec, @@ -1320,38 +1320,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(sugg_span) = sugg_span && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) - { - let path = self.tcx.def_path_str(trait_ref.skip_binder().def_id); - - let ty = match item.kind { - ty::AssocKind::Const | ty::AssocKind::Type => impl_ty, - ty::AssocKind::Fn => self - .tcx - .fn_sig(item.def_id) - .instantiate_identity() - .inputs() - .skip_binder() - .get(0) - .filter(|ty| ty.is_ref() && !rcvr_ty.is_ref()) - .copied() - .unwrap_or(rcvr_ty), - }; - if let Some(sugg) = print_disambiguation_help( - item_name, - args, + && let Some(sugg) = print_disambiguation_help( + self.tcx, err, - path, - ty, - Some(impl_ty), - item.kind, - self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id), - sugg_span, + self_source, + args, + trait_ref.instantiate( + self.tcx, + self.fresh_args_for_item(sugg_span, impl_did) + ).with_self_ty(self.tcx, rcvr_ty), idx, - self.tcx.sess.source_map(), - item.fn_has_self_parameter, - ) { - suggs.push(sugg); - } + sugg_span, + item, + ) + { + suggs.push(sugg); } } CandidateSource::Trait(trait_did) => { @@ -1373,24 +1356,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_note(item_span, msg); None }; - if let Some(sugg_span) = sugg_span { - let path = self.tcx.def_path_str(trait_did); - if let Some(sugg) = print_disambiguation_help( - item_name, - args, + if let Some(sugg_span) = sugg_span + && let Some(sugg) = print_disambiguation_help( + self.tcx, err, - path, - rcvr_ty, - None, - item.kind, - self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id), - sugg_span, + self_source, + args, + ty::TraitRef::new( + self.tcx, + trait_did, + self.fresh_args_for_item(sugg_span, trait_did) + ).with_self_ty(self.tcx, rcvr_ty), idx, - self.tcx.sess.source_map(), - item.fn_has_self_parameter, - ) { - suggs.push(sugg); - } + sugg_span, + item, + ) + { + suggs.push(sugg); } } } @@ -1410,18 +1392,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Look at all the associated functions without receivers in the type's inherent impls /// to look for builders that return `Self`, `Option` or `Result`. - fn find_builder_fn(&self, err: &mut Diagnostic, rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>) { + fn find_builder_fn(&self, err: &mut Diagnostic, rcvr_ty: Ty<'tcx>) { let ty::Adt(adt_def, _) = rcvr_ty.kind() else { return; }; - let SelfSource::QPath(ty) = source else { - return; - }; - let hir = self.tcx.hir(); - if let Some(Node::Pat(_)) = hir.find(hir.parent_id(ty.hir_id)) { - // Do not suggest a fn call when a pattern is expected. - return; - } let mut items = self .tcx .inherent_impls(adt_def.did()) @@ -1504,7 +1478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>, item_name: Ident, - args: Option>, + args: Option<&'tcx [hir::Expr<'tcx>]>, sugg_span: Span, ) { let mut has_unsuggestable_args = false; @@ -1578,38 +1552,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; let mut applicability = Applicability::MachineApplicable; - let args = if let Some(MethodCallComponents { receiver, args, .. }) = args { - // The first arg is the same kind as the receiver - let explicit_args = if first_arg.is_some() { - std::iter::once(receiver).chain(args.iter()).collect::>() + let args = if let SelfSource::MethodCall(receiver) = source + && let Some(args) = args + { + // The first arg is the same kind as the receiver + let explicit_args = if first_arg.is_some() { + std::iter::once(receiver).chain(args.iter()).collect::>() + } else { + // There is no `Self` kind to infer the arguments from + if has_unsuggestable_args { + applicability = Applicability::HasPlaceholders; + } + args.iter().collect() + }; + format!( + "({}{})", + first_arg.unwrap_or(""), + explicit_args + .iter() + .map(|arg| self + .tcx + .sess + .source_map() + .span_to_snippet(arg.span) + .unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "_".to_owned() + })) + .collect::>() + .join(", "), + ) } else { - // There is no `Self` kind to infer the arguments from - if has_unsuggestable_args { - applicability = Applicability::HasPlaceholders; - } - args.iter().collect() + applicability = Applicability::HasPlaceholders; + "(...)".to_owned() }; - format!( - "({}{})", - first_arg.unwrap_or(""), - explicit_args - .iter() - .map(|arg| self - .tcx - .sess - .source_map() - .span_to_snippet(arg.span) - .unwrap_or_else(|_| { - applicability = Applicability::HasPlaceholders; - "_".to_owned() - })) - .collect::>() - .join(", "), - ) - } else { - applicability = Applicability::HasPlaceholders; - "(...)".to_owned() - }; err.span_suggestion( sugg_span, "use associated function syntax instead", @@ -3267,56 +3243,59 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec { } fn print_disambiguation_help<'tcx>( - item_name: Ident, - args: Option>, + tcx: TyCtxt<'tcx>, err: &mut Diagnostic, - trait_name: String, - rcvr_ty: Ty<'_>, - impl_self_ty: Option>, - kind: ty::AssocKind, - def_kind_descr: &'static str, + source: SelfSource<'tcx>, + args: Option<&'tcx [hir::Expr<'tcx>]>, + trait_ref: ty::TraitRef<'tcx>, + candidate_idx: Option, span: Span, - candidate: Option, - source_map: &source_map::SourceMap, - fn_has_self_parameter: bool, + item: ty::AssocItem, ) -> Option { + let trait_ref = if item.fn_has_self_parameter { + trait_ref.print_only_trait_name().to_string() + } else { + format!("<{} as {}>", trait_ref.args[0], trait_ref.print_only_trait_name()) + }; Some( - if let (ty::AssocKind::Fn, Some(MethodCallComponents { receiver, args, .. })) = (kind, args) + if matches!(item.kind, ty::AssocKind::Fn) + && let SelfSource::MethodCall(receiver) = source + && let Some(args) = args { + let def_kind_descr = tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id); + let item_name = item.ident(tcx); + let rcvr_ref = tcx.fn_sig(item.def_id).skip_binder().skip_binder().inputs()[0] + .ref_mutability() + .map_or("", |mutbl| mutbl.ref_prefix_str()); let args = format!( "({}{})", - rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()), + rcvr_ref, std::iter::once(receiver) .chain(args.iter()) - .map(|arg| source_map + .map(|arg| tcx + .sess + .source_map() .span_to_snippet(arg.span) .unwrap_or_else(|_| { "_".to_owned() })) .collect::>() .join(", "), ); - let trait_name = if !fn_has_self_parameter && let Some(impl_self_ty) = impl_self_ty { - format!("<{impl_self_ty} as {trait_name}>") - } else { - trait_name - }; err.span_suggestion_verbose( span, format!( "disambiguate the {def_kind_descr} for {}", - if let Some(candidate) = candidate { + if let Some(candidate) = candidate_idx { format!("candidate #{candidate}") } else { "the candidate".to_string() }, ), - format!("{trait_name}::{item_name}{args}"), + format!("{trait_ref}::{item_name}{args}"), Applicability::HasPlaceholders, ); return None; - } else if let Some(impl_self_ty) = impl_self_ty { - format!("<{impl_self_ty} as {trait_name}>::") } else { - format!("{trait_name}::") + format!("{trait_ref}::") }, ) } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 50684482c0d55..b30f9b82fbbf7 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -166,9 +166,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; let path_res = match &pat.kind { - PatKind::Path(qpath) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) - } + PatKind::Path(qpath) => Some( + self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None), + ), _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); @@ -1060,7 +1060,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Resolve the path and check the definition for errors. let (res, opt_ty, segments) = - self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span); + self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None); if res == Res::Err { let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index e20e9d9312c1b..cdde6a596a80e 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -2,7 +2,8 @@ use crate::mir; use crate::query::CyclePlaceholder; use crate::traits; use crate::ty::{self, Ty}; -use std::mem::{size_of, transmute_copy, MaybeUninit}; +use std::intrinsics::transmute_unchecked; +use std::mem::{size_of, MaybeUninit}; #[derive(Copy, Clone)] pub struct Erased { @@ -29,8 +30,15 @@ pub fn erase(src: T) -> Erase { }; Erased::<::Result> { + // `transmute_unchecked` is needed here because it does not have `transmute`'s size check + // (and thus allows to transmute between `T` and `MaybeUninit`) (we do the size + // check ourselves in the `const` block above). + // + // `transmute_copy` is also commonly used for this (and it would work here since + // `EraseType: Copy`), but `transmute_unchecked` better explains the intent. + // // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes. - data: unsafe { transmute_copy(&src) }, + data: unsafe { transmute_unchecked::>(src) }, } } @@ -38,22 +46,24 @@ pub fn erase(src: T) -> Erase { #[inline(always)] pub fn restore(value: Erase) -> T { let value: Erased<::Result> = value; + // See comment in `erase` for why we use `transmute_unchecked`. + // // SAFETY: Due to the use of impl Trait in `Erase` the only way to safely create an instance // of `Erase` is to call `erase`, so we know that `value.data` is a valid instance of `T` of // the right size. - unsafe { transmute_copy(&value.data) } + unsafe { transmute_unchecked::, T>(value.data) } } impl EraseType for &'_ T { - type Result = [u8; size_of::<*const ()>()]; + type Result = [u8; size_of::<&'static ()>()]; } impl EraseType for &'_ [T] { - type Result = [u8; size_of::<*const [()]>()]; + type Result = [u8; size_of::<&'static [()]>()]; } impl EraseType for &'_ ty::List { - type Result = [u8; size_of::<*const ()>()]; + type Result = [u8; size_of::<&'static ty::List<()>>()]; } impl EraseType for &'_ rustc_index::IndexSlice { diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 9e2ff3820af64..6cd75e0872763 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -305,9 +305,14 @@ pub enum ObligationCauseCode<'tcx> { SizedCoroutineInterior(LocalDefId), /// `[expr; N]` requires `type_of(expr): Copy`. RepeatElementCopy { - /// If element is a `const fn` we display a help message suggesting to move the - /// function call to a new `const` item while saying that `T` doesn't implement `Copy`. - is_const_fn: bool, + /// If element is a `const fn` or const ctor we display a help message suggesting + /// to move it to a new `const` item while saying that `T` doesn't implement `Copy`. + is_constable: IsConstable, + elt_type: Ty<'tcx>, + elt_span: Span, + /// Span of the statement/item in which the repeat expression occurs. We can use this to + /// place a `const` declaration before it + elt_stmt_span: Span, }, /// Types of fields (other than the last, except for packed structs) in a struct must be sized. @@ -455,6 +460,21 @@ pub enum ObligationCauseCode<'tcx> { TypeAlias(InternedObligationCauseCode<'tcx>, Span, DefId), } +/// Whether a value can be extracted into a const. +/// Used for diagnostics around array repeat expressions. +#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum IsConstable { + No, + /// Call to a const fn + Fn, + /// Use of a const ctor + Ctor, +} + +crate::TrivialTypeTraversalAndLiftImpls! { + IsConstable, +} + /// The 'location' at which we try to perform HIR-based wf checking. /// This information is used to obtain an `hir::Ty`, which /// we can walk in order to obtain precise spans for any diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 77a50fa9276db..0094825fc70d4 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -274,7 +274,7 @@ pub fn suggest_constraining_type_params<'a>( span, if span_to_replace.is_some() { constraint.clone() - } else if constraint.starts_with("<") { + } else if constraint.starts_with('<') { constraint.to_string() } else if bound_list_non_empty { format!(" + {constraint}") diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index edcc22d56c682..93db6cfc4635d 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1545,7 +1545,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let (span, sugg, post) = if let SuggestionTarget::SimilarlyNamed = suggestion.target && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) && let Some(span) = suggestion.span - && let Some(candidate) = suggestion.candidate.as_str().strip_prefix("_") + && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_') && snippet == candidate { // When the suggested binding change would be from `x` to `_x`, suggest changing the diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c94e0d0ed3942..930fb693eb6d2 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2477,7 +2477,7 @@ pub fn parse_externs( let mut error = handler.early_struct_error(format!( "crate name `{name}` passed to `--extern` is not a valid ASCII identifier" )); - let adjusted_name = name.replace("-", "_"); + let adjusted_name = name.replace('-', "_"); if crate::utils::is_ascii_ident(&adjusted_name) { error.help(format!( "consider replacing the dashes with underscores: `{adjusted_name}`" diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 78ceddcd26308..6b09bc89873ea 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -28,6 +28,7 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime}; use rustc_middle::hir::map; +use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, @@ -2768,20 +2769,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { )); } } - ObligationCauseCode::RepeatElementCopy { is_const_fn } => { + ObligationCauseCode::RepeatElementCopy { is_constable, elt_type, elt_span, elt_stmt_span } => { err.note( "the `Copy` trait is required because this value will be copied for each element of the array", ); - - if is_const_fn { - err.help( - "consider creating a new `const` item and initializing it with the result \ - of the function call to be used in the repeat position, like \ - `const VAL: Type = const_fn();` and `let x = [VAL; 42];`", - ); + let value_kind = match is_constable { + IsConstable::Fn => Some("the result of the function call"), + IsConstable::Ctor => Some("the result of the constructor"), + _ => None + }; + let sm = tcx.sess.source_map(); + if let Some(value_kind) = value_kind && + let Ok(snip) = sm.span_to_snippet(elt_span) + { + let help_msg = format!( + "consider creating a new `const` item and initializing it with {value_kind} \ + to be used in the repeat position"); + let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default(); + err.multipart_suggestion(help_msg, vec![ + (elt_stmt_span.shrink_to_lo(), format!("const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}")), + (elt_span, "ARRAY_REPEAT_VALUE".to_string()) + ], Applicability::MachineApplicable); } - if self.tcx.sess.is_nightly_build() && is_const_fn { + if self.tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn|IsConstable::Ctor) { err.help( "create an inline `const` block, see RFC #2920 \ for more information", diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 013814b1f7dca..974ba1e3bd939 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -599,7 +599,7 @@ fn build_module_items( let prim_ty = clean::PrimitiveType::from(p); items.push(clean::Item { name: None, - attrs: Box::new(clean::Attributes::default()), + attrs: Box::default(), // We can use the item's `DefId` directly since the only information ever used // from it is `DefId.krate`. item_id: ItemId::DefId(did), diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index a718cb37d89f8..88ee4e3a2abdc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1327,6 +1327,7 @@ impl WherePredicate { pub(crate) enum GenericParamDefKind { Lifetime { outlives: ThinVec }, Type { did: DefId, bounds: ThinVec, default: Option>, synthetic: bool }, + // Option> makes this type smaller than `Option` would. Const { ty: Box, default: Option>, is_host_effect: bool }, } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index dea7bfaf7e22f..ca1b782492c4f 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -588,7 +588,7 @@ pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool { /// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable. pub(crate) const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL"); pub(crate) static DOC_CHANNEL: Lazy<&'static str> = - Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit("/").filter(|c| !c.is_empty()).next().unwrap()); + Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').filter(|c| !c.is_empty()).next().unwrap()); /// Render a sequence of macro arms in a format suitable for displaying to the user /// as part of an item declaration. diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 3e58dd96ed92a..d2c7c578c083c 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -356,15 +356,12 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; let content = format!( "

List of all crates

    {}
", - krates - .iter() - .map(|s| { - format!( - "
  • {s}
  • ", - trailing_slash = ensure_trailing_slash(s), - ) - }) - .collect::() + krates.iter().format_with("", |k, f| { + f(&format_args!( + "
  • {k}
  • ", + trailing_slash = ensure_trailing_slash(k), + )) + }) ); let v = layout::render(&shared.layout, &page, "", content, &shared.style_files); shared.fs.write(dst, v)?; diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index 0c15bf5f764ff..472781e7d2294 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -87,7 +87,7 @@ fn check_redundant_explicit_link<'md>( let link_data = collect_link_data(&mut offset_iter); if let Some(resolvable_link) = link_data.resolvable_link.as_ref() { - if &link_data.display_link.replace("`", "") != resolvable_link { + if &link_data.display_link.replace('`', "") != resolvable_link { // Skips if display link does not match to actual // resolvable link, usually happens if display link // has several segments, e.g. diff --git a/tests/ui/consts/const-blocks/fn-call-in-non-const.stderr b/tests/ui/consts/const-blocks/fn-call-in-non-const.stderr index 174103eeba4c4..eb8b8ac753430 100644 --- a/tests/ui/consts/const-blocks/fn-call-in-non-const.stderr +++ b/tests/ui/consts/const-blocks/fn-call-in-non-const.stderr @@ -6,13 +6,17 @@ LL | let _: [Option; 2] = [no_copy(); 2]; | = note: required for `Option` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array - = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` = help: create an inline `const` block, see RFC #2920 for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] LL | struct Bar; | +help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position + | +LL ~ const ARRAY_REPEAT_VALUE: Option = no_copy(); +LL ~ let _: [Option; 2] = [ARRAY_REPEAT_VALUE; 2]; + | error: aborting due to previous error diff --git a/tests/ui/consts/const-blocks/trait-error.stderr b/tests/ui/consts/const-blocks/trait-error.stderr index 06fa4b0b1f30c..858ffa820e215 100644 --- a/tests/ui/consts/const-blocks/trait-error.stderr +++ b/tests/ui/consts/const-blocks/trait-error.stderr @@ -10,9 +10,13 @@ note: required for `Foo` to implement `Copy` LL | #[derive(Copy, Clone)] | ^^^^ unsatisfied trait bound introduced in this `derive` macro = note: the `Copy` trait is required because this value will be copied for each element of the array - = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` = help: create an inline `const` block, see RFC #2920 for more information = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position + | +LL ~ const ARRAY_REPEAT_VALUE: Foo = Foo(String::new()); +LL ~ [ARRAY_REPEAT_VALUE; 4]; + | error: aborting due to previous error diff --git a/tests/ui/consts/const-fn-in-vec.rs b/tests/ui/consts/const-fn-in-vec.rs index a40290eca0972..0483800efefd1 100644 --- a/tests/ui/consts/const-fn-in-vec.rs +++ b/tests/ui/consts/const-fn-in-vec.rs @@ -1,7 +1,11 @@ +static _MAYBE_STRINGS: [Option; 5] = [None; 5]; +//~^ ERROR the trait bound `String: Copy` is not satisfied + fn main() { // should hint to create an inline `const` block // or to create a new `const` item - let strings: [String; 5] = [String::new(); 5]; + let _strings: [String; 5] = [String::new(); 5]; + //~^ ERROR the trait bound `String: Copy` is not satisfied + let _maybe_strings: [Option; 5] = [None; 5]; //~^ ERROR the trait bound `String: Copy` is not satisfied - println!("{:?}", strings); } diff --git a/tests/ui/consts/const-fn-in-vec.stderr b/tests/ui/consts/const-fn-in-vec.stderr index 9eb7524b5044b..4593034bfaea3 100644 --- a/tests/ui/consts/const-fn-in-vec.stderr +++ b/tests/ui/consts/const-fn-in-vec.stderr @@ -1,13 +1,47 @@ error[E0277]: the trait bound `String: Copy` is not satisfied - --> $DIR/const-fn-in-vec.rs:4:33 + --> $DIR/const-fn-in-vec.rs:1:47 | -LL | let strings: [String; 5] = [String::new(); 5]; - | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` +LL | static _MAYBE_STRINGS: [Option; 5] = [None; 5]; + | ^^^^ the trait `Copy` is not implemented for `String` | + = note: required for `Option` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array - = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` = help: create an inline `const` block, see RFC #2920 for more information +help: consider creating a new `const` item and initializing it with the result of the constructor to be used in the repeat position + | +LL + const ARRAY_REPEAT_VALUE: Option = None; +LL ~ static _MAYBE_STRINGS: [Option; 5] = [ARRAY_REPEAT_VALUE; 5]; + | + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/const-fn-in-vec.rs:7:34 + | +LL | let _strings: [String; 5] = [String::new(); 5]; + | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | + = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: create an inline `const` block, see RFC #2920 for more information +help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position + | +LL ~ const ARRAY_REPEAT_VALUE: String = String::new(); +LL ~ let _strings: [String; 5] = [ARRAY_REPEAT_VALUE; 5]; + | + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/const-fn-in-vec.rs:9:48 + | +LL | let _maybe_strings: [Option; 5] = [None; 5]; + | ^^^^ the trait `Copy` is not implemented for `String` + | + = note: required for `Option` to implement `Copy` + = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: create an inline `const` block, see RFC #2920 for more information +help: consider creating a new `const` item and initializing it with the result of the constructor to be used in the repeat position + | +LL ~ const ARRAY_REPEAT_VALUE: Option = None; +LL ~ let _maybe_strings: [Option; 5] = [ARRAY_REPEAT_VALUE; 5]; + | -error: aborting due to previous error +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr b/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr index a9e9c679fdb67..ccdd9a95451b6 100644 --- a/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr +++ b/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr @@ -29,10 +29,10 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | ::foo(&s); - | ~~~~~~~~~~ -LL | ::foo(&s); - | ~~~~~~~~~~ +LL | A::foo(&s); + | ~~~ +LL | B::foo(&s); + | ~~~ error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-blanket-impl.rs:33:8 @@ -52,9 +52,9 @@ LL | const CONST: usize = 2; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | ::CONST; +LL | ::CONST; | ~~~~~~~~~~ -LL | ::CONST; +LL | ::CONST; | ~~~~~~~~~~ error: aborting due to 3 previous errors diff --git a/tests/ui/methods/disambiguate-multiple-impl.stderr b/tests/ui/methods/disambiguate-multiple-impl.stderr index 901bfc30a3f23..4172120770c6d 100644 --- a/tests/ui/methods/disambiguate-multiple-impl.stderr +++ b/tests/ui/methods/disambiguate-multiple-impl.stderr @@ -29,10 +29,10 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | ::foo(&s); - | ~~~~~~~~~~ -LL | ::foo(&s); - | ~~~~~~~~~~ +LL | A::foo(&s); + | ~~~ +LL | B::foo(&s); + | ~~~ error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-impl.rs:34:16 diff --git a/tests/ui/methods/disambiguate-multiple-trait-2.stderr b/tests/ui/methods/disambiguate-multiple-trait-2.stderr index 0f9c60ce243df..2778f254a5619 100644 --- a/tests/ui/methods/disambiguate-multiple-trait-2.stderr +++ b/tests/ui/methods/disambiguate-multiple-trait-2.stderr @@ -37,12 +37,12 @@ LL | fn foo(&self); | ^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | A::foo(t); - | ~~~~~~~~~ +LL | A::foo(&t); + | ~~~~~~~~~~ help: disambiguate the method for candidate #2 | -LL | B::foo(t); - | ~~~~~~~~~ +LL | B::foo(&t); + | ~~~~~~~~~~ error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait-2.rs:20:16 @@ -62,10 +62,10 @@ LL | const CONST: usize; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | let _ = A::CONST; - | ~~~ -LL | let _ = B::CONST; - | ~~~ +LL | let _ = ::CONST; + | ~~~~~~~~~~ +LL | let _ = ::CONST; + | ~~~~~~~~~~ error[E0223]: ambiguous associated type --> $DIR/disambiguate-multiple-trait-2.rs:52:12 @@ -98,10 +98,10 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | ::foo(&s); - | ~~~~~~~~~~ -LL | ::foo(&s); - | ~~~~~~~~~~ +LL | A::foo(&s); + | ~~~ +LL | B::foo(&s); + | ~~~ error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait-2.rs:49:16 @@ -121,9 +121,9 @@ LL | const CONST: usize = 1; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | let _ = ::CONST; +LL | let _ = ::CONST; | ~~~~~~~~~~ -LL | let _ = ::CONST; +LL | let _ = ::CONST; | ~~~~~~~~~~ error: aborting due to 6 previous errors diff --git a/tests/ui/methods/disambiguate-multiple-trait.stderr b/tests/ui/methods/disambiguate-multiple-trait.stderr index 9a50d51245ba9..e00498ca62b43 100644 --- a/tests/ui/methods/disambiguate-multiple-trait.stderr +++ b/tests/ui/methods/disambiguate-multiple-trait.stderr @@ -29,10 +29,10 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | ::foo(&s); - | ~~~~~~~~~~ -LL | ::foo(&s); - | ~~~~~~~~~~ +LL | A::foo(&s); + | ~~~ +LL | B::foo(&s); + | ~~~ error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait.rs:27:16 @@ -52,9 +52,9 @@ LL | const CONST: usize = 2; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | let _ = ::CONST; +LL | let _ = ::CONST; | ~~~~~~~~~~ -LL | let _ = ::CONST; +LL | let _ = ::CONST; | ~~~~~~~~~~ error: aborting due to 3 previous errors diff --git a/tests/ui/methods/method-ambig-two-traits-from-bounds.stderr b/tests/ui/methods/method-ambig-two-traits-from-bounds.stderr index 601e6bbb006f7..9a84768a9f4a6 100644 --- a/tests/ui/methods/method-ambig-two-traits-from-bounds.stderr +++ b/tests/ui/methods/method-ambig-two-traits-from-bounds.stderr @@ -16,12 +16,12 @@ LL | trait B { fn foo(&self); } | ^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | A::foo(t); - | ~~~~~~~~~ +LL | A::foo(&t); + | ~~~~~~~~~~ help: disambiguate the method for candidate #2 | -LL | B::foo(t); - | ~~~~~~~~~ +LL | B::foo(&t); + | ~~~~~~~~~~ error: aborting due to previous error diff --git a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr index 4e83e4b77f10e..755179650bbaa 100644 --- a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr +++ b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -54,8 +54,8 @@ LL | let z = NuisanceFoo::foo(x); | ~~~~~~~~~~~~~~~~~~~ help: disambiguate the method for candidate #3 | -LL | let z = FinalFoo::foo(x); - | ~~~~~~~~~~~~~~~~ +LL | let z = FinalFoo::foo(&x); + | ~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:139:24 diff --git a/tests/ui/span/issue-37767.stderr b/tests/ui/span/issue-37767.stderr index b612fdf16fc8c..457870821a19a 100644 --- a/tests/ui/span/issue-37767.stderr +++ b/tests/ui/span/issue-37767.stderr @@ -16,12 +16,12 @@ LL | fn foo(&mut self) {} | ^^^^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | A::foo(&a) - | ~~~~~~~~~~ +LL | A::foo(&mut a) + | ~~~~~~~~~~~~~~ help: disambiguate the method for candidate #2 | -LL | B::foo(&a) - | ~~~~~~~~~~ +LL | B::foo(&mut a) + | ~~~~~~~~~~~~~~ error[E0034]: multiple applicable items in scope --> $DIR/issue-37767.rs:22:7