Rollup merge of #156058 - qaijuang:issue-151393, r=JohnTitor

Print HRTB binders before fn qualifiers

Fixes rust-lang/rust#151393

This PR updates the custom fn-signature diff used in type mismatch diagnostics to print higher-ranked binders before fn qualifiers.

We might want to update reference also: https://github.com/rust-lang/reference/blob/581920f9109f141b88b860b3e1e8359e3896a150/src/items/external-blocks.md?plain=1#L60
This commit is contained in:
Jonathan Brouwer
2026-05-05 14:25:23 +02:00
committed by GitHub
3 changed files with 90 additions and 28 deletions
@@ -779,29 +779,40 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let (lt1, sig1) = get_lifetimes(sig1);
let (lt2, sig2) = get_lifetimes(sig2);
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
let mut values =
(DiagStyledString::normal("".to_string()), DiagStyledString::normal("".to_string()));
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^^^^
let safety = |fn_def, sig: ty::FnSig<'_>| match fn_def {
None => sig.safety().prefix_str(),
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
// ^^^^^^^^^^^^^^^^^^
let fn_item_prefix_and_safety = |fn_def, sig: ty::FnSig<'_>| match fn_def {
None => ("", sig.safety().prefix_str()),
Some((did, _)) => {
if self.tcx.codegen_fn_attrs(did).safe_target_features {
"#[target_features] "
("#[target_features] ", "")
} else {
sig.safety().prefix_str()
("", sig.safety().prefix_str())
}
}
};
let safety1 = safety(fn_def1, sig1);
let safety2 = safety(fn_def2, sig2);
let (prefix1, safety1) = fn_item_prefix_and_safety(fn_def1, sig1);
let (prefix2, safety2) = fn_item_prefix_and_safety(fn_def2, sig2);
values.0.push(prefix1, prefix1 != prefix2);
values.1.push(prefix2, prefix1 != prefix2);
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
// ^^^^^^^^
let lifetime_diff = lt1 != lt2;
values.0.push(lt1, lifetime_diff);
values.1.push(lt2, lifetime_diff);
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
// ^^^^^^
values.0.push(safety1, safety1 != safety2);
values.1.push(safety2, safety1 != safety2);
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^^^^^^^^
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
// ^^^^^^^^^^
if sig1.abi() != ExternAbi::Rust {
values.0.push(format!("extern {} ", sig1.abi()), sig1.abi() != sig2.abi());
}
@@ -809,19 +820,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
values.1.push(format!("extern {} ", sig2.abi()), sig1.abi() != sig2.abi());
}
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^^^^^^
let lifetime_diff = lt1 != lt2;
values.0.push(lt1, lifetime_diff);
values.1.push(lt2, lifetime_diff);
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
// ^^^
values.0.push_normal("fn(");
values.1.push_normal("fn(");
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^^^
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
// ^^^^^
let len1 = sig1.inputs().len();
let len2 = sig2.inputs().len();
if len1 == len2 {
@@ -859,13 +864,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
values.1.push("...", !sig1.c_variadic());
}
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
// ^
values.0.push_normal(")");
values.1.push_normal(")");
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^^^^^^
// #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T
// ^^^^^^^^
let output1 = sig1.output();
let output2 = sig2.output();
let (x1, x2) = self.cmp(output1, output2);
@@ -10,7 +10,19 @@ fn foo(x: i32) -> u32 {
0
}
extern "C" fn extern_foo(_: &i32) {}
unsafe extern "C" fn unsafe_extern_foo(_: &i32) {}
fn rust_foo(_: &i32) {}
fn main() {
let b: fn() -> u32 = bar; //~ ERROR mismatched types [E0308]
let f: fn(i32) = foo; //~ ERROR mismatched types [E0308]
// See https://github.com/rust-lang/rust/issues/151393
let _: for<'a> fn(&'a i32) = extern_foo; //~ ERROR mismatched types [E0308]
let _: for<'a> fn(&'a i32) = unsafe_extern_foo; //~ ERROR mismatched types [E0308]
let _: for<'a> extern "C" fn(&'a i32) = rust_foo; //~ ERROR mismatched types [E0308]
let _: for<'a> unsafe extern "C" fn(&'a i32) = rust_foo; //~ ERROR mismatched types [E0308]
}
@@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/fn-pointer-mismatch-diagnostics.rs:14:26
--> $DIR/fn-pointer-mismatch-diagnostics.rs:20:26
|
LL | let b: fn() -> u32 = bar;
| ----------- ^^^ expected fn pointer, found fn item
@@ -10,7 +10,7 @@ LL | let b: fn() -> u32 = bar;
found fn item `fn() -> () {bar}`
error[E0308]: mismatched types
--> $DIR/fn-pointer-mismatch-diagnostics.rs:15:22
--> $DIR/fn-pointer-mismatch-diagnostics.rs:21:22
|
LL | let f: fn(i32) = foo;
| ------- ^^^ expected fn pointer, found fn item
@@ -20,6 +20,51 @@ LL | let f: fn(i32) = foo;
= note: expected fn pointer `fn(_) -> ()`
found fn item `fn(_) -> u32 {foo}`
error: aborting due to 2 previous errors
error[E0308]: mismatched types
--> $DIR/fn-pointer-mismatch-diagnostics.rs:24:34
|
LL | let _: for<'a> fn(&'a i32) = extern_foo;
| ------------------- ^^^^^^^^^^ expected "Rust" fn, found "C" fn
| |
| expected due to this
|
= note: expected fn pointer `for<'a> fn(&'a _)`
found fn item `for<'a> extern "C" fn(&'a _) {extern_foo}`
error[E0308]: mismatched types
--> $DIR/fn-pointer-mismatch-diagnostics.rs:25:34
|
LL | let _: for<'a> fn(&'a i32) = unsafe_extern_foo;
| ------------------- ^^^^^^^^^^^^^^^^^ expected safe fn, found unsafe fn
| |
| expected due to this
|
= note: expected fn pointer `for<'a> fn(&'a _)`
found fn item `for<'a> unsafe extern "C" fn(&'a _) {unsafe_extern_foo}`
= note: unsafe functions cannot be coerced into safe function pointers
error[E0308]: mismatched types
--> $DIR/fn-pointer-mismatch-diagnostics.rs:26:45
|
LL | let _: for<'a> extern "C" fn(&'a i32) = rust_foo;
| ------------------------------ ^^^^^^^^ expected "C" fn, found "Rust" fn
| |
| expected due to this
|
= note: expected fn pointer `for<'a> extern "C" fn(&'a _)`
found fn item `for<'a> fn(&'a _) {rust_foo}`
error[E0308]: mismatched types
--> $DIR/fn-pointer-mismatch-diagnostics.rs:27:52
|
LL | let _: for<'a> unsafe extern "C" fn(&'a i32) = rust_foo;
| ------------------------------------- ^^^^^^^^ expected "C" fn, found "Rust" fn
| |
| expected due to this
|
= note: expected fn pointer `for<'a> unsafe extern "C" fn(&'a _)`
found fn item `for<'a> fn(&'a _) {rust_foo}`
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0308`.