1use std::collections::{BTreeMap, BTreeSet};
33use std::sync::Arc;
34
35use beck_diag::depth::Nesting;
36use beck_diag::{Diagnostic, Diagnostics, Span};
37use beck_syntax::{sym, Lit, Node, ScopeSet, Symbol};
38
39use crate::core::{Arm, Const, Core, CoreKind, Pattern, Prim, VarId};
40use crate::iface::Interface;
41use crate::prelude;
42use crate::render;
43use crate::ty::{self, Effect, Mismatch, Row, RowVarId, Scheme, Subst, Tier, Ty, TyDecl, Variant};
44
45#[derive(Clone, Debug)]
47pub struct Program {
48 pub name: String,
49 pub types: BTreeMap<Arc<str>, TyDecl>,
50 pub traits: Vec<ty::TraitSig>,
52 pub impls: Vec<ty::ImplSig>,
56 pub own_types: Vec<Arc<str>>,
59 pub imports: Vec<String>,
61 pub defs: BTreeMap<Arc<str>, Def>,
62 pub def_order: Vec<Arc<str>>,
64 pub signals: Vec<SignalDecl>,
65 pub tests: Vec<crate::testing::TestDef>,
66 pub docs: BTreeMap<Arc<str>, Arc<str>>,
74 pub identity: Option<IdentityDecl>,
81}
82
83fn issuer_host(url: &str) -> Option<String> {
91 let rest = url.strip_prefix("https://")?;
92 let authority = rest.split('/').next().unwrap_or(rest);
93 if authority.contains('@') {
94 return None;
95 }
96 let host = authority.rsplit_once(':').map_or(authority, |(h, _)| h);
97 crate::net::is_nameable_host(host).then(|| host.to_string())
98}
99
100#[derive(Clone, Debug, PartialEq, Eq)]
108pub enum IdentityDecl {
109 External {
111 issuer: Arc<str>,
114 host: Arc<str>,
117 span: Span,
118 },
119 Managed { span: Span },
125}
126
127impl IdentityDecl {
128 pub fn span(&self) -> Span {
130 match self {
131 IdentityDecl::External { span, .. } | IdentityDecl::Managed { span } => *span,
132 }
133 }
134}
135
136#[derive(Clone, Debug)]
137pub struct Def {
138 pub name: Arc<str>,
139 pub typarams: Vec<Arc<str>>,
145 pub params: Vec<(VarId, Arc<str>, Ty)>,
146 pub ret: Ty,
147 pub body: Core,
149 pub tier: Tier,
150 pub effects: Vec<Effect>,
153 pub row: Row,
155 pub declared_effects: Vec<Effect>,
157 pub bounds: Vec<(Arc<str>, Vec<Arc<str>>)>,
163 pub row_is_declared: bool,
171 pub tier_is_annotated: bool,
173 pub is_declaration: bool,
175 pub declares_signal: bool,
177 pub span: Span,
178 pub tier_span: Span,
179}
180
181#[derive(Clone, Debug)]
183pub struct SignalDecl {
184 pub name: Arc<str>,
185 pub ty: Ty,
186 pub expr: Core,
187 pub tier: Tier,
188 pub effects: Vec<Effect>,
189 pub row: Row,
190 pub tier_is_annotated: bool,
191 pub render: Option<(render::Mode, Span)>,
194 pub span: Span,
195 pub tier_span: Span,
196}
197
198#[derive(Clone, Copy, Debug, Default)]
203struct Decorations {
204 tier: Option<(Tier, Span)>,
205 declares_signal: bool,
208 render: Option<(render::Mode, Span)>,
209}
210
211#[derive(Clone, Debug, Default)]
214struct TestSubjects {
215 state: Option<Ty>,
216 event: Option<Ty>,
217 result: Option<Ty>,
218 command: Option<Ty>,
219}
220
221#[derive(Clone, Debug)]
222enum BindKind {
223 Local(VarId, Ty),
224 Global(Arc<str>),
225 Prim(Prim),
226 TraitMethod(Arc<str>),
228 Ctor(Arc<str>, Arc<str>),
230 Model(Arc<str>),
232}
233
234#[derive(Clone, Debug)]
235struct Binding {
236 name: Arc<str>,
237 scopes: ScopeSet,
238 kind: BindKind,
239}
240
241pub struct Checker<'a> {
242 diags: &'a mut Diagnostics,
243 subst: Subst,
244 types: BTreeMap<Arc<str>, TyDecl>,
245 schemes: BTreeMap<Arc<str>, Scheme>,
246 prims: BTreeMap<Arc<str>, (Prim, Scheme)>,
247 locals: Vec<Binding>,
249 globals: Vec<Binding>,
250 declared: BTreeMap<Arc<str>, Row>,
252 row_aliases: BTreeMap<Arc<str>, Row>,
257 identity: Option<IdentityDecl>,
258 own_types: Vec<Arc<str>>,
260 def_row: BTreeMap<Arc<str>, RowVarId>,
263 generic_rows: BTreeMap<Arc<str>, Vec<RowVarId>>,
266 row: Row,
268 next_var: VarId,
269 in_fold: bool,
271 typarams: BTreeSet<Arc<str>>,
274 decl_typarams: BTreeMap<Arc<str>, u32>,
278 traits: BTreeMap<Arc<str>, traits::TraitDecl>,
280 trait_methods: BTreeMap<Arc<str>, Arc<str>>,
283 impls: BTreeMap<(Arc<str>, Arc<str>), traits::ImplDecl>,
285 own_traits: Vec<Arc<str>>,
288 own_impls: Vec<(Arc<str>, Arc<str>)>,
289 impl_methods: BTreeSet<Arc<str>>,
292 dicts: BTreeMap<Arc<str>, Vec<traits::DictParam>>,
295 nesting: Nesting,
299 block_nesting: Nesting,
309 parallel_siblings: Vec<Arc<str>>,
314 mode: Mode,
315}
316
317#[derive(Clone, Copy, Debug, PartialEq, Eq)]
319pub enum Mode {
320 Module,
322 Interface,
324}
325
326pub fn check_module(module: &Node, diags: &mut Diagnostics) -> Program {
328 check_module_with(module, Mode::Module, &[], diags)
329}
330
331pub fn check_module_with(
337 module: &Node,
338 mode: Mode,
339 imports: &[(String, Interface)],
340 diags: &mut Diagnostics,
341) -> Program {
342 let name = module
343 .args
344 .first()
345 .and_then(|n| n.as_var())
346 .map(|s| s.as_str().to_string())
347 .unwrap_or_else(|| "main".into());
348
349 let mut ck = Checker {
350 diags,
351 subst: Subst::new(),
352 types: prelude::types(),
353 schemes: BTreeMap::new(),
354 prims: BTreeMap::new(),
355 locals: Vec::new(),
356 globals: Vec::new(),
357 declared: BTreeMap::new(),
358 row_aliases: BTreeMap::new(),
359 identity: None,
360 own_types: Vec::new(),
361 def_row: BTreeMap::new(),
362 row: Row::empty(),
363 next_var: 0,
364 in_fold: false,
365 typarams: BTreeSet::new(),
366 decl_typarams: BTreeMap::new(),
367 traits: BTreeMap::new(),
368 trait_methods: BTreeMap::new(),
369 impls: BTreeMap::new(),
370 own_traits: Vec::new(),
371 own_impls: Vec::new(),
372 impl_methods: BTreeSet::new(),
373 dicts: BTreeMap::new(),
374 generic_rows: BTreeMap::new(),
375 nesting: Nesting::new(),
376 block_nesting: Nesting::with_limit(beck_diag::depth::MAX_BLOCK),
377 parallel_siblings: Vec::new(),
378 mode,
379 };
380 for (name, prim, scheme) in prelude::prims() {
381 ck.prims.insert(Arc::from(name), (prim, scheme));
382 ck.globals.push(Binding {
383 name: Arc::from(name),
384 scopes: ScopeSet::empty(),
385 kind: BindKind::Prim(prim),
386 });
387 }
388
389 ck.import_traits(&prelude::traits(), &[]);
393
394 for (module_name, iface) in imports {
397 let (types, names) = iface.exports();
398 for (n, d) in types {
399 ck.types.insert(n, d);
400 }
401 ck.import_traits(&iface.traits, &iface.impls);
402 for (n, e) in names {
403 let scheme = if e.bounds.is_empty() {
406 e.scheme
407 } else {
408 ck.import_bounded(&n, &e.bounds, e.scheme)
409 };
410 ck.schemes.insert(n.clone(), scheme);
411 ck.declared.insert(n.clone(), e.row);
412 ck.globals.push(Binding {
413 name: n.clone(),
414 scopes: ScopeSet::empty(),
415 kind: BindKind::Global(n),
416 });
417 }
418 let _ = module_name;
419 }
420
421 let items: Vec<&Node> = module.args.iter().skip(1).collect();
422 ck.declare_type_names(&items);
426 ck.collect_aliases(&items);
427 ck.collect_types(&items);
428 ck.register_type_constructors();
429 ck.collect_row_aliases(&items);
435 ck.collect_identity(&items);
436 ck.collect_traits(&items);
437 let expanded = ck.expand_impls(&items);
438 let bounded: Vec<(usize, Node)> = items
442 .iter()
443 .enumerate()
444 .filter_map(|(i, it)| ck.expand_bounds(it).map(|n| (i, n)))
445 .collect();
446 let mut items: Vec<&Node> = expanded.iter().chain(items).collect();
453 for (i, node) in &bounded {
454 items[expanded.len() + *i] = node;
455 }
456 ck.collect_signatures(&items);
457 ck.collect_signal_names(&items);
458 let mut program = ck.check_items(&items, name);
459 program.imports = imports.iter().map(|(n, _)| n.clone()).collect();
460 program
461}
462
463mod exhaust;
464mod tests_in_beck;
465mod traits;
466
467pub use traits::is_impl_method;
468
469fn flatten_alts(p: &Node, out: &mut Vec<Node>) {
471 if p.has_head("|") && p.args.len() == 2 {
472 flatten_alts(&p.args[0], out);
473 flatten_alts(&p.args[1], out);
474 return;
475 }
476 out.push(p.clone());
477}
478
479fn rename_binders(p: &mut Pattern, map: &BTreeMap<VarId, VarId>) {
481 match p {
482 Pattern::Wildcard | Pattern::Const(_) => {}
483 Pattern::Bind(v) => {
484 if let Some(&to) = map.get(v) {
485 *v = to;
486 }
487 }
488 Pattern::Ctor { binds, .. } => {
489 for (_, sub) in binds {
490 rename_binders(sub, map);
491 }
492 }
493 Pattern::List { items, rest } => {
494 for sub in items {
495 rename_binders(sub, map);
496 }
497 if let Some(Some(v)) = rest {
498 if let Some(&to) = map.get(v) {
499 *v = to;
500 }
501 }
502 }
503 Pattern::Or(alts) => {
504 for sub in alts {
505 rename_binders(sub, map);
506 }
507 }
508 Pattern::At { var, inner } => {
509 if let Some(&to) = map.get(var) {
510 *var = to;
511 }
512 rename_binders(inner, map);
513 }
514 }
515}
516
517impl<'a> Checker<'a> {
518 fn error(&mut self, code: &'static str, msg: impl Into<String>, span: Span) {
519 self.diags.push(Diagnostic::error(code, msg, span));
520 }
521
522 fn fresh_var(&mut self) -> VarId {
523 let v = self.next_var;
524 self.next_var += 1;
525 v
526 }
527
528 fn perform(&mut self, row: &Row) {
530 let acc = std::mem::take(&mut self.row);
531 self.row = acc.union(row);
532 }
533
534 fn in_scope<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> (T, Row) {
538 let outer = std::mem::take(&mut self.row);
539 let out = f(self);
540 let inner = std::mem::replace(&mut self.row, outer);
541 (out, inner)
542 }
543
544 fn undecorate<'n>(&mut self, item: &'n Node) -> (&'n Node, Option<(Tier, Span)>) {
548 let (inner, decos) = self.undecorate_full(item);
549 (inner, decos.tier)
550 }
551
552 fn undecorate_full<'n>(&mut self, item: &'n Node) -> (&'n Node, Decorations) {
554 let mut inner = item;
555 let mut decos = Decorations::default();
556 while inner.is_form(sym::DECORATE) && inner.args.len() == 2 {
557 let deco = &inner.args[0];
558 let named = |d: &Node| d.args[0].as_var().map(|s| s.name.clone());
559 if deco.head_name() == Some("signal") && deco.args.is_empty() {
560 decos.declares_signal = true;
561 } else if deco.has_head(sym::ON) && deco.args.len() == 1 {
562 let span = deco.span();
563 match named(deco).and_then(|s| Tier::parse(&s)) {
564 Some(t) => decos.tier = Some((t, span)),
565 None => self.error(
566 "B0300",
567 format!("`{}` is not a tier", named(deco).unwrap_or(Arc::from("?"))),
568 span,
569 ),
570 }
571 } else if deco.has_head(sym::RENDER) && deco.args.len() == 1 {
572 let span = deco.span();
573 match named(deco).and_then(|s| render::Mode::parse(&s)) {
574 Some(m) => decos.render = Some((m, span)),
575 None => self.diags.push(
576 Diagnostic::error(
577 "B0306",
578 format!(
579 "`{}` is not a rendering mode",
580 named(deco).unwrap_or(Arc::from("?"))
581 ),
582 span,
583 )
584 .with_fix("`@render(server)` for Mode A, `@render(client)` for Mode B"),
585 ),
586 }
587 } else {
588 self.diags.push(
589 Diagnostic::warning("B0301", "unsupported decorator", deco.span())
590 .with_note("`@on(client|server|data|any)` and `@render(client|server)`"),
591 );
592 }
593 inner = &inner.args[1];
594 }
595 (inner, decos)
596 }
597
598 fn declare_type_names(&mut self, items: &[&Node]) {
621 for item in items {
622 let (item, _) = self.undecorate(item);
623 let Some(name) = item
624 .args
625 .first()
626 .and_then(|n| n.as_var())
627 .map(|s| s.name.clone())
628 else {
629 continue;
630 };
631 if self.refuse_builtin_name(&name, item.span()) {
632 continue;
633 }
634 let params = Self::typaram_names(item);
640 let placeholder = if item.is_form(sym::MODEL) {
644 TyDecl::Model {
645 name: name.clone(),
646 params,
647 fields: Vec::new(),
648 }
649 } else if item.is_form(sym::UNION) {
650 TyDecl::Union {
651 name: name.clone(),
652 params,
653 variants: Vec::new(),
654 }
655 } else if item.is_form(sym::NEWTYPE) {
656 TyDecl::Newtype {
657 name: name.clone(),
658 params,
659 inner: Ty::unit(),
660 }
661 } else {
662 continue;
666 };
667 if self.types.insert(name.clone(), placeholder).is_some() {
668 self.error(
669 "B0302",
670 format!("type `{name}` is declared twice"),
671 item.span(),
672 );
673 }
674 self.own_types.push(name.clone());
675 }
676 }
677
678 fn refuse_builtin_name(&mut self, name: &Arc<str>, span: Span) -> bool {
692 if prelude::builtin_arity(name).is_none() {
693 return false;
694 }
695 self.diags.push(
696 Diagnostic::error(
697 "B0317",
698 format!("`{name}` is already a type in the language"),
699 span,
700 )
701 .with_primary_label("this name is a builtin type")
702 .with_note(
703 "a declaration that took it would make every other mention in the module ambiguous \
704 — the builtin in one signature and this declaration in the next",
705 ),
706 );
707 true
708 }
709
710 fn typaram_names(item: &Node) -> Vec<Arc<str>> {
716 item.args
717 .get(1)
718 .filter(|n| n.is_form(sym::TYPARAMS))
719 .map(|n| {
720 n.args
721 .iter()
722 .filter_map(|p| p.as_var().map(|s| s.name.clone()))
723 .collect()
724 })
725 .unwrap_or_default()
726 }
727
728 fn bind_decl_typarams(&mut self, item: &Node, decl_name: &str) -> Vec<Arc<str>> {
738 self.decl_typarams.clear();
739 let mut out: Vec<Arc<str>> = Vec::new();
740 let Some(list) = item.args.get(1).filter(|n| n.is_form(sym::TYPARAMS)) else {
741 return out;
742 };
743 for p in &list.args {
744 let Some(s) = p.as_var() else { continue };
745 let name = s.name.clone();
746 if self.types.contains_key(&name) || prelude::builtin_arity(&name).is_some() {
747 self.diags.push(
748 Diagnostic::error(
749 "B0314",
750 format!(
751 "`{name}` is already a type, so `{decl_name}` cannot take it as a \
752 parameter"
753 ),
754 p.span(),
755 )
756 .with_primary_label("this name already names a type")
757 .with_note(
758 "a type parameter is a name the declaration invents, and one that shadowed \
759 an existing type would make its fields read as though they mentioned that \
760 type",
761 ),
762 );
763 continue;
764 }
765 if out.contains(&name) {
766 self.error(
767 "B0315",
768 format!("`{name}` is repeated in `{decl_name}`'s type parameters"),
769 p.span(),
770 );
771 continue;
772 }
773 self.decl_typarams
774 .insert(name.clone(), ty::SCHEME_BASE + out.len() as u32);
775 out.push(name);
776 }
777 out
778 }
779
780 fn collect_aliases(&mut self, items: &[&Node]) {
793 let mut pending: BTreeMap<Arc<str>, (Node, Span)> = BTreeMap::new();
794 let mut order: Vec<Arc<str>> = Vec::new();
795 for item in items {
796 let (item, _) = self.undecorate(item);
797 if !item.is_form(sym::TYPE) || item.args.len() < 3 {
798 continue;
799 }
800 let Some(name) = item.args[0].as_var().map(|s| s.name.clone()) else {
801 continue;
802 };
803 if self.types.contains_key(&name) || pending.contains_key(&name) {
804 self.error(
805 "B0302",
806 format!("type `{name}` is declared twice"),
807 item.span(),
808 );
809 continue;
810 }
811 if prelude::builtin_arity(&name).is_some() {
815 continue;
816 }
817 pending.insert(name.clone(), (item.clone(), item.span()));
820 order.push(name);
821 }
822 let mut resolving: Vec<Arc<str>> = Vec::new();
823 for name in order {
824 self.resolve_alias(&name, &pending, &mut resolving);
825 }
826 }
827
828 fn resolve_alias(
829 &mut self,
830 name: &Arc<str>,
831 pending: &BTreeMap<Arc<str>, (Node, Span)>,
832 resolving: &mut Vec<Arc<str>>,
833 ) {
834 if self.types.contains_key(name) {
835 return;
836 }
837 let Some((item, span)) = pending.get(name) else {
838 return;
839 };
840 let (item, span) = (item.clone(), *span);
841 let node = &item.args[2];
842 if resolving.contains(name) {
843 self.error(
844 "B0312",
845 format!(
846 "type alias `{name}` is defined in terms of itself — an alias is transparent, \
847 so this describes no type; a `union` may be recursive, an alias may not"
848 ),
849 span,
850 );
851 let ty = self.subst.fresh();
854 self.types.insert(
855 name.clone(),
856 TyDecl::Alias {
857 name: name.clone(),
858 params: Self::typaram_names(&item),
859 ty,
860 },
861 );
862 return;
863 }
864 resolving.push(name.clone());
865 for referenced in Self::type_names_in(node) {
866 if pending.contains_key(&referenced) {
867 self.resolve_alias(&referenced, pending, resolving);
868 }
869 }
870 resolving.pop();
871 if self.types.contains_key(name) {
872 return; }
874 let params = self.bind_decl_typarams(&item, name);
877 let ty = self.ty_from_node(node);
878 self.decl_typarams.clear();
879 self.types.insert(
880 name.clone(),
881 TyDecl::Alias {
882 name: name.clone(),
883 params,
884 ty,
885 },
886 );
887 self.own_types.push(name.clone());
888 }
889
890 fn type_names_in(n: &Node) -> Vec<Arc<str>> {
897 let mut out = Vec::new();
898 fn walk(n: &Node, out: &mut Vec<Arc<str>>) {
899 if let Some(name) = n.head_name() {
900 out.push(Arc::from(name));
901 }
902 for a in &n.args {
903 walk(a, out);
904 }
905 }
906 walk(n, &mut out);
907 out
908 }
909
910 fn collect_types(&mut self, items: &[&Node]) {
911 for item in items {
912 let (item, _) = self.undecorate(item);
913 let Some(name) = item
914 .args
915 .first()
916 .and_then(|n| n.as_var())
917 .map(|s| s.name.clone())
918 else {
919 continue;
920 };
921 if !item.is_form(sym::MODEL) && !item.is_form(sym::UNION) && !item.is_form(sym::NEWTYPE)
922 {
923 continue;
926 }
927 for (p, _) in traits::bounds_of(&item.args[1]) {
930 self.diags.push(
931 Diagnostic::error(
932 "B0316",
933 format!("`{name}` cannot bound its type parameter `{p}`"),
934 item.args[1].span(),
935 )
936 .with_note(
937 "a bound says what a body may call, and a declaration has no body; the \
938 definitions that take this type apart are where the bound belongs",
939 ),
940 );
941 }
942 let params = self.bind_decl_typarams(item, &name);
945 let decl = if item.is_form(sym::MODEL) {
946 let fields = item.args[2..]
947 .iter()
948 .filter_map(|f| self.field_decl(f))
949 .collect();
950 TyDecl::Model {
951 name: name.clone(),
952 params,
953 fields,
954 }
955 } else if item.is_form(sym::UNION) {
956 let variants = item.args[2..]
957 .iter()
958 .map(|vn| Variant {
959 name: vn
960 .args
961 .first()
962 .and_then(|n| n.as_var())
963 .map(|s| s.name.clone())
964 .unwrap_or_else(|| Arc::from("?")),
965 fields: vn.args[1..]
966 .iter()
967 .filter_map(|f| self.field_decl(f))
968 .collect(),
969 })
970 .collect();
971 TyDecl::Union {
972 name: name.clone(),
973 params,
974 variants,
975 }
976 } else {
977 TyDecl::Newtype {
978 name: name.clone(),
979 params,
980 inner: self.ty_from_node(&item.args[2]),
981 }
982 };
983 self.decl_typarams.clear();
984 self.types.insert(name.clone(), decl);
988 }
989 }
990
991 fn field_decl(&mut self, f: &Node) -> Option<(Arc<str>, Ty)> {
992 if !f.is_form(sym::FIELD) || f.args.len() != 2 {
993 return None;
994 }
995 let name = f.args[0].as_var()?.name.clone();
996 Some((name, self.ty_from_node(&f.args[1])))
997 }
998
999 fn register_type_constructors(&mut self) {
1000 let decls: Vec<TyDecl> = self.types.values().cloned().collect();
1001 for d in decls {
1002 match &d {
1003 TyDecl::Union { name, variants, .. } => {
1004 for v in variants {
1005 self.globals.push(Binding {
1006 name: v.name.clone(),
1007 scopes: ScopeSet::empty(),
1008 kind: BindKind::Ctor(name.clone(), v.name.clone()),
1009 });
1010 }
1011 }
1012 TyDecl::Model { name, .. } | TyDecl::Newtype { name, .. } => {
1013 self.globals.push(Binding {
1014 name: name.clone(),
1015 scopes: ScopeSet::empty(),
1016 kind: BindKind::Model(name.clone()),
1017 });
1018 }
1019 TyDecl::Alias { .. } => {}
1020 }
1021 }
1022 }
1023
1024 fn collect_signatures(&mut self, items: &[&Node]) {
1027 for item in items {
1028 let (item, _) = self.undecorate(item);
1029 if !item.is_form(sym::DEF) || item.args.len() < 5 {
1030 continue;
1031 }
1032 let Some(name) = item.args[0].as_var().map(|s| s.name.clone()) else {
1033 continue;
1034 };
1035 let typarams = self.bind_typarams(&item.args[1], &name);
1038 let params: Vec<Ty> = item.args[2]
1039 .args
1040 .iter()
1041 .map(|p| {
1042 if p.is_form(sym::ANNOT) && p.args.len() == 2 {
1043 self.ty_from_node(&p.args[1])
1044 } else {
1045 self.error(
1046 "B0303",
1047 "a top-level parameter needs a type annotation",
1048 p.span(),
1049 );
1050 self.subst.fresh()
1051 }
1052 })
1053 .collect();
1054 let ret = match item.args[3].args.first() {
1055 Some(t) => self.ty_from_node(t),
1056 None => {
1057 self.error(
1058 "B0304",
1059 format!("`{name}` needs a return type"),
1060 item.args[0].span(),
1061 );
1062 self.subst.fresh()
1063 }
1064 };
1065 self.typarams.clear();
1066 if sym::RESERVED_FORMS.contains(&name.as_ref()) {
1069 self.diags.push(
1070 Diagnostic::error(
1071 "B0312",
1072 format!("`{name}` is a form of the language, so nothing can be named it"),
1073 item.args[0].span(),
1074 )
1075 .with_primary_label("this name is matched as syntax before it is resolved")
1076 .with_note(
1077 "the checker recognises these heads structurally, so a definition with one \
1078 of their names would be shadowed by the form and never called",
1079 ),
1080 );
1081 }
1082 let declared = self.declared_row(item.args.get(4));
1083
1084 let rv = self.subst.fresh_row_var();
1087 let generic_rows = self.generalisable_rows(¶ms, &ret);
1092 let mut latent = Row::var(rv);
1093 latent.tails.extend(generic_rows.iter().copied());
1094 self.schemes.insert(
1095 name.clone(),
1096 Scheme {
1097 vars: Vec::new(),
1098 row_vars: generic_rows.clone(),
1099 params: typarams,
1100 ty: Ty::fun_eff(params, ret, latent),
1101 },
1102 );
1103 self.def_row.insert(name.clone(), rv);
1104 self.generic_rows.insert(name.clone(), generic_rows);
1105 self.declared.insert(name.clone(), declared);
1106 self.globals.push(Binding {
1107 name: name.clone(),
1108 scopes: ScopeSet::empty(),
1109 kind: BindKind::Global(name.clone()),
1110 });
1111 }
1112 }
1113
1114 fn generalisable_rows(&self, params: &[Ty], ret: &Ty) -> Vec<RowVarId> {
1123 let mut in_ret = Vec::new();
1124 row_vars_of(ret, &mut in_ret);
1125 if !in_ret.is_empty() {
1126 return Vec::new();
1127 }
1128 let mut out = Vec::new();
1129 for p in params {
1130 row_vars_of(p, &mut out);
1131 }
1132 out.sort_unstable();
1133 out.dedup();
1134 out
1135 }
1136
1137 fn bind_typarams(&mut self, node: &Node, def_name: &str) -> Vec<Arc<str>> {
1144 self.typarams.clear();
1145 let mut out: Vec<Arc<str>> = Vec::new();
1146 for p in &node.args {
1147 let Some(name) = traits::typaram_name(p) else {
1148 continue;
1149 };
1150 if self.types.contains_key(&name) || prelude::builtin_arity(&name).is_some() {
1151 self.diags.push(
1152 Diagnostic::error(
1153 "B0314",
1154 format!("`{name}` is already a type, so `{def_name}` cannot take it as a parameter"),
1155 p.span(),
1156 )
1157 .with_primary_label("this name already names a type")
1158 .with_note(
1159 "a type parameter is a name the definition invents, and one that shadowed \
1160 an existing type would make its signature read as though it mentioned that \
1161 type",
1162 ),
1163 );
1164 continue;
1165 }
1166 if out.contains(&name) {
1167 self.error(
1168 "B0315",
1169 format!("`{name}` is repeated in `{def_name}`'s type parameters"),
1170 p.span(),
1171 );
1172 continue;
1173 }
1174 out.push(name.clone());
1175 self.typarams.insert(name);
1176 }
1177 out
1178 }
1179
1180 fn declared_row(&mut self, uses: Option<&Node>) -> Row {
1183 let mut row = Row::empty();
1184 let Some(u) = uses else { return row };
1185 for e in &u.args {
1186 let text = written_form(e).unwrap_or_default();
1190 if let Some(atom) = Effect::parse(&text) {
1194 row.add(atom);
1195 } else if let Some(alias) = self.row_aliases.get(text.as_str()).cloned() {
1196 row = row.union(&alias);
1197 } else {
1198 let mut d = Diagnostic::error(
1199 "B0305",
1200 format!(
1201 "`{}` is neither an effect nor a row",
1202 if text.is_empty() { "?" } else { &text }
1203 ),
1204 e.span(),
1205 );
1206 if let Some(path) = text.strip_prefix("fs(").and_then(|r| r.strip_suffix(')')) {
1210 d = d.with_note(format!(
1211 "`fs` is two atoms: write `fs.read({path})` or `fs.write({path})`. One \
1212 name for both could not say whether a mount needs to be writable, or \
1213 whether two children of a `parallel:` scope may touch it at once"
1214 ));
1215 }
1216 self.diags.push(d);
1217 }
1218 }
1219 row
1220 }
1221
1222 fn collect_row_aliases(&mut self, items: &[&Node]) {
1230 for item in items {
1231 let (item, _) = self.undecorate(item);
1232 if !item.is_form(sym::ROW) || item.args.len() < 2 {
1233 continue;
1234 }
1235 let Some(name) = item.args[0].as_var().map(|s| s.name.clone()) else {
1236 continue;
1237 };
1238 if self.row_aliases.contains_key(&name) {
1239 self.error(
1240 "B0394",
1241 format!("row `{name}` is declared twice"),
1242 item.span(),
1243 );
1244 continue;
1245 }
1246 let body = Node::form("uses", item.args[1..].to_vec(), item.span());
1247 let row = self.declared_row(Some(&body));
1248 self.row_aliases.insert(name, row);
1249 }
1250 }
1251
1252 fn collect_identity(&mut self, items: &[&Node]) {
1259 for item in items {
1260 let (item, _) = self.undecorate(item);
1261 if !item.is_form(sym::IDENTITY) || item.args.len() != 1 {
1262 continue;
1263 }
1264 let span = item.span();
1265 if self.identity.is_some() {
1266 self.error("B0359", "identity is declared twice", span);
1267 continue;
1268 }
1269 let call = &item.args[0];
1273 if call.is_form("managed") {
1274 if !call.args.is_empty() {
1275 self.error("B0359", "`managed()` takes no arguments", span);
1276 continue;
1277 }
1278 self.identity = Some(IdentityDecl::Managed { span });
1279 continue;
1280 }
1281 if !call.is_form("external") {
1282 self.error(
1283 "B0359",
1284 "`external(issuer=\"…\")` and `managed()` are the identity providers",
1285 span,
1286 );
1287 continue;
1288 }
1289 let issuer: Option<String> = call.args.iter().find_map(|a| {
1292 let named = a.is_form(sym::KW_ARG)
1293 && a.args.first().and_then(|n| n.as_var()).map(|v| &*v.name) == Some("issuer");
1294 if !named {
1295 return None;
1296 }
1297 match a.args.get(1).and_then(|v| v.as_lit()) {
1298 Some(beck_syntax::Lit::Str(s)) => Some(s.to_string()),
1299 _ => None,
1300 }
1301 });
1302 let Some(issuer) = issuer else {
1303 self.error(
1304 "B0359",
1305 "`external` needs `issuer=\"https://…\"`, written as a literal",
1306 span,
1307 );
1308 continue;
1309 };
1310 let Some(host) = issuer_host(&issuer) else {
1311 self.error(
1312 "B0359",
1313 format!("`{issuer}` is not an https URL whose host an egress rule could name"),
1314 span,
1315 );
1316 continue;
1317 };
1318 self.identity = Some(IdentityDecl::External {
1319 issuer: Arc::from(issuer.trim_end_matches('/')),
1320 host: Arc::from(host.as_str()),
1321 span,
1322 });
1323 }
1324 }
1325
1326 fn collect_signal_names(&mut self, items: &[&Node]) {
1333 for item in items {
1334 let (item, _) = self.undecorate(item);
1335 if !(item.is_form(sym::LET) || item.is_form(sym::VAR)) || item.args.len() != 2 {
1336 continue;
1337 }
1338 let target = &item.args[0];
1339 let (name_node, annot) = if target.is_form(sym::ANNOT) && target.args.len() == 2 {
1340 (&target.args[0], Some(&target.args[1]))
1341 } else {
1342 (target, None)
1343 };
1344 let Some(s) = name_node.as_var() else {
1345 continue;
1346 };
1347 let ty = match annot {
1348 Some(t) => self.ty_from_node(t),
1349 None => self.subst.fresh(),
1350 };
1351 self.schemes.insert(s.name.clone(), Scheme::mono(ty));
1352 self.globals.push(Binding {
1353 name: s.name.clone(),
1354 scopes: s.scopes.clone(),
1355 kind: BindKind::Global(s.name.clone()),
1356 });
1357 }
1358 }
1359
1360 fn check_items(mut self, items: &[&Node], name: String) -> Program {
1361 let docs = crate::docgen::collect_docs(items);
1362 let mut defs = BTreeMap::new();
1363 let mut def_order = Vec::new();
1364 let mut signals = Vec::new();
1365 let mut test_items: Vec<&Node> = Vec::new();
1366
1367 for item in items {
1368 let (inner, decos) = self.undecorate_full(item);
1369 let declares_signal = decos.declares_signal;
1370 let tier_is_annotated = decos.tier.is_some();
1371 let (tier, tier_span) = decos.tier.unwrap_or((Tier::Any, inner.span()));
1372
1373 if let Some((_, span)) = decos.render {
1377 if !(inner.is_form(sym::LET) || inner.is_form(sym::VAR)) {
1378 self.diags.push(
1379 Diagnostic::error(
1380 "B0405",
1381 "only a component can say where it renders",
1382 span,
1383 )
1384 .with_primary_label("`@render` belongs on a `Signal[Html]` declaration")
1385 .with_note(
1386 "A definition is unplaced code, compiled to every tier that needs it \
1387 (§3.3). What renders where is decided per component, which is what a \
1388 page signal is.",
1389 ),
1390 );
1391 }
1392 }
1393
1394 if inner.is_form(sym::DEF) {
1395 if let Some(def) =
1396 self.check_def(inner, tier, tier_span, tier_is_annotated, declares_signal)
1397 {
1398 def_order.push(def.name.clone());
1399 defs.insert(def.name.clone(), def);
1400 }
1401 } else if inner.is_form(sym::LET) || inner.is_form(sym::VAR) {
1402 if let Some(mut s) = self.check_signal(inner, tier, tier_span, tier_is_annotated) {
1403 s.render = decos.render;
1404 signals.push(s);
1405 }
1406 } else if inner.is_form(sym::TEST) || inner.is_form(sym::PROPERTY) {
1407 test_items.push(inner);
1412 } else if inner.is_form(sym::MODEL)
1413 || inner.is_form(sym::UNION)
1414 || inner.is_form(sym::TYPE)
1415 || inner.is_form(sym::NEWTYPE)
1416 || inner.is_form(sym::IMPORT)
1417 || inner.is_form(sym::TRAIT)
1418 || inner.is_form(sym::IMPL)
1419 || inner.is_form(sym::ROW)
1420 || inner.is_form(sym::IDENTITY)
1421 {
1422 } else {
1426 self.error("B0307", "unsupported top-level item", inner.span());
1427 }
1428 }
1429
1430 let subjects = self.test_subjects(&signals, &defs);
1438 let broken_topology =
1439 !signals.is_empty() && (subjects.state.is_none() || subjects.event.is_none());
1440 let mut tests = Vec::new();
1441 if !broken_topology {
1442 for item in test_items {
1443 if let Some(t) = self.check_test(item, &subjects, &defs) {
1444 tests.push(t);
1445 }
1446 }
1447 }
1448
1449 for def in defs.values_mut() {
1454 def.ret = self.subst.resolve(&def.ret);
1455 for p in &mut def.params {
1456 p.2 = self.subst.resolve(&p.2);
1457 }
1458 resolve_types(&mut def.body, &self.subst);
1459 def.row = self.subst.resolve_row(&def.row);
1460 let mut bindable = Vec::new();
1470 for (_, _, t) in &def.params {
1471 self.subst.free_row_vars(t, &mut bindable);
1472 }
1473 def.row.tails.retain(|v| bindable.contains(v));
1474 def.effects = def.row.atoms.iter().cloned().collect();
1475 }
1476 for t in &mut tests {
1477 for clause in &mut t.clauses {
1478 for c in clause_cores_mut(clause) {
1479 resolve_types(c, &self.subst);
1480 }
1481 }
1482 for p in &mut t.params {
1483 p.2 = self.subst.resolve(&p.2);
1484 }
1485 }
1486 for s in &mut signals {
1487 s.ty = self.subst.resolve(&s.ty);
1488 resolve_types(&mut s.expr, &self.subst);
1489 s.row = self.subst.resolve_row(&s.row);
1490 s.row.tails.clear();
1493 s.effects = s.row.atoms.iter().cloned().collect();
1494 }
1495
1496 for name in &def_order {
1501 let Some(def) = defs.get(name) else { continue };
1502 if !def.row_is_declared {
1503 continue;
1504 }
1505 let undeclared: Vec<Effect> = def
1506 .effects
1507 .iter()
1508 .filter(|e| !e.is_ambient() && !def.declared_effects.contains(e))
1509 .cloned()
1510 .collect();
1511 if undeclared.is_empty() {
1512 continue;
1513 }
1514 let names: Vec<String> = undeclared.iter().map(|e| e.name()).collect();
1515 self.diags.push(
1516 Diagnostic::error(
1517 "B0370",
1518 format!("`{name}` performs more than its signature declares"),
1519 def.span,
1520 )
1521 .with_primary_label(format!("undeclared: {}", names.join(", ")))
1522 .with_note(
1523 "a `uses` clause is the published bound, and widening it is a breaking API \
1524 change — so the compiler will not widen it for you",
1525 )
1526 .with_fix(format!(
1527 "declare it: `uses {}`",
1528 def.effects
1529 .iter()
1530 .filter(|e| !e.is_ambient())
1531 .map(|e| e.name())
1532 .collect::<Vec<_>>()
1533 .join(", ")
1534 )),
1535 );
1536 }
1537
1538 let traits: Vec<ty::TraitSig> = self
1541 .own_traits
1542 .iter()
1543 .filter_map(|n| self.traits.get(n).map(|d| d.sig.clone()))
1544 .collect();
1545 let impls: Vec<ty::ImplSig> = self
1550 .own_impls
1551 .iter()
1552 .filter_map(|k| self.impls.get(k).map(|d| d.sig.clone()))
1553 .map(|mut sig| {
1554 let head = sig.head();
1555 if let Some(decl) = self.traits.get(&sig.trait_name) {
1556 for m in &decl.sig.methods {
1557 let mangled = traits::mangle(&sig.trait_name, &m.name, &head);
1558 let Some(def) = defs.get(&mangled) else {
1559 continue;
1560 };
1561 let row: Vec<Effect> = def
1562 .effects
1563 .iter()
1564 .filter(|e| !e.is_ambient())
1565 .cloned()
1566 .collect();
1567 if !row.is_empty() {
1568 sig.effects.push((m.name.clone(), row));
1569 }
1570 }
1571 }
1572 sig
1573 })
1574 .collect();
1575
1576 Program {
1577 name,
1578 types: self.types,
1579 traits,
1580 impls,
1581 own_types: self.own_types,
1582 imports: Vec::new(),
1583 defs,
1584 def_order,
1585 signals,
1586 tests,
1587 docs,
1588 identity: self.identity,
1589 }
1590 }
1591
1592 fn check_def(
1601 &mut self,
1602 item: &Node,
1603 tier: Tier,
1604 tier_span: Span,
1605 tier_is_annotated: bool,
1606 declares_signal: bool,
1607 ) -> Option<Def> {
1608 let name = item.args[0].as_var()?.name.clone();
1609 let scheme = self.schemes.get(&name)?.clone();
1610 let Ty::Fun(param_tys, ret, latent) = scheme.ty.clone() else {
1611 return None;
1612 };
1613 self.typarams = scheme.params.iter().cloned().collect();
1616
1617 let before = self.locals.len();
1618 let mut params = Vec::new();
1619 for (p, ty) in item.args[2].args.iter().zip(¶m_tys) {
1620 let target = if p.is_form(sym::ANNOT) { &p.args[0] } else { p };
1621 let Some(s) = target.as_var() else { continue };
1622 let id = self.fresh_var();
1623 params.push((id, s.name.clone(), ty.clone()));
1624 self.locals.push(Binding {
1625 name: s.name.clone(),
1626 scopes: s.scopes.clone(),
1627 kind: BindKind::Local(id, ty.clone()),
1628 });
1629 }
1630
1631 let body_node = item.args.get(5);
1632 let span = item.span();
1633 if body_node.is_none() && self.mode == Mode::Module {
1636 self.diags.push(
1637 Diagnostic::error("B0335", format!("`{name}` has no body"), span)
1638 .with_primary_label("a signature with nothing behind it")
1639 .with_note(
1640 "a bodyless `def` is a declaration, which is what a `.becki` interface file \
1641 is made of; an ordinary module has to define what it declares",
1642 ),
1643 );
1644 }
1645 let (body, performed) = self.in_scope(|ck| match body_node {
1646 Some(b) => ck.block(&b.args, Some(&ret)),
1647 None => Core::new(CoreKind::Const(Const::Unit), ret.as_ref().clone(), span),
1651 });
1652 if body_node.is_some() {
1653 self.unify(&body.ty, &ret, body.span, "return type");
1654 }
1655 self.locals.truncate(before);
1656 self.typarams.clear();
1657
1658 let declared = self.declared.get(&name).cloned().unwrap_or_default();
1659 let inferred = performed.union(&declared);
1663 if let Some(rv) = self.def_row.get(&name).copied() {
1664 let generic: &[RowVarId] = self
1670 .generic_rows
1671 .get(&name)
1672 .map(|v| v.as_slice())
1673 .unwrap_or(&[]);
1674 let mut own = self.subst.resolve_row(&inferred);
1675 own.tails.retain(|t| !generic.contains(t));
1676 self.subst.bind_row(rv, own);
1677 }
1678
1679 let lam = Core {
1680 kind: CoreKind::Lam {
1681 params: params.iter().map(|(id, _, _)| *id).collect(),
1682 body: Arc::new(body),
1683 },
1684 ty: Ty::Fun(param_tys, ret.clone(), latent),
1685 tier,
1686 span,
1687 last_use: false,
1688 order: crate::fields::UNORDERED,
1689 locals: 0,
1690 };
1691
1692 let mut declared_effects: Vec<Effect> = declared.atoms.iter().cloned().collect();
1693 declared_effects.sort();
1694 let row_is_declared = !declared_effects.is_empty();
1703 let bounds = self.bounds_of_def(&name);
1704 Some(Def {
1705 name,
1706 typarams: scheme.params.clone(),
1707 params,
1708 ret: *ret,
1709 body: lam,
1710 tier,
1711 effects: Vec::new(),
1712 row: inferred,
1713 declared_effects,
1714 bounds,
1715 row_is_declared,
1716 tier_is_annotated,
1717 is_declaration: body_node.is_none(),
1718 declares_signal,
1719 span,
1720 tier_span,
1721 })
1722 }
1723
1724 fn check_signal(
1725 &mut self,
1726 item: &Node,
1727 tier: Tier,
1728 tier_span: Span,
1729 tier_is_annotated: bool,
1730 ) -> Option<SignalDecl> {
1731 let target = &item.args[0];
1732 let (name_node, annot) = if target.is_form(sym::ANNOT) && target.args.len() == 2 {
1733 (&target.args[0], Some(&target.args[1]))
1734 } else {
1735 (target, None)
1736 };
1737 let name = name_node.as_var()?.name.clone();
1738 let expected = annot.map(|t| self.ty_from_node(t));
1739
1740 let (expr, row) = self.in_scope(|ck| ck.expr(&item.args[1], expected.as_ref()));
1744 if let Some(e) = &expected {
1745 self.unify(&expr.ty, e, expr.span, "declared type");
1746 }
1747
1748 if let Some(pre) = self.schemes.get(&name).cloned() {
1751 self.unify(&expr.ty, &pre.ty, expr.span, "declared type");
1752 }
1753
1754 Some(SignalDecl {
1755 name,
1756 ty: expr.ty.clone(),
1757 expr,
1758 tier,
1759 effects: Vec::new(),
1760 row,
1761 tier_is_annotated,
1762 render: None,
1763 span: item.span(),
1764 tier_span,
1765 })
1766 }
1767
1768 fn ty_from_node(&mut self, n: &Node) -> Ty {
1771 if !self.enter(n.span()) {
1772 return self.subst.fresh();
1773 }
1774 let out = self.ty_from_node_inner(n);
1775 self.nesting.leave();
1776 out
1777 }
1778
1779 fn enter_block(&mut self, span: Span) -> bool {
1793 if self.block_nesting.enter() {
1794 return true;
1795 }
1796 if self.block_nesting.should_report() {
1797 let note = self.block_nesting.note_about("statements in one block");
1798 self.diags.push(
1799 Diagnostic::error("B0389", "this block has too many statements to check", span)
1800 .with_primary_label("the checker gave up here")
1801 .with_note(note),
1802 );
1803 }
1804 false
1805 }
1806
1807 fn enter(&mut self, span: Span) -> bool {
1808 if self.nesting.enter() {
1809 return true;
1810 }
1811 if self.nesting.should_report() {
1812 let note = self.nesting.note();
1813 self.diags.push(
1814 Diagnostic::error("B0390", "the expression nests too deep to check", span)
1815 .with_primary_label("the checker gave up here")
1816 .with_note(note),
1817 );
1818 }
1819 false
1820 }
1821
1822 fn ty_from_node_inner(&mut self, n: &Node) -> Ty {
1823 let span = n.span();
1824 if n.has_head("fn-type") && !n.args.is_empty() {
1829 let params: Vec<Ty> = n.args[..n.args.len() - 1]
1830 .iter()
1831 .map(|a| self.ty_from_node(a))
1832 .collect();
1833 let ret = self.ty_from_node(&n.args[n.args.len() - 1]);
1834 return Ty::fun_eff(params, ret, self.subst.fresh_row());
1838 }
1839 let Some(name) = n.head_name() else {
1840 self.error("B0308", "expected a type", span);
1841 return self.subst.fresh();
1842 };
1843 if self.typarams.contains(name) {
1848 if !n.args.is_empty() {
1849 self.error(
1850 "B0313",
1851 format!("`{name}` is a type parameter, so it takes no type arguments"),
1852 span,
1853 );
1854 }
1855 return Ty::con(name);
1856 }
1857 if let Some(v) = self.decl_typarams.get(name).copied() {
1861 if !n.args.is_empty() {
1862 self.error(
1863 "B0313",
1864 format!("`{name}` is a type parameter, so it takes no type arguments"),
1865 span,
1866 );
1867 }
1868 return Ty::Var(v);
1869 }
1870
1871 let args: Vec<Ty> = n.args.iter().map(|a| self.ty_from_node(a)).collect();
1872
1873 if let Some(TyDecl::Alias { ty, params, .. }) = self.types.get(name) {
1878 let (ty, params) = (ty.clone(), params.clone());
1879 if !self.check_arity(name, ¶ms, args.len(), span) {
1880 return self.subst.fresh();
1881 }
1882 return ty::instantiate_decl(&ty, &args);
1883 }
1884
1885 let params = match prelude::builtin_arity(name) {
1886 Some(a) => letters(a),
1887 None => match self.types.get(name) {
1888 Some(d) => d.params().to_vec(),
1889 None => {
1890 self.error("B0310", format!("cannot find type `{name}`"), span);
1891 return self.subst.fresh();
1892 }
1893 },
1894 };
1895 if !self.check_arity(name, ¶ms, args.len(), span) {
1896 return self.subst.fresh();
1897 }
1898 Ty::Con(Arc::from(name), args)
1899 }
1900
1901 fn check_arity(&mut self, name: &str, params: &[Arc<str>], got: usize, span: Span) -> bool {
1911 let arity = params.len();
1912 if arity == got {
1913 return true;
1914 }
1915 let d = Diagnostic::error(
1916 "B0311",
1917 format!("`{name}` takes {arity} type argument(s), got {got}"),
1918 span,
1919 );
1920 self.diags.push(if arity == 0 {
1921 d.with_primary_label("this type takes no arguments")
1922 } else {
1923 let written = params
1924 .iter()
1925 .map(|p| p.as_ref())
1926 .collect::<Vec<_>>()
1927 .join(", ");
1928 let one = params[0].as_ref();
1929 let d = d.with_primary_label(format!("write `{name}[{written}]`"));
1930 if got < arity {
1931 d.with_note(format!(
1934 "each argument is a concrete type, or a parameter bound where this mention \
1935 is — `def f[{one}]`, `model M[{one}]`, or an `impl[{one}]` head"
1936 ))
1937 } else {
1938 d
1939 }
1940 });
1941 false
1942 }
1943
1944 fn join(&mut self, then: &Ty, alt: &Ty, span: Span) -> Ty {
1951 match self.subst.unify_join(then, alt) {
1952 Ok(ty) => ty,
1953 Err(e) => {
1954 let msg = self.mismatch(e, "the two branches");
1955 self.error("B0320", msg, span);
1956 then.clone()
1957 }
1958 }
1959 }
1960
1961 fn unify(&mut self, actual: &Ty, expected: &Ty, span: Span, what: &str) {
1962 if let Err(e) = self.subst.unify(actual, expected) {
1963 let msg = self.mismatch(e, what);
1964 self.error("B0320", msg, span);
1965 }
1966 }
1967
1968 fn mismatch(&self, e: Mismatch, what: &str) -> String {
1969 match e {
1970 Mismatch::Different(pair) => {
1971 let (a, b) = *pair;
1972 format!("{what} mismatch: expected `{b}`, found `{a}`")
1973 }
1974 Mismatch::Arity(a, b) => {
1975 format!("{what} takes {b} argument(s), got {a}")
1976 }
1977 Mismatch::Infinite => format!("{what} would be an infinite type"),
1978 Mismatch::Effects(e) => {
1979 format!("{what} may not perform {{{e}}} here")
1980 }
1981 Mismatch::UnknownEffects => {
1982 format!(
1983 "{what} may perform effects this context does not allow: one side's effects \
1984 are not decided here, and the other's are fixed and empty"
1985 )
1986 }
1987 }
1988 }
1989
1990 fn resolve(&self, s: &Symbol) -> Option<&Binding> {
1995 self.locals
1996 .iter()
1997 .rev()
1998 .chain(self.globals.iter().rev())
1999 .find(|b| b.name == s.name && b.scopes.is_subset_of(&s.scopes))
2000 }
2001
2002 fn block(&mut self, stmts: &[Node], expected: Option<&Ty>) -> Core {
2005 let span = stmts.first().map(|s| s.span()).unwrap_or(Span::NONE);
2006 self.block_from(stmts, expected, span)
2007 }
2008
2009 fn block_from(&mut self, stmts: &[Node], expected: Option<&Ty>, span: Span) -> Core {
2010 let Some((first, rest)) = stmts.split_first() else {
2011 return Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span);
2012 };
2013 if !self.enter_block(first.span()) {
2014 return Core::new(CoreKind::Const(Const::Unit), self.subst.fresh(), span);
2015 }
2016 let out = self.block_step(first, rest, expected, span);
2017 self.block_nesting.leave();
2018 out
2019 }
2020
2021 fn block_step(
2023 &mut self,
2024 first: &Node,
2025 rest: &[Node],
2026 expected: Option<&Ty>,
2027 span: Span,
2028 ) -> Core {
2029 if first.is_form(sym::RETURN) {
2030 if !rest.is_empty() {
2031 self.diags.push(Diagnostic::warning(
2032 "B0330",
2033 "statements after `return` are unreachable",
2034 rest[0].span(),
2035 ));
2036 }
2037 return match first.args.first() {
2038 Some(e) => self.expr(e, expected),
2039 None => Core::new(CoreKind::Const(Const::Unit), Ty::unit(), first.span()),
2040 };
2041 }
2042
2043 if (first.is_form(sym::LET) || first.is_form(sym::VAR)) && first.args.len() == 2 {
2044 let target = &first.args[0];
2045 let (name_node, annot) = if target.is_form(sym::ANNOT) && target.args.len() == 2 {
2046 (&target.args[0], Some(&target.args[1]))
2047 } else {
2048 (target, None)
2049 };
2050 let want = annot.map(|t| self.ty_from_node(t));
2051 let value = self.expr(&first.args[1], want.as_ref());
2052 if let Some(w) = &want {
2053 self.unify(&value.ty, w, value.span, "declared type");
2054 }
2055 let id = self.fresh_var();
2056 if let Some(s) = name_node.as_var() {
2057 self.locals.push(Binding {
2058 name: s.name.clone(),
2059 scopes: s.scopes.clone(),
2060 kind: BindKind::Local(id, value.ty.clone()),
2061 });
2062 }
2063 let body = self.block_from(rest, expected, span);
2064 self.locals.pop();
2065 let ty = body.ty.clone();
2066 return Core::new(
2067 CoreKind::Let {
2068 var: id,
2069 value: Box::new(value),
2070 body: Box::new(body),
2071 },
2072 ty,
2073 first.span(),
2074 );
2075 }
2076
2077 if first.is_form(sym::FOR) || first.is_form(sym::WHILE) {
2078 self.diags.push(
2079 Diagnostic::error("B0331", "loops are not available in Phase 1", first.span())
2080 .with_primary_label("no statement-level iteration yet")
2081 .with_note(
2082 "everything is an expression and `var` is not yet mutable, so a loop has \
2083 nothing to accumulate into",
2084 )
2085 .with_fix("use `map_list`, `filter_list` or `fold`"),
2086 );
2087 return Core::new(CoreKind::Const(Const::Unit), Ty::unit(), first.span());
2088 }
2089
2090 if first.is_form(sym::IF) && !rest.is_empty() && first.args.len() >= 2 {
2094 let cond = self.expr(&first.args[0], Some(&Ty::bool_()));
2095 self.unify(&cond.ty, &Ty::bool_(), cond.span, "condition");
2096 let then = self.body_expr(&first.args[1], expected);
2097 let alt = match first.args.get(2) {
2098 Some(explicit) => {
2099 self.diags.push(Diagnostic::warning(
2100 "B0330",
2101 "statements after an `if`/`else` that both return are unreachable",
2102 rest[0].span(),
2103 ));
2104 self.body_expr(explicit, expected)
2105 }
2106 None => self.block_from(rest, expected, span),
2107 };
2108 let ty = self.join(&then.ty, &alt.ty, then.span);
2109 return Core::new(
2110 CoreKind::If {
2111 cond: Box::new(cond),
2112 then: Box::new(then),
2113 alt: Box::new(alt),
2114 },
2115 ty,
2116 first.span(),
2117 );
2118 }
2119
2120 if rest.is_empty() {
2122 return self.expr(first, expected);
2123 }
2124 let value = self.expr(first, None);
2125 let body = self.block_from(rest, expected, span);
2126 let id = self.fresh_var();
2127 let ty = body.ty.clone();
2128 Core::new(
2129 CoreKind::Let {
2130 var: id,
2131 value: Box::new(value),
2132 body: Box::new(body),
2133 },
2134 ty,
2135 first.span(),
2136 )
2137 }
2138
2139 fn body_expr(&mut self, n: &Node, expected: Option<&Ty>) -> Core {
2141 if n.is_form(sym::DO) {
2142 let before = self.locals.len();
2143 let out = self.block(&n.args, expected);
2144 self.locals.truncate(before);
2145 out
2146 } else {
2147 self.expr(n, expected)
2148 }
2149 }
2150
2151 fn expr(&mut self, n: &Node, expected: Option<&Ty>) -> Core {
2154 if !self.enter(n.span()) {
2155 return Core::new(CoreKind::Const(Const::Unit), self.subst.fresh(), n.span());
2156 }
2157 let out = self.expr_inner(n, expected);
2158 self.nesting.leave();
2159 out
2160 }
2161
2162 fn expr_inner(&mut self, n: &Node, expected: Option<&Ty>) -> Core {
2163 let span = n.span();
2164
2165 if n.args.len() == 2 && (n.has_head("|") || n.has_head("@")) {
2169 let op = if n.has_head("|") { "|" } else { "@" };
2170 self.error(
2171 "B0357",
2172 format!("`{op}` is only meaningful in a `case` pattern"),
2173 span,
2174 );
2175 return Core::new(CoreKind::Const(Const::Unit), self.subst.fresh(), span);
2176 }
2177
2178 if let Some(l) = n.as_lit() {
2179 return match l {
2180 Lit::Int(i) => Core::new(CoreKind::Const(Const::Int(*i)), Ty::int(), span),
2181 Lit::Float(f) => {
2182 Core::new(CoreKind::Const(Const::Float(*f)), Ty::con(Ty::FLOAT), span)
2183 }
2184 Lit::Bool(b) => Core::new(CoreKind::Const(Const::Bool(*b)), Ty::bool_(), span),
2185 Lit::Str(s) => Core::new(CoreKind::Const(Const::Str(s.clone())), Ty::str_(), span),
2186 Lit::Keyword(k) => {
2187 Core::new(CoreKind::Const(Const::Str(k.clone())), Ty::str_(), span)
2188 }
2189 };
2190 }
2191
2192 if let Some(s) = n.as_var() {
2193 if s.as_str() == "unit" {
2194 return Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span);
2195 }
2196 return self.var_ref(s, span);
2197 }
2198
2199 let head = n.head_name().unwrap_or("");
2200 match head {
2201 sym::DO => self.body_expr(n, expected),
2202 sym::IF if n.args.len() >= 2 => {
2203 let cond = self.expr(&n.args[0], Some(&Ty::bool_()));
2204 self.unify(&cond.ty, &Ty::bool_(), cond.span, "condition");
2205 let then = self.body_expr(&n.args[1], expected);
2206 let alt = match n.args.get(2) {
2207 Some(a) => self.body_expr(a, expected),
2208 None => Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span),
2209 };
2210 let ty = self.join(&then.ty, &alt.ty, alt.span);
2211 Core::new(
2212 CoreKind::If {
2213 cond: Box::new(cond),
2214 then: Box::new(then),
2215 alt: Box::new(alt),
2216 },
2217 ty,
2218 span,
2219 )
2220 }
2221 sym::FN if n.args.len() == 2 => self.lambda(n, expected, span),
2222 sym::RAISE if n.args.len() == 1 => self.raise_expr(&n.args[0], span),
2223 sym::TRY if n.args.len() == 1 => self.try_expr(&n.args[0], expected, span),
2224 sym::PARALLEL if n.args.len() == 1 => self.parallel_expr(&n.args[0], expected, span),
2225 sym::MATCH if !n.args.is_empty() => self.match_expr(n, expected, span),
2226 sym::LIST => {
2227 let elem = expected
2228 .and_then(|t| match t {
2229 Ty::Con(c, args) if c.as_ref() == Ty::LIST && args.len() == 1 => {
2230 Some(args[0].clone())
2231 }
2232 _ => None,
2233 })
2234 .unwrap_or_else(|| self.subst.fresh());
2235 let items: Vec<Core> = n
2236 .args
2237 .iter()
2238 .map(|a| {
2239 let c = self.expr(a, Some(&elem));
2240 self.unify(&c.ty, &elem, c.span, "list element");
2241 c
2242 })
2243 .collect();
2244 Core::new(CoreKind::ListLit(items), Ty::list(elem), span)
2245 }
2246 sym::MAP => {
2247 let k = self.subst.fresh();
2248 let v = self.subst.fresh();
2249 let mut pairs = Vec::new();
2250 for pair in n.args.chunks(2) {
2251 if pair.len() != 2 {
2252 break;
2253 }
2254 let kc = self.expr(&pair[0], Some(&k));
2255 self.unify(&kc.ty, &k, kc.span, "map key");
2256 let vc = self.expr(&pair[1], Some(&v));
2257 self.unify(&vc.ty, &v, vc.span, "map value");
2258 pairs.push((kc, vc));
2259 }
2260 Core::new(CoreKind::MapLit(pairs), Ty::map(k, v), span)
2261 }
2262 sym::RECORD => self.record_lit(n, expected, span),
2263 sym::DOT if n.args.len() >= 2 => self.dot(n, span),
2264 "index" if n.args.len() == 2 => {
2265 let base = self.expr(&n.args[0], None);
2266 let key = self.expr(&n.args[1], None);
2267 let v = self.subst.fresh();
2268 self.unify(
2269 &base.ty,
2270 &Ty::map(key.ty.clone(), v.clone()),
2271 span,
2272 "indexing",
2273 );
2274 Core::new(
2275 CoreKind::Prim {
2276 op: Prim::MapGet,
2277 args: vec![base, key],
2278 },
2279 Ty::option(v),
2280 span,
2281 )
2282 }
2283 "+" | "-" | "*" | "/" if n.args.len() == 2 => {
2284 let op = match head {
2285 "+" => Prim::Add,
2286 "-" => Prim::Sub,
2287 "*" => Prim::Mul,
2288 _ => Prim::Div,
2289 };
2290 self.arith(op, &n.args[0], &n.args[1], expected, span)
2291 }
2292 "negate" if n.args.len() == 1 => {
2293 let arg = self.expr(&n.args[0], expected);
2294 let want = self.numeric_of(&arg.ty, expected).unwrap_or_else(Ty::int);
2295 self.unify(&arg.ty, &want, arg.span, "operand of `-`");
2296 Core::new(
2297 CoreKind::Prim {
2298 op: Prim::Neg,
2299 args: vec![arg],
2300 },
2301 want,
2302 span,
2303 )
2304 }
2305 "abs" if n.args.len() == 1 && n.applied => {
2306 let arg = self.expr(&n.args[0], expected);
2310 let want = match self.numeric_of(&arg.ty, expected) {
2311 Some(t) => t,
2312 None => Ty::int(),
2313 };
2314 self.unify(&arg.ty, &want, arg.span, "operand of `abs`");
2315 Core::new(
2316 CoreKind::Prim {
2317 op: Prim::Abs,
2318 args: vec![arg],
2319 },
2320 want,
2321 span,
2322 )
2323 }
2324 sym::QUOTE => {
2325 self.error("B0332", "a `quote` survived macro expansion", span);
2326 Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span)
2327 }
2328 sym::KW_ARG => {
2329 self.error("B0333", "a keyword argument outside a call", span);
2330 Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span)
2331 }
2332 _ if n.applied => self.call(n, expected, span),
2333 _ => {
2334 self.error("B0334", "unsupported expression", span);
2335 Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span)
2336 }
2337 }
2338 }
2339
2340 fn arith(
2351 &mut self,
2352 op: Prim,
2353 lhs_node: &Node,
2354 rhs_node: &Node,
2355 expected: Option<&Ty>,
2356 span: Span,
2357 ) -> Core {
2358 let lhs = self.expr(lhs_node, None);
2359 let rhs = self.expr(rhs_node, None);
2360 let is_str = op == Prim::Add
2362 && (self.subst.resolve(&lhs.ty).con_name() == Some(Ty::STR)
2363 || self.subst.resolve(&rhs.ty).con_name() == Some(Ty::STR)
2364 || expected
2365 .map(|t| t.con_name() == Some(Ty::STR))
2366 .unwrap_or(false));
2367 let numeric = if is_str {
2368 Some(Ty::str_())
2369 } else {
2370 self.numeric_of(&lhs.ty, None)
2371 .or_else(|| self.numeric_of(&rhs.ty, None))
2372 .or_else(|| expected.and_then(|t| self.numeric_of(t, None)))
2373 };
2374 if numeric.is_none() {
2379 if let Some(core) = self.arith_through_num(op, &lhs, &rhs, span) {
2380 return core;
2381 }
2382 }
2383 let want = numeric.unwrap_or_else(Ty::int);
2384 let label = format!("operand of `{}`", op.name());
2385 self.unify(&lhs.ty, &want, lhs.span, &label);
2386 self.unify(&rhs.ty, &want, rhs.span, &label);
2387 Core::new(
2388 CoreKind::Prim {
2389 op,
2390 args: vec![lhs, rhs],
2391 },
2392 want,
2393 span,
2394 )
2395 }
2396
2397 fn arith_through_num(&mut self, op: Prim, lhs: &Core, rhs: &Core, span: Span) -> Option<Core> {
2409 let method: Arc<str> = Arc::from(prelude::num_method(op)?);
2410 let num: Arc<str> = Arc::from(prelude::NUM);
2411 let ty = [&lhs.ty, &rhs.ty]
2412 .into_iter()
2413 .map(|t| self.subst.resolve(t))
2414 .find(|t| self.joins_the_tower(t))?;
2415 let head = ty.con_name().map(Arc::<str>::from)?;
2416 let known = self.impls.contains_key(&(num.clone(), head.clone()))
2417 || self
2418 .resolve(&Symbol::new(traits::mangle(&num, &method, &head)))
2419 .is_some();
2420 if !known {
2421 if self.types.contains_key(&head) {
2425 self.diags.push(
2426 Diagnostic::error(
2427 "B0387",
2428 format!("`{head}` does not implement `{num}`"),
2429 span,
2430 )
2431 .with_primary_label(format!("`{}` resolves through it", op.name()))
2432 .with_fix(format!("write `impl {num} for {head}`")),
2433 );
2434 return Some(Core::new(CoreKind::Const(Const::Unit), ty, span));
2435 }
2436 return None;
2437 }
2438 let func = self.dictionary(&num, &method, &ty, span)?;
2439 let Ty::Fun(params, ret, row) = self.subst.resolve(&func.ty) else {
2440 return None;
2441 };
2442 self.perform(&row);
2443 let label = format!("operand of `{}`", op.name());
2444 self.unify(&lhs.ty, ¶ms[0], lhs.span, &label);
2445 self.unify(&rhs.ty, ¶ms[1], rhs.span, &label);
2446 Some(Core::new(
2447 CoreKind::App {
2448 func: Box::new(func),
2449 args: vec![lhs.clone(), rhs.clone()],
2450 },
2451 *ret,
2452 span,
2453 ))
2454 }
2455
2456 fn joins_the_tower(&self, t: &Ty) -> bool {
2462 !matches!(
2463 t.con_name(),
2464 None | Some(Ty::INT) | Some(Ty::FLOAT) | Some(Ty::STR)
2465 )
2466 }
2467
2468 fn numeric_of(&mut self, ty: &Ty, expected: Option<&Ty>) -> Option<Ty> {
2474 for candidate in [Some(ty), expected].into_iter().flatten() {
2475 match self.subst.resolve(candidate).con_name() {
2476 Some(Ty::INT) => return Some(Ty::int()),
2477 Some(Ty::FLOAT) => return Some(Ty::con(Ty::FLOAT)),
2478 _ => {}
2479 }
2480 }
2481 None
2482 }
2483
2484 fn var_ref(&mut self, s: &Symbol, span: Span) -> Core {
2485 let Some(b) = self.resolve(s).cloned() else {
2486 if self.parallel_siblings.contains(&s.name) {
2488 self.error(
2489 "B0398",
2490 format!(
2491 "`{s}` is another child of this `parallel:` scope, so it has not run yet — \
2492 children cannot see each other, which is what lets them run together"
2493 ),
2494 span,
2495 );
2496 } else {
2497 self.error("B0340", format!("cannot find `{s}` in this scope"), span);
2498 }
2499 let t = self.subst.fresh();
2500 return Core::new(CoreKind::Const(Const::Unit), t, span);
2501 };
2502 match b.kind {
2503 BindKind::Local(id, ty) => Core::new(CoreKind::Var(id), ty, span),
2504 BindKind::TraitMethod(m) => {
2507 let owner = self.trait_methods.get(&m).cloned();
2508 self.diags.push(
2509 Diagnostic::error(
2510 "B0386",
2511 format!("`{m}` is a trait method and cannot be used as a value"),
2512 span,
2513 )
2514 .with_primary_label(match &owner {
2515 Some(t) => format!("declared by trait `{t}`"),
2516 None => "a trait method".into(),
2517 })
2518 .with_note(
2519 "which implementation it means is decided by the type of its receiver, so \
2520 it has to be called rather than passed; passing one needs bounds on a type \
2521 parameter, which is not built",
2522 ),
2523 );
2524 Core::new(CoreKind::Const(Const::Unit), self.subst.fresh(), span)
2525 }
2526 BindKind::Global(name) => {
2527 if self.dicts.contains_key(&name) {
2528 self.diags.push(
2529 Diagnostic::error(
2530 "B0386",
2531 format!("`{name}` has a bound, so it cannot be used as a value"),
2532 span,
2533 )
2534 .with_note(
2535 "a bounded definition is handed its implementations at the call site, \
2536 and a reference that is never called has no call site to hand them \
2537 over",
2538 ),
2539 );
2540 }
2541 let ty = self
2542 .schemes
2543 .get(&name)
2544 .map(|sc| self.subst.instantiate(sc))
2545 .unwrap_or_else(|| self.subst.fresh());
2546 Core::new(CoreKind::Global(name), ty, span)
2547 }
2548 BindKind::Prim(p) => {
2549 let (_, scheme) = self.prims.get(p.name()).cloned().expect("prim registered");
2552 let ty = self.subst.instantiate(&scheme);
2553 let Ty::Fun(params, ret, latent) = ty.clone() else {
2556 return Core::new(
2557 CoreKind::Prim {
2558 op: p,
2559 args: vec![],
2560 },
2561 ty,
2562 span,
2563 );
2564 };
2565 let ids: Vec<VarId> = params.iter().map(|_| self.fresh_var()).collect();
2566 let args: Vec<Core> = ids
2567 .iter()
2568 .zip(¶ms)
2569 .map(|(id, t)| Core::new(CoreKind::Var(*id), t.clone(), span))
2570 .collect();
2571 Core::new(
2572 CoreKind::Lam {
2573 params: ids.into(),
2574 body: Arc::new(Core::new(
2575 CoreKind::Prim { op: p, args },
2576 *ret.clone(),
2577 span,
2578 )),
2579 },
2580 Ty::Fun(params, ret, latent),
2581 span,
2582 )
2583 }
2584 BindKind::Ctor(union, variant) => self.make(&union, Some(&variant), &[], span),
2585 BindKind::Model(model) => self.make(&model, None, &[], span),
2586 }
2587 }
2588
2589 fn raise_expr(&mut self, arg: &Node, span: Span) -> Core {
2597 let value = self.expr(arg, None);
2598 let ty = self.subst.resolve(&value.ty);
2599 let Some(name) = error_ty_name(&ty) else {
2600 self.error(
2601 "B0391",
2602 format!("a raised value must have a declared type, and this one is `{ty}`"),
2603 value.span,
2604 );
2605 return Core::new(CoreKind::Const(Const::Unit), self.subst.fresh(), span);
2606 };
2607 self.perform(&Row::of([Effect::Raises(name)]));
2610 Core::new(
2611 CoreKind::Prim {
2612 op: Prim::Raise,
2613 args: vec![value],
2614 },
2615 self.subst.fresh(),
2616 span,
2617 )
2618 }
2619
2620 fn try_expr(&mut self, body: &Node, expected: Option<&Ty>, span: Span) -> Core {
2637 let (inner_expected, expected_error) = match expected.map(|t| self.subst.resolve(t)) {
2640 Some(Ty::Con(c, args)) if c.as_ref() == Ty::RESULT && args.len() == 2 => (
2641 Some(args[0].clone()),
2642 match self.subst.resolve(&args[1]) {
2643 Ty::Con(e, es) if es.is_empty() => Some(e),
2644 _ => None,
2645 },
2646 ),
2647 _ => (None, None),
2648 };
2649
2650 let outer = std::mem::take(&mut self.row);
2651 let before = self.locals.len();
2652 let core = self.body_expr(body, inner_expected.as_ref());
2653 self.locals.truncate(before);
2654 let inner = self
2657 .subst
2658 .resolve_row(&std::mem::replace(&mut self.row, outer));
2659
2660 let mut raised: Vec<Arc<str>> = Vec::new();
2661 for atom in &inner.atoms {
2662 if let Effect::Raises(t) = atom {
2663 if !raised.contains(t) {
2664 raised.push(t.clone());
2665 }
2666 }
2667 }
2668 raised.sort();
2669
2670 let error = match expected_error {
2671 Some(e) => e,
2672 None => match raised.len() {
2673 1 => raised[0].clone(),
2674 0 => {
2675 self.error(
2676 "B0392",
2677 "nothing here can fail, and nothing says what this would catch",
2678 span,
2679 );
2680 return core;
2681 }
2682 _ => {
2683 let names: Vec<String> = raised.iter().map(|t| format!("`{t}`")).collect();
2684 self.error(
2685 "B0393",
2686 format!(
2687 "this block can fail in {} ways ({}), so say which one to catch — a \
2688 `Result[T, E]` on the enclosing signature is how",
2689 raised.len(),
2690 names.join(", ")
2691 ),
2692 span,
2693 );
2694 raised[0].clone()
2695 }
2696 },
2697 };
2698
2699 let mut rest = Row::empty();
2703 rest.tails = inner.tails.clone();
2704 for atom in &inner.atoms {
2705 if !matches!(atom, Effect::Raises(t) if *t == error) {
2706 rest.atoms.insert(atom.clone());
2707 }
2708 }
2709 self.perform(&rest);
2710
2711 let value_ty = core.ty.clone();
2712 let result_ty = Ty::app(Ty::RESULT, vec![value_ty, Ty::con(&error)]);
2713 let thunk = Core::new(
2714 CoreKind::Lam {
2715 params: Arc::from(Vec::new()),
2716 body: Arc::new(core),
2717 },
2718 self.subst.fresh(),
2719 span,
2720 );
2721 Core::new(
2722 CoreKind::Prim {
2723 op: Prim::Try,
2724 args: vec![
2725 thunk,
2726 Core::new(CoreKind::Const(Const::Str(error.clone())), Ty::str_(), span),
2727 ],
2728 },
2729 result_ty,
2730 span,
2731 )
2732 }
2733
2734 fn parallel_expr(&mut self, body: &Node, expected: Option<&Ty>, span: Span) -> Core {
2754 let stmts: &[Node] = if body.is_form(sym::DO) {
2755 &body.args
2756 } else {
2757 std::slice::from_ref(body)
2758 };
2759
2760 let children = stmts
2763 .iter()
2764 .take_while(|s| (s.is_form(sym::LET) || s.is_form(sym::VAR)) && s.args.len() == 2)
2765 .count();
2766 if children < 2 {
2767 self.error(
2768 "B0397",
2769 format!(
2770 "a `parallel:` scope runs its bindings as children, and this one has {children}"
2771 ),
2772 span,
2773 );
2774 let before = self.locals.len();
2775 let out = self.block_from(stmts, expected, span);
2776 self.locals.truncate(before);
2777 return out;
2778 }
2779
2780 self.perform(&Row::of([Effect::Spawn]));
2784
2785 let outer_siblings = std::mem::take(&mut self.parallel_siblings);
2786 let before = self.locals.len();
2787 let mut thunks = Vec::with_capacity(children);
2788 let mut bound: Vec<(VarId, Ty)> = Vec::with_capacity(children);
2789 let mut names: Vec<(Option<Symbol>, Ty)> = Vec::with_capacity(children);
2790
2791 for stmt in &stmts[..children] {
2792 let target = &stmt.args[0];
2793 let (name_node, annot) = if target.is_form(sym::ANNOT) && target.args.len() == 2 {
2794 (&target.args[0], Some(&target.args[1]))
2795 } else {
2796 (target, None)
2797 };
2798 let want = annot.map(|t| self.ty_from_node(t));
2799 let (value, row) = self.in_scope(|ck| ck.expr(&stmt.args[1], want.as_ref()));
2802 if let Some(w) = &want {
2803 self.unify(&value.ty, w, value.span, "declared type");
2804 }
2805 let row = self.subst.resolve_row(&row);
2806 let refused: Vec<String> = row
2807 .atoms
2808 .iter()
2809 .filter(|a| observable_order(a))
2810 .map(|a| format!("`{}`", a.name()))
2811 .collect();
2812 if !refused.is_empty() {
2813 self.error(
2814 "B0399",
2815 format!(
2816 "a child of a `parallel:` scope may not perform {} — another child would \
2817 be able to tell what order they ran in",
2818 refused.join(", ")
2819 ),
2820 value.span,
2821 );
2822 }
2823 self.perform(&row);
2824
2825 let ty = value.ty.clone();
2826 let vspan = value.span;
2827 thunks.push(Core::new(
2828 CoreKind::Lam {
2829 params: Arc::from(Vec::new()),
2830 body: Arc::new(value),
2831 },
2832 Ty::fun_eff(Vec::new(), ty.clone(), row),
2833 vspan,
2834 ));
2835 let id = self.fresh_var();
2836 bound.push((id, ty.clone()));
2837 names.push((name_node.as_var().cloned(), ty));
2838 if let Some(s) = name_node.as_var() {
2839 self.parallel_siblings.push(s.name.clone());
2840 }
2841 }
2842
2843 self.parallel_siblings = outer_siblings;
2845 for ((id, _), (name, ty)) in bound.iter().zip(names.iter()) {
2846 if let Some(s) = name {
2847 self.locals.push(Binding {
2848 name: s.name.clone(),
2849 scopes: s.scopes.clone(),
2850 kind: BindKind::Local(*id, ty.clone()),
2851 });
2852 }
2853 }
2854 let tail = self.block_from(&stmts[children..], expected, span);
2855 self.locals.truncate(before);
2856
2857 let tail_ty = tail.ty.clone();
2858 let param_tys: Vec<Ty> = bound.iter().map(|(_, t)| t.clone()).collect();
2859 let ids: Vec<VarId> = bound.iter().map(|(id, _)| *id).collect();
2860 let k = Core::new(
2861 CoreKind::Lam {
2862 params: ids.into(),
2863 body: Arc::new(tail),
2864 },
2865 Ty::fun_eff(param_tys, tail_ty.clone(), Row::empty()),
2869 span,
2870 );
2871 let mut args = thunks;
2872 args.push(k);
2873 Core::new(
2874 CoreKind::Prim {
2875 op: Prim::Parallel,
2876 args,
2877 },
2878 tail_ty,
2879 span,
2880 )
2881 }
2882
2883 fn lambda(&mut self, n: &Node, expected: Option<&Ty>, span: Span) -> Core {
2884 let want: Option<(Vec<Ty>, Ty)> = expected.and_then(|t| match self.subst.resolve(t) {
2885 Ty::Fun(ps, r, _) => Some((ps, *r)),
2886 _ => None,
2887 });
2888 let before = self.locals.len();
2889 let mut ids = Vec::new();
2890 let mut tys = Vec::new();
2891 for (i, p) in n.args[0].args.iter().enumerate() {
2892 let (target, annot) = if p.is_form(sym::ANNOT) && p.args.len() == 2 {
2893 (&p.args[0], Some(&p.args[1]))
2894 } else {
2895 (p, None)
2896 };
2897 let ty = match annot {
2898 Some(t) => self.ty_from_node(t),
2899 None => want
2900 .as_ref()
2901 .and_then(|(ps, _)| ps.get(i).cloned())
2902 .unwrap_or_else(|| self.subst.fresh()),
2903 };
2904 let id = self.fresh_var();
2905 if let Some(s) = target.as_var() {
2906 self.locals.push(Binding {
2907 name: s.name.clone(),
2908 scopes: s.scopes.clone(),
2909 kind: BindKind::Local(id, ty.clone()),
2910 });
2911 }
2912 ids.push(id);
2913 tys.push(ty);
2914 }
2915 let ret_want = want.as_ref().map(|(_, r)| r.clone());
2916 let (body, row) = self.in_scope(|ck| ck.body_expr(&n.args[1], ret_want.as_ref()));
2919 self.locals.truncate(before);
2920 let ret = body.ty.clone();
2921 Core::new(
2922 CoreKind::Lam {
2923 params: ids.into(),
2924 body: Arc::new(body),
2925 },
2926 Ty::fun_eff(tys, ret, row),
2927 span,
2928 )
2929 }
2930
2931 fn match_expr(&mut self, n: &Node, expected: Option<&Ty>, span: Span) -> Core {
2932 let scrutinee = self.expr(&n.args[0], None);
2933 let scrut_ty = self.subst.resolve(&scrutinee.ty);
2934 let result = expected.cloned().unwrap_or_else(|| self.subst.fresh());
2935
2936 let mut arms = Vec::new();
2937 for arm in &n.args[1..] {
2938 if !arm.is_form(sym::CASE) || !(2..=3).contains(&arm.args.len()) {
2941 continue;
2942 }
2943 let before = self.locals.len();
2944 let pattern = self.pattern(&arm.args[0], &scrut_ty);
2945 let guard = arm.args.get(2).map(|g| {
2948 let c = self.expr(g, Some(&Ty::bool_()));
2949 self.unify(&c.ty, &Ty::bool_(), c.span, "a `case` guard");
2950 c
2951 });
2952 let body = self.body_expr(&arm.args[1], Some(&result));
2953 self.unify(&body.ty, &result, body.span, "match arm");
2954 self.locals.truncate(before);
2955 arms.push(Arm {
2956 pattern,
2957 guard,
2958 body,
2959 span: arm.span(),
2960 });
2961 }
2962
2963 let shapes: Vec<Pattern> = arms
2971 .iter()
2972 .filter(|a| a.guard.is_none())
2973 .map(|a| a.pattern.clone())
2974 .collect();
2975 let scrut_ty = self.subst.resolve(&scrut_ty);
2978 let guarded: Vec<usize> = arms
2986 .iter()
2987 .enumerate()
2988 .filter(|(_, a)| a.guard.is_none())
2989 .map(|(i, _)| i)
2990 .collect();
2991 for i in exhaust::unreachable(&shapes, &scrut_ty, &self.types)
2992 .into_iter()
2993 .map(|i| guarded[i])
2994 {
2995 self.diags.push(
2996 Diagnostic::warning("B0355", "this case can never match", arms[i].span)
2997 .with_primary_label("the arms above it already cover every value this matches")
2998 .with_note(
2999 "an arm that cannot run is either a mistake about what the arms above it \
3000 match, or a line to delete",
3001 ),
3002 );
3003 }
3004
3005 if let exhaust::Coverage::Missing(missing) =
3006 exhaust::coverage(&shapes, &scrut_ty, &self.types)
3007 {
3008 let note = if scrut_ty.con_name() == Some(Ty::LIST) {
3009 "a list is empty or it is not, and a fold that handles only one of those is a \
3010 fold that fails on the input nobody tested"
3011 } else {
3012 "adding a variant must break every fold that consumes it — that is what makes a \
3013 missed migration a compile error rather than a 3 a.m. page"
3014 };
3015 self.diags.push(
3016 Diagnostic::error("B0341", "match is not exhaustive", span)
3017 .with_primary_label(format!("missing: {}", missing.join(", ")))
3018 .with_note(note),
3019 );
3020 }
3021
3022 Core::new(
3023 CoreKind::Match {
3024 scrutinee: Box::new(scrutinee),
3025 arms,
3026 },
3027 result,
3028 span,
3029 )
3030 }
3031
3032 fn or_pattern(&mut self, p: &Node, scrut: &Ty, span: Span) -> Pattern {
3039 let mut nodes = Vec::new();
3042 flatten_alts(p, &mut nodes);
3043
3044 let before = self.locals.len();
3045 let mut alts: Vec<Pattern> = Vec::new();
3046 let mut first: Vec<(Arc<str>, VarId, Ty)> = Vec::new();
3047 for (i, node) in nodes.iter().enumerate() {
3048 self.locals.truncate(before);
3049 let pat = self.pattern(node, scrut);
3050 let bound: Vec<(Arc<str>, VarId, Ty)> = self.locals[before..]
3051 .iter()
3052 .filter_map(|b| match &b.kind {
3053 BindKind::Local(id, ty) => Some((b.name.clone(), *id, ty.clone())),
3054 _ => None,
3055 })
3056 .collect();
3057 if i == 0 {
3058 first = bound;
3059 alts.push(pat);
3060 continue;
3061 }
3062 let missing: Vec<&str> = first
3064 .iter()
3065 .filter(|(n, _, _)| !bound.iter().any(|(m, _, _)| m == n))
3066 .map(|(n, _, _)| n.as_ref())
3067 .collect();
3068 let extra: Vec<&str> = bound
3069 .iter()
3070 .filter(|(n, _, _)| !first.iter().any(|(m, _, _)| m == n))
3071 .map(|(n, _, _)| n.as_ref())
3072 .collect();
3073 if !missing.is_empty() || !extra.is_empty() {
3074 let mut said = Vec::new();
3075 if !missing.is_empty() {
3076 said.push(format!(
3077 "this alternative does not bind {}",
3078 missing.join(", ")
3079 ));
3080 }
3081 if !extra.is_empty() {
3082 said.push(format!("only this one binds {}", extra.join(", ")));
3083 }
3084 self.diags.push(
3085 Diagnostic::error(
3086 "B0356",
3087 "the alternatives of an or-pattern bind different names",
3088 node.span(),
3089 )
3090 .with_primary_label(said.join("; "))
3091 .with_note(
3092 "every alternative has to bind the same names, because the body reads them \
3093 without knowing which one matched",
3094 ),
3095 );
3096 }
3097 let mut rename: BTreeMap<VarId, VarId> = BTreeMap::new();
3101 for (name, id, ty) in &bound {
3102 if let Some((_, target, want)) = first.iter().find(|(n, _, _)| n == name) {
3103 self.unify(ty, want, node.span(), "an or-pattern's alternatives");
3104 rename.insert(*id, *target);
3105 }
3106 }
3107 let mut pat = pat;
3108 rename_binders(&mut pat, &rename);
3109 alts.push(pat);
3110 }
3111
3112 self.locals.truncate(before);
3115 for (name, id, ty) in first {
3116 self.locals.push(Binding {
3117 name,
3118 scopes: ScopeSet::default(),
3119 kind: BindKind::Local(id, ty),
3120 });
3121 }
3122 let _ = span;
3123 Pattern::Or(alts)
3124 }
3125
3126 fn pattern(&mut self, p: &Node, scrut: &Ty) -> Pattern {
3134 let span = p.span();
3135 if !self.enter(span) {
3136 return Pattern::Wildcard;
3137 }
3138 let out = self.pattern_inner(p, scrut, span);
3139 self.nesting.leave();
3140 out
3141 }
3142
3143 fn pattern_inner(&mut self, p: &Node, scrut: &Ty, span: Span) -> Pattern {
3144 if p.has_head("|") && p.args.len() == 2 {
3145 return self.or_pattern(p, scrut, span);
3146 }
3147 if p.has_head("@") && p.args.len() == 2 {
3148 let Some(name) = p.args[0].as_var() else {
3149 self.error("B0358", "the left of `@` is a name", p.args[0].span());
3150 return self.pattern(&p.args[1], scrut);
3151 };
3152 let inner = self.pattern(&p.args[1], scrut);
3155 let id = self.fresh_var();
3156 self.locals.push(Binding {
3157 name: name.name.clone(),
3158 scopes: name.scopes.clone(),
3159 kind: BindKind::Local(id, scrut.clone()),
3160 });
3161 return Pattern::At {
3162 var: id,
3163 inner: Box::new(inner),
3164 };
3165 }
3166 if let Some(l) = p.as_lit() {
3167 return Pattern::Const(match l {
3168 Lit::Int(i) => Const::Int(*i),
3169 Lit::Float(f) => Const::Float(*f),
3170 Lit::Bool(b) => Const::Bool(*b),
3171 Lit::Str(s) | Lit::Keyword(s) => Const::Str(s.clone()),
3172 });
3173 }
3174 if let Some(s) = p.as_var() {
3175 if s.as_str() == sym::WILDCARD {
3176 return Pattern::Wildcard;
3177 }
3178 if let Some(Binding {
3180 kind: BindKind::Ctor(_, variant),
3181 ..
3182 }) = self.resolve(s).cloned()
3183 {
3184 return Pattern::Ctor {
3185 variant,
3186 binds: Vec::new(),
3187 };
3188 }
3189 let id = self.fresh_var();
3190 self.locals.push(Binding {
3191 name: s.name.clone(),
3192 scopes: s.scopes.clone(),
3193 kind: BindKind::Local(id, scrut.clone()),
3194 });
3195 return Pattern::Bind(id);
3196 }
3197
3198 if p.is_form(sym::LIST) {
3201 let elem = self.subst.fresh();
3202 self.unify(
3203 scrut,
3204 &Ty::list(elem.clone()),
3205 span,
3206 "a list pattern matches a list",
3207 );
3208 let mut items = Vec::new();
3209 let mut rest = None;
3210 for (i, item) in p.args.iter().enumerate() {
3211 let is_rest = item.is_form(sym::REST) && item.args.len() == 1;
3212 if !is_rest {
3213 items.push(self.pattern(item, &elem));
3214 continue;
3215 }
3216 if i + 1 != p.args.len() {
3217 self.error(
3218 "B0346",
3219 "`*rest` has to be the last element of a list pattern",
3220 item.span(),
3221 );
3222 continue;
3223 }
3224 let target = &item.args[0];
3227 rest = Some(match target.as_var() {
3228 Some(s) if s.as_str() == sym::WILDCARD => None,
3229 Some(s) => {
3230 let id = self.fresh_var();
3231 self.locals.push(Binding {
3232 name: s.name.clone(),
3233 scopes: s.scopes.clone(),
3234 kind: BindKind::Local(id, Ty::list(elem.clone())),
3235 });
3236 Some(id)
3237 }
3238 None => {
3239 self.error(
3240 "B0345",
3241 "the tail of a list pattern is a name, not a pattern",
3242 target.span(),
3243 );
3244 None
3245 }
3246 });
3247 }
3248 return Pattern::List { items, rest };
3249 }
3250
3251 if p.is_form(sym::REST) {
3252 self.error(
3253 "B0347",
3254 "`*name` is only meaningful inside a list pattern",
3255 span,
3256 );
3257 return Pattern::Wildcard;
3258 }
3259
3260 let Some(head) = p.head_sym().cloned() else {
3261 self.error("B0342", "unsupported pattern", span);
3262 return Pattern::Wildcard;
3263 };
3264 let Some(Binding {
3265 kind: BindKind::Ctor(union, variant),
3266 ..
3267 }) = self.resolve(&head).cloned()
3268 else {
3269 self.error("B0343", format!("`{head}` is not a constructor"), span);
3270 return Pattern::Wildcard;
3271 };
3272 let fields = match self.types.get(&union) {
3273 Some(TyDecl::Union { variants, .. }) => variants
3274 .iter()
3275 .find(|v| v.name == variant)
3276 .map(|v| v.fields.clone())
3277 .unwrap_or_default(),
3278 _ => Vec::new(),
3279 };
3280 let field_tys = self.variant_field_types(scrut, &union, &fields);
3281
3282 let mut binds = Vec::new();
3283 for (i, arg) in p.args.iter().enumerate() {
3284 let (field_name, target) = if arg.is_form(sym::KW_ARG) && arg.args.len() == 2 {
3286 (arg.args[0].as_var().map(|s| s.name.clone()), &arg.args[1])
3287 } else {
3288 (fields.get(i).map(|(n, _)| n.clone()), arg)
3289 };
3290 let Some(field_name) = field_name else {
3291 self.error("B0344", "cannot tell which field this binds", arg.span());
3292 continue;
3293 };
3294 let ty = field_tys
3295 .get(&field_name)
3296 .cloned()
3297 .unwrap_or_else(|| self.subst.fresh());
3298 binds.push((field_name, self.pattern(target, &ty)));
3299 }
3300 Pattern::Ctor { variant, binds }
3301 }
3302
3303 fn variant_field_types(
3305 &mut self,
3306 scrut: &Ty,
3307 union: &str,
3308 fields: &[(Arc<str>, Ty)],
3309 ) -> BTreeMap<Arc<str>, Ty> {
3310 let mut args: Vec<Ty> = Vec::new();
3312 if let Ty::Con(name, xs) = self.subst.resolve(scrut) {
3313 if name.as_ref() == union {
3314 args = xs;
3315 }
3316 }
3317 fields
3318 .iter()
3319 .map(|(n, t)| (n.clone(), ty::instantiate_decl(t, &args)))
3320 .collect()
3321 }
3322
3323 fn record_lit(&mut self, n: &Node, expected: Option<&Ty>, span: Span) -> Core {
3324 if let Some(Ty::Con(name, args)) = expected.map(|t| self.subst.resolve(t)) {
3327 if name.as_ref() == Ty::MAP && args.len() == 2 {
3328 let mut pairs = Vec::new();
3329 for pair in n.args.chunks(2) {
3330 if pair.len() != 2 {
3331 break;
3332 }
3333 let kc = self.expr(&pair[0], Some(&args[0]));
3334 self.unify(&kc.ty, &args[0], kc.span, "map key");
3335 let vc = self.expr(&pair[1], Some(&args[1]));
3336 self.unify(&vc.ty, &args[1], vc.span, "map value");
3337 pairs.push((kc, vc));
3338 }
3339 return Core::new(
3340 CoreKind::MapLit(pairs),
3341 Ty::map(args[0].clone(), args[1].clone()),
3342 span,
3343 );
3344 }
3345 }
3346 let Some(model) = expected
3347 .map(|t| self.subst.resolve(t))
3348 .and_then(|t| t.con_name().map(Arc::<str>::from))
3349 else {
3350 self.error("B0346", "cannot tell which model this record builds", span);
3351 return Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span);
3352 };
3353 let mut args: Vec<Node> = Vec::new();
3354 for pair in n.args.chunks(2) {
3355 if pair.len() != 2 {
3356 break;
3357 }
3358 let key = pair[0].as_keyword().unwrap_or("?");
3359 args.push(Node::form(
3360 sym::KW_ARG,
3361 vec![Node::sym(key, pair[0].span()), pair[1].clone()],
3362 pair[1].span(),
3363 ));
3364 }
3365 self.make(&model, None, &args, span)
3366 }
3367
3368 fn dot(&mut self, n: &Node, span: Span) -> Core {
3369 let base = self.expr(&n.args[0], None);
3370 let Some(name) = n.args[1].as_var().map(|s| s.name.clone()) else {
3371 self.error("B0347", "expected a field or method name", span);
3372 return Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span);
3373 };
3374 let rest = &n.args[2..];
3375
3376 if name.as_ref() == "with" {
3378 let base_ty = self.subst.resolve(&base.ty);
3379 let field_tys = self.model_fields(&base_ty);
3380 let mut fields = Vec::new();
3381 for a in rest {
3382 if !a.is_form(sym::KW_ARG) || a.args.len() != 2 {
3383 self.error("B0348", "`with` takes named fields", a.span());
3384 continue;
3385 }
3386 let Some(fname) = a.args[0].as_var().map(|s| s.name.clone()) else {
3387 continue;
3388 };
3389 let want = field_tys.get(&fname).cloned();
3390 let value = self.expr(&a.args[1], want.as_ref());
3391 match want {
3392 Some(w) => self.unify(&value.ty, &w, value.span, &format!("field `{fname}`")),
3393 None => self.error(
3394 "B0349",
3395 format!("no field `{fname}` on `{base_ty}`"),
3396 a.span(),
3397 ),
3398 }
3399 fields.push((fname, value));
3400 }
3401 let ty = base.ty.clone();
3402 return Core::new(
3403 CoreKind::With {
3404 base: Box::new(base),
3405 fields,
3406 },
3407 ty,
3408 span,
3409 );
3410 }
3411
3412 if rest.is_empty() {
3414 let base_ty = self.subst.resolve(&base.ty);
3415 if let Some(ty) = self.model_fields(&base_ty).get(&name).cloned() {
3416 return Core::new(
3417 CoreKind::Field {
3418 base: Box::new(base),
3419 name,
3420 },
3421 ty,
3422 span,
3423 );
3424 }
3425 }
3426
3427 let mut call_args = vec![n.args[0].clone()];
3429 call_args.extend(rest.iter().cloned());
3430 let call = Node::form_sym(
3431 n.args[1]
3432 .head_sym()
3433 .cloned()
3434 .unwrap_or_else(|| Symbol::new(&name)),
3435 call_args,
3436 span,
3437 );
3438 if self.resolve(&Symbol::new(&name)).is_some() {
3439 return self.call(&call, None, span);
3440 }
3441 let base_ty = self.subst.resolve(&base.ty);
3442 self.error(
3443 "B0350",
3444 format!("no field or function `{name}` for `{base_ty}`"),
3445 span,
3446 );
3447 Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span)
3448 }
3449
3450 fn model_fields(&self, ty: &Ty) -> BTreeMap<Arc<str>, Ty> {
3451 let Some(name) = ty.con_name() else {
3452 return BTreeMap::new();
3453 };
3454 match self.types.get(name) {
3455 Some(TyDecl::Model { fields, .. }) => {
3456 let args: &[Ty] = match ty {
3457 Ty::Con(_, args) => args,
3458 _ => &[],
3459 };
3460 fields
3461 .iter()
3462 .map(|(n, t)| (n.clone(), ty::instantiate_decl(t, args)))
3463 .collect()
3464 }
3465 Some(TyDecl::Newtype { inner, .. }) => {
3466 BTreeMap::from([(Arc::from("value"), inner.clone())])
3467 }
3468 _ => BTreeMap::new(),
3469 }
3470 }
3471
3472 fn call(&mut self, n: &Node, expected: Option<&Ty>, span: Span) -> Core {
3473 let head = n.head_sym().cloned().unwrap_or_else(|| Symbol::new("?"));
3474
3475 if head.as_str() == sym::CALL && !n.args.is_empty() {
3477 let func = self.expr(&n.args[0], None);
3478 return self.apply_fn(func, &n.args[1..], span);
3479 }
3480
3481 match self.resolve(&head).cloned().map(|b| b.kind) {
3482 Some(BindKind::Prim(p)) => self.prim_call(p, &n.args, expected, span),
3483 Some(BindKind::TraitMethod(m)) => self.trait_call(&m, &n.args, span),
3484 Some(BindKind::Ctor(union, variant)) => {
3485 self.make(&union, Some(&variant), &n.args, span)
3486 }
3487 Some(BindKind::Model(model)) => self.make(&model, None, &n.args, span),
3488 Some(BindKind::Global(name)) => {
3489 if let Some(specs) = self.dicts.get(&name).cloned() {
3490 return self.apply_bounded(&name, &specs, &n.args, expected, span);
3491 }
3492 let ty = self
3493 .schemes
3494 .get(&name)
3495 .map(|sc| self.subst.instantiate(sc))
3496 .unwrap_or_else(|| self.subst.fresh());
3497 let func = Core::new(CoreKind::Global(name), ty, span);
3498 self.apply_fn(func, &n.args, span)
3499 }
3500 Some(BindKind::Local(id, ty)) => {
3501 let func = Core::new(CoreKind::Var(id), ty, span);
3502 self.apply_fn(func, &n.args, span)
3503 }
3504 None => {
3505 self.error("B0340", format!("cannot find `{head}` in this scope"), span);
3506 Core::new(CoreKind::Const(Const::Unit), self.subst.fresh(), span)
3507 }
3508 }
3509 }
3510
3511 fn apply_fn_with(
3517 &mut self,
3518 func: Core,
3519 done: Core,
3520 at: usize,
3521 args: &[Node],
3522 span: Span,
3523 ) -> Core {
3524 let ftype = self.subst.resolve(&func.ty);
3525 let Ty::Fun(param_tys, ret, latent) = ftype else {
3526 return self.apply_fn(func, args, span);
3527 };
3528 self.perform(&latent);
3529 if args.len() != param_tys.len() {
3530 self.error(
3531 "B0351",
3532 format!(
3533 "expected {} argument(s), got {}",
3534 param_tys.len(),
3535 args.len()
3536 ),
3537 span,
3538 );
3539 }
3540 if let Some(want) = param_tys.get(at) {
3541 self.unify(&done.ty, want, done.span, "receiver");
3542 }
3543 let mut checked = Vec::with_capacity(args.len());
3544 for (i, a) in args.iter().enumerate() {
3545 if i == at {
3546 checked.push(done.clone());
3547 continue;
3548 }
3549 let one = self.check_args(std::slice::from_ref(a), ¶m_tys[i..]);
3550 checked.extend(one);
3551 }
3552 Core::new(
3553 CoreKind::App {
3554 func: Box::new(func),
3555 args: checked,
3556 },
3557 *ret,
3558 span,
3559 )
3560 }
3561
3562 fn apply_fn(&mut self, func: Core, args: &[Node], span: Span) -> Core {
3563 let ftype = self.subst.resolve(&func.ty);
3564 let (param_tys, ret, latent) = match &ftype {
3565 Ty::Fun(ps, r, row) => (ps.clone(), (**r).clone(), row.clone()),
3566 _ => {
3567 let ps: Vec<Ty> = args.iter().map(|_| self.subst.fresh()).collect();
3568 let r = self.subst.fresh();
3569 let row = self.subst.fresh_row();
3570 self.unify(
3571 &func.ty,
3572 &Ty::fun_eff(ps.clone(), r.clone(), row.clone()),
3573 span,
3574 "callee",
3575 );
3576 (ps, r, row)
3577 }
3578 };
3579 self.perform(&latent);
3581 if args.len() != param_tys.len() {
3582 self.error(
3583 "B0351",
3584 format!(
3585 "expected {} argument(s), got {}",
3586 param_tys.len(),
3587 args.len()
3588 ),
3589 span,
3590 );
3591 }
3592 let checked = self.check_args(args, ¶m_tys);
3593 Core::new(
3594 CoreKind::App {
3595 func: Box::new(func),
3596 args: checked,
3597 },
3598 ret,
3599 span,
3600 )
3601 }
3602
3603 fn check_args(&mut self, args: &[Node], param_tys: &[Ty]) -> Vec<Core> {
3604 args.iter()
3605 .enumerate()
3606 .map(|(i, a)| {
3607 let a = if a.is_form(sym::KW_ARG) && a.args.len() == 2 {
3608 &a.args[1]
3609 } else {
3610 a
3611 };
3612 let want = param_tys.get(i).cloned();
3613 let c = self.expr(a, want.as_ref());
3614 if let Some(w) = want {
3615 self.unify(&c.ty, &w, c.span, "argument");
3616 }
3617 c
3618 })
3619 .collect()
3620 }
3621
3622 fn prim_call(&mut self, p: Prim, args: &[Node], _expected: Option<&Ty>, span: Span) -> Core {
3623 let (_, scheme) = self.prims.get(p.name()).cloned().expect("prim registered");
3624 let ty = self.subst.instantiate(&scheme);
3625 let Ty::Fun(param_tys, ret, latent) = ty else {
3626 self.error("B0352", format!("`{}` is not callable", p.name()), span);
3627 return Core::new(CoreKind::Const(Const::Unit), Ty::unit(), span);
3628 };
3629 if args.len() != param_tys.len() {
3630 self.error(
3631 "B0351",
3632 format!(
3633 "`{}` takes {} argument(s), got {}",
3634 p.name(),
3635 param_tys.len(),
3636 args.len()
3637 ),
3638 span,
3639 );
3640 }
3641
3642 if matches!(p, Prim::NewUuid | Prim::Now) && self.in_fold {
3645 self.diags.push(
3646 Diagnostic::error(
3647 "B0360",
3648 format!("`{}()` cannot be called inside a fold", p.name()),
3649 span,
3650 )
3651 .with_primary_label("this would make replay non-deterministic")
3652 .with_note(
3653 "a fold must be replay-pure: time is data on the envelope (`env.at`), and \
3654 entity ids are minted at the edge",
3655 )
3656 .with_fix("mint the id in the client's command and read it from the event"),
3657 );
3658 }
3659
3660 let was_in_fold = self.in_fold;
3661 if p == Prim::Fold {
3662 self.in_fold = true;
3663 }
3664 let checked = self.check_args(args, ¶m_tys);
3665 self.in_fold = was_in_fold;
3666 self.perform(&latent);
3669 if p == Prim::HttpFetch {
3670 self.outbound_host(checked.first(), span);
3671 }
3672 if let Some(core) = short_circuit(p, &checked, (*ret).clone(), span) {
3673 return core;
3674 }
3675
3676 Core::new(
3677 CoreKind::Prim {
3678 op: p,
3679 args: checked,
3680 },
3681 *ret,
3682 span,
3683 )
3684 }
3685
3686 fn outbound_host(&mut self, arg: Option<&Core>, span: Span) {
3699 let (Some(arg), Some(host)) = (arg, arg.and_then(crate::core::literal_str)) else {
3700 let at = arg.map(|a| a.span).unwrap_or(span);
3701 self.diags.push(
3702 Diagnostic::error(
3703 "B0395",
3704 "the host of an outbound call has to be written at the call site".to_string(),
3705 at,
3706 )
3707 .with_primary_label("this is computed, so nothing knows which host it reaches")
3708 .with_note(
3709 "`http_fetch` performs `net.out(host)`, and the cluster's egress policy is \
3710 that atom (§6.5). A host that is not written here is a call the deployment \
3711 cannot be told about",
3712 )
3713 .with_fix(
3714 "write the host as a literal and compute the path instead — or take a \
3715 closure, so the caller names its own host and the row carries it out",
3716 ),
3717 );
3718 return;
3719 };
3720 if host.as_ref() == "origin" {
3724 self.diags.push(
3725 Diagnostic::error(
3726 "B0396",
3727 "`origin` is not a host `http_fetch` can call".to_string(),
3728 arg.span,
3729 )
3730 .with_primary_label("this names the program's own origin")
3731 .with_note(
3732 "`net.out(origin)` is the one outbound atom a client tier discharges, and a \
3733 client reaches its server over the command channel rather than by fetching",
3734 )
3735 .with_fix("send a command, or name the service's own host"),
3736 );
3737 return;
3738 }
3739 if !crate::net::is_nameable_host(&host) {
3740 self.diags.push(
3741 Diagnostic::error(
3742 "B0396",
3743 format!("`{host}` is not a host `http_fetch` can call"),
3744 arg.span,
3745 )
3746 .with_primary_label("this is not a name a `uses net.out(…)` clause could write")
3747 .with_note(
3748 "the host is a DNS name — ASCII labels separated by dots — because it becomes \
3749 a NetworkPolicy peer. A scheme, a port or a path is not part of it",
3750 )
3751 .with_fix(
3752 "give the host alone; the port is a field of the request and the path \
3753 is its own argument",
3754 ),
3755 );
3756 return;
3757 }
3758 self.perform(&Row::of([Effect::NetOut(host)]));
3759 }
3760
3761 fn make(&mut self, ty_name: &str, variant: Option<&str>, args: &[Node], span: Span) -> Core {
3763 let decl = self.types.get(ty_name).cloned();
3764 let (declared, arity): (Vec<(Arc<str>, Ty)>, usize) = match (&decl, variant) {
3765 (Some(TyDecl::Union { variants, .. }), Some(v)) => {
3766 match variants.iter().find(|x| x.name.as_ref() == v) {
3767 Some(found) => (found.fields.clone(), found.fields.len()),
3768 None => {
3769 self.error("B0353", format!("no variant `{v}` on `{ty_name}`"), span);
3770 (Vec::new(), 0)
3771 }
3772 }
3773 }
3774 (Some(TyDecl::Model { fields, .. }), _) => (fields.clone(), fields.len()),
3775 (Some(TyDecl::Newtype { inner, .. }), _) => {
3776 (vec![(Arc::from("value"), inner.clone())], 1)
3777 }
3778 _ => {
3779 self.error("B0354", format!("cannot construct `{ty_name}`"), span);
3780 (Vec::new(), 0)
3781 }
3782 };
3783
3784 let param_count = decl.as_ref().map(|d| d.arity()).unwrap_or(0);
3790 let ty_args: Vec<Ty> = (0..param_count).map(|_| self.subst.fresh()).collect();
3791
3792 if args.len() != arity {
3793 self.error(
3794 "B0351",
3795 format!(
3796 "`{}` takes {arity} field(s), got {}",
3797 variant.unwrap_or(ty_name),
3798 args.len()
3799 ),
3800 span,
3801 );
3802 }
3803
3804 let mut fields = Vec::new();
3805 for (i, a) in args.iter().enumerate() {
3806 let (fname, value_node) = if a.is_form(sym::KW_ARG) && a.args.len() == 2 {
3807 (a.args[0].as_var().map(|s| s.name.clone()), &a.args[1])
3808 } else {
3809 (declared.get(i).map(|(n, _)| n.clone()), a)
3810 };
3811 let Some(fname) = fname else {
3812 self.error("B0344", "cannot tell which field this sets", a.span());
3813 continue;
3814 };
3815 let want = declared
3816 .iter()
3817 .find(|(n, _)| *n == fname)
3818 .map(|(_, t)| ty::instantiate_decl(t, &ty_args));
3819 let value = self.expr(value_node, want.as_ref());
3820 match want {
3821 Some(w) => self.unify(&value.ty, &w, value.span, &format!("field `{fname}`")),
3822 None => self.error(
3823 "B0349",
3824 format!("no field `{fname}` on `{}`", variant.unwrap_or(ty_name)),
3825 a.span(),
3826 ),
3827 }
3828 fields.push((fname, value));
3829 }
3830
3831 Core::new(
3832 CoreKind::Make {
3833 ty: Arc::from(ty_name),
3834 variant: variant.map(Arc::from),
3835 fields,
3836 },
3837 Ty::Con(Arc::from(ty_name), ty_args),
3838 span,
3839 )
3840 }
3841}
3842
3843fn written_form(n: &Node) -> Option<String> {
3845 if let Some(s) = n.as_var() {
3846 return Some(s.as_str().to_string());
3847 }
3848 if let Some(s) = n.as_str_lit() {
3849 return Some(s.to_string());
3850 }
3851 if n.is_form(sym::DOT) && n.args.len() >= 2 {
3852 let base = written_form(&n.args[0])?;
3853 let field = n.args[1].as_var()?.as_str().to_string();
3854 let rest = &n.args[2..];
3855 if rest.is_empty() {
3856 return Some(format!("{base}.{field}"));
3857 }
3858 let args: Vec<String> = rest.iter().filter_map(written_form).collect();
3859 return Some(format!("{base}.{field}({})", args.join(", ")));
3860 }
3861 let head = n.head_name()?;
3862 if n.args.is_empty() {
3863 return Some(head.to_string());
3864 }
3865 let args: Vec<String> = n.args.iter().filter_map(written_form).collect();
3866 Some(format!("{head}({})", args.join(", ")))
3867}
3868
3869fn clause_cores_mut(c: &mut crate::testing::Clause) -> Vec<&mut Core> {
3871 use crate::testing::{Clause, Count, Expectation};
3872 match c {
3873 Clause::Given { events, .. } => vec![events],
3874 Clause::When { commands, .. } => commands.iter_mut().collect(),
3875 Clause::Stub { value, .. } => vec![value],
3876 Clause::Expect { what, .. } => match what {
3877 Expectation::Holds(e) => vec![e],
3878 Expectation::PageContains { needle, .. } => vec![needle],
3879 Expectation::FoldEquals { events, .. } => vec![events],
3880 Expectation::Performed {
3881 how: Count::With(e),
3882 ..
3883 } => vec![e],
3884 _ => Vec::new(),
3885 },
3886 }
3887}
3888
3889fn letters(n: usize) -> Vec<Arc<str>> {
3894 (0..n)
3895 .map(|i| Arc::from(((b'a' + i as u8) as char).to_string().as_str()))
3896 .collect()
3897}
3898
3899fn error_ty_name(t: &Ty) -> Option<Arc<str>> {
3907 match t {
3908 Ty::Con(name, args) if args.is_empty() => match name.as_ref() {
3909 Ty::INT | Ty::FLOAT | Ty::BOOL | Ty::STR | Ty::UNIT => None,
3910 _ => Some(name.clone()),
3911 },
3912 _ => None,
3913 }
3914}
3915
3916fn observable_order(e: &Effect) -> bool {
3942 matches!(
3943 e,
3944 Effect::Ingress
3945 | Effect::Durable
3946 | Effect::Dom
3947 | Effect::FsWrite(_)
3948 | Effect::ExternalWrite(_)
3949 )
3950}
3951
3952fn resolve_types(c: &mut Core, s: &Subst) {
3953 c.ty = s.resolve(&c.ty);
3954 match &mut c.kind {
3955 CoreKind::Lam { body, .. } => resolve_types(std::sync::Arc::make_mut(body), s),
3956 CoreKind::App { func, args } => {
3957 resolve_types(func, s);
3958 for a in args {
3959 resolve_types(a, s);
3960 }
3961 }
3962 CoreKind::Prim { args, .. } => {
3963 for a in args {
3964 resolve_types(a, s);
3965 }
3966 }
3967 CoreKind::Let { value, body, .. } => {
3968 resolve_types(value, s);
3969 resolve_types(body, s);
3970 }
3971 CoreKind::If { cond, then, alt } => {
3972 resolve_types(cond, s);
3973 resolve_types(then, s);
3974 resolve_types(alt, s);
3975 }
3976 CoreKind::Match { scrutinee, arms } => {
3977 resolve_types(scrutinee, s);
3978 for a in arms {
3979 resolve_types(&mut a.body, s);
3980 }
3981 }
3982 CoreKind::Make { fields, .. } | CoreKind::With { fields, .. } => {
3983 for (_, f) in fields {
3984 resolve_types(f, s);
3985 }
3986 }
3987 CoreKind::Field { base, .. } => resolve_types(base, s),
3988 CoreKind::ListLit(xs) => {
3989 for x in xs {
3990 resolve_types(x, s);
3991 }
3992 }
3993 CoreKind::MapLit(kvs) => {
3994 for (k, v) in kvs {
3995 resolve_types(k, s);
3996 resolve_types(v, s);
3997 }
3998 }
3999 CoreKind::Const(_) | CoreKind::Var(_) | CoreKind::Global(_) => {}
4000 }
4001 if let CoreKind::With { base, .. } = &mut c.kind {
4002 resolve_types(base, s);
4003 }
4004}
4005
4006fn row_vars_of(t: &Ty, out: &mut Vec<RowVarId>) {
4008 match t {
4009 Ty::Var(_) => {}
4010 Ty::Con(_, args) => {
4011 for a in args {
4012 row_vars_of(a, out);
4013 }
4014 }
4015 Ty::Fun(ps, r, row) => {
4016 for p in ps {
4017 row_vars_of(p, out);
4018 }
4019 row_vars_of(r, out);
4020 out.extend(row.tails.iter().copied());
4021 }
4022 }
4023}
4024
4025#[cfg(test)]
4026mod tests {
4027 use crate::check_str;
4028 use crate::ty::Effect;
4029
4030 fn row_of(src: &str, name: &str) -> Vec<String> {
4032 let (program, d, map) = check_str("t.beck", src);
4033 assert!(
4034 !d.iter()
4035 .any(|x| x.code.starts_with("B03") && x.code != "B0370"),
4036 "{}",
4037 d.render(&map)
4038 );
4039 program
4040 .defs
4041 .get(name)
4042 .unwrap_or_else(|| panic!("no `{name}` in {:?}", program.defs.keys()))
4043 .effects
4044 .iter()
4045 .map(|e| e.name())
4046 .collect()
4047 }
4048
4049 fn codes(src: &str) -> Vec<&'static str> {
4050 let (_, d, _) = check_str("t.beck", src);
4051 d.iter().map(|x| x.code).collect()
4052 }
4053
4054 fn flat_body(n: usize) -> String {
4056 let mut src = String::from("def f() -> Int:\n");
4057 for i in 0..n {
4058 src.push_str(&format!(" v{i} = {i}\n"));
4059 }
4060 src.push_str(" return v0\n");
4061 src
4062 }
4063
4064 fn block_codes(src: &str) -> Vec<&'static str> {
4075 beck_diag::depth::on_the_front_end_stack(|| {
4076 let (_, d, _) = check_str("t.beck", src);
4077 d.iter().map(|x| x.code).collect()
4078 })
4079 }
4080
4081 #[test]
4082 fn a_block_past_the_ceiling_is_refused_with_a_diagnostic() {
4083 let over = beck_diag::depth::MAX_BLOCK as usize + 8;
4084 let found = block_codes(&flat_body(over));
4085 assert!(found.contains(&"B0389"), "{found:?}");
4086 assert_eq!(found.iter().filter(|c| **c == "B0389").count(), 1);
4088 }
4089
4090 #[test]
4093 fn a_long_block_under_the_ceiling_is_ordinary() {
4094 let found = block_codes(&flat_body(beck_diag::depth::MAX_BLOCK as usize - 8));
4095 assert!(found.is_empty(), "{found:?}");
4096 }
4097
4098 #[test]
4104 fn the_block_ceiling_fits_the_declared_stack() {
4105 const PROBE: usize = 400;
4106 let spent = std::thread::Builder::new()
4107 .stack_size(256 * 1024 * 1024)
4108 .spawn(|| {
4109 let src = flat_body(PROBE);
4110 beck_diag::depth::probe::stack_spent(|| check_str("probe.beck", &src))
4111 })
4112 .expect("a thread")
4113 .join()
4114 .expect("the probe checks");
4115
4116 let per_level = spent / PROBE;
4117 println!("checker: {spent} bytes for {PROBE} statements ({per_level} per statement)");
4118 let needed = beck_diag::depth::MAX_BLOCK as usize * per_level * 2;
4121 assert!(
4122 needed < beck_diag::depth::STACK_BYTES,
4123 "a ceiling of {} statements at {per_level} bytes each needs {needed} bytes with the \
4124 margin, against a declared STACK_BYTES of {} — raise the declaration or lower the \
4125 ceiling",
4126 beck_diag::depth::MAX_BLOCK,
4127 beck_diag::depth::STACK_BYTES
4128 );
4129 }
4130
4131 #[test]
4132 fn an_effect_reached_through_an_undeclared_function_is_still_inferred() {
4133 let src = "\
4137def mint() -> Str:
4138 return uuid()
4139
4140def label(prefix: Str) -> Str:
4141 return prefix + mint()
4142";
4143 assert_eq!(row_of(src, "mint"), ["nondet"]);
4144 assert_eq!(
4145 row_of(src, "label"),
4146 ["nondet"],
4147 "an effect must travel as far as the calls do"
4148 );
4149 }
4150
4151 #[test]
4152 fn referencing_a_function_performs_nothing_but_applying_it_performs_everything() {
4153 let src = "\
4156def mint() -> Str:
4157 return uuid()
4158
4159def names() -> list[Str]:
4160 return map_list([\"a\"], lambda x: x)
4161
4162def held() -> (Str) -> Str:
4163 return lambda x: x + mint()
4164
4165def used() -> Str:
4166 return mint()
4167";
4168 assert!(row_of(src, "names").is_empty());
4169 assert!(
4170 row_of(src, "held").is_empty(),
4171 "returning a function that would mint an id mints nothing"
4172 );
4173 assert_eq!(row_of(src, "used"), ["nondet"]);
4174 }
4175
4176 #[test]
4177 fn effect_polymorphism_carries_a_lambdas_row_through_map_list() {
4178 let src = "\
4181def pure_labels(xs: list[Str]) -> list[Str]:
4182 return map_list(xs, lambda x: x + \"!\")
4183
4184def minted_labels(xs: list[Str]) -> list[Str]:
4185 return map_list(xs, lambda x: x + uuid())
4186";
4187 assert!(row_of(src, "pure_labels").is_empty());
4188 assert_eq!(row_of(src, "minted_labels"), ["nondet"]);
4189 }
4190
4191 #[test]
4197 fn a_user_higher_order_function_is_polymorphic_over_its_arguments_row() {
4198 let src = "\
4199def apply(f: (Str) -> Str, x: Str) -> Str:
4200 return f(x)
4201
4202def pure_use() -> Str:
4203 return apply(lambda s: s, \"a\")
4204
4205def impure_use() -> Str:
4206 return apply(lambda s: s + uuid(), \"b\")
4207";
4208 assert!(
4209 row_of(src, "apply").is_empty(),
4210 "`apply` performs nothing of its own: {:?}",
4211 row_of(src, "apply")
4212 );
4213 assert!(
4214 row_of(src, "pure_use").is_empty(),
4215 "and a pure caller stays pure however another caller uses it: {:?}",
4216 row_of(src, "pure_use")
4217 );
4218 assert_eq!(
4219 row_of(src, "impure_use"),
4220 ["nondet"],
4221 "while the effectful caller is charged for exactly what it passed"
4222 );
4223 }
4224
4225 #[test]
4226 fn a_generalised_row_is_still_charged_to_whoever_supplies_it() {
4227 let src = "\
4231def twice(f: (Int) -> Int, n: Int) -> Int:
4232 return f(f(n))
4233
4234def stamped(n: Int) -> Int:
4235 return twice(lambda m: m + now(), n)
4236
4237def plain(n: Int) -> Int:
4238 return twice(lambda m: m + 1, n)
4239";
4240 assert!(row_of(src, "twice").is_empty());
4241 assert_eq!(row_of(src, "stamped"), ["nondet"]);
4242 assert!(row_of(src, "plain").is_empty());
4243 }
4244
4245 #[test]
4246 fn a_quantified_row_is_charged_even_when_the_body_never_calls_the_argument() {
4247 let src = "\
4255def ignore(xs: list[Int], f: (Int) -> Int) -> Int:
4256 return list_len(xs)
4257
4258def caller(xs: list[Int]) -> Int:
4259 return ignore(xs, lambda n: now())
4260";
4261 assert!(row_of(src, "ignore").is_empty());
4262 assert_eq!(row_of(src, "caller"), ["nondet"]);
4263 }
4264
4265 #[test]
4266 fn a_definition_that_returns_a_function_keeps_the_older_monomorphic_row() {
4267 let src = "\
4272def hold(f: (Int) -> Int) -> (Int) -> Int:
4273 return f
4274
4275def use_pure(n: Int) -> Int:
4276 return hold(lambda m: m + 1)(n)
4277
4278def use_impure(n: Int) -> Int:
4279 return hold(lambda m: m + now())(n)
4280";
4281 assert_eq!(
4282 row_of(src, "use_pure"),
4283 ["nondet"],
4284 "still contaminated, and this is the test that will start failing when it is not"
4285 );
4286 }
4287
4288 #[test]
4289 fn mutual_recursion_needs_no_ordering() {
4290 let src = "\
4293def ping(n: Int) -> Str:
4294 if n < 1:
4295 return uuid()
4296 return pong(n - 1)
4297
4298def pong(n: Int) -> Str:
4299 return ping(n - 1)
4300";
4301 assert_eq!(row_of(src, "ping"), ["nondet"]);
4302 assert_eq!(row_of(src, "pong"), ["nondet"]);
4303 }
4304
4305 #[test]
4306 fn a_declared_row_is_a_bound_and_exceeding_it_is_an_error() {
4307 let src = "\
4309def charge(amount: Int) -> Str uses net.out(payments.example.com):
4310 return uuid()
4311";
4312 assert!(codes(src).contains(&"B0370"), "{:?}", codes(src));
4313
4314 let ok = "\
4316def charge(amount: Int) -> Str uses net.out(payments.example.com), nondet:
4317 return uuid()
4318";
4319 assert!(!codes(ok).contains(&"B0370"), "{:?}", codes(ok));
4320 let (program, _, _) = check_str("t.beck", ok);
4321 let row = &program.defs["charge"].row;
4322 assert!(row
4323 .atoms
4324 .contains(&Effect::NetOut("payments.example.com".into())));
4325 assert!(row.atoms.contains(&Effect::Nondet));
4326 }
4327
4328 #[test]
4329 fn an_outbound_call_performs_the_host_it_names() {
4330 let src = "\
4333def fetch_rate() -> Str uses net.out(rates.example.com), raises(HttpError):
4334 r = http_fetch(\"rates.example.com\", HttpRequest(method=\"GET\", path=\"/usd\", headers={}, body=\"\", port=80, tls=False, secrets={}))
4335 return r.body
4336";
4337 assert_eq!(
4338 row_of(src, "fetch_rate"),
4339 ["net.out(rates.example.com)", "raises(HttpError)"]
4340 );
4341 }
4342
4343 #[test]
4344 fn an_outbound_call_to_a_host_it_cannot_name_is_refused() {
4345 let req =
4346 "HttpRequest(method=\"GET\", path=\"/\", headers={}, body=\"\", port=80, tls=False, secrets={})";
4347 let computed = format!(
4349 "def go(host: Str) -> Str uses net.out(x.example.com), raises(HttpError):\n \
4350 return http_fetch(host, {req}).body\n"
4351 );
4352 assert!(
4353 codes(&computed).contains(&"B0395"),
4354 "{:?}",
4355 codes(&computed)
4356 );
4357
4358 for bad in ["https://x.example.com", "x.example.com:8080", "origin"] {
4360 let src = format!(
4361 "def go() -> Str uses net.out(x.example.com), raises(HttpError):\n \
4362 return http_fetch(\"{bad}\", {req}).body\n"
4363 );
4364 assert!(codes(&src).contains(&"B0396"), "{bad}: {:?}", codes(&src));
4365 }
4366 }
4367
4368 #[test]
4369 fn a_declared_effect_survives_an_empty_body() {
4370 let src = "\
4373def charge(amount: Int) -> Str uses net.out(payments.example.com):
4374 return \"receipt\"
4375";
4376 assert_eq!(row_of(src, "charge"), ["net.out(payments.example.com)"]);
4377 }
4378
4379 #[test]
4380 fn ambient_effects_are_carried_but_never_printed_in_a_signature() {
4381 let src = "\
4382def audit(what: Str) -> Str uses log:
4383 return what
4384";
4385 let (program, _, _) = check_str("t.beck", src);
4386 let def = &program.defs["audit"];
4387 assert_eq!(def.effects, vec![Effect::Ambient(crate::ty::Ambient::Log)]);
4388 assert!(
4389 def.row.visible().is_empty(),
4390 "§3.2 elides the ambient set from signatures"
4391 );
4392 }
4393
4394 const TREE: &str = "\
4397union Tree[T]:
4398 Leaf(value: T)
4399 Node(kids: list[Tree[T]])
4400
4401def count[T](t: Tree[T]) -> Int:
4402 match t:
4403 case Leaf(value):
4404 return 1
4405 case Node(kids):
4406 return list_len(kids)
4407";
4408
4409 #[test]
4410 fn a_declaration_may_take_a_type_parameter_and_mention_itself_under_one() {
4411 assert_eq!(codes(TREE), Vec::<&str>::new());
4412 }
4413
4414 #[test]
4415 fn a_parameterised_declaration_is_a_different_type_at_each_argument() {
4416 let src = format!(
4419 "{TREE}
4420def ints() -> Tree[Int]:
4421 return Leaf(value=1)
4422
4423def strs() -> Tree[Str]:
4424 return ints()
4425"
4426 );
4427 assert!(codes(&src).contains(&"B0320"), "{:?}", codes(&src));
4428 }
4429
4430 #[test]
4431 fn a_pattern_binds_the_argument_the_scrutinee_carries() {
4432 let ok = format!(
4434 "{TREE}
4435def first(t: Tree[Str]) -> Str:
4436 match t:
4437 case Leaf(value):
4438 return value
4439 case Node(kids):
4440 return \"\"
4441"
4442 );
4443 assert_eq!(codes(&ok), Vec::<&str>::new());
4444
4445 let bad = ok.replace("return value", "return value + 1");
4446 assert!(!codes(&bad).is_empty(), "a `Str` is not an `Int`");
4447 }
4448
4449 #[test]
4450 fn a_mention_carries_one_argument_per_declared_parameter() {
4451 for (src, why) in [
4452 ("union Box[T]:\n Held(value: T)\n\ndef f(b: Box) -> Int:\n return 1\n", "none"),
4453 (
4454 "union Box[T]:\n Held(value: T)\n\ndef f(b: Box[Int, Str]) -> Int:\n return 1\n",
4455 "two",
4456 ),
4457 ] {
4458 assert!(codes(src).contains(&"B0311"), "{why}: {:?}", codes(src));
4459 }
4460 }
4461
4462 #[test]
4468 fn the_spelling_a_missing_type_argument_suggests_is_one_that_compiles() {
4469 let bare = "\
4470type Set[T] = newtype[Map[T, Bool]]
4471
4472trait Sized:
4473 def size(self) -> Int
4474
4475impl Sized for Set:
4476 def size(self):
4477 return map_len(self.value)
4478";
4479 let (_, d, map) = check_str("t.beck", bare);
4480 let text = d.render(&map);
4481 assert!(text.contains("write `Set[T]`"), "{text}");
4482 assert!(
4483 !text.contains("Set[_]"),
4484 "there is no wildcard type:\n{text}"
4485 );
4486
4487 let fixed = bare.replace("impl Sized for Set:", "impl[T] Sized for Set[T]:");
4488 assert_eq!(
4489 codes(&fixed),
4490 Vec::<&str>::new(),
4491 "the suggestion has to check clean"
4492 );
4493 }
4494
4495 #[test]
4496 fn a_parameter_a_declaration_never_mentions_is_still_a_parameter() {
4497 let src = "\
4500model Tag[T]:
4501 label: Str
4502
4503def a() -> Tag[Int]:
4504 return Tag(label=\"a\")
4505
4506def b() -> Tag[Str]:
4507 return a()
4508";
4509 assert!(codes(src).contains(&"B0320"), "{:?}", codes(src));
4510 let bare = src.replace("Tag[Int]", "Tag");
4511 assert!(codes(&bare).contains(&"B0311"), "{:?}", codes(&bare));
4512 }
4513
4514 #[test]
4515 fn a_type_parameter_may_not_shadow_a_type_or_repeat_itself() {
4516 let shadow = "model Note:\n text: Str\n\nmodel Box[Note]:\n held: Note\n";
4517 assert!(codes(shadow).contains(&"B0314"), "{:?}", codes(shadow));
4518
4519 let repeat = "model Pair[T, T]:\n a: T\n b: T\n";
4520 assert!(codes(repeat).contains(&"B0315"), "{:?}", codes(repeat));
4521 }
4522
4523 #[test]
4532 fn a_declaration_may_not_take_a_builtin_types_name() {
4533 for name in [
4534 "Int", "Str", "Bool", "Float", "Unit", "Html", "Attr", "list", "Map", "Stream",
4535 "Signal", "Envelope", "secret", "internal", "Option", "Result",
4536 ] {
4537 let src = format!("model {name}:\n held: Int\n");
4538 assert!(
4539 codes(&src).contains(&"B0317"),
4540 "`model {name}` was accepted: {:?}",
4541 codes(&src)
4542 );
4543 let alias = format!("type {name} = Int\n");
4544 assert!(
4545 codes(&alias).contains(&"B0317"),
4546 "`type {name}` was accepted: {:?}",
4547 codes(&alias)
4548 );
4549 }
4550
4551 let src = "model Int:\n held: Int\n\ndef f(n: Int) -> Int:\n return n + 1\n";
4554 assert_eq!(
4555 codes(src),
4556 vec!["B0317"],
4557 "refusing the declaration should not cascade"
4558 );
4559
4560 let fine = "model Note:\n text: Str\n";
4562 assert!(codes(fine).is_empty(), "{:?}", codes(fine));
4563 }
4564
4565 #[test]
4566 fn a_parameterised_alias_is_expanded_and_applied() {
4567 let src = "\
4570type Pairs[T] = list[Map[T, T]]
4571
4572def f(xs: Pairs[Int]) -> Int:
4573 return list_len(xs)
4574
4575def g(xs: Pairs[Str]) -> Int:
4576 return f(xs)
4577";
4578 let (_, d, map) = check_str("t.beck", src);
4579 let text = d.render(&map);
4580 assert!(text.contains("B0320"), "{text}");
4581 assert!(
4582 !text.contains("Pairs"),
4583 "an alias is transparent, so nothing downstream should still be talking about it:\n{text}"
4584 );
4585 }
4586
4587 #[test]
4588 fn a_definitions_parameter_and_a_declarations_parameter_do_not_meet() {
4589 let src = "\
4592union Box[T]:
4593 Held(value: T)
4594
4595def unwrap[T](b: Box[T]) -> T:
4596 match b:
4597 case Held(value):
4598 return value
4599";
4600 assert_eq!(codes(src), Vec::<&str>::new());
4601
4602 let bad = src.replace("-> T:", "-> Int:");
4603 assert!(!codes(&bad).is_empty(), "a `T` is not an `Int`");
4604 }
4605}
4606
4607#[cfg(test)]
4612mod nesting_tests {
4613 use crate::check_str;
4614 use beck_diag::depth::{MAX_NESTING, STACK_BYTES};
4615
4616 fn nested_type(n: usize) -> String {
4618 let mut ty = String::from("Int");
4619 for _ in 0..n {
4620 ty = format!("list[{ty}]");
4621 }
4622 format!("def f(x: {ty}) -> Int:\n return 1\n")
4623 }
4624
4625 fn nested_expr(n: usize) -> String {
4626 format!(
4627 "def f() -> Int:\n return {}1{}\n",
4628 "(".repeat(n),
4629 ")".repeat(n)
4630 )
4631 }
4632
4633 fn codes(src: &str) -> Vec<String> {
4634 beck_diag::depth::on_the_front_end_stack(|| {
4635 let (_, d, _) = check_str("deep.beck", src);
4636 d.iter().map(|x| x.code.to_string()).collect()
4637 })
4638 }
4639
4640 #[test]
4641 fn a_type_past_the_ceiling_is_a_diagnostic_rather_than_an_abort() {
4642 let found = codes(&nested_type(MAX_NESTING as usize + 8));
4648 assert!(
4649 found.iter().any(|c| c == "B0121" || c == "B0390"),
4650 "expected a nesting refusal from the reader or the checker, got {found:?}"
4651 );
4652 }
4653
4654 #[test]
4655 fn an_expression_past_the_ceiling_is_refused_by_whichever_pass_reaches_it_first() {
4656 let found = codes(&nested_expr(MAX_NESTING as usize + 8));
4659 assert!(
4660 found.iter().any(|c| c == "B0121" || c == "B0390"),
4661 "expected a nesting refusal, got {found:?}"
4662 );
4663 }
4664
4665 #[test]
4666 fn nesting_a_person_would_write_still_checks() {
4667 assert!(codes(&nested_type(16)).is_empty());
4668 }
4669
4670 #[test]
4671 fn the_ceiling_fits_the_declared_stack() {
4672 const PROBE_DEPTH: usize = 100;
4673 let spent = std::thread::Builder::new()
4674 .stack_size(256 * 1024 * 1024)
4675 .spawn(|| {
4676 let src = nested_type(PROBE_DEPTH);
4677 beck_diag::depth::probe::stack_spent(|| check_str("probe.beck", &src))
4678 })
4679 .expect("a thread")
4680 .join()
4681 .expect("the probe checks");
4682
4683 let per_level = spent / PROBE_DEPTH;
4684 println!("checker: {spent} bytes for {PROBE_DEPTH} levels ({per_level} per level)");
4685 let needed = MAX_NESTING as usize * per_level * 2;
4686 assert!(
4687 needed < STACK_BYTES,
4688 "a ceiling of {MAX_NESTING} levels at {per_level} bytes each needs {needed} bytes \
4689 with the margin, against a declared STACK_BYTES of {STACK_BYTES} — raise the \
4690 declaration or lower the ceiling"
4691 );
4692 }
4693}
4694
4695fn short_circuit(p: Prim, args: &[Core], ty: Ty, span: Span) -> Option<Core> {
4718 let [lhs, rhs] = args else {
4719 return None;
4720 };
4721 let constant = |b: bool| Core::new(CoreKind::Const(Const::Bool(b)), ty.clone(), span);
4722 let (then, alt) = match p {
4723 Prim::And => (rhs.clone(), constant(false)),
4724 Prim::Or => (constant(true), rhs.clone()),
4725 _ => return None,
4726 };
4727 Some(Core::new(
4728 CoreKind::If {
4729 cond: Box::new(lhs.clone()),
4730 then: Box::new(then),
4731 alt: Box::new(alt),
4732 },
4733 ty,
4734 span,
4735 ))
4736}