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}