Skip to content

Commit 9e54570

Browse files
authored
Rollup merge of rust-lang#101753 - oli-obk:tait_closure_args, r=compiler-errors
Prefer explict closure sig types over expected ones fixes rust-lang#100800 Previously we only checked that given closure arguments are equal to expected closure arguments, but now we choose the given closure arguments for the signature that is used when type checking the closure body, and keep the other signature for the type of the closure as seen outside of it.
2 parents 1003a36 + 7794ea5 commit 9e54570

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)