|
1 | 1 | use scale_info::{form::PortableForm, Field, PortableRegistry, Type, TypeDef, TypeParameter};
|
2 |
| -use std::collections::HashMap; |
| 2 | +use smallvec::{smallvec, SmallVec}; |
| 3 | +use std::collections::{hash_map::Entry, HashMap}; |
3 | 4 |
|
4 | 5 | use crate::TypegenError;
|
5 | 6 |
|
@@ -81,25 +82,50 @@ pub fn ensure_unique_type_paths(types: &mut PortableRegistry) {
|
81 | 82 | /// all type ids mentioned in the TypeDef are either:
|
82 | 83 | /// - equal
|
83 | 84 | /// - or different, but map essentially to the same generic type parameter
|
84 |
| -fn types_equal_extended_to_params(a: &Type<PortableForm>, b: &Type<PortableForm>) -> bool { |
85 |
| - let collect_params = |type_params: &[TypeParameter<PortableForm>]| { |
86 |
| - type_params |
87 |
| - .iter() |
88 |
| - .filter_map(|p| p.ty.map(|ty| (ty.id, p.name.clone()))) |
89 |
| - .collect::<HashMap<u32, String>>() |
90 |
| - }; |
| 85 | +pub(crate) fn types_equal_extended_to_params( |
| 86 | + a: &Type<PortableForm>, |
| 87 | + b: &Type<PortableForm>, |
| 88 | +) -> bool { |
| 89 | + // We map each type ID to all type params if could refer to. Type IDs can refer to multiple parameters: |
| 90 | + // E.g. Foo<A,B> can be parameterized as Foo<u8,u8>, so if 42 is the type id of u8, a field with id=42 could refer to either A or B. |
| 91 | + fn collect_params( |
| 92 | + type_params: &[TypeParameter<PortableForm>], |
| 93 | + ) -> HashMap<u32, SmallVec<[&str; 2]>> { |
| 94 | + let mut hm: HashMap<u32, SmallVec<[&str; 2]>> = HashMap::new(); |
| 95 | + for p in type_params { |
| 96 | + if let Some(ty) = &p.ty { |
| 97 | + match hm.entry(ty.id) { |
| 98 | + Entry::Occupied(mut e) => { |
| 99 | + e.get_mut().push(p.name.as_str()); |
| 100 | + } |
| 101 | + Entry::Vacant(e) => { |
| 102 | + e.insert(smallvec![p.name.as_str()]); |
| 103 | + } |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | + hm |
| 108 | + } |
91 | 109 |
|
92 | 110 | let type_params_a = collect_params(&a.type_params);
|
93 |
| - let type_params_b = collect_params(&a.type_params); |
| 111 | + let type_params_b = collect_params(&b.type_params); |
94 | 112 |
|
95 |
| - if type_params_a.len() != type_params_b.len() { |
| 113 | + if a.type_params.len() != b.type_params.len() { |
96 | 114 | return false;
|
97 | 115 | }
|
98 |
| - |
99 |
| - // returns true if the ids are the same OR if they point to the same generic parameter. |
| 116 | + // Returns true if the ids are the same OR if they point to the same generic parameter. |
100 | 117 | let ids_equal = |a: u32, b: u32| -> bool {
|
101 |
| - a == b |
102 |
| - || matches!((type_params_a.get(&a), type_params_b.get(&b)), (Some(a_name), Some(b_name)) if a_name == b_name) |
| 118 | + if a == b { |
| 119 | + return true; |
| 120 | + } |
| 121 | + let Some(a_param_names) = type_params_a.get(&a) else { |
| 122 | + return false; |
| 123 | + }; |
| 124 | + let Some(b_param_names) = type_params_b.get(&b) else { |
| 125 | + return false; |
| 126 | + }; |
| 127 | + // Check if there is any intersection, meaning that both IDs map to the same generic type param: |
| 128 | + a_param_names.iter().any(|a_p| b_param_names.contains(a_p)) |
103 | 129 | };
|
104 | 130 |
|
105 | 131 | let fields_equal = |a: &[Field<PortableForm>], b: &[Field<PortableForm>]| -> bool {
|
|
0 commit comments