Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit b5e73bf

Browse files
committed
Groundwork for detecting ambiguous candidates
NB: Since we are using the same InferCtxt in each iteration, we essentially *spoil* the inference variables and we only ever get at most *one* applicable candidate (only the 1st candidate has clean variables that can still unify correctly).
1 parent cc65ebd commit b5e73bf

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

compiler/rustc_hir_analysis/src/astconv/errors.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,66 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
222222
err.emit()
223223
}
224224

225+
pub(crate) fn complain_about_ambiguous_inherent_assoc_type(
226+
&self,
227+
name: Ident,
228+
candidates: Vec<(DefId, DefId)>,
229+
span: Span,
230+
) -> ErrorGuaranteed {
231+
let mut err = struct_span_err!(
232+
self.tcx().sess,
233+
name.span,
234+
E0034,
235+
"multiple applicable items in scope"
236+
);
237+
err.span_label(name.span, format!("multiple `{name}` found"));
238+
self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span);
239+
err.emit()
240+
}
241+
242+
// FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
243+
fn note_ambiguous_inherent_assoc_type(
244+
&self,
245+
err: &mut Diagnostic,
246+
candidates: Vec<(DefId, DefId)>,
247+
span: Span,
248+
) {
249+
let tcx = self.tcx();
250+
251+
// Dynamic limit to avoid hiding just one candidate, which is silly.
252+
let limit = if candidates.len() == 5 { 5 } else { 4 };
253+
254+
for (index, &(assoc_item, _)) in candidates.iter().take(limit).enumerate() {
255+
let impl_ = tcx.impl_of_method(assoc_item).unwrap();
256+
257+
let note_span = if assoc_item.is_local() {
258+
Some(tcx.def_span(assoc_item))
259+
} else if impl_.is_local() {
260+
Some(tcx.def_span(impl_))
261+
} else {
262+
None
263+
};
264+
265+
let title = if candidates.len() > 1 {
266+
format!("candidate #{}", index + 1)
267+
} else {
268+
"the candidate".into()
269+
};
270+
271+
let impl_ty = tcx.at(span).type_of(impl_).subst_identity();
272+
let note = format!("{title} is defined in an impl for the type `{impl_ty}`");
273+
274+
if let Some(span) = note_span {
275+
err.span_note(span, &note);
276+
} else {
277+
err.note(&note);
278+
}
279+
}
280+
if candidates.len() > limit {
281+
err.note(&format!("and {} others", candidates.len() - limit));
282+
}
283+
}
284+
225285
// FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
226286
pub(crate) fn complain_about_inherent_assoc_type_not_found(
227287
&self,

compiler/rustc_hir_analysis/src/astconv/mod.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,12 +2217,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22172217
return Ok(None);
22182218
}
22192219

2220+
// In contexts that have no inference context, just make a new one.
2221+
// We do need a local variable to store it, though.
2222+
let infcx_;
2223+
let infcx = match self.infcx() {
2224+
Some(infcx) => infcx,
2225+
None => {
2226+
assert!(!self_ty.needs_infer());
2227+
infcx_ = tcx.infer_ctxt().ignoring_regions().build();
2228+
&infcx_
2229+
}
2230+
};
2231+
22202232
let param_env = tcx.param_env(block.owner.to_def_id());
22212233
let cause = ObligationCause::misc(span, block.owner.def_id);
22222234
let mut fulfillment_errors = Vec::new();
2235+
let mut applicable_candidates = Vec::new();
22232236

22242237
for &(impl_, (assoc_item, def_scope)) in &candidates {
2225-
let infcx = tcx.infer_ctxt().ignoring_regions().build();
22262238
let ocx = ObligationCtxt::new(&infcx);
22272239

22282240
let impl_ty = tcx.type_of(impl_);
@@ -2253,6 +2265,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22532265
continue;
22542266
}
22552267

2268+
applicable_candidates.push((assoc_item, def_scope));
2269+
}
2270+
2271+
if applicable_candidates.len() > 1 {
2272+
return Err(self.complain_about_ambiguous_inherent_assoc_type(
2273+
name,
2274+
applicable_candidates,
2275+
span,
2276+
));
2277+
}
2278+
2279+
if let Some((assoc_item, def_scope)) = applicable_candidates.pop() {
22562280
self.check_assoc_ty(assoc_item, name, def_scope, block, span);
22572281

22582282
let ty::Adt(_, adt_substs) = self_ty.kind() else {
@@ -2269,7 +2293,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22692293
// associated type hold, if any.
22702294
let ty = tcx.type_of(assoc_item).subst(tcx, item_substs);
22712295

2272-
// FIXME(fmease): Don't return early here! There might be multiple applicable candidates.
22732296
return Ok(Some((ty, assoc_item)));
22742297
}
22752298

0 commit comments

Comments
 (0)