Skip to content

Commit da3ecb0

Browse files
Use proper InferCtxt when probing for associated types in astconv
1 parent c8e6a9e commit da3ecb0

File tree

5 files changed

+97
-46
lines changed

5 files changed

+97
-46
lines changed

compiler/rustc_hir_analysis/src/astconv/mod.rs

+61-45
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
2727
use rustc_hir::def_id::{DefId, LocalDefId};
2828
use rustc_hir::intravisit::{walk_generics, Visitor as _};
2929
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
30-
use rustc_infer::infer::TyCtxtInferExt;
30+
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
3131
use rustc_middle::middle::stability::AllowUnstable;
3232
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
3333
use rustc_middle::ty::GenericParamDefKind;
@@ -37,7 +37,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT
3737
use rustc_span::edition::Edition;
3838
use rustc_span::lev_distance::find_best_match_for_name;
3939
use rustc_span::symbol::{kw, Ident, Symbol};
40-
use rustc_span::{sym, Span};
40+
use rustc_span::{sym, Span, DUMMY_SP};
4141
use rustc_target::spec::abi;
4242
use rustc_trait_selection::traits;
4343
use rustc_trait_selection::traits::astconv_object_safety_violations;
@@ -54,7 +54,7 @@ use std::slice;
5454
pub struct PathSeg(pub DefId, pub usize);
5555

5656
pub trait AstConv<'tcx> {
57-
fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
57+
fn tcx(&self) -> TyCtxt<'tcx>;
5858

5959
fn item_def_id(&self) -> DefId;
6060

@@ -131,6 +131,8 @@ pub trait AstConv<'tcx> {
131131
{
132132
self
133133
}
134+
135+
fn infcx(&self) -> Option<&InferCtxt<'tcx>>;
134136
}
135137

