From 97e07da611e7f33b4d41861cd55cdeadc79608c5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 19 Jan 2025 23:37:12 +0000 Subject: [PATCH] Do not assume const params are printed after type params --- .../src/error_reporting/infer/mod.rs | 259 ++++++++---------- ...lf-from-method-substs-with-receiver.stderr | 4 +- ...ary-self-from-method-substs.feature.stderr | 2 +- .../ice-self-mismatch-const-generics.stderr | 8 +- 4 files changed, 128 insertions(+), 145 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 9778299eb191f..4fd0351552d2f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -717,53 +717,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { value: &mut DiagStyledString, other_value: &mut DiagStyledString, name: String, - sub: ty::GenericArgsRef<'tcx>, + args: &[ty::GenericArg<'tcx>], pos: usize, other_ty: Ty<'tcx>, ) { // `value` and `other_value` hold two incomplete type representation for display. // `name` is the path of both types being compared. `sub` value.push_highlighted(name); - let len = sub.len(); - if len > 0 { - value.push_highlighted("<"); - } - // Output the lifetimes for the first type - let lifetimes = sub - .regions() - .map(|lifetime| { - let s = lifetime.to_string(); - if s.is_empty() { "'_".to_string() } else { s } - }) - .collect::>() - .join(", "); - if !lifetimes.is_empty() { - if sub.regions().count() < len { - value.push_normal(lifetimes + ", "); - } else { - value.push_normal(lifetimes); - } + if args.is_empty() { + return; } + value.push_highlighted("<"); - // 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()); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + value.push_normal(", "); } - if len > 0 && i != len - 1 { - 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`, @@ -791,27 +785,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { t1_out: &mut DiagStyledString, t2_out: &mut DiagStyledString, path: String, - sub: &'tcx [ty::GenericArg<'tcx>], + args: &'tcx [ty::GenericArg<'tcx>], other_path: String, other_ty: Ty<'tcx>, - ) -> Option<()> { - // FIXME/HACK: Go back to `GenericArgsRef` to use its inherent methods, - // ideally that shouldn't be necessary. - let sub = self.tcx.mk_args(sub); - for (i, ta) in sub.types().enumerate() { - if ta == other_ty { - 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 path_ == other_path { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); + ) -> bool { + for (i, arg) in args.iter().enumerate() { + if let Some(ta) = arg.as_type() { + if ta == other_ty { + self.highlight_outer(t1_out, t2_out, path, args, i, other_ty); + return true; + } + if let ty::Adt(def, _) = ta.kind() { + let path_ = self.tcx.def_path_str(def.did()); + if path_ == other_path { + self.highlight_outer(t1_out, t2_out, path, args, i, other_ty); + return true; + } } } } - None + false } /// Adds a `,` to the type representation only if it is appropriate. @@ -819,10 +812,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, value: &mut DiagStyledString, other_value: &mut DiagStyledString, - len: usize, pos: usize, ) { - if len > 0 && pos != len - 1 { + if pos > 0 { value.push_normal(", "); other_value.push_normal(", "); } @@ -899,10 +891,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len2 = sig2.inputs().len(); if len1 == len2 { 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); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); - self.push_comma(&mut values.0, &mut values.1, len1, i); } } else { 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 len2 = sub_no_defaults_2.len(); let common_len = cmp::min(len1, len2); - let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); - let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); + let remainder1 = &sub1[common_len..]; + let remainder2 = &sub2[common_len..]; let common_default_params = iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) .filter(|(a, b)| a == b) .count(); let len = sub1.len() - common_default_params; - let consts_offset = len - sub1.consts().count(); // Only draw `<...>` if there are lifetime/type arguments. if len > 0 { @@ -1169,70 +1160,68 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let s = lifetime.to_string(); 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 - // 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 { - values.0.push_normal("_"); - values.1.push_normal("_"); - } else { - recurse(ta1, ta2, &mut values); + 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<'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>) + 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 + // elide them from the output. + ty::GenericArgKind::Const(ca1) => { + let ca2 = arg2.expect_const(); + maybe_highlight(ca1, ca2, &mut values, self.tcx); + } } - self.push_comma(&mut values.0, &mut values.1, len, i); - } - - // Do the same for const arguments, if they are equal, do not highlight and - // elide them from the output. - let const_arguments = sub1.consts().zip(sub2.consts()); - for (i, (ca1, ca2)) in const_arguments.enumerate() { - let i = i + consts_offset; - maybe_highlight(ca1, ca2, &mut values, self.tcx); - self.push_comma(&mut values.0, &mut values.1, len, i); } // Close the type argument bracket. - // Only draw `<...>` if there are lifetime/type arguments. + // Only draw `<...>` if there are arguments. if len > 0 { values.0.push_normal(">"); values.1.push_normal(">"); @@ -1244,17 +1233,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Foo // ------- this type argument is exactly the same as the other type // Bar - if self - .cmp_type_arg( - &mut values.0, - &mut values.1, - path1.clone(), - sub_no_defaults_1, - path2.clone(), - t2, - ) - .is_some() - { + if self.cmp_type_arg( + &mut values.0, + &mut values.1, + path1.clone(), + sub_no_defaults_1, + path2.clone(), + t2, + ) { return values; } // Check for case: @@ -1262,17 +1248,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Bar // Foo> // ------- this type argument is exactly the same as the other type - if self - .cmp_type_arg( - &mut values.1, - &mut values.0, - path2, - sub_no_defaults_2, - path1, - t1, - ) - .is_some() - { + if self.cmp_type_arg( + &mut values.1, + &mut values.0, + path2, + sub_no_defaults_2, + path1, + t1, + ) { return values; } @@ -1343,8 +1326,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("(")); let len = args1.len(); 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); - self.push_comma(&mut values.0, &mut values.1, len, i); } if len == 1 { // Keep the output for single element tuples as `(ty,)`. diff --git a/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr b/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr index 9af2a08f3712d..bafa290a3cfd0 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr @@ -53,7 +53,7 @@ LL | assert_eq!(smart_ptr.a::<&Foo>(), 2); | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error[E0308]: mismatched types --> $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>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error: aborting due to 8 previous errors diff --git a/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr b/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr index 6e864f44aa348..f67918a2577ac 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr @@ -83,7 +83,7 @@ LL | smart_ptr.get::<&Foo>(); | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error[E0271]: type mismatch resolving `::Receiver == Foo` --> $DIR/arbitrary-self-from-method-substs.rs:92:9 diff --git a/tests/ui/typeck/ice-self-mismatch-const-generics.stderr b/tests/ui/typeck/ice-self-mismatch-const-generics.stderr index c502ea4565f68..068cf3ee90366 100644 --- a/tests/ui/typeck/ice-self-mismatch-const-generics.stderr +++ b/tests/ui/typeck/ice-self-mismatch-const-generics.stderr @@ -8,8 +8,8 @@ LL | pub fn new(thing: T) -> GenericStruct<1, T> { LL | Self { thing } | ^^^^^^^^^^^^^^ expected `1`, found `0` | - = note: expected struct `GenericStruct<_, 1>` - found struct `GenericStruct<_, 0>` + = note: expected struct `GenericStruct<1, _>` + found struct `GenericStruct<0, _>` help: use the type name directly | LL | GenericStruct::<1, T> { thing } @@ -25,8 +25,8 @@ LL | pub fn new(thing: T) -> GenericStruct2<1, T> { LL | Self { 0: thing } | ^^^^^^^^^^^^^^^^^ expected `1`, found `0` | - = note: expected struct `GenericStruct2<_, 1>` - found struct `GenericStruct2<_, 0>` + = note: expected struct `GenericStruct2<1, _>` + found struct `GenericStruct2<0, _>` help: use the type name directly | LL | GenericStruct2::<1, T> { 0: thing }