Skip to content

Fix unuseful span in type error in some format_args!() invocations #140916

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

moatom
Copy link

@moatom moatom commented May 11, 2025

Fixed #140578.

r? @m-ou-se

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label May 11, 2025
@moatom
Copy link
Author

moatom commented May 11, 2025

Report: Current Situation (May 11 JST) Currently, my approach is to sort the candidates so that the error associated with the span of the later argument is selected: https://github.com//pull/140916/files#diff-eccdf22e4bc52ce30713fd36374bb637af1f5d233bf7f60bdf84a21de12dbe9fR166-R168

At first glance, this seems to resolve the issue:

// a1.rs
fn main() {
  println!("{:?}", []);
}

// a2.rs
fn main() {
  println!("{:?} {a} {a:?}", [], a = 1 + 1);
}
$ ./build/x86_64-unknown-linux-gnu/stage1/bin/rustc a1.rs
error[E0282]: type annotations needed
 --> a1.rs:2:20
  |
2 |   println!("{:?}", []);
  |                    ^^ cannot infer type
  |
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0282`.

$ ./build/x86_64-unknown-linux-gnu/stage1/bin/rustc a2.rs
error[E0282]: type annotations needed
 --> a2.rs:2:30
  |
2 |   println!("{:?} {a} {a:?}", [], a = 1 + 1);
  |                              ^^ cannot infer type
  |
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0282`.

However, this approach is ad hoc and doesn't make sense. It seems necessary to limit the sorting targets to spans originating from the same println macro invocation:

$ ./x.py test --stage 1
...
failures:
    ...
    [ui] tests/ui/typeck/issue-7813.rs

test result: FAILED. 17957 passed; 23 failed; 168 ignored; 0 measured; 0 filtered out; finished in 301.39s

$ git --no-pager diff --no-index \
tests/ui/typeck/issue-7813.stderr \
<(./build/x86_64-unknown-linux-gnu/stage1/bin/rustc tests/ui/typeck/issue-7813.rs 2>&1)
diff --git a/tests/ui/typeck/issue-7813.stderr b/dev/fd/63
index 953cbd206dc..00000000000 100644
--- a/tests/ui/typeck/issue-7813.stderr
+++ b/dev/fd/63
@@ -1,13 +1,15 @@
 error[E0282]: type annotations needed for `&[_; 0]`
-  --> $DIR/issue-7813.rs:2:9
-   |
-LL |     let v = &[];
-   |         ^   --- type must be known at this point
-   |
+ --> tests/ui/typeck/issue-7813.rs:2:9
+  |
+2 |     let v = &[]; //~ ERROR type annotations needed
+  |         ^
+3 |     let it = v.iter();
+  |              - type must be known at this point
+  |
 help: consider giving `v` an explicit type, where the placeholders `_` are specified
-   |
-LL |     let v: &[_; 0] = &[];
-   |          +++++++++
+  |
+2 |     let v: &[_; 0] = &[]; //~ ERROR type annotations needed
+  |          +++++++++

 error: aborting due to 1 previous error

Is there any variable or field (e.g., in the obligation) that can help determine whether a span originates from the same println macro invocation when reporting an E0282 error? The following message suggests that such data might already be stored in the context:

= note: this error originates in the macro $crate::format_args_nl which comes from the expansion of the macro println (in Nightly builds, run with -Z macro-backtrace for more info)

If not, where would be the appropriate place to add such information? 🤔

e.obligation.cause.span.macro_backtrace().kind would be helpful.

@rust-log-analyzer

This comment has been minimized.

Copy link
Author

@moatom moatom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-ou-se Could you review it at your convenience?

