1use core::{
7 any::Any,
8 cell::{Cell, Ref, RefCell, RefMut},
9 fmt::Display,
10 hash::Hash,
11 marker::PhantomData,
12};
13
14use crate::{
15 arg_error_noloc,
16 basic_block::BasicBlock,
17 common_traits::Verify,
18 dialect::{Dialect, DialectName},
19 identifier::Identifier,
20 operation::Operation,
21 printable::{self, Printable},
22 region::Region,
23 result::Result,
24 std_deps::sync::LazyLock,
25 storage_uniquer::UniqueStore,
26 r#type::TypeObj,
27 uniqued_any::UniquedAny,
28 utils::table::{HMap, HSet, IMap},
29 verify_err_noloc,
30};
31use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec};
32use slotmap::{SlotMap, new_key_type};
33
34new_key_type! {
35 pub struct ArenaIndex;
37}
38
39new_key_type! {
40 pub struct AuxDataIndex;
42}
43
44impl Display for ArenaIndex {
45 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
46 write!(f, "{:?}", self.0)
47 }
48}
49
50pub type Arena<T> = SlotMap<ArenaIndex, RefCell<T>>;
52
53pub struct Context {
55 pub(crate) value_counter: Cell<u64>,
58 pub(crate) use_counter: Cell<u64>,
61 pub(crate) operations: Arena<Operation>,
63 pub(crate) basic_blocks: Arena<BasicBlock>,
65 pub(crate) regions: Arena<Region>,
67 pub(crate) dialects: HMap<DialectName, Dialect>,
69 pub(crate) type_store: UniqueStore<TypeObj>,
71 pub(crate) uniqued_any_store: UniqueStore<UniquedAny>,
73 pub aux_data: SlotMap<AuxDataIndex, Box<dyn Any>>,
75 pub aux_data_map: HMap<Identifier, AuxDataIndex>,
77}
78
79impl Context {
80 pub fn new() -> Context {
81 Self::default()
82 }
83
84 pub fn is_ir_empty(&self) -> bool {
88 self.operations.is_empty() && self.basic_blocks.is_empty() && self.regions.is_empty()
89 }
90
91 pub(crate) fn get_new_value_uid(&self) -> u64 {
93 let uid = self.value_counter.get();
94 self.value_counter.set(uid + 1);
95 uid
96 }
97
98 pub(crate) fn get_new_use_uid(&self) -> u64 {
100 let uid = self.use_counter.get();
101 self.use_counter.set(uid + 1);
102 uid
103 }
104}
105
106impl Default for Context {
107 fn default() -> Self {
108 let mut ctx = Context {
109 value_counter: Cell::new(0),
110 use_counter: Cell::new(0),
111 operations: Arena::default(),
112 basic_blocks: Arena::default(),
113 regions: Arena::default(),
114 dialects: HMap::default(),
115 type_store: UniqueStore::default(),
116 uniqued_any_store: UniqueStore::default(),
117 aux_data: SlotMap::with_key(),
118 aux_data_map: HMap::default(),
119 };
120
121 if let Err(err) = &*DICT_KEYS_VERIFIER {
123 panic!("{}", err.err);
124 }
125
126 for registration in get_context_registrations() {
128 registration(&mut ctx);
129 }
130
131 ctx
132 }
133}
134
135pub(crate) mod private {
136 use super::*;
137
138 pub trait ArenaObj
140 where
141 Self: Sized,
142 {
143 fn get_arena(ctx: &Context) -> &Arena<Self>;
145 fn get_arena_mut(ctx: &mut Context) -> &mut Arena<Self>;
147 fn get_self_ptr(&self, ctx: &Context) -> Ptr<Self>;
149 fn dealloc_sub_objects(ptr: Ptr<Self>, ctx: &mut Context);
152
153 fn alloc<T: FnOnce(Ptr<Self>) -> Self>(ctx: &mut Context, f: T) -> Ptr<Self> {
155 let creator = |idx: ArenaIndex| {
156 let t = f(Ptr::<Self> {
157 idx,
158 _dummy: PhantomData::<Self>,
159 });
160 RefCell::new(t)
161 };
162 Ptr::<Self> {
163 idx: Self::get_arena_mut(ctx).insert_with_key(creator),
164 _dummy: PhantomData,
165 }
166 }
167
168 fn dealloc(ptr: Ptr<Self>, ctx: &mut Context) {
170 Self::dealloc_sub_objects(ptr, ctx);
171 Self::get_arena_mut(ctx).remove(ptr.idx);
172 }
173 }
174}
175
176use private::ArenaObj;
177
178pub struct Ptr<T: ArenaObj> {
180 pub(crate) idx: ArenaIndex,
181 pub(crate) _dummy: PhantomData<T>,
182}
183
184impl<T: ArenaObj> core::fmt::Debug for Ptr<T> {
185 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186 write!(f, "Ptr<{}>[{}]", core::any::type_name::<T>(), self.idx)
187 }
188}
189
190#[derive(Debug, thiserror::Error)]
191#[error("Attempt to dereference a dangling Ptr")]
192pub struct DanglingPtrDerefError;
193
194impl<'a, T: ArenaObj> Ptr<T> {
195 #[track_caller]
201 pub fn deref(&self, ctx: &'a Context) -> Ref<'a, T> {
202 T::get_arena(ctx)
203 .get(self.idx)
204 .expect("Dangling Ptr deref")
205 .borrow()
206 }
207
208 #[track_caller]
214 pub fn deref_mut(&self, ctx: &'a Context) -> RefMut<'a, T> {
215 T::get_arena(ctx)
216 .get(self.idx)
217 .expect("Dangling Ptr deref_mut")
218 .borrow_mut()
219 }
220
221 pub fn try_deref(&self, ctx: &'a Context) -> Result<Ref<'a, T>> {
226 T::get_arena(ctx)
227 .get(self.idx)
228 .ok_or_else(|| arg_error_noloc!(DanglingPtrDerefError))?
229 .try_borrow()
230 .map_err(|err| arg_error_noloc!(err))
231 }
232
233 pub fn try_deref_mut(&self, ctx: &'a Context) -> Result<RefMut<'a, T>> {
238 T::get_arena(ctx)
239 .get(self.idx)
240 .ok_or_else(|| arg_error_noloc!(DanglingPtrDerefError))?
241 .try_borrow_mut()
242 .map_err(|err| arg_error_noloc!(err))
243 }
244
245 pub(crate) fn make_name(&self, name_base: &str) -> Identifier {
247 let idx = format!("{}", self.idx);
248 (name_base.to_string() + &idx).try_into().unwrap()
249 }
250}
251
252impl<T: ArenaObj> Clone for Ptr<T> {
253 fn clone(&self) -> Ptr<T> {
254 *self
255 }
256}
257
258impl<T: ArenaObj> Copy for Ptr<T> {}
259
260impl<T: ArenaObj> PartialEq for Ptr<T> {
261 fn eq(&self, other: &Self) -> bool {
262 self.idx == other.idx
263 }
264}
265
266impl<T: ArenaObj> Eq for Ptr<T> {}
267
268impl<T: ArenaObj + 'static> Hash for Ptr<T> {
269 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
270 self.idx.hash(state);
271 }
272}
273
274impl<T: ArenaObj + Printable> Printable for Ptr<T> {
275 fn fmt(
276 &self,
277 ctx: &Context,
278 state: &printable::State,
279 f: &mut core::fmt::Formatter<'_>,
280 ) -> core::fmt::Result {
281 self.deref(ctx).fmt(ctx, state, f)
282 }
283}
284
285impl<T: ArenaObj + Verify> Verify for Ptr<T> {
286 fn verify(&self, ctx: &Context) -> Result<()> {
287 self.deref(ctx).verify(ctx)
288 }
289}
290
291#[doc(hidden)]
292#[derive(Eq, PartialEq, Debug, Clone)]
294pub struct DictKeyId {
295 pub id: Identifier,
297 pub file: &'static str,
299 pub line: u32,
301 pub column: u32,
303}
304
305pub type ContextRegistration = fn(&mut Context);
309
310#[doc(hidden)]
311#[cfg(not(target_family = "wasm"))]
318pub mod statics {
319 use super::*;
320
321 #[::pliron::linkme::distributed_slice]
322 #[linkme(crate = ::pliron::linkme)]
323 pub static DICT_KEY_IDS: [LazyLock<DictKeyId>];
324
325 pub fn get_dict_key_ids() -> impl Iterator<Item = &'static LazyLock<DictKeyId>> {
326 DICT_KEY_IDS.iter()
327 }
328
329 #[::pliron::linkme::distributed_slice]
330 #[linkme(crate = ::pliron::linkme)]
331 pub static CONTEXT_REGISTRATIONS: [ContextRegistration];
332
333 pub fn get_context_registrations() -> impl Iterator<Item = &'static ContextRegistration> {
334 CONTEXT_REGISTRATIONS.iter()
335 }
336}
337
338#[cfg(target_family = "wasm")]
339pub mod statics {
340 use super::*;
341 use crate::InventoryWrapper;
342
343 ::pliron::inventory::collect!(InventoryWrapper<LazyLock<DictKeyId>>);
344
345 pub fn get_dict_key_ids() -> impl Iterator<Item = &'static LazyLock<DictKeyId>> {
346 ::pliron::inventory::iter::<InventoryWrapper<LazyLock<DictKeyId>>>().map(|llw| llw.0)
347 }
348
349 ::pliron::inventory::collect!(InventoryWrapper<ContextRegistration>);
350
351 pub fn get_context_registrations() -> impl Iterator<Item = &'static ContextRegistration> {
352 ::pliron::inventory::iter::<InventoryWrapper<ContextRegistration>>().map(|llw| llw.0)
353 }
354}
355
356pub use statics::*;
357
358#[doc(hidden)]
359pub static DICT_KEYS_VERIFIER: LazyLock<Result<()>> = LazyLock::new(verify_dict_keys);
360
361#[doc(hidden)]
362pub(crate) fn collect_deduped_interface_verifiers<Id, AllVerifiers, Verifier>(
368 interface_verifiers: impl Iterator<Item = &'static (Id, AllVerifiers)>,
369) -> HMap<Id, Vec<Verifier>>
370where
371 Id: Eq + Hash + Clone + 'static,
372 AllVerifiers: Fn() -> Vec<Verifier> + Clone + 'static,
373 Verifier: Eq + Hash + Clone,
374{
375 let mut grouped = IMap::default();
376 for entry in interface_verifiers {
377 let (id, all_verifiers_for_interface) = entry;
378 grouped
379 .entry(id.clone())
380 .and_modify(|verifiers: &mut Vec<AllVerifiers>| {
381 verifiers.push(all_verifiers_for_interface.clone())
382 })
383 .or_insert(vec![all_verifiers_for_interface.clone()]);
384 }
385
386 grouped
390 .into_iter()
391 .map(|(id, verifiers)| {
392 let mut dedupd_verifiers = Vec::new();
393 let mut seen = HSet::default();
394 for verifier_fn_list in verifiers {
395 for verifier in verifier_fn_list() {
396 if seen.insert(verifier.clone()) {
397 dedupd_verifiers.push(verifier);
398 }
399 }
400 }
401 (id, dedupd_verifiers)
402 })
403 .collect()
404}
405
406#[doc(hidden)]
407pub fn verify_dict_keys() -> Result<()> {
411 let mut seen: HMap<Identifier, (&'static str, u32, u32)> = HMap::default();
412 for key in get_dict_key_ids() {
413 if let Some((file, line, column)) = seen.get(&key.id) {
414 return verify_err_noloc!(
415 "Duplicate dictionary key \"{}\" declared in {}:{}:{} and {}:{}:{}",
416 key.id,
417 file,
418 line,
419 column,
420 key.file,
421 key.line,
422 key.column
423 );
424 }
425 seen.insert(key.id.clone(), (key.file, key.line, key.column));
426 }
427 Ok(())
428}
429
430#[macro_export]
446macro_rules! dict_key {
447 ( $(#[$outer:meta])*
448 $decl:ident, $name:expr
449 ) => {
450 const _: () = {
454 #[cfg_attr(not(target_family = "wasm"),
455 ::pliron::linkme::distributed_slice(::pliron::context::DICT_KEY_IDS), linkme(crate = ::pliron::linkme))]
456 pub static $decl: $crate::std_deps::sync::LazyLock<::pliron::context::DictKeyId> =
457 $crate::std_deps::sync::LazyLock::new(|| ::pliron::context::DictKeyId {
458 id: $name.try_into().unwrap(),
459 file: file!(),
460 line: line!(),
461 column: column!(),
462 });
463
464 #[cfg(target_family = "wasm")]
465 ::pliron::inventory::submit! {
466 ::pliron::InventoryWrapper(&$decl)
467 }
468 };
469 $(#[$outer])*
470 pub static $decl: $crate::std_deps::sync::LazyLock<::pliron::identifier::Identifier> =
472 $crate::std_deps::sync::LazyLock::new(|| $name.try_into().unwrap());
473 };
474}
475
476#[macro_export]
488macro_rules! context_registration {
489 ( $(#[$outer:meta])*
490 $registration:expr
491 ) => {
492 const _: () = {
493 $(#[$outer])*
494 #[cfg_attr(not(target_family = "wasm"),
495 ::pliron::linkme::distributed_slice(::pliron::context::CONTEXT_REGISTRATIONS), linkme(crate = ::pliron::linkme))]
496 static CONTEXT_REGISTRATION: ::pliron::context::ContextRegistration = $registration;
497
498 #[cfg(target_family = "wasm")]
499 ::pliron::inventory::submit! {
500 ::pliron::InventoryWrapper(&CONTEXT_REGISTRATION)
501 }
502 };
503 };
504}