Auto merge of #101834 - compiler-errors:super-deduce-closure-sig, r=lcnr
Elaborate supertrait obligations when deducing closure signatures We elaborate the supertrait obligations of any registered predicates for a closure to see if we can infer a closure signature. This is not as general of a fix as it *could* be, since we just elaborate supertrait bounds instead of doing a theoretical walk of _all_ registered predicates that might cause us to deduce `Fn` trait information for a closure infer var. I don't even know how to come up with an example that fails here but would work with a more general system. Fixes #23012 Also fixes the existing compile failure in #57611 r? `@ghost` for now until I do a perf run cc `@nikomatsakis` since you commented on https://github.com/rust-lang/rust/issues/23012#issuecomment-1152397290
This commit is contained in:
commit
6718ea1cff
@ -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;
|
||||
@ -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 mut expected_sig = None;
|
||||
let mut expected_kind = None;
|
||||
|
||||
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
|
||||
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 trait_def_id = match bound_predicate.skip_binder() {
|
||||
ty::PredicateKind::Projection(data) => {
|
||||
Some(data.projection_ty.trait_def_id(self.tcx))
|
||||
}
|
||||
});
|
||||
|
||||
// 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))));
|
||||
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)
|
||||
}
|
||||
@ -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)
|
||||
})?
|
||||
}
|
||||
|
@ -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;
|
||||
@ -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() {
|
||||
@ -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>> {
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
@ -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)]
|
||||
|
@ -1,26 +0,0 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-57611-trait-alias.rs:21:9
|
||||
|
|
||||
LL | |x| x
|
||||
| ^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected trait `for<'a> Fn<(&'a X,)>`
|
||||
found trait `Fn<(&X,)>`
|
||||
note: this closure does not fulfill the lifetime requirements
|
||||
--> $DIR/issue-57611-trait-alias.rs:21:9
|
||||
|
|
||||
LL | |x| x
|
||||
| ^^^
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/issue-57611-trait-alias.rs:21:9
|
||||
|
|
||||
LL | |x| x
|
||||
| ^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 X) -> &X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
x
Reference in New Issue
Block a user