Skip to main content

simplicity/node/
hiding.rs

1use crate::jet::Jet;
2use crate::node::{
3    CoreConstructible, DisconnectConstructible, JetConstructible, WitnessConstructible,
4};
5use crate::types::{Context, Error};
6use crate::{Cmr, FailEntropy, HasCmr, Word};
7
8/// Wrapper that allows a node to be "hidden" during program construction.
9///
10/// ## Program construction
11///
12/// When a program is constructed in post-order,
13/// the parent node is created based on its children.
14/// We use this fact to introduce special branching logic:
15///
16/// 1. A `case` node with a left "hidden" child and a right non-hidden child becomes `assertr`.
17/// 2. A `case` node with a left non-hidden child and a right "hidden" child becomes `assertl`.
18/// 3. Otherwise, any node with "hidden" children becomes itself "hidden" with an updated CMR.
19/// 4. Any node with non-hidden children remains unchanged.
20///
21/// The program can be extracted from the wrapper when construction is finished.
22/// The program is invalid if the root node is "hidden".
23///
24/// ## Wrapping
25///
26/// A node can be wrapped via [`Hiding::from`] to add hiding support.
27/// A wrapped node can be converted into a "hidden" node via [`Hiding::hide`].
28/// Finally, a "hidden" node can be manually created via [`Hiding::hidden`].
29///
30/// ## Virtual hidden nodes
31///
32/// The wrapper merely _simulates_ hidden nodes.
33/// At no point are actual hidden nodes created.
34/// To stress this fact, I write "hidden" in quotation marks.
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub struct Hiding<'brand, N> {
37    result: HidingResult<N>,
38    /// Inference context for program construction.
39    ///
40    /// Even a "hidden" node needs an inference context
41    /// because the context may be queried via [`CoreConstructible::inference_context`].
42    /// When a "hidden" node is converted into an assertion via the
43    /// [`CoreConstructible::case`] constructor, this context is required to build the case node.
44    /// For soundness, the same context should be returned for all nodes of the same program.
45    ctx: Context<'brand>,
46}
47
48type HidingResult<N> = Result<N, Cmr>;
49
50impl<'brand, N> Hiding<'brand, N> {
51    /// Create a "hidden" node with the given CMR.
52    ///
53    /// To enable the construction of possible parent nodes,
54    /// the inference context of the current program must be passed.
55    pub const fn hidden(cmr: Cmr, ctx: Context<'brand>) -> Self {
56        // # Soundness
57        // The hidden node introduces no type variables.
58        Self {
59            result: Err(cmr),
60            ctx,
61        }
62    }
63
64    fn hidden_cloned_ctx(&self, cmr: Cmr) -> Self {
65        Self {
66            result: Err(cmr),
67            ctx: self.ctx.shallow_clone(),
68        }
69    }
70
71    /// Access the non-hidden node inside in the wrapper.
72    ///
73    /// Return `None` if the wrapped node is "hidden".
74    pub fn as_node(&self) -> Option<&N> {
75        self.result.as_ref().ok()
76    }
77
78    /// Consume the wrapper and return the non-hidden node that was inside.
79    ///
80    /// Return `None` if the wrapped node is "hidden".
81    pub fn get_node(self) -> Option<N> {
82        self.result.ok()
83    }
84}
85
86impl<N: HasCmr> Hiding<'_, N> {
87    /// Ensure that the wrapped node is "hidden".
88    /// Convert non-hidden nodes into "hidden" nodes with the same CMR.
89    pub fn hide(self) -> Self {
90        // # Soundness
91        // Hiding a node means converting it into its CMR.
92        // The node's type variables remain in the inference context.
93        //
94        // The type variables of a "hidden" child don't influence the construction of a parent,
95        // because merely the child's CMR is passed to the parent constructor.
96        // A CMR has no connection to any type variables.
97        //
98        // Hiding a node creates a CMR that is independent from the original node.
99        // The CMR can be used in one part of the program
100        // while the node itself is used in a different part.
101        match self.result {
102            Ok(node) => Self::hidden(node.cmr(), self.ctx),
103            Err(..) => self,
104        }
105    }
106}
107
108impl<N: HasCmr> HasCmr for Hiding<'_, N> {
109    fn cmr(&self) -> Cmr {
110        match &self.result {
111            Ok(node) => node.cmr(),
112            Err(cmr) => *cmr,
113        }
114    }
115}
116
117// We need `N: CoreConstructible` to access the inference context.
118// Because of this, implementations of `{Jet, Disconnect, Witness}Constructible`
119// for `Hiding<N>` require `N: CoreConstructible`.
120impl<'brand, N: CoreConstructible<'brand>> From<N> for Hiding<'brand, N> {
121    fn from(node: N) -> Self {
122        Self {
123            ctx: node.inference_context().shallow_clone(),
124            result: Ok(node),
125        }
126    }
127}
128
129// # Soundness
130// See [`Hiding::hide`].
131impl<'brand, N: CoreConstructible<'brand> + HasCmr> CoreConstructible<'brand>
132    for Hiding<'brand, N>
133{
134    fn iden(inference_context: &Context<'brand>) -> Self {
135        N::iden(inference_context).into()
136    }
137
138    fn unit(inference_context: &Context<'brand>) -> Self {
139        N::unit(inference_context).into()
140    }
141
142    fn injl(child: &Self) -> Self {
143        match &child.result {
144            Ok(child) => N::injl(child).into(),
145            Err(cmr) => child.hidden_cloned_ctx(Cmr::injl(*cmr)),
146        }
147    }
148
149    fn injr(child: &Self) -> Self {
150        match &child.result {
151            Ok(child) => N::injr(child).into(),
152            Err(cmr) => child.hidden_cloned_ctx(Cmr::injr(*cmr)),
153        }
154    }
155
156    fn take(child: &Self) -> Self {
157        match &child.result {
158            Ok(child) => N::take(child).into(),
159            Err(cmr) => child.hidden_cloned_ctx(Cmr::take(*cmr)),
160        }
161    }
162
163    fn drop_(child: &Self) -> Self {
164        match &child.result {
165            Ok(child) => N::drop_(child).into(),
166            Err(cmr) => child.hidden_cloned_ctx(Cmr::drop(*cmr)),
167        }
168    }
169
170    fn comp(left: &Self, right: &Self) -> Result<Self, Error> {
171        match (&left.result, &right.result) {
172            (Ok(left), Ok(right)) => N::comp(left, right).map(Self::from),
173            _ => Ok(left.hidden_cloned_ctx(Cmr::comp(left.cmr(), right.cmr()))),
174        }
175    }
176
177    fn case(left: &Self, right: &Self) -> Result<Self, Error> {
178        match (&left.result, &right.result) {
179            (Ok(left), Ok(right)) => N::case(left, right).map(Self::from),
180            (Err(left), Ok(right)) => N::assertr(*left, right).map(Self::from),
181            (Ok(left), Err(right)) => N::assertl(left, *right).map(Self::from),
182            _ => Ok(left.hidden_cloned_ctx(Cmr::case(left.cmr(), right.cmr()))),
183        }
184    }
185
186    fn assertl(left: &Self, right: Cmr) -> Result<Self, Error> {
187        match &left.result {
188            Ok(left) => N::assertl(left, right).map(Self::from),
189            _ => Ok(left.hidden_cloned_ctx(Cmr::case(left.cmr(), right))),
190        }
191    }
192
193    fn assertr(left: Cmr, right: &Self) -> Result<Self, Error> {
194        match &right.result {
195            Ok(right) => N::assertr(left, right).map(Self::from),
196            _ => Ok(right.hidden_cloned_ctx(Cmr::case(left, right.cmr()))),
197        }
198    }
199
200    fn pair(left: &Self, right: &Self) -> Result<Self, Error> {
201        match (&left.result, &right.result) {
202            (Ok(left), Ok(right)) => N::pair(left, right).map(Self::from),
203            _ => Ok(left.hidden_cloned_ctx(Cmr::pair(left.cmr(), right.cmr()))),
204        }
205    }
206
207    fn fail(inference_context: &Context<'brand>, entropy: FailEntropy) -> Self {
208        N::fail(inference_context, entropy).into()
209    }
210
211    fn const_word(inference_context: &Context<'brand>, word: Word) -> Self {
212        N::const_word(inference_context, word).into()
213    }
214
215    fn inference_context(&self) -> &Context<'brand> {
216        &self.ctx
217    }
218}
219
220impl<'brand, N> JetConstructible<'brand> for Hiding<'brand, N>
221where
222    N: JetConstructible<'brand> + CoreConstructible<'brand>,
223{
224    fn jet(inference_context: &Context<'brand>, jet: &dyn Jet) -> Self {
225        N::jet(inference_context, jet).into()
226    }
227}
228
229impl<'brand, X, N> DisconnectConstructible<'brand, Option<X>> for Hiding<'brand, N>
230where
231    N: DisconnectConstructible<'brand, Option<X>> + CoreConstructible<'brand> + HasCmr,
232{
233    fn disconnect(left: &Self, right: &Option<X>) -> Result<Self, Error> {
234        match &left.result {
235            Ok(left) => N::disconnect(left, right).map(Self::from),
236            Err(..) => Ok(left.hidden_cloned_ctx(Cmr::disconnect(left.cmr()))),
237        }
238    }
239}
240
241impl<'brand, W, N> WitnessConstructible<'brand, W> for Hiding<'brand, N>
242where
243    N: WitnessConstructible<'brand, W> + CoreConstructible<'brand>,
244{
245    fn witness(inference_context: &Context<'brand>, witness: W) -> Self {
246        N::witness(inference_context, witness).into()
247    }
248}