Skip to content

Commit 0acd76c

Browse files
committed
fix clippy (and MIR printing) handling of ConstValue::Indirect slices
1 parent 7e0d6f3 commit 0acd76c

File tree

3 files changed

+66
-39
lines changed

3 files changed

+66
-39
lines changed

compiler/rustc_middle/src/mir/interpret/value.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use rustc_apfloat::{
99
use rustc_macros::HashStable;
1010
use rustc_target::abi::{HasDataLayout, Size};
1111

12-
use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
12+
use crate::{
13+
mir::interpret::alloc_range,
14+
ty::{ParamEnv, ScalarInt, Ty, TyCtxt},
15+
};
1316

1417
use super::{
1518
AllocId, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
@@ -114,6 +117,54 @@ impl<'tcx> ConstValue<'tcx> {
114117
pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
115118
ConstValue::Scalar(Scalar::from_target_usize(i, cx))
116119
}
120+
121+
/// Must only be called on constants of type `&str` or `&[u8]`!
122+
pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
123+
let (data, start, end) = match self {
124+
ConstValue::Scalar(_) | ConstValue::ZeroSized => {
125+
bug!("`try_get_slice_bytes` on non-slice constant")
126+
}
127+
&ConstValue::Slice { data, start, end } => (data, start, end),
128+
&ConstValue::Indirect { alloc_id, offset } => {
129+
// The reference itself is stored behind an indirection.
130+
// Load the reference, and then load the actual slice contents.
131+
let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
132+
let ptr_size = tcx.data_layout.pointer_size;
133+
if a.size() < offset + 2 * ptr_size {
134+
// (partially) dangling reference
135+
return None;
136+
}
137+
// Read the wide pointer components.
138+
let ptr = a
139+
.read_scalar(
140+
&tcx,
141+
alloc_range(offset, ptr_size),
142+
/* read_provenance */ true,
143+
)
144+
.ok()?;
145+
let ptr = ptr.to_pointer(&tcx).ok()?;
146+
let len = a
147+
.read_scalar(
148+
&tcx,
149+
alloc_range(offset + ptr_size, ptr_size),
150+
/* read_provenance */ false,
151+
)
152+
.ok()?;
153+
let len = len.to_target_usize(&tcx).ok()?;
154+
let len: usize = len.try_into().ok()?;
155+
if len == 0 {
156+
return Some(&[]);
157+
}
158+
// Non-empty slice, must have memory. We know this is a relative pointer.
159+
let (inner_alloc_id, offset) = ptr.into_parts();
160+
let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
161+
(data, offset.bytes_usize(), offset.bytes_usize() + len)
162+
}
163+
};
164+
165+
// This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
166+
Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
167+
}
117168
}
118169

119170
/// A `Scalar` represents an immediate, primitive value existing outside of a

compiler/rustc_middle/src/mir/mod.rs

+10-25
Original file line numberDiff line numberDiff line change
@@ -2887,31 +2887,16 @@ fn pretty_print_const_value<'tcx>(
28872887
let u8_type = tcx.types.u8;
28882888
match (ct, ty.kind()) {
28892889
// Byte/string slices, printed as (byte) string literals.
2890-
(ConstValue::Slice { data, start, end }, ty::Ref(_, inner, _)) => {
2891-
match inner.kind() {
2892-
ty::Slice(t) => {
2893-
if *t == u8_type {
2894-
// The `inspect` here is okay since we checked the bounds, and `u8` carries
2895-
// no provenance (we have an active slice reference here). We don't use
2896-
// this result to affect interpreter execution.
2897-
let byte_str = data
2898-
.inner()
2899-
.inspect_with_uninit_and_ptr_outside_interpreter(start..end);
2900-
pretty_print_byte_str(fmt, byte_str)?;
2901-
return Ok(());
2902-
}
2903-
}
2904-
ty::Str => {
2905-
// The `inspect` here is okay since we checked the bounds, and `str` carries
2906-
// no provenance (we have an active `str` reference here). We don't use this
2907-
// result to affect interpreter execution.
2908-
let slice = data
2909-
.inner()
2910-
.inspect_with_uninit_and_ptr_outside_interpreter(start..end);
2911-
fmt.write_str(&format!("{:?}", String::from_utf8_lossy(slice)))?;
2912-
return Ok(());
2913-
}
2914-
_ => {}
2890+
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
2891+
if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
2892+
fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
2893+
return Ok(());
2894+
}
2895+
}
2896+
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
2897+
if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
2898+
pretty_print_byte_str(fmt, data)?;
2899+
return Ok(());
29152900
}
29162901
}
29172902
(ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {

src/tools/clippy/clippy_utils/src/consts.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -671,19 +671,10 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t
671671
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
672672
_ => None,
673673
},
674-
mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() {
675-
ty::Ref(_, tam, _) => match tam.kind() {
676-
ty::Str => String::from_utf8(
677-
data.inner()
678-
.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
679-
.to_owned(),
680-
)
681-
.ok()
682-
.map(Constant::Str),
683-
_ => None,
684-
},
685-
_ => None,
686-
},
674+
mir::ConstantKind::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => {
675+
let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
676+
String::from_utf8(data.to_owned()).ok().map(Constant::Str)
677+
}
687678
mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => {
688679
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory();
689680
match result.ty().kind() {

0 commit comments

Comments
 (0)