Skip to content

Commit 05804f7

Browse files
committed
Auto merge of #122267 - compiler-errors:closure-like-goals, r=<try>
Eagerly instantiate closure/coroutine-like bounds with placeholders to deal with binders correctly A follow-up to #119849, however it aims to fix a different set of issues. Currently, we have trouble confirming goals where built-in closure/fnptr/coroutine signatures are compared against higher-ranked goals. Currently, we don't support goals like `for<'a> fn(&'a ()): Fn(&'a ())` because we don't expect the self type of goal to reference any bound regions from the goal, because we don't really know how to deal with the double binder of predicate + self type. However, this definitely can be reached (#121653) -- and in fact, it results in post-mono errors in the case of #112347 where the builtin type (e.g. a coroutine) is hidden behind a TAIT. The proper fix here is to instantiate the goal before trying to extract the signature from the self type. See final two commits. r? lcnr
2 parents 3cdcdaf + 0d9269f commit 05804f7

File tree

11 files changed

+119
-141
lines changed

11 files changed

+119
-141
lines changed

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+56-64
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::traits::{
2828
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
2929
ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, PolyTraitObligation,
3030
PredicateObligation, Selection, SelectionError, SignatureMismatch, TraitNotObjectSafe,
31-
Unimplemented,
31+
TraitObligation, Unimplemented,
3232
};
3333

3434
use super::BuiltinImplConditions;
@@ -676,17 +676,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
676676
fn_host_effect: ty::Const<'tcx>,
677677
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
678678
debug!(?obligation, "confirm_fn_pointer_candidate");
679+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
680+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
679681

680682
let tcx = self.tcx();
681-
682-
let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else {
683-
// FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`,
684-
// but we do not currently. Luckily, such a bound is not
685-
// particularly useful, so we don't expect users to write
686-
// them often.
687-
return Err(SelectionError::Unimplemented);
688-
};
689-
690683
let sig = self_ty.fn_sig(tcx);
691684
let trait_ref = closure_trait_ref_and_return_type(
692685
tcx,
@@ -698,7 +691,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
698691
)
699692
.map_bound(|(trait_ref, _)| trait_ref);
700693

701-
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
694+
let mut nested =
695+
self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?;
702696
let cause = obligation.derived_cause(BuiltinDerivedObligation);
703697

704698
// Confirm the `type Output: Sized;` bound that is present on `FnOnce`
@@ -746,10 +740,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
746740
&mut self,
747741
obligation: &PolyTraitObligation<'tcx>,
748742
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
749-
// Okay to skip binder because the args on coroutine types never
750-
// touch bound regions, they just capture the in-scope
751-
// type/region parameters.
752-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
743+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
744+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
753745
let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
754746
bug!("closure candidate for non-closure {:?}", obligation);
755747
};
@@ -758,23 +750,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
758750

759751
let coroutine_sig = args.as_coroutine().sig();
760752

761-
// NOTE: The self-type is a coroutine type and hence is
762-
// in fact unparameterized (or at least does not reference any
763-
// regions bound in the obligation).
764-
let self_ty = obligation
765-
.predicate
766-
.self_ty()
767-
.no_bound_vars()
768-
.expect("unboxed closure type should not capture bound vars from the predicate");
769-
770753
let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs(
771754
self.tcx(),
772755
obligation.predicate.def_id(),
773756
self_ty,
774757
coroutine_sig,
775758
);
776759

777-
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
760+
let nested = self.equate_trait_refs(
761+
obligation.with(self.tcx(), placeholder_predicate),
762+
ty::Binder::dummy(trait_ref),
763+
)?;
778764
debug!(?trait_ref, ?nested, "coroutine candidate obligations");
779765

780766
Ok(nested)
@@ -784,10 +770,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
784770
&mut self,
785771
obligation: &PolyTraitObligation<'tcx>,
786772
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
787-
// Okay to skip binder because the args on coroutine types never
788-
// touch bound regions, they just capture the in-scope
789-
// type/region parameters.
790-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
773+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
774+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
791775
let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
792776
bug!("closure candidate for non-closure {:?}", obligation);
793777
};
@@ -799,11 +783,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
799783
let (trait_ref, _) = super::util::future_trait_ref_and_outputs(
800784
self.tcx(),
801785
obligation.predicate.def_id(),
802-
obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(),
786+
self_ty,
803787
coroutine_sig,
804788
);
805789

806-
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
790+
let nested = self.equate_trait_refs(
791+
obligation.with(self.tcx(), placeholder_predicate),
792+
ty::Binder::dummy(trait_ref),
793+
)?;
807794
debug!(?trait_ref, ?nested, "future candidate obligations");
808795

