edict-lang 1.2.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/README.md +39 -13
  2. package/dist/ast/nodes.d.ts +4 -3
  3. package/dist/ast/nodes.d.ts.map +1 -1
  4. package/dist/ast/nodes.js +1 -0
  5. package/dist/ast/nodes.js.map +1 -1
  6. package/dist/ast/type-constants.d.ts +12 -0
  7. package/dist/ast/type-constants.d.ts.map +1 -0
  8. package/dist/ast/type-constants.js +16 -0
  9. package/dist/ast/type-constants.js.map +1 -0
  10. package/dist/ast/types.d.ts +1 -1
  11. package/dist/ast/types.d.ts.map +1 -1
  12. package/dist/builtins/builtin-enums.d.ts +12 -0
  13. package/dist/builtins/builtin-enums.d.ts.map +1 -0
  14. package/dist/builtins/builtin-enums.js +45 -0
  15. package/dist/builtins/builtin-enums.js.map +1 -0
  16. package/dist/builtins/builtin-types.d.ts +20 -0
  17. package/dist/builtins/builtin-types.d.ts.map +1 -0
  18. package/dist/builtins/builtin-types.js +6 -0
  19. package/dist/builtins/builtin-types.js.map +1 -0
  20. package/dist/builtins/builtins.d.ts +3 -0
  21. package/dist/builtins/builtins.d.ts.map +1 -0
  22. package/dist/builtins/builtins.js +3 -0
  23. package/dist/builtins/builtins.js.map +1 -0
  24. package/dist/builtins/domains/array.d.ts +3 -0
  25. package/dist/builtins/domains/array.d.ts.map +1 -0
  26. package/dist/builtins/domains/array.js +236 -0
  27. package/dist/builtins/domains/array.js.map +1 -0
  28. package/dist/builtins/domains/core.d.ts +3 -0
  29. package/dist/builtins/domains/core.d.ts.map +1 -0
  30. package/dist/builtins/domains/core.js +45 -0
  31. package/dist/builtins/domains/core.js.map +1 -0
  32. package/dist/builtins/domains/crypto.d.ts +3 -0
  33. package/dist/builtins/domains/crypto.d.ts.map +1 -0
  34. package/dist/builtins/domains/crypto.js +49 -0
  35. package/dist/builtins/domains/crypto.js.map +1 -0
  36. package/dist/builtins/domains/datetime.d.ts +3 -0
  37. package/dist/builtins/domains/datetime.d.ts.map +1 -0
  38. package/dist/builtins/domains/datetime.js +45 -0
  39. package/dist/builtins/domains/datetime.js.map +1 -0
  40. package/dist/builtins/domains/http.d.ts +3 -0
  41. package/dist/builtins/domains/http.d.ts.map +1 -0
  42. package/dist/builtins/domains/http.js +55 -0
  43. package/dist/builtins/domains/http.js.map +1 -0
  44. package/dist/builtins/domains/int64.d.ts +3 -0
  45. package/dist/builtins/domains/int64.d.ts.map +1 -0
  46. package/dist/builtins/domains/int64.js +31 -0
  47. package/dist/builtins/domains/int64.js.map +1 -0
  48. package/dist/builtins/domains/io.d.ts +3 -0
  49. package/dist/builtins/domains/io.d.ts.map +1 -0
  50. package/dist/builtins/domains/io.js +79 -0
  51. package/dist/builtins/domains/io.js.map +1 -0
  52. package/dist/builtins/domains/json.d.ts +3 -0
  53. package/dist/builtins/domains/json.d.ts.map +1 -0
  54. package/dist/builtins/domains/json.js +47 -0
  55. package/dist/builtins/domains/json.js.map +1 -0
  56. package/dist/builtins/domains/math.d.ts +3 -0
  57. package/dist/builtins/domains/math.d.ts.map +1 -0
  58. package/dist/builtins/domains/math.js +47 -0
  59. package/dist/builtins/domains/math.js.map +1 -0
  60. package/dist/builtins/domains/option.d.ts +3 -0
  61. package/dist/builtins/domains/option.d.ts.map +1 -0
  62. package/dist/builtins/domains/option.js +56 -0
  63. package/dist/builtins/domains/option.js.map +1 -0
  64. package/dist/builtins/domains/random.d.ts +3 -0
  65. package/dist/builtins/domains/random.d.ts.map +1 -0
  66. package/dist/builtins/domains/random.js +50 -0
  67. package/dist/builtins/domains/random.js.map +1 -0
  68. package/dist/builtins/domains/regex.d.ts +3 -0
  69. package/dist/builtins/domains/regex.d.ts.map +1 -0
  70. package/dist/builtins/domains/regex.js +69 -0
  71. package/dist/builtins/domains/regex.js.map +1 -0
  72. package/dist/builtins/domains/result.d.ts +3 -0
  73. package/dist/builtins/domains/result.d.ts.map +1 -0
  74. package/dist/builtins/domains/result.js +84 -0
  75. package/dist/builtins/domains/result.js.map +1 -0
  76. package/dist/builtins/domains/string.d.ts +3 -0
  77. package/dist/builtins/domains/string.d.ts.map +1 -0
  78. package/dist/builtins/domains/string.js +139 -0
  79. package/dist/builtins/domains/string.js.map +1 -0
  80. package/dist/builtins/domains/type-conversion.d.ts +3 -0
  81. package/dist/builtins/domains/type-conversion.d.ts.map +1 -0
  82. package/dist/builtins/domains/type-conversion.js +42 -0
  83. package/dist/builtins/domains/type-conversion.js.map +1 -0
  84. package/dist/builtins/host-helpers.d.ts +66 -0
  85. package/dist/builtins/host-helpers.d.ts.map +1 -0
  86. package/dist/builtins/host-helpers.js +127 -0
  87. package/dist/builtins/host-helpers.js.map +1 -0
  88. package/dist/builtins/registry.d.ts +48 -0
  89. package/dist/builtins/registry.d.ts.map +1 -0
  90. package/dist/builtins/registry.js +118 -0
  91. package/dist/builtins/registry.js.map +1 -0
  92. package/dist/check.d.ts +9 -2
  93. package/dist/check.d.ts.map +1 -1
  94. package/dist/check.js +30 -9
  95. package/dist/check.js.map +1 -1
  96. package/dist/checker/check.d.ts +18 -1
  97. package/dist/checker/check.d.ts.map +1 -1
  98. package/dist/checker/check.js +157 -108
  99. package/dist/checker/check.js.map +1 -1
  100. package/dist/checker/type-env.d.ts +2 -0
  101. package/dist/checker/type-env.d.ts.map +1 -1
  102. package/dist/checker/type-env.js +9 -0
  103. package/dist/checker/type-env.js.map +1 -1
  104. package/dist/codegen/browser-host-adapter.d.ts +29 -0
  105. package/dist/codegen/browser-host-adapter.d.ts.map +1 -0
  106. package/dist/codegen/browser-host-adapter.js +51 -0
  107. package/dist/codegen/browser-host-adapter.js.map +1 -0
  108. package/dist/codegen/builtins.d.ts +2 -26
  109. package/dist/codegen/builtins.d.ts.map +1 -1
  110. package/dist/codegen/builtins.js +2 -341
  111. package/dist/codegen/builtins.js.map +1 -1
  112. package/dist/codegen/closures.d.ts +17 -0
  113. package/dist/codegen/closures.d.ts.map +1 -0
  114. package/dist/codegen/closures.js +140 -0
  115. package/dist/codegen/closures.js.map +1 -0
  116. package/dist/codegen/codegen.d.ts +6 -30
  117. package/dist/codegen/codegen.d.ts.map +1 -1
  118. package/dist/codegen/codegen.js +154 -1103
  119. package/dist/codegen/codegen.js.map +1 -1
  120. package/dist/codegen/collect-strings.d.ts +4 -0
  121. package/dist/codegen/collect-strings.d.ts.map +1 -0
  122. package/dist/codegen/collect-strings.js +76 -0
  123. package/dist/codegen/collect-strings.js.map +1 -0
  124. package/dist/codegen/compile-calls.d.ts +10 -0
  125. package/dist/codegen/compile-calls.d.ts.map +1 -0
  126. package/dist/codegen/compile-calls.js +374 -0
  127. package/dist/codegen/compile-calls.js.map +1 -0
  128. package/dist/codegen/compile-data.d.ts +22 -0
  129. package/dist/codegen/compile-data.d.ts.map +1 -0
  130. package/dist/codegen/compile-data.js +243 -0
  131. package/dist/codegen/compile-data.js.map +1 -0
  132. package/dist/codegen/compile-expr.d.ts +10 -0
  133. package/dist/codegen/compile-expr.d.ts.map +1 -0
  134. package/dist/codegen/compile-expr.js +156 -0
  135. package/dist/codegen/compile-expr.js.map +1 -0
  136. package/dist/codegen/compile-match.d.ts +7 -0
  137. package/dist/codegen/compile-match.d.ts.map +1 -0
  138. package/dist/codegen/compile-match.js +195 -0
  139. package/dist/codegen/compile-match.js.map +1 -0
  140. package/dist/codegen/compile-scalars.d.ts +25 -0
  141. package/dist/codegen/compile-scalars.d.ts.map +1 -0
  142. package/dist/codegen/compile-scalars.js +211 -0
  143. package/dist/codegen/compile-scalars.js.map +1 -0
  144. package/dist/codegen/hof-generators.d.ts +39 -0
  145. package/dist/codegen/hof-generators.d.ts.map +1 -0
  146. package/dist/codegen/hof-generators.js +336 -0
  147. package/dist/codegen/hof-generators.js.map +1 -0
  148. package/dist/codegen/host-adapter.d.ts +44 -0
  149. package/dist/codegen/host-adapter.d.ts.map +1 -0
  150. package/dist/codegen/host-adapter.js +9 -0
  151. package/dist/codegen/host-adapter.js.map +1 -0
  152. package/dist/codegen/imports.d.ts +15 -0
  153. package/dist/codegen/imports.d.ts.map +1 -0
  154. package/dist/codegen/imports.js +165 -0
  155. package/dist/codegen/imports.js.map +1 -0
  156. package/dist/codegen/node-host-adapter.d.ts +35 -0
  157. package/dist/codegen/node-host-adapter.d.ts.map +1 -0
  158. package/dist/codegen/node-host-adapter.js +155 -0
  159. package/dist/codegen/node-host-adapter.js.map +1 -0
  160. package/dist/codegen/runner.d.ts +36 -2
  161. package/dist/codegen/runner.d.ts.map +1 -1
  162. package/dist/codegen/runner.js +147 -271
  163. package/dist/codegen/runner.js.map +1 -1
  164. package/dist/codegen/types.d.ts +96 -0
  165. package/dist/codegen/types.d.ts.map +1 -0
  166. package/dist/codegen/types.js +63 -0
  167. package/dist/codegen/types.js.map +1 -0
  168. package/dist/compact/expand.d.ts +25 -0
  169. package/dist/compact/expand.d.ts.map +1 -0
  170. package/dist/compact/expand.js +199 -0
  171. package/dist/compact/expand.js.map +1 -0
  172. package/dist/compile.d.ts +2 -1
  173. package/dist/compile.d.ts.map +1 -1
  174. package/dist/compile.js +2 -2
  175. package/dist/compile.js.map +1 -1
  176. package/dist/contracts/translate.js.map +1 -1
  177. package/dist/contracts/verify.d.ts +10 -2
  178. package/dist/contracts/verify.d.ts.map +1 -1
  179. package/dist/contracts/verify.js +26 -21
  180. package/dist/contracts/verify.js.map +1 -1
  181. package/dist/effects/call-graph.d.ts.map +1 -1
  182. package/dist/effects/call-graph.js +27 -4
  183. package/dist/effects/call-graph.js.map +1 -1
  184. package/dist/effects/effect-check.d.ts +10 -2
  185. package/dist/effects/effect-check.d.ts.map +1 -1
  186. package/dist/effects/effect-check.js +12 -7
  187. package/dist/effects/effect-check.js.map +1 -1
  188. package/dist/errors/error-catalog.d.ts +1 -1
  189. package/dist/errors/error-catalog.d.ts.map +1 -1
  190. package/dist/errors/error-catalog.js +119 -0
  191. package/dist/errors/error-catalog.js.map +1 -1
  192. package/dist/errors/structured-errors.d.ts +28 -1
  193. package/dist/errors/structured-errors.d.ts.map +1 -1
  194. package/dist/errors/structured-errors.js +9 -0
  195. package/dist/errors/structured-errors.js.map +1 -1
  196. package/dist/index.d.ts +20 -9
  197. package/dist/index.d.ts.map +1 -1
  198. package/dist/index.js +26 -10
  199. package/dist/index.js.map +1 -1
  200. package/dist/lint/lint.d.ts +9 -0
  201. package/dist/lint/lint.d.ts.map +1 -0
  202. package/dist/lint/lint.js +354 -0
  203. package/dist/lint/lint.js.map +1 -0
  204. package/dist/lint/warnings.d.ts +54 -0
  205. package/dist/lint/warnings.d.ts.map +1 -0
  206. package/dist/lint/warnings.js +39 -0
  207. package/dist/lint/warnings.js.map +1 -0
  208. package/dist/mcp/create-server.d.ts.map +1 -1
  209. package/dist/mcp/create-server.js +66 -5
  210. package/dist/mcp/create-server.js.map +1 -1
  211. package/dist/mcp/handlers.d.ts +21 -5
  212. package/dist/mcp/handlers.d.ts.map +1 -1
  213. package/dist/mcp/handlers.js +65 -15
  214. package/dist/mcp/handlers.js.map +1 -1
  215. package/dist/mcp/prompts.d.ts +17 -0
  216. package/dist/mcp/prompts.d.ts.map +1 -0
  217. package/dist/mcp/prompts.js +181 -0
  218. package/dist/mcp/prompts.js.map +1 -0
  219. package/dist/mcp/server.js +1 -4
  220. package/dist/mcp/server.js.map +1 -1
  221. package/dist/resolver/resolve.d.ts.map +1 -1
  222. package/dist/resolver/resolve.js +62 -16
  223. package/dist/resolver/resolve.js.map +1 -1
  224. package/dist/validator/node-validators.d.ts.map +1 -1
  225. package/dist/validator/node-validators.js +60 -5
  226. package/dist/validator/node-validators.js.map +1 -1
  227. package/package.json +4 -2
