1use crate::{context::Context, space::SpaceRef};
24use serde::{Deserialize, Serialize};
25use std::fmt::{Debug, Display, Formatter};
26
27#[macro_export]
34macro_rules! composite_id {
35 ($name:ident, $local:ty) => {
36 #[derive(
37 Clone,
38 Copy,
39 PartialEq,
40 Eq,
41 Hash,
42 PartialOrd,
43 Ord,
44 Default,
45 ::serde::Serialize,
46 ::serde::Deserialize,
47 )]
48 pub struct $name {
49 pub func: $crate::value::FunctionId,
50 pub local: $local,
51 }
52
53 impl $name {
54 pub const fn new(func: $crate::value::FunctionId, local: $local) -> Self {
55 Self { func, local }
56 }
57
58 #[inline]
63 pub fn localize(self, func: $crate::value::FunctionId) -> $local {
64 debug_assert_eq!(
65 self.func, func,
66 concat!(stringify!($name), "::localize: foreign id"),
67 );
68 self.local
69 }
70 }
71
72 impl ::core::fmt::Debug for $name {
73 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
74 write!(
75 f,
76 concat!(stringify!($name), "({}:{})"),
77 self.func, self.local
78 )
79 }
80 }
81
82 impl ::core::fmt::Display for $name {
83 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
84 write!(f, "{}:{}", self.func, self.local)
85 }
86 }
87 };
88}
89
90pub use block::cfg::LocalBlockId;
91pub use block::{BasicBlock, BlockId, BlockMutRef, BlockRef};
92pub use block_param::{BlockParam, BlockParamId, BlockParamMutRef, BlockParamRef, LocalParamId};
93pub use bytes::{
94 Bytes, BytesDisplay, BytesId, BytesRef, StringEncoding, decode_string, escape_decoded,
95 render_bytes_literal,
96};
97pub use function::{
98 ArgMemKind, BodyArenaKindStats, BodyArenaStats, DerivedOutput, ExternArg, ExternArgmem,
99 ExternInterface, ExternSlot, Footprint, FunctionBody, FunctionEffects, FunctionId,
100 FunctionKind, FunctionMutRef, FunctionRef, InterfaceSlot, MemoryChannelState,
101 MemoryInterfaceMap, ParamAttrs, RamBase, RamField, RamLocations, RamObject, RamRegion,
102 RegisterChannelState, RegisterEffectSets, RegisterInterfaceMap, SlotBase, WrittenSpaces,
103 WrittenSpacesState,
104};
105pub use insn::LocalInsnId;
106pub use insn::{Instruction, InstructionId, InstructionRef};
107pub use literal::{LiteralId, LiteralRef};
108pub use poison::{Poison, PoisonId, PoisonRef};
109pub use temp::{
110 LocalTempId, LocalTempSpaceId, Temp, TempId, TempRef, TempSpace, TempSpaceId, TempSpaceRef,
111};
112pub use util::named::{Named, Renameable};
113pub use varnode::{Varnode, VarnodeId, VarnodeRef, register::Register, register::RegisterId};
114
115pub mod block;
116pub mod block_param;
117pub mod bytes;
118pub mod function;
119pub mod insn;
120pub mod interner;
121pub mod literal;
122pub mod poison;
123pub mod registry;
124pub mod temp;
125pub mod util;
126pub mod varnode;
127pub mod view;
128pub mod view_mut;
129
130pub use view::{BodyView, ModuleView, QCodeView};
131pub use view_mut::QCodeMut;
132
133#[non_exhaustive]
148#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
149pub enum ValueId {
150 Literal(LiteralId),
152 Bytes(BytesId),
154 Instruction(InstructionId),
156 BasicBlock(BlockId),
158 BlockParam(BlockParamId),
160 Varnode(VarnodeId),
162 Temp(TempId),
164 Function(FunctionId),
166 Poison(PoisonId),
170}
171
172impl ValueId {
173 pub fn ty(&self) -> &'static str {
174 match self {
175 ValueId::Literal(_) => "Literal",
176 ValueId::Bytes(_) => "Bytes",
177 ValueId::Instruction(_) => "Instruction",
178 ValueId::BasicBlock(_) => "BasicBlock",
179 ValueId::BlockParam(_) => "BlockParam",
180 ValueId::Varnode(_) => "Varnode",
181 ValueId::Temp(_) => "Temp",
182 ValueId::Function(_) => "Function",
183 ValueId::Poison(_) => "Poison",
184 }
185 }
186
187 pub fn owning_function(self) -> Option<FunctionId> {
193 match self {
194 ValueId::Instruction(id) => Some(id.func),
195 ValueId::BlockParam(id) => Some(id.func),
196 ValueId::Temp(id) => Some(id.func),
197 _ => None,
198 }
199 }
200
201 pub fn name_scope_function(self) -> Option<FunctionId> {
208 match self {
209 ValueId::Instruction(id) => Some(id.func),
210 ValueId::BlockParam(id) => Some(id.func),
211 ValueId::BasicBlock(id) => Some(id.func),
212 ValueId::Temp(id) => Some(id.func),
213 _ => None,
214 }
215 }
216
217 pub fn as_literal(self) -> Option<LiteralId> {
218 if let ValueId::Literal(id) = self {
219 Some(id)
220 } else {
221 None
222 }
223 }
224
225 pub fn as_instruction(self) -> Option<InstructionId> {
226 if let ValueId::Instruction(id) = self {
227 Some(id)
228 } else {
229 None
230 }
231 }
232
233 pub fn as_block(self) -> Option<BlockId> {
234 if let ValueId::BasicBlock(id) = self {
235 Some(id)
236 } else {
237 None
238 }
239 }
240
241 pub fn as_block_param(self) -> Option<BlockParamId> {
242 if let ValueId::BlockParam(id) = self {
243 Some(id)
244 } else {
245 None
246 }
247 }
248
249 pub fn as_bytes(self) -> Option<BytesId> {
250 if let ValueId::Bytes(id) = self {
251 Some(id)
252 } else {
253 None
254 }
255 }
256
257 pub fn is_varnode(self) -> bool {
258 matches!(self, ValueId::Varnode(_))
259 }
260
261 pub fn as_varnode(self) -> Option<VarnodeId> {
262 if let ValueId::Varnode(id) = self {
263 Some(id)
264 } else {
265 None
266 }
267 }
268
269 pub fn as_temp(self) -> Option<TempId> {
270 if let ValueId::Temp(id) = self {
271 Some(id)
272 } else {
273 None
274 }
275 }
276
277 pub fn as_function(self) -> Option<FunctionId> {
278 if let ValueId::Function(id) = self {
279 Some(id)
280 } else {
281 None
282 }
283 }
284
285 pub fn as_poison(self) -> Option<PoisonId> {
286 if let ValueId::Poison(id) = self {
287 Some(id)
288 } else {
289 None
290 }
291 }
292
293 pub fn is_poison(self) -> bool {
294 matches!(self, ValueId::Poison(_))
295 }
296}
297
298impl From<LiteralId> for ValueId {
299 fn from(id: LiteralId) -> Self {
300 ValueId::Literal(id)
301 }
302}
303
304impl From<BytesId> for ValueId {
305 fn from(id: BytesId) -> Self {
306 ValueId::Bytes(id)
307 }
308}
309
310impl From<InstructionId> for ValueId {
311 fn from(id: InstructionId) -> Self {
312 ValueId::Instruction(id)
313 }
314}
315
316impl From<BlockId> for ValueId {
317 fn from(id: BlockId) -> Self {
318 ValueId::BasicBlock(id)
319 }
320}
321
322impl From<BlockParamId> for ValueId {
323 fn from(id: BlockParamId) -> Self {
324 ValueId::BlockParam(id)
325 }
326}
327
328impl From<VarnodeId> for ValueId {
329 fn from(id: VarnodeId) -> Self {
330 ValueId::Varnode(id)
331 }
332}
333
334impl From<TempId> for ValueId {
335 fn from(id: TempId) -> Self {
336 ValueId::Temp(id)
337 }
338}
339
340impl From<FunctionId> for ValueId {
341 fn from(id: FunctionId) -> Self {
342 ValueId::Function(id)
343 }
344}
345
346impl From<PoisonId> for ValueId {
347 fn from(id: PoisonId) -> Self {
348 ValueId::Poison(id)
349 }
350}
351
352impl ValueId {
353 pub fn order_key(&self) -> (u8, u32, u32) {
358 match *self {
359 ValueId::Literal(id) => (0, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
360 ValueId::Bytes(id) => (1, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
361 ValueId::Varnode(id) => (2, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
362 ValueId::Function(id) => (3, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
363 ValueId::Instruction(id) => {
364 (4, usize::from(id.func) as u32, usize::from(id.local) as u32)
365 }
366 ValueId::BasicBlock(id) => {
367 (5, usize::from(id.func) as u32, usize::from(id.local) as u32)
368 }
369 ValueId::BlockParam(id) => {
370 (6, usize::from(id.func) as u32, usize::from(id.local) as u32)
371 }
372 ValueId::Temp(id) => (7, usize::from(id.func) as u32, usize::from(id.local) as u32),
373 ValueId::Poison(id) => (8, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
374 }
375 }
376}
377
378impl Display for ValueId {
379 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
380 match *self {
381 ValueId::Literal(id) => write!(f, "Literal({})", usize::from(id)),
382 ValueId::Bytes(id) => write!(f, "Bytes({})", usize::from(id)),
383 ValueId::Varnode(id) => write!(f, "Varnode({})", usize::from(id)),
384 ValueId::Function(id) => write!(f, "Function({})", usize::from(id)),
385 ValueId::Instruction(id) => write!(f, "Instruction({id})"),
386 ValueId::BasicBlock(id) => write!(f, "BasicBlock({id})"),
387 ValueId::BlockParam(id) => write!(f, "BlockParam({id})"),
388 ValueId::Temp(id) => write!(f, "Temp({id})"),
389 ValueId::Poison(id) => write!(f, "Poison({})", usize::from(id)),
390 }
391 }
392}
393
394#[non_exhaustive]
410#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
411pub enum LocalValueId {
412 Literal(LiteralId),
414 Bytes(BytesId),
416 Instruction(LocalInsnId),
418 BasicBlock(LocalBlockId),
420 BlockParam(LocalParamId),
422 Varnode(VarnodeId),
424 Temp(LocalTempId),
426 Function(FunctionId),
428 Poison(PoisonId),
430}
431
432impl LocalValueId {
433 pub fn qualify(self, func: FunctionId) -> ValueId {
437 match self {
438 LocalValueId::Literal(id) => ValueId::Literal(id),
439 LocalValueId::Bytes(id) => ValueId::Bytes(id),
440 LocalValueId::Varnode(id) => ValueId::Varnode(id),
441 LocalValueId::Function(id) => ValueId::Function(id),
442 LocalValueId::Poison(id) => ValueId::Poison(id),
443 LocalValueId::Instruction(local) => {
444 ValueId::Instruction(InstructionId::new(func, local))
445 }
446 LocalValueId::BasicBlock(local) => ValueId::BasicBlock(BlockId::new(func, local)),
447 LocalValueId::BlockParam(local) => ValueId::BlockParam(BlockParamId::new(func, local)),
448 LocalValueId::Temp(local) => ValueId::Temp(TempId::new(func, local)),
449 }
450 }
451}
452
453impl ValueId {
454 pub fn localize(self, func: FunctionId) -> LocalValueId {
460 match self {
461 ValueId::Literal(id) => LocalValueId::Literal(id),
462 ValueId::Bytes(id) => LocalValueId::Bytes(id),
463 ValueId::Varnode(id) => LocalValueId::Varnode(id),
464 ValueId::Function(id) => LocalValueId::Function(id),
465 ValueId::Poison(id) => LocalValueId::Poison(id),
466 ValueId::Instruction(id) => {
467 debug_assert_eq!(
468 id.func, func,
469 "localize: foreign instruction operand {id:?} in function {func} \
470 (strict IR locality, ruling 2)"
471 );
472 LocalValueId::Instruction(id.local)
473 }
474 ValueId::BasicBlock(id) => {
475 debug_assert_eq!(
476 id.func, func,
477 "localize: foreign block operand {id:?} in function {func} \
478 (strict IR locality, ruling 2)"
479 );
480 LocalValueId::BasicBlock(id.local)
481 }
482 ValueId::BlockParam(id) => {
483 debug_assert_eq!(
484 id.func, func,
485 "localize: foreign block-param operand {id:?} in function {func} \
486 (strict IR locality, ruling 2)"
487 );
488 LocalValueId::BlockParam(id.local)
489 }
490 ValueId::Temp(id) => LocalValueId::Temp(id.localize(func)),
491 }
492 }
493
494 pub fn strip_func(self) -> LocalValueId {
502 match self {
503 ValueId::Literal(id) => LocalValueId::Literal(id),
504 ValueId::Bytes(id) => LocalValueId::Bytes(id),
505 ValueId::Varnode(id) => LocalValueId::Varnode(id),
506 ValueId::Function(id) => LocalValueId::Function(id),
507 ValueId::Poison(id) => LocalValueId::Poison(id),
508 ValueId::Instruction(id) => LocalValueId::Instruction(id.local),
509 ValueId::BasicBlock(id) => LocalValueId::BasicBlock(id.local),
510 ValueId::BlockParam(id) => LocalValueId::BlockParam(id.local),
511 ValueId::Temp(id) => LocalValueId::Temp(id.local),
512 }
513 }
514
515 pub fn as_function_agnostic(self) -> Option<LocalValueId> {
523 match self {
524 ValueId::Literal(id) => Some(LocalValueId::Literal(id)),
525 ValueId::Bytes(id) => Some(LocalValueId::Bytes(id)),
526 ValueId::Varnode(id) => Some(LocalValueId::Varnode(id)),
527 ValueId::Function(id) => Some(LocalValueId::Function(id)),
528 ValueId::Poison(id) => Some(LocalValueId::Poison(id)),
529 ValueId::Instruction(_)
530 | ValueId::BasicBlock(_)
531 | ValueId::BlockParam(_)
532 | ValueId::Temp(_) => None,
533 }
534 }
535}
536
537pub trait Value<'str, 'ctx>: Display {
542 fn id(&self) -> ValueId;
544
545 fn size(&self) -> usize;
548}
549
550pub enum ValueRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
556 Literal(LiteralRef<'str, 'ctx>),
557 Bytes(BytesRef<'str, 'ctx>),
558 Instruction(InstructionRef<'str, 'ctx, R>),
559 BasicBlock(BlockRef<'str, 'ctx, R>),
560 BlockParam(BlockParamRef<'str, 'ctx, R>),
561 Varnode(VarnodeRef<'str, 'ctx>),
562 Temp(TempRef<'str, 'ctx, R>),
563 Function(FunctionRef<'str, 'ctx, R>),
564 Poison(PoisonRef<'str, 'ctx>),
565}
566
567impl<R> Debug for ValueRef<'_, '_, R> {
568 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
569 match self {
570 ValueRef::Literal(_) => f.write_str("Literal"),
571 ValueRef::Bytes(_) => f.write_str("Bytes"),
572 ValueRef::Instruction(_) => f.write_str("Instruction"),
573 ValueRef::BasicBlock(_) => f.write_str("BasicBlock"),
574 ValueRef::BlockParam(_) => f.write_str("BlockParam"),
575 ValueRef::Varnode(_) => f.write_str("Varnode"),
576 ValueRef::Temp(_) => f.write_str("Temp"),
577 ValueRef::Function(_) => f.write_str("Function"),
578 ValueRef::Poison(_) => f.write_str("Poison"),
579 }
580 }
581}
582
583impl<'str, 'ctx, R> From<LiteralRef<'str, 'ctx>> for ValueRef<'str, 'ctx, R> {
584 fn from(lit_ref: LiteralRef<'str, 'ctx>) -> Self {
585 ValueRef::Literal(lit_ref)
586 }
587}
588
589impl<'str, 'ctx, R> From<BytesRef<'str, 'ctx>> for ValueRef<'str, 'ctx, R> {
590 fn from(bytes_ref: BytesRef<'str, 'ctx>) -> Self {
591 ValueRef::Bytes(bytes_ref)
592 }
593}
594
595impl<'str, 'ctx, R> From<InstructionRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
596 fn from(insn_ref: InstructionRef<'str, 'ctx, R>) -> Self {
597 ValueRef::Instruction(insn_ref)
598 }
599}
600
601impl<'str, 'ctx, R> From<BlockRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
602 fn from(bb_ref: BlockRef<'str, 'ctx, R>) -> Self {
603 ValueRef::BasicBlock(bb_ref)
604 }
605}
606
607impl<'str, 'ctx, R> From<BlockParamRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
608 fn from(param_ref: BlockParamRef<'str, 'ctx, R>) -> Self {
609 ValueRef::BlockParam(param_ref)
610 }
611}
612
613impl<'str, 'ctx, R> From<VarnodeRef<'str, 'ctx>> for ValueRef<'str, 'ctx, R> {
614 fn from(var_ref: VarnodeRef<'str, 'ctx>) -> Self {
615 ValueRef::Varnode(var_ref)
616 }
617}
618
619impl<'str, 'ctx, R> From<TempRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
620 fn from(temp_ref: TempRef<'str, 'ctx, R>) -> Self {
621 ValueRef::Temp(temp_ref)
622 }
623}
624
625impl<'str, 'ctx, R> From<FunctionRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
626 fn from(fn_ref: FunctionRef<'str, 'ctx, R>) -> Self {
627 ValueRef::Function(fn_ref)
628 }
629}
630
631impl<'str, 'ctx, R> From<PoisonRef<'str, 'ctx>> for ValueRef<'str, 'ctx, R> {
632 fn from(poison_ref: PoisonRef<'str, 'ctx>) -> Self {
633 ValueRef::Poison(poison_ref)
634 }
635}
636
637impl<'str, 'ctx> ValueRef<'str, 'ctx> {
638 pub fn new(id: ValueId, ctx: &'ctx Context<'str>) -> Self {
639 ValueRef::from_view(ModuleView::new(ctx), id)
640 }
641
642 pub fn from_id(ctx: &'ctx Context<'str>, id: ValueId) -> Self {
643 Self::new(id, ctx)
644 }
645}
646
647impl<'str: 'ctx, 'ctx, R> ValueRef<'str, 'ctx, R>
648where
649 R: QCodeView<'ctx, 'str>,
650{
651 pub fn from_view(view: R, id: ValueId) -> Self {
652 match id {
653 ValueId::Literal(id) => ValueRef::Literal(LiteralRef::from_id(view.shared(), id)),
654 ValueId::Bytes(id) => ValueRef::Bytes(BytesRef::from_id(view.shared(), id)),
655 ValueId::Varnode(id) => ValueRef::Varnode(Varnode::from_id(view.shared(), id)),
656 ValueId::Temp(id) => ValueRef::Temp(TempRef::new(view, id)),
657 ValueId::Instruction(id) => ValueRef::Instruction(InstructionRef::new(view, id)),
658 ValueId::BasicBlock(id) => ValueRef::BasicBlock(BlockRef::new(view, id)),
659 ValueId::BlockParam(id) => ValueRef::BlockParam(BlockParamRef::new(view, id)),
660 ValueId::Function(id) => ValueRef::Function(FunctionRef::new(view, id)),
661 ValueId::Poison(id) => ValueRef::Poison(PoisonRef::from_id(view.shared(), id)),
662 }
663 }
664
665 fn inner(&self) -> &dyn Value<'str, 'ctx> {
666 match self {
667 ValueRef::Literal(r) => r,
668 ValueRef::Bytes(r) => r,
669 ValueRef::Instruction(r) => r,
670 ValueRef::BasicBlock(r) => r,
671 ValueRef::BlockParam(r) => r,
672 ValueRef::Varnode(r) => r,
673 ValueRef::Temp(r) => r,
674 ValueRef::Function(r) => r,
675 ValueRef::Poison(r) => r,
676 }
677 }
678
679 pub fn space(&self) -> Option<SpaceRef<'ctx>> {
680 match self {
681 ValueRef::Varnode(v) => Some(v.space()),
682 ValueRef::Instruction(i) => i.space(),
683 ValueRef::Temp(_) => None,
684 ValueRef::Literal(_)
685 | ValueRef::Bytes(_)
686 | ValueRef::BasicBlock(_)
687 | ValueRef::BlockParam(_)
688 | ValueRef::Function(_)
689 | ValueRef::Poison(_) => None,
690 }
691 }
692
693 pub fn memory_space(&self) -> Option<crate::space::MemorySpaceId> {
697 match self {
698 ValueRef::Varnode(v) => Some(crate::space::MemorySpaceId::Shared(v.space().id)),
699 ValueRef::Instruction(i) => i.memory_space(),
700 ValueRef::Temp(t) => Some(t.memory_space()),
701 ValueRef::Literal(_)
702 | ValueRef::Bytes(_)
703 | ValueRef::BasicBlock(_)
704 | ValueRef::BlockParam(_)
705 | ValueRef::Function(_)
706 | ValueRef::Poison(_) => None,
707 }
708 }
709}
710
711impl<'str: 'ctx, 'ctx, R> Display for ValueRef<'str, 'ctx, R>
712where
713 R: QCodeView<'ctx, 'str>,
714{
715 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
716 let tokens = match self {
725 ValueRef::Literal(r) => insn::segment::value_tokens_shared(r.ctx, self.id()),
726 ValueRef::Bytes(r) => insn::segment::value_tokens_shared(r.ctx, self.id()),
727 ValueRef::Varnode(r) => insn::segment::value_tokens_shared(r.ctx, self.id()),
728 ValueRef::Poison(r) => insn::segment::value_tokens_shared(r.ctx, self.id()),
729 ValueRef::Temp(r) => insn::segment::value_tokens_view(r.view, self.id()),
730 ValueRef::Instruction(r) => insn::segment::value_tokens_view(r.view, self.id()),
731 ValueRef::BasicBlock(r) => insn::segment::value_tokens_view(r.view, self.id()),
732 ValueRef::BlockParam(r) => insn::segment::value_tokens_view(r.view, self.id()),
733 ValueRef::Function(r) => insn::segment::value_tokens_view(r.view, self.id()),
734 };
735 for token in tokens {
736 write!(f, "{}", token.text)?;
737 }
738 Ok(())
739 }
740}
741
742impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for ValueRef<'str, 'ctx, R>
743where
744 R: QCodeView<'ctx, 'str>,
745{
746 fn id(&self) -> ValueId {
747 self.inner().id()
748 }
749
750 fn size(&self) -> usize {
751 self.inner().size()
752 }
753}
754
755#[cfg(test)]
756mod local_value_id_tests {
757 use super::*;
758
759 #[test]
763 fn qualify_localize_round_trips_every_variant() {
764 let func = FunctionId::from(7usize);
765 let other = FunctionId::from(3usize);
766
767 let arena: [ValueId; 4] = [
768 ValueId::Instruction(InstructionId::new(func, LocalInsnId::from(2usize))),
769 ValueId::BasicBlock(BlockId::new(func, LocalBlockId::from(5usize))),
770 ValueId::BlockParam(BlockParamId::new(func, LocalParamId::from(1usize))),
771 ValueId::Temp(TempId::new(func, LocalTempId::from(4usize))),
772 ];
773 for id in arena {
774 assert_eq!(id.localize(func).qualify(func), id, "{id:?}");
775 }
776
777 let shared: [ValueId; 4] = [
779 ValueId::Literal(LiteralId::from(0usize)),
780 ValueId::Bytes(BytesId::from(0usize)),
781 ValueId::Varnode(VarnodeId::from(0usize)),
782 ValueId::Function(other),
783 ];
784 for id in shared {
785 assert_eq!(id.localize(func).qualify(func), id, "{id:?}");
786 assert_eq!(id.localize(func).qualify(other), id, "{id:?}");
788 }
789 }
790
791 #[test]
794 #[should_panic(expected = "strict IR locality")]
795 #[cfg(debug_assertions)]
796 fn localize_rejects_foreign_arena_id() {
797 let func = FunctionId::from(7usize);
798 let foreign = FunctionId::from(9usize);
799 let id = ValueId::Instruction(InstructionId::new(foreign, LocalInsnId::from(0usize)));
800 let _ = id.localize(func);
801 }
802
803 #[test]
804 #[should_panic(expected = "TempId::localize: foreign id")]
805 #[cfg(debug_assertions)]
806 fn localize_rejects_foreign_temporary_id() {
807 let owner = FunctionId::from(7usize);
808 let foreign = FunctionId::from(9usize);
809 let id = ValueId::Temp(TempId::new(foreign, LocalTempId::from(0usize)));
810 let _ = id.localize(owner);
811 }
812}