rukalang/elab/
instantiation.rs

1use super::helpers::{canonical_nominal_name, ty_to_type_expr};
2use super::prelude::*;
3
4impl Elaborator {
5    pub(super) fn ensure_ty_instantiated(&mut self, ty: &Ty) -> Result<(), ElabError> {
6        match ty {
7            Ty::Pointer(item)
8            | Ty::Option(item)
9            | Ty::Slice(item)
10            | Ty::RefRo(item)
11            | Ty::RefMut(item) => self.ensure_ty_instantiated(item),
12            Ty::Array { item, .. } => self.ensure_ty_instantiated(item),
13            Ty::Tuple(items) => {
14                for item in items {
15                    self.ensure_ty_instantiated(item)?;
16                }
17                Ok(())
18            }
19            Ty::Struct { name, args } => {
20                let template = self
21                    .templates
22                    .get(name)
23                    .cloned()
24                    .ok_or_else(|| ElabError::UnknownTypeConstructor { name: name.clone() })?;
25                let canonical_name = canonical_nominal_name(ty);
26                if self.concrete_structs.contains_key(&canonical_name) {
27                    return Ok(());
28                }
29                if !self.instantiating_structs.insert(canonical_name.clone()) {
30                    return Ok(());
31                }
32
33                let result = (|| {
34                    let mut bindings = BTreeMap::new();
35                    for (param, arg) in template.type_params.iter().zip(args.iter()) {
36                        bindings.insert(param.clone(), arg.clone());
37                    }
38                    let type_params = template
39                        .type_params
40                        .iter()
41                        .cloned()
42                        .collect::<BTreeSet<_>>();
43
44                    let mut fields = Vec::with_capacity(template.fields.len());
45                    for field in &template.fields {
46                        let field_ty =
47                            self.resolve_type_expr(&field.ty, &bindings, &type_params, None)?;
48                        self.ensure_ty_instantiated(&field_ty)?;
49                        fields.push(NamedTypeField {
50                            name: field.name.clone(),
51                            ty: ty_to_type_expr(&field_ty),
52                        });
53                    }
54
55                    self.concrete_structs.insert(
56                        canonical_name.clone(),
57                        StructDecl {
58                            name: canonical_name.clone(),
59                            type_params: Vec::new(),
60                            fields,
61                        },
62                    );
63                    Ok(())
64                })();
65                let _ = self.instantiating_structs.remove(&canonical_name);
66                result
67            }
68            Ty::Enum { args, .. } => {
69                for arg in args {
70                    self.ensure_ty_instantiated(arg)?;
71                }
72                Ok(())
73            }
74            Ty::Unit
75            | Ty::U8
76            | Ty::U16
77            | Ty::U32
78            | Ty::U64
79            | Ty::I8
80            | Ty::I16
81            | Ty::I32
82            | Ty::I64
83            | Ty::F32
84            | Ty::F64
85            | Ty::String
86            | Ty::Bool => Ok(()),
87        }
88    }
89
90    pub(super) fn unify_struct_type_param_bindings(
91        &self,
92        schema: &TypeExpr,
93        actual: &Ty,
94        type_params: &BTreeSet<String>,
95        bindings: &mut BTreeMap<String, Ty>,
96    ) -> Result<(), ElabError> {
97        match schema {
98            TypeExpr::Named(name) if type_params.contains(name) => {
99                if let Some(existing) = bindings.get(name) {
100                    if existing != actual {
101                        return Err(ElabError::TypeMismatch {
102                            expected: existing.clone(),
103                            actual: actual.clone(),
104                        });
105                    }
106                } else {
107                    bindings.insert(name.clone(), actual.clone());
108                }
109                Ok(())
110            }
111            TypeExpr::Named(_) => {
112                let expected = self.resolve_type_expr(schema, bindings, type_params, None)?;
113                if &expected != actual {
114                    return Err(ElabError::TypeMismatch {
115                        expected,
116                        actual: actual.clone(),
117                    });
118                }
119                Ok(())
120            }
121            TypeExpr::Array { item, len } => {
122                let Ty::Array {
123                    item: actual_item,
124                    len: actual_len,
125                } = actual
126                else {
127                    return Err(ElabError::TypeMismatch {
128                        expected: self.resolve_type_expr(schema, bindings, type_params, None)?,
129                        actual: actual.clone(),
130                    });
131                };
132                if len != actual_len {
133                    return Err(ElabError::TypeMismatch {
134                        expected: self.resolve_type_expr(schema, bindings, type_params, None)?,
135                        actual: actual.clone(),
136                    });
137                }
138                self.unify_struct_type_param_bindings(item, actual_item, type_params, bindings)
139            }
140            TypeExpr::Slice { item } => {
141                let Ty::Slice(actual_item) = actual else {
142                    return Err(ElabError::TypeMismatch {
143                        expected: self.resolve_type_expr(schema, bindings, type_params, None)?,
144                        actual: actual.clone(),
145                    });
146                };
147                self.unify_struct_type_param_bindings(item, actual_item, type_params, bindings)
148            }
149            TypeExpr::Pointer { item } => {
150                let Ty::Pointer(actual_item) = actual else {
151                    return Err(ElabError::TypeMismatch {
152                        expected: self.resolve_type_expr(schema, bindings, type_params, None)?,
153                        actual: actual.clone(),
154                    });
155                };
156                self.unify_struct_type_param_bindings(item, actual_item, type_params, bindings)
157            }
158            TypeExpr::Tuple(items) => {
159                let actual_items = match actual {
160                    Ty::Unit if items.is_empty() => return Ok(()),
161                    Ty::Tuple(actual_items) if actual_items.len() == items.len() => actual_items,
162                    _ => {
163                        return Err(ElabError::TypeMismatch {
164                            expected: self.resolve_type_expr(
165                                schema,
166                                bindings,
167                                type_params,
168                                None,
169                            )?,
170                            actual: actual.clone(),
171                        });
172                    }
173                };
174                for (expected_item, actual_item) in items.iter().zip(actual_items.iter()) {
175                    self.unify_struct_type_param_bindings(
176                        expected_item,
177                        actual_item,
178                        type_params,
179                        bindings,
180                    )?;
181                }
182                Ok(())
183            }
184            TypeExpr::Apply { callee, args } => {
185                if callee == "Option" {
186                    let Ty::Option(actual_item) = actual else {
187                        return Err(ElabError::TypeMismatch {
188                            expected: self.resolve_type_expr(
189                                schema,
190                                bindings,
191                                type_params,
192                                None,
193                            )?,
194                            actual: actual.clone(),
195                        });
196                    };
197                    if args.len() != 1 {
198                        return Err(ElabError::TypeArgCountMismatch {
199                            name: callee.clone(),
200                            expected: 1,
201                            actual: args.len(),
202                        });
203                    }
204                    return self.unify_struct_type_param_bindings(
205                        &args[0],
206                        actual_item,
207                        type_params,
208                        bindings,
209                    );
210                }
211                let Ty::Struct {
212                    name,
213                    args: actual_args,
214                } = actual
215                else {
216                    return Err(ElabError::TypeMismatch {
217                        expected: self.resolve_type_expr(schema, bindings, type_params, None)?,
218                        actual: actual.clone(),
219                    });
220                };
221                if name != callee || args.len() != actual_args.len() {
222                    return Err(ElabError::TypeMismatch {
223                        expected: self.resolve_type_expr(schema, bindings, type_params, None)?,
224                        actual: actual.clone(),
225                    });
226                }
227                for (expected_arg, actual_arg) in args.iter().zip(actual_args.iter()) {
228                    self.unify_struct_type_param_bindings(
229                        expected_arg,
230                        actual_arg,
231                        type_params,
232                        bindings,
233                    )?;
234                }
235                Ok(())
236            }
237            _ => {
238                let expected = self.resolve_type_expr(schema, bindings, type_params, None)?;
239                if &expected != actual {
240                    return Err(ElabError::TypeMismatch {
241                        expected,
242                        actual: actual.clone(),
243                    });
244                }
245                Ok(())
246            }
247        }
248    }
249}