1use core::fmt;
7
8use crate::alloc::fmt::TryWrite;
9use crate::alloc::prelude::*;
10use crate::alloc::{self, try_format, Box, HashMap, String, Vec};
11use crate::ast::{Span, Spanned};
12use crate::compile::meta;
13use crate::compile::{self, Assembly, AssemblyInst, ErrorKind, Location, Pool, WithSpan};
14use crate::hash;
15use crate::query::QueryInner;
16use crate::runtime::debug::{DebugArgs, DebugSignature};
17use crate::runtime::inst;
18use crate::runtime::unit::UnitEncoder;
19use crate::runtime::{
20 Address, Call, ConstValue, DebugInfo, DebugInst, Inst, Label, Protocol, Rtti, RttiKind,
21 StaticString, Unit, UnitFn,
22};
23use crate::sync::Arc;
24use crate::{Context, Diagnostics, Hash, Item, SourceId};
25
26#[derive(Debug)]
28#[allow(missing_docs)]
29#[non_exhaustive]
30pub enum LinkerError {
31 MissingFunction {
32 hash: Hash,
33 spans: Vec<(Span, SourceId)>,
34 },
35}
36
37impl fmt::Display for LinkerError {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 match self {
40 LinkerError::MissingFunction { hash, .. } => {
41 write!(f, "Missing function with hash {hash}")
42 }
43 }
44 }
45}
46
47impl core::error::Error for LinkerError {}
48
49#[derive(Debug, Default)]
51pub(crate) struct UnitBuilder {
52 reexports: HashMap<Hash, Hash>,
54 functions: hash::Map<UnitFn>,
56 functions_rev: HashMap<usize, Hash>,
58 static_strings: Vec<Arc<StaticString>>,
60 static_string_rev: HashMap<Hash, usize>,
62 static_bytes: Vec<Vec<u8>>,
64 static_bytes_rev: HashMap<Hash, usize>,
66 static_object_keys: Vec<Box<[String]>>,
73 static_object_keys_rev: HashMap<Hash, usize>,
75 drop_sets: Vec<Arc<[Address]>>,
77 drop_sets_rev: HashMap<Vec<Address>, usize>,
79 rtti: hash::Map<Arc<Rtti>>,
81 label_count: usize,
83 required_functions: HashMap<Hash, Vec<(Span, SourceId)>>,
85 debug: Option<Box<DebugInfo>>,
87 constants: hash::Map<ConstValue>,
89 hash_to_ident: HashMap<Hash, Box<str>>,
91}
92
93impl UnitBuilder {
94 pub(crate) fn drop_set(&mut self) -> DropSet<'_> {
96 DropSet {
97 builder: self,
98 addresses: Vec::new(),
99 }
100 }
101
102 pub(crate) fn insert_debug_ident(&mut self, ident: &str) -> alloc::Result<()> {
104 self.hash_to_ident
105 .try_insert(Hash::ident(ident), ident.try_into()?)?;
106 Ok(())
107 }
108
109 pub(crate) fn build<S>(mut self, span: Span, storage: S) -> compile::Result<Unit<S>> {
113 if let Some(debug) = &mut self.debug {
114 debug.functions_rev = self.functions_rev;
115 debug.hash_to_ident = self.hash_to_ident;
116 }
117
118 for (from, to) in self.reexports {
119 if let Some(info) = self.functions.get(&to) {
120 let info = *info;
121 if self
122 .functions
123 .try_insert(from, info)
124 .with_span(span)?
125 .is_some()
126 {
127 return Err(compile::Error::new(
128 span,
129 ErrorKind::FunctionConflictHash { hash: from },
130 ));
131 }
132 continue;
133 }
134
135 if let Some(value) = self.constants.get(&to) {
136 let const_value = value.try_clone()?;
137
138 if self
139 .constants
140 .try_insert(from, const_value)
141 .with_span(span)?
142 .is_some()
143 {
144 return Err(compile::Error::new(
145 span,
146 ErrorKind::ConstantConflict { hash: from },
147 ));
148 }
149
150 continue;
151 }
152
153 return Err(compile::Error::new(
154 span,
155 ErrorKind::MissingFunctionHash { hash: to },
156 ));
157 }
158
159 Ok(Unit::new(
160 storage,
161 self.functions,
162 self.static_strings,
163 self.static_bytes,
164 self.static_object_keys,
165 self.drop_sets,
166 self.rtti,
167 self.debug,
168 self.constants,
169 ))
170 }
171
172 pub(crate) fn new_static_string(
177 &mut self,
178 span: &dyn Spanned,
179 current: &str,
180 ) -> compile::Result<usize> {
181 let current = StaticString::new(current)?;
182 let hash = current.hash();
183
184 if let Some(existing_slot) = self.static_string_rev.get(&hash).copied() {
185 let Some(existing) = self.static_strings.get(existing_slot) else {
186 return Err(compile::Error::new(
187 span,
188 ErrorKind::StaticStringMissing {
189 hash,
190 slot: existing_slot,
191 },
192 ));
193 };
194
195 if ***existing != *current {
196 return Err(compile::Error::new(
197 span,
198 ErrorKind::StaticStringHashConflict {
199 hash,
200 current: (*current).try_clone()?,
201 existing: (***existing).try_clone()?,
202 },
203 ));
204 }
205
206 return Ok(existing_slot);
207 }
208
209 let new_slot = self.static_strings.len();
210 self.static_strings.try_push(Arc::try_new(current)?)?;
211 self.static_string_rev.try_insert(hash, new_slot)?;
212 Ok(new_slot)
213 }
214
215 pub(crate) fn new_static_bytes(
220 &mut self,
221 span: &dyn Spanned,
222 current: &[u8],
223 ) -> compile::Result<usize> {
224 let hash = Hash::static_bytes(current);
225
226 if let Some(existing_slot) = self.static_bytes_rev.get(&hash).copied() {
227 let existing = self.static_bytes.get(existing_slot).ok_or_else(|| {
228 compile::Error::new(
229 span,
230 ErrorKind::StaticBytesMissing {
231 hash,
232 slot: existing_slot,
233 },
234 )
235 })?;
236
237 if &**existing != current {
238 return Err(compile::Error::new(
239 span,
240 ErrorKind::StaticBytesHashConflict {
241 hash,
242 current: current.try_to_owned()?,
243 existing: existing.try_clone()?,
244 },
245 ));
246 }
247
248 return Ok(existing_slot);
249 }
250
251 let new_slot = self.static_bytes.len();
252 self.static_bytes.try_push(current.try_to_owned()?)?;
253 self.static_bytes_rev.try_insert(hash, new_slot)?;
254 Ok(new_slot)
255 }
256
257 pub(crate) fn new_static_object_keys_iter(
260 &mut self,
261 span: &dyn Spanned,
262 current: impl IntoIterator<Item: AsRef<str>>,
263 ) -> compile::Result<usize> {
264 let current = current
265 .into_iter()
266 .map(|s| s.as_ref().try_to_owned())
267 .try_collect::<alloc::Result<Box<_>>>()??;
268
269 self.new_static_object_keys(span, current)
270 }
271
272 pub(crate) fn new_static_object_keys(
275 &mut self,
276 span: &dyn Spanned,
277 current: Box<[String]>,
278 ) -> compile::Result<usize> {
279 let hash = Hash::object_keys(¤t[..]);
280
281 if let Some(existing_slot) = self.static_object_keys_rev.get(&hash).copied() {
282 let existing = self.static_object_keys.get(existing_slot).ok_or_else(|| {
283 compile::Error::new(
284 span,
285 ErrorKind::StaticObjectKeysMissing {
286 hash,
287 slot: existing_slot,
288 },
289 )
290 })?;
291
292 if *existing != current {
293 return Err(compile::Error::new(
294 span,
295 ErrorKind::StaticObjectKeysHashConflict {
296 hash,
297 current,
298 existing: existing.try_clone()?,
299 },
300 ));
301 }
302
303 return Ok(existing_slot);
304 }
305
306 let new_slot = self.static_object_keys.len();
307 self.static_object_keys.try_push(current)?;
308 self.static_object_keys_rev.try_insert(hash, new_slot)?;
309 Ok(new_slot)
310 }
311
312 pub(crate) fn insert_meta(
314 &mut self,
315 span: &dyn Spanned,
316 meta: &meta::Meta,
317 pool: &Pool,
318 query: &mut QueryInner,
319 ) -> compile::Result<()> {
320 debug_assert_eq! {
321 pool.item_type_hash(meta.item_meta.item),
322 meta.hash,
323 };
324
325 match meta.kind {
326 meta::Kind::Type { .. } => {
327 let rtti = Arc::try_new(Rtti {
328 kind: RttiKind::Empty,
329 hash: meta.hash,
330 variant_hash: Hash::EMPTY,
331 item: pool.item(meta.item_meta.item).try_to_owned()?,
332 fields: HashMap::default(),
333 })?;
334
335 self.constants
336 .try_insert(
337 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
338 ConstValue::try_from(rtti.item.try_to_string()?)?,
339 )
340 .with_span(span)?;
341
342 if self
343 .rtti
344 .try_insert(meta.hash, rtti)
345 .with_span(span)?
346 .is_some()
347 {
348 return Err(compile::Error::new(
349 span,
350 ErrorKind::TypeRttiConflict { hash: meta.hash },
351 ));
352 }
353 }
354 meta::Kind::Struct {
355 fields: meta::Fields::Empty,
356 enum_hash: Hash::EMPTY,
357 ..
358 } => {
359 let info = UnitFn::EmptyStruct { hash: meta.hash };
360
361 let signature = DebugSignature::new(
362 pool.item(meta.item_meta.item).try_to_owned()?,
363 DebugArgs::EmptyArgs,
364 );
365
366 let rtti = Arc::try_new(Rtti {
367 kind: RttiKind::Empty,
368 hash: meta.hash,
369 variant_hash: Hash::EMPTY,
370 item: pool.item(meta.item_meta.item).try_to_owned()?,
371 fields: HashMap::default(),
372 })?;
373
374 if self
375 .rtti
376 .try_insert(meta.hash, rtti)
377 .with_span(span)?
378 .is_some()
379 {
380 return Err(compile::Error::new(
381 span,
382 ErrorKind::TypeRttiConflict { hash: meta.hash },
383 ));
384 }
385
386 if self
387 .functions
388 .try_insert(meta.hash, info)
389 .with_span(span)?
390 .is_some()
391 {
392 return Err(compile::Error::new(
393 span,
394 ErrorKind::FunctionConflict {
395 existing: signature,
396 },
397 ));
398 }
399
400 self.constants
401 .try_insert(
402 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
403 ConstValue::try_from(signature.path.try_to_string()?)?,
404 )
405 .with_span(span)?;
406
407 self.debug_mut()?
408 .functions
409 .try_insert(meta.hash, signature)?;
410 }
411 meta::Kind::Struct {
412 fields: meta::Fields::Empty,
413 enum_hash,
414 ..
415 } => {
416 let rtti = Arc::try_new(Rtti {
417 kind: RttiKind::Empty,
418 hash: enum_hash,
419 variant_hash: meta.hash,
420 item: pool.item(meta.item_meta.item).try_to_owned()?,
421 fields: HashMap::default(),
422 })?;
423
424 if self
425 .rtti
426 .try_insert(meta.hash, rtti)
427 .with_span(span)?
428 .is_some()
429 {
430 return Err(compile::Error::new(
431 span,
432 ErrorKind::RttiConflict { hash: meta.hash },
433 ));
434 }
435
436 let info = UnitFn::EmptyStruct { hash: meta.hash };
437
438 let signature = DebugSignature::new(
439 pool.item(meta.item_meta.item).try_to_owned()?,
440 DebugArgs::EmptyArgs,
441 );
442
443 if self
444 .functions
445 .try_insert(meta.hash, info)
446 .with_span(span)?
447 .is_some()
448 {
449 return Err(compile::Error::new(
450 span,
451 ErrorKind::FunctionConflict {
452 existing: signature,
453 },
454 ));
455 }
456
457 self.debug_mut()?
458 .functions
459 .try_insert(meta.hash, signature)?;
460 }
461 meta::Kind::Struct {
462 fields: meta::Fields::Unnamed(args),
463 enum_hash: Hash::EMPTY,
464 ..
465 } => {
466 let info = UnitFn::TupleStruct {
467 hash: meta.hash,
468 args,
469 };
470
471 let signature = DebugSignature::new(
472 pool.item(meta.item_meta.item).try_to_owned()?,
473 DebugArgs::TupleArgs(args),
474 );
475
476 let rtti = Arc::try_new(Rtti {
477 kind: RttiKind::Tuple,
478 hash: meta.hash,
479 variant_hash: Hash::EMPTY,
480 item: pool.item(meta.item_meta.item).try_to_owned()?,
481 fields: HashMap::default(),
482 })?;
483
484 if self
485 .rtti
486 .try_insert(meta.hash, rtti)
487 .with_span(span)?
488 .is_some()
489 {
490 return Err(compile::Error::new(
491 span,
492 ErrorKind::TypeRttiConflict { hash: meta.hash },
493 ));
494 }
495
496 if self
497 .functions
498 .try_insert(meta.hash, info)
499 .with_span(span)?
500 .is_some()
501 {
502 return Err(compile::Error::new(
503 span,
504 ErrorKind::FunctionConflict {
505 existing: signature,
506 },
507 ));
508 }
509
510 self.constants
511 .try_insert(
512 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
513 ConstValue::try_from(signature.path.try_to_string()?)?,
514 )
515 .with_span(span)?;
516
517 self.debug_mut()?
518 .functions
519 .try_insert(meta.hash, signature)?;
520 }
521 meta::Kind::Struct {
522 fields: meta::Fields::Unnamed(args),
523 enum_hash,
524 ..
525 } => {
526 let rtti = Arc::try_new(Rtti {
527 kind: RttiKind::Tuple,
528 hash: enum_hash,
529 variant_hash: meta.hash,
530 item: pool.item(meta.item_meta.item).try_to_owned()?,
531 fields: HashMap::default(),
532 })?;
533
534 if self
535 .rtti
536 .try_insert(meta.hash, rtti)
537 .with_span(span)?
538 .is_some()
539 {
540 return Err(compile::Error::new(
541 span,
542 ErrorKind::RttiConflict { hash: meta.hash },
543 ));
544 }
545
546 let info = UnitFn::TupleStruct {
547 hash: meta.hash,
548 args,
549 };
550
551 let signature = DebugSignature::new(
552 pool.item(meta.item_meta.item).try_to_owned()?,
553 DebugArgs::TupleArgs(args),
554 );
555
556 if self
557 .functions
558 .try_insert(meta.hash, info)
559 .with_span(span)?
560 .is_some()
561 {
562 return Err(compile::Error::new(
563 span,
564 ErrorKind::FunctionConflict {
565 existing: signature,
566 },
567 ));
568 }
569
570 self.debug_mut()?
571 .functions
572 .try_insert(meta.hash, signature)?;
573 }
574 meta::Kind::Struct {
575 fields: meta::Fields::Named(ref named),
576 enum_hash: Hash::EMPTY,
577 ..
578 } => {
579 let rtti = Arc::try_new(Rtti {
580 kind: RttiKind::Struct,
581 hash: meta.hash,
582 variant_hash: Hash::EMPTY,
583 item: pool.item(meta.item_meta.item).try_to_owned()?,
584 fields: named.to_fields()?,
585 })?;
586
587 self.constants
588 .try_insert(
589 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
590 ConstValue::try_from(rtti.item.try_to_string()?)?,
591 )
592 .with_span(span)?;
593
594 if self
595 .rtti
596 .try_insert(meta.hash, rtti)
597 .with_span(span)?
598 .is_some()
599 {
600 return Err(compile::Error::new(
601 span,
602 ErrorKind::TypeRttiConflict { hash: meta.hash },
603 ));
604 }
605 }
606 meta::Kind::Struct {
607 fields: meta::Fields::Named(ref named),
608 enum_hash,
609 ..
610 } => {
611 let rtti = Arc::try_new(Rtti {
612 kind: RttiKind::Struct,
613 hash: enum_hash,
614 variant_hash: meta.hash,
615 item: pool.item(meta.item_meta.item).try_to_owned()?,
616 fields: named.to_fields()?,
617 })?;
618
619 if self
620 .rtti
621 .try_insert(meta.hash, rtti)
622 .with_span(span)?
623 .is_some()
624 {
625 return Err(compile::Error::new(
626 span,
627 ErrorKind::RttiConflict { hash: meta.hash },
628 ));
629 }
630 }
631 meta::Kind::Enum { .. } => {
632 let name = pool
633 .item(meta.item_meta.item)
634 .try_to_string()
635 .with_span(span)?;
636
637 self.constants
638 .try_insert(
639 Hash::associated_function(meta.hash, &Protocol::INTO_TYPE_NAME),
640 ConstValue::try_from(name)?,
641 )
642 .with_span(span)?;
643 }
644 meta::Kind::Const => {
645 let Some(const_value) = query.get_const_value(meta.hash) else {
646 return Err(compile::Error::msg(
647 span,
648 try_format!("Missing constant for hash {}", meta.hash),
649 ));
650 };
651
652 let value = const_value.try_clone().with_span(span)?;
653
654 self.constants
655 .try_insert(meta.hash, value)
656 .with_span(span)?;
657 }
658 meta::Kind::Macro => (),
659 meta::Kind::AttributeMacro => (),
660 meta::Kind::Function { .. } => (),
661 meta::Kind::Closure { .. } => (),
662 meta::Kind::AsyncBlock { .. } => (),
663 meta::Kind::ConstFn => (),
664 meta::Kind::Import { .. } => (),
665 meta::Kind::Alias { .. } => (),
666 meta::Kind::Module => (),
667 meta::Kind::Trait => (),
668 }
669
670 Ok(())
671 }
672
673 pub(crate) fn new_assembly(&self, location: Location) -> Assembly {
675 Assembly::new(location, self.label_count)
676 }
677
678 pub(crate) fn new_function_reexport(
680 &mut self,
681 location: Location,
682 item: &Item,
683 target: &Item,
684 ) -> compile::Result<()> {
685 let hash = Hash::type_hash(item);
686 let target = Hash::type_hash(target);
687
688 if self.reexports.try_insert(hash, target)?.is_some() {
689 return Err(compile::Error::new(
690 location.span,
691 ErrorKind::FunctionReExportConflict { hash },
692 ));
693 }
694
695 Ok(())
696 }
697
698 pub(crate) fn new_function(
700 &mut self,
701 location: Location,
702 item: &Item,
703 instance: Option<(Hash, &str)>,
704 args: usize,
705 captures: Option<usize>,
706 assembly: Assembly,
707 call: Call,
708 debug_args: Box<[Box<str>]>,
709 unit_storage: &mut dyn UnitEncoder,
710 size: usize,
711 ) -> compile::Result<()> {
712 tracing::trace!("instance fn: {}", item);
713
714 let offset = unit_storage.offset();
715
716 let info = UnitFn::Offset {
717 offset,
718 call,
719 args,
720 captures,
721 };
722 let signature = DebugSignature::new(item.try_to_owned()?, DebugArgs::Named(debug_args));
723
724 if let Some((type_hash, name)) = instance {
725 let instance_fn = Hash::associated_function(type_hash, name);
726
727 if self
728 .functions
729 .try_insert(instance_fn, info)
730 .with_span(location.span)?
731 .is_some()
732 {
733 return Err(compile::Error::new(
734 location.span,
735 ErrorKind::FunctionConflict {
736 existing: signature,
737 },
738 ));
739 }
740
741 self.debug_mut()?
742 .functions
743 .try_insert(instance_fn, signature.try_clone()?)?;
744 }
745
746 let hash = Hash::type_hash(item);
747
748 if self
749 .functions
750 .try_insert(hash, info)
751 .with_span(location.span)?
752 .is_some()
753 {
754 return Err(compile::Error::new(
755 location.span,
756 ErrorKind::FunctionConflict {
757 existing: signature,
758 },
759 ));
760 }
761
762 self.constants
763 .try_insert(
764 Hash::associated_function(hash, &Protocol::INTO_TYPE_NAME),
765 ConstValue::try_from(signature.path.try_to_string().with_span(location.span)?)?,
766 )
767 .with_span(location.span)?;
768
769 self.debug_mut()?.functions.try_insert(hash, signature)?;
770 self.functions_rev.try_insert(offset, hash)?;
771 self.add_assembly(location, assembly, unit_storage, size)?;
772 Ok(())
773 }
774
775 pub(crate) fn link(
780 &mut self,
781 context: &Context,
782 diagnostics: &mut Diagnostics,
783 ) -> alloc::Result<()> {
784 for (hash, spans) in &self.required_functions {
785 if self.functions.get(hash).is_none() && context.lookup_function(*hash).is_none() {
786 diagnostics.error(
787 SourceId::empty(),
788 LinkerError::MissingFunction {
789 hash: *hash,
790 spans: spans.try_clone()?,
791 },
792 )?;
793 }
794 }
795
796 Ok(())
797 }
798
799 fn debug_mut(&mut self) -> alloc::Result<&mut DebugInfo> {
801 if self.debug.is_none() {
802 self.debug = Some(Box::try_new(DebugInfo::default())?);
803 }
804
805 Ok(self.debug.as_mut().unwrap())
806 }
807
808 fn add_assembly(
810 &mut self,
811 location: Location,
812 assembly: Assembly,
813 storage: &mut dyn UnitEncoder,
814 size: usize,
815 ) -> compile::Result<()> {
816 self.label_count = assembly.label_count;
817
818 storage
819 .encode(Inst::new(inst::Kind::Allocate { size }))
820 .with_span(location.span)?;
821
822 let base = storage.extend_offsets(assembly.labels.len())?;
823
824 self.required_functions
825 .try_extend(assembly.required_functions)?;
826
827 for (offset, (_, labels)) in &assembly.labels {
828 for label in labels {
829 if let Some(jump) = label.jump() {
830 label.set_jump(storage.label_jump(base, *offset, jump));
831 }
832 }
833 }
834
835 for (pos, (inst, span)) in assembly.instructions.into_iter().enumerate() {
836 let mut comment = String::new();
837
838 let at = storage.offset();
839
840 let mut labels = Vec::new();
841
842 for label in assembly
843 .labels
844 .get(&pos)
845 .map(|e| e.1.as_slice())
846 .unwrap_or_default()
847 {
848 if let Some(index) = label.jump() {
849 storage.mark_offset(index);
850 }
851
852 labels.try_push(label.to_debug_label())?;
853 }
854
855 let build_label = |label: Label| {
856 label
857 .jump()
858 .ok_or(ErrorKind::MissingLabelLocation {
859 name: label.name,
860 index: label.index,
861 })
862 .with_span(span)
863 };
864
865 match inst {
866 AssemblyInst::Jump { label } => {
867 write!(comment, "label:{label}")?;
868 let jump = build_label(label)?;
869 storage
870 .encode(Inst::new(inst::Kind::Jump { jump }))
871 .with_span(span)?;
872 }
873 AssemblyInst::JumpIf { addr, label } => {
874 write!(comment, "label:{label}")?;
875 let jump = build_label(label)?;
876 storage
877 .encode(Inst::new(inst::Kind::JumpIf { cond: addr, jump }))
878 .with_span(span)?;
879 }
880 AssemblyInst::JumpIfNot { addr, label } => {
881 write!(comment, "label:{label}")?;
882 let jump = build_label(label)?;
883 storage
884 .encode(Inst::new(inst::Kind::JumpIfNot { cond: addr, jump }))
885 .with_span(span)?;
886 }
887 AssemblyInst::IterNext { addr, label, out } => {
888 write!(comment, "label:{label}")?;
889 let jump = build_label(label)?;
890 storage
891 .encode(Inst::new(inst::Kind::IterNext { addr, jump, out }))
892 .with_span(span)?;
893 }
894 AssemblyInst::Raw { raw } => {
895 let kind = match raw {
898 inst @ inst::Kind::Call {
899 hash,
900 addr,
901 args,
902 out,
903 } => {
904 if let Some(UnitFn::Offset { offset, call, .. }) =
905 self.functions.get(&hash)
906 {
907 inst::Kind::CallOffset {
908 offset: *offset,
909 call: *call,
910 addr,
911 args,
912 out,
913 }
914 } else {
915 inst
916 }
917 }
918 kind => kind,
919 };
920
921 storage.encode(Inst::new(kind)).with_span(span)?;
922 }
923 }
924
925 if let Some(c) = assembly.comments.get(&pos) {
926 if !comment.is_empty() {
927 comment.try_push_str("; ")?;
928 }
929
930 comment.try_push_str(c)?;
931 }
932
933 let comment = if comment.is_empty() {
934 None
935 } else {
936 Some(comment.try_into()?)
937 };
938
939 self.debug_mut()?.instructions.try_insert(
940 at,
941 DebugInst::new(location.source_id, span, comment, labels),
942 )?;
943 }
944
945 Ok(())
946 }
947}
948
949pub(crate) struct DropSet<'a> {
951 builder: &'a mut UnitBuilder,
952 addresses: Vec<Address>,
953}
954
955impl DropSet<'_> {
956 pub(crate) fn push(&mut self, addr: Address) -> alloc::Result<()> {
958 self.addresses.try_push(addr)
959 }
960
961 pub(crate) fn finish(self) -> alloc::Result<Option<usize>> {
962 if self.addresses.is_empty() {
963 return Ok(None);
964 }
965
966 if let Some(set) = self.builder.drop_sets_rev.get(&self.addresses) {
967 return Ok(Some(*set));
968 }
969
970 let set = self.builder.drop_sets.len();
971
972 self.builder
973 .drop_sets_rev
974 .try_insert(self.addresses.try_clone()?, set)?;
975 self.builder
976 .drop_sets
977 .try_push(Arc::copy_from_slice(&self.addresses[..])?)?;
978 Ok(Some(set))
979 }
980}