From a9ce33c059d0a91fb12a79d75aa1a868d76bf6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 1 Sep 2019 02:22:42 -0700 Subject: [PATCH 1/2] Account for arbitrary self types in E0599 --- src/librustc/ty/context.rs | 18 +++++ src/librustc_typeck/check/expr.rs | 72 +++++++++++++------ src/librustc_typeck/check/method/mod.rs | 2 +- src/librustc_typeck/check/method/suggest.rs | 9 +-- src/librustc_typeck/check/mod.rs | 2 +- .../no-method-suggested-traits.stderr | 8 +++ .../point-at-arbitrary-self-type-method.rs | 9 +++ ...point-at-arbitrary-self-type-method.stderr | 15 ++++ ...int-at-arbitrary-self-type-trait-method.rs | 10 +++ ...at-arbitrary-self-type-trait-method.stderr | 18 +++++ src/test/ui/traits/trait-item-privacy.stderr | 7 ++ 11 files changed, 144 insertions(+), 26 deletions(-) create mode 100644 src/test/ui/self/point-at-arbitrary-self-type-method.rs create mode 100644 src/test/ui/self/point-at-arbitrary-self-type-method.stderr create mode 100644 src/test/ui/self/point-at-arbitrary-self-type-trait-method.rs create mode 100644 src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index e240e0df8b948..f355e231914d2 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2402,6 +2402,24 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_generic_adt(def_id, ty) } + #[inline] + pub fn mk_pin(self, ty: Ty<'tcx>) -> Ty<'tcx> { + let def_id = self.require_lang_item(lang_items::PinTypeLangItem, None); + self.mk_generic_adt(def_id, ty) + } + + #[inline] + pub fn mk_rc(self, ty: Ty<'tcx>) -> Ty<'tcx> { + let def_id = self.require_lang_item(lang_items::Rc, None); + self.mk_generic_adt(def_id, ty) + } + + #[inline] + pub fn mk_arc(self, ty: Ty<'tcx>) -> Ty<'tcx> { + let def_id = self.require_lang_item(lang_items::Arc, None); + self.mk_generic_adt(def_id, ty) + } + #[inline] pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> { let def_id = self.require_lang_item(lang_items::MaybeUninitLangItem, None); diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index a53fb12367d0e..efff8bcdacbcf 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -12,7 +12,7 @@ use crate::check::fatally_break_rust; use crate::check::report_unexpected_variant_res; use crate::check::Needs; use crate::check::TupleArgumentsFlag::DontTupleArguments; -use crate::check::method::SelfSource; +use crate::check::method::{probe, SelfSource}; use crate::util::common::ErrorReported; use crate::util::nodemap::FxHashMap; use crate::astconv::AstConv as _; @@ -775,35 +775,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // no need to check for bot/err -- callee does that let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); - let method = match self.lookup_method(rcvr_t, - segment, - span, - expr, - rcvr) { + let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| { + if let Ok(pick) = self.lookup_probe( + span, + segment.ident, + new_rcvr_t, + rcvr, + probe::ProbeScope::AllTraits, + ) { + err.span_label( + pick.item.ident.span, + &format!("the method is available for `{}` here", new_rcvr_t), + ); + } + }; + + let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) { Ok(method) => { self.write_method_call(expr.hir_id, method); Ok(method) } Err(error) => { if segment.ident.name != kw::Invalid { - self.report_method_error(span, - rcvr_t, - segment.ident, - SelfSource::MethodCall(rcvr), - error, - Some(args)); + if let Some(mut err) = self.report_method_error( + span, + rcvr_t, + segment.ident, + SelfSource::MethodCall(rcvr), + error, + Some(args), + ) { + if let ty::Adt(..) = rcvr_t.sty { + // Try alternative arbitrary self types that could fulfill this call. + // FIXME: probe for all types that *could* be arbitrary self-types, not + // just this whitelist. + let box_rcvr_t = self.tcx.mk_box(rcvr_t); + try_alt_rcvr(&mut err, box_rcvr_t); + let pin_rcvr_t = self.tcx.mk_pin(rcvr_t); + try_alt_rcvr(&mut err, pin_rcvr_t); + let arc_rcvr_t = self.tcx.mk_arc(rcvr_t); + try_alt_rcvr(&mut err, arc_rcvr_t); + let rc_rcvr_t = self.tcx.mk_rc(rcvr_t); + try_alt_rcvr(&mut err, rc_rcvr_t); + } + err.emit(); + } } Err(()) } }; // Call the generic checker. - self.check_method_argument_types(span, - expr.span, - method, - &args[1..], - DontTupleArguments, - expected) + self.check_method_argument_types( + span, + expr.span, + method, + &args[1..], + DontTupleArguments, + expected, + ) } fn check_expr_cast( @@ -1466,8 +1496,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let struct_variant_def = def.non_enum_variant(); let field_names = self.available_field_names(struct_variant_def); if !field_names.is_empty() { - err.note(&format!("available fields are: {}", - self.name_series_display(field_names))); + err.note(&format!( + "available fields are: {}", + self.name_series_display(field_names), + )); } } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index a7e4f8e5c6289..1509c0f8a2196 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(result.callee) } - fn lookup_probe( + pub fn lookup_probe( &self, span: Span, method_name: ast::Ident, diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 440e7e5d0e314..72e6f59715960 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -69,12 +69,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source: SelfSource<'b>, error: MethodError<'tcx>, args: Option<&'tcx [hir::Expr]>, - ) { + ) -> Option> { let orig_span = span; let mut span = span; // Avoid suggestions when we don't know what's going on. if rcvr_ty.references_error() { - return; + return None; } let print_disambiguation_help = | @@ -314,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } err.emit(); - return; + return None; } else { span = item_name.span; let mut err = struct_span_err!( @@ -529,7 +529,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - err.emit(); + return Some(err); } MethodError::Ambiguity(sources) => { @@ -573,6 +573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bug!("no return type expectations but got BadReturnType") } } + None } fn suggest_use_candidates(&self, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a80550486d627..2d9cacea68f31 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3580,7 +3580,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { SelfSource::QPath(qself), error, None, - ); + ).map(|mut e| e.emit()); } result }); diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.stderr b/src/test/ui/impl-trait/no-method-suggested-traits.stderr index d980d7cccadd5..002b60f9f258d 100644 --- a/src/test/ui/impl-trait/no-method-suggested-traits.stderr +++ b/src/test/ui/impl-trait/no-method-suggested-traits.stderr @@ -49,6 +49,14 @@ LL | use foo::Bar; error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope --> $DIR/no-method-suggested-traits.rs:32:43 | +LL | fn method(&self) {} + | ------ + | | + | the method is available for `std::boxed::Box>>` here + | the method is available for `std::pin::Pin>>` here + | the method is available for `std::sync::Arc>>` here + | the method is available for `std::rc::Rc>>` here +... LL | std::rc::Rc::new(&mut Box::new(&'a')).method(); | ^^^^^^ | diff --git a/src/test/ui/self/point-at-arbitrary-self-type-method.rs b/src/test/ui/self/point-at-arbitrary-self-type-method.rs new file mode 100644 index 0000000000000..0f7deeacad292 --- /dev/null +++ b/src/test/ui/self/point-at-arbitrary-self-type-method.rs @@ -0,0 +1,9 @@ +struct A; + +impl A { + fn foo(self: Box) {} +} + +fn main() { + A.foo(); //~ ERROR E0599 +} diff --git a/src/test/ui/self/point-at-arbitrary-self-type-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr new file mode 100644 index 0000000000000..06dad7caa6735 --- /dev/null +++ b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr @@ -0,0 +1,15 @@ +error[E0599]: no method named `foo` found for type `A` in the current scope + --> $DIR/point-at-arbitrary-self-type-method.rs:8:7 + | +LL | struct A; + | --------- method `foo` not found for this +... +LL | fn foo(self: Box) {} + | --- the method is available for `std::boxed::Box` here +... +LL | A.foo(); + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.rs b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.rs new file mode 100644 index 0000000000000..53d992771186f --- /dev/null +++ b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.rs @@ -0,0 +1,10 @@ +trait B { fn foo(self: Box); } +struct A; + +impl B for A { + fn foo(self: Box) {} +} + +fn main() { + A.foo() //~ ERROR E0599 +} diff --git a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr new file mode 100644 index 0000000000000..90cd3b8074580 --- /dev/null +++ b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr @@ -0,0 +1,18 @@ +error[E0599]: no method named `foo` found for type `A` in the current scope + --> $DIR/point-at-arbitrary-self-type-trait-method.rs:9:7 + | +LL | trait B { fn foo(self: Box); } + | --- the method is available for `std::boxed::Box` here +LL | struct A; + | --------- method `foo` not found for this +... +LL | A.foo() + | ^^^ + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `foo`, perhaps you need to implement it: + candidate #1: `B` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/traits/trait-item-privacy.stderr b/src/test/ui/traits/trait-item-privacy.stderr index de699a69fa8bc..ce2919c8e7741 100644 --- a/src/test/ui/traits/trait-item-privacy.stderr +++ b/src/test/ui/traits/trait-item-privacy.stderr @@ -17,6 +17,13 @@ error[E0599]: no method named `b` found for type `S` in the current scope LL | struct S; | --------- method `b` not found for this ... +LL | fn b(&self) { } + | - + | | + | the method is available for `std::boxed::Box` here + | the method is available for `std::sync::Arc` here + | the method is available for `std::rc::Rc` here +... LL | S.b(); | ^ | From 141f5a7558289acb6c7aaf9c6500eb9f6dbeec1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 1 Sep 2019 11:20:33 -0700 Subject: [PATCH 2/2] review comments --- src/librustc/ty/context.rs | 16 +----- src/librustc_typeck/check/expr.rs | 94 ++++++++++++++++++------------- 2 files changed, 57 insertions(+), 53 deletions(-) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index f355e231914d2..17c9e520bcea2 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2403,20 +2403,8 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_pin(self, ty: Ty<'tcx>) -> Ty<'tcx> { - let def_id = self.require_lang_item(lang_items::PinTypeLangItem, None); - self.mk_generic_adt(def_id, ty) - } - - #[inline] - pub fn mk_rc(self, ty: Ty<'tcx>) -> Ty<'tcx> { - let def_id = self.require_lang_item(lang_items::Rc, None); - self.mk_generic_adt(def_id, ty) - } - - #[inline] - pub fn mk_arc(self, ty: Ty<'tcx>) -> Ty<'tcx> { - let def_id = self.require_lang_item(lang_items::Arc, None); + pub fn mk_lang_item(self, ty: Ty<'tcx>, item: lang_items::LangItem) -> Ty<'tcx> { + let def_id = self.require_lang_item(item, None); self.mk_generic_adt(def_id, ty) } diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index efff8bcdacbcf..fbaa9904d8303 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -12,7 +12,7 @@ use crate::check::fatally_break_rust; use crate::check::report_unexpected_variant_res; use crate::check::Needs; use crate::check::TupleArgumentsFlag::DontTupleArguments; -use crate::check::method::{probe, SelfSource}; +use crate::check::method::{probe, SelfSource, MethodError}; use crate::util::common::ErrorReported; use crate::util::nodemap::FxHashMap; use crate::astconv::AstConv as _; @@ -29,6 +29,7 @@ use rustc::hir::def::{CtorKind, Res, DefKind}; use rustc::hir::ptr::P; use rustc::infer; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc::middle::lang_items; use rustc::mir::interpret::GlobalId; use rustc::ty; use rustc::ty::adjustment::{ @@ -775,21 +776,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // no need to check for bot/err -- callee does that let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); - let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| { - if let Ok(pick) = self.lookup_probe( - span, - segment.ident, - new_rcvr_t, - rcvr, - probe::ProbeScope::AllTraits, - ) { - err.span_label( - pick.item.ident.span, - &format!("the method is available for `{}` here", new_rcvr_t), - ); - } - }; - let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) { Ok(method) => { self.write_method_call(expr.hir_id, method); @@ -797,29 +783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Err(error) => { if segment.ident.name != kw::Invalid { - if let Some(mut err) = self.report_method_error( - span, - rcvr_t, - segment.ident, - SelfSource::MethodCall(rcvr), - error, - Some(args), - ) { - if let ty::Adt(..) = rcvr_t.sty { - // Try alternative arbitrary self types that could fulfill this call. - // FIXME: probe for all types that *could* be arbitrary self-types, not - // just this whitelist. - let box_rcvr_t = self.tcx.mk_box(rcvr_t); - try_alt_rcvr(&mut err, box_rcvr_t); - let pin_rcvr_t = self.tcx.mk_pin(rcvr_t); - try_alt_rcvr(&mut err, pin_rcvr_t); - let arc_rcvr_t = self.tcx.mk_arc(rcvr_t); - try_alt_rcvr(&mut err, arc_rcvr_t); - let rc_rcvr_t = self.tcx.mk_rc(rcvr_t); - try_alt_rcvr(&mut err, rc_rcvr_t); - } - err.emit(); - } + self.report_extended_method_error(segment, span, args, rcvr_t, error); } Err(()) } @@ -836,6 +800,58 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } + fn report_extended_method_error( + &self, + segment: &hir::PathSegment, + span: Span, + args: &'tcx [hir::Expr], + rcvr_t: Ty<'tcx>, + error: MethodError<'tcx> + ) { + let rcvr = &args[0]; + let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| { + if let Ok(pick) = self.lookup_probe( + span, + segment.ident, + new_rcvr_t, + rcvr, + probe::ProbeScope::AllTraits, + ) { + err.span_label( + pick.item.ident.span, + &format!("the method is available for `{}` here", new_rcvr_t), + ); + } + }; + + if let Some(mut err) = self.report_method_error( + span, + rcvr_t, + segment.ident, + SelfSource::MethodCall(rcvr), + error, + Some(args), + ) { + if let ty::Adt(..) = rcvr_t.sty { + // Try alternative arbitrary self types that could fulfill this call. + // FIXME: probe for all types that *could* be arbitrary self-types, not + // just this whitelist. + let box_rcvr_t = self.tcx.mk_box(rcvr_t); + try_alt_rcvr(&mut err, box_rcvr_t); + let pin_rcvr_t = self.tcx.mk_lang_item( + rcvr_t, + lang_items::PinTypeLangItem, + ); + try_alt_rcvr(&mut err, pin_rcvr_t); + let arc_rcvr_t = self.tcx.mk_lang_item(rcvr_t, lang_items::Arc); + try_alt_rcvr(&mut err, arc_rcvr_t); + let rc_rcvr_t = self.tcx.mk_lang_item(rcvr_t, lang_items::Rc); + try_alt_rcvr(&mut err, rc_rcvr_t); + } + err.emit(); + } + } + fn check_expr_cast( &self, e: &'tcx hir::Expr,