qcode/value/util/base_ref.rs
1// TODO: maybe try to reuse some of num_ref's code here?
2
3use crate::{
4 context::{Context, Shared},
5 value::ValueId,
6};
7
8/// A generic wrapper struct for referencing an ID with a context.
9/// This is the basis for all `*Ref` and `*MutRef`.
10///
11/// `Id` is any cheap `Copy` handle — either a `Registry` `Identifier` (varnode,
12/// literal, function, …) or a composite IR id (`InstructionId`, `BlockId`, …).
13/// The bound is intentionally just `Copy`; routing to storage is the concern of
14/// the concrete ref impls, not this wrapper.
15pub struct BaseRef<Ctx, Id: Copy> {
16 pub id: Id,
17 pub(in crate::value) ctx: Ctx,
18}
19
20impl<Ctx, Id: Copy> BaseRef<Ctx, Id> {
21 pub fn new(ctx: Ctx, id: Id) -> Self {
22 BaseRef { id, ctx }
23 }
24}
25
26impl<Ctx, Id: Copy + Into<ValueId>> BaseRef<Ctx, Id> {
27 pub fn id(&self) -> ValueId {
28 self.id.into()
29 }
30}
31
32impl<'str, 'ctx, Id: Copy> BaseRef<&'ctx Context<'str>, Id> {
33 pub fn from_id(ctx: &'ctx Context<'str>, id: Id) -> Self {
34 BaseRef { id, ctx }
35 }
36}
37
38/// Construct a shared-leaf wrapper ref (literal, bytes, varnode) over the
39/// module's [`Shared`] IR state, taking **either** a `&Context` or a `&Shared`
40/// (via [`AsShared`]) so module- and pass-scope callers share one spelling
41/// (context-split stage 5b-ii item #1).
42impl<'a, 'str, Id: Copy> BaseRef<&'a Shared<'str>, Id> {
43 pub fn from_id(src: impl AsShared<'a, 'str>, id: Id) -> Self {
44 BaseRef {
45 id,
46 ctx: src.as_shared(),
47 }
48 }
49}
50
51impl<'str, 'ctx, Id: Copy> BaseRef<&'ctx mut Context<'str>, Id> {
52 pub fn from_id(ctx: &'ctx mut Context<'str>, id: Id) -> Self {
53 BaseRef { id, ctx }
54 }
55}
56
57/// Convert mutable refs to immutable refs by cloning the ID and sharing the context reference.
58impl<'str, 'ctx, Id: Copy> From<BaseRef<&'ctx mut Context<'str>, Id>>
59 for BaseRef<&'ctx Context<'str>, Id>
60{
61 fn from(r: BaseRef<&'ctx mut Context<'str>, Id>) -> Self {
62 BaseRef {
63 id: r.id,
64 ctx: r.ctx,
65 }
66 }
67}
68
69pub trait WithCtx<'s, 'ctx: 's, 'str: 'ctx> {
70 fn ctx(&'s self) -> &'ctx Context<'str>;
71}
72
73/// Narrows a module handle to its [`Shared`] IR state, so the shared-leaf
74/// wrapper-ref constructors (`Varnode::from_id`, `Space::from_id`,
75/// `LiteralRef`/`BytesRef`) accept **either** a whole `&Context` (module scope)
76/// **or** a bare `&Shared` (pass scope, via `QCodeView::shared()` /
77/// `BodyMut::shr()`) with no call-site churn (context-split stage 5b-ii item
78/// #1). The leaf refs themselves store only a `&Shared`.
79pub trait AsShared<'a, 'str> {
80 // Implemented only for `Copy` reference types (`&Context`, `&Shared`), so
81 // taking `self` by value is a cheap pointer copy, not an owning move.
82 #[allow(clippy::wrong_self_convention)]
83 fn as_shared(self) -> &'a Shared<'str>;
84}
85
86impl<'a, 'str> AsShared<'a, 'str> for &'a Shared<'str> {
87 fn as_shared(self) -> &'a Shared<'str> {
88 self
89 }
90}
91
92impl<'a, 'str> AsShared<'a, 'str> for &'a Context<'str> {
93 fn as_shared(self) -> &'a Shared<'str> {
94 &self.shared
95 }
96}
97
98/// The shared-leaf twin of [`WithCtx`]: a ref that can yield the module's
99/// [`Shared`] IR state (interners, spaces, registers, name map) for its read
100/// methods. Implemented by the shared-leaf wrapper refs (literal, bytes,
101/// varnode) — which now carry only a `&Shared` — and by their `&mut Context`
102/// mutation twins (which narrow through `self.ctx.shared`). This replaces the
103/// leaf refs' old `WithCtx` dependency: a shared-leaf value never needs the
104/// whole `&Context`, only its shared part (context-split stage 5b-ii item #1).
105pub trait WithShared<'s, 'sh: 's, 'str: 'sh> {
106 fn shared(&'s self) -> &'sh Shared<'str>;
107}
108
109pub trait WithCtxMut<'s, 'str: 's>: WithCtx<'s, 's, 'str> {
110 fn ctx_mut(&'s mut self) -> &'s mut Context<'str>;
111}