Skip to content

Commit 9e6c4fd

Browse files
committed
Auto merge of rust-lang#123531 - compiler-errors:closure-wf, r=oli-obk
Enforce closure args + return type are WF I found this out when investigating rust-lang#123461 (comment). Turns out we don't register WF obligations for closure args and return types, leading to the ICE. ~~I think this is a useful thing to check for, but I'd like to check what the fallout is.~~ crater is complete. ~~Worst case, I think we should enforce this across an edition boundary (and possibly eventually migrate this for all editions) -- this should be super easy to do, since this is a check in HIR wfcheck, so it can be made edition dependent.~~ I believe the regressions are manageable enough to not necessitate edition-specific behavior. Fixes rust-lang#123461
2 parents 6a9758d + 870ed4b commit 9e6c4fd

16 files changed

+126
-44
lines changed

compiler/rustc_errors/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ struct DiagCtxtInner {
502502
}
503503

504504
/// A key denoting where from a diagnostic was stashed.
505-
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
505+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
506506
pub enum StashKey {
507507
ItemNoType,
508508
UnderscoreForArrayLengths,
@@ -779,7 +779,7 @@ impl DiagCtxt {
779779
// We delay a bug here so that `-Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs`
780780
// can be used to create a backtrace at the stashing site insted of whenever the
781781
// diagnostic context is dropped and thus delayed bugs are emitted.
782-
Error => Some(self.span_delayed_bug(span, "stashing {key:?}")),
782+
Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))),
783783
DelayedBug => return self.inner.borrow_mut().emit_diagnostic(diag),
784784
ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
785785
| Expect(_) => None,

compiler/rustc_hir_typeck/src/check.rs

+19
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem;
1010
use rustc_hir_analysis::check::{check_function_signature, forbid_intrinsic_abi};
1111
use rustc_infer::infer::type_variable::TypeVariableOrigin;
1212
use rustc_infer::infer::RegionVariableOrigin;
13+
use rustc_infer::traits::WellFormedLoc;
1314
use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
1415
use rustc_span::def_id::LocalDefId;
1516
use rustc_span::symbol::sym;
@@ -71,6 +72,18 @@ pub(super) fn check_fn<'a, 'tcx>(
7172
let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
7273
let inputs_fn = fn_sig.inputs().iter().copied();
7374
for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
75+
// We checked the root's signature during wfcheck, but not the child.
76+
if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
77+
fcx.register_wf_obligation(
78+
param_ty.into(),
79+
param.span,
80+
traits::WellFormed(Some(WellFormedLoc::Param {
81+
function: fn_def_id,
82+
param_idx: idx,
83+
})),
84+
);
85+
}
86+
7487
// Check the pattern.
7588
let ty: Option<&hir::Ty<'_>> = inputs_hir.and_then(|h| h.get(idx));
7689
let ty_span = ty.map(|ty| ty.span);
@@ -108,7 +121,13 @@ pub(super) fn check_fn<'a, 'tcx>(
108121
hir::FnRetTy::DefaultReturn(_) => body.value.span,
109122
hir::FnRetTy::Return(ty) => ty.span,
110123
};
124+
111125
fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::SizedReturnType);
126+
// We checked the root's signature during wfcheck, but not the child.
127+
if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
128+
fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::WellFormed(None));
129+
}
130+
112131
fcx.is_whole_body.set(true);
113132
fcx.check_return_expr(body.value, false);
114133

compiler/rustc_middle/src/traits/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ pub enum WellFormedLoc {
487487
/// The index of the parameter to use.
488488
/// Parameters are indexed from 0, with the return type
489489
/// being the last 'parameter'
490-
param_idx: u16,
490+
param_idx: usize,
491491
},
492492
}
493493

tests/crashes/123461.rs

