Skip to content

Do not assume const params are printed after type params #135749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 121 additions & 138 deletions compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>()
.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`,
Expand Down Expand Up @@ -791,38 +785,36 @@ 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.
fn push_comma(
&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(", ");
}
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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 {
Expand All @@ -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(">");
Expand All @@ -1244,35 +1233,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// Foo<Bar<Qux>
// ------- this type argument is exactly the same as the other type
// Bar<Qux>
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:
// let x: Bar<Qux> = y:<Foo<Bar<Qux>>>();
// Bar<Qux>
// Foo<Bar<Qux>>
// ------- 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;
}

Expand Down Expand Up @@ -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,)`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<Silly as FindReceiver>::Receiver == Foo`
--> $DIR/arbitrary-self-from-method-substs.rs:92:9
Expand Down
Loading
Loading