Skip to content

Treat projections with infer as placeholder during fast reject in new solver #108830

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

Merged
merged 3 commits into from
Mar 14, 2023
Merged
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
16 changes: 13 additions & 3 deletions compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams};
use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams, TreatProjections};
use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt};
use rustc_span::symbol::sym;

Expand Down Expand Up @@ -99,7 +99,12 @@ impl<'tcx> InherentCollect<'tcx> {
}
}

if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) {
if let Some(simp) = simplify_type(
self.tcx,
self_ty,
TreatParams::AsCandidateKey,
TreatProjections::AsCandidateKey,
) {
self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
} else {
bug!("unexpected self type: {:?}", self_ty);
Expand Down Expand Up @@ -159,7 +164,12 @@ impl<'tcx> InherentCollect<'tcx> {
}
}

if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::AsInfer) {
if let Some(simp) = simplify_type(
self.tcx,
ty,
TreatParams::AsCandidateKey,
TreatProjections::AsCandidateKey,
) {
self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
} else {
bug!("unexpected primitive type: {:?}", ty);
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rustc_infer::infer::canonical::OriginalQueryValues;
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_middle::middle::stability;
use rustc_middle::ty::fast_reject::TreatProjections;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::AssocItem;
use rustc_middle::ty::GenericParamDefKind;
Expand Down Expand Up @@ -699,7 +700,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}

fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) {
let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) else {
let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey, TreatProjections::AsCandidateKey) else {
bug!("unexpected incoherent type: {:?}", self_ty)
};
for &impl_def_id in self.tcx.incoherent_impls(simp) {
Expand Down
22 changes: 15 additions & 7 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use rustc_infer::infer::{
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::traits::util::supertraits;
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::fast_reject::TreatProjections;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths};
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt};
Expand Down Expand Up @@ -1257,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let target_ty = self
.autoderef(sugg_span, rcvr_ty)
.find(|(rcvr_ty, _)| {
DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer }
DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }
.types_may_unify(*rcvr_ty, impl_ty)
})
.map_or(impl_ty, |(ty, _)| ty)
Expand Down Expand Up @@ -1516,7 +1517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.into_iter()
.any(|info| self.associated_value(info.def_id, item_name).is_some());
let found_assoc = |ty: Ty<'tcx>| {
simplify_type(tcx, ty, TreatParams::AsInfer)
simplify_type(tcx, ty, TreatParams::AsCandidateKey, TreatProjections::AsCandidateKey)
.and_then(|simp| {
tcx.incoherent_impls(simp)
.iter()
Expand Down Expand Up @@ -2645,9 +2646,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// FIXME: Even though negative bounds are not implemented, we could maybe handle
// cases where a positive bound implies a negative impl.
(candidates, Vec::new())
} else if let Some(simp_rcvr_ty) =
simplify_type(self.tcx, rcvr_ty, TreatParams::AsPlaceholder)
{
} else if let Some(simp_rcvr_ty) = simplify_type(
self.tcx,
rcvr_ty,
TreatParams::ForLookup,
TreatProjections::ForLookup,
) {
let mut potential_candidates = Vec::new();
let mut explicitly_negative = Vec::new();
for candidate in candidates {
Expand All @@ -2660,8 +2664,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
.any(|imp_did| {
let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity();
let imp_simp =
simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder);
let imp_simp = simplify_type(
self.tcx,
imp.self_ty(),
TreatParams::ForLookup,
TreatProjections::ForLookup,
);
imp_simp.map_or(false, |s| s == simp_rcvr_ty)
})
{
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use rustc_middle::middle::exported_symbols::{
use rustc_middle::mir::interpret;
use rustc_middle::traits::specialization_graph;
use rustc_middle::ty::codec::TyEncoder;
use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams, TreatProjections};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
use rustc_middle::util::common::to_readable_str;
Expand Down Expand Up @@ -1858,7 +1858,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let simplified_self_ty = fast_reject::simplify_type(
self.tcx,
trait_ref.self_ty(),
TreatParams::AsInfer,
TreatParams::AsCandidateKey,
TreatProjections::AsCandidateKey,
);

fx_hash_map
Expand Down
63 changes: 39 additions & 24 deletions compiler/rustc_middle/src/ty/fast_reject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,35 @@ pub enum SimplifiedType {
/// generic parameters as if they were inference variables in that case.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum TreatParams {
/// Treat parameters as placeholders in the given environment.
/// Treat parameters as infer vars. This is the correct mode for caching
/// an impl's type for lookup.
AsCandidateKey,
/// Treat parameters as placeholders in the given environment. This is the
/// correct mode for *lookup*, as during candidate selection.
ForLookup,
}

/// During fast-rejection, we have the choice of treating projection types
/// as either simplifyable or not, depending on whether we expect the projection
/// to be normalized/rigid.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum TreatProjections {
/// In candidates, we may be able to normalize the projection
/// after instantiating the candidate and equating it with a goal.
///
/// Note that this also causes us to treat projections as if they were
/// placeholders. This is only correct if the given projection cannot
/// be normalized in the current context. Even if normalization fails,
/// it may still succeed later if the projection contains any inference
/// variables.
AsPlaceholder,
AsInfer,
/// We must assume that the `impl<T> Trait<T> for <T as Id>::This`
/// can apply to all self types so we don't return a simplified type
/// for `<T as Id>::This`.
AsCandidateKey,
/// In the old solver we don't try to normalize projections
/// when looking up impls and only access them by using the
/// current self type. This means that if the self type is
/// a projection which could later be normalized, we must not
/// treat it as rigid.
ForLookup,
/// We can treat projections in the self type as opaque as
/// we separately look up impls for the normalized self type.
NextSolverLookup,
}

/// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists.
Expand Down Expand Up @@ -87,6 +107,7 @@ pub fn simplify_type<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
treat_params: TreatParams,
treat_projections: TreatProjections,
) -> Option<SimplifiedType> {
match *ty.kind() {
ty::Bool => Some(BoolSimplifiedType),
Expand Down Expand Up @@ -115,19 +136,13 @@ pub fn simplify_type<'tcx>(
ty::FnPtr(f) => Some(FunctionSimplifiedType(f.skip_binder().inputs().len())),
ty::Placeholder(..) => Some(PlaceholderSimplifiedType),
ty::Param(_) => match treat_params {
TreatParams::AsPlaceholder => Some(PlaceholderSimplifiedType),
TreatParams::AsInfer => None,
TreatParams::ForLookup => Some(PlaceholderSimplifiedType),
TreatParams::AsCandidateKey => None,
},
ty::Alias(..) => match treat_params {
// When treating `ty::Param` as a placeholder, projections also
// don't unify with anything else as long as they are fully normalized.
//
// We will have to be careful with lazy normalization here.
TreatParams::AsPlaceholder if !ty.has_non_region_infer() => {
debug!("treating `{}` as a placeholder", ty);
Some(PlaceholderSimplifiedType)
}
TreatParams::AsPlaceholder | TreatParams::AsInfer => None,
ty::Alias(..) => match treat_projections {
TreatProjections::ForLookup if !ty.needs_infer() => Some(PlaceholderSimplifiedType),
TreatProjections::NextSolverLookup => Some(PlaceholderSimplifiedType),
TreatProjections::AsCandidateKey | TreatProjections::ForLookup => None,
},
ty::Foreign(def_id) => Some(ForeignSimplifiedType(def_id)),
ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None,
Expand Down Expand Up @@ -295,8 +310,8 @@ impl DeepRejectCtxt {
// Depending on the value of `treat_obligation_params`, we either
// treat generic parameters like placeholders or like inference variables.
ty::Param(_) => match self.treat_obligation_params {
TreatParams::AsPlaceholder => false,
TreatParams::AsInfer => true,
TreatParams::ForLookup => false,
TreatParams::AsCandidateKey => true,
},

ty::Infer(_) => true,
Expand Down Expand Up @@ -333,8 +348,8 @@ impl DeepRejectCtxt {
let k = impl_ct.kind();
match obligation_ct.kind() {
ty::ConstKind::Param(_) => match self.treat_obligation_params {
TreatParams::AsPlaceholder => false,
TreatParams::AsInfer => true,
TreatParams::ForLookup => false,
TreatParams::AsCandidateKey => true,
},

// As we don't necessarily eagerly evaluate constants,
Expand Down
58 changes: 41 additions & 17 deletions compiler/rustc_middle/src/ty/trait_def.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::traits::specialization_graph;
use crate::ty::fast_reject::{self, SimplifiedType, TreatParams};
use crate::ty::fast_reject::{self, SimplifiedType, TreatParams, TreatProjections};
use crate::ty::visit::TypeVisitableExt;
use crate::ty::{Ident, Ty, TyCtxt};
use hir::def_id::LOCAL_CRATE;
Expand Down Expand Up @@ -118,16 +118,32 @@ impl<'tcx> TyCtxt<'tcx> {
/// Iterate over every impl that could possibly match the self type `self_ty`.
///
/// `trait_def_id` MUST BE the `DefId` of a trait.
pub fn for_each_relevant_impl<F: FnMut(DefId)>(
pub fn for_each_relevant_impl(
self,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
mut f: F,
f: impl FnMut(DefId),
) {
let _: Option<()> = self.find_map_relevant_impl(trait_def_id, self_ty, |did| {
f(did);
None
});
self.for_each_relevant_impl_treating_projections(
trait_def_id,
self_ty,
TreatProjections::ForLookup,
f,
)
}

pub fn for_each_relevant_impl_treating_projections(
self,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
treat_projections: TreatProjections,
mut f: impl FnMut(DefId),
) {
let _: Option<()> =
self.find_map_relevant_impl(trait_def_id, self_ty, treat_projections, |did| {
f(did);
None
});
}

/// `trait_def_id` MUST BE the `DefId` of a trait.
Expand All @@ -137,7 +153,12 @@ impl<'tcx> TyCtxt<'tcx> {
self_ty: Ty<'tcx>,
) -> impl Iterator<Item = DefId> + 'tcx {
let impls = self.trait_impls_of(trait_def_id);
if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsInfer) {
if let Some(simp) = fast_reject::simplify_type(
self,
self_ty,
TreatParams::AsCandidateKey,
TreatProjections::AsCandidateKey,
) {
if let Some(impls) = impls.non_blanket_impls.get(&simp) {
return impls.iter().copied();
}
Expand All @@ -150,11 +171,12 @@ impl<'tcx> TyCtxt<'tcx> {
/// the first non-none value.
///
/// `trait_def_id` MUST BE the `DefId` of a trait.
pub fn find_map_relevant_impl<T, F: FnMut(DefId) -> Option<T>>(
pub fn find_map_relevant_impl<T>(
self,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
mut f: F,
treat_projections: TreatProjections,
mut f: impl FnMut(DefId) -> Option<T>,
) -> Option<T> {
// FIXME: This depends on the set of all impls for the trait. That is
// unfortunate wrt. incremental compilation.
Expand All @@ -169,14 +191,13 @@ impl<'tcx> TyCtxt<'tcx> {
}
}

// Note that we're using `TreatParams::AsPlaceholder` to query `non_blanket_impls` while using
// `TreatParams::AsInfer` while actually adding them.
//
// This way, when searching for some impl for `T: Trait`, we do not look at any impls
// whose outer level is not a parameter or projection. Especially for things like
// `T: Clone` this is incredibly useful as we would otherwise look at all the impls
// of `Clone` for `Option<T>`, `Vec<T>`, `ConcreteType` and so on.
if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsPlaceholder) {
if let Some(simp) =
fast_reject::simplify_type(self, self_ty, TreatParams::ForLookup, treat_projections)
{
if let Some(impls) = impls.non_blanket_impls.get(&simp) {
for &impl_def_id in impls {
if let result @ Some(_) = f(impl_def_id) {
Expand Down Expand Up @@ -237,9 +258,12 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait
continue;
}

if let Some(simplified_self_ty) =
fast_reject::simplify_type(tcx, impl_self_ty, TreatParams::AsInfer)
{
if let Some(simplified_self_ty) = fast_reject::simplify_type(
tcx,
impl_self_ty,
TreatParams::AsCandidateKey,
TreatProjections::AsCandidateKey,
) {
impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id);
} else {
impls.blanket_impls.push(impl_def_id);
Expand Down
21 changes: 14 additions & 7 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::mir;
use crate::ty::fast_reject::TreatProjections;
use crate::ty::layout::IntegerExt;
use crate::ty::{
self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
Expand Down Expand Up @@ -363,14 +364,20 @@ impl<'tcx> TyCtxt<'tcx> {
self.ensure().coherent_trait(drop_trait);

let ty = self.type_of(adt_did).subst_identity();
let (did, constness) = self.find_map_relevant_impl(drop_trait, ty, |impl_did| {
if let Some(item_id) = self.associated_item_def_ids(impl_did).first() {
if validate(self, impl_did).is_ok() {
return Some((*item_id, self.constness(impl_did)));
let (did, constness) = self.find_map_relevant_impl(
drop_trait,
ty,
// FIXME: This could also be some other mode, like "unexpected"
TreatProjections::ForLookup,
|impl_did| {
if let Some(item_id) = self.associated_item_def_ids(impl_did).first() {
if validate(self, impl_did).is_ok() {
return Some((*item_id, self.constness(impl_did)));
}
}
}
None
})?;
None
},
)?;

Some(ty::Destructor { did, constness })
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2215,7 +2215,7 @@ impl CheckAttrVisitor<'_> {
// `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
//
// Properly checking this means pulling in additional `rustc` crates, so we don't.
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer };
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey };

if sig.abi != Abi::Rust {
tcx.sess.emit_err(errors::ProcMacroInvalidAbi {
Expand Down
Loading