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).
This commit is contained in:
parent
cc65ebd0d2
commit
b5e73bfe90
2 changed files with 85 additions and 2 deletions
|
@ -222,6 +222,66 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
err.emit()
|
err.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn complain_about_ambiguous_inherent_assoc_type(
|
||||||
|
&self,
|
||||||
|
name: Ident,
|
||||||
|
candidates: Vec<(DefId, DefId)>,
|
||||||
|
span: Span,
|
||||||
|
) -> ErrorGuaranteed {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
self.tcx().sess,
|
||||||
|
name.span,
|
||||||
|
E0034,
|
||||||
|
"multiple applicable items in scope"
|
||||||
|
);
|
||||||
|
err.span_label(name.span, format!("multiple `{name}` found"));
|
||||||
|
self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span);
|
||||||
|
err.emit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
|
||||||
|
fn note_ambiguous_inherent_assoc_type(
|
||||||
|
&self,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
candidates: Vec<(DefId, DefId)>,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
|
||||||
|
// Dynamic limit to avoid hiding just one candidate, which is silly.
|
||||||
|
let limit = if candidates.len() == 5 { 5 } else { 4 };
|
||||||
|
|
||||||
|
for (index, &(assoc_item, _)) in candidates.iter().take(limit).enumerate() {
|
||||||
|
let impl_ = tcx.impl_of_method(assoc_item).unwrap();
|
||||||
|
|
||||||
|
let note_span = if assoc_item.is_local() {
|
||||||
|
Some(tcx.def_span(assoc_item))
|
||||||
|
} else if impl_.is_local() {
|
||||||
|
Some(tcx.def_span(impl_))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let title = if candidates.len() > 1 {
|
||||||
|
format!("candidate #{}", index + 1)
|
||||||
|
} else {
|
||||||
|
"the candidate".into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let impl_ty = tcx.at(span).type_of(impl_).subst_identity();
|
||||||
|
let note = format!("{title} is defined in an impl for the type `{impl_ty}`");
|
||||||
|
|
||||||
|
if let Some(span) = note_span {
|
||||||
|
err.span_note(span, ¬e);
|
||||||
|
} else {
|
||||||
|
err.note(¬e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if candidates.len() > limit {
|
||||||
|
err.note(&format!("and {} others", candidates.len() - limit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
|
// FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
|
||||||
pub(crate) fn complain_about_inherent_assoc_type_not_found(
|
pub(crate) fn complain_about_inherent_assoc_type_not_found(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -2217,12 +2217,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In contexts that have no inference context, just make a new one.
|
||||||
|
// We do need a local variable to store it, though.
|
||||||
|
let infcx_;
|
||||||
|
let infcx = match self.infcx() {
|
||||||
|
Some(infcx) => infcx,
|
||||||
|
None => {
|
||||||
|
assert!(!self_ty.needs_infer());
|
||||||
|
infcx_ = tcx.infer_ctxt().ignoring_regions().build();
|
||||||
|
&infcx_
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let param_env = tcx.param_env(block.owner.to_def_id());
|
let param_env = tcx.param_env(block.owner.to_def_id());
|
||||||
let cause = ObligationCause::misc(span, block.owner.def_id);
|
let cause = ObligationCause::misc(span, block.owner.def_id);
|
||||||
let mut fulfillment_errors = Vec::new();
|
let mut fulfillment_errors = Vec::new();
|
||||||
|
let mut applicable_candidates = Vec::new();
|
||||||
|
|
||||||
for &(impl_, (assoc_item, def_scope)) in &candidates {
|
for &(impl_, (assoc_item, def_scope)) in &candidates {
|
||||||
let infcx = tcx.infer_ctxt().ignoring_regions().build();
|
|
||||||
let ocx = ObligationCtxt::new(&infcx);
|
let ocx = ObligationCtxt::new(&infcx);
|
||||||
|
|
||||||
let impl_ty = tcx.type_of(impl_);
|
let impl_ty = tcx.type_of(impl_);
|
||||||
|
@ -2253,6 +2265,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applicable_candidates.push((assoc_item, def_scope));
|
||||||
|
}
|
||||||
|
|
||||||
|
if applicable_candidates.len() > 1 {
|
||||||
|
return Err(self.complain_about_ambiguous_inherent_assoc_type(
|
||||||
|
name,
|
||||||
|
applicable_candidates,
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((assoc_item, def_scope)) = applicable_candidates.pop() {
|
||||||
self.check_assoc_ty(assoc_item, name, def_scope, block, span);
|
self.check_assoc_ty(assoc_item, name, def_scope, block, span);
|
||||||
|
|
||||||
let ty::Adt(_, adt_substs) = self_ty.kind() else {
|
let ty::Adt(_, adt_substs) = self_ty.kind() else {
|
||||||
|
@ -2269,7 +2293,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
// associated type hold, if any.
|
// associated type hold, if any.
|
||||||
let ty = tcx.type_of(assoc_item).subst(tcx, item_substs);
|
let ty = tcx.type_of(assoc_item).subst(tcx, item_substs);
|
||||||
|
|
||||||
// FIXME(fmease): Don't return early here! There might be multiple applicable candidates.
|
|
||||||
return Ok(Some((ty, assoc_item)));
|
return Ok(Some((ty, assoc_item)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue