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,38 +717,35 @@ 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() {
return;
}
value.push_highlighted("<"); value.push_highlighted("<");
for (i, arg) in args.iter().enumerate() {
if i > 0 {
value.push_normal(", ");
} }
// Output the lifetimes for the first type match arg.unpack() {
let lifetimes = sub ty::GenericArgKind::Lifetime(lt) => {
.regions() let s = lt.to_string();
.map(|lifetime| { value.push_normal(if s.is_empty() { "'_" } else { &s });
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);
} }
ty::GenericArgKind::Const(ct) => {
value.push_normal(ct.to_string());
} }
// Highlight all the type arguments that aren't at `pos` and compare
// Highlight all the type arguments that aren't at `pos` and compare the type argument at // the type argument at `pos` and `other_ty`.
// `pos` and `other_ty`. ty::GenericArgKind::Type(type_arg) => {
for (i, type_arg) in sub.types().enumerate() {
if i == pos { if i == pos {
let values = self.cmp(type_arg, other_ty); let values = self.cmp(type_arg, other_ty);
value.0.extend((values.0).0); value.0.extend((values.0).0);
@ -756,15 +753,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
} else { } else {
value.push_highlighted(type_arg.to_string()); value.push_highlighted(type_arg.to_string());
} }
}
}
}
if len > 0 && i != len - 1 {
value.push_normal(", ");
}
}
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`,
/// as that is the difference to the other type. /// as that is the difference to the other type.
@ -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);
for (i, ta) in sub.types().enumerate() {
if ta == other_ty { if ta == other_ty {
self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); self.highlight_outer(t1_out, t2_out, path, args, i, other_ty);
return Some(()); return true;
} }
if let ty::Adt(def, _) = ta.kind() { if let ty::Adt(def, _) = ta.kind() {
let path_ = self.tcx.def_path_str(def.did()); let path_ = self.tcx.def_path_str(def.did());
if path_ == other_path { if path_ == other_path {
self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); self.highlight_outer(t1_out, t2_out, path, args, i, other_ty);
return Some(()); return true;
} }
} }
} }
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,8 +1160,12 @@ 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 for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) {
self.push_comma(&mut values.0, &mut values.1, i);
match arg1.unpack() {
// 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<'x, '_, Bar>
// Foo<'y, '_, Qux> // Foo<'y, '_, Qux>
@ -1185,54 +1180,48 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// //
// for<'r, 's> fn(Inv<'r>, Inv<'s>) // for<'r, 's> fn(Inv<'r>, Inv<'s>)
// for<'r> fn(Inv<'r>, Inv<'r>) // for<'r> fn(Inv<'r>, Inv<'r>)
let lifetimes = sub1.regions().zip(sub2.regions()); ty::GenericArgKind::Lifetime(l1) => {
for (i, lifetimes) in lifetimes.enumerate() { let l1_str = lifetime_display(l1);
let l1 = lifetime_display(lifetimes.0); let l2 = arg2.expect_region();
let l2 = lifetime_display(lifetimes.1); let l2_str = lifetime_display(l2);
if lifetimes.0 != lifetimes.1 { if l1 != l2 {
values.0.push_highlighted(l1); values.0.push_highlighted(l1_str);
values.1.push_highlighted(l2); values.1.push_highlighted(l2_str);
} else if lifetimes.0.is_bound() || self.tcx.sess.opts.verbose { } else if l1.is_bound() || self.tcx.sess.opts.verbose {
values.0.push_normal(l1); values.0.push_normal(l1_str);
values.1.push_normal(l2); values.1.push_normal(l2_str);
} else { } else {
values.0.push_normal("'_"); values.0.push_normal("'_");
values.1.push_normal("'_"); values.1.push_normal("'_");
} }
self.push_comma(&mut values.0, &mut values.1, len, i);
} }
ty::GenericArgKind::Type(ta1) => {
// We're comparing two types with the same path, so we compare the type let ta2 = arg2.expect_ty();
// 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
let type_arguments = sub1.types().zip(sub2.types());
let regions_len = sub1.regions().count();
let num_display_types = consts_offset - regions_len;
for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() {
let i = i + regions_len;
if ta1 == ta2 && !self.tcx.sess.opts.verbose { if ta1 == ta2 && !self.tcx.sess.opts.verbose {
values.0.push_normal("_"); values.0.push_normal("_");
values.1.push_normal("_"); values.1.push_normal("_");
} else { } else {
recurse(ta1, ta2, &mut values); recurse(ta1, ta2, &mut values);
} }
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
// 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 }