Skip to content

Fix Ord, Eq and Hash implementation of panic::Location #144510

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
Open
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
40 changes: 39 additions & 1 deletion library/core/src/panic/location.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::cmp::Ordering;
use crate::ffi::CStr;
use crate::fmt;
use crate::hash::{Hash, Hasher};
use crate::marker::PhantomData;
use crate::ptr::NonNull;

Expand Down Expand Up @@ -32,7 +34,7 @@ use crate::ptr::NonNull;
/// Files are compared as strings, not `Path`, which could be unexpected.
/// See [`Location::file`]'s documentation for more discussion.
#[lang = "panic_location"]
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[derive(Copy, Clone)]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub struct Location<'a> {
// A raw pointer is used rather than a reference because the pointer is valid for one more byte
Expand All @@ -44,6 +46,42 @@ pub struct Location<'a> {
_filename: PhantomData<&'a str>,
}

#[stable(feature = "panic_hooks", since = "1.10.0")]
impl PartialEq for Location<'_> {
fn eq(&self, other: &Self) -> bool {
self.file() == other.file() && self.line == other.line && self.col == other.col
Copy link
Member

Choose a reason for hiding this comment

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

Is it worth flipping the order here? Comparing the filename is expensive, but in 99% of cases the line + column will mismatch on different locations I'd guess.

Copy link
Contributor Author

@orlp orlp Jul 27, 2025

Choose a reason for hiding this comment

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

I think that's fair enough for equality. What about the ordering? You could apply the same efficiency argument there although I think people would expect the ordering to be the filename > line > col.

EDIT: after double-checking the documentation, it very clearly states filename > line > col for both ordering and equality. However since that order isn't observable for equality I'll change this for efficiency.

}
}

#[stable(feature = "panic_hooks", since = "1.10.0")]
impl Eq for Location<'_> {}

#[stable(feature = "panic_hooks", since = "1.10.0")]
impl Ord for Location<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.file()
.cmp(other.file())
.then_with(|| self.line.cmp(&other.line))
.then_with(|| self.col.cmp(&other.col))
}
}

#[stable(feature = "panic_hooks", since = "1.10.0")]
impl PartialOrd for Location<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

#[stable(feature = "panic_hooks", since = "1.10.0")]
impl Hash for Location<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.file().hash(state);
self.line.hash(state);
self.col.hash(state);
}
}

Copy link
Member

Choose a reason for hiding this comment

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

Can you add some test coverage for these? That would help prevent future accidental regressions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm actually not 100% sure if I can? In what little tests I've written for the standard library it's been single-file-per-test.

Copy link
Contributor

Choose a reason for hiding this comment

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

You should be able to easily add some tests into library/coretests somewhere, I believe. Although you will need to have some sort of perma-unstable method to construct these that's marked #[doc(hidden)], since these are private fields.

#[stable(feature = "panic_hooks", since = "1.10.0")]
impl fmt::Debug for Location<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down
Loading