Skip to content

Don't build ParamEnv and do trait solving in ItemCtxts when lowering IATs #140247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,22 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause};
use rustc_middle::query::Providers;
use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypingMode, fold_regions};
use rustc_middle::ty::{
self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, TypingMode, fold_regions,
};
use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{ObligationCtxt, hir_ty_lowering_dyn_compatibility_violations};
use rustc_trait_selection::traits::{
FulfillmentError, ObligationCtxt, hir_ty_lowering_dyn_compatibility_violations,
};
use tracing::{debug, instrument};

use crate::errors;
use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};
use crate::hir_ty_lowering::{
FeedConstTy, HirTyLowerer, InherentAssocCandidate, RegionInferReason,
};

pub(crate) mod dump;
mod generics_of;
Expand Down Expand Up @@ -364,6 +370,58 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_ident))
}

#[instrument(level = "debug", skip(self, _span), ret)]
fn select_inherent_assoc_candidates(
&self,
_span: Span,
self_ty: Ty<'tcx>,
candidates: Vec<InherentAssocCandidate>,
) -> (Vec<InherentAssocCandidate>, Vec<FulfillmentError<'tcx>>) {
assert!(!self_ty.has_infer());

// We don't just call the normal normalization routine here as we can't provide the
// correct `ParamEnv` and it would be wrong to invoke arbitrary trait solving under
// the wrong `ParamEnv`. Expanding free aliases doesn't need a `ParamEnv` so we do
// this just to make resolution a little bit smarter.
let self_ty = self.tcx.expand_free_alias_tys(self_ty);
debug!("select_inherent_assoc_candidates: self_ty={:?}", self_ty);

let candidates = candidates
.into_iter()
.filter(|&InherentAssocCandidate { impl_, .. }| {
let impl_ty = self.tcx().type_of(impl_).instantiate_identity();

// See comment on doing this operation for `self_ty`
let impl_ty = self.tcx.expand_free_alias_tys(impl_ty);
debug!("select_inherent_assoc_candidates: impl_ty={:?}", impl_ty);

// We treat parameters in the self ty as rigid and parameters in the impl ty as infers
// because it allows `impl<T> Foo<T>` to unify with `Foo<u8>::IAT`, while also disallowing
// `Foo<T>::IAT` from unifying with `impl Foo<u8>`.
//
// We don't really care about a depth limit here because we're only working with user-written
// types and if they wrote a type that would take hours to walk then that's kind of on them. On
// the other hand the default depth limit is relatively low and could realistically be hit by
// users in normal cases.
//
// `DeepRejectCtxt` leads to slightly worse IAT resolution than real type equality in cases
// where the `impl_ty` has repeated uses of generic parameters. E.g. `impl<T> Foo<T, T>` would
// be considered a valid candidate when resolving `Foo<u8, u16>::IAT`.
//
// Not replacing escaping bound vars in `self_ty` with placeholders also leads to slightly worse
// resolution, but it probably won't come up in practice and it would be backwards compatible
// to switch over to doing that.
ty::DeepRejectCtxt::relate_rigid_infer(self.tcx).types_may_unify_with_depth(
self_ty,
impl_ty,
usize::MAX,
)
})
.collect();

(candidates, vec![])
}