-5
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0277]: the trait bound `usize: Fsm` is not satisfied
2+
--> $DIR/issue-80409.rs:36:31
3+
|
4+
LL | builder.state().on_entry(|_| {});
5+
| ^ the trait `Fsm` is not implemented for `usize`
6+
|
7+
help: this trait has no implementations, consider adding one
8+
--> $DIR/issue-80409.rs:26:1
9+
|
10+
LL | trait Fsm {
11+
| ^^^^^^^^^
12+
note: required by a bound in `StateContext`
13+
--> $DIR/issue-80409.rs:30:31
14+
|
15+
LL | struct StateContext<'a, TFsm: Fsm> {
16+
| ^^^ required by this bound in `StateContext`
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
error: internal compiler error: error performing operation: fully_perform
2-
--> $DIR/issue-80409.rs:49:30
1+
error[E0277]: the trait bound `usize: Fsm` is not satisfied
2+
--> $DIR/issue-80409.rs:36:31
33
|
44
LL | builder.state().on_entry(|_| {});
5-
| ^^^
5+
| ^ the trait `Fsm` is not implemented for `usize`
66
|
7-
note:
8-
--> $DIR/issue-80409.rs:49:30
7+
help: this trait has no implementations, consider adding one
8+
--> $DIR/issue-80409.rs:26:1
99
|
10-
LL | builder.state().on_entry(|_| {});
11-
| ^^^
10+
LL | trait Fsm {
11+
| ^^^^^^^^^
12+
note: required by a bound in `StateContext`
13+
--> $DIR/issue-80409.rs:30:31
14+
|
15+
LL | struct StateContext<'a, TFsm: Fsm> {
16+
| ^^^ required by this bound in `StateContext`
17+
18+
error: aborting due to 1 previous error
1219

13-
query stack during panic:
14-
end of query stack
20+
For more information about this error, try `rustc --explain E0277`.

tests/ui/inference/issue-80409.rs

+1-13
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,5 @@
1-
// This should not pass, because `usize: Fsm` does not hold. However, it currently ICEs.
2-
3-
// ignore-tidy-linelength
4-
51
//@ revisions: compat no-compat
6-
//@[compat] check-pass
72
//@[no-compat] compile-flags: -Zno-implied-bounds-compat
8-
//@[no-compat] check-fail
9-
//@[no-compat] known-bug: #80409
10-
//@[no-compat] failure-status: 101
11-
//@[no-compat] normalize-stderr-test "delayed at.*" -> ""
12-
//@[no-compat] normalize-stderr-test "note: .*\n\n" -> ""
13-
//@[no-compat] normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
14-
//@[no-compat] normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: "
15-
//@[no-compat] rustc-env:RUST_BACKTRACE=0
163

174
#![allow(unreachable_code, unused)]
185

@@ -47,4 +34,5 @@ struct StateContext<'a, TFsm: Fsm> {
4734
fn main() {
4835
let mut builder: FsmBuilder<usize> = todo!();
4936
builder.state().on_entry(|_| {});
37+
//~^ ERROR the trait bound `usize: Fsm` is not satisfied
5038
}

tests/ui/issues/issue-66706.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fn b() {
1212
fn c() {
1313
[0; [|&_: _ &_| {}; 0 ].len()]
1414
//~^ ERROR expected `,`, found `&`
15+
//~| ERROR type annotations needed
1516
}
1617

1718
fn d() {

tests/ui/issues/issue-66706.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ LL | [0; [|&_: _ &_| {}; 0 ].len()]
2121
| help: missing `,`
2222

2323
error: expected identifier, found reserved identifier `_`
24-
--> $DIR/issue-66706.rs:18:26
24+
--> $DIR/issue-66706.rs:19:26
2525
|
2626
LL | [0; match [|f @ &ref _| () ] {} ]
2727
| ----- ^ expected identifier, found reserved identifier
@@ -34,6 +34,12 @@ error[E0282]: type annotations needed
3434
LL | [0; [|_: _ &_| ()].len()]
3535
| ^ cannot infer type
3636

37-
error: aborting due to 5 previous errors
37+
error[E0282]: type annotations needed
38+
--> $DIR/issue-66706.rs:13:11
39+
|
40+
LL | [0; [|&_: _ &_| {}; 0 ].len()]
41+
| ^^^^^ cannot infer type
42+
43+
error: aborting due to 6 previous errors
3844

3945
For more information about this error, try `rustc --explain E0282`.

tests/ui/traits/mutual-recursion-issue-75860.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ pub fn iso<A, B, F1, F2>(a: F1, b: F2) -> (Box<dyn Fn(A) -> B>, Box<dyn Fn(B) ->
66
(Box::new(a), Box::new(b))
77
}
88
pub fn iso_un_option<A, B>() -> (Box<dyn Fn(A) -> B>, Box<dyn Fn(B) -> A>) {
9-
let left = |o_a: Option<_>| o_a.unwrap();
9+
let left = |o_a: Option<_>| o_a.unwrap();
10+
//~^ ERROR overflow
1011
let right = |o_b: Option<_>| o_b.unwrap();
1112
iso(left, right)
12-
//~^ ERROR overflow
1313
}
1414

1515
fn main() {}

tests/ui/traits/mutual-recursion-issue-75860.stderr

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
error[E0275]: overflow evaluating the requirement `Option<_>: Sized`
2-
--> $DIR/mutual-recursion-issue-75860.rs:11:5
1+
error[E0275]: overflow assigning `_` to `Option<_>`
2+
--> $DIR/mutual-recursion-issue-75860.rs:9:33
33
|
4-
LL | iso(left, right)
5-
| ^^^
6-
|
7-
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mutual_recursion_issue_75860`)
8-
note: required by an implicit `Sized` bound in `Option`
9-
--> $SRC_DIR/core/src/option.rs:LL:COL
4+
LL | let left = |o_a: Option<_>| o_a.unwrap();
5+
| ^^^
106

117
error: aborting due to 1 previous error
128

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Minimized test for <https://github.com/rust-lang/rust/issues/123461>.
2+
3+
struct Unconstrained<T>(T);
4+
5+
fn main() {
6+
unsafe { std::mem::transmute::<_, ()>(|o_b: Unconstrained<_>| {}) };
7+
//~^ ERROR type annotations needed
8+
// We unfortunately don't check `Wf(Unconstrained<_>)`, so we won't
9+
// hit an ambiguity error before checking the transmute. That means
10+
// we still may have inference variables in our transmute src.
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0282]: type annotations needed
2+
--> $DIR/ambiguity-in-closure-arg.rs:6:44
3+
|
4+
LL | unsafe { std::mem::transmute::<_, ()>(|o_b: Unconstrained<_>| {}) };
5+
| ^^^^^^^^^^^^^^^^^^^^^ cannot infer type
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0282`.

tests/ui/type/type-check/unknown_type_for_closure.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0282]: type annotations needed
2-
--> $DIR/unknown_type_for_closure.rs:2:13
2+
--> $DIR/unknown_type_for_closure.rs:2:14
33
|
44
LL | let x = |b: Vec<_>| {};
5-
| ^^^^^^^^^^^^^^ cannot infer type for struct `Vec<_>`
5+
| ^^^^^^^^^ cannot infer type
66

77
error[E0282]: type annotations needed
88
--> $DIR/unknown_type_for_closure.rs:6:14

tests/ui/wf/closure-wf.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
trait Bound {}
2+
struct NeedsBound<T: Bound>(T);
3+
4+
// Checks that we enforce that closure args are WF.
5+
6+
fn constrain_inner<T, F: for<'a> FnOnce(&'a (), NeedsBound<T>)>(_: T, _: F) {}
7+
8+
fn main() {
9+
constrain_inner(1u32, |&(), _| ());
10+
//~^ ERROR the trait bound `u32: Bound` is not satisfied
11+
}

tests/ui/wf/closure-wf.stderr

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0277]: the trait bound `u32: Bound` is not satisfied
2+
--> $DIR/closure-wf.rs:9:33
3+
|
4+
LL | constrain_inner(1u32, |&(), _| ());
5+
| ^ the trait `Bound` is not implemented for `u32`
6+
|
7+
help: this trait has no implementations, consider adding one
8+
--> $DIR/closure-wf.rs:1:1
9+
|
10+
LL | trait Bound {}
11+
| ^^^^^^^^^^^
12+
note: required by a bound in `NeedsBound`
13+
--> $DIR/closure-wf.rs:2:22
14+
|
15+
LL | struct NeedsBound<T: Bound>(T);
16+
| ^^^^^ required by this bound in `NeedsBound`
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)