Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 73810aa

Browse files
committedJan 3, 2025·
Add Location::file_witH_nul
This is useful for C/C++ APIs which expect the const char* returned from __FILE__ or std::source_location::file_name.
1 parent 4363f9b commit 73810aa

File tree

3 files changed

+76
-23
lines changed

3 files changed

+76
-23
lines changed
 

‎compiler/rustc_const_eval/src/util/caller_location.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,20 @@ fn alloc_caller_location<'tcx>(
1616
line: u32,
1717
col: u32,
1818
) -> MPlaceTy<'tcx> {
19+
// Ensure that the filename itself does not contain nul bytes.
20+
// This isn't possible via POSIX or Windows, but we should ensure no one
21+
// ever does such a thing.
22+
assert!(!filename.as_str().as_bytes().contains(&0));
23+
1924
let loc_details = ecx.tcx.sess.opts.unstable_opts.location_detail;
20-
// This can fail if rustc runs out of memory right here. Trying to emit an error would be
21-
// pointless, since that would require allocating more memory than these short strings.
22-
let file = if loc_details.file {
23-
ecx.allocate_str_dedup(filename.as_str()).unwrap()
24-
} else {
25-
ecx.allocate_str_dedup("<redacted>").unwrap()
25+
let file_wide_ptr = {
26+
let filename = if loc_details.file { filename.as_str() } else { "<redacted>" };
27+
let filename_with_nul = filename.to_owned() + "\0";
28+
// This can fail if rustc runs out of memory right here. Trying to emit an error would be
29+
// pointless, since that would require allocating more memory than these short strings.
30+
let file_ptr = ecx.allocate_bytes_dedup(filename_with_nul.as_bytes()).unwrap();
31+
Immediate::new_slice(file_ptr.into(), filename_with_nul.len().try_into().unwrap(), ecx)
2632
};
27-
let file = file.map_provenance(CtfeProvenance::as_immutable);
2833
let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
2934
let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) };
3035

@@ -37,7 +42,7 @@ fn alloc_caller_location<'tcx>(
3742
let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
3843

3944
// Initialize fields.
40-
ecx.write_immediate(file.to_ref(ecx), &ecx.project_field(&location, 0).unwrap())
45+
ecx.write_immediate(file_wide_ptr, &ecx.project_field(&location, 0).unwrap())
4146
.expect("writing to memory we just allocated cannot fail");
4247
ecx.write_scalar(line, &ecx.project_field(&location, 1).unwrap())
4348
.expect("writing to memory we just allocated cannot fail");

‎library/core/src/panic/location.rs

+38-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[cfg(not(bootstrap))]
2+
use crate::ffi::CStr;
13
use crate::fmt;
24

35
/// A struct containing information about the location of a panic.
@@ -32,7 +34,16 @@ use crate::fmt;
3234
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
3335
#[stable(feature = "panic_hooks", since = "1.10.0")]
3436
pub struct Location<'a> {
37+
#[cfg(bootstrap)]
3538
file: &'a str,
39+
40+
// Note: this filename will have exactly one nul byte at its end, but otherwise
41+
// it must never contain interior nul bytes. This is relied on for the conversion
42+
// to `CStr` below.
43+
//
44+
// The prefix of the string without the trailing nul byte will be a regular UTF8 `str`.
45+
#[cfg(not(bootstrap))]
46+
file_bytes_with_nul: &'a [u8],
3647
line: u32,
3748
col: u32,
3849
}
@@ -125,9 +136,33 @@ impl<'a> Location<'a> {
125136
#[must_use]
126137
#[stable(feature = "panic_hooks", since = "1.10.0")]
127138
#[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")]
128-
#[inline]
129139
pub const fn file(&self) -> &str {
130-
self.file
140+
#[cfg(bootstrap)]
141+
{
142+
self.file
143+
}
144+
145+
#[cfg(not(bootstrap))]
146+
{
147+
let str_len = self.file_bytes_with_nul.len() - 1;
148+
// SAFETY: `file_bytes_with_nul` without the trailing nul byte is guaranteed to be
149+
// valid UTF8.
150+
unsafe { crate::str::from_raw_parts(self.file_bytes_with_nul.as_ptr(), str_len) }
151+
}
152+
}
153+
154+
/// Returns the name of the source file as a nul-terminated `CStr`.
155+
///
156+
/// This is useful for interop with APIs that expect C/C++ `__FILE__` or
157+
/// `std::source_location::file_name`, both of which return a nul-terminated `const char*`.
158+
#[cfg(not(bootstrap))]
159+
#[must_use]
160+
#[unstable(feature = "file_with_nul", issue = "none")]
161+
#[inline]
162+
pub const fn file_with_nul(&self) -> &CStr {
163+
// SAFETY: `file_bytes_with_nul` is guaranteed to have a trailing nul byte and no
164+
// interior nul bytes.
165+
unsafe { CStr::from_bytes_with_nul_unchecked(self.file_bytes_with_nul) }
131166
}
132167

133168
/// Returns the line number from which the panic originated.
@@ -181,22 +216,10 @@ impl<'a> Location<'a> {
181216
}
182217
}
183218

184-
#[unstable(
185-
feature = "panic_internals",
186-
reason = "internal details of the implementation of the `panic!` and related macros",
187-
issue = "none"
188-
)]
189-
impl<'a> Location<'a> {
190-
#[doc(hidden)]
191-
pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self {
192-
Location { file, line, col }
193-
}
194-
}
195-
196219
#[stable(feature = "panic_hook_display", since = "1.26.0")]
197220
impl fmt::Display for Location<'_> {
198221
#[inline]
199222
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
200-
write!(formatter, "{}:{}:{}", self.file, self.line, self.col)
223+
write!(formatter, "{}:{}:{}", self.file(), self.line, self.col)
201224
}
202225
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//@ run-pass
2+
#![feature(file_with_nul)]
3+
4+
#[track_caller]
5+
const fn assert_file_has_trailing_zero() {
6+
let caller = core::panic::Location::caller();
7+
let file_str = caller.file();
8+
let file_with_nul = caller.file_with_nul();
9+
if file_str.len() != file_with_nul.count_bytes() {
10+
panic!("mismatched lengths");
11+
}
12+
let trailing_byte: core::ffi::c_char = unsafe {
13+
*file_with_nul.as_ptr().offset(file_with_nul.count_bytes() as _)
14+
};
15+
if trailing_byte != 0 {
16+
panic!("trailing byte was nonzero")
17+
}
18+
}
19+
20+
#[allow(dead_code)]
21+
const _: () = assert_file_has_trailing_zero();
22+
23+
fn main() {
24+
assert_file_has_trailing_zero();
25+
}

0 commit comments

Comments
 (0)
Please sign in to comment.