fn lower_assoc_item_path(
&self,
span: Span,
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use rustc_trait_selection::traits::{
use smallvec::SmallVec;
use tracing::debug;

use super::InherentAssocCandidate;
use crate::errors::{
self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams,
ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
Expand Down Expand Up @@ -742,7 +743,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&self,
name: Ident,
self_ty: Ty<'tcx>,
candidates: Vec<(DefId, (DefId, DefId))>,
candidates: Vec<InherentAssocCandidate>,
fulfillment_errors: Vec<FulfillmentError<'tcx>>,
span: Span,
assoc_tag: ty::AssocTag,
Expand Down Expand Up @@ -776,8 +777,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let type_candidates = candidates
.iter()
.take(limit)
.map(|&(impl_, _)| {
format!("- `{}`", tcx.at(span).type_of(impl_).instantiate_identity())
.map(|cand| {
format!("- `{}`", tcx.at(span).type_of(cand.impl_).instantiate_identity())
})
.collect::<Vec<_>>()
.join("\n");
Expand Down
151 changes: 39 additions & 112 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,22 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause};
use rustc_infer::traits::DynCompatibilityViolation;
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
use rustc_middle::ty::{
self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt,
TypeVisitableExt, TypingMode, Upcast, fold_regions,
self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt,
TypingMode, Upcast, fold_regions,
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_session::parse::feature_err;
use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, ObligationCtxt};
use rustc_trait_selection::traits::{self, FulfillmentError};
use tracing::{debug, instrument};

use crate::check::check_abi_fn_ptr;
Expand Down Expand Up @@ -99,6 +100,13 @@ pub enum RegionInferReason<'a> {
OutlivesBound,
}

#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Debug)]
pub struct InherentAssocCandidate {
pub impl_: DefId,
pub assoc_item: DefId,
pub scope: DefId,
}

/// A context which can lower type-system entities from the [HIR][hir] to
/// the [`rustc_middle::ty`] representation.
///
Expand Down Expand Up @@ -148,6 +156,13 @@ pub trait HirTyLowerer<'tcx> {
assoc_ident: Ident,
) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>;

fn select_inherent_assoc_candidates(
&self,
span: Span,
self_ty: Ty<'tcx>,
candidates: Vec<InherentAssocCandidate>,
) -> (Vec<InherentAssocCandidate>, Vec<FulfillmentError<'tcx>>);

/// Lower a path to an associated item (of a trait) to a projection.
///
/// This method has to be defined by the concrete lowering context because
Expand Down Expand Up @@ -1449,48 +1464,32 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.filter_map(|&impl_| {
let (item, scope) =
self.probe_assoc_item_unchecked(name, assoc_tag, block, impl_)?;
Some((impl_, (item.def_id, scope)))
Some(InherentAssocCandidate { impl_, assoc_item: item.def_id, scope })
})
.collect();

if candidates.is_empty() {
return Ok(None);
}

//
// Select applicable inherent associated type candidates modulo regions.
//

// In contexts that have no inference context, just make a new one.
// We do need a local variable to store it, though.
let infcx = match self.infcx() {
Some(infcx) => infcx,
None => {
assert!(!self_ty.has_infer());
&tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis())
}
};
let (applicable_candidates, fulfillment_errors) =
self.select_inherent_assoc_candidates(span, self_ty, candidates.clone());

// FIXME(inherent_associated_types): Acquiring the ParamEnv this early leads to cycle errors
// when inside of an ADT (#108491) or where clause.
let param_env = tcx.param_env(block.owner);
let InherentAssocCandidate { impl_, assoc_item, scope: def_scope } =
match &applicable_candidates[..] {
&[] => Err(self.report_unresolved_inherent_assoc_item(
name,
self_ty,
candidates,
fulfillment_errors,
span,
assoc_tag,
)),

let mut universes = if self_ty.has_escaping_bound_vars() {
vec![None; self_ty.outer_exclusive_binder().as_usize()]
} else {
vec![]
};
&[applicable_candidate] => Ok(applicable_candidate),

let (impl_, (assoc_item, def_scope)) = crate::traits::with_replaced_escaping_bound_vars(
infcx,
&mut universes,
self_ty,
|self_ty| {
self.select_inherent_assoc_candidates(
infcx, name, span, self_ty, param_env, candidates, assoc_tag,
)
},
)?;
&[_, ..] => Err(self.report_ambiguous_inherent_assoc_item(
name,
candidates.into_iter().map(|cand| cand.assoc_item).collect(),
span,
)),
}?;

self.check_assoc_item(assoc_item, name, def_scope, block, span);

Expand All @@ -1507,78 +1506,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ok(Some((assoc_item, args)))
}

fn select_inherent_assoc_candidates(
&self,
infcx: &InferCtxt<'tcx>,
name: Ident,
span: Span,
self_ty: Ty<'tcx>,
param_env: ParamEnv<'tcx>,
candidates: Vec<(DefId, (DefId, DefId))>,
assoc_tag: ty::AssocTag,
) -> Result<(DefId, (DefId, DefId)), ErrorGuaranteed> {
let tcx = self.tcx();
let mut fulfillment_errors = Vec::new();

let applicable_candidates: Vec<_> = candidates
.iter()
.copied()
.filter(|&(impl_, _)| {
infcx.probe(|_| {
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
let self_ty = ocx.normalize(&ObligationCause::dummy(), param_env, self_ty);

let impl_args = infcx.fresh_args_for_item(span, impl_);
let impl_ty = tcx.type_of(impl_).instantiate(tcx, impl_args);
let impl_ty = ocx.normalize(&ObligationCause::dummy(), param_env, impl_ty);

// Check that the self types can be related.
if ocx.eq(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() {
return false;
}

// Check whether the impl imposes obligations we have to worry about.
let impl_bounds = tcx.predicates_of(impl_).instantiate(tcx, impl_args);
let impl_bounds =
ocx.normalize(&ObligationCause::dummy(), param_env, impl_bounds);
let impl_obligations = traits::predicates_for_generics(
|_, _| ObligationCause::dummy(),
param_env,
impl_bounds,
);
ocx.register_obligations(impl_obligations);

let mut errors = ocx.select_where_possible();
if !errors.is_empty() {
fulfillment_errors.append(&mut errors);
return false;
}

true
})
})
.collect();

match &applicable_candidates[..] {
&[] => Err(self.report_unresolved_inherent_assoc_item(
name,
self_ty,
candidates,
fulfillment_errors,
span,
assoc_tag,
)),

&[applicable_candidate] => Ok(applicable_candidate),

&[_, ..] => Err(self.report_ambiguous_inherent_assoc_item(
name,
applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
span,
)),
}
}

/// Given name and kind search for the assoc item in the provided scope and check if it's accessible[^1].
///
/// [^1]: I.e., accessible in the provided scope wrt. visibility and stability.
Expand Down
Loading
Loading