Skip to content
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

Elaborate supertrait obligations when deducing closure signatures #101834

Merged
merged 3 commits into from
Nov 4, 2022
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
72 changes: 45 additions & 27 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Span;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::ArgKind;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use std::cmp;
Expand Down Expand Up @@ -225,33 +226,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
expected_vid: ty::TyVid,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
let expected_sig =
self.obligations_for_self_ty(expected_vid).find_map(|(_, obligation)| {
debug!(?obligation.predicate);

let bound_predicate = obligation.predicate.kind();
if let ty::PredicateKind::Projection(proj_predicate) =
obligation.predicate.kind().skip_binder()
{
// Given a Projection predicate, we can potentially infer
// the complete signature.
self.deduce_sig_from_projection(
Some(obligation.cause.span),
bound_predicate.rebind(proj_predicate),
)
} else {
None
}
});
let mut expected_sig = None;
let mut expected_kind = None;

for obligation in traits::elaborate_obligations(
self.tcx,
// Reverse the obligations here, since `elaborate_*` uses a stack,
// and we want to keep inference generally in the same order of
// the registered obligations.
self.obligations_for_self_ty(expected_vid).rev().collect(),
) {
debug!(?obligation.predicate);
let bound_predicate = obligation.predicate.kind();

// Given a Projection predicate, we can potentially infer
// the complete signature.
if expected_sig.is_none()
&& let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder()
{
expected_sig = self.deduce_sig_from_projection(
Some(obligation.cause.span),
bound_predicate.rebind(proj_predicate),
);
}

// Even if we can't infer the full signature, we may be able to
// infer the kind. This can occur when we elaborate a predicate
// like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive.
let expected_kind = self
.obligations_for_self_ty(expected_vid)
.filter_map(|(tr, _)| self.tcx.fn_trait_kind_from_lang_item(tr.def_id()))
.fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur))));
// Even if we can't infer the full signature, we may be able to
// infer the kind. This can occur when we elaborate a predicate
// like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive.
let trait_def_id = match bound_predicate.skip_binder() {
ty::PredicateKind::Projection(data) => {
Some(data.projection_ty.trait_def_id(self.tcx))
}
ty::PredicateKind::Trait(data) => Some(data.def_id()),
_ => None,
};
if let Some(closure_kind) =
trait_def_id.and_then(|def_id| self.tcx.fn_trait_kind_from_lang_item(def_id))
{
expected_kind = Some(
expected_kind
.map_or_else(|| closure_kind, |current| cmp::min(current, closure_kind)),
);
}
}

(expected_sig, expected_kind)
}
Expand Down Expand Up @@ -689,7 +707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let output_ty = match *ret_ty.kind() {
ty::Infer(ty::TyVar(ret_vid)) => {
self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| {
self.obligations_for_self_ty(ret_vid).find_map(|obligation| {
get_future_output(obligation.predicate, obligation.cause.span)
})?
}
Expand Down
97 changes: 50 additions & 47 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMut
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{
self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef,
ToPredicate, Ty, UserType,
self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPredicate, Ty,
UserType,
};
use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts};
use rustc_session::lint;
Expand Down Expand Up @@ -650,12 +650,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

#[instrument(skip(self), level = "debug")]
fn self_type_matches_expected_vid(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
expected_vid: ty::TyVid,
) -> bool {
let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
fn self_type_matches_expected_vid(&self, self_ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool {
let self_ty = self.shallow_resolve(self_ty);
debug!(?self_ty);

match *self_ty.kind() {
Expand All @@ -674,54 +670,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn obligations_for_self_ty<'b>(
&'b self,
self_ty: ty::TyVid,
) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
+ Captures<'tcx>
+ 'b {
) -> impl DoubleEndedIterator<Item = traits::PredicateObligation<'tcx>> + Captures<'tcx> + 'b
{
// FIXME: consider using `sub_root_var` here so we
// can see through subtyping.
let ty_var_root = self.root_var(self_ty);
trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations());

self.fulfillment_cx
.borrow()
.pending_obligations()
.into_iter()
.filter_map(move |obligation| {
let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Projection(data) => Some((
bound_predicate.rebind(data).required_poly_trait_ref(self.tcx),
obligation,
)),
ty::PredicateKind::Trait(data) => {
Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation))
}
ty::PredicateKind::Subtype(..) => None,
ty::PredicateKind::Coerce(..) => None,
ty::PredicateKind::RegionOutlives(..) => None,
ty::PredicateKind::TypeOutlives(..) => None,
ty::PredicateKind::WellFormed(..) => None,
ty::PredicateKind::ObjectSafe(..) => None,
ty::PredicateKind::ConstEvaluatable(..) => None,
ty::PredicateKind::ConstEquate(..) => None,
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
// `ClosureType` represents some `Closure`. It can't
// possibly be referring to the current closure,
// because we haven't produced the `Closure` for
// this closure yet; this is exactly why the other
// code is looking for a self type of an unresolved
// inference variable.
ty::PredicateKind::ClosureKind(..) => None,
ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
self.fulfillment_cx.borrow().pending_obligations().into_iter().filter_map(
move |obligation| match &obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Projection(data)
if self.self_type_matches_expected_vid(
data.projection_ty.self_ty(),
ty_var_root,
) =>
{
Some(obligation)
}
})
.filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
ty::PredicateKind::Trait(data)
if self.self_type_matches_expected_vid(data.self_ty(), ty_var_root) =>
{
Some(obligation)
}

ty::PredicateKind::Trait(..)
| ty::PredicateKind::Projection(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::RegionOutlives(..)
| ty::PredicateKind::TypeOutlives(..)
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
// `ClosureType` represents some `Closure`. It can't
// possibly be referring to the current closure,
// because we haven't produced the `Closure` for
// this closure yet; this is exactly why the other
// code is looking for a self type of an unresolved
// inference variable.
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
},
)
}

pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
self.obligations_for_self_ty(self_ty)
.any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait())
let sized_did = self.tcx.lang_items().sized_trait();
self.obligations_for_self_ty(self_ty).any(|obligation| {
match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Trait(data) => Some(data.def_id()) == sized_did,
_ => false,
}
})
}

pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
Expand Down
29 changes: 29 additions & 0 deletions src/test/ui/closures/issue-23012-supertrait-signature-inference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// check-pass
// Checks that we can infer a closure signature even if the `FnOnce` bound is
// a supertrait of the obligations we have currently registered for the Ty var.

pub trait Receive<T, E>: FnOnce(Result<T, E>) {
fn receive(self, res: Result<T, E>);
}

impl<T, E, F: FnOnce(Result<T, E>)> Receive<T, E> for F {
fn receive(self, res: Result<T, E>) {
self(res)
}
}

pub trait Async<T, E> {
fn receive<F: Receive<T, E>>(self, f: F);
}

impl<T, E> Async<T, E> for Result<T, E> {
fn receive<F: Receive<T, E>>(self, f: F) {
f(self)
}
}

pub fn main() {
Ok::<u32, ()>(123).receive(|res| {
res.unwrap();
});
}
3 changes: 1 addition & 2 deletions src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// check-pass
// Regression test for issue #57611
// Ensures that we don't ICE
// FIXME: This should compile, but it currently doesn't
// known-bug: unknown

#![feature(trait_alias)]
#![feature(type_alias_impl_trait)]
Expand Down
26 changes: 0 additions & 26 deletions src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr

This file was deleted.