1use crate::core::optimized_types::OptimizedAllocationInfo;
8use crate::core::string_pool::intern_string;
9use crate::core::types::AllocationInfo;
10use std::sync::Arc;
11
12pub struct AllocationInfoAdapter {
17 inner: OptimizedAllocationInfo,
18}
19
20impl AllocationInfoAdapter {
21 pub fn new(optimized: OptimizedAllocationInfo) -> Self {
23 Self { inner: optimized }
24 }
25
26 pub fn from_allocation(ptr: usize, size: usize) -> Self {
28 Self {
29 inner: OptimizedAllocationInfo::new(ptr, size),
30 }
31 }
32
33 pub fn inner(&self) -> &OptimizedAllocationInfo {
35 &self.inner
36 }
37
38 pub fn inner_mut(&mut self) -> &mut OptimizedAllocationInfo {
40 &mut self.inner
41 }
42
43 pub fn to_allocation_info(&self) -> AllocationInfo {
45 self.inner.clone().into()
46 }
47
48 pub fn from_allocation_info(info: AllocationInfo) -> Self {
50 Self {
51 inner: OptimizedAllocationInfo::from(info),
52 }
53 }
54
55 pub fn ptr(&self) -> usize {
58 self.inner.ptr
59 }
60
61 pub fn size(&self) -> usize {
62 self.inner.size
63 }
64
65 pub fn var_name(&self) -> Option<String> {
66 self.inner.var_name.as_ref().map(|s| s.to_string())
67 }
68
69 pub fn set_var_name(&mut self, name: Option<String>) {
70 self.inner.var_name = name.map(|s| intern_string(&s));
71 }
72
73 pub fn type_name(&self) -> Option<String> {
74 self.inner.type_name.as_ref().map(|s| s.to_string())
75 }
76
77 pub fn set_type_name(&mut self, name: Option<String>) {
78 self.inner.type_name = name.map(|s| intern_string(&s));
79 }
80
81 pub fn scope_name(&self) -> Option<String> {
82 self.inner.scope_name.as_ref().map(|s| s.to_string())
83 }
84
85 pub fn set_scope_name(&mut self, name: Option<String>) {
86 self.inner.scope_name = name.map(|s| intern_string(&s));
87 }
88
89 pub fn timestamp_alloc(&self) -> u64 {
90 self.inner.timestamp_alloc
91 }
92
93 pub fn timestamp_dealloc(&self) -> Option<u64> {
94 self.inner.timestamp_dealloc
95 }
96
97 pub fn set_timestamp_dealloc(&mut self, timestamp: Option<u64>) {
98 self.inner.timestamp_dealloc = timestamp;
99 if let Some(dealloc) = timestamp {
100 self.inner.lifetime_ms = Some((dealloc - self.inner.timestamp_alloc) / 1_000_000);
101 }
102 }
103
104 pub fn thread_id(&self) -> String {
105 self.inner.thread_id.to_string()
106 }
107
108 pub fn set_thread_id(&mut self, id: String) {
109 self.inner.thread_id = intern_string(&id);
110 }
111
112 pub fn borrow_count(&self) -> usize {
113 self.inner.borrow_count
114 }
115
116 pub fn set_borrow_count(&mut self, count: usize) {
117 self.inner.borrow_count = count;
118 }
119
120 pub fn stack_trace(&self) -> Option<Vec<String>> {
121 self.inner
122 .stack_trace
123 .as_ref()
124 .map(|trace| trace.iter().map(|frame| frame.to_string()).collect())
125 }
126
127 pub fn set_stack_trace(&mut self, trace: Option<Vec<String>>) {
128 self.inner.stack_trace =
129 trace.map(|t| Arc::new(t.into_iter().map(|frame| intern_string(&frame)).collect()));
130 }
131
132 pub fn is_leaked(&self) -> bool {
133 self.inner.is_leaked
134 }
135
136 pub fn set_is_leaked(&mut self, leaked: bool) {
137 self.inner.is_leaked = leaked;
138 }
139
140 pub fn lifetime_ms(&self) -> Option<u64> {
141 self.inner.lifetime_ms
142 }
143
144 pub fn is_active(&self) -> bool {
145 self.inner.is_active()
146 }
147
148 pub fn mark_deallocated(&mut self) {
149 self.inner.mark_deallocated();
150 }
151}
152
153impl Clone for AllocationInfoAdapter {
154 fn clone(&self) -> Self {
155 Self {
156 inner: self.inner.clone(),
157 }
158 }
159}
160
161impl std::fmt::Debug for AllocationInfoAdapter {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 f.debug_struct("AllocationInfoAdapter")
164 .field("inner", &self.inner)
165 .finish()
166 }
167}
168
169impl PartialEq for AllocationInfoAdapter {
170 fn eq(&self, other: &Self) -> bool {
171 self.inner == other.inner
172 }
173}
174
175impl From<AllocationInfo> for AllocationInfoAdapter {
176 fn from(info: AllocationInfo) -> Self {
177 Self::from_allocation_info(info)
178 }
179}
180
181impl From<AllocationInfoAdapter> for AllocationInfo {
182 fn from(adapter: AllocationInfoAdapter) -> Self {
183 adapter.to_allocation_info()
184 }
185}
186
187impl From<OptimizedAllocationInfo> for AllocationInfoAdapter {
188 fn from(optimized: OptimizedAllocationInfo) -> Self {
189 Self::new(optimized)
190 }
191}
192
193impl From<AllocationInfoAdapter> for OptimizedAllocationInfo {
194 fn from(adapter: AllocationInfoAdapter) -> Self {
195 adapter.inner
196 }
197}
198
199pub struct AllocationCollection {
201 allocations: Vec<AllocationInfoAdapter>,
202}
203
204impl AllocationCollection {
205 pub fn new() -> Self {
207 Self {
208 allocations: Vec::new(),
209 }
210 }
211
212 pub fn from_allocation_infos(infos: Vec<AllocationInfo>) -> Self {
214 Self {
215 allocations: infos.into_iter().map(AllocationInfoAdapter::from).collect(),
216 }
217 }
218
219 pub fn from_optimized_infos(infos: Vec<OptimizedAllocationInfo>) -> Self {
221 Self {
222 allocations: infos.into_iter().map(AllocationInfoAdapter::from).collect(),
223 }
224 }
225
226 pub fn push(&mut self, allocation: AllocationInfoAdapter) {
228 self.allocations.push(allocation);
229 }
230
231 pub fn len(&self) -> usize {
233 self.allocations.len()
234 }
235
236 pub fn is_empty(&self) -> bool {
238 self.allocations.is_empty()
239 }
240
241 pub fn iter(&self) -> std::slice::Iter<'_, AllocationInfoAdapter> {
243 self.allocations.iter()
244 }
245
246 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, AllocationInfoAdapter> {
248 self.allocations.iter_mut()
249 }
250
251 pub fn to_allocation_infos(&self) -> Vec<AllocationInfo> {
253 self.allocations
254 .iter()
255 .map(|a| a.to_allocation_info())
256 .collect()
257 }
258
259 pub fn to_optimized_infos(&self) -> Vec<OptimizedAllocationInfo> {
261 self.allocations.iter().map(|a| a.inner.clone()).collect()
262 }
263
264 pub fn memory_stats(&self) -> CollectionMemoryStats {
266 let total_size: usize = self.allocations.iter().map(|a| a.size()).sum();
267 let active_count = self.allocations.iter().filter(|a| a.is_active()).count();
268 let leaked_count = self.allocations.iter().filter(|a| a.is_leaked()).count();
269
270 CollectionMemoryStats {
271 total_allocations: self.allocations.len(),
272 active_allocations: active_count,
273 leaked_allocations: leaked_count,
274 total_size_bytes: total_size,
275 }
276 }
277}
278
279impl Default for AllocationCollection {
280 fn default() -> Self {
281 Self::new()
282 }
283}
284
285#[derive(Debug, Clone)]
287pub struct CollectionMemoryStats {
288 pub total_allocations: usize,
289 pub active_allocations: usize,
290 pub leaked_allocations: usize,
291 pub total_size_bytes: usize,
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297 use crate::core::string_pool::clear_string_pool;
298
299 #[test]
300 fn test_allocation_adapter_basic_operations() {
301 clear_string_pool();
302
303 let mut adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
304
305 assert_eq!(adapter.ptr(), 0x1000);
306 assert_eq!(adapter.size(), 64);
307 assert!(adapter.is_active());
308
309 adapter.set_var_name(Some("test_var".to_string()));
310 adapter.set_type_name(Some("TestType".to_string()));
311
312 assert_eq!(adapter.var_name(), Some("test_var".to_string()));
313 assert_eq!(adapter.type_name(), Some("TestType".to_string()));
314
315 adapter.mark_deallocated();
316 assert!(!adapter.is_active());
317 assert!(adapter.lifetime_ms().is_some());
318 }
319
320 #[test]
321 fn test_conversion_compatibility() {
322 clear_string_pool();
323
324 let original = AllocationInfo {
326 ptr: 0x1000,
327 size: 64,
328 var_name: Some("test".to_string()),
329 type_name: Some("TestType".to_string()),
330 scope_name: None,
331 timestamp_alloc: 12345,
332 timestamp_dealloc: None,
333 thread_id: "thread-1".to_string(),
334 borrow_count: 0,
335 stack_trace: None,
336 is_leaked: false,
337 lifetime_ms: None,
338 borrow_info: None,
339 clone_info: None,
340 ownership_history_available: false,
341 smart_pointer_info: None,
342 memory_layout: None,
343 generic_info: None,
344 dynamic_type_info: None,
345 runtime_state: None,
346 stack_allocation: None,
347 temporary_object: None,
348 fragmentation_analysis: None,
349 generic_instantiation: None,
350 type_relationships: None,
351 type_usage: None,
352 function_call_tracking: None,
353 lifecycle_tracking: None,
354 access_tracking: None,
355 drop_chain_analysis: None,
356 };
357
358 let adapter = AllocationInfoAdapter::from(original.clone());
360 let converted_back = AllocationInfo::from(adapter);
361
362 assert_eq!(converted_back.ptr, original.ptr);
363 assert_eq!(converted_back.size, original.size);
364 assert_eq!(converted_back.var_name, original.var_name);
365 assert_eq!(converted_back.type_name, original.type_name);
366 }
367
368 #[test]
369 fn test_allocation_collection() {
370 clear_string_pool();
371
372 let mut collection = AllocationCollection::new();
373
374 let adapter1 = AllocationInfoAdapter::from_allocation(0x1000, 64);
375 let adapter2 = AllocationInfoAdapter::from_allocation(0x2000, 128);
376
377 collection.push(adapter1);
378 collection.push(adapter2);
379
380 assert_eq!(collection.len(), 2);
381 assert!(!collection.is_empty());
382
383 let stats = collection.memory_stats();
384 assert_eq!(stats.total_allocations, 2);
385 assert_eq!(stats.active_allocations, 2);
386 assert_eq!(stats.total_size_bytes, 192); }
388
389 #[test]
390 fn test_string_interning_through_adapter() {
391 clear_string_pool();
392
393 let mut adapter1 = AllocationInfoAdapter::from_allocation(0x1000, 64);
394 let mut adapter2 = AllocationInfoAdapter::from_allocation(0x2000, 128);
395
396 adapter1.set_var_name(Some("shared_name".to_string()));
397 adapter2.set_var_name(Some("shared_name".to_string()));
398
399 assert!(Arc::ptr_eq(
401 adapter1
402 .inner()
403 .var_name
404 .as_ref()
405 .expect("Missing variable name"),
406 adapter2
407 .inner()
408 .var_name
409 .as_ref()
410 .expect("Missing variable name")
411 ));
412 }
413
414 #[test]
415 fn test_allocation_adapter_comprehensive() {
416 clear_string_pool();
417
418 let mut adapter = AllocationInfoAdapter::from_allocation(0x5000, 256);
419
420 adapter.set_var_name(Some("comprehensive_var".to_string()));
422 adapter.set_type_name(Some("Vec<String>".to_string()));
423 adapter.set_scope_name(Some("main".to_string()));
424 adapter.set_thread_id("worker-thread".to_string());
425 adapter.set_borrow_count(5);
426 adapter.set_is_leaked(true);
427
428 assert_eq!(adapter.ptr(), 0x5000);
430 assert_eq!(adapter.size(), 256);
431 assert_eq!(adapter.var_name(), Some("comprehensive_var".to_string()));
432 assert_eq!(adapter.type_name(), Some("Vec<String>".to_string()));
433 assert_eq!(adapter.scope_name(), Some("main".to_string()));
434 assert_eq!(adapter.thread_id(), "worker-thread");
435 assert_eq!(adapter.borrow_count(), 5);
436 assert!(adapter.is_leaked());
437 assert!(adapter.is_active());
438
439 adapter.mark_deallocated();
441 assert!(!adapter.is_active());
442 assert!(adapter.lifetime_ms().is_some());
443 assert!(adapter.timestamp_dealloc().is_some());
444 }
445
446 #[test]
447 fn test_allocation_adapter_edge_cases() {
448 clear_string_pool();
449
450 let mut zero_adapter = AllocationInfoAdapter::from_allocation(0x0, 0);
452 assert_eq!(zero_adapter.ptr(), 0x0);
453 assert_eq!(zero_adapter.size(), 0);
454 assert!(zero_adapter.is_active());
455
456 let mut max_adapter = AllocationInfoAdapter::from_allocation(usize::MAX, usize::MAX);
458 assert_eq!(max_adapter.ptr(), usize::MAX);
459 assert_eq!(max_adapter.size(), usize::MAX);
460
461 zero_adapter.set_var_name(Some("".to_string()));
463 zero_adapter.set_type_name(Some("".to_string()));
464 assert_eq!(zero_adapter.var_name(), Some("".to_string()));
465 assert_eq!(zero_adapter.type_name(), Some("".to_string()));
466
467 zero_adapter.set_var_name(None);
469 zero_adapter.set_type_name(None);
470 assert_eq!(zero_adapter.var_name(), None);
471 assert_eq!(zero_adapter.type_name(), None);
472
473 let long_string = "a".repeat(10000);
475 max_adapter.set_var_name(Some(long_string.clone()));
476 assert_eq!(max_adapter.var_name(), Some(long_string));
477 }
478
479 #[test]
480 fn test_allocation_collection_comprehensive() {
481 clear_string_pool();
482
483 let mut collection = AllocationCollection::new();
484
485 assert_eq!(collection.len(), 0);
487 assert!(collection.is_empty());
488 let empty_stats = collection.memory_stats();
489 assert_eq!(empty_stats.total_allocations, 0);
490 assert_eq!(empty_stats.active_allocations, 0);
491 assert_eq!(empty_stats.total_size_bytes, 0);
492
493 let adapter1 = AllocationInfoAdapter::from_allocation(0x1000, 64);
495 let mut adapter2 = AllocationInfoAdapter::from_allocation(0x2000, 128);
496 let mut adapter3 = AllocationInfoAdapter::from_allocation(0x3000, 256);
497
498 adapter2.set_is_leaked(true);
500
501 adapter3.mark_deallocated();
503
504 collection.push(adapter1);
505 collection.push(adapter2);
506 collection.push(adapter3);
507
508 assert_eq!(collection.len(), 3);
509 assert!(!collection.is_empty());
510
511 let stats = collection.memory_stats();
512 assert_eq!(stats.total_allocations, 3);
513 assert_eq!(stats.active_allocations, 2); assert_eq!(stats.total_size_bytes, 448); let mut count = 0;
518 for adapter in collection.iter() {
519 assert!(adapter.ptr() >= 0x1000);
520 count += 1;
521 }
522 assert_eq!(count, 3);
523
524 for adapter in collection.iter_mut() {
526 adapter.set_borrow_count(adapter.borrow_count() + 1);
527 }
528
529 for adapter in collection.iter() {
531 assert_eq!(adapter.borrow_count(), 1);
532 }
533 }
534
535 #[test]
536 fn test_allocation_collection_operations() {
537 clear_string_pool();
538
539 let mut collection = AllocationCollection::new();
540
541 let capacity_collection = AllocationCollection::new();
543 assert_eq!(capacity_collection.len(), 0);
544
545 for i in 0..50 {
547 let adapter = AllocationInfoAdapter::from_allocation(0x1000 + i * 0x100, 64 + i);
548 collection.push(adapter);
549 }
550
551 assert_eq!(collection.len(), 50);
552
553 let stats = collection.memory_stats();
554 assert_eq!(stats.total_allocations, 50);
555 assert_eq!(stats.active_allocations, 50);
556
557 collection.allocations.clear();
559 assert_eq!(collection.len(), 0);
560 assert!(collection.is_empty());
561 }
562
563 #[test]
564 fn test_allocation_adapter_timestamps() {
565 clear_string_pool();
566
567 let adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
568 let alloc_time = adapter.timestamp_alloc();
569 assert!(alloc_time > 0);
570
571 assert_eq!(adapter.timestamp_dealloc(), None);
573
574 let mut mutable_adapter = adapter;
576 mutable_adapter.mark_deallocated();
577 let dealloc_time = mutable_adapter.timestamp_dealloc();
578 assert!(dealloc_time.is_some());
579 assert!(dealloc_time.unwrap() >= alloc_time);
580
581 let lifetime = mutable_adapter.lifetime_ms();
583 assert!(lifetime.is_some());
584 }
585
586 #[test]
587 fn test_allocation_adapter_borrow_tracking() {
588 clear_string_pool();
589
590 let mut adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
591
592 assert_eq!(adapter.borrow_count(), 0);
594
595 adapter.set_borrow_count(5);
597 assert_eq!(adapter.borrow_count(), 5);
598
599 adapter.set_borrow_count(adapter.borrow_count() + 1);
601 assert_eq!(adapter.borrow_count(), 6);
602
603 adapter.set_borrow_count(usize::MAX);
605 assert_eq!(adapter.borrow_count(), usize::MAX);
606 }
607
608 #[test]
609 fn test_allocation_adapter_leak_detection() {
610 clear_string_pool();
611
612 let mut adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
613
614 assert!(!adapter.is_leaked());
616
617 adapter.set_is_leaked(true);
619 assert!(adapter.is_leaked());
620
621 adapter.set_is_leaked(false);
623 assert!(!adapter.is_leaked());
624 }
625
626 #[test]
627 fn test_allocation_adapter_thread_tracking() {
628 clear_string_pool();
629
630 let mut adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
631
632 let default_thread = adapter.thread_id();
634 assert!(!default_thread.is_empty());
635
636 adapter.set_thread_id("custom-thread-123".to_string());
638 assert_eq!(adapter.thread_id(), "custom-thread-123");
639
640 adapter.set_thread_id("".to_string());
642 assert_eq!(adapter.thread_id(), "");
643
644 let long_thread_id = "thread-".repeat(1000);
646 adapter.set_thread_id(long_thread_id.clone());
647 assert_eq!(adapter.thread_id(), long_thread_id);
648 }
649
650 #[test]
651 fn test_allocation_adapter_conversion_roundtrip() {
652 clear_string_pool();
653
654 let original = AllocationInfo {
656 ptr: 0x12345678,
657 size: 1024,
658 var_name: Some("test_variable".to_string()),
659 type_name: Some("HashMap<String, Vec<i32>>".to_string()),
660 scope_name: Some("function_scope".to_string()),
661 timestamp_alloc: 1000000,
662 timestamp_dealloc: Some(2000000),
663 thread_id: "main-thread".to_string(),
664 borrow_count: 10,
665 stack_trace: Some(vec!["frame1".to_string(), "frame2".to_string()]),
666 is_leaked: true,
667 lifetime_ms: Some(1000),
668 borrow_info: None,
669 clone_info: None,
670 ownership_history_available: true,
671 smart_pointer_info: None,
672 memory_layout: None,
673 generic_info: None,
674 dynamic_type_info: None,
675 runtime_state: None,
676 stack_allocation: None,
677 temporary_object: None,
678 fragmentation_analysis: None,
679 generic_instantiation: None,
680 type_relationships: None,
681 type_usage: None,
682 function_call_tracking: None,
683 lifecycle_tracking: None,
684 access_tracking: None,
685 drop_chain_analysis: None,
686 };
687
688 let adapter = AllocationInfoAdapter::from(original.clone());
690 let converted = AllocationInfo::from(adapter);
691
692 assert_eq!(converted.ptr, original.ptr);
694 assert_eq!(converted.size, original.size);
695 assert_eq!(converted.var_name, original.var_name);
696 assert_eq!(converted.type_name, original.type_name);
697 assert_eq!(converted.scope_name, original.scope_name);
698 assert_eq!(converted.timestamp_alloc, original.timestamp_alloc);
699 assert_eq!(converted.timestamp_dealloc, original.timestamp_dealloc);
700 assert_eq!(converted.thread_id, original.thread_id);
701 assert_eq!(converted.borrow_count, original.borrow_count);
702 assert_eq!(converted.is_leaked, original.is_leaked);
703 assert_eq!(converted.lifetime_ms, original.lifetime_ms);
704 assert_eq!(
705 converted.ownership_history_available,
706 original.ownership_history_available
707 );
708 }
709
710 #[test]
711 fn test_allocation_collection_memory_stats_edge_cases() {
712 clear_string_pool();
713
714 let mut collection = AllocationCollection::new();
715
716 let mut adapter1 = AllocationInfoAdapter::from_allocation(0x1000, 100);
718 let mut adapter2 = AllocationInfoAdapter::from_allocation(0x2000, 200);
719 adapter1.mark_deallocated();
720 adapter2.mark_deallocated();
721
722 collection.push(adapter1);
723 collection.push(adapter2);
724
725 let stats = collection.memory_stats();
726 assert_eq!(stats.total_allocations, 2);
727 assert_eq!(stats.active_allocations, 0); assert_eq!(stats.total_size_bytes, 300); collection.allocations.clear();
732 let mut adapter3 = AllocationInfoAdapter::from_allocation(0x3000, 150);
733 let mut adapter4 = AllocationInfoAdapter::from_allocation(0x4000, 250);
734 adapter3.set_is_leaked(true);
735 adapter4.set_is_leaked(true);
736
737 collection.push(adapter3);
738 collection.push(adapter4);
739
740 let stats = collection.memory_stats();
741 assert_eq!(stats.total_allocations, 2);
742 assert_eq!(stats.active_allocations, 2); assert_eq!(stats.total_size_bytes, 400);
744 }
745
746 #[test]
747 fn test_allocation_adapter_concurrent_access() {
748 use std::sync::{Arc, Mutex};
749 use std::thread;
750
751 clear_string_pool();
752
753 let collection = Arc::new(Mutex::new(AllocationCollection::new()));
754 let mut handles = vec![];
755
756 for i in 0..10 {
758 let collection_clone = collection.clone();
759 let handle = thread::spawn(move || {
760 let adapter = AllocationInfoAdapter::from_allocation(0x1000 + i * 0x100, 64 + i);
761 let mut coll = collection_clone.lock().expect("Failed to lock collection");
762 coll.push(adapter);
763 });
764 handles.push(handle);
765 }
766
767 for handle in handles {
768 handle.join().expect("Thread should complete");
769 }
770
771 let final_collection = collection.lock().expect("Failed to lock collection");
772 assert_eq!(final_collection.len(), 10);
773
774 let stats = final_collection.memory_stats();
775 assert_eq!(stats.total_allocations, 10);
776 assert_eq!(stats.active_allocations, 10);
777 }
778
779 #[test]
780 fn test_allocation_adapter_string_interning_efficiency() {
781 clear_string_pool();
782
783 let mut adapters = Vec::new();
784 let common_type = "std::collections::HashMap<String, Vec<i32>>";
785 let common_var_prefix = "map_instance_";
786
787 for i in 0..100 {
789 let mut adapter = AllocationInfoAdapter::from_allocation(0x1000 + i * 0x100, 64);
790 adapter.set_type_name(Some(common_type.to_string()));
791 adapter.set_var_name(Some(format!("{}{}", common_var_prefix, i)));
792 adapters.push(adapter);
793 }
794
795 for i in 1..adapters.len() {
797 assert_eq!(
798 adapters[0].type_name().unwrap(),
799 adapters[i].type_name().unwrap(),
800 "Type names should have the same content"
801 );
802 }
803
804 for adapter in &adapters {
806 assert!(adapter.var_name().unwrap().starts_with(common_var_prefix));
807 }
808
809 for adapter in &adapters {
811 assert_eq!(adapter.type_name(), Some(common_type.to_string()));
812 }
813 }
814}