about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2023-02-17 17:06:27 +0100
committerLeón Orell Valerian Liehr <me@fmease.dev>2023-02-19 18:35:35 +0100
commitb5e73bfe90f7b4905829f1a6509b61ac5577ee07 (patch)
treeac8880bf2099c4b08cd2aadba41264e776567de8
parentcc65ebd0d29a79a2a1d3fd77e679c763837b33c4 (diff)
downloadrust-b5e73bfe90f7b4905829f1a6509b61ac5577ee07.tar.gz
rust-b5e73bfe90f7b4905829f1a6509b61ac5577ee07.zip
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).
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs60
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs27
2 files changed, 85 insertions, 2 deletions
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index 0e9f5fb3dae..f3a03805a44 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -222,6 +222,66 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         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, &note);
+            } else {
+                err.note(&note);
+            }
+        }
+        if candidates.len() > limit {
+            err.note(&format!("and {} others", candidates.len() - limit));
+        }
+    }
+
     // FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
     pub(crate) fn complain_about_inherent_assoc_type_not_found(
         &self,
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index d3468f03eed..43db8af7bac 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -2217,12 +2217,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             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 cause = ObligationCause::misc(span, block.owner.def_id);
         let mut fulfillment_errors = Vec::new();
+        let mut applicable_candidates = Vec::new();
 
         for &(impl_, (assoc_item, def_scope)) in &candidates {
-            let infcx = tcx.infer_ctxt().ignoring_regions().build();
             let ocx = ObligationCtxt::new(&infcx);
 
             let impl_ty = tcx.type_of(impl_);
@@ -2253,6 +2265,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 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);
 
             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.
             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)));
         }