Skip to content

Commit dae8864

Browse files
committed
Auto merge of #43600 - scalexm:issue-35976, r=nikomatsakis
Add a more precise error message for issue #35976 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. r? @nikomatsakis
2 parents f2a5af7 + e7e620d commit dae8864

File tree

7 files changed

+229
-49
lines changed

7 files changed

+229
-49
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: 48 additions & 5 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 illegal_sized_bound: 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,12 +96,26 @@ 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/ui/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 illegal_sized_bound = 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

97112
// Add any trait/regions obligations specified on the method's type parameters.
98-
let method_ty = self.tcx.mk_fn_ptr(ty::Binder(method_sig));
99-
self.add_obligations(method_ty, all_substs, &method_predicates);
113+
// We won't add these if we encountered an illegal sized bound, so that we can use
114+
// a custom error in that case.
115+
if !illegal_sized_bound {
116+
let method_ty = self.tcx.mk_fn_ptr(ty::Binder(method_sig));
117+
self.add_obligations(method_ty, all_substs, &method_predicates);
118+
}
100119

101120
// Create the final `MethodCallee`.
102121
let callee = MethodCallee {
@@ -109,7 +128,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
109128
self.convert_lvalue_derefs_to_mutable();
110129
}
111130

112-
callee
131+
ConfirmResult { callee, illegal_sized_bound }
113132
}
114133

115134
///////////////////////////////////////////////////////////////////////////
@@ -533,6 +552,30 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
533552
///////////////////////////////////////////////////////////////////////////
534553
// MISCELLANY
535554

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

src/librustc_typeck/check/method/mod.rs

Lines changed: 65 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> {
@@ -60,6 +60,10 @@ pub enum MethodError<'tcx> {
6060

6161
// Found an applicable method, but it is not visible.
6262
PrivateMatch(Def),
63+
64+
// Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
65+
// forgotten to import a trait.
66+
IllegalSizedBound(Vec<DefId>),
6367
}
6468

6569
// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
@@ -106,12 +110,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
106110
-> bool {
107111
let mode = probe::Mode::MethodCall;
108112
match self.probe_for_name(span, mode, method_name, IsSuggestion(false),
109-
self_ty, call_expr_id) {
113+
self_ty, call_expr_id, ProbeScope::TraitsInScope) {
110114
Ok(..) => true,
111115
Err(NoMatch(..)) => false,
112116
Err(Ambiguity(..)) => true,
113117
Err(ClosureAmbiguity(..)) => true,
114118
Err(PrivateMatch(..)) => allow_private,
119+
Err(IllegalSizedBound(..)) => true,
115120
}
116121
}
117122

@@ -142,10 +147,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
142147
call_expr,
143148
self_expr);
144149

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)?;
150+
let pick = self.lookup_probe(
151+
span,
152+
segment.name,
153+
self_ty,
154+
call_expr,
155+
ProbeScope::TraitsInScope
156+
)?;
149157

150158
if let Some(import_id) = pick.import_id {
151159
let import_def_id = self.tcx.hir.local_def_id(import_id);
@@ -155,12 +163,56 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
155163

156164
self.tcx.check_stability(pick.item.def_id, call_expr.id, span);
157165

158-
Ok(self.confirm_method(span,
159-
self_expr,
160-
call_expr,
161-
self_ty,
162-
pick,
163-
segment))
166+
let result = self.confirm_method(span,
167+
self_expr,
168+
call_expr,
169+
self_ty,
170+
pick.clone(),
171+
segment);
172+
173+
if result.illegal_sized_bound {
174+
// We probe again, taking all traits into account (not only those in scope).
175+
let candidates =
176+
match self.lookup_probe(span,
177+
segment.name,
178+
self_ty,
179+
call_expr,
180+
ProbeScope::AllTraits) {
181+
182+
// If we find a different result the caller probably forgot to import a trait.
183+
Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
184+
Err(Ambiguity(ref sources)) => {
185+
sources.iter()
186+
.filter_map(|source| {
187+
match *source {
188+
// Note: this cannot come from an inherent impl,
189+
// because the first probing succeeded.
190+
ImplSource(def) => self.tcx.trait_id_of_impl(def),
191+
TraitSource(_) => None,
192+
}
193+
})
194+
.collect()
195+
}
196+
_ => Vec::new(),
197+
};
198+
199+
return Err(IllegalSizedBound(candidates));
200+
}
201+
202+
Ok(result.callee)
203+
}
204+
205+
fn lookup_probe(&self,
206+
span: Span,
207+
method_name: ast::Name,
208+
self_ty: ty::Ty<'tcx>,
209+
call_expr: &'gcx hir::Expr,
210+
scope: ProbeScope)
211+
-> probe::PickResult<'tcx> {
212+
let mode = probe::Mode::MethodCall;
213+
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
214+
self.probe_for_name(span, mode, method_name, IsSuggestion(false),
215+
self_ty, call_expr.id, scope)
164216
}
165217

166218
/// `lookup_method_in_trait` is used for overloaded operators.
@@ -299,7 +351,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
299351
-> Result<Def, MethodError<'tcx>> {
300352
let mode = probe::Mode::Path;
301353
let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
302-
self_ty, expr_id)?;
354+
self_ty, expr_id, ProbeScope::TraitsInScope)?;
303355

304356
if let Some(import_id) = pick.import_id {
305357
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
}

0 commit comments

Comments
 (0)