git.delta.rocks / jrsonnet / refs/commits / 752087cb9057

difftreelog

source

crates/jrsonnet-evaluator/src/ctx.rs8.3 KiBsourcehistory
1use std::{2	cell::{Cell, OnceCell, RefCell},3	clone::Clone,4	fmt::{self, Debug},5};67use educe::Educe;8use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;1011use crate::{12	Result, SupThis, Thunk, Val,13	analyze::{CaptureSlot, ClosureShape, LSlot, LocalId, LocalSlot},14	bail, error,15	error::ErrorKind::*,16};1718#[derive(Debug, Trace, Clone, Educe)]19#[educe(PartialEq)]20pub struct Context(#[educe(PartialEq(method = Cc::ptr_eq))] pub(crate) Cc<ContextInternal>);2122#[derive(Trace)]23pub(crate) struct ContextInternal {24	/// Immutable, packed at closure-create time.25	pub(crate) captures: Cc<Vec<Thunk<Val>>>,26	/// Filled during closure initialization27	pub(crate) locals: Cc<LocalsFrame>,28	pub(crate) sup_this: Option<SupThis>,29}3031impl Debug for ContextInternal {32	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {33		f.debug_struct("ContextInternal")34			.field("captures", &self.captures.len())35			.field("locals", &self.locals)36			.field("sup_this", &self.sup_this.is_some())37			.finish()38	}39}4041#[derive(Trace, Debug)]42pub(crate) struct IterFrame {43	slots: Vec<RefCell<Option<Thunk<Val>>>>,44	captured: Cell<bool>,45}46impl IterFrame {47	pub fn new(n: u16) -> IterFrame {48		let cells: Vec<RefCell<Option<Thunk<Val>>>> = (0..n).map(|_| RefCell::new(None)).collect();49		IterFrame {50			slots: cells,51			captured: Cell::new(false),52		}53	}54	pub fn set(&self, slot: LocalSlot, value: Thunk<Val>) {55		*self.slots[slot.0 as usize].borrow_mut() = Some(value);56	}57}5859#[derive(Trace, Debug)]60pub(crate) enum LocalsFrame {61	Once1(OnceCell<Thunk<Val>>),62	/// Letrec/function/object/for frames - slots are filled during frame setup63	Once(Vec<OnceCell<Thunk<Val>>>),64	/// Comp-eager fast-path, cells are reset per iteration for the unique frames (i.e for the non-capturing thunks)65	Iter(IterFrame),66}67impl LocalsFrame {68	pub fn set(&self, slot: LocalSlot, value: Thunk<Val>) {69		match self {70			LocalsFrame::Once1(cell) => {71				debug_assert_eq!(slot.0, 0, "Once1 only holds slot 0");72				cell.set(value)73					.map_err(|_| ())74					.expect("slot already filled");75			}76			LocalsFrame::Once(cells) => {77				cells[slot.0 as usize]78					.set(value)79					.map_err(|_| ())80					.expect("slot already filled");81			}82			LocalsFrame::Iter(_) => unreachable!("iter frame has different constructors"),83		}84	}85}8687impl LocalsFrame {88	pub(crate) fn new_once(n: u16) -> Cc<Self> {89		if n == 1 {90			return Cc::new(Self::Once1(OnceCell::new()));91		}92		let cells: Vec<OnceCell<Thunk<Val>>> = (0..n).map(|_| OnceCell::new()).collect();93		Cc::new(Self::Once(cells))94	}95}9697pub(crate) struct IterContext {98	context: Context,99}100impl IterContext {101	pub(crate) fn create(&self, build: impl FnOnce(&IterFrame)) -> Result<Context> {102		if !Cc::is_unique(&self.context.0.locals) {103			bail!(EagerCompspecCaptured);104		}105		let LocalsFrame::Iter(frame) = &*self.context.0.locals else {106			unreachable!("IterContext is only created for Iter ctx");107		};108		if frame.captured.get() {109			bail!(EagerCompspecCaptured);110		}111		build(frame);112		Ok(self.context.clone())113	}114}115116#[derive(Trace, Clone)]117pub(crate) struct PackedContext {118	captures: Cc<Vec<Thunk<Val>>>,119	n_locals: u16,120}121impl PackedContext {122	pub fn enter(self, sup_this: SupThis, build: impl FnOnce(&LocalsFrame, &Context)) -> Context {123		let locals = LocalsFrame::new_once(self.n_locals);124		let val = Context(Cc::new(ContextInternal {125			captures: self.captures.clone(),126			locals,127			sup_this: Some(sup_this),128		}));129		build(&val.0.locals, &val);130		val131	}132}133#[derive(Trace, Clone, Educe, Debug)]134#[educe(PartialEq)]135pub(crate) struct PackedContextSupThis {136	#[educe(PartialEq(method = Cc::ptr_eq))]137	captures: Cc<Vec<Thunk<Val>>>,138	n_locals: u16,139	sup_this: Option<SupThis>,140}141impl PackedContextSupThis {142	pub fn enter(self, build: impl FnOnce(&LocalsFrame, &Context)) -> Context {143		let locals = LocalsFrame::new_once(self.n_locals);144		let val = Context(Cc::new(ContextInternal {145			captures: self.captures.clone(),146			locals,147			sup_this: self.sup_this,148		}));149		build(&val.0.locals, &val);150		val151	}152}153154impl Context {155	#[inline]156	pub fn slot(&self, slot: LSlot) -> Thunk<Val> {157		match slot {158			LSlot::Local(i) => self.local(i),159			LSlot::Capture(i) => self.capture(i),160		}161	}162	/// Read a local slot from the shared locals frame.163	///164	/// # Panics165	/// If the slot has not yet been filled. The analyzer guarantees166	/// that slot indices are in range and that letrec setup completes167	/// before the first read. A panic indicates an analyzer/runtime168	/// invariant violation, not a user error.169	#[inline]170	pub fn local(&self, slot: LocalSlot) -> Thunk<Val> {171		match &*self.0.locals {172			LocalsFrame::Once1(cell) => {173				debug_assert_eq!(slot.0, 0, "Once1 only holds slot 0");174				cell.get().expect("local read before letrec init").clone()175			}176			LocalsFrame::Once(cells) => cells[slot.0 as usize]177				.get()178				.expect("local read before letrec init")179				.clone(),180			LocalsFrame::Iter(cells) => cells.slots[slot.0 as usize]181				.borrow()182				.as_ref()183				.expect("iter local read before iteration filled it")184				.clone(),185		}186	}187188	/// Read a captured slot from this closure's capture pack.189	#[inline]190	pub fn capture(&self, slot: CaptureSlot) -> Thunk<Val> {191		(*self.0.captures)[slot.0 as usize].clone()192	}193194	pub fn sup_this(&self) -> Option<&SupThis> {195		self.0.sup_this.as_ref()196	}197198	pub fn try_sup_this(&self) -> Result<SupThis> {199		self.0200			.sup_this201			.clone()202			.ok_or_else(|| error!(CantUseSelfSupOutsideOfObject))203	}204205	/// Build a root context: empty captures, externals filled into a206	/// fresh Once locals frame in declaration order. Used once at207	/// program entry to construct the context the analyzed root LIR208	/// runs against.209	pub(crate) fn root(externals: Vec<Thunk<Val>>) -> Self {210		let n: u16 = externals211			.len()212			.try_into()213			.expect("more than u16::MAX externals");214		let cells: Vec<OnceCell<Thunk<Val>>> = externals215			.into_iter()216			.map(|t| {217				let cell = OnceCell::new();218				cell.set(t).map_err(|_| ()).expect("fresh cell");219				cell220			})221			.collect();222		debug_assert_eq!(cells.len(), n as usize);223		let locals = Cc::new(LocalsFrame::Once(cells));224		Self(Cc::new(ContextInternal {225			captures: Cc::new(Vec::new()),226			locals,227			sup_this: None,228		}))229	}230231	pub(crate) fn pack_captures(&self, shape: &ClosureShape) -> PackedContext {232		PackedContext {233			captures: Cc::new(pack_captures(self, &shape.captures)),234			n_locals: shape.n_locals,235		}236	}237	pub(crate) fn pack_captures_sup_this(&self, shape: &ClosureShape) -> PackedContextSupThis {238		PackedContextSupThis {239			captures: Cc::new(pack_captures(self, &shape.captures)),240			n_locals: shape.n_locals,241			sup_this: self.0.sup_this.clone(),242		}243	}244245	pub(crate) fn enter_iter(246		parent: &Context,247		shape: &ClosureShape,248		cb: impl FnOnce(IterContext) -> Result<()>,249	) -> Result<()> {250		let captures = Cc::new(pack_captures(parent, &shape.captures));251		let locals = IterFrame::new(shape.n_locals);252		cb(IterContext {253			context: Self(Cc::new(ContextInternal {254				captures,255				locals: Cc::new(LocalsFrame::Iter(locals)),256				sup_this: parent.0.sup_this.clone(),257			})),258		})259	}260261	pub(crate) fn enter_using(parent: &Context, shape: &ClosureShape) -> Self {262		debug_assert_eq!(shape.n_locals, 0);263		if shape.captures.is_empty() {264			if let LocalsFrame::Iter(i) = &*parent.0.locals {265				i.captured.set(true);266			}267			// Value never uses captures, thus evaluating it against the parent gives the same result268			return parent.clone();269		}270		let captures = Cc::new(pack_captures(parent, &shape.captures));271		Self(Cc::new(ContextInternal {272			captures,273			locals: parent.0.locals.clone(),274			sup_this: parent.0.sup_this.clone(),275		}))276	}277}278279fn pack_captures(parent: &Context, sources: &[LSlot]) -> Vec<Thunk<Val>> {280	sources.iter().map(|src| parent.slot(*src)).collect()281}282283pub struct InitialContextBuilder {284	externals: Vec<(IStr, LocalId)>,285	values: Vec<Thunk<Val>>,286	next_id: u32,287}288289impl InitialContextBuilder {290	pub(crate) fn new() -> Self {291		Self {292			externals: Vec::new(),293			values: Vec::new(),294			next_id: 0,295		}296	}297298	pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) {299		let name = name.into();300		let id = LocalId(self.next_id);301		self.next_id += 1;302		self.externals.push((name, id));303		self.values.push(value);304	}305306	pub(crate) fn build(self) -> (Vec<(IStr, LocalId)>, Vec<Thunk<Val>>) {307		(self.externals, self.values)308	}309}310311impl Default for InitialContextBuilder {312	fn default() -> Self {313		Self::new()314	}315}