1
Fork 0

Rollup merge of #125259 - compiler-errors:fn-mut-as-a-treat, r=oli-obk

An async closure may implement `FnMut`/`Fn` if it has no self-borrows

There's no reason that async closures may not implement `FnMut` or `Fn` if they don't actually borrow anything with the closure's env lifetime. Specifically, #123660 made it so that we don't always need to borrow captures from the closure's env.

See the doc comment on `should_reborrow_from_env_of_parent_coroutine_closure`:

c00957a3e2/compiler/rustc_hir_typeck/src/upvar.rs (L1777-L1823)

If there are no such borrows, then we are free to implement `FnMut` and `Fn` as permitted by our closure's inferred `ClosureKind`.

As far as I can tell, this change makes `async || {}` work in precisely the set of places they used to work before #120361.
Fixes #125247.

r? oli-obk
This commit is contained in:
León Orell Valerian Liehr 2024-05-22 19:04:45 +02:00 committed by GitHub
commit 44c7a2dbff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 70 additions and 20 deletions

View file

@ -401,6 +401,45 @@ impl<'tcx> CoroutineClosureArgs<'tcx> {
pub fn coroutine_witness_ty(self) -> Ty<'tcx> {
self.split().coroutine_witness_ty
}
pub fn has_self_borrows(&self) -> bool {
match self.coroutine_captures_by_ref_ty().kind() {
ty::FnPtr(sig) => sig
.skip_binder()
.visit_with(&mut HasRegionsBoundAt { binder: ty::INNERMOST })
.is_break(),
ty::Error(_) => true,
_ => bug!(),
}
}
}
/// Unlike `has_escaping_bound_vars` or `outermost_exclusive_binder`, this will
/// detect only regions bound *at* the debruijn index.
struct HasRegionsBoundAt {
binder: ty::DebruijnIndex,
}
// FIXME: Could be optimized to not walk into components with no escaping bound vars.
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasRegionsBoundAt {
type Result = ControlFlow<()>;
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
&mut self,
t: &ty::Binder<'tcx, T>,
) -> Self::Result {
self.binder.shift_in(1);
t.super_visit_with(self)?;
self.binder.shift_out(1);
ControlFlow::Continue(())
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
if let ty::ReBound(binder, _) = *r
&& self.binder == binder
{
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]