1use std::collections::HashMap;
2
3use rue_types::{BuiltinTypes, TypeId};
4
5use crate::{Database, HirId, SymbolId, TypePath};
6
7pub type Mappings = HashMap<SymbolId, HashMap<Vec<TypePath>, TypeId>>;
8
9#[derive(Debug, Clone)]
10#[must_use]
11pub struct Value {
12 pub hir: HirId,
13 pub ty: TypeId,
14 pub reference: Option<SymbolPath>,
15 pub then_map: Mappings,
16 pub else_map: Mappings,
17}
18
19impl Value {
20 pub fn new(hir: HirId, ty: TypeId) -> Self {
21 Self {
22 hir,
23 ty,
24 reference: None,
25 then_map: Mappings::new(),
26 else_map: Mappings::new(),
27 }
28 }
29
30 pub fn with_hir(self, hir: HirId) -> Self {
31 Self { hir, ..self }
32 }
33
34 pub fn with_type(self, ty: TypeId) -> Self {
35 Self { ty, ..self }
36 }
37
38 pub fn with_reference(self, reference: SymbolPath) -> Self {
39 Self {
40 reference: Some(reference),
41 ..self
42 }
43 }
44
45 pub fn with_mappings(self, then_map: Mappings, else_map: Mappings) -> Self {
46 Self {
47 then_map,
48 else_map,
49 ..self
50 }
51 }
52
53 pub fn flip_mappings(self) -> Self {
54 Self {
55 then_map: self.else_map,
56 else_map: self.then_map,
57 ..self
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
63pub struct SymbolPath {
64 pub symbol: SymbolId,
65 pub path: Vec<TypePath>,
66}
67
68impl SymbolPath {
69 pub fn new(symbol: SymbolId, path: Vec<TypePath>) -> Self {
70 Self { symbol, path }
71 }
72}
73
74pub fn merge_mappings(
79 db: &mut Database,
80 builtins: &BuiltinTypes,
81 a: &Mappings,
82 b: &Mappings,
83) -> Mappings {
84 let mut map = a.clone();
85
86 for (&symbol, mappings) in b {
87 for (path, &ty) in mappings {
88 let paths = map.entry(symbol).or_default();
89
90 let ty = if let Some(&existing) = paths.get(path) {
91 rue_types::intersect(db.types_mut(), builtins, existing, ty)
92 } else {
93 ty
94 };
95
96 paths.insert(path.clone(), ty);
97 }
98 }
99
100 map
101}
102
103#[cfg(test)]
104mod tests {
105 use rue_types::{Pair, Type, Union};
106
107 use crate::Symbol;
108
109 use super::*;
110
111 fn mappings(entries: &[(SymbolId, Vec<TypePath>, TypeId)]) -> Mappings {
112 let mut map = Mappings::new();
113 for (symbol, path, ty) in entries {
114 map.entry(*symbol).or_default().insert(path.clone(), *ty);
115 }
116 map
117 }
118
119 fn setup() -> (Database, BuiltinTypes, SymbolId, SymbolId) {
120 let mut db = Database::new();
121 let builtins = BuiltinTypes::new(db.types_mut());
122 let x = db.alloc_symbol(Symbol::Unresolved);
123 let y = db.alloc_symbol(Symbol::Unresolved);
124 (db, builtins, x, y)
125 }
126
127 #[test]
128 fn test_merge_disjoint() {
129 let (mut db, builtins, x, y) = setup();
130
131 let left = mappings(&[(x, vec![], builtins.int)]);
132 let right = mappings(&[
133 (y, vec![], builtins.bytes32),
134 (x, vec![TypePath::First], builtins.nil),
135 ]);
136 let merged = merge_mappings(&mut db, &builtins, &left, &right);
137
138 assert_eq!(merged.len(), 2);
139 assert_eq!(merged[&x].len(), 2);
140 assert_eq!(merged[&x][&vec![]], builtins.int);
141 assert_eq!(merged[&x][&vec![TypePath::First]], builtins.nil);
142 assert_eq!(merged[&y][&vec![]], builtins.bytes32);
143 }
144
145 #[test]
146 fn test_merge_empty() {
147 let (mut db, builtins, x, _) = setup();
148
149 let left = mappings(&[(x, vec![], builtins.int)]);
150 let merged = merge_mappings(&mut db, &builtins, &left, &Mappings::new());
151 assert_eq!(merged[&x][&vec![]], builtins.int);
152
153 let merged = merge_mappings(&mut db, &builtins, &Mappings::new(), &left);
154 assert_eq!(merged[&x][&vec![]], builtins.int);
155 }
156
157 #[test]
158 fn test_merge_same_path_intersects() {
159 let (mut db, builtins, x, _) = setup();
160
161 let pair = db.alloc_type(Type::Pair(Pair::new(builtins.int, builtins.int)));
163 let not_nil = db.alloc_type(Type::Union(Union::new(vec![builtins.bytes32, pair])));
164 let not_bytes32 = db.alloc_type(Type::Union(Union::new(vec![builtins.nil, pair])));
165
166 let left = mappings(&[(x, vec![], not_nil)]);
167 let right = mappings(&[(x, vec![], not_bytes32)]);
168 let merged = merge_mappings(&mut db, &builtins, &left, &right);
169
170 let ty = merged[&x][&vec![]];
171 assert_eq!(rue_types::stringify(db.types_mut(), ty), "(Int, Int)");
172 }
173
174 #[test]
175 fn test_merge_same_path_subset() {
176 let (mut db, builtins, x, _) = setup();
177
178 let atom_or_nil = db.alloc_type(Type::Union(Union::new(vec![
179 builtins.bytes32,
180 builtins.nil,
181 ])));
182
183 let left = mappings(&[(x, vec![TypePath::Rest], builtins.bytes32)]);
185 let right = mappings(&[(x, vec![TypePath::Rest], atom_or_nil)]);
186 assert_eq!(
187 merge_mappings(&mut db, &builtins, &left, &right)[&x][&vec![TypePath::Rest]],
188 builtins.bytes32
189 );
190 assert_eq!(
191 merge_mappings(&mut db, &builtins, &right, &left)[&x][&vec![TypePath::Rest]],
192 builtins.bytes32
193 );
194 }
195
196 #[test]
197 fn test_merge_same_path_disjoint() {
198 let (mut db, builtins, x, _) = setup();
199
200 let pair = db.alloc_type(Type::Pair(Pair::new(builtins.int, builtins.int)));
202 let left = mappings(&[(x, vec![], builtins.bytes32)]);
203 let right = mappings(&[(x, vec![], pair)]);
204 let merged = merge_mappings(&mut db, &builtins, &left, &right);
205
206 assert_eq!(merged[&x][&vec![]], builtins.never);
207 }
208}