]> git.lizzy.rs Git - rust.git/commitdiff
Use proper InferCtxt when probing for associated types in astconv
authorMichael Goulet <michael@errs.io>
Sun, 22 Jan 2023 05:11:24 +0000 (05:11 +0000)
committerMichael Goulet <michael@errs.io>
Mon, 23 Jan 2023 16:53:23 +0000 (16:53 +0000)
compiler/rustc_hir_analysis/src/astconv/mod.rs
compiler/rustc_hir_analysis/src/collect.rs
compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
tests/ui/typeck/issue-107087.rs [new file with mode: 0644]
tests/ui/typeck/issue-107087.stderr [new file with mode: 0644]

index 6435b05cef8a8bac5041a2fe8db5207c5d9b86d9..d23368e9bae5a268b2673681343822ecf057911a 100644 (file)
@@ -27,7 +27,7 @@
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{walk_generics, Visitor as _};
 use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_middle::middle::stability::AllowUnstable;
 use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
 use rustc_middle::ty::GenericParamDefKind;
@@ -37,7 +37,7 @@
 use rustc_span::edition::Edition;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{sym, Span};
+use rustc_span::{sym, Span, DUMMY_SP};
 use rustc_target::spec::abi;
 use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::astconv_object_safety_violations;
@@ -54,7 +54,7 @@
 pub struct PathSeg(pub DefId, pub usize);
 
 pub trait AstConv<'tcx> {
-    fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
+    fn tcx(&self) -> TyCtxt<'tcx>;
 
     fn item_def_id(&self) -> DefId;
 
@@ -131,6 +131,8 @@ fn astconv(&self) -> &dyn AstConv<'tcx>
     {
         self
     }
+
+    fn infcx(&self) -> Option<&InferCtxt<'tcx>>;
 }
 
 #[derive(Debug)]
