Skip to content

Commit ac919d5

Browse files
committed
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.
1 parent 12aad38 commit ac919d5

File tree

5 files changed

+157
-23
lines changed

5 files changed

+157
-23
lines changed

src/librustc/ty/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub struct ImplHeader<'tcx> {
160160
pub predicates: Vec<Predicate<'tcx>>,
161161
}
162162

163-
#[derive(Copy, Clone, Debug)]
163+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
164164
pub struct AssociatedItem {
165165
pub def_id: DefId,
166166
pub name: Name,

src/librustc_typeck/check/method/confirm.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ impl<'a, 'gcx, 'tcx> Deref for ConfirmContext<'a, 'gcx, 'tcx> {
3838
}
3939
}
4040

41+
pub struct ConfirmResult<'tcx> {
42+
pub callee: MethodCallee<'tcx>,
43+
pub rerun: bool,
44+
}
45+
4146
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
4247
pub fn confirm_method(&self,
4348
span: Span,
@@ -46,7 +51,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
4651
unadjusted_self_ty: Ty<'tcx>,
4752
pick: probe::Pick<'tcx>,
4853
segment: &hir::PathSegment)
49-
-> MethodCallee<'tcx> {
54+
-> ConfirmResult<'tcx> {
5055
debug!("confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})",
5156
unadjusted_self_ty,
5257
pick,
@@ -75,7 +80,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
7580
unadjusted_self_ty: Ty<'tcx>,
7681
pick: probe::Pick<'tcx>,
7782
segment: &hir::PathSegment)
78-
-> MethodCallee<'tcx> {
83+
-> ConfirmResult<'tcx> {
7984
// Adjust the self expression the user provided and obtain the adjusted type.
8085
let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick);
8186

@@ -91,6 +96,16 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
9196
// Create the final signature for the method, replacing late-bound regions.
9297
let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs);
9398

99+
// If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
100+
// something which derefs to `Self` actually implements the trait and the caller
101+
// wanted to make a static dispatch on it but forgot to import the trait.
102+
// See test `src/test/compile-fail/issue-35976.rs`.
103+
//
104+
// In that case, we'll error anyway, but we'll also re-run the search with all traits
105+
// in scope, and if we find another method which can be used, we'll output an
106+
// appropriate hint suggesting to import the trait.
107+
let rerun = self.predicates_require_illegal_sized_bound(&method_predicates);
108+
94109
// Unify the (adjusted) self type with what the method expects.
95110
self.unify_receivers(self_ty, method_sig.inputs()[0]);
96111

@@ -109,7 +124,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
109124
self.convert_lvalue_derefs_to_mutable();
110125
}
111126

112-
callee
127+
ConfirmResult { callee, rerun }
113128
}
114129

115130
///////////////////////////////////////////////////////////////////////////
@@ -533,6 +548,30 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
533548
///////////////////////////////////////////////////////////////////////////
534549
// MISCELLANY
535550