Comment on lines 284 to 287
#[inline]
pub fn len(self) -> u32 {
(self.len_with_tag_or_marker & !PARENT_TAG) as u32
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: no need in the current logic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be removed since it unused.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, remove this pls

},
) =>
{
(-1, e.obligation.cause.span.index())
Copy link
Author

@moatom moatom May 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Is -1 appropriate? (Should I use unsigned integers for this sorting?)
  2. Is span.index() an appropriate metric to prioritize arguments? (My initial assumption was that the negative of span.index() would help prioritize arguments, but that didn't seem to be the case.)
Copy link
Member

@compiler-errors compiler-errors May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

span.index seems strange... what's it for?

Copy link
Author

@moatom moatom May 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@compiler-errors
I now understand the relevant rustc code better!
Instead of using -span.index, it seems better to sort by -e.obligation.cause.span.data().lo, or by:

let sm = self.tcx.sess.source_map();
let bpos = e.obligation.cause.span.data().lo
let sf_bpos = sm.lookup_byte_offset(bpos);
let line_col_dcol = sf_bpos.sf.lookup_file_pos_with_col_display(sf_bpos.pos);
-line_col_dcol.1

However, this approach also seems to have a problem.

For the following span:

2 |   print!("{:?} {a} {a:?}", [], a = 1 + 1);
  |                            ^^ cannot infer type

the captured values during sorting are bpos=BytePos(52), line_col_dcol.0=2, and line_col_dcol.1=CharPos(27).

On the other hand, for the following span:

2 |   print!("{:?} {a} {a:?}", [], a = 1 + 1);
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type

the captured values during sorting are bpos=BytePos(191660), line_col_dcol.0=374, and line_col_dcol.1=CharPos(4294769381).

Thus, the later span is mistakenly reported as the error by -e.obligation.cause.span.data().lo or -line_col_dcol.1.

So my question here is:

  • Is this BytePos(191660) just completely wrong? Could this be a bug in the lookup_file_pos_with_col_display method?
  • Or is it possible that self.tcx.sess.source_map() is somehow inappropriate, causing lookup_file_pos_with_col_display to return an incorrect result?
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this BytePos(191660) just completely wrong? Could this be a bug in the lookup_file_pos_with_col_display method?

I found out that lookup_file_pos_with_col_display reports the location within the macro-expanded virtual file. In contrast, lookup_char_pos appears to provide the correct location in the original source file. 🤔

Comment on lines 167 to 170
if e.obligation.cause.span.macro_backtrace().next().is_some_and(
|trace| match trace.kind {
ExpnKind::Macro(_, name) => {
name.as_str().rsplit("::").next() == Some("format_args_nl")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should does this search format_args rather than format_args_nl?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not limit it only for format_args_nl, otherwise other macro such as print! will do not got right.

@moatom moatom marked this pull request as ready for review May 12, 2025 10:06
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 12, 2025
@chenyukang
Copy link
Member

@m-ou-se may be busy nowadays.
r? compiler

@rustbot rustbot assigned wesleywiser and unassigned m-ou-se May 16, 2025
@chenyukang
Copy link
Member

I tried to understand why adding a sort strategy could resolve the issue, but haven't figured out it yet.

maybe r? @compiler-errors know what's going on here, since the logic of suppressing was written by him.

@@ -163,14 +163,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
// with more relevant type information and hide redundant E0282 errors.
errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Subtype(_)
if e.obligation.cause.span.macro_backtrace().next().is_some_and(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be simplified with some other method like span.parent or span.expn_kind or something? Look on the span type for relevant functions, b/c using backtrace.next smells wrong 🤔

},
) =>
{
(-1, e.obligation.cause.span.index())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could use an enum with PartialOrd implemented on it to give these ordering precedences a name, or bump everything +1.

@rust-log-analyzer

This comment has been minimized.

@rustbot rustbot added the T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) label May 18, 2025
@moatom
Copy link
Author

moatom commented May 18, 2025

@compiler-errors (CC @chenyukang)
Thank you for taking the time to review my PR, despite my limited understanding of Rust and the rough state of the code.

I've addressed all the points you mentioned! Please take a look when you have a moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
7 participants