1use super::arrays::{ArrayShape, array_shape, const_i64};
34use super::real::{real_kind, state_ty, strip_conversions};
35use super::*;
36use crate::constraints::ir::{ArrayNode, Assignment, eval};
37use crate::constraints::real_domain::{FloatKind as Fk, RealExpr};
38use crate::state::storage::{field_slice, storage_width};
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
42pub enum Step {
43 Index(i64),
45 Field(u32),
47}
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
51pub enum Declared {
52 Int(u32),
54 Real(u32),
56}
57
58#[derive(Clone, Copy)]
60pub(super) enum RawStep<'d> {
61 Index(Expression<'d>),
63 Const(i64),
65 Field(u32),
67}
68
69#[derive(Clone)]
71pub(super) struct Chain<'d> {
72 pub root: Symbol<'d>,
74 pub steps: Vec<RawStep<'d>>,
77}
78
79pub(super) fn aggregate_chain<'d>(
83 e: Expression<'d>,
84 forced: &HashSet<SymbolId>,
85) -> Option<Chain<'d>> {
86 let e = strip_conversions(e);
87 let kind = e.kind();
88 if kind == ExpressionKind::NamedValue {
90 let sym = e.referenced_symbol()?;
91 return (sym.kind() == SymbolKind::ClassProperty || forced.contains(&sym.uid())).then(
92 || Chain {
93 root: sym,
94 steps: Vec::new(),
95 },
96 );
97 }
98 if kind == ExpressionKind::MemberAccess {
99 let base = e.left()?;
100 let member = e.member_symbol()?;
101 let is_this = strip_conversions(base).kind() == ExpressionKind::NamedValue
102 && strip_conversions(base)
103 .referenced_symbol()
104 .is_some_and(|s| s.name() == "this");
105 if is_this {
106 return (member.kind() == SymbolKind::ClassProperty).then(|| Chain {
107 root: member,
108 steps: Vec::new(),
109 });
110 }
111 let bt = base.expr_type()?.canonical();
112 if !bt.is_unpacked_struct() {
113 return None;
114 }
115 let index = bt.fields().position(|f| f.name() == member.name())?;
116 let mut chain = aggregate_chain(base, forced)?;
117 chain.steps.push(RawStep::Field(u32::try_from(index).ok()?));
118 return Some(chain);
119 }
120 if kind == ExpressionKind::ElementSelect {
121 let base = e.select_value()?;
122 if array_shape(base.expr_type()?) != Some(ArrayShape::Fixed) {
123 return None;
124 }
125 let mut chain = aggregate_chain(base, forced)?;
126 chain.steps.push(RawStep::Index(e.selector()?));
127 return Some(chain);
128 }
129 None
130}
131
132pub fn is_aggregate_type(t: Type<'_>) -> bool {
135 let c = t.canonical();
136 c.is_unpacked_struct() || array_shape(c) == Some(ArrayShape::Fixed)
137}
138
139pub fn property_is_random(sym: Symbol<'_>) -> bool {
145 if sym.rand_mode() != RandMode::None {
146 return true;
147 }
148 sym.value_type().is_some_and(has_random_member)
149}
150
151fn has_random_member(t: Type<'_>) -> bool {
152 let c = t.canonical();
153 if c.is_unpacked_struct() {
154 return c.fields().any(|f| {
155 f.field_rand_mode() != RandMode::None || f.value_type().is_some_and(has_random_member)
156 });
157 }
158 array_shape(c) == Some(ArrayShape::Fixed) && c.element_type().is_some_and(has_random_member)
159}
160
161#[derive(Clone, Copy, Debug)]
163pub(super) enum LeafKind {
164 Int(Ty),
166 Real(Fk),
168 Vif,
170 Skipped,
172 Other,
174}
175
176#[derive(Clone)]
178pub(super) struct LeafRef<'d> {
179 pub root: SymbolId,
181 pub name: String,
183 pub path: Vec<Step>,
185 pub window: Window,
187 pub mode: RandMode,
189 pub kind: LeafKind,
191 pub vt: Type<'d>,
193}
194
195impl LeafRef<'_> {
196 pub(super) fn randc(&self) -> bool {
197 self.mode == RandMode::RandC
198 }
199}
200
201pub(super) enum Sel<'d> {
203 Leaf(LeafRef<'d>),
204 Pick {
205 index: Expr,
206 low: i64,
207 elems: Vec<Sel<'d>>,
208 },
209}
210
211#[derive(Clone)]
213struct Cursor<'d> {
214 root: SymbolId,
215 whole: u64,
217 ty: Type<'d>,
218 mode: RandMode,
219 path: Vec<Step>,
220 name: String,
221 bit: u64,
223}
224
225fn missing(what: impl Into<String>) -> BuildError {
226 BuildError::Missing { what: what.into() }
227}
228
229impl<'d> Cursor<'d> {
230 fn of(root: Symbol<'d>) -> Result<Cursor<'d>, BuildError> {
231 let ty = root
232 .value_type()
233 .ok_or_else(|| missing(format!("value type for `{}`", root.name())))?;
234 let whole = storage_width(&ty).ok_or_else(|| {
235 missing(format!(
236 "stored width of `{}` of type `{}`",
237 root.name(),
238 ty.to_sv_string()
239 ))
240 })?;
241 Ok(Cursor {
242 root: root.uid(),
243 whole,
244 ty,
245 mode: root.rand_mode(),
246 path: Vec::new(),
247 name: root.name().to_string(),
248 bit: 0,
249 })
250 }
251
252 fn field(&self, i: u32) -> Result<Cursor<'d>, BuildError> {
254 let t = self.ty.canonical();
255 if !t.is_unpacked_struct() {
256 return Err(missing(format!(
257 "`{}` is not an unpacked struct",
258 self.name
259 )));
260 }
261 let f = t
262 .fields()
263 .nth(i as usize)
264 .ok_or_else(|| missing(format!("member {i} of `{}`", self.name)))?;
265 let (lsb, _) = field_slice(&t, i)
266 .ok_or_else(|| missing(format!("stored position of member {i} of `{}`", self.name)))?;
267 let ty = f
268 .value_type()
269 .ok_or_else(|| missing(format!("type of `{}.{}`", self.name, f.name())))?;
270 let mut path = self.path.clone();
271 path.push(Step::Field(i));
272 Ok(Cursor {
273 root: self.root.clone(),
274 whole: self.whole,
275 ty,
276 mode: match f.field_rand_mode() {
278 RandMode::None => self.mode,
279 own => own,
280 },
281 path,
282 name: format!("{}.{}", self.name, f.name()),
283 bit: self.bit + lsb,
284 })
285 }
286
287 fn dim(&self) -> Result<(i64, i64, Type<'d>), BuildError> {
290 let t = self.ty.canonical();
291 match array_shape(t) {
292 Some(ArrayShape::Fixed) => {}
293 Some(ArrayShape::Collection | ArrayShape::Assoc) => {
294 return Err(unsupported(
295 format!(
296 "`{}`, whose size is only known at run time (a dynamic array, queue or \
297 associative array), as an element of an aggregate",
298 self.name
299 ),
300 "C14",
301 ));
302 }
303 None => {
304 return Err(BuildError::OutOfRangeSelect {
305 what: format!("`{}` is not an array, so it has no elements", self.name),
306 });
307 }
308 }
309 let r = t.fixed_unpacked_array_range();
310 let elem = t
311 .element_type()
312 .ok_or_else(|| missing(format!("element type of the array `{}`", self.name)))?;
313 Ok((
314 i64::from(r.left).min(i64::from(r.right)),
315 i64::from(r.left).max(i64::from(r.right)),
316 elem,
317 ))
318 }
319
320 fn index(&self, i: i64) -> Result<Cursor<'d>, BuildError> {
322 let (lo, hi, elem) = self.dim()?;
323 if !(lo..=hi).contains(&i) {
324 let r = self.ty.canonical().fixed_unpacked_array_range();
325 return Err(BuildError::OutOfRangeSelect {
326 what: format!("`{}` index {i} outside [{}:{}]", self.name, r.left, r.right),
327 });
328 }
329 let ew = storage_width(&elem)
330 .ok_or_else(|| missing(format!("stored width of an element of `{}`", self.name)))?;
331 let mut path = self.path.clone();
332 path.push(Step::Index(i));
333 Ok(Cursor {
334 root: self.root.clone(),
335 whole: self.whole,
336 ty: elem,
337 mode: self.mode,
338 path,
339 name: format!("{}[{i}]", self.name),
340 bit: self.bit + (i - lo) as u64 * ew,
341 })
342 }
343
344 fn leaf(self) -> Result<LeafRef<'d>, BuildError> {
345 let width = storage_width(&self.ty)
346 .ok_or_else(|| missing(format!("stored width of `{}`", self.name)))?;
347 let narrow = |v: u64| {
348 u32::try_from(v).map_err(|_| {
349 missing(format!(
350 "a window of `{}` within 32-bit offsets (the property is {} bits)",
351 self.name, self.whole
352 ))
353 })
354 };
355 let window = Window {
356 bit: narrow(self.bit)?,
357 width: narrow(width)?,
358 field_width: narrow(self.whole)?,
359 };
360 let c = self.ty.canonical();
361 let kind = if let Some(k) = real_kind(Some(self.ty)) {
362 LeafKind::Real(k)
363 } else if c.is_virtual_interface() {
364 LeafKind::Vif
365 } else if c.is_integral() {
366 LeafKind::Int(ty_of(Some(self.ty), &format!("`{}`", self.name))?)
367 } else if c.is_string() || c.is_chandle() {
368 LeafKind::Skipped
369 } else {
370 LeafKind::Other
371 };
372 Ok(LeafRef {
373 root: self.root,
374 name: self.name,
375 path: self.path,
376 window,
377 mode: self.mode,
378 kind,
379 vt: self.ty,
380 })
381 }
382
383 fn random_leaves(self, out: &mut Vec<LeafRef<'d>>) -> Result<(), BuildError> {
385 let t = self.ty.canonical();
386 if t.is_unpacked_struct() {
387 for i in 0..t.fields().count() {
388 self.field(i as u32)?.random_leaves(out)?;
389 }
390 return Ok(());
391 }
392 if array_shape(t) == Some(ArrayShape::Fixed) {
393 let (lo, hi, _) = self.dim()?;
394 for i in lo..=hi {
395 self.index(i)?.random_leaves(out)?;
396 }
397 return Ok(());
398 }
399 if self.mode == RandMode::None || t.is_unpacked_union() {
400 return Ok(());
401 }
402 let leaf = self.leaf()?;
403 match leaf.kind {
404 LeafKind::Int(_) | LeafKind::Real(_) => out.push(leaf),
405 LeafKind::Skipped => {}
406 LeafKind::Vif => return Err(BuildError::VirtualInterfaceRand { name: leaf.name }),
407 LeafKind::Other => {
408 return Err(unsupported(
409 format!(
410 "the random member `{}` of type `{}` inside an unpacked struct or array \
411 (a class handle, queue, dynamic array or associative array nested in a \
412 flat aggregate cannot be randomized)",
413 leaf.name,
414 leaf.vt.to_sv_string()
415 ),
416 "C14",
417 ));
418 }
419 }
420 Ok(())
421 }
422}
423
424impl Builder<'_> {
425 fn cursor_of<'d>(&self, sym: Symbol<'d>) -> Result<Cursor<'d>, BuildError> {
428 let mut cur = Cursor::of(sym)?;
429 if self.forced_roots.contains(&cur.root) {
430 cur.mode = RandMode::Rand;
431 }
432 Ok(cur)
433 }
434
435 pub fn declare_aggregate_forced(
443 &mut self,
444 sym: Symbol<'_>,
445 ) -> Result<Vec<Declared>, BuildError> {
446 self.forced_roots.insert(sym.uid());
447 self.declare_rand_aggregate(sym)
448 }
449
450 pub fn declare_rand_aggregate(&mut self, sym: Symbol<'_>) -> Result<Vec<Declared>, BuildError> {
455 let cur = self.cursor_of(sym)?;
456 if !is_aggregate_type(cur.ty) {
457 return Err(missing(format!(
458 "an unpacked struct or fixed-size array type for `{}` (use `declare_rand`)",
459 sym.name()
460 )));
461 }
462 let mut leaves = Vec::new();
463 cur.random_leaves(&mut leaves)?;
464 leaves.iter().map(|l| self.declare_leaf(l)).collect()
465 }
466
467 pub fn declare_rand_element(
471 &mut self,
472 array: Symbol<'_>,
473 idx: &[i64],
474 ) -> Result<u32, BuildError> {
475 let mut cur = Cursor::of(array)?;
476 for &i in idx {
477 cur = cur.index(i)?;
478 }
479 if is_aggregate_type(cur.ty) {
480 return Err(BuildError::OutOfRangeSelect {
481 what: format!(
482 "`{}` is still an aggregate: name one index per unpacked dimension",
483 cur.name
484 ),
485 });
486 }
487 if cur.mode == RandMode::None {
488 return Err(BuildError::NotRand { name: cur.name });
489 }
490 if idx.is_empty() {
491 return Err(BuildError::OutOfRangeSelect {
492 what: format!(
493 "`{}` is not an array element (use `declare_rand`)",
494 cur.name
495 ),
496 });
497 }
498 let leaf = cur.leaf()?;
499 match self.declare_leaf(&leaf)? {
500 Declared::Int(id) => Ok(id),
501 Declared::Real(_) => Err(BuildError::RealConstraint {
502 what: format!(
503 "`{}` is real: it is a real random variable, not an integral element",
504 leaf.name
505 ),
506 }),
507 }
508 }
509
510 fn declare_leaf(&mut self, leaf: &LeafRef<'_>) -> Result<Declared, BuildError> {
512 let key = (leaf.root.clone(), leaf.path.clone());
513 match leaf.kind {
514 LeafKind::Int(_) => {
515 if let Some(&id) = self.elem_ix.get(&key) {
516 return Ok(Declared::Int(id));
517 }
518 let id = self.new_rand_var(
519 leaf.root.clone(),
520 leaf.name.clone(),
521 leaf.vt,
522 leaf.randc(),
523 leaf.path.clone(),
524 Some(leaf.window),
525 )?;
526 self.elem_ix.insert(key, id);
527 Ok(Declared::Int(id))
528 }
529 LeafKind::Real(kind) => {
530 if let Some(&id) = self.real_leaf_ix.get(&key) {
531 return Ok(Declared::Real(id));
532 }
533 if leaf.randc() {
534 return Err(BuildError::RealConstraint {
535 what: format!(
536 "`randc` variable `{}` is real; `randc` is for integral types only \
537 (IEEE 1800-2023 18.4.2)",
538 leaf.name
539 ),
540 });
541 }
542 let id = self.real_vars.len() as u32;
543 self.real_vars.push(RealVarInfo {
544 symbol: leaf.root.clone(),
545 name: leaf.name.clone(),
546 kind,
547 window: Some(leaf.window),
548 });
549 self.real_leaf_ix.insert(key, id);
550 Ok(Declared::Real(id))
551 }
552 LeafKind::Vif | LeafKind::Skipped | LeafKind::Other => Err(missing(format!(
553 "a randomizable value for `{}` of type `{}`",
554 leaf.name,
555 leaf.vt.to_sv_string()
556 ))),
557 }
558 }
559
560 fn resolve_chain<'d>(&mut self, chain: &Chain<'d>) -> Result<Sel<'d>, BuildError> {
563 let cur = self.cursor_of(chain.root)?;
564 self.walk(cur, &chain.steps)
565 }
566
567 fn walk<'d>(&mut self, cur: Cursor<'d>, steps: &[RawStep<'d>]) -> Result<Sel<'d>, BuildError> {
568 let Some((first, rest)) = steps.split_first() else {
569 return Ok(Sel::Leaf(cur.leaf()?));
570 };
571 match *first {
572 RawStep::Field(i) => self.walk(cur.field(i)?, rest),
573 RawStep::Const(i) => self.walk(cur.index(i)?, rest),
574 RawStep::Index(e) => {
575 let ix = self.expr(e)?;
576 if let Some(i) = Self::constant_index(&ix) {
577 let (lo, hi, _) = cur.dim()?;
578 if (lo..=hi).contains(&i) {
579 return self.walk(cur.index(i)?, rest);
580 }
581 }
582 self.not_in_function(
583 "an element of an unpacked array property at a computed index",
584 )?;
585 self.arrays_allowed("an element of an unpacked array property")?;
586 let mut has_var = false;
587 ix.for_each_var(&mut |_, _| has_var = true);
588 if has_var {
589 return Err(BuildError::BadArray {
590 what: format!(
591 "a random variable in the index of the array `{}`: an index is a \
592 constant, a state variable, a loop variable or the size (IEEE \
593 1800-2023 18.5.7.1)",
594 cur.name
595 ),
596 });
597 }
598 let (lo, hi, _) = cur.dim()?;
602 let mut elems = Vec::new();
603 for i in lo..=hi {
604 elems.push(self.walk(cur.index(i)?, rest)?);
605 }
606 Ok(Sel::Pick {
607 index: ix,
608 low: lo,
609 elems,
610 })
611 }
612 }
613 }
614
615 pub(super) fn constant_index(ix: &Expr) -> Option<i64> {
617 if ix.any_node(&mut |n| matches!(n, Expr::Var(..) | Expr::State(..) | Expr::Array(_))) {
618 return None;
619 }
620 let v = eval(ix, &Assignment::default()).ok()?;
621 const_i64(ix.ty(), &v)
622 }
623
624 pub(super) fn declared_leaf<'d>(
627 &mut self,
628 chain: Chain<'d>,
629 what: &str,
630 ) -> Result<(LeafRef<'d>, Option<Declared>), BuildError> {
631 let Sel::Leaf(leaf) = self.resolve_chain(&chain)? else {
632 return Err(unsupported(
633 format!("{what} that selects an element at a computed index"),
634 "C14",
635 ));
636 };
637 let key = (leaf.root.clone(), leaf.path.clone());
638 let declared = match leaf.kind {
639 LeafKind::Int(_) => self.elem_ix.get(&key).map(|&id| Declared::Int(id)),
640 LeafKind::Real(_) => self.real_leaf_ix.get(&key).map(|&id| Declared::Real(id)),
641 LeafKind::Vif | LeafKind::Skipped | LeafKind::Other => {
642 return Err(BuildError::NonIntegral {
643 what: format!("`{}` of type `{}`", leaf.name, leaf.vt.to_sv_string()),
644 });
645 }
646 };
647 Ok((leaf, declared))
648 }
649
650 fn undeclared(leaf: &LeafRef<'_>) -> BuildError {
652 unsupported(
653 format!(
654 "the random variable `{}`, a leaf of an aggregate property that was not declared \
655 random (`declare_rand_aggregate`)",
656 leaf.name
657 ),
658 "C14",
659 )
660 }
661
662 fn leaf_state(&mut self, leaf: &LeafRef<'_>, ty: Ty) -> Expr {
664 let key = (leaf.root.clone(), leaf.path.clone());
665 if let Some(&id) = self.leaf_state_ix.get(&key) {
666 return Expr::State(id, self.states[id as usize].ty);
667 }
668 let id = self.states.len() as u32;
669 self.states.push(StateInfo {
670 root_bit: 0,
671 symbol: leaf.root.clone(),
672 name: leaf.name.clone(),
673 ty,
674 via: Vec::new(),
675 window: Some(leaf.window),
676 class_of: false,
677 });
678 self.leaf_state_ix.insert(key, id);
679 Expr::State(id, ty)
680 }
681
682 fn leaf_expr(&mut self, leaf: &LeafRef<'_>) -> Result<Expr, BuildError> {
684 let LeafKind::Int(ty) = leaf.kind else {
685 return Err(BuildError::NonIntegral {
686 what: format!("`{}` of type `{}`", leaf.name, leaf.vt.to_sv_string()),
687 });
688 };
689 if leaf.mode == RandMode::None {
690 return Ok(self.leaf_state(leaf, ty));
691 }
692 let key = (leaf.root.clone(), leaf.path.clone());
693 let &id = self
694 .elem_ix
695 .get(&key)
696 .ok_or_else(|| Self::undeclared(leaf))?;
697 if self.frame.is_some() {
698 return Err(self.rand_read_in_function(&leaf.name));
699 }
700 Ok(Expr::Var(id, self.vars[id as usize].ty))
701 }
702
703 fn sel_expr(&mut self, sel: Sel<'_>, ty: Ty) -> Result<Expr, BuildError> {
704 match sel {
705 Sel::Leaf(leaf) => {
706 let x = self.leaf_expr(&leaf)?;
707 Ok(if x.ty() == ty {
708 x
709 } else {
710 Expr::Resize(Box::new(x), ty)
711 })
712 }
713 Sel::Pick { index, low, elems } => {
714 let elems = elems
715 .into_iter()
716 .map(|s| self.sel_expr(s, ty))
717 .collect::<Result<Vec<_>, _>>()?;
718 Ok(Expr::Array(ArrayNode::Pick {
719 index: Box::new(index),
720 low,
721 elems,
722 ty,
723 }))
724 }
725 }
726 }
727
728 pub(super) fn aggregate_value(&mut self, chain: Chain<'_>, ty: Ty) -> Result<Expr, BuildError> {
730 let sel = self.resolve_chain(&chain)?;
731 self.sel_expr(sel, ty)
732 }
733
734 pub(super) fn aggregate_bitstream(&mut self, chain: Chain<'_>) -> Result<Expr, BuildError> {
739 let mut parts = Vec::new();
740 let mut cur = self.cursor_of(chain.root)?;
741 let mut rest: &[RawStep<'_>] = &chain.steps;
744 while let Some((first, tail)) = rest.split_first() {
745 cur = match *first {
746 RawStep::Field(i) => cur.field(i)?,
747 RawStep::Const(i) => cur.index(i)?,
748 RawStep::Index(e) => {
749 let ix = self.expr(e)?;
750 let i = Self::constant_index(&ix).ok_or_else(|| {
751 unsupported("a bit-stream cast of an element at a computed index", "C2")
752 })?;
753 cur.index(i)?
754 }
755 };
756 rest = tail;
757 }
758 self.stream_leaves(cur, &mut parts)?;
759 let w = parts.iter().map(|p| p.ty().width).sum();
760 Ok(Expr::Concat(parts, Ty::unsigned(w)))
761 }
762
763 fn stream_leaves(&mut self, cur: Cursor<'_>, out: &mut Vec<Expr>) -> Result<(), BuildError> {
764 let t = cur.ty.canonical();
765 if t.is_unpacked_struct() {
766 for i in 0..t.fields().count() {
767 self.stream_leaves(cur.field(i as u32)?, out)?;
768 }
769 } else if array_shape(t) == Some(ArrayShape::Fixed) {
770 let r = t.fixed_unpacked_array_range();
771 let (l, h) = (i64::from(r.left), i64::from(r.right));
772 let idx: Vec<i64> = if l <= h {
773 (l..=h).collect()
774 } else {
775 (h..=l).rev().collect()
776 };
777 for i in idx {
778 self.stream_leaves(cur.index(i)?, out)?;
779 }
780 } else {
781 let leaf = cur.leaf()?;
782 let LeafKind::Int(ty) = leaf.kind else {
783 return Err(BuildError::NonIntegral {
784 what: format!(
785 "`{}` of type `{}` in a bit-stream cast",
786 leaf.name,
787 leaf.vt.to_sv_string()
788 ),
789 });
790 };
791 let x = self.leaf_expr(&leaf)?;
792 out.push(Expr::Resize(Box::new(x), Ty::unsigned(ty.width)));
793 }
794 Ok(())
795 }
796
797 pub(super) fn aggregate_element(
800 &mut self,
801 arr: Expression<'_>,
802 i: i64,
803 ty: Ty,
804 ) -> Result<Expr, BuildError> {
805 let mut chain = aggregate_chain(arr, &self.forced_roots).ok_or_else(|| {
806 unsupported(
807 "a method over an unpacked array that is not a property (or a member of a struct \
808 property) of this class",
809 "C14",
810 )
811 })?;
812 chain.steps.push(RawStep::Const(i));
813 self.aggregate_value(chain, ty)
814 }
815
816 pub(super) fn real_aggregate(
818 &mut self,
819 chain: Chain<'_>,
820 kind: Fk,
821 ) -> Result<RealExpr, BuildError> {
822 let leaf = match self.resolve_chain(&chain)? {
823 Sel::Leaf(leaf) => leaf,
824 Sel::Pick { .. } => {
825 return Err(BuildError::RealConstraint {
826 what: "a real element of an array at a computed index (only an integral \
827 element can be chosen by a state or loop variable)"
828 .into(),
829 });
830 }
831 };
832 if !matches!(leaf.kind, LeafKind::Real(_)) {
833 return Err(missing(format!("a real value for `{}`", leaf.name)));
834 }
835 if leaf.mode == RandMode::None {
836 let st = self.leaf_state(&leaf, state_ty(kind));
837 return self.real_state(st, kind);
838 }
839 let key = (leaf.root.clone(), leaf.path.clone());
840 let &id = self
841 .real_leaf_ix
842 .get(&key)
843 .ok_or_else(|| Self::undeclared(&leaf))?;
844 Ok(RealExpr::Var(id))
845 }
846}