551+
fn predicates_require_illegal_sized_bound(&self,
552+
predicates: &ty::InstantiatedPredicates<'tcx>)
553+
-> bool {
554+
let sized_def_id = match self.tcx.lang_items.sized_trait() {
555+
Some(def_id) => def_id,
556+
None => return false,
557+
};
558+
559+
traits::elaborate_predicates(self.tcx, predicates.predicates.clone())
560+
.filter_map(|predicate| {
561+
match predicate {
562+
ty::Predicate::Trait(trait_pred) if trait_pred.def_id() == sized_def_id =>
563+
Some(trait_pred),
564+
_ => None,
565+
}
566+
})
567+
.any(|trait_pred| {
568+
match trait_pred.0.self_ty().sty {
569+
ty::TyDynamic(..) => true,
570+
_ => false,
571+
}
572+
})
573+
}
574+
536575
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick) {
537576
// Disallow calls to the method `drop` defined in the `Drop` trait.
538577
match pick.item.container {

src/librustc_typeck/check/method/mod.rs

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ mod confirm;
3333
pub mod probe;
3434
mod suggest;
3535

36-
use self::probe::IsSuggestion;
36+
use self::probe::{IsSuggestion, ProbeScope};
3737

3838
#[derive(Clone, Copy, Debug)]
3939
pub struct MethodCallee<'tcx> {
@@ -106,7 +106,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
106106
-> bool {
107107
let mode = probe::Mode::MethodCall;
108108
match self.probe_for_name(span, mode, method_name, IsSuggestion(false),
109-
self_ty, call_expr_id) {
109+
self_ty, call_expr_id, ProbeScope::TraitsInScope) {
110110
Ok(..) => true,
111111
Err(NoMatch(..)) => false,
112112
Err(Ambiguity(..)) => true,
@@ -142,10 +142,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
142142
call_expr,
143143
self_expr);
144144

145-
let mode = probe::Mode::MethodCall;
146-
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
147-
let pick = self.probe_for_name(span, mode, segment.name, IsSuggestion(false),
148-
self_ty, call_expr.id)?;
145+
let pick = self.lookup_probe(
146+
span,
147+
segment.name,
148+
self_ty,
149+
call_expr,
150+
ProbeScope::TraitsInScope
151+
)?;
149152

150153
if let Some(import_id) = pick.import_id {
151154
let import_def_id = self.tcx.hir.local_def_id(import_id);
@@ -155,12 +158,53 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
155158

156159
self.tcx.check_stability(pick.item.def_id, call_expr.id, span);
157160

158-
Ok(self.confirm_method(span,
159-
self_expr,
160-
call_expr,
161-
self_ty,
162-
pick,
163-
segment))
161+
let result = self.confirm_method(span,
162+
self_expr,
163+
call_expr,
164+
self_ty,
165+
pick.clone(),
166+
segment);
167+
168+
if result.rerun {
169+
// We probe again, taking all traits into account (not only those in scope).
170+
if let Ok(new_pick) = self.lookup_probe(span,
171+
segment.name,
172+
self_ty,
173+
call_expr,
174+
ProbeScope::AllTraits) {
175+
// If we find a different result, the caller probably forgot to import the trait.
176+
// We span an error with an appropriate help message.
177+
if new_pick != pick {
178+
let error = MethodError::NoMatch(
179+
NoMatchData::new(Vec::new(),
180+
Vec::new(),
181+
vec![new_pick.item.container.id()],
182+
probe::Mode::MethodCall)
183+
);
184+
self.report_method_error(span,
185+
self_ty,
186+
segment.name,
187+
Some(self_expr),
188+
error,
189+
None);
190+
}
191+
}
192+
}
193+
194+
Ok(result.callee)
195+
}
196+
197+
fn lookup_probe(&self,
198+
span: Span,
199+
method_name: ast::Name,
200+
self_ty: ty::Ty<'tcx>,
201+
call_expr: &'gcx hir::Expr,
202+
scope: ProbeScope)
203+
-> probe::PickResult<'tcx> {
204+
let mode = probe::Mode::MethodCall;
205+
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
206+
self.probe_for_name(span, mode, method_name, IsSuggestion(false),
207+
self_ty, call_expr.id, scope)
164208
}
165209

166210
/// `lookup_method_in_trait` is used for overloaded operators.
@@ -299,7 +343,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
299343
-> Result<Def, MethodError<'tcx>> {
300344
let mode = probe::Mode::Path;
301345
let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
302-
self_ty, expr_id)?;
346+
self_ty, expr_id, ProbeScope::TraitsInScope)?;
303347

304348
if let Some(import_id) = pick.import_id {
305349
let import_def_id = self.tcx.hir.local_def_id(import_id);

src/librustc_typeck/check/method/probe.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ enum CandidateKind<'tcx> {
106106
ty::PolyTraitRef<'tcx>),
107107
}
108108

