State from_resident's borrow-window contract at its call sites

This commit is contained in:
Dennis Kobert
2026-09-07 16:11:32 +00:00
parent f07f708ee8
commit 313c591f70
2 changed files with 17 additions and 6 deletions

View File

@@ -502,7 +502,9 @@ impl NodeRuntime {
// Graphic run: thumbnail (text-aware bounds, since the `BoundingBox` trait can't lay out `Graphic::Text` content)
if type_id == std::any::TypeId::of::<Graphic>() {
if update_thumbnails {
// SAFETY: the batch is resident for the read.
// SAFETY: `introspect_with`'s closure is higher-ranked over the batch's
// lifetime, so the item cannot escape this read window, which the
// frames outlive.
let item = unsafe { GroupItem::from_resident(batch) };
let bounds = graphene_std::renderer::graphic_list_bounding_box(&RunView::<Graphic>::new(&item)?, DAffine2::IDENTITY);
let group = Graphic::Group(Group { row: None, content: item });
@@ -514,7 +516,9 @@ impl NodeRuntime {
// clips content to those rectangles so anything outside isn't visible
else if type_id == std::any::TypeId::of::<Artboard>() {
if update_thumbnails {
// SAFETY: the batch is resident for the read.
// SAFETY: `introspect_with`'s closure is higher-ranked over the batch's
// lifetime, so the item cannot escape this read window, which the
// frames outlive.
let item = unsafe { GroupItem::from_resident(batch) };
let run = RunView::<Artboard>::new(&item)?;
let bounds = artboard_clip_bounds(&run);
@@ -524,7 +528,9 @@ impl NodeRuntime {
}
// Vector run: vector modifications
else if type_id == std::any::TypeId::of::<Vector>() {
// SAFETY: the batch is resident for the read.
// SAFETY: `introspect_with`'s closure is higher-ranked over the batch's
// lifetime, so the item cannot escape this read window, which the
// frames outlive.
let item = unsafe { GroupItem::from_resident(batch) };
let run = RunView::<Vector>::new(&item)?;
use graphene_std::core_types::lane::LaneSource;
@@ -534,7 +540,9 @@ impl NodeRuntime {
// String run: thumbnail (bounds need text layout, which the `BoundingBox` trait can't do for a bare `String`)
else if type_id == std::any::TypeId::of::<String>() {
if update_thumbnails {
// SAFETY: the batch is resident for the read.
// SAFETY: `introspect_with`'s closure is higher-ranked over the batch's
// lifetime, so the item cannot escape this read window, which the
// frames outlive.
let item = unsafe { GroupItem::from_resident(batch) };
let run = RunView::<String>::new(&item)?;
let bounds = graphene_std::renderer::text_list_bounding_box(&run, DAffine2::IDENTITY);

View File

@@ -248,8 +248,11 @@ impl<'e> GroupItem<'e> {
/// A `GroupItem` over the batch's frames, without copying.
///
/// # Safety
/// The frames must stay valid for the evaluation. Arena-resident batches
/// qualify, caller stack scratch does not.
/// The item borrows the batch's frames at `'e`, so the frames must stay
/// valid for the whole of `'e` and nothing derived from the item may outlive
/// it. Arena-resident batches span the evaluation; a caller's serve scratch
/// spans only the window it lends the batch for, which is sound exactly when
/// `'e` is bounded to that window.
pub unsafe fn from_resident(batch: crate::node::RecordBatch<'e>) -> Self {
let layout = batch.layout().clone();
assert_element_glue(&layout);