lisette_semantics/pattern_analysis/
normalize.rs1use crate::store::Store;
2use syntax::ast::{Literal, MatchArm, TypedPattern};
3use syntax::program::Definition;
4use syntax::types::Type;
5
6use super::NormalizedPattern::Wildcard;
7use super::inhabitance::{InhabitanceCache, is_inhabited, is_variant_inhabited};
8use super::types::Row;
9use super::types::*;
10
11fn make_type_key(name: &str, type_args: &[Type]) -> String {
12 if type_args.is_empty() {
13 name.to_string()
14 } else {
15 let args = type_args
16 .iter()
17 .map(|t| t.to_string())
18 .collect::<Vec<_>>()
19 .join(", ");
20 format!("{}<{}>", name, args)
21 }
22}
23
24pub struct NormalizationContext<'a> {
25 pub store: &'a Store,
26 pub cache: &'a InhabitanceCache,
27}
28
29pub fn normalize_arm(
30 arm: &MatchArm,
31 unions: &mut UnionTable,
32 ctx: &NormalizationContext,
33) -> Vec<Row> {
34 let typed_pattern = arm
35 .typed_pattern
36 .as_ref()
37 .expect("typed pattern should be populated during inference");
38
39 match typed_pattern {
40 TypedPattern::Or { alternatives } => alternatives
41 .iter()
42 .map(|alt| vec![normalize_typed_pattern(alt, unions, ctx)])
43 .collect(),
44 _ => {
45 vec![vec![normalize_typed_pattern(typed_pattern, unions, ctx)]]
46 }
47 }
48}
49
50pub fn normalize_typed_pattern(
51 typed_pattern: &TypedPattern,
52 unions: &mut UnionTable,
53 ctx: &NormalizationContext,
54) -> NormalizedPattern {
55 match typed_pattern {
56 TypedPattern::Wildcard => Wildcard,
57
58 TypedPattern::Literal(literal) => {
59 if let Literal::Boolean(b) = literal {
60 return normalize_boolean(*b, unions);
61 }
62
63 NormalizedPattern::Literal(literal.clone())
64 }
65
66 TypedPattern::EnumVariant {
67 enum_name,
68 variant_name,
69 fields,
70 type_args,
71 ..
72 } => {
73 let patterns = fields
74 .iter()
75 .map(|f| normalize_typed_pattern(f, unions, ctx))
76 .collect();
77
78 let type_name = make_type_key(enum_name, type_args);
79
80 if unions.get(&type_name).is_none() {
81 let alternatives = match ctx.store.get_definition(enum_name) {
82 Some(Definition::Enum {
83 variants, generics, ..
84 }) => variants
85 .iter()
86 .filter(|v| {
87 is_variant_inhabited(v, type_args, generics, ctx.store, ctx.cache)
88 })
89 .map(|v| Constructor {
90 tag_id: format!("{}.{}", enum_name, v.name),
91 arity: v.fields.len(),
92 })
93 .collect(),
94 Some(Definition::ValueEnum { variants, .. }) => {
95 let mut alts: Vec<Constructor> = variants
96 .iter()
97 .map(|v| Constructor {
98 tag_id: format!("{}.{}", enum_name, v.name),
99 arity: 0,
100 })
101 .collect();
102 alts.push(Constructor {
103 tag_id: format!("{}.__value_enum_unknown__", enum_name),
104 arity: 0,
105 });
106 alts
107 }
108 _ => vec![],
109 };
110
111 unions.insert(type_name.clone(), alternatives);
112 }
113
114 let variant_name = variant_name.rsplit('.').next().unwrap_or(variant_name);
115 let tag = format!("{}.{}", enum_name, variant_name);
116
117 NormalizedPattern::Constructor {
118 type_name,
119 tag,
120 args: patterns,
121 }
122 }
123
124 TypedPattern::EnumStructVariant {
125 enum_name,
126 variant_name,
127 variant_fields,
128 pattern_fields,
129 type_args,
130 } => {
131 let patterns = variant_fields
132 .iter()
133 .map(|f| {
134 pattern_fields
135 .iter()
136 .find_map(|(name, pattern)| {
137 if *name == f.name {
138 Some(normalize_typed_pattern(pattern, unions, ctx))
139 } else {
140 None
141 }
142 })
143 .unwrap_or(Wildcard)
144 })
145 .collect();
146
147 let type_name = make_type_key(enum_name, type_args);
148
149 if unions.get(&type_name).is_none() {
150 let alternatives = match ctx.store.get_definition(enum_name) {
151 Some(Definition::Enum {
152 variants, generics, ..
153 }) => variants
154 .iter()
155 .filter(|v| {
156 is_variant_inhabited(v, type_args, generics, ctx.store, ctx.cache)
157 })
158 .map(|v| Constructor {
159 tag_id: format!("{}.{}", enum_name, v.name),
160 arity: v.fields.len(),
161 })
162 .collect(),
163 _ => vec![],
164 };
165
166 unions.insert(type_name.clone(), alternatives);
167 }
168
169 let variant_name = variant_name.rsplit('.').next().unwrap_or(variant_name);
170 let tag = format!("{}.{}", enum_name, variant_name);
171
172 NormalizedPattern::Constructor {
173 type_name,
174 tag,
175 args: patterns,
176 }
177 }
178
179 TypedPattern::Struct {
180 struct_name,
181 struct_fields,
182 pattern_fields,
183 type_args,
184 } => {
185 let patterns = struct_fields
186 .iter()
187 .map(|f| {
188 pattern_fields
189 .iter()
190 .find_map(|(name, pattern)| {
191 if *name == f.name {
192 Some(normalize_typed_pattern(pattern, unions, ctx))
193 } else {
194 None
195 }
196 })
197 .unwrap_or(Wildcard)
198 })
199 .collect();
200
201 let type_name = make_type_key(struct_name, type_args);
202
203 if unions.get(&type_name).is_none() {
204 let is_inhabited = ctx
205 .store
206 .get_definition(struct_name)
207 .map(|definition| match definition {
208 Definition::Struct {
209 generics, fields, ..
210 } => super::inhabitance::is_struct_inhabited(
211 fields, type_args, generics, ctx.store, ctx.cache,
212 ),
213 _ => true,
214 })
215 .unwrap_or(true);
216
217 if is_inhabited {
218 let constructor = Constructor {
219 tag_id: struct_name.to_string(),
220 arity: struct_fields.len(),
221 };
222 unions.insert(type_name.clone(), vec![constructor]);
223 } else {
224 unions.insert(type_name.clone(), vec![]);
225 }
226 }
227
228 NormalizedPattern::Constructor {
229 type_name,
230 tag: struct_name.to_string(),
231 args: patterns,
232 }
233 }
234
235 TypedPattern::Slice {
236 prefix,
237 has_rest,
238 element_type,
239 } => normalize_slice(prefix, *has_rest, element_type, unions, ctx),
240
241 TypedPattern::Tuple { arity, elements } => normalize_tuple(elements, *arity, unions, ctx),
242
243 TypedPattern::Or { .. } => {
244 unreachable!("Or-pattern should be handled by normalize_arm")
245 }
246 }
247}
248
249fn normalize_slice(
262 prefix: &[TypedPattern],
263 has_rest: bool,
264 element_type: &Type,
265 unions: &mut UnionTable,
266 ctx: &NormalizationContext,
267) -> NormalizedPattern {
268 let type_name = make_type_key("Slice", std::slice::from_ref(element_type));
269 if unions.get(&type_name).is_none() {
270 let element_inhabited = is_inhabited(element_type, ctx.store, ctx.cache);
271
272 let mut constructors = vec![Constructor {
273 tag_id: "EmptySlice".to_string(),
274 arity: 0,
275 }];
276
277 if element_inhabited {
278 constructors.push(Constructor {
279 tag_id: "NonEmptySlice".to_string(),
280 arity: 2, });
282 }
283
284 unions.insert(type_name.clone(), constructors);
285 }
286
287 if prefix.is_empty() && has_rest {
288 return Wildcard;
289 }
290
291 if prefix.is_empty() && !has_rest {
292 return NormalizedPattern::Constructor {
293 type_name,
294 tag: "EmptySlice".to_string(),
295 args: vec![],
296 };
297 }
298
299 let tail = if has_rest {
300 Wildcard
301 } else {
302 NormalizedPattern::Constructor {
303 type_name: type_name.clone(),
304 tag: "EmptySlice".to_string(),
305 args: vec![],
306 }
307 };
308
309 let mut result = tail;
310 for element in prefix.iter().rev() {
311 let head = normalize_typed_pattern(element, unions, ctx);
312 result = NormalizedPattern::Constructor {
313 type_name: type_name.clone(),
314 tag: "NonEmptySlice".to_string(),
315 args: vec![head, result],
316 };
317 }
318
319 result
320}
321
322fn normalize_tuple(
323 elements: &[TypedPattern],
324 arity: usize,
325 unions: &mut UnionTable,
326 ctx: &NormalizationContext,
327) -> NormalizedPattern {
328 let type_name = format!("Tuple{}", arity);
329
330 if unions.get(&type_name).is_none() {
331 let constructor = Constructor {
332 tag_id: type_name.clone(),
333 arity,
334 };
335 unions.insert(type_name.clone(), vec![constructor]);
336 }
337
338 let patterns = elements
339 .iter()
340 .map(|e| normalize_typed_pattern(e, unions, ctx))
341 .collect();
342
343 NormalizedPattern::Constructor {
344 type_name: type_name.clone(),
345 tag: type_name,
346 args: patterns,
347 }
348}
349
350fn normalize_boolean(boolean: bool, unions: &mut UnionTable) -> NormalizedPattern {
351 let type_name = "Bool".to_string();
352
353 if unions.get(&type_name).is_none() {
354 let make_alt = |b: bool| Constructor {
355 tag_id: b.to_string(),
356 arity: 0,
357 };
358
359 unions.insert(type_name.clone(), vec![make_alt(true), make_alt(false)]);
360 }
361
362 NormalizedPattern::Constructor {
363 type_name,
364 tag: boolean.to_string(),
365 args: vec![],
366 }
367}