109-
#[derive(Debug)]
109+
#[derive(Debug, PartialEq, Eq, Clone)]
110110
pub struct Pick<'tcx> {
111111
pub item: ty::AssociatedItem,
112112
pub kind: PickKind<'tcx>,
@@ -130,7 +130,7 @@ pub struct Pick<'tcx> {
130130
pub unsize: Option<Ty<'tcx>>,
131131
}
132132

133-
#[derive(Clone,Debug)]
133+
#[derive(Clone, Debug, PartialEq, Eq)]
134134
pub enum PickKind<'tcx> {
135135
InherentImplPick,
136136
ExtensionImplPick(// Impl
@@ -155,6 +155,15 @@ pub enum Mode {
155155
Path,
156156
}
157157

158+
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
159+
pub enum ProbeScope {
160+
// Assemble candidates coming only from traits in scope.
161+
TraitsInScope,
162+
163+
// Assemble candidates coming from all traits.
164+
AllTraits,
165+
}
166+
158167
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
159168
/// This is used to offer suggestions to users. It returns methods
160169
/// that could have been called which have the desired return
@@ -175,14 +184,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
175184
scope_expr_id);
176185
let method_names =
177186
self.probe_op(span, mode, LookingFor::ReturnType(return_type), IsSuggestion(true),
178-
self_ty, scope_expr_id,
187+
self_ty, scope_expr_id, ProbeScope::TraitsInScope,
179188
|probe_cx| Ok(probe_cx.candidate_method_names()))
180189
.unwrap_or(vec![]);
181190
method_names
182191
.iter()
183192
.flat_map(|&method_name| {
184193
match self.probe_for_name(span, mode, method_name, IsSuggestion(true), self_ty,
185-
scope_expr_id) {
194+
scope_expr_id, ProbeScope::TraitsInScope) {
186195
Ok(pick) => Some(pick.item),
187196
Err(_) => None,
188197
}
@@ -196,7 +205,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
196205
item_name: ast::Name,
197206
is_suggestion: IsSuggestion,
198207
self_ty: Ty<'tcx>,
199-
scope_expr_id: ast::NodeId)
208+
scope_expr_id: ast::NodeId,
209+
scope: ProbeScope)
200210
-> PickResult<'tcx> {
201211
debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})",
202212
self_ty,
@@ -208,6 +218,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
208218
is_suggestion,
209219
self_ty,
210220
scope_expr_id,
221+
scope,
211222
|probe_cx| probe_cx.pick())
212223
}
213224

@@ -218,6 +229,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
218229
is_suggestion: IsSuggestion,
219230
self_ty: Ty<'tcx>,
220231
scope_expr_id: ast::NodeId,
232+
scope: ProbeScope,
221233
op: OP)
222234
-> Result<R, MethodError<'tcx>>
223235
where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result<R, MethodError<'tcx>>
@@ -275,8 +287,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
275287
let mut probe_cx =
276288
ProbeContext::new(self, span, mode, looking_for,
277289
steps, opt_simplified_steps);
290+
278291
probe_cx.assemble_inherent_candidates();
279-
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?;
292+
match scope {
293+
ProbeScope::TraitsInScope =>
294+
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?,
295+
ProbeScope::AllTraits =>
296+
probe_cx.assemble_extension_candidates_for_all_traits()?,
297+
};
280298
op(probe_cx)
281299
})
282300
}

src/test/compile-fail/issue-35976.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
mod private {
12+
pub trait Future {
13+
fn wait(&self) where Self: Sized;
14+
}
15+
16+
impl Future for Box<Future> {
17+
fn wait(&self) { }
18+
}
19+
}
20+
21+
//use private::Future;
22+
23+
fn bar(arg: Box<private::Future>) {
24+
arg.wait();
25+
//~^ ERROR no method named `wait` found for type `std::boxed::Box<private::Future + 'static>`
26+
//~| the following trait is implemented but not in scope
27+
//~| ERROR the trait bound `private::Future + 'static: std::marker::Sized` is not satisfied
28+
//~| `private::Future + 'static` does not have a constant size known at compile-time
29+
}
30+
31+
fn main() {
32+
33+
}

0 commit comments

Comments
 (0)