Skip to content

Commit f4aca71

Browse files
authored
Merge pull request #21367 from ChayimFriedman2/upvars
fix: Have an `upvars_mentioned()` query that only computes what upvars a closure captures
2 parents d711705 + d584655 commit f4aca71

4 files changed

Lines changed: 359 additions & 23 deletions

File tree

crates/hir-ty/src/infer/coerce.rs

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ use tracing::{debug, instrument};
5656
use crate::{
5757
Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, TargetFeatures,
5858
autoderef::Autoderef,
59-
db::{HirDatabase, InternedClosureId},
59+
db::{HirDatabase, InternedClosure, InternedClosureId},
6060
infer::{
6161
AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead,
6262
},
@@ -74,6 +74,7 @@ use crate::{
7474
},
7575
obligation_ctxt::ObligationCtxt,
7676
},
77+
upvars::upvars_mentioned,
7778
utils::TargetFeatureIsSafeInTarget,
7879
};
7980

@@ -896,27 +897,15 @@ where
896897
fn coerce_closure_to_fn(
897898
&mut self,
898899
a: Ty<'db>,
899-
_closure_def_id_a: InternedClosureId,
900+
closure_def_id_a: InternedClosureId,
900901
args_a: GenericArgs<'db>,
901902
b: Ty<'db>,
902903
) -> CoerceResult<'db> {
903904
debug_assert!(self.infcx().shallow_resolve(a) == a);
904905
debug_assert!(self.infcx().shallow_resolve(b) == b);
905906

906907
match b.kind() {
907-
// FIXME: We need to have an `upvars_mentioned()` query:
908-
// At this point we haven't done capture analysis, which means
909-
// that the ClosureArgs just contains an inference variable instead
910-
// of tuple of captured types.
911-
//
912-
// All we care here is if any variable is being captured and not the exact paths,
913-
// so we check `upvars_mentioned` for root variables being captured.
914-
TyKind::FnPtr(_, hdr) =>
915-
// if self
916-
// .db
917-
// .upvars_mentioned(closure_def_id_a.expect_local())
918-
// .is_none_or(|u| u.is_empty()) =>
919-
{
908+
TyKind::FnPtr(_, hdr) if !is_capturing_closure(self.db(), closure_def_id_a) => {
920909
// We coerce the closure, which has fn type
921910
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
922911
// to
@@ -1089,14 +1078,12 @@ impl<'db> InferenceContext<'_, 'db> {
10891078
// Special-case that coercion alone cannot handle:
10901079
// Function items or non-capturing closures of differing IDs or GenericArgs.
10911080
let (a_sig, b_sig) = {
1092-
let is_capturing_closure = |_ty: Ty<'db>| {
1093-
// FIXME:
1094-
// if let TyKind::Closure(closure_def_id, _args) = ty.kind() {
1095-
// self.db.upvars_mentioned(closure_def_id.expect_local()).is_some()
1096-
// } else {
1097-
// false
1098-
// }
1099-
false
1081+
let is_capturing_closure = |ty: Ty<'db>| {
1082+
if let TyKind::Closure(closure_def_id, _args) = ty.kind() {
1083+
is_capturing_closure(self.db, closure_def_id.0)
1084+
} else {
1085+
false
1086+
}
11001087
};
11011088
if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) {
11021089
(None, None)
@@ -1728,3 +1715,9 @@ fn coerce<'db>(
17281715
.collect();
17291716
Ok((adjustments, ty))
17301717
}
1718+
1719+
fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool {
1720+
let InternedClosure(owner, expr) = closure.loc(db);
1721+
upvars_mentioned(db, owner)
1722+
.is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty()))
1723+
}

crates/hir-ty/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub mod method_resolution;
5050
pub mod mir;
5151
pub mod primitive;
5252
pub mod traits;
53+
pub mod upvars;
5354

5455
#[cfg(test)]
5556
mod test_db;

crates/hir-ty/src/tests/simple.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use expect_test::expect;
22

3+
use crate::tests::check_infer_with_mismatches;
4+
35
use super::{check, check_infer, check_no_mismatches, check_types};
46

57
#[test]
@@ -3956,3 +3958,24 @@ fn bar() {
39563958
"#,
39573959
);
39583960
}
3961+
3962+
#[test]
3963+
fn cannot_coerce_capturing_closure_to_fn_ptr() {
3964+
check_infer_with_mismatches(
3965+
r#"
3966+
fn foo() {
3967+
let a = 1;
3968+
let _: fn() -> i32 = || a;
3969+
}
3970+
"#,
3971+
expect![[r#"
3972+
9..58 '{ ...| a; }': ()
3973+
19..20 'a': i32
3974+
23..24 '1': i32
3975+
34..35 '_': fn() -> i32
3976+
51..55 '|| a': impl Fn() -> i32
3977+
54..55 'a': i32
3978+
51..55: expected fn() -> i32, got impl Fn() -> i32
3979+
"#]],
3980+
);
3981+
}

0 commit comments

Comments
 (0)