Skip to content

Commit 7794ea5

Browse files
committed
Prefer explict closure sig types over expected ones
1 parent a0d1df4 commit 7794ea5

File tree

7 files changed

+87
-30
lines changed

7 files changed

+87
-30
lines changed

compiler/rustc_typeck/src/check/closure.rs

+39-21
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ struct ExpectedSig<'tcx> {
3030
}
3131

3232
struct ClosureSignatures<'tcx> {
33+
/// The signature users of the closure see.
3334
bound_sig: ty::PolyFnSig<'tcx>,
35+
/// The signature within the function body.
36+
/// This mostly differs in the sense that lifetimes are now early bound and any
37+
/// opaque types from the signature expectation are overriden in case there are
38+
/// explicit hidden types written by the user in the closure signature.
3439
liberated_sig: ty::FnSig<'tcx>,
3540
}
3641

@@ -444,18 +449,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
444449
// Along the way, it also writes out entries for types that the user
445450
// wrote into our typeck results, which are then later used by the privacy
446451
// check.
447-
match self.check_supplied_sig_against_expectation(
452+
match self.merge_supplied_sig_with_expectation(
448453
hir_id,
449454
expr_def_id,
450455
decl,
451456
body,
452-
&closure_sigs,
457+
closure_sigs,
453458
) {
454459
Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok),
455-
Err(_) => return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body),
460+
Err(_) => self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body),
456461
}
457-
458-
closure_sigs
459462
}
460463

