Skip to content

Commit e2cf2cb

Browse files
committed
Auto merge of #122267 - compiler-errors:closure-like-goals, r=lcnr
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 6bbd8c5 + 09ea3f9 commit e2cf2cb

File tree

12 files changed

+158
-141
lines changed

12 files changed

+158
-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;
@@ -678,17 +678,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
678678
fn_host_effect: ty::Const<'tcx>,
679679
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
680680
debug!(?obligation, "confirm_fn_pointer_candidate");
681+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
682+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
681683

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

703-
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
696+
let mut nested =
697+
self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?;
704698
let cause = obligation.derived_cause(BuiltinDerivedObligation);
705699

706700
// Confirm the `type Output: Sized;` bound that is present on `FnOnce`
@@ -748,10 +742,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
748742
&mut self,
749743
obligation: &PolyTraitObligation<'tcx>,
750744
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
751-
// Okay to skip binder because the args on coroutine types never
752-
// touch bound regions, they just capture the in-scope
753-
// type/region parameters.
754-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
745+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
746+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
755747
let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
756748
bug!("closure candidate for non-closure {:?}", obligation);
757749
};
@@ -760,23 +752,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
760752

761753
let coroutine_sig = args.as_coroutine().sig();
762754

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

779-
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
762+
let nested = self.equate_trait_refs(
763+
obligation.with(self.tcx(), placeholder_predicate),
764+
ty::Binder::dummy(trait_ref),
765+
)?;
780766
debug!(?trait_ref, ?nested, "coroutine candidate obligations");
781767

782768
Ok(nested)
@@ -786,10 +772,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
786772
&mut self,
787773
obligation: &PolyTraitObligation<'tcx>,
788774
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
789-
// Okay to skip binder because the args on coroutine types never
790-
// touch bound regions, they just capture the in-scope
791-
// type/region parameters.
792-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
775+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
776+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
793777
let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
794778
bug!("closure candidate for non-closure {:?}", obligation);
795779
};
@@ -801,11 +785,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
801785
let (trait_ref, _) = super::util::future_trait_ref_and_outputs(
802786
self.tcx(),
803787
obligation.predicate.def_id(),
804-
obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(),
788+
self_ty,
805789
coroutine_sig,
806790
);
807791

808-
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
792+
let nested = self.equate_trait_refs(
793+
obligation.with(self.tcx(), placeholder_predicate),
794+
ty::Binder::dummy(trait_ref),
795+
)?;
809796
debug!(?trait_ref, ?nested, "future candidate obligations");
810797

811798
Ok(nested)
@@ -815,10 +802,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
815802
&mut self,
816803
obligation: &PolyTraitObligation<'tcx>,
817804
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
818-
// Okay to skip binder because the args on coroutine types never
819-
// touch bound regions, they just capture the in-scope
820-
// type/region parameters.
821-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
805+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
806+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
822807
let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
823808
bug!("closure candidate for non-closure {:?}", obligation);
824809
};
@@ -830,11 +815,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
830815
let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs(
831816
self.tcx(),
832817
obligation.predicate.def_id(),
833-
obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
818+
self_ty,
834819
gen_sig,
835820
);
836821

837-
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
822+
let nested = self.equate_trait_refs(
823+
obligation.with(self.tcx(), placeholder_predicate),
824+
ty::Binder::dummy(trait_ref),
825+
)?;
838826
debug!(?trait_ref, ?nested, "iterator candidate obligations");
839827

840828
Ok(nested)
@@ -844,10 +832,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
844832
&mut self,
845833
obligation: &PolyTraitObligation<'tcx>,
846834
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
847-
// Okay to skip binder because the args on coroutine types never
848-
// touch bound regions, they just capture the in-scope
849-
// type/region parameters.
850-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
835+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
836+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
851837
let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
852838
bug!("closure candidate for non-closure {:?}", obligation);
853839
};
@@ -859,11 +845,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
859845
let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs(
860846
self.tcx(),
861847
obligation.predicate.def_id(),
862-
obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
848+
self_ty,
863849
gen_sig,
864850
);
865851

866-
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
852+
let nested = self.equate_trait_refs(
853+
obligation.with(self.tcx(), placeholder_predicate),
854+
ty::Binder::dummy(trait_ref),
855+
)?;
867856
debug!(?trait_ref, ?nested, "iterator candidate obligations");
868857

869858
Ok(nested)
@@ -874,14 +863,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
874863
&mut self,
875864
obligation: &PolyTraitObligation<'tcx>,
876865
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
877-
// Okay to skip binder because the args on closure types never
878-
// touch bound regions, they just capture the in-scope
879-
// type/region parameters.
880-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
866+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
867+
let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
868+
881869
let trait_ref = match *self_ty.kind() {
882-
ty::Closure(_, args) => {
883-
self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_)
884-
}
870+
ty::Closure(..) => self.closure_trait_ref_unnormalized(
871+
self_ty,
872+
obligation.predicate.def_id(),
873+
self.tcx().consts.true_,
874+
),
885875
ty::CoroutineClosure(_, args) => {
886876
args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
887877
ty::TraitRef::new(
@@ -896,16 +886,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
896886
}
897887
};
898888

899-
self.confirm_poly_trait_refs(obligation, trait_ref)
889+
self.equate_trait_refs(obligation.with(self.tcx(), placeholder_predicate), trait_ref)
900890
}
901891