@@ -7,22 +7,28 @@ import { typeMismatch, arityMismatch, notAFunction, unknownField, unknownRecord,
7
7
  import { findCandidates } from "../resolver/levenshtein.js";
8
8
  import { TypeEnv } from "./type-env.js";
9
9
  import { typesEqual, isUnknown, resolveType } from "./types-equal.js";
10
- import { BUILTIN_FUNCTIONS } from "../codegen/builtins.js";
11
- const UNKNOWN_TYPE = { kind: "named", name: "unknown" };
12
- const INT_TYPE = { kind: "basic", name: "Int" };
13
- const FLOAT_TYPE = { kind: "basic", name: "Float" };
14
- const STRING_TYPE = { kind: "basic", name: "String" };
15
- const BOOL_TYPE = { kind: "basic", name: "Bool" };
10
+ import { BUILTIN_FUNCTIONS } from "../builtins/builtins.js";
11
+ import { OPTION_ENUM_DEF, RESULT_ENUM_DEF } from "../builtins/builtin-enums.js";
12
+ import { UNKNOWN_TYPE, INT_TYPE, FLOAT_TYPE, STRING_TYPE, BOOL_TYPE } from "../ast/type-constants.js";
16
13
  /**
17
14
  * Entry point: type-check a validated + resolved Edict module.
18
15
  */
19
16
  export function typeCheck(module) {
20
17
  const errors = [];
18
+ const typeInfo = {
19
+ inferredReturnTypes: new Map(),
20
+ inferredLetTypes: new Map(),
21
+ inferredLambdaParamTypes: new Map(),
22
+ };
21
23
  const rootEnv = new TypeEnv();
22
24
  // Register built-in function signatures
23
25
  for (const [name, builtin] of BUILTIN_FUNCTIONS) {
24
26
  rootEnv.bind(name, builtin.type);
25
27
  }
28
+ // Register built-in enum type definitions (Option, Result)
29
+ // so enum_constructor and match patterns work through typeCheck.
30
+ rootEnv.registerTypeDef("Option", OPTION_ENUM_DEF);
31
+ rootEnv.registerTypeDef("Result", RESULT_ENUM_DEF);
26
32
  // Register type definitions (records, enums, type aliases)
27
33
  for (const def of module.definitions) {
28
34
  registerTypeDef(def, rootEnv);
@@ -31,25 +37,26 @@ export function typeCheck(module) {
31
37
  for (const def of module.definitions) {
32
38
  registerValueDef(def, rootEnv);
33
39
  }
34
- // Register imports as unknown
40
+ // Register imports use declared types when available, fall back to unknown
35
41
  for (const imp of module.imports) {
36
42
  for (const name of imp.names) {
37
- rootEnv.bind(name, UNKNOWN_TYPE);
43
+ const declaredType = imp.types?.[name];
44
+ rootEnv.bind(name, declaredType ?? UNKNOWN_TYPE);
38
45
  }
39
46
  }
40
47
  // Type-check each definition
41
48
  for (const def of module.definitions) {
42
49
  switch (def.kind) {
43
50
  case "fn":
44
- checkFunction(def, rootEnv, errors);
51
+ checkFunction(def, rootEnv, errors, typeInfo);
45
52
  break;
46
53
  case "const":
47
- checkConst(def, rootEnv, errors);
54
+ checkConst(def, rootEnv, errors, typeInfo);
48
55
  break;
49
56
  // record, enum, type — no body to type-check
50
57
  }
51
58
  }
52
- return errors;
59
+ return { errors, typeInfo };
53
60
  }
54
61
  // =============================================================================
55
62
  // Registration
@@ -68,9 +75,9 @@ function registerValueDef(def, env) {
68
75
  case "fn": {
69
76
  const fnType = {
70
77
  kind: "fn_type",
71
- params: def.params.map((p) => p.type),
78
+ params: def.params.map((p) => p.type ?? UNKNOWN_TYPE),
72
79
  effects: [...def.effects],
73
- returnType: def.returnType,
80
+ returnType: def.returnType ?? UNKNOWN_TYPE,
74
81
  };
75
82
  env.bind(def.name, fnType);
76
83
  break;
@@ -83,89 +90,96 @@ function registerValueDef(def, env) {
83
90
  // =============================================================================
84
91
  // Function checking
85
92
  // =============================================================================
86
- function checkFunction(fn, rootEnv, errors) {
93
+ function checkFunction(fn, rootEnv, errors, typeInfo) {
87
94
  const fnEnv = rootEnv.child();
88
95
  // Bind params
89
96
  for (const param of fn.params) {
90
97
  fnEnv.bind(param.name, param.type);
91
98
  }
92
- // Check contracts
99
+ // Infer body type first (needed when returnType is omitted)
100
+ const bodyType = inferExprList(fn.body, fnEnv, errors, typeInfo);
101
+ // Determine effective return type: explicit annotation or inferred from body
102
+ const hadExplicitReturnType = !!fn.returnType;
103
+ const effectiveReturnType = fn.returnType ?? bodyType;
104
+ // Store inferred return type in side-table (no AST mutation)
105
+ if (!fn.returnType) {
106
+ typeInfo.inferredReturnTypes.set(fn.id, effectiveReturnType);
107
+ }
108
+ // Check contracts (postconditions bind `result` to the effective return type)
93
109
  for (const contract of fn.contracts) {
94
110
  if (contract.kind === "post") {
95
111
  const postEnv = fnEnv.child();
96
- postEnv.bind("result", fn.returnType);
97
- const condType = inferExpr(contract.condition, postEnv, errors);
98
- checkExpectedType(condType, BOOL_TYPE, contract.id, fnEnv, errors, "post-contract condition must be Bool");
112
+ postEnv.bind("result", effectiveReturnType);
113
+ const condType = inferExpr(contract.condition, postEnv, errors, typeInfo);
114
+ checkExpectedType(condType, BOOL_TYPE, contract.id, fnEnv, errors);
99
115
  }
100
116
  else {
101
- const condType = inferExpr(contract.condition, fnEnv, errors);
102
- checkExpectedType(condType, BOOL_TYPE, contract.id, fnEnv, errors, "pre-contract condition must be Bool");
117
+ const condType = inferExpr(contract.condition, fnEnv, errors, typeInfo);
118
+ checkExpectedType(condType, BOOL_TYPE, contract.id, fnEnv, errors);
103
119
  }
104
120
  }
105
- // Infer body type
106
- const bodyType = inferExprList(fn.body, fnEnv, errors);
107
- // Check return type
108
- if (bodyType && !isUnknown(bodyType) && !isUnknown(resolveType(fn.returnType, fnEnv))) {
109
- checkExpectedType(bodyType, fn.returnType, fn.id, fnEnv, errors, "function body type must match return type");
121
+ // If returnType was explicit, check body against it
122
+ if (hadExplicitReturnType && bodyType && !isUnknown(bodyType) && !isUnknown(resolveType(fn.returnType, fnEnv))) {
123
+ checkExpectedType(bodyType, fn.returnType, fn.id, fnEnv, errors);
110
124
  }
111
125
  }
112
- function checkConst(def, env, errors) {
113
- const valType = inferExpr(def.value, env, errors);
114
- checkExpectedType(valType, def.type, def.id, env, errors, "const value must match declared type");
126
+ function checkConst(def, env, errors, typeInfo) {
127
+ const valType = inferExpr(def.value, env, errors, typeInfo);
128
+ checkExpectedType(valType, def.type, def.id, env, errors);
115
129
  }
116
130
  // =============================================================================
117
131
  // Type inference
118
132
  // =============================================================================
119
- function inferExprList(exprs, env, errors) {
133
+ function inferExprList(exprs, env, errors, typeInfo) {
120
134
  if (exprs.length === 0)
121
135
  return UNKNOWN_TYPE;
122
136
  let currentEnv = env;
123
137
  let lastType = UNKNOWN_TYPE;
124
138
  for (const expr of exprs) {
125
- lastType = inferExpr(expr, currentEnv, errors);
139
+ lastType = inferExpr(expr, currentEnv, errors, typeInfo);
126
140
  if (expr.kind === "let") {
127
141
  currentEnv = currentEnv.child();
128
- // Bind the let name in child env
129
- const bindType = expr.type ?? lastType;
142
+ // Bind the let name in child env, using inferred type from side-table if needed
143
+ const bindType = expr.type ?? typeInfo.inferredLetTypes.get(expr.id) ?? lastType;
130
144
  currentEnv.bind(expr.name, bindType);
131
145
  }
132
146
  }
133
147
  return lastType;
134
148
  }
135
- function inferExpr(expr, env, errors) {
149
+ function inferExpr(expr, env, errors, typeInfo) {
136
150
  switch (expr.kind) {
137
151
  case "literal":
138
152
  return inferLiteral(expr);
139
153
  case "ident":
140
154
  return env.getType(expr.name) ?? UNKNOWN_TYPE;
141
155
  case "binop":
142
- return inferBinop(expr, env, errors);
156
+ return inferBinop(expr, env, errors, typeInfo);
143
157
  case "unop":
144
- return inferUnop(expr, env, errors);
158
+ return inferUnop(expr, env, errors, typeInfo);
145
159
  case "call":
146
- return inferCall(expr, env, errors);
160
+ return inferCall(expr, env, errors, typeInfo);
147
161
  case "if":
148
- return inferIf(expr, env, errors);
162
+ return inferIf(expr, env, errors, typeInfo);
149
163
  case "let":
150
- return inferLet(expr, env, errors);
164
+ return inferLet(expr, env, errors, typeInfo);
151
165
  case "match":
152
- return inferMatch(expr, env, errors);
166
+ return inferMatch(expr, env, errors, typeInfo);
153
167
  case "array":
154
- return inferArray(expr, env, errors);
168
+ return inferArray(expr, env, errors, typeInfo);
155
169
  case "tuple_expr":
156
- return inferTuple(expr, env, errors);
170
+ return inferTuple(expr, env, errors, typeInfo);
157
171
  case "record_expr":
158
- return inferRecordExpr(expr, env, errors);
172
+ return inferRecordExpr(expr, env, errors, typeInfo);
159
173
  case "enum_constructor":
160
- return inferEnumConstructor(expr, env, errors);
174
+ return inferEnumConstructor(expr, env, errors, typeInfo);
161
175
  case "access":
162
- return inferAccess(expr, env, errors);
176
+ return inferAccess(expr, env, errors, typeInfo);
163
177
  case "lambda":
164
- return inferLambda(expr, env, errors);
178
+ return inferLambda(expr, env, errors, typeInfo);
165
179
  case "block":
166
- return inferExprList(expr.body, env.child(), errors);
180
+ return inferExprList(expr.body, env.child(), errors, typeInfo);
167
181
  case "string_interp":
168
- return inferStringInterp(expr, env, errors);
182
+ return inferStringInterp(expr, env, errors, typeInfo);
169
183
  }
170
184
  }
171
185
  // =============================================================================
@@ -184,9 +198,9 @@ function inferLiteral(expr) {
184
198
  }
185
199
  return UNKNOWN_TYPE;
186
200
  }
187
- function inferBinop(expr, env, errors) {
188
- const leftType = inferExpr(expr.left, env, errors);
189
- const rightType = inferExpr(expr.right, env, errors);
201
+ function inferBinop(expr, env, errors, typeInfo) {
202
+ const leftType = inferExpr(expr.left, env, errors, typeInfo);
203
+ const rightType = inferExpr(expr.right, env, errors, typeInfo);
190
204
  // unknown propagation
191
205
  if (isUnknown(leftType) || isUnknown(rightType))
192
206
  return UNKNOWN_TYPE;
@@ -234,8 +248,8 @@ function inferBinop(expr, env, errors) {
234
248
  errors.push(typeMismatch(expr.id, UNKNOWN_TYPE, leftType)); // Expected numeric
235
249
  return UNKNOWN_TYPE;
236
250
  }
237
- function inferUnop(expr, env, errors) {
238
- const operandType = inferExpr(expr.operand, env, errors);
251
+ function inferUnop(expr, env, errors, typeInfo) {
252
+ const operandType = inferExpr(expr.operand, env, errors, typeInfo);
239
253
  if (isUnknown(operandType))
240
254
  return UNKNOWN_TYPE;
241
255
  if (expr.op === "not") {
@@ -251,12 +265,12 @@ function inferUnop(expr, env, errors) {
251
265
  }
252
266
  return operandType;
253
267
  }
254
- function inferCall(expr, env, errors) {
255
- const fnType = inferExpr(expr.fn, env, errors);
268
+ function inferCall(expr, env, errors, typeInfo) {
269
+ const fnType = inferExpr(expr.fn, env, errors, typeInfo);
256
270
  if (isUnknown(fnType)) {
257
271
  // If fn is unknown, we can't type-check args. Infer them for side effects (let bindings)
258
272
  for (const arg of expr.args)
259
- inferExpr(arg, env, errors);
273
+ inferExpr(arg, env, errors, typeInfo);
260
274
  return UNKNOWN_TYPE;
261
275
  }
262
276
  const resolved = resolveType(fnType, env);
@@ -264,7 +278,7 @@ function inferCall(expr, env, errors) {
264
278
  errors.push(notAFunction(expr.id, fnType));
265
279
  // Still infer arg types for error propagation
266
280
  for (const arg of expr.args)
267
- inferExpr(arg, env, errors);
281
+ inferExpr(arg, env, errors, typeInfo);
268
282
  return UNKNOWN_TYPE;
269
283
  }
270
284
  // Check arity
@@ -274,21 +288,27 @@ function inferCall(expr, env, errors) {
274
288
  // Check arg types (up to the minimum of args/params)
275
289
  const checkCount = Math.min(expr.args.length, resolved.params.length);
276
290
  for (let i = 0; i < checkCount; i++) {
277
- const argType = inferExpr(expr.args[i], env, errors);
278
- checkExpectedType(argType, resolved.params[i], expr.args[i].id, env, errors, `argument ${i + 1}`);
291
+ const arg = expr.args[i];
292
+ const expectedParamType = resolved.params[i];
293
+ // If arg is a lambda and expected param is fn_type, propagate param types
294
+ const resolvedExpected = resolveType(expectedParamType, env);
295
+ const argType = (arg.kind === "lambda" && resolvedExpected.kind === "fn_type")
296
+ ? inferLambdaWithContext(arg, resolvedExpected, env, errors, typeInfo)
297
+ : inferExpr(arg, env, errors, typeInfo);
298
+ checkExpectedType(argType, expectedParamType, arg.id, env, errors);
279
299
  }
280
300
  // Infer remaining surplus args
281
301
  for (let i = checkCount; i < expr.args.length; i++) {
282
- inferExpr(expr.args[i], env, errors);
302
+ inferExpr(expr.args[i], env, errors, typeInfo);
283
303
  }
284
304
  return resolved.returnType;
285
305
  }
286
- function inferIf(expr, env, errors) {
287
- const condType = inferExpr(expr.condition, env, errors);
288
- checkExpectedType(condType, BOOL_TYPE, expr.id, env, errors, "if condition must be Bool");
289
- const thenType = inferExprList(expr.then, env.child(), errors);
306
+ function inferIf(expr, env, errors, typeInfo) {
307
+ const condType = inferExpr(expr.condition, env, errors, typeInfo);
308
+ checkExpectedType(condType, BOOL_TYPE, expr.id, env, errors);
309
+ const thenType = inferExprList(expr.then, env.child(), errors, typeInfo);
290
310
  if (expr.else) {
291
- const elseType = inferExprList(expr.else, env.child(), errors);
311
+ const elseType = inferExprList(expr.else, env.child(), errors, typeInfo);
292
312
  if (!isUnknown(thenType) && !isUnknown(elseType)) {
293
313
  if (!typesEqual(thenType, elseType, env)) {
294
314
  errors.push(typeMismatch(expr.id, thenType, elseType));
@@ -299,21 +319,25 @@ function inferIf(expr, env, errors) {
299
319
  // No else → Option<thenType>
300
320
  return { kind: "option", inner: thenType };
301
321
  }
302
- function inferLet(expr, env, errors) {
303
- const valType = inferExpr(expr.value, env, errors);
322
+ function inferLet(expr, env, errors, typeInfo) {
323
+ const valType = inferExpr(expr.value, env, errors, typeInfo);
304
324
  if (expr.type) {
305
- checkExpectedType(valType, expr.type, expr.id, env, errors, "let value must match type annotation");
325
+ checkExpectedType(valType, expr.type, expr.id, env, errors);
306
326
  return expr.type;
307
327
  }
328
+ // Store inferred type in side-table (no AST mutation)
329
+ if (!isUnknown(valType)) {
330
+ typeInfo.inferredLetTypes.set(expr.id, valType);
331
+ }
308
332
  return valType;
309
333
  }
310
- function inferMatch(expr, env, errors) {
311
- const targetType = inferExpr(expr.target, env, errors);
334
+ function inferMatch(expr, env, errors, typeInfo) {
335
+ const targetType = inferExpr(expr.target, env, errors, typeInfo);
312
336
  let resultType = null;
313
337
  for (const arm of expr.arms) {
314
338
  const armEnv = env.child();
315
339
  inferPattern(arm.pattern, targetType, armEnv, env, errors);
316
- const bodyType = inferExprList(arm.body, armEnv, errors);
340
+ const bodyType = inferExprList(arm.body, armEnv, errors, typeInfo);
317
341
  if (resultType === null) {
318
342
  resultType = bodyType;
319
343
  }
@@ -382,13 +406,13 @@ function inferPattern(pattern, targetType, armEnv, rootEnv, errors) {
382
406
  }
383
407
  }
384
408
  }
385
- function inferArray(expr, env, errors) {
409
+ function inferArray(expr, env, errors, typeInfo) {
386
410
  if (expr.elements.length === 0) {
387
411
  return { kind: "array", element: UNKNOWN_TYPE };
388
412
  }
389
- const firstType = inferExpr(expr.elements[0], env, errors);
413
+ const firstType = inferExpr(expr.elements[0], env, errors, typeInfo);
390
414
  for (let i = 1; i < expr.elements.length; i++) {
391
- const elType = inferExpr(expr.elements[i], env, errors);
415
+ const elType = inferExpr(expr.elements[i], env, errors, typeInfo);
392
416
  if (!isUnknown(firstType) && !isUnknown(elType)) {
393
417
  if (!typesEqual(firstType, elType, env)) {
394
418
  errors.push(typeMismatch(expr.elements[i].id, firstType, elType));
@@ -397,31 +421,31 @@ function inferArray(expr, env, errors) {
397
421
  }
398
422
  return { kind: "array", element: firstType };
399
423
  }
400
- function inferTuple(expr, env, errors) {
401
- const elementTypes = expr.elements.map((el) => inferExpr(el, env, errors));
424
+ function inferTuple(expr, env, errors, typeInfo) {
425
+ const elementTypes = expr.elements.map((el) => inferExpr(el, env, errors, typeInfo));
402
426
  return { kind: "tuple", elements: elementTypes };
403
427
  }
404
- function inferRecordExpr(expr, env, errors) {
428
+ function inferRecordExpr(expr, env, errors, typeInfo) {
405
429
  const def = env.lookupTypeDef(expr.name);
406
430
  if (!def) {
407
- const cands = collectTypeDefNames(env, "record");
431
+ const cands = env.allTypeDefNames("record");
408
432
  const suggestion = cands.length > 0
409
433
  ? { nodeId: expr.id, field: "name", value: findCandidates(expr.name, cands)[0] ?? cands[0] }
410
434
  : undefined;
411
435
  errors.push(unknownRecord(expr.id, expr.name, cands, suggestion));
412
436
  // Still infer field value types
413
437
  for (const f of expr.fields)
414
- inferExpr(f.value, env, errors);
438
+ inferExpr(f.value, env, errors, typeInfo);
415
439
  return UNKNOWN_TYPE;
416
440
  }
417
441
  if (def.kind !== "record") {
418
- const cands = collectTypeDefNames(env, "record");
442
+ const cands = env.allTypeDefNames("record");
419
443
  const suggestion = cands.length > 0
420
444
  ? { nodeId: expr.id, field: "name", value: findCandidates(expr.name, cands)[0] ?? cands[0] }
421
445
  : undefined;
422
446
  errors.push(unknownRecord(expr.id, expr.name, cands, suggestion));
423
447
  for (const f of expr.fields)
424
- inferExpr(f.value, env, errors);
448
+ inferExpr(f.value, env, errors, typeInfo);
425
449
  return UNKNOWN_TYPE;
426
450
  }
427
451
  const recordDef = def;
@@ -444,34 +468,34 @@ function inferRecordExpr(expr, env, errors) {
444
468
  ? { nodeId: expr.id, field: "field", value: cands[0] }
445
469
  : undefined;
446
470
  errors.push(unknownField(expr.id, expr.name, fieldInit.name, availFields, suggestion));
447
- inferExpr(fieldInit.value, env, errors);
471
+ inferExpr(fieldInit.value, env, errors, typeInfo);
448
472
  continue;
449
473
  }
450
- const valType = inferExpr(fieldInit.value, env, errors);
451
- checkExpectedType(valType, fieldDef.type, expr.id, env, errors, `field '${fieldInit.name}'`);
474
+ const valType = inferExpr(fieldInit.value, env, errors, typeInfo);
475
+ checkExpectedType(valType, fieldDef.type, expr.id, env, errors);
452
476
  }
453
477
  return { kind: "named", name: expr.name };
454
478
  }
455
- function inferEnumConstructor(expr, env, errors) {
479
+ function inferEnumConstructor(expr, env, errors, typeInfo) {
456
480
  const def = env.lookupTypeDef(expr.enumName);
457
481
  if (!def) {
458
- const cands = collectTypeDefNames(env, "enum");
482
+ const cands = env.allTypeDefNames("enum");
459
483
  const suggestion = cands.length > 0
460
484
  ? { nodeId: expr.id, field: "enumName", value: findCandidates(expr.enumName, cands)[0] ?? cands[0] }
461
485
  : undefined;
462
486
  errors.push(unknownEnum(expr.id, expr.enumName, cands, suggestion));
463
487
  for (const f of expr.fields)
464
- inferExpr(f.value, env, errors);
488
+ inferExpr(f.value, env, errors, typeInfo);
465
489
  return UNKNOWN_TYPE;
466
490
  }
467
491
  if (def.kind !== "enum") {
468
- const cands = collectTypeDefNames(env, "enum");
492
+ const cands = env.allTypeDefNames("enum");
469
493
  const suggestion = cands.length > 0
470
494
  ? { nodeId: expr.id, field: "enumName", value: findCandidates(expr.enumName, cands)[0] ?? cands[0] }
471
495
  : undefined;
472
496
  errors.push(unknownEnum(expr.id, expr.enumName, cands, suggestion));
473
497
  for (const f of expr.fields)
474
- inferExpr(f.value, env, errors);
498
+ inferExpr(f.value, env, errors, typeInfo);
475
499
  return UNKNOWN_TYPE;
476
500
  }
477
501
  const enumDef = def;
@@ -484,7 +508,7 @@ function inferEnumConstructor(expr, env, errors) {
484
508
  : (availVariants.length > 0 ? { nodeId: expr.id, field: "variant", value: availVariants[0] } : undefined);
485
509
  errors.push(unknownVariant(expr.id, expr.enumName, expr.variant, availVariants, suggestion));
486
510
  for (const f of expr.fields)
487
- inferExpr(f.value, env, errors);
511
+ inferExpr(f.value, env, errors, typeInfo);
488
512
  return UNKNOWN_TYPE;
489
513
  }
490
514
  // Check field count and types
@@ -497,16 +521,16 @@ function inferEnumConstructor(expr, env, errors) {
497
521
  ? { nodeId: expr.id, field: "field", value: cands[0] }
498
522
  : undefined;
499
523
  errors.push(unknownField(expr.id, `${expr.enumName}.${expr.variant}`, fieldInit.name, availFields, suggestion));
500
- inferExpr(fieldInit.value, env, errors);
524
+ inferExpr(fieldInit.value, env, errors, typeInfo);
501
525
  continue;
502
526
  }
503
- const valType = inferExpr(fieldInit.value, env, errors);
504
- checkExpectedType(valType, fieldDef.type, expr.id, env, errors, `field '${fieldInit.name}'`);
527
+ const valType = inferExpr(fieldInit.value, env, errors, typeInfo);
528
+ checkExpectedType(valType, fieldDef.type, expr.id, env, errors);
505
529
  }
506
530
  return { kind: "named", name: expr.enumName };
507
531
  }
508
- function inferAccess(expr, env, errors) {
509
- const targetType = inferExpr(expr.target, env, errors);
532
+ function inferAccess(expr, env, errors, typeInfo) {
533
+ const targetType = inferExpr(expr.target, env, errors, typeInfo);
510
534
  if (isUnknown(targetType))
511
535
  return UNKNOWN_TYPE;
512
536
  const resolved = resolveType(targetType, env);
@@ -531,22 +555,53 @@ function inferAccess(expr, env, errors) {
531
555
  }
532
556
  return field.type;
533
557
  }
534
- function inferLambda(expr, env, errors) {
558
+ function inferLambda(expr, env, errors, typeInfo) {
535
559
  const lamEnv = env.child();
536
560
  for (const param of expr.params) {
537
- lamEnv.bind(param.name, param.type);
561
+ lamEnv.bind(param.name, param.type ?? UNKNOWN_TYPE);
562
+ }
563
+ const bodyType = inferExprList(expr.body, lamEnv, errors, typeInfo);
564
+ return {
565
+ kind: "fn_type",
566
+ params: expr.params.map((p) => p.type ?? UNKNOWN_TYPE),
567
+ effects: [],
568
+ returnType: bodyType,
569
+ };
570
+ }
571
+ /**
572
+ * Infer lambda type with expected fn_type context from a call site.
573
+ * Propagates param types from the expected signature to untyped lambda params.
574
+ */
575
+ function inferLambdaWithContext(expr, expectedType, env, errors, typeInfo) {
576
+ const lamEnv = env.child();
577
+ for (let i = 0; i < expr.params.length; i++) {
578
+ const param = expr.params[i];
579
+ if (param.type) {
580
+ // Explicit type — use it
581
+ lamEnv.bind(param.name, param.type);
582
+ }
583
+ else if (i < expectedType.params.length) {
584
+ // Infer from expected fn_type
585
+ const inferred = expectedType.params[i];
586
+ // Store inferred type in side-table (no AST mutation)
587
+ typeInfo.inferredLambdaParamTypes.set(param.id, inferred);
588
+ lamEnv.bind(param.name, inferred);
589
+ }
590
+ else {
591
+ lamEnv.bind(param.name, UNKNOWN_TYPE);
592
+ }
538
593
  }
539
- const bodyType = inferExprList(expr.body, lamEnv, errors);
594
+ const bodyType = inferExprList(expr.body, lamEnv, errors, typeInfo);
540
595
  return {
541
596
  kind: "fn_type",
542
- params: expr.params.map((p) => p.type),
597
+ params: expr.params.map((p) => p.type ?? typeInfo.inferredLambdaParamTypes.get(p.id) ?? UNKNOWN_TYPE),
543
598
  effects: [],
544
599
  returnType: bodyType,
545
600
  };
546
601
  }
547
- function inferStringInterp(expr, env, errors) {
602
+ function inferStringInterp(expr, env, errors, typeInfo) {
548
603
  for (const part of expr.parts) {
549
- const partType = inferExpr(part, env, errors);
604
+ const partType = inferExpr(part, env, errors, typeInfo);
550
605
  checkExpectedType(partType, STRING_TYPE, part.id, env, errors);
551
606
  }
552
607
  return STRING_TYPE;
@@ -554,7 +609,7 @@ function inferStringInterp(expr, env, errors) {
554
609
  // =============================================================================
555
610
  // Utility functions
556
611
  // =============================================================================
557
- function checkExpectedType(actual, expected, nodeId, env, errors, _hint) {
612
+ function checkExpectedType(actual, expected, nodeId, env, errors) {
558
613
  if (isUnknown(actual) || isUnknown(expected))
559
614
  return;
560
615
  if (!typesEqual(actual, expected, env)) {
@@ -573,7 +628,7 @@ function isString(type) {
573
628
  function isNumeric(type, env) {
574
629
  const resolved = resolveType(type, env);
575
630
  if (resolved.kind === "basic") {
576
- return resolved.name === "Int" || resolved.name === "Float";
631
+ return resolved.name === "Int" || resolved.name === "Int64" || resolved.name === "Float";
577
632
  }
578
633
  if (resolved.kind === "unit_type")
579
634
  return true;
@@ -589,10 +644,4 @@ function inferLiteralPatternType(value) {
589
644
  }
590
645
  return UNKNOWN_TYPE;
591
646
  }
592
- /**
593
- * Collect known type definition names of a specific kind (for error candidates).
594
- */
595
- function collectTypeDefNames(env, kind) {
596
- return env.allTypeDefNames(kind);
597
- }
598
647
  //# sourceMappingURL=check.js.map