Skip to content

Commit 429218f

Browse files
committed
const-eval: full support for pointer fragments
1 parent 8c12d76 commit 429218f

34 files changed

+475
-319
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const_eval_const_context = {$kind ->
5757
}
5858
5959
const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global
60-
.note = use `const_make_global` to make allocated pointers immutable before returning
60+
.note = use `const_make_global` to turn allocated pointers into immutable globals before returning
6161
6262
const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc}
6363
@@ -231,6 +231,9 @@ const_eval_mutable_borrow_escaping =
231231
232232
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
233233
234+
const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind}
235+
.note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
236+
234237
const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
235238
236239
const_eval_non_const_await =
@@ -299,10 +302,8 @@ const_eval_panic = evaluation panicked: {$msg}
299302
300303
const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str`
301304
302-
const_eval_partial_pointer_copy =
303-
unable to copy parts of a pointer from memory at {$ptr}
304-
const_eval_partial_pointer_overwrite =
305-
unable to overwrite parts of a pointer in memory at {$ptr}
305+
const_eval_partial_pointer_read =
306+
unable to read parts of a pointer from memory at {$ptr}
306307
const_eval_pointer_arithmetic_overflow =
307308
overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
308309

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
117117
ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }),
118118
)));
119119
}
120+
Err(InternError::PartialPointer) => {
121+
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
122+
ecx.tcx
123+
.dcx()
124+
.emit_err(errors::PartialPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
125+
)));
126+
}
120127
}
121128

122129
interp_ok(R::make_result(ret, ecx))

compiler/rustc_const_eval/src/errors.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ pub(crate) struct ConstHeapPtrInFinal {
5151
pub span: Span,
5252
}
5353

54+
#[derive(Diagnostic)]
55+
#[diag(const_eval_partial_pointer_in_final)]
56+
#[note]
57+
pub(crate) struct PartialPtrInFinal {
58+
#[primary_span]
59+
pub span: Span,
60+
pub kind: InternKind,
61+
}
62+
5463
#[derive(Diagnostic)]
5564
#[diag(const_eval_unstable_in_stable_exposed)]
5665
pub(crate) struct UnstableInStableExposed {
@@ -832,8 +841,7 @@ impl ReportErrorExt for UnsupportedOpInfo {
832841
UnsupportedOpInfo::Unsupported(s) => s.clone().into(),
833842
UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field,
834843
UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local,
835-
UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite,
836-
UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy,
844+
UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_read,
837845
UnsupportedOpInfo::ReadPointerAsInt(_) => const_eval_read_pointer_as_int,
838846
UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static,
839847
UnsupportedOpInfo::ExternStatic(_) => const_eval_extern_static,
@@ -844,7 +852,7 @@ impl ReportErrorExt for UnsupportedOpInfo {
844852
use UnsupportedOpInfo::*;
845853

846854
use crate::fluent_generated::*;
847-
if let ReadPointerAsInt(_) | OverwritePartialPointer(_) | ReadPartialPointer(_) = self {
855+
if let ReadPointerAsInt(_) | ReadPartialPointer(_) = self {
848856
diag.help(const_eval_ptr_as_bytes_1);
849857
diag.help(const_eval_ptr_as_bytes_2);
850858
}
@@ -856,7 +864,7 @@ impl ReportErrorExt for UnsupportedOpInfo {
856864
| UnsupportedOpInfo::ExternTypeField
857865
| Unsupported(_)
858866
| ReadPointerAsInt(_) => {}
859-
OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => {
867+
ReadPartialPointer(ptr) => {
860868
diag.arg("ptr", ptr);
861869
}
862870
ThreadLocalStatic(did) | ExternStatic(did) => rustc_middle::ty::tls::with(|tcx| {

compiler/rustc_const_eval/src/interpret/intern.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,18 @@ fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>(
7979
// `const_make_global`. We want to error here, but we have to first put the
8080
// allocation back into the `alloc_map` to keep things in a consistent state.
8181
ecx.memory.alloc_map.insert(alloc_id, (kind, alloc));
82+
ecx.tcx.dcx().delayed_bug("non-global heap allocation in const value");
8283
return Err(InternError::ConstAllocNotGlobal);
8384
}
8485
}
8586
MemoryKind::Stack | MemoryKind::CallerLocation => {}
8687
}
88+
if !alloc.provenance_merge_bytes(&ecx.tcx) {
89+
// Per-byte provenance is not supported by backends, so we cannot accept it here.
90+
ecx.memory.alloc_map.insert(alloc_id, (kind, alloc));
91+
ecx.tcx.dcx().delayed_bug("partial pointer in const value");
92+
return Err(InternError::PartialPointer);
93+
}
8794

8895
// Set allocation mutability as appropriate. This is used by LLVM to put things into
8996
// read-only memory, and also by Miri when evaluating other globals that
@@ -166,6 +173,7 @@ pub enum InternError {
166173
BadMutablePointer,
167174
DanglingPointer,
168175
ConstAllocNotGlobal,
176+
PartialPointer,
169177
}
170178

171179
/// Intern `ret` and everything it references.
@@ -225,17 +233,14 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>(
225233
alloc.1.mutability = base_mutability;
226234
alloc.1.provenance().ptrs().iter().map(|&(_, prov)| prov).collect()
227235
} else {
228-
intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator))
229-
.unwrap()
230-
.collect()
236+
intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator))?.collect()
231237
};
232238
// We need to distinguish "has just been interned" from "was already in `tcx`",
233239
// so we track this in a separate set.
234240
let mut just_interned: FxHashSet<_> = std::iter::once(base_alloc_id).collect();
235241
// Whether we encountered a bad mutable pointer.
236242
// We want to first report "dangling" and then "mutable", so we need to delay reporting these
237243
// errors.
238-
let mut result = Ok(());
239244
let mut found_bad_mutable_ptr = false;
240245

241246
// Keep interning as long as there are things to intern.
@@ -310,28 +315,23 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>(
310315
// okay with losing some potential for immutability here. This can anyway only affect
311316
// `static mut`.
312317
just_interned.insert(alloc_id);
313-
match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) {
314-
Ok(nested) => todo.extend(nested),
315-
Err(err) => {
316-
ecx.tcx.dcx().delayed_bug("error during const interning");
317-
result = Err(err);
318-
}
319-
}
318+
let next = intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator))?;
319+
todo.extend(next);
320320
}
321-
if found_bad_mutable_ptr && result.is_ok() {
321+
if found_bad_mutable_ptr {
322322
// We found a mutable pointer inside a const where inner allocations should be immutable,
323323
// and there was no other error. This should usually never happen! However, this can happen
324324
// in unleash-miri mode, so report it as a normal error then.
325325
if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
326-
result = Err(InternError::BadMutablePointer);
326+
return Err(InternError::BadMutablePointer);
327327
} else {
328328
span_bug!(
329329
ecx.tcx.span,
330330
"the static const safety checks accepted a mutable pointer they should not have accepted"
331331
);
332332
}
333333
}
334-
result
334+
Ok(())
335335
}
336336

337337
/// Intern `ret`. This function assumes that `ret` references no other allocation.

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,29 +1309,20 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>
13091309
}
13101310

13111311
/// Mark the given sub-range (relative to this allocation reference) as uninitialized.
1312-
pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> {
1312+
pub fn write_uninit(&mut self, range: AllocRange) {
13131313
let range = self.range.subrange(range);
13141314

1315-
self.alloc
1316-
.write_uninit(&self.tcx, range)
1317-
.map_err(|e| e.to_interp_error(self.alloc_id))
1318-
.into()
1315+
self.alloc.write_uninit(&self.tcx, range);
13191316
}
13201317

13211318
/// Mark the entire referenced range as uninitialized
1322-
pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> {
1323-
self.alloc
1324-
.write_uninit(&self.tcx, self.range)
1325-
.map_err(|e| e.to_interp_error(self.alloc_id))
1326-
.into()
1319+
pub fn write_uninit_full(&mut self) {
1320+
self.alloc.write_uninit(&self.tcx, self.range);
13271321
}
13281322

13291323
/// Remove all provenance in the reference range.
1330-
pub fn clear_provenance(&mut self) -> InterpResult<'tcx> {
1331-
self.alloc
1332-
.clear_provenance(&self.tcx, self.range)
1333-
.map_err(|e| e.to_interp_error(self.alloc_id))
1334-
.into()
1324+
pub fn clear_provenance(&mut self) {
1325+
self.alloc.clear_provenance(&self.tcx, self.range);
13351326
}
13361327
}
13371328

@@ -1422,11 +1413,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
14221413

14231414
// Side-step AllocRef and directly access the underlying bytes more efficiently.
14241415
// (We are staying inside the bounds here and all bytes do get overwritten so all is good.)
1425-
let alloc_id = alloc_ref.alloc_id;
1426-
let bytes = alloc_ref
1427-
.alloc
1428-
.get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range)
1429-
.map_err(move |e| e.to_interp_error(alloc_id))?;
1416+
let bytes =
1417+
alloc_ref.alloc.get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range);
14301418
// `zip` would stop when the first iterator ends; we want to definitely
14311419
// cover all of `bytes`.
14321420
for dest in bytes {
@@ -1508,10 +1496,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
15081496
// `get_bytes_mut` will clear the provenance, which is correct,
15091497
// since we don't want to keep any provenance at the target.
15101498
// This will also error if copying partial provenance is not supported.
1511-
let provenance = src_alloc
1512-
.provenance()
1513-
.prepare_copy(src_range, dest_offset, num_copies, self)
1514-
.map_err(|e| e.to_interp_error(src_alloc_id))?;
1499+
let provenance =
1500+
src_alloc.provenance().prepare_copy(src_range, dest_offset, num_copies, self);
15151501
// Prepare a copy of the initialization mask.
15161502
let init = src_alloc.init_mask().prepare_copy(src_range);
15171503

@@ -1529,10 +1515,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
15291515
dest_range,
15301516
)?;
15311517
// Yes we do overwrite all bytes in `dest_bytes`.
1532-
let dest_bytes = dest_alloc
1533-
.get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range)
1534-
.map_err(|e| e.to_interp_error(dest_alloc_id))?
1535-
.as_mut_ptr();
1518+
let dest_bytes =
1519+
dest_alloc.get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range).as_mut_ptr();
15361520

15371521
if init.no_bytes_init() {
15381522
// Fast path: If all bytes are `uninit` then there is nothing to copy. The target range
@@ -1541,9 +1525,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
15411525
// This also avoids writing to the target bytes so that the backing allocation is never
15421526
// touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary
15431527
// operating system this can avoid physically allocating the page.
1544-
dest_alloc
1545-
.write_uninit(&tcx, dest_range)
1546-
.map_err(|e| e.to_interp_error(dest_alloc_id))?;
1528+
dest_alloc.write_uninit(&tcx, dest_range);
15471529
// `write_uninit` also resets the provenance, so we are done.
15481530
return interp_ok(());
15491531
}

compiler/rustc_const_eval/src/interpret/place.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ where
700700

701701
match value {
702702
Immediate::Scalar(scalar) => {
703-
alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)
703+
alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)?;
704704
}
705705
Immediate::ScalarPair(a_val, b_val) => {
706706
let BackendRepr::ScalarPair(a, b) = layout.backend_repr else {
@@ -720,10 +720,10 @@ where
720720
alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?;
721721
alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?;
722722
// We don't have to reset padding here, `write_immediate` will anyway do a validation run.
723-
interp_ok(())
724723
}
725724
Immediate::Uninit => alloc.write_uninit_full(),
726725
}
726+
interp_ok(())
727727
}
728728

729729
pub fn write_uninit(
@@ -743,7 +743,7 @@ where
743743
// Zero-sized access
744744
return interp_ok(());
745745
};
746-
alloc.write_uninit_full()?;
746+
alloc.write_uninit_full();
747747
}
748748
}
749749
interp_ok(())
@@ -767,7 +767,7 @@ where
767767
// Zero-sized access
768768
return interp_ok(());
769769
};
770-
alloc.clear_provenance()?;
770+
alloc.clear_provenance();
771771
}
772772
}
773773
interp_ok(())

compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
949949
let padding_size = offset - padding_cleared_until;
950950
let range = alloc_range(padding_start, padding_size);
951951
trace!("reset_padding on {}: resetting padding range {range:?}", mplace.layout.ty);
952-
alloc.write_uninit(range)?;
952+
alloc.write_uninit(range);
953953
}
954954
padding_cleared_until = offset + size;
955955
}
@@ -1239,7 +1239,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
12391239
if self.reset_provenance_and_padding {
12401240
// We can't share this with above as above, we might be looking at read-only memory.
12411241
let mut alloc = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)?.expect("we already excluded size 0");
1242-
alloc.clear_provenance()?;
1242+
alloc.clear_provenance();
12431243
// Also, mark this as containing data, not padding.
12441244
self.add_data_range(mplace.ptr(), size);
12451245
}

0 commit comments

Comments
 (0)