902892
#[instrument(skip(self), level = "debug")]
903893
fn confirm_async_closure_candidate(
904894
&mut self,
905895
obligation: &PolyTraitObligation<'tcx>,
906896
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
897+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
898+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
899+
907900
let tcx = self.tcx();
908-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
909901

910902
let mut nested = vec![];
911903
let (trait_ref, kind_ty) = match *self_ty.kind() {
@@ -972,7 +964,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
972964
_ => bug!("expected callable type for AsyncFn candidate"),
973965
};
974966

975-
nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?);
967+
nested.extend(
968+
self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?,
969+
);
976970

977971
let goal_kind =
978972
self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
@@ -1025,42 +1019,40 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10251019
/// selection of the impl. Therefore, if there is a mismatch, we
10261020
/// report an error to the user.
10271021
#[instrument(skip(self), level = "trace")]
1028-
fn confirm_poly_trait_refs(
1022+
fn equate_trait_refs(
10291023
&mut self,
1030-
obligation: &PolyTraitObligation<'tcx>,
1031-
self_ty_trait_ref: ty::PolyTraitRef<'tcx>,
1024+
obligation: TraitObligation<'tcx>,
1025+
found_trait_ref: ty::PolyTraitRef<'tcx>,
10321026
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
1033-
let obligation_trait_ref =
1034-
self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref());
1035-
let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
1027+
let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
10361028
obligation.cause.span,
10371029
HigherRankedType,
1038-
self_ty_trait_ref,
1030+
found_trait_ref,
10391031
);
10401032
// Normalize the obligation and expected trait refs together, because why not
1041-
let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } =
1033+
let Normalized { obligations: nested, value: (obligation_trait_ref, found_trait_ref) } =
10421034
ensure_sufficient_stack(|| {
10431035
normalize_with_depth(
10441036
self,
10451037
obligation.param_env,
10461038
obligation.cause.clone(),
10471039
obligation.recursion_depth + 1,
1048-
(obligation_trait_ref, self_ty_trait_ref),
1040+
(obligation.predicate.trait_ref, found_trait_ref),
10491041
)
10501042
});
10511043

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

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

+6-14
Original file line numberDiff line numberDiff line change
@@ -2679,26 +2679,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
26792679
#[instrument(skip(self), level = "debug")]
26802680
fn closure_trait_ref_unnormalized(
26812681
&mut self,
2682-
obligation: &PolyTraitObligation<'tcx>,
2683-
args: GenericArgsRef<'tcx>,
2682+
self_ty: Ty<'tcx>,
2683+
fn_trait_def_id: DefId,
26842684
fn_host_effect: ty::Const<'tcx>,
26852685
) -> ty::PolyTraitRef<'tcx> {
2686+
let ty::Closure(_, args) = *self_ty.kind() else {
2687+
bug!("expected closure, found {self_ty}");
2688+
};
26862689
let closure_sig = args.as_closure().sig();
26872690

2688-
debug!(?closure_sig);
2689-
2690-
// NOTE: The self-type is an unboxed closure type and hence is
2691-
// in fact unparameterized (or at least does not reference any
2692-
// regions bound in the obligation).
2693-
let self_ty = obligation
2694-
.predicate
2695-
.self_ty()
2696-
.no_bound_vars()
2697-
.expect("unboxed closure type should not capture bound vars from the predicate");
2698-
26992691
closure_trait_ref_and_return_type(
27002692
self.tcx(),
2701-
obligation.predicate.def_id(),
2693+
fn_trait_def_id,
27022694
self_ty,
27032695
closure_sig,
27042696
util::TupleArgumentsFlag::No,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//@ edition:2024
2+
//@ compile-flags: -Zunstable-options
3+
//@ revisions: current next
4+
//@[next] compile-flags: -Znext-solver
5+
//@ check-pass
6+
7+
// Makes sure that we support closure/coroutine goals where the signature of
8+
// the item references higher-ranked lifetimes from the *predicate* binder,
9+
// not its own internal signature binder.
10+
//
11+
// This was fixed in <https://github.com/rust-lang/rust/pull/122267>.
12+
13+
#![feature(unboxed_closures, gen_blocks)]
14+
15+
trait Dispatch {
16+
fn dispatch(self);
17+
}
18+
19+
struct Fut<T>(T);
20+
impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Fut<T>
21+
where
22+
for<'a> <T as FnOnce<(&'a (),)>>::Output: Future,
23+
{
24+
fn dispatch(self) {
25+
(self.0)(&());
26+
}
27+
}
28+
29+
struct Gen<T>(T);
30+
impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Gen<T>
31+
where
32+
for<'a> <T as FnOnce<(&'a (),)>>::Output: Iterator,
33+
{
34+
fn dispatch(self) {
35+
(self.0)(&());
36+
}
37+
}
38+
39+
struct Closure<T>(T);
40+
impl<T: for<'a> Fn<(&'a (),)>> Dispatch for Closure<T>
41+
where
42+
for<'a> <T as FnOnce<(&'a (),)>>::Output: Fn<(&'a (),)>,
43+
{
44+
fn dispatch(self) {
45+
(self.0)(&())(&());
46+
}
47+
}
48+
49+
fn main() {
50+
async fn foo(_: &()) {}
51+
Fut(foo).dispatch();
52+
53+
gen fn bar(_: &()) {}
54+
Gen(bar).dispatch();
55+
56+
fn uwu<'a>(x: &'a ()) -> impl Fn(&'a ()) { |_| {} }
57+
Closure(uwu).dispatch();
58+
}

0 commit comments

Comments
 (0)