461464
fn sig_of_closure_with_mismatched_number_of_arguments(
@@ -497,21 +500,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
497500
/// Enforce the user's types against the expectation. See
498501
/// `sig_of_closure_with_expectation` for details on the overall
499502
/// strategy.
500-
fn check_supplied_sig_against_expectation(
503+
#[instrument(level = "debug", skip(self, hir_id, expr_def_id, decl, body, expected_sigs))]
504+
fn merge_supplied_sig_with_expectation(
501505
&self,
502506
hir_id: hir::HirId,
503507
expr_def_id: DefId,
504508
decl: &hir::FnDecl<'_>,
505509
body: &hir::Body<'_>,
506-
expected_sigs: &ClosureSignatures<'tcx>,
507-
) -> InferResult<'tcx, ()> {
510+
mut expected_sigs: ClosureSignatures<'tcx>,
511+
) -> InferResult<'tcx, ClosureSignatures<'tcx>> {
508512
// Get the signature S that the user gave.
509513
//
510514
// (See comment on `sig_of_closure_with_expectation` for the
511515
// meaning of these letters.)
512516
let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body);
513517

514-
debug!("check_supplied_sig_against_expectation: supplied_sig={:?}", supplied_sig);
518+
debug!(?supplied_sig);
515519

516520
// FIXME(#45727): As discussed in [this comment][c1], naively
517521
// forcing equality here actually results in suboptimal error
@@ -529,23 +533,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
529533
// [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796
530534
self.commit_if_ok(|_| {
531535
let mut all_obligations = vec![];
536+
let inputs: Vec<_> = iter::zip(
537+
decl.inputs,
538+
supplied_sig.inputs().skip_binder(), // binder moved to (*) below
539+
)
540+
.map(|(hir_ty, &supplied_ty)| {
541+
// Instantiate (this part of..) S to S', i.e., with fresh variables.
542+
self.replace_bound_vars_with_fresh_vars(
543+
hir_ty.span,
544+
LateBoundRegionConversionTime::FnCall,
545+
// (*) binder moved to here
546+
supplied_sig.inputs().rebind(supplied_ty),
547+
)
548+
})
549+
.collect();
532550

533551
// The liberated version of this signature should be a subtype
534552
// of the liberated form of the expectation.
535553
for ((hir_ty, &supplied_ty), expected_ty) in iter::zip(
536-
iter::zip(
537-
decl.inputs,
538-
supplied_sig.inputs().skip_binder(), // binder moved to (*) below
539-
),
554+
iter::zip(decl.inputs, &inputs),
540555
expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'.
541556
) {
542-
// Instantiate (this part of..) S to S', i.e., with fresh variables.
543-
let supplied_ty = self.replace_bound_vars_with_fresh_vars(
544-
hir_ty.span,
545-
LateBoundRegionConversionTime::FnCall,
546-
supplied_sig.inputs().rebind(supplied_ty),
547-
); // recreated from (*) above
548-
549557
// Check that E' = S'.
550558
let cause = self.misc(hir_ty.span);
551559
let InferOk { value: (), obligations } =
@@ -564,7 +572,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
564572
.eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?;
565573
all_obligations.extend(obligations);
566574

567-
Ok(InferOk { value: (), obligations: all_obligations })
575+
let inputs = inputs.into_iter().map(|ty| self.resolve_vars_if_possible(ty));
576+
577+
expected_sigs.liberated_sig = self.tcx.mk_fn_sig(
578+
inputs,
579+
supplied_output_ty,
580+
expected_sigs.liberated_sig.c_variadic,
581+
hir::Unsafety::Normal,
582+
Abi::RustCall,
583+
);
584+
585+
Ok(InferOk { value: expected_sigs, obligations: all_obligations })
568586
})
569587
}
570588

src/test/mir-opt/retag.main-{closure#0}.SimplifyCfg-elaborate-drops.after.mir

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fn main::{closure#0}(_1: &[closure@main::{closure#0}], _2: &i32) -> &i32 {
1414
StorageLive(_3); // scope 0 at $DIR/retag.rs:+1:13: +1:15
1515
_3 = _2; // scope 0 at $DIR/retag.rs:+1:18: +1:19
1616
Retag(_3); // scope 0 at $DIR/retag.rs:+1:18: +1:19
17-
_0 = _2; // scope 1 at $DIR/retag.rs:+2:9: +2:10
17+
_0 = &(*_2); // scope 1 at $DIR/retag.rs:+2:9: +2:10
1818
Retag(_0); // scope 1 at $DIR/retag.rs:+2:9: +2:10
1919
StorageDead(_3); // scope 0 at $DIR/retag.rs:+3:5: +3:6
2020
return; // scope 0 at $DIR/retag.rs:+3:6: +3:6

src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,26 @@ error[E0308]: mismatched types
2525
LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
2626
| ^ one type is more general than the other
2727
|
28-
= note: expected fn pointer `for<'r> fn(&'r u32)`
29-
found fn pointer `fn(&u32)`
28+
= note: expected fn pointer `fn(&u32)`
29+
found fn pointer `for<'r> fn(&'r u32)`
3030

3131
error[E0308]: mismatched types
3232
--> $DIR/expect-fn-supply-fn.rs:39:50
3333
|
3434
LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
3535
| ^ one type is more general than the other
3636
|
37-
= note: expected fn pointer `fn(&'x u32)`
38-
found fn pointer `for<'r> fn(&'r u32)`
37+
= note: expected fn pointer `for<'r> fn(&'r u32)`
38+
found fn pointer `fn(&u32)`
3939

4040
error[E0308]: mismatched types
4141
--> $DIR/expect-fn-supply-fn.rs:48:50
4242
|
4343
LL | with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| {
4444
| ^ one type is more general than the other
4545
|
46-
= note: expected fn pointer `fn(&u32)`
47-
found fn pointer `for<'r> fn(&'r u32)`
46+
= note: expected fn pointer `for<'r> fn(&'r u32)`
47+
found fn pointer `fn(&u32)`
4848

4949
error: aborting due to 5 previous errors
5050

src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | with_closure(|x: u32, y| {});
66
|
77
help: consider giving this closure parameter an explicit type
88
|
9-
LL | with_closure(|x: u32, y: B| {});
9+
LL | with_closure(|x: u32, y: _| {});
1010
| +++
1111

1212
error: aborting due to previous error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// check-pass
2+
3+
// regression test for https://github.com/rust-lang/rust/issues/100800
4+
5+
#![feature(type_alias_impl_trait)]
6+
7+
trait Anything {}
8+
impl<T> Anything for T {}
9+
type Input = impl Anything;
10+
fn run<F: FnOnce(Input) -> ()>(f: F, i: Input) {
11+
f(i);
12+
}
13+
14+
fn main() {
15+
run(|x: u32| {println!("{x}");}, 0);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// run-pass
2+
3+
#![feature(type_alias_impl_trait)]
4+
5+
trait Foo {
6+
// This was reachable in https://github.com/rust-lang/rust/issues/100800
7+
fn foo(&self) { unreachable!() }
8+
}
9+
impl<T> Foo for T {}
10+
11+
struct B;
12+
impl B {
13+
fn foo(&self) {}
14+
}
15+
16+
type Input = impl Foo;
17+
fn run1<F: FnOnce(Input)>(f: F, i: Input) {f(i)}
18+
fn run2<F: FnOnce(B)>(f: F, i: B) {f(i)}
19+
20+
fn main() {
21+
run1(|x: B| {x.foo()}, B);
22+
run2(|x: B| {x.foo()}, B);
23+
}

src/test/ui/type-alias-impl-trait/issue-60371.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ error[E0277]: the trait bound `(): Bug` is not satisfied
1111
--> $DIR/issue-60371.rs:10:40
1212
|
1313
LL | const FUN: fn() -> Self::Item = || ();
14-
| ^ the trait `Bug` is not implemented for `()`
14+
| ^^ the trait `Bug` is not implemented for `()`
1515
|
1616
= help: the trait `Bug` is implemented for `&()`
1717

0 commit comments

Comments
 (0)