136138
#[derive(Debug)]
@@ -2132,48 +2134,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
21322134
)
21332135
.emit() // Already reported in an earlier stage.
21342136
} else {
2135-
// Find all the `impl`s that `qself_ty` has for any trait that has the
2136-
// associated type, so that we suggest the right one.
2137-
let infcx = tcx.infer_ctxt().build();
2138-
// We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()`
2139-
// to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`.
2140-
let param_env = ty::ParamEnv::empty();
2141-
let traits: Vec<_> = self
2142-
.tcx()
2143-
.all_traits()
2144-
.filter(|trait_def_id| {
2145-
// Consider only traits with the associated type
2146-
tcx.associated_items(*trait_def_id)
2147-
.in_definition_order()
2148-
.any(|i| {
2149-
i.kind.namespace() == Namespace::TypeNS
2150-
&& i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
2151-
&& matches!(i.kind, ty::AssocKind::Type)
2152-
})
2153-
// Consider only accessible traits
2154-
&& tcx.visibility(*trait_def_id)
2155-
.is_accessible_from(self.item_def_id(), tcx)
2156-
&& tcx.all_impls(*trait_def_id)
2157-
.any(|impl_def_id| {
2158-
let trait_ref = tcx.impl_trait_ref(impl_def_id);
2159-
trait_ref.map_or(false, |trait_ref| {
2160-
let impl_ = trait_ref.subst(
2161-
tcx,
2162-
infcx.fresh_substs_for_item(span, impl_def_id),
2163-
);
2164-
infcx
2165-
.can_eq(
2166-
param_env,
2167-
tcx.erase_regions(impl_.self_ty()),
2168-
tcx.erase_regions(qself_ty),
2169-
)
2170-
.is_ok()
2171-
})
2172-
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
2173-
})
2174-
})
2175-
.map(|trait_def_id| tcx.def_path_str(trait_def_id))
2176-
.collect();
2137+
let traits: Vec<_> =
2138+
self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
21772139

21782140
// Don't print `TyErr` to the user.
21792141
self.report_ambiguous_associated_type(
@@ -2232,6 +2194,60 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22322194
Ok((ty, DefKind::AssocTy, assoc_ty_did))
22332195
}
22342196

2197+
fn probe_traits_that_match_assoc_ty(
2198+
&self,
2199+
qself_ty: Ty<'tcx>,
2200+
assoc_ident: Ident,
2201+
) -> Vec<String> {
2202+
let tcx = self.tcx();
2203+
2204+
// In contexts that have no inference context, just make a new one.
2205+
// We do need a local variable to store it, though.
2206+
let infcx_;
2207+
let infcx = if let Some(infcx) = self.infcx() {
2208+
infcx
2209+
} else {
2210+
assert!(!qself_ty.needs_infer());
2211+
infcx_ = tcx.infer_ctxt().build();
2212+
&infcx_
2213+
};
2214+
2215+
tcx.all_traits()
2216+
.filter(|trait_def_id| {
2217+
// Consider only traits with the associated type
2218+
tcx.associated_items(*trait_def_id)
2219+
.in_definition_order()
2220+
.any(|i| {
2221+
i.kind.namespace() == Namespace::TypeNS
2222+
&& i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
2223+
&& matches!(i.kind, ty::AssocKind::Type)
2224+
})
2225+
// Consider only accessible traits
2226+
&& tcx.visibility(*trait_def_id)
2227+
.is_accessible_from(self.item_def_id(), tcx)
2228+
&& tcx.all_impls(*trait_def_id)
2229+
.any(|impl_def_id| {
2230+
let trait_ref = tcx.impl_trait_ref(impl_def_id);
2231+
trait_ref.map_or(false, |trait_ref| {
2232+
let impl_ = trait_ref.subst(
2233+
tcx,
2234+
infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id),
2235+
);
2236+
infcx
2237+
.can_eq(
2238+
ty::ParamEnv::empty(),
2239+
tcx.erase_regions(impl_.self_ty()),
2240+
tcx.erase_regions(qself_ty),
2241+
)
2242+
.is_ok()
2243+
})
2244+
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
2245+
})
2246+
})
2247+
.map(|trait_def_id| tcx.def_path_str(trait_def_id))
2248+
.collect()
2249+
}
2250+
22352251
fn lookup_assoc_ty(
22362252
&self,
22372253
ident: Ident,

compiler/rustc_hir_analysis/src/collect.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_hir as hir;
2525
use rustc_hir::def_id::{DefId, LocalDefId};
2626
use rustc_hir::intravisit::{self, Visitor};
2727
use rustc_hir::{GenericParamKind, Node};
28-
use rustc_infer::infer::TyCtxtInferExt;
28+
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
2929
use rustc_infer::traits::ObligationCause;
3030
use rustc_middle::hir::nested_filter;
3131
use rustc_middle::ty::query::Providers;
@@ -517,6 +517,10 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
517517
fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) {
518518
// There's no place to record types from signatures?
519519
}
520+
521+
fn infcx(&self) -> Option<&InferCtxt<'tcx>> {
522+
None
523+
}
520524
}
521525

522526
/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
324324
let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty };
325325
self.write_ty(hir_id, ty)
326326
}
327+
328+
fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> {
329+
Some(&self.infcx)
330+
}
327331
}
328332

329333
/// Represents a user-provided type in the raw form (never normalized).

tests/ui/typeck/issue-107087.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
struct A<T>(T);
2+
3+
trait Foo {
4+
type B;
5+
}
6+
7+
impl Foo for A<u32> {
8+
type B = i32;
9+
}
10+
11+
impl Foo for A<i32> {
12+
type B = i32;
13+
}
14+
15+
fn main() {
16+
A::B::<>::C
17+
//~^ ERROR ambiguous associated type
18+
}

tests/ui/typeck/issue-107087.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0223]: ambiguous associated type
2+
--> $DIR/issue-107087.rs:16:5
3+
|
4+
LL | A::B::<>::C
5+
| ^^^^^^^^ help: use the fully-qualified path: `<A<_> as Foo>::B`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0223`.

0 commit comments

Comments
 (0)