mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 15:38:12 +08:00
Refuse a lift that never filled its layout's fields
This commit is contained in:
@@ -111,6 +111,7 @@ impl<'e> Frames<'e> {
|
|||||||
inline: RecordValue::zeroed(),
|
inline: RecordValue::zeroed(),
|
||||||
frame,
|
frame,
|
||||||
free: self.reborrow(),
|
free: self.reborrow(),
|
||||||
|
filled_fields: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ impl<'a> SlotRun<'a> {
|
|||||||
inline: RecordValue::zeroed(),
|
inline: RecordValue::zeroed(),
|
||||||
frame: (self.layout.frame_bytes() != 0).then_some(frame),
|
frame: (self.layout.frame_bytes() != 0).then_some(frame),
|
||||||
free: frames.reborrow(),
|
free: frames.reborrow(),
|
||||||
|
filled_fields: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +83,9 @@ pub struct FrameClaim<'e, 'l> {
|
|||||||
pub(in crate::record) inline: RecordValue<'static>,
|
pub(in crate::record) inline: RecordValue<'static>,
|
||||||
pub(in crate::record) frame: Option<*mut u8>,
|
pub(in crate::record) frame: Option<*mut u8>,
|
||||||
pub(in crate::record) free: Frames<'e>,
|
pub(in crate::record) free: Frames<'e>,
|
||||||
|
/// Set by the writes that fill the declared fields, so the safe closers can
|
||||||
|
/// refuse a field-bearing frame that was never filled.
|
||||||
|
pub(in crate::record) filled_fields: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'l> FrameClaim<'e, 'l> {
|
impl<'e, 'l> FrameClaim<'e, 'l> {
|
||||||
@@ -118,6 +122,7 @@ impl<'e, 'l> FrameClaim<'e, 'l> {
|
|||||||
/// must be the wiring-resolved plan of this frame's layout.
|
/// must be the wiring-resolved plan of this frame's layout.
|
||||||
pub unsafe fn carry(&mut self, src: Rec<'_>, plan: &[(usize, usize, usize)]) {
|
pub unsafe fn carry(&mut self, src: Rec<'_>, plan: &[(usize, usize, usize)]) {
|
||||||
unsafe { apply_plan(src, self.dst(), plan) };
|
unsafe { apply_plan(src, self.dst(), plan) };
|
||||||
|
self.filled_fields = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a field at its wiring-resolved offset.
|
/// Writes a field at its wiring-resolved offset.
|
||||||
@@ -126,6 +131,7 @@ impl<'e, 'l> FrameClaim<'e, 'l> {
|
|||||||
/// `offset` must be this layout's resolved offset for a field of `T`.
|
/// `offset` must be this layout's resolved offset for a field of `T`.
|
||||||
pub unsafe fn attr_at<T>(&mut self, offset: usize, value: T) {
|
pub unsafe fn attr_at<T>(&mut self, offset: usize, value: T) {
|
||||||
unsafe { write_field(self.dst(), offset, value) };
|
unsafe { write_field(self.dst(), offset, value) };
|
||||||
|
self.filled_fields = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the element; `None` reports arena exhaustion for a parked
|
/// Writes the element; `None` reports arena exhaustion for a parked
|
||||||
@@ -140,9 +146,17 @@ impl<'e, 'l> FrameClaim<'e, 'l> {
|
|||||||
/// Lifts a kernel's poll into the frame and closes it: the element
|
/// Lifts a kernel's poll into the frame and closes it: the element
|
||||||
/// writes on value polls, every poll keeps the frame claimed, and arena
|
/// writes on value polls, every poll keeps the frame claimed, and arena
|
||||||
/// exhaustion of a parked element reports as an error poll. Panics where
|
/// exhaustion of a parked element reports as an error poll. Panics where
|
||||||
/// the element does not match the wired layout.
|
/// the element does not match the wired layout, or where a field-bearing
|
||||||
|
/// layout closes on a frame no carry or field write ever filled, since the
|
||||||
|
/// [`Served`] proof certifies the whole record and the fields would be the
|
||||||
|
/// prior frame's bytes.
|
||||||
pub fn lift<T: Send + Sync + dyn_any::StaticTypeSized>(mut self, poll: GPoll<T>, arena: &'e crate::arena::Arena) -> GPoll<RecordValue<'e>> {
|
pub fn lift<T: Send + Sync + dyn_any::StaticTypeSized>(mut self, poll: GPoll<T>, arena: &'e crate::arena::Arena) -> GPoll<RecordValue<'e>> {
|
||||||
self.check_element::<T>();
|
self.check_element::<T>();
|
||||||
|
assert!(
|
||||||
|
self.layout.fields.is_empty() || self.filled_fields,
|
||||||
|
"a layout with {} fields must carry or write them before lifting",
|
||||||
|
self.layout.fields.len()
|
||||||
|
);
|
||||||
let frame_bytes = self.layout.frame_bytes();
|
let frame_bytes = self.layout.frame_bytes();
|
||||||
let dst = self.dst();
|
let dst = self.dst();
|
||||||
// SAFETY: the frame is this layout's fresh claim, the element check
|
// SAFETY: the frame is this layout's fresh claim, the element check
|
||||||
@@ -158,6 +172,7 @@ impl<'e, 'l> FrameClaim<'e, 'l> {
|
|||||||
/// references outlive the serving evaluation.
|
/// references outlive the serving evaluation.
|
||||||
pub unsafe fn fill_copy(&mut self, src: *const u8) {
|
pub unsafe fn fill_copy(&mut self, src: *const u8) {
|
||||||
unsafe { std::ptr::copy_nonoverlapping(src, self.dst(), self.layout.size) };
|
unsafe { std::ptr::copy_nonoverlapping(src, self.dst(), self.layout.size) };
|
||||||
|
self.filled_fields = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The served record. The frame stays claimed for the consumer; the drop
|
/// The served record. The frame stays claimed for the consumer; the drop
|
||||||
@@ -208,6 +223,7 @@ impl<'e, 'l> FrameClaim<'e, 'l> {
|
|||||||
/// translate into this frame's layout.
|
/// translate into this frame's layout.
|
||||||
pub unsafe fn translate(&mut self, src: Rec<'_>, plan: &SourcePlan) {
|
pub unsafe fn translate(&mut self, src: Rec<'_>, plan: &SourcePlan) {
|
||||||
unsafe { plan.translate(src, self.dst()) };
|
unsafe { plan.translate(src, self.dst()) };
|
||||||
|
self.filled_fields = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,6 +336,17 @@ mod tests {
|
|||||||
frames.claim(&layout).element(1u32, &arena);
|
frames.claim(&layout).element(1u32, &arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_run_refuses_a_lane_count_whose_stride_product_overflows() {
|
||||||
|
let layout = Layout::default().with_writes(0, element_write::<f64>(), &[]);
|
||||||
|
let mut scratch = [std::mem::MaybeUninit::<u64>::uninit(); 4];
|
||||||
|
let mut frame_arena = FrameArena::new();
|
||||||
|
frame_arena.reserve(64);
|
||||||
|
let frames = frame_arena.frames();
|
||||||
|
let wrapping = usize::MAX / layout.lane_stride() + 1;
|
||||||
|
assert!(frames.run(&mut scratch, wrapping, &layout).is_none(), "a wrapped capacity product must not pass the check");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "serves out of order")]
|
#[should_panic(expected = "serves out of order")]
|
||||||
fn a_run_refuses_a_gapped_serve() {
|
fn a_run_refuses_a_gapped_serve() {
|
||||||
@@ -338,17 +365,6 @@ mod tests {
|
|||||||
run.served(2, &proof);
|
run.served(2, &proof);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_run_refuses_a_lane_count_whose_stride_product_overflows() {
|
|
||||||
let layout = Layout::default().with_writes(0, element_write::<f64>(), &[]);
|
|
||||||
let mut scratch = [std::mem::MaybeUninit::<u64>::uninit(); 4];
|
|
||||||
let mut frame_arena = FrameArena::new();
|
|
||||||
frame_arena.reserve(64);
|
|
||||||
let frames = frame_arena.frames();
|
|
||||||
let wrapping = usize::MAX / layout.lane_stride() + 1;
|
|
||||||
assert!(frames.run(&mut scratch, wrapping, &layout).is_none(), "a wrapped capacity product must not pass the check");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_claim_shortens_onto_a_derived_lifetime() {
|
fn a_claim_shortens_onto_a_derived_lifetime() {
|
||||||
fn shorten<'long: 'short, 'short, 'l>(claim: FrameClaim<'long, 'l>) -> FrameClaim<'short, 'l> {
|
fn shorten<'long: 'short, 'short, 'l>(claim: FrameClaim<'long, 'l>) -> FrameClaim<'short, 'l> {
|
||||||
|
|||||||
Reference in New Issue
Block a user