mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Add a more precise error message
When trying to perform static dispatch on something which derefs to a trait object, and the target trait is not in scope, we had confusing error messages if the target method had a `Self: Sized` bound. We add a more precise error message in this case: "consider using trait ...". Fixes #35976.
This commit is contained in:
parent
12aad38ad4
commit
ac919d527c
@ -160,7 +160,7 @@ pub struct ImplHeader<'tcx> {
|
|||||||
pub predicates: Vec<Predicate<'tcx>>,
|
pub predicates: Vec<Predicate<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct AssociatedItem {
|
pub struct AssociatedItem {
|
||||||
pub def_id: DefId,
|
pub def_id: DefId,
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
|
@ -38,6 +38,11 @@ impl<'a, 'gcx, 'tcx> Deref for ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ConfirmResult<'tcx> {
|
||||||
|
pub callee: MethodCallee<'tcx>,
|
||||||
|
pub rerun: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
pub fn confirm_method(&self,
|
pub fn confirm_method(&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
@ -46,7 +51,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
unadjusted_self_ty: Ty<'tcx>,
|
unadjusted_self_ty: Ty<'tcx>,
|
||||||
pick: probe::Pick<'tcx>,
|
pick: probe::Pick<'tcx>,
|
||||||
segment: &hir::PathSegment)
|
segment: &hir::PathSegment)
|
||||||
-> MethodCallee<'tcx> {
|
-> ConfirmResult<'tcx> {
|
||||||
debug!("confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})",
|
debug!("confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})",
|
||||||
unadjusted_self_ty,
|
unadjusted_self_ty,
|
||||||
pick,
|
pick,
|
||||||
@ -75,7 +80,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
unadjusted_self_ty: Ty<'tcx>,
|
unadjusted_self_ty: Ty<'tcx>,
|
||||||
pick: probe::Pick<'tcx>,
|
pick: probe::Pick<'tcx>,
|
||||||
segment: &hir::PathSegment)
|
segment: &hir::PathSegment)
|
||||||
-> MethodCallee<'tcx> {
|
-> ConfirmResult<'tcx> {
|
||||||
// Adjust the self expression the user provided and obtain the adjusted type.
|
// Adjust the self expression the user provided and obtain the adjusted type.
|
||||||
let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick);
|
let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick);
|
||||||
|
|
||||||
@ -91,6 +96,16 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
// Create the final signature for the method, replacing late-bound regions.
|
// Create the final signature for the method, replacing late-bound regions.
|
||||||
let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs);
|
let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs);
|
||||||
|
|
||||||
|
// If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
|
||||||
|
// something which derefs to `Self` actually implements the trait and the caller
|
||||||
|
// wanted to make a static dispatch on it but forgot to import the trait.
|
||||||
|
// See test `src/test/compile-fail/issue-35976.rs`.
|
||||||
|
//
|
||||||
|
// In that case, we'll error anyway, but we'll also re-run the search with all traits
|
||||||
|
// in scope, and if we find another method which can be used, we'll output an
|
||||||
|
// appropriate hint suggesting to import the trait.
|
||||||
|
let rerun = self.predicates_require_illegal_sized_bound(&method_predicates);
|
||||||
|
|
||||||
// Unify the (adjusted) self type with what the method expects.
|
// Unify the (adjusted) self type with what the method expects.
|
||||||
self.unify_receivers(self_ty, method_sig.inputs()[0]);
|
self.unify_receivers(self_ty, method_sig.inputs()[0]);
|
||||||
|
|
||||||
@ -109,7 +124,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
self.convert_lvalue_derefs_to_mutable();
|
self.convert_lvalue_derefs_to_mutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
callee
|
ConfirmResult { callee, rerun }
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@ -533,6 +548,30 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// MISCELLANY
|
// MISCELLANY
|
||||||
|
|
||||||
|
fn predicates_require_illegal_sized_bound(&self,
|
||||||
|
predicates: &ty::InstantiatedPredicates<'tcx>)
|
||||||
|
-> bool {
|
||||||
|
let sized_def_id = match self.tcx.lang_items.sized_trait() {
|
||||||
|
Some(def_id) => def_id,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
traits::elaborate_predicates(self.tcx, predicates.predicates.clone())
|
||||||
|
.filter_map(|predicate| {
|
||||||
|
match predicate {
|
||||||
|
ty::Predicate::Trait(trait_pred) if trait_pred.def_id() == sized_def_id =>
|
||||||
|
Some(trait_pred),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.any(|trait_pred| {
|
||||||
|
match trait_pred.0.self_ty().sty {
|
||||||
|
ty::TyDynamic(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick) {
|
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick) {
|
||||||
// Disallow calls to the method `drop` defined in the `Drop` trait.
|
// Disallow calls to the method `drop` defined in the `Drop` trait.
|
||||||
match pick.item.container {
|
match pick.item.container {
|
||||||
|
@ -33,7 +33,7 @@ mod confirm;
|
|||||||
pub mod probe;
|
pub mod probe;
|
||||||
mod suggest;
|
mod suggest;
|
||||||
|
|
||||||
use self::probe::IsSuggestion;
|
use self::probe::{IsSuggestion, ProbeScope};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct MethodCallee<'tcx> {
|
pub struct MethodCallee<'tcx> {
|
||||||
@ -106,7 +106,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
-> bool {
|
-> bool {
|
||||||
let mode = probe::Mode::MethodCall;
|
let mode = probe::Mode::MethodCall;
|
||||||
match self.probe_for_name(span, mode, method_name, IsSuggestion(false),
|
match self.probe_for_name(span, mode, method_name, IsSuggestion(false),
|
||||||
self_ty, call_expr_id) {
|
self_ty, call_expr_id, ProbeScope::TraitsInScope) {
|
||||||
Ok(..) => true,
|
Ok(..) => true,
|
||||||
Err(NoMatch(..)) => false,
|
Err(NoMatch(..)) => false,
|
||||||
Err(Ambiguity(..)) => true,
|
Err(Ambiguity(..)) => true,
|
||||||
@ -142,10 +142,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
call_expr,
|
call_expr,
|
||||||
self_expr);
|
self_expr);
|
||||||
|
|
||||||
let mode = probe::Mode::MethodCall;
|
let pick = self.lookup_probe(
|
||||||
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
|
span,
|
||||||
let pick = self.probe_for_name(span, mode, segment.name, IsSuggestion(false),
|
segment.name,
|
||||||
self_ty, call_expr.id)?;
|
self_ty,
|
||||||
|
call_expr,
|
||||||
|
ProbeScope::TraitsInScope
|
||||||
|
)?;
|
||||||
|
|
||||||
if let Some(import_id) = pick.import_id {
|
if let Some(import_id) = pick.import_id {
|
||||||
let import_def_id = self.tcx.hir.local_def_id(import_id);
|
let import_def_id = self.tcx.hir.local_def_id(import_id);
|
||||||
@ -155,12 +158,53 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
self.tcx.check_stability(pick.item.def_id, call_expr.id, span);
|
self.tcx.check_stability(pick.item.def_id, call_expr.id, span);
|
||||||
|
|
||||||
Ok(self.confirm_method(span,
|
let result = self.confirm_method(span,
|
||||||
self_expr,
|
self_expr,
|
||||||
call_expr,
|
call_expr,
|
||||||
self_ty,
|
self_ty,
|
||||||
pick,
|
pick.clone(),
|
||||||
segment))
|
segment);
|
||||||
|
|
||||||
|
if result.rerun {
|
||||||
|
// We probe again, taking all traits into account (not only those in scope).
|
||||||
|
if let Ok(new_pick) = self.lookup_probe(span,
|
||||||
|
segment.name,
|
||||||
|
self_ty,
|
||||||
|
call_expr,
|
||||||
|
ProbeScope::AllTraits) {
|
||||||
|
// If we find a different result, the caller probably forgot to import the trait.
|
||||||
|
// We span an error with an appropriate help message.
|
||||||
|
if new_pick != pick {
|
||||||
|
let error = MethodError::NoMatch(
|
||||||
|
NoMatchData::new(Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
vec![new_pick.item.container.id()],
|
||||||
|
probe::Mode::MethodCall)
|
||||||
|
);
|
||||||
|
self.report_method_error(span,
|
||||||
|
self_ty,
|
||||||
|
segment.name,
|
||||||
|
Some(self_expr),
|
||||||
|
error,
|
||||||
|
None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result.callee)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_probe(&self,
|
||||||
|
span: Span,
|
||||||
|
method_name: ast::Name,
|
||||||
|
self_ty: ty::Ty<'tcx>,
|
||||||
|
call_expr: &'gcx hir::Expr,
|
||||||
|
scope: ProbeScope)
|
||||||
|
-> probe::PickResult<'tcx> {
|
||||||
|
let mode = probe::Mode::MethodCall;
|
||||||
|
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
|
||||||
|
self.probe_for_name(span, mode, method_name, IsSuggestion(false),
|
||||||
|
self_ty, call_expr.id, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `lookup_method_in_trait` is used for overloaded operators.
|
/// `lookup_method_in_trait` is used for overloaded operators.
|
||||||
@ -299,7 +343,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
-> Result<Def, MethodError<'tcx>> {
|
-> Result<Def, MethodError<'tcx>> {
|
||||||
let mode = probe::Mode::Path;
|
let mode = probe::Mode::Path;
|
||||||
let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
|
let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
|
||||||
self_ty, expr_id)?;
|
self_ty, expr_id, ProbeScope::TraitsInScope)?;
|
||||||
|
|
||||||
if let Some(import_id) = pick.import_id {
|
if let Some(import_id) = pick.import_id {
|
||||||
let import_def_id = self.tcx.hir.local_def_id(import_id);
|
let import_def_id = self.tcx.hir.local_def_id(import_id);
|
||||||
|
@ -106,7 +106,7 @@ enum CandidateKind<'tcx> {
|
|||||||
ty::PolyTraitRef<'tcx>),
|
ty::PolyTraitRef<'tcx>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Pick<'tcx> {
|
pub struct Pick<'tcx> {
|
||||||
pub item: ty::AssociatedItem,
|
pub item: ty::AssociatedItem,
|
||||||
pub kind: PickKind<'tcx>,
|
pub kind: PickKind<'tcx>,
|
||||||
@ -130,7 +130,7 @@ pub struct Pick<'tcx> {
|
|||||||
pub unsize: Option<Ty<'tcx>>,
|
pub unsize: Option<Ty<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum PickKind<'tcx> {
|
pub enum PickKind<'tcx> {
|
||||||
InherentImplPick,
|
InherentImplPick,
|
||||||
ExtensionImplPick(// Impl
|
ExtensionImplPick(// Impl
|
||||||
@ -155,6 +155,15 @@ pub enum Mode {
|
|||||||
Path,
|
Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
pub enum ProbeScope {
|
||||||
|
// Assemble candidates coming only from traits in scope.
|
||||||
|
TraitsInScope,
|
||||||
|
|
||||||
|
// Assemble candidates coming from all traits.
|
||||||
|
AllTraits,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
/// This is used to offer suggestions to users. It returns methods
|
/// This is used to offer suggestions to users. It returns methods
|
||||||
/// that could have been called which have the desired return
|
/// that could have been called which have the desired return
|
||||||
@ -175,14 +184,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
scope_expr_id);
|
scope_expr_id);
|
||||||
let method_names =
|
let method_names =
|
||||||
self.probe_op(span, mode, LookingFor::ReturnType(return_type), IsSuggestion(true),
|
self.probe_op(span, mode, LookingFor::ReturnType(return_type), IsSuggestion(true),
|
||||||
self_ty, scope_expr_id,
|
self_ty, scope_expr_id, ProbeScope::TraitsInScope,
|
||||||
|probe_cx| Ok(probe_cx.candidate_method_names()))
|
|probe_cx| Ok(probe_cx.candidate_method_names()))
|
||||||
.unwrap_or(vec![]);
|
.unwrap_or(vec![]);
|
||||||
method_names
|
method_names
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|&method_name| {
|
.flat_map(|&method_name| {
|
||||||
match self.probe_for_name(span, mode, method_name, IsSuggestion(true), self_ty,
|
match self.probe_for_name(span, mode, method_name, IsSuggestion(true), self_ty,
|
||||||
scope_expr_id) {
|
scope_expr_id, ProbeScope::TraitsInScope) {
|
||||||
Ok(pick) => Some(pick.item),
|
Ok(pick) => Some(pick.item),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
@ -196,7 +205,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
item_name: ast::Name,
|
item_name: ast::Name,
|
||||||
is_suggestion: IsSuggestion,
|
is_suggestion: IsSuggestion,
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Ty<'tcx>,
|
||||||
scope_expr_id: ast::NodeId)
|
scope_expr_id: ast::NodeId,
|
||||||
|
scope: ProbeScope)
|
||||||
-> PickResult<'tcx> {
|
-> PickResult<'tcx> {
|
||||||
debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})",
|
debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})",
|
||||||
self_ty,
|
self_ty,
|
||||||
@ -208,6 +218,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
is_suggestion,
|
is_suggestion,
|
||||||
self_ty,
|
self_ty,
|
||||||
scope_expr_id,
|
scope_expr_id,
|
||||||
|
scope,
|
||||||
|probe_cx| probe_cx.pick())
|
|probe_cx| probe_cx.pick())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +229,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
is_suggestion: IsSuggestion,
|
is_suggestion: IsSuggestion,
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Ty<'tcx>,
|
||||||
scope_expr_id: ast::NodeId,
|
scope_expr_id: ast::NodeId,
|
||||||
|
scope: ProbeScope,
|
||||||
op: OP)
|
op: OP)
|
||||||
-> Result<R, MethodError<'tcx>>
|
-> Result<R, MethodError<'tcx>>
|
||||||
where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result<R, MethodError<'tcx>>
|
where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result<R, MethodError<'tcx>>
|
||||||
@ -275,8 +287,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
let mut probe_cx =
|
let mut probe_cx =
|
||||||
ProbeContext::new(self, span, mode, looking_for,
|
ProbeContext::new(self, span, mode, looking_for,
|
||||||
steps, opt_simplified_steps);
|
steps, opt_simplified_steps);
|
||||||
|
|
||||||
probe_cx.assemble_inherent_candidates();
|
probe_cx.assemble_inherent_candidates();
|
||||||
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?;
|
match scope {
|
||||||
|
ProbeScope::TraitsInScope =>
|
||||||
|
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?,
|
||||||
|
ProbeScope::AllTraits =>
|
||||||
|
probe_cx.assemble_extension_candidates_for_all_traits()?,
|
||||||
|
};
|
||||||
op(probe_cx)
|
op(probe_cx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
33
src/test/compile-fail/issue-35976.rs
Normal file
33
src/test/compile-fail/issue-35976.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
pub trait Future {
|
||||||
|
fn wait(&self) where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for Box<Future> {
|
||||||
|
fn wait(&self) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//use private::Future;
|
||||||
|
|
||||||
|
fn bar(arg: Box<private::Future>) {
|
||||||
|
arg.wait();
|
||||||
|
//~^ ERROR no method named `wait` found for type `std::boxed::Box<private::Future + 'static>`
|
||||||
|
//~| the following trait is implemented but not in scope
|
||||||
|
//~| ERROR the trait bound `private::Future + 'static: std::marker::Sized` is not satisfied
|
||||||
|
//~| `private::Future + 'static` does not have a constant size known at compile-time
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user