Do not assume const params are printed after type params

This commit is contained in:
Michael Goulet 2025-01-19 23:37:12 +00:00
parent 39dc268459
commit 97e07da611
4 changed files with 130 additions and 147 deletions

View file

@ -717,53 +717,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
value: &mut DiagStyledString, value: &mut DiagStyledString,
other_value: &mut DiagStyledString, other_value: &mut DiagStyledString,
name: String, name: String,
sub: ty::GenericArgsRef<'tcx>, args: &[ty::GenericArg<'tcx>],
pos: usize, pos: usize,
other_ty: Ty<'tcx>, other_ty: Ty<'tcx>,
) { ) {
// `value` and `other_value` hold two incomplete type representation for display. // `value` and `other_value` hold two incomplete type representation for display.
// `name` is the path of both types being compared. `sub` // `name` is the path of both types being compared. `sub`
value.push_highlighted(name); value.push_highlighted(name);
let len = sub.len();
if len > 0 { if args.is_empty() {
value.push_highlighted("<"); return;
} }
value.push_highlighted("<");
// Output the lifetimes for the first type for (i, arg) in args.iter().enumerate() {
let lifetimes = sub if i > 0 {
.regions()
.map(|lifetime| {
let s = lifetime.to_string();
if s.is_empty() { "'_".to_string() } else { s }
})
.collect::<Vec<_>>()
.join(", ");
if !lifetimes.is_empty() {
if sub.regions().count() < len {
value.push_normal(lifetimes + ", ");
} else {
value.push_normal(lifetimes);
}
}
// Highlight all the type arguments that aren't at `pos` and compare the type argument at
// `pos` and `other_ty`.
for (i, type_arg) in sub.types().enumerate() {
if i == pos {
let values = self.cmp(type_arg, other_ty);
value.0.extend((values.0).0);
other_value.0.extend((values.1).0);
} else {
value.push_highlighted(type_arg.to_string());
}
if len > 0 && i != len - 1 {
value.push_normal(", "); value.push_normal(", ");
} }
match arg.unpack() {
ty::GenericArgKind::Lifetime(lt) => {
let s = lt.to_string();
value.push_normal(if s.is_empty() { "'_" } else { &s });
}
ty::GenericArgKind::Const(ct) => {
value.push_normal(ct.to_string());
}
// Highlight all the type arguments that aren't at `pos` and compare
// the type argument at `pos` and `other_ty`.
ty::GenericArgKind::Type(type_arg) => {
if i == pos {
let values = self.cmp(type_arg, other_ty);
value.0.extend((values.0).0);
other_value.0.extend((values.1).0);
} else {
value.push_highlighted(type_arg.to_string());
}
}
}
} }
if len > 0 {
value.push_highlighted(">"); value.push_highlighted(">");
}
} }
/// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`, /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`,
@ -791,27 +785,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
t1_out: &mut DiagStyledString, t1_out: &mut DiagStyledString,
t2_out: &mut DiagStyledString, t2_out: &mut DiagStyledString,
path: String, path: String,
sub: &'tcx [ty::GenericArg<'tcx>], args: &'tcx [ty::GenericArg<'tcx>],
other_path: String, other_path: String,
other_ty: Ty<'tcx>, other_ty: Ty<'tcx>,
) -> Option<()> { ) -> bool {
// FIXME/HACK: Go back to `GenericArgsRef` to use its inherent methods, for (i, arg) in args.iter().enumerate() {
// ideally that shouldn't be necessary. if let Some(ta) = arg.as_type() {
let sub = self.tcx.mk_args(sub); if ta == other_ty {
for (i, ta) in sub.types().enumerate() { self.highlight_outer(t1_out, t2_out, path, args, i, other_ty);
if ta == other_ty { return true;
self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); }
return Some(()); if let ty::Adt(def, _) = ta.kind() {
} let path_ = self.tcx.def_path_str(def.did());
if let ty::Adt(def, _) = ta.kind() { if path_ == other_path {
let path_ = self.tcx.def_path_str(def.did()); self.highlight_outer(t1_out, t2_out, path, args, i, other_ty);
if path_ == other_path { return true;
self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); }
return Some(());
} }
} }
} }
None false
} }
/// Adds a `,` to the type representation only if it is appropriate. /// Adds a `,` to the type representation only if it is appropriate.
@ -819,10 +812,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
&self, &self,
value: &mut DiagStyledString, value: &mut DiagStyledString,
other_value: &mut DiagStyledString, other_value: &mut DiagStyledString,
len: usize,
pos: usize, pos: usize,
) { ) {
if len > 0 && pos != len - 1 { if pos > 0 {
value.push_normal(", "); value.push_normal(", ");
other_value.push_normal(", "); other_value.push_normal(", ");
} }
@ -899,10 +891,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let len2 = sig2.inputs().len(); let len2 = sig2.inputs().len();
if len1 == len2 { if len1 == len2 {
for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() {
self.push_comma(&mut values.0, &mut values.1, i);
let (x1, x2) = self.cmp(*l, *r); let (x1, x2) = self.cmp(*l, *r);
(values.0).0.extend(x1.0); (values.0).0.extend(x1.0);
(values.1).0.extend(x2.0); (values.1).0.extend(x2.0);
self.push_comma(&mut values.0, &mut values.1, len1, i);
} }
} else { } else {
for (i, l) in sig1.inputs().iter().enumerate() { for (i, l) in sig1.inputs().iter().enumerate() {
@ -1150,14 +1142,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let len1 = sub_no_defaults_1.len(); let len1 = sub_no_defaults_1.len();
let len2 = sub_no_defaults_2.len(); let len2 = sub_no_defaults_2.len();
let common_len = cmp::min(len1, len2); let common_len = cmp::min(len1, len2);
let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); let remainder1 = &sub1[common_len..];
let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); let remainder2 = &sub2[common_len..];
let common_default_params = let common_default_params =
iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) iter::zip(remainder1.iter().rev(), remainder2.iter().rev())
.filter(|(a, b)| a == b) .filter(|(a, b)| a == b)
.count(); .count();
let len = sub1.len() - common_default_params; let len = sub1.len() - common_default_params;
let consts_offset = len - sub1.consts().count();
// Only draw `<...>` if there are lifetime/type arguments. // Only draw `<...>` if there are lifetime/type arguments.
if len > 0 { if len > 0 {
@ -1169,70 +1160,68 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let s = lifetime.to_string(); let s = lifetime.to_string();
if s.is_empty() { "'_".to_string() } else { s } if s.is_empty() { "'_".to_string() } else { s }
} }
// At one point we'd like to elide all lifetimes here, they are irrelevant for
// all diagnostics that use this output
//
// Foo<'x, '_, Bar>
// Foo<'y, '_, Qux>
// ^^ ^^ --- type arguments are not elided
// | |
// | elided as they were the same
// not elided, they were different, but irrelevant
//
// For bound lifetimes, keep the names of the lifetimes,
// even if they are the same so that it's clear what's happening
// if we have something like
//
// for<'r, 's> fn(Inv<'r>, Inv<'s>)
// for<'r> fn(Inv<'r>, Inv<'r>)
let lifetimes = sub1.regions().zip(sub2.regions());
for (i, lifetimes) in lifetimes.enumerate() {
let l1 = lifetime_display(lifetimes.0);
let l2 = lifetime_display(lifetimes.1);
if lifetimes.0 != lifetimes.1 {
values.0.push_highlighted(l1);
values.1.push_highlighted(l2);
} else if lifetimes.0.is_bound() || self.tcx.sess.opts.verbose {
values.0.push_normal(l1);
values.1.push_normal(l2);
} else {
values.0.push_normal("'_");
values.1.push_normal("'_");
}
self.push_comma(&mut values.0, &mut values.1, len, i);
}
// We're comparing two types with the same path, so we compare the type for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) {
// arguments for both. If they are the same, do not highlight and elide from the self.push_comma(&mut values.0, &mut values.1, i);
// output. match arg1.unpack() {
// Foo<_, Bar> // At one point we'd like to elide all lifetimes here, they are
// Foo<_, Qux> // irrelevant for all diagnostics that use this output.
// ^ elided type as this type argument was the same in both sides //
let type_arguments = sub1.types().zip(sub2.types()); // Foo<'x, '_, Bar>
let regions_len = sub1.regions().count(); // Foo<'y, '_, Qux>
let num_display_types = consts_offset - regions_len; // ^^ ^^ --- type arguments are not elided
for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() { // | |
let i = i + regions_len; // | elided as they were the same
if ta1 == ta2 && !self.tcx.sess.opts.verbose { // not elided, they were different, but irrelevant
values.0.push_normal("_"); //
values.1.push_normal("_"); // For bound lifetimes, keep the names of the lifetimes,
} else { // even if they are the same so that it's clear what's happening
recurse(ta1, ta2, &mut values); // if we have something like
} //
self.push_comma(&mut values.0, &mut values.1, len, i); // for<'r, 's> fn(Inv<'r>, Inv<'s>)
} // for<'r> fn(Inv<'r>, Inv<'r>)
ty::GenericArgKind::Lifetime(l1) => {
let l1_str = lifetime_display(l1);
let l2 = arg2.expect_region();
let l2_str = lifetime_display(l2);
if l1 != l2 {
values.0.push_highlighted(l1_str);
values.1.push_highlighted(l2_str);
} else if l1.is_bound() || self.tcx.sess.opts.verbose {
values.0.push_normal(l1_str);
values.1.push_normal(l2_str);
} else {
values.0.push_normal("'_");
values.1.push_normal("'_");
}
}
ty::GenericArgKind::Type(ta1) => {
let ta2 = arg2.expect_ty();
if ta1 == ta2 && !self.tcx.sess.opts.verbose {
values.0.push_normal("_");
values.1.push_normal("_");
} else {
recurse(ta1, ta2, &mut values);
}
}
// We're comparing two types with the same path, so we compare the type
// arguments for both. If they are the same, do not highlight and elide
// from the output.
// Foo<_, Bar>
// Foo<_, Qux>
// ^ elided type as this type argument was the same in both sides
// Do the same for const arguments, if they are equal, do not highlight and // Do the same for const arguments, if they are equal, do not highlight and
// elide them from the output. // elide them from the output.
let const_arguments = sub1.consts().zip(sub2.consts()); ty::GenericArgKind::Const(ca1) => {
for (i, (ca1, ca2)) in const_arguments.enumerate() { let ca2 = arg2.expect_const();
let i = i + consts_offset; maybe_highlight(ca1, ca2, &mut values, self.tcx);
maybe_highlight(ca1, ca2, &mut values, self.tcx); }
self.push_comma(&mut values.0, &mut values.1, len, i); }
} }
// Close the type argument bracket. // Close the type argument bracket.
// Only draw `<...>` if there are lifetime/type arguments. // Only draw `<...>` if there are arguments.
if len > 0 { if len > 0 {
values.0.push_normal(">"); values.0.push_normal(">");
values.1.push_normal(">"); values.1.push_normal(">");
@ -1244,17 +1233,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// Foo<Bar<Qux> // Foo<Bar<Qux>
// ------- this type argument is exactly the same as the other type // ------- this type argument is exactly the same as the other type
// Bar<Qux> // Bar<Qux>
if self if self.cmp_type_arg(
.cmp_type_arg( &mut values.0,
&mut values.0, &mut values.1,
&mut values.1, path1.clone(),
path1.clone(), sub_no_defaults_1,
sub_no_defaults_1, path2.clone(),
path2.clone(), t2,
t2, ) {
)
.is_some()
{
return values; return values;
} }
// Check for case: // Check for case:
@ -1262,17 +1248,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// Bar<Qux> // Bar<Qux>
// Foo<Bar<Qux>> // Foo<Bar<Qux>>
// ------- this type argument is exactly the same as the other type // ------- this type argument is exactly the same as the other type
if self if self.cmp_type_arg(
.cmp_type_arg( &mut values.1,
&mut values.1, &mut values.0,
&mut values.0, path2,
path2, sub_no_defaults_2,
sub_no_defaults_2, path1,
path1, t1,
t1, ) {
)
.is_some()
{
return values; return values;
} }
@ -1343,8 +1326,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("(")); let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("("));
let len = args1.len(); let len = args1.len();
for (i, (left, right)) in args1.iter().zip(args2).enumerate() { for (i, (left, right)) in args1.iter().zip(args2).enumerate() {
self.push_comma(&mut values.0, &mut values.1, i);
recurse(left, right, &mut values); recurse(left, right, &mut values);
self.push_comma(&mut values.0, &mut values.1, len, i);
} }
if len == 1 { if len == 1 {
// Keep the output for single element tuples as `(ty,)`. // Keep the output for single element tuples as `(ty,)`.

View file

@ -53,7 +53,7 @@ LL | assert_eq!(smart_ptr.a::<&Foo>(), 2);
| ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>`
| |
= note: expected reference `&Foo` = note: expected reference `&Foo`
found struct `SmartPtr<'_, Foo, >` found struct `SmartPtr<'_, Foo>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/arbitrary-self-from-method-substs-with-receiver.rs:62:16 --> $DIR/arbitrary-self-from-method-substs-with-receiver.rs:62:16
@ -62,7 +62,7 @@ LL | assert_eq!(smart_ptr.b::<&Foo>(), 1);
| ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>`
| |
= note: expected reference `&Foo` = note: expected reference `&Foo`
found struct `SmartPtr<'_, Foo, >` found struct `SmartPtr<'_, Foo>`
error: aborting due to 8 previous errors error: aborting due to 8 previous errors

View file

@ -83,7 +83,7 @@ LL | smart_ptr.get::<&Foo>();
| ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>`
| |
= note: expected reference `&Foo` = note: expected reference `&Foo`
found struct `SmartPtr<'_, Foo, >` found struct `SmartPtr<'_, Foo>`
error[E0271]: type mismatch resolving `<Silly as FindReceiver>::Receiver == Foo` error[E0271]: type mismatch resolving `<Silly as FindReceiver>::Receiver == Foo`
--> $DIR/arbitrary-self-from-method-substs.rs:92:9 --> $DIR/arbitrary-self-from-method-substs.rs:92:9

View file

@ -8,8 +8,8 @@ LL | pub fn new(thing: T) -> GenericStruct<1, T> {
LL | Self { thing } LL | Self { thing }
| ^^^^^^^^^^^^^^ expected `1`, found `0` | ^^^^^^^^^^^^^^ expected `1`, found `0`
| |
= note: expected struct `GenericStruct<_, 1>` = note: expected struct `GenericStruct<1, _>`
found struct `GenericStruct<_, 0>` found struct `GenericStruct<0, _>`
help: use the type name directly help: use the type name directly
| |
LL | GenericStruct::<1, T> { thing } LL | GenericStruct::<1, T> { thing }
@ -25,8 +25,8 @@ LL | pub fn new(thing: T) -> GenericStruct2<1, T> {
LL | Self { 0: thing } LL | Self { 0: thing }
| ^^^^^^^^^^^^^^^^^ expected `1`, found `0` | ^^^^^^^^^^^^^^^^^ expected `1`, found `0`
| |
= note: expected struct `GenericStruct2<_, 1>` = note: expected struct `GenericStruct2<1, _>`
found struct `GenericStruct2<_, 0>` found struct `GenericStruct2<0, _>`
help: use the type name directly help: use the type name directly
| |
LL | GenericStruct2::<1, T> { 0: thing } LL | GenericStruct2::<1, T> { 0: thing }