@@ -2132,48 +2134,8 @@ pub fn associated_path_to_ty(
                     )
                     .emit() // Already reported in an earlier stage.
                 } else {
-                    // Find all the `impl`s that `qself_ty` has for any trait that has the
-                    // associated type, so that we suggest the right one.
-                    let infcx = tcx.infer_ctxt().build();
-                    // We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()`
-                    // to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`.
-                    let param_env = ty::ParamEnv::empty();
-                    let traits: Vec<_> = self
-                        .tcx()
-                        .all_traits()
-                        .filter(|trait_def_id| {
-                            // Consider only traits with the associated type
-                            tcx.associated_items(*trait_def_id)
-                                .in_definition_order()
-                                .any(|i| {
-                                    i.kind.namespace() == Namespace::TypeNS
-                                        && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
-                                        && matches!(i.kind, ty::AssocKind::Type)
-                                })
-                            // Consider only accessible traits
-                            && tcx.visibility(*trait_def_id)
-                                .is_accessible_from(self.item_def_id(), tcx)
-                            && tcx.all_impls(*trait_def_id)
-                                .any(|impl_def_id| {
-                                    let trait_ref = tcx.impl_trait_ref(impl_def_id);
-                                    trait_ref.map_or(false, |trait_ref| {
-                                        let impl_ = trait_ref.subst(
-                                            tcx,
-                                            infcx.fresh_substs_for_item(span, impl_def_id),
-                                        );
-                                        infcx
-                                            .can_eq(
-                                                param_env,
-                                                tcx.erase_regions(impl_.self_ty()),
-                                                tcx.erase_regions(qself_ty),
-                                            )
-                                            .is_ok()
-                                    })
-                                    && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
-                                })
-                        })
-                        .map(|trait_def_id| tcx.def_path_str(trait_def_id))
-                        .collect();
+                    let traits: Vec<_> =
+                        self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
 
                     // Don't print `TyErr` to the user.
                     self.report_ambiguous_associated_type(
@@ -2232,6 +2194,60 @@ pub fn associated_path_to_ty(
         Ok((ty, DefKind::AssocTy, assoc_ty_did))
     }
 
+    fn probe_traits_that_match_assoc_ty(
+        &self,
+        qself_ty: Ty<'tcx>,
+        assoc_ident: Ident,
+    ) -> Vec<String> {
+        let tcx = self.tcx();
+
+        // 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 = if let Some(infcx) = self.infcx() {
+            infcx
+        } else {
+            assert!(!qself_ty.needs_infer());
+            infcx_ = tcx.infer_ctxt().build();
+            &infcx_
+        };
+
+        tcx.all_traits()
+            .filter(|trait_def_id| {
+                // Consider only traits with the associated type
+                tcx.associated_items(*trait_def_id)
+                        .in_definition_order()
+                        .any(|i| {
+                            i.kind.namespace() == Namespace::TypeNS
+                                && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
+                                && matches!(i.kind, ty::AssocKind::Type)
+                        })
+                    // Consider only accessible traits
+                    && tcx.visibility(*trait_def_id)
+                        .is_accessible_from(self.item_def_id(), tcx)
+                    && tcx.all_impls(*trait_def_id)
+                        .any(|impl_def_id| {
+                            let trait_ref = tcx.impl_trait_ref(impl_def_id);
+                            trait_ref.map_or(false, |trait_ref| {
+                                let impl_ = trait_ref.subst(
+                                    tcx,
+                                    infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id),
+                                );
+                                infcx
+                                    .can_eq(
+                                        ty::ParamEnv::empty(),
+                                        tcx.erase_regions(impl_.self_ty()),
+                                        tcx.erase_regions(qself_ty),
+                                    )
+                                    .is_ok()
+                            })
+                            && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
+                        })
+            })
+            .map(|trait_def_id| tcx.def_path_str(trait_def_id))
+            .collect()
+    }
+
     fn lookup_assoc_ty(
         &self,
         ident: Ident,
index c17778ce8bc090e934b82b8399a5d05aa501ac22..c3ea867785b1461f1fdb2eb41409895ee7342a74 100644 (file)
@@ -25,7 +25,7 @@
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{GenericParamKind, Node};
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::query::Providers;
@@ -517,6 +517,10 @@ fn set_tainted_by_errors(&self, _: ErrorGuaranteed) {
     fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) {
         // There's no place to record types from signatures?
     }
+
+    fn infcx(&self) -> Option<&InferCtxt<'tcx>> {
+        None
+    }
 }
 
 /// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.
index 428fde642bc0901bff4a7fb92c8c6cb6c589c100..2747dabc2368fab1ab9e5b6789b6774e367e15b3 100644 (file)
@@ -324,6 +324,10 @@ fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
         let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty };
         self.write_ty(hir_id, ty)
     }
+
+    fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> {
+        Some(&self.infcx)
+    }
 }
 
 /// Represents a user-provided type in the raw form (never normalized).
diff --git a/tests/ui/typeck/issue-107087.rs b/tests/ui/typeck/issue-107087.rs
new file mode 100644 (file)
index 0000000..135cdf1
--- /dev/null
@@ -0,0 +1,18 @@
+struct A<T>(T);
+
+trait Foo {
+    type B;
+}
+
+impl Foo for A<u32> {
+    type B = i32;
+}
+
+impl Foo for A<i32> {
+    type B = i32;
+}
+
+fn main() {
+    A::B::<>::C
+    //~^ ERROR ambiguous associated type
+}
diff --git a/tests/ui/typeck/issue-107087.stderr b/tests/ui/typeck/issue-107087.stderr
new file mode 100644 (file)
index 0000000..70f1932
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0223]: ambiguous associated type
+  --> $DIR/issue-107087.rs:16:5
+   |
+LL |     A::B::<>::C
+   |     ^^^^^^^^ help: use the fully-qualified path: `<A<_> as Foo>::B`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0223`.