1use crate::types::*;
4use indexmap::IndexMap;
5
6#[derive(Debug, Clone)]
7pub struct TypeDef {
8 pub params: Vec<String>,
9 pub kind: TypeDefKind,
10}
11
12#[derive(Debug, Clone)]
13pub enum TypeDefKind {
14 Union(IndexMap<String, Option<Ty>>),
16 Alias(Ty),
18 Opaque,
20}
21
22#[derive(Debug, Clone, Default)]
23pub struct TypeEnv {
24 pub types: IndexMap<String, TypeDef>,
26 pub ctor_to_type: IndexMap<String, String>,
28 pub dep_alias_prefixes: IndexMap<String, String>,
36}
37
38impl TypeEnv {
39 pub fn new_with_builtins() -> Self {
40 let mut e = TypeEnv::default();
41 let mut r_variants = IndexMap::new();
43 r_variants.insert("Ok".into(), Some(Ty::Var(0))); r_variants.insert("Err".into(), Some(Ty::Var(1))); e.types.insert("Result".into(), TypeDef {
46 params: vec!["T".into(), "E".into()],
47 kind: TypeDefKind::Union(r_variants),
48 });
49 e.ctor_to_type.insert("Ok".into(), "Result".into());
50 e.ctor_to_type.insert("Err".into(), "Result".into());
51
52 let mut o_variants = IndexMap::new();
54 o_variants.insert("Some".into(), Some(Ty::Var(0))); o_variants.insert("None".into(), None);
56 e.types.insert("Option".into(), TypeDef {
57 params: vec!["T".into()],
58 kind: TypeDefKind::Union(o_variants),
59 });
60 e.ctor_to_type.insert("Some".into(), "Option".into());
61 e.ctor_to_type.insert("None".into(), "Option".into());
62
63 e.types.insert("Nil".into(), TypeDef {
65 params: vec![],
66 kind: TypeDefKind::Alias(Ty::Unit),
67 });
68
69 e.types.insert("Map".into(), TypeDef { params: vec!["K".into(), "V".into()], kind: TypeDefKind::Opaque });
71 e.types.insert("Set".into(), TypeDef { params: vec!["T".into()], kind: TypeDefKind::Opaque });
72
73 let mut sp_variants = IndexMap::new();
77 sp_variants.insert("PStr".into(), Some(Ty::str()));
78 sp_variants.insert("PInt".into(), Some(Ty::int()));
79 sp_variants.insert("PFloat".into(), Some(Ty::float()));
80 sp_variants.insert("PBool".into(), Some(Ty::bool()));
81 sp_variants.insert("PNull".into(), None);
82 e.types.insert("SqlParam".into(), TypeDef {
83 params: vec![],
84 kind: TypeDefKind::Union(sp_variants),
85 });
86 for ctor in &["PStr", "PInt", "PFloat", "PBool", "PNull"] {
87 e.ctor_to_type.insert((*ctor).into(), "SqlParam".into());
88 }
89
90 e.types.insert("SqlTx".into(), TypeDef { params: vec![], kind: TypeDefKind::Opaque });
95
96 let mut se_fields = IndexMap::new();
104 se_fields.insert("message".into(), Ty::str());
105 se_fields.insert("code".into(), Ty::Con("Option".into(), vec![Ty::str()]));
106 se_fields.insert("detail".into(), Ty::Con("Option".into(), vec![Ty::str()]));
107 e.types.insert("SqlError".into(), TypeDef {
108 params: vec![],
109 kind: TypeDefKind::Alias(Ty::Record(se_fields)),
110 });
111
112 let mut ar_fields = IndexMap::new();
119 ar_fields.insert("ciphertext".into(), Ty::bytes());
120 ar_fields.insert("tag".into(), Ty::bytes());
121 e.types.insert("AeadResult".into(), TypeDef {
122 params: vec![],
123 kind: TypeDefKind::Alias(Ty::Record(ar_fields)),
124 });
125
126 let mut dg_fields = IndexMap::new();
135 dg_fields.insert("data".into(), Ty::bytes());
136 dg_fields.insert("host".into(), Ty::str());
137 dg_fields.insert("port".into(), Ty::int());
138 e.types.insert("UdpDatagram".into(), TypeDef {
139 params: vec![],
140 kind: TypeDefKind::Alias(Ty::Record(dg_fields)),
141 });
142
143 e.types.insert("Iter".into(), TypeDef { params: vec!["T".into()], kind: TypeDefKind::Opaque });
147
148 e.types.insert("Stream".into(), TypeDef { params: vec!["T".into()], kind: TypeDefKind::Opaque });
155
156 let mut tz_variants = IndexMap::new();
162 tz_variants.insert("Utc".into(), None);
163 tz_variants.insert("Local".into(), None);
164 tz_variants.insert("Offset".into(), Some(Ty::int()));
165 tz_variants.insert("Iana".into(), Some(Ty::str()));
166 e.types.insert("Tz".into(), TypeDef {
167 params: vec![],
168 kind: TypeDefKind::Union(tz_variants),
169 });
170 for ctor in &["Utc", "Local", "Offset", "Iana"] {
171 e.ctor_to_type.insert((*ctor).into(), "Tz".into());
172 }
173
174 let mut http_err_variants = IndexMap::new();
180 http_err_variants.insert("NetworkError".into(), Some(Ty::str()));
181 http_err_variants.insert("TimeoutError".into(), None);
182 http_err_variants.insert("TlsError".into(), Some(Ty::str()));
183 http_err_variants.insert("DecodeError".into(), Some(Ty::str()));
184 e.types.insert("HttpError".into(), TypeDef {
185 params: vec![],
186 kind: TypeDefKind::Union(http_err_variants),
187 });
188 for ctor in &["NetworkError", "TimeoutError", "TlsError", "DecodeError"] {
189 e.ctor_to_type.insert((*ctor).into(), "HttpError".into());
190 }
191
192 let json_ty = || Ty::Con("Json".into(), vec![]);
202 let mut json_variants = IndexMap::new();
203 json_variants.insert("JNull".into(), None);
204 json_variants.insert("JBool".into(), Some(Ty::bool()));
205 json_variants.insert("JInt".into(), Some(Ty::int()));
206 json_variants.insert("JFloat".into(), Some(Ty::float()));
207 json_variants.insert("JStr".into(), Some(Ty::str()));
208 json_variants.insert("JList".into(), Some(Ty::List(Box::new(json_ty()))));
209 json_variants.insert(
210 "JObj".into(),
211 Some(Ty::List(Box::new(Ty::Tuple(vec![Ty::str(), json_ty()])))),
212 );
213 e.types.insert("Json".into(), TypeDef {
214 params: vec![],
215 kind: TypeDefKind::Union(json_variants),
216 });
217 for ctor in &["JNull", "JBool", "JInt", "JFloat", "JStr", "JList", "JObj"] {
218 e.ctor_to_type.insert((*ctor).into(), "Json".into());
219 }
220
221 let mut req_fields = IndexMap::new();
230 req_fields.insert("method".into(), Ty::str());
231 req_fields.insert("url".into(), Ty::str());
232 req_fields.insert("headers".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
233 req_fields.insert("body".into(), Ty::Con("Option".into(), vec![Ty::bytes()]));
234 req_fields.insert("timeout_ms".into(), Ty::Con("Option".into(), vec![Ty::int()]));
235 e.types.insert("HttpRequest".into(), TypeDef {
236 params: vec![],
237 kind: TypeDefKind::Alias(Ty::Record(req_fields)),
238 });
239
240 let mut resp_fields = IndexMap::new();
244 resp_fields.insert("status".into(), Ty::int());
245 resp_fields.insert("headers".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
246 resp_fields.insert("body".into(), Ty::bytes());
247 e.types.insert("HttpResponse".into(), TypeDef {
248 params: vec![],
249 kind: TypeDefKind::Alias(Ty::Record(resp_fields)),
250 });
251
252 let mut mat_fields = IndexMap::new();
259 mat_fields.insert("rows".into(), Ty::int());
260 mat_fields.insert("cols".into(), Ty::int());
261 mat_fields.insert("data".into(), Ty::List(Box::new(Ty::float())));
262 e.types.insert("Matrix".into(), TypeDef {
263 params: vec![],
264 kind: TypeDefKind::Alias(Ty::Record(mat_fields)),
265 });
266
267 let mut net_req_fields = IndexMap::new();
273 net_req_fields.insert("method".into(), Ty::str());
274 net_req_fields.insert("path".into(), Ty::str());
275 net_req_fields.insert("query".into(), Ty::str());
276 net_req_fields.insert("body".into(), Ty::str());
277 net_req_fields.insert("headers".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
278 net_req_fields.insert("path_params".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
279 e.types.insert("Request".into(), TypeDef {
280 params: vec![],
281 kind: TypeDefKind::Alias(Ty::Record(net_req_fields)),
282 });
283
284 let mut rb_variants = IndexMap::new();
290 rb_variants.insert("BodyStr".into(), Some(Ty::str()));
291 rb_variants.insert(
292 "BodyStream".into(),
293 Some(Ty::Con("Iter".into(), vec![Ty::str()])),
294 );
295 rb_variants.insert(
296 "BodyBytes".into(),
297 Some(Ty::Con("Iter".into(), vec![Ty::List(Box::new(Ty::int()))])),
298 );
299 e.types.insert("ResponseBody".into(), TypeDef {
300 params: vec![],
301 kind: TypeDefKind::Union(rb_variants),
302 });
303 for ctor in &["BodyStr", "BodyStream", "BodyBytes"] {
304 e.ctor_to_type.insert((*ctor).into(), "ResponseBody".into());
305 }
306
307 let mut net_resp_fields = IndexMap::new();
308 net_resp_fields.insert("status".into(), Ty::int());
309 net_resp_fields.insert("body".into(), Ty::Con("ResponseBody".into(), vec![]));
310 net_resp_fields.insert("headers".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
311 e.types.insert("Response".into(), TypeDef {
312 params: vec![],
313 kind: TypeDefKind::Alias(Ty::Record(net_resp_fields)),
314 });
315
316 let mut ws_conn_fields = IndexMap::new();
319 ws_conn_fields.insert("id".into(), Ty::str());
320 ws_conn_fields.insert("path".into(), Ty::str());
321 ws_conn_fields.insert("subprotocol".into(), Ty::str());
322 e.types.insert("WsConn".into(), TypeDef {
323 params: vec![],
324 kind: TypeDefKind::Alias(Ty::Record(ws_conn_fields)),
325 });
326
327 let mut ws_msg_variants = IndexMap::new();
329 ws_msg_variants.insert("WsText".into(), Some(Ty::str()));
330 ws_msg_variants.insert("WsBinary".into(), Some(Ty::List(Box::new(Ty::int()))));
331 ws_msg_variants.insert("WsPing".into(), None);
332 ws_msg_variants.insert("WsClose".into(), None);
333 e.types.insert("WsMessage".into(), TypeDef {
334 params: vec![],
335 kind: TypeDefKind::Union(ws_msg_variants),
336 });
337 for ctor in &["WsText", "WsBinary", "WsPing", "WsClose"] {
338 e.ctor_to_type.insert((*ctor).into(), "WsMessage".into());
339 }
340
341 let mut ws_act_variants = IndexMap::new();
346 ws_act_variants.insert("WsSend".into(), Some(Ty::str()));
347 ws_act_variants.insert("WsSendBinary".into(), Some(Ty::List(Box::new(Ty::int()))));
348 ws_act_variants.insert("WsNoOp".into(), None);
349 e.types.insert("WsAction".into(), TypeDef {
350 params: vec![],
351 kind: TypeDefKind::Union(ws_act_variants),
352 });
353 for ctor in &["WsSend", "WsSendBinary", "WsNoOp"] {
354 e.ctor_to_type.insert((*ctor).into(), "WsAction".into());
355 }
356
357 let mut ce_variants = IndexMap::new();
363 ce_variants.insert("AlreadyRegistered".into(), Some(Ty::str()));
364 ce_variants.insert("NotRegistered".into(), Some(Ty::str()));
365 e.types.insert("ConcError".into(), TypeDef {
366 params: vec![],
367 kind: TypeDefKind::Union(ce_variants),
368 });
369 for ctor in &["AlreadyRegistered", "NotRegistered"] {
370 e.ctor_to_type.insert((*ctor).into(), "ConcError".into());
371 }
372
373 e.types.insert("ConnRedis".into(), TypeDef { params: vec![], kind: TypeDefKind::Opaque });
377
378 e
379 }
380
381 pub fn add_user_type(&mut self, name: &str, decl: lex_ast::TypeDecl) -> Result<(), String> {
382 match &decl.definition {
383 lex_ast::TypeExpr::Union { variants } => {
384 let mut vmap = IndexMap::new();
389 for v in variants {
390 let payload = v.payload.as_ref().map(|p| ty_from_canon_env(p, &decl.params, self));
391 vmap.insert(v.name.clone(), payload);
392 }
393 for v in variants {
394 self.ctor_to_type.insert(v.name.clone(), name.to_string());
395 }
396 self.types.insert(name.to_string(), TypeDef {
397 params: decl.params.clone(),
398 kind: TypeDefKind::Union(vmap),
399 });
400 }
401 other => {
402 let ty = ty_from_canon_env(other, &decl.params, self);
403 self.types.insert(name.to_string(), TypeDef {
404 params: decl.params.clone(),
405 kind: TypeDefKind::Alias(ty),
406 });
407 }
408 }
409 Ok(())
410 }
411}
412
413pub fn ty_from_canon(t: &lex_ast::TypeExpr, params: &[String]) -> Ty {
417 match t {
418 lex_ast::TypeExpr::Named { name, args } => {
419 if let Some(idx) = params.iter().position(|p| p == name) {
421 if !args.is_empty() {
422 return Ty::Con(name.clone(), args.iter().map(|a| ty_from_canon(a, params)).collect());
424 }
425 return Ty::Var(idx as u32);
426 }
427 match name.as_str() {
429 "Int" => return Ty::int(),
430 "Float" => return Ty::float(),
431 "Bool" => return Ty::bool(),
432 "Str" => return Ty::str(),
433 "Bytes" => return Ty::bytes(),
434 "Unit" | "Nil" => return Ty::Unit,
435 "Never" => return Ty::Never,
436 "List" if args.len() == 1 => return Ty::List(Box::new(ty_from_canon(&args[0], params))),
437 "Tuple" => return Ty::Tuple(args.iter().map(|a| ty_from_canon(a, params)).collect()),
442 _ => {}
443 }
444 Ty::Con(name.clone(), args.iter().map(|a| ty_from_canon(a, params)).collect())
445 }
446 lex_ast::TypeExpr::Record { fields } => {
447 let mut m = IndexMap::new();
448 for f in fields { m.insert(f.name.clone(), ty_from_canon(&f.ty, params)); }
449 Ty::Record(m)
450 }
451 lex_ast::TypeExpr::Tuple { items } => Ty::Tuple(items.iter().map(|t| ty_from_canon(t, params)).collect()),
452 lex_ast::TypeExpr::Function { params: ps, effects, effect_row_var, ret } => {
453 let effs = EffectSet {
455 concrete: {
456 let mut s = std::collections::BTreeSet::new();
457 for e in effects {
458 let arg = e.arg.as_ref().map(|a| match a {
459 lex_ast::EffectArg::Str { value } => crate::types::EffectArg::Str(value.clone()),
460 lex_ast::EffectArg::Int { value } => crate::types::EffectArg::Int(*value),
461 lex_ast::EffectArg::Ident { value } => crate::types::EffectArg::Ident(value.clone()),
462 });
463 s.insert(crate::types::EffectKind { name: e.name.clone(), arg });
464 }
465 s
466 },
467 var: effect_row_var
473 .as_ref()
474 .and_then(|name| params.iter().position(|p| p == name))
475 .map(|i| i as u32),
476 };
477 Ty::Function {
478 params: ps.iter().map(|t| ty_from_canon(t, params)).collect(),
479 effects: effs,
480 ret: Box::new(ty_from_canon(ret, params)),
481 }
482 }
483 lex_ast::TypeExpr::Union { .. } => {
484 Ty::Unit
486 }
487 lex_ast::TypeExpr::Refined { base, .. } => {
488 ty_from_canon(base, params)
498 }
499 lex_ast::TypeExpr::RecordWithSpreads { .. } => {
500 Ty::Unit
502 }
503 }
504}
505
506pub fn ty_from_canon_env(t: &lex_ast::TypeExpr, params: &[String], env: &TypeEnv) -> Ty {
510 if !env.dep_alias_prefixes.is_empty() {
516 if let Some(normalized) = normalize_dep_alias(t, env) {
517 return ty_from_canon_env(&normalized, params, env);
518 }
519 }
520 match t {
521 lex_ast::TypeExpr::RecordWithSpreads { spreads, fields } => {
522 let mut m = IndexMap::new();
523 for spread_name in spreads {
524 if let Some(td) = env.types.get(spread_name.as_str()) {
525 if let TypeDefKind::Alias(Ty::Record(spread_fields)) = &td.kind {
526 for (k, v) in spread_fields {
527 m.insert(k.clone(), v.clone());
528 }
529 }
530 }
531 }
532 for f in fields {
533 m.insert(f.name.clone(), ty_from_canon_env(&f.ty, params, env));
534 }
535 Ty::Record(m)
536 }
537 other => ty_from_canon(other, params),
538 }
539}
540
541fn normalize_dep_alias(t: &lex_ast::TypeExpr, env: &TypeEnv) -> Option<lex_ast::TypeExpr> {
547 use lex_ast::TypeExpr as T;
548 match t {
549 T::Named { name, args } => {
550 let renamed = name
551 .split_once('.')
552 .and_then(|(alias, rest)| {
553 env.dep_alias_prefixes.get(alias).map(|p| format!("{p}.{rest}"))
554 });
555 let new_args: Vec<Option<T>> = args.iter().map(|a| normalize_dep_alias(a, env)).collect();
556 if renamed.is_none() && new_args.iter().all(|a| a.is_none()) {
557 return None;
558 }
559 let args = args
560 .iter()
561 .zip(new_args)
562 .map(|(orig, changed)| changed.unwrap_or_else(|| orig.clone()))
563 .collect();
564 Some(T::Named { name: renamed.unwrap_or_else(|| name.clone()), args })
565 }
566 T::Record { fields } => rewrite_fields(fields, env).map(|fields| T::Record { fields }),
567 T::Tuple { items } => rewrite_items(items, env).map(|items| T::Tuple { items }),
568 T::Function { params, effects, effect_row_var, ret } => {
569 let new_params: Vec<Option<T>> = params.iter().map(|p| normalize_dep_alias(p, env)).collect();
570 let new_ret = normalize_dep_alias(ret, env);
571 if new_ret.is_none() && new_params.iter().all(|p| p.is_none()) {
572 return None;
573 }
574 let params = params
575 .iter()
576 .zip(new_params)
577 .map(|(orig, changed)| changed.unwrap_or_else(|| orig.clone()))
578 .collect();
579 Some(T::Function {
580 params,
581 effects: effects.clone(),
582 effect_row_var: effect_row_var.clone(),
583 ret: Box::new(new_ret.unwrap_or_else(|| (**ret).clone())),
584 })
585 }
586 T::Union { variants } => {
587 let rewritten: Vec<Option<T>> = variants
588 .iter()
589 .map(|v| v.payload.as_ref().and_then(|p| normalize_dep_alias(p, env)))
590 .collect();
591 if rewritten.iter().all(|r| r.is_none()) {
592 return None;
593 }
594 let variants = variants
595 .iter()
596 .zip(rewritten)
597 .map(|(v, changed)| lex_ast::UnionVariant {
598 name: v.name.clone(),
599 payload: changed.or_else(|| v.payload.clone()),
600 })
601 .collect();
602 Some(T::Union { variants })
603 }
604 T::RecordWithSpreads { spreads, fields } => {
605 let new_spreads: Vec<String> = spreads
607 .iter()
608 .map(|s| {
609 s.split_once('.')
610 .and_then(|(a, rest)| env.dep_alias_prefixes.get(a).map(|p| format!("{p}.{rest}")))
611 .unwrap_or_else(|| s.clone())
612 })
613 .collect();
614 let spreads_changed = new_spreads != *spreads;
615 let new_fields = rewrite_fields(fields, env);
616 if !spreads_changed && new_fields.is_none() {
617 return None;
618 }
619 Some(T::RecordWithSpreads {
620 spreads: new_spreads,
621 fields: new_fields.unwrap_or_else(|| fields.clone()),
622 })
623 }
624 T::Refined { base, binding, predicate } => normalize_dep_alias(base, env).map(|b| T::Refined {
625 base: Box::new(b),
626 binding: binding.clone(),
627 predicate: predicate.clone(),
628 }),
629 }
630}
631
632fn rewrite_fields(fields: &[lex_ast::TypeField], env: &TypeEnv) -> Option<Vec<lex_ast::TypeField>> {
633 let rewritten: Vec<Option<lex_ast::TypeExpr>> =
634 fields.iter().map(|f| normalize_dep_alias(&f.ty, env)).collect();
635 if rewritten.iter().all(|r| r.is_none()) {
636 return None;
637 }
638 Some(
639 fields
640 .iter()
641 .zip(rewritten)
642 .map(|(f, changed)| lex_ast::TypeField {
643 name: f.name.clone(),
644 ty: changed.unwrap_or_else(|| f.ty.clone()),
645 })
646 .collect(),
647 )
648}
649
650fn rewrite_items(items: &[lex_ast::TypeExpr], env: &TypeEnv) -> Option<Vec<lex_ast::TypeExpr>> {
651 let rewritten: Vec<Option<lex_ast::TypeExpr>> =
652 items.iter().map(|it| normalize_dep_alias(it, env)).collect();
653 if rewritten.iter().all(|r| r.is_none()) {
654 return None;
655 }
656 Some(
657 items
658 .iter()
659 .zip(rewritten)
660 .map(|(it, changed)| changed.unwrap_or_else(|| it.clone()))
661 .collect(),
662 )
663}