Skip to content

Commit

Permalink
Rollup merge of #107100 - compiler-errors:issue-107087, r=lcnr
Browse files Browse the repository at this point in the history
Use proper `InferCtxt` when probing for associated types in astconv

Fixes #107087
  • Loading branch information
matthiaskrgr authored Jan 28, 2023
2 parents 7b78b6a + da3ecb0 commit 28188d1
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 46 deletions.
106 changes: 61 additions & 45 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
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;
Expand All @@ -37,7 +37,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT
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;
Expand All @@ -54,7 +54,7 @@ use std::slice;
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;

Expand Down Expand Up @@ -131,6 +131,8 @@ pub trait AstConv<'tcx> {
{
self
}

fn infcx(&self) -> Option<&InferCtxt<'tcx>>;
}

#[derive(Debug)]
Expand Down Expand Up @@ -2132,48 +2134,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
)
.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(
Expand Down Expand Up @@ -2232,6 +2194,60 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
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,
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use rustc_hir as hir;
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;
Expand Down Expand Up @@ -517,6 +517,10 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
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.
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
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).
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/typeck/issue-107087.rs
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 9 additions & 0 deletions tests/ui/typeck/issue-107087.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 28188d1

Please sign in to comment.