809796
Ok(nested)
@@ -813,10 +800,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
813800
&mut self,
814801
obligation: &PolyTraitObligation<'tcx>,
815802
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
816-
// Okay to skip binder because the args on coroutine types never
817-
// touch bound regions, they just capture the in-scope
818-
// type/region parameters.
819-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
803+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
804+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
820805
let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
821806
bug!("closure candidate for non-closure {:?}", obligation);
822807
};
@@ -828,11 +813,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
828813
let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs(
829814
self.tcx(),
830815
obligation.predicate.def_id(),
831-
obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
816+
self_ty,
832817
gen_sig,
833818
);
834819

835-
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
820+
let nested = self.equate_trait_refs(
821+
obligation.with(self.tcx(), placeholder_predicate),
822+
ty::Binder::dummy(trait_ref),
823+
)?;
836824
debug!(?trait_ref, ?nested, "iterator candidate obligations");
837825

838826
Ok(nested)
@@ -842,10 +830,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
842830
&mut self,
843831
obligation: &PolyTraitObligation<'tcx>,
844832
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
845-
// Okay to skip binder because the args on coroutine types never
846-
// touch bound regions, they just capture the in-scope
847-
// type/region parameters.
848-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
833+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
834+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
849835
let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
850836
bug!("closure candidate for non-closure {:?}", obligation);
851837
};
@@ -857,11 +843,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
857843
let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs(
858844
self.tcx(),
859845
obligation.predicate.def_id(),
860-
obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
846+
self_ty,
861847
gen_sig,
862848
);
863849

864-
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
850+
let nested = self.equate_trait_refs(
851+
obligation.with(self.tcx(), placeholder_predicate),
852+
ty::Binder::dummy(trait_ref),
853+
)?;
865854
debug!(?trait_ref, ?nested, "iterator candidate obligations");
866855

867856
Ok(nested)
@@ -872,14 +861,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
872861
&mut self,
873862
obligation: &PolyTraitObligation<'tcx>,
874863
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
875-
// Okay to skip binder because the args on closure types never
876-
// touch bound regions, they just capture the in-scope
877-
// type/region parameters.
878-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
864+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
865+
let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
866+
879867
let trait_ref = match *self_ty.kind() {
880-
ty::Closure(_, args) => {
881-
self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_)
882-
}
868+
ty::Closure(..) => self.closure_trait_ref_unnormalized(
869+
self_ty,
870+
obligation.predicate.def_id(),
871+
self.tcx().consts.true_,
872+
),
883873
ty::CoroutineClosure(_, args) => {
884874
args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
885875
ty::TraitRef::new(
@@ -894,16 +884,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
894884
}
895885
};
896886

897-
self.confirm_poly_trait_refs(obligation, trait_ref)
887+
self.equate_trait_refs(obligation.with(self.tcx(), placeholder_predicate), trait_ref)
898888
}
899889

900890
#[instrument(skip(self), level = "debug")]
901891
fn confirm_async_closure_candidate(
902892
&mut self,
903893
obligation: &PolyTraitObligation<'tcx>,
904894
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
895+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
896+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
897+
905898
let tcx = self.tcx();
906-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
907899

908900
let mut nested = vec![];
909901
let (trait_ref, kind_ty) = match *self_ty.kind() {
@@ -970,7 +962,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
970962
_ => bug!("expected callable type for AsyncFn candidate"),
971963
};
972964

973-
nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?);
965+
nested.extend(
966+
self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?,
967+
);
974968

975969
let goal_kind =
976970
self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
@@ -1023,42 +1017,40 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10231017
/// selection of the impl. Therefore, if there is a mismatch, we
10241018
/// report an error to the user.
10251019
#[instrument(skip(self), level = "trace")]
1026-
fn confirm_poly_trait_refs(
1020+
fn equate_trait_refs(
10271021
&mut self,
1028-
obligation: &PolyTraitObligation<'tcx>,
1029-
self_ty_trait_ref: ty::PolyTraitRef<'tcx>,
1022+
obligation: TraitObligation<'tcx>,
1023+
found_trait_ref: ty::PolyTraitRef<'tcx>,
10301024
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
1031-
let obligation_trait_ref =
1032-
self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref());
1033-
let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
1025+
let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
10341026
obligation.cause.span,
10351027
HigherRankedType,
1036-
self_ty_trait_ref,
1028+
found_trait_ref,
10371029
);
10381030
// Normalize the obligation and expected trait refs together, because why not
1039-
let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } =
1031+
let Normalized { obligations: nested, value: (obligation_trait_ref, found_trait_ref) } =
10401032
ensure_sufficient_stack(|| {
10411033
normalize_with_depth(
10421034
self,
10431035
obligation.param_env,
10441036
obligation.cause.clone(),
10451037
obligation.recursion_depth + 1,
1046-
(obligation_trait_ref, self_ty_trait_ref),
1038+
(obligation.predicate.trait_ref, found_trait_ref),
10471039
)
10481040
});
10491041

10501042
// needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs
10511043
self.infcx
10521044
.at(&obligation.cause, obligation.param_env)
1053-
.eq(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref)
1045+
.eq(DefineOpaqueTypes::Yes, obligation_trait_ref, found_trait_ref)
10541046
.map(|InferOk { mut obligations, .. }| {
10551047
obligations.extend(nested);
10561048
obligations
10571049
})
10581050
.map_err(|terr| {
10591051
SignatureMismatch(Box::new(SignatureMismatchData {
10601052
expected_trait_ref: ty::Binder::dummy(obligation_trait_ref),
1061-
found_trait_ref: ty::Binder::dummy(expected_trait_ref),
1053+
found_trait_ref: ty::Binder::dummy(found_trait_ref),
10621054
terr,
10631055
}))
10641056
})

compiler/rustc_trait_selection/src/traits/select/mod.rs

+6-14
Original file line numberDiff line numberDiff line change
@@ -2667,26 +2667,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
26672667
#[instrument(skip(self), level = "debug")]
26682668
fn closure_trait_ref_unnormalized(
26692669
&mut self,
2670-
obligation: &PolyTraitObligation<'tcx>,
2671-
args: GenericArgsRef<'tcx>,
2670+
self_ty: Ty<'tcx>,
2671+
fn_trait_def_id: DefId,
26722672
fn_host_effect: ty::Const<'tcx>,
26732673
) -> ty::PolyTraitRef<'tcx> {
2674+
let ty::Closure(_, args) = *self_ty.kind() else {
2675+
bug!("expected closure, found {self_ty}");
2676+
};
26742677
let closure_sig = args.as_closure().sig();
26752678

2676-
debug!(?closure_sig);
2677-
2678-
// NOTE: The self-type is an unboxed closure type and hence is
2679-
// in fact unparameterized (or at least does not reference any
2680-
// regions bound in the obligation).
2681-
let self_ty = obligation
2682-
.predicate
2683-
.self_ty()
2684-
.no_bound_vars()
2685-
.expect("unboxed closure type should not capture bound vars from the predicate");
2686-
26872679
closure_trait_ref_and_return_type(
26882680
self.tcx(),
2689-
obligation.predicate.def_id(),
2681+
fn_trait_def_id,
26902682
self_ty,
26912683
closure_sig,
26922684
util::TupleArgumentsFlag::No,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//@ edition:2024
2+
//@ compile-flags: -Zunstable-options
3+
//@ revisions: current next
4+
//@[next] compile-flags: -Znext-solver
5+
//@ check-pass
6+
7+
#![feature(unboxed_closures, gen_blocks)]
8+
9+
trait Dispatch {
10+
fn dispatch(self);
11+
}
12+
13+
struct Fut<T>(T);
14+
impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Fut<T>
15+
where
16+
for<'a> <T as FnOnce<(&'a (),)>>::Output: Future,
17+
{
18+
fn dispatch(self) {
19+
(self.0)(&());
20+
}
21+
}
22+
23+
struct Gen<T>(T);
24+
impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Gen<T>
25+
where
26+
for<'a> <T as FnOnce<(&'a (),)>>::Output: Iterator,
27+
{
28+
fn dispatch(self) {
29+
(self.0)(&());
30+
}
31+
}
32+
33+
struct Closure<T>(T);
34+
impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Closure<T>
35+
where
36+
for<'a> <T as FnOnce<(&'a (),)>>::Output: Fn<(&'a (),)>,
37+
{
38+
fn dispatch(self) {
39+
(self.0)(&())(&());
40+
}
41+
}
42+
43+
fn main() {
44+
async fn foo(_: &()) {}
45+
Fut(foo).dispatch();
46+
47+
gen fn bar(_: &()) {}
48+
Gen(bar).dispatch();
49+
50+
fn uwu<'a>(x: &'a ()) -> impl Fn(&'a ()) { |_| {} }
51+
Closure(uwu).dispatch();
52+
}

tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr

-19
This file was deleted.

0 commit comments

Comments
 (0)