@typecad/cuttlefish 1.0.0-alpha.14 → 1.0.0-alpha.16

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 (315) hide show
  1. package/README.md +82 -79
  2. package/assets/editor-extensions/typecad-debug/README.md +25 -26
  3. package/dist/api/config.d.ts +22 -26
  4. package/dist/api/schema/types.d.ts +20 -3
  5. package/dist/api/shared/async-runtime-static.d.ts +1 -1
  6. package/dist/api/shared/async-runtime-static.js +5 -5
  7. package/dist/api/shared/async-types.d.ts +3 -2
  8. package/dist/api/shared/board-catalog.d.ts +2 -0
  9. package/dist/api/shared/board-catalog.js +6 -0
  10. package/dist/api/shared/board-resolver.d.ts +4 -7
  11. package/dist/api/shared/board-resolver.js +2 -1
  12. package/dist/api/shared/coop-scheduler.d.ts +1 -1
  13. package/dist/api/shared/coop-scheduler.js +1 -1
  14. package/dist/api/shared/cpp-type-ir.d.ts +1 -1
  15. package/dist/api/shared/cpp-type-ir.js +1 -1
  16. package/dist/api/shared/display-profile.d.ts +1 -1
  17. package/dist/api/shared/display-profile.js +3 -5
  18. package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
  19. package/dist/api/shared/framework-manifest-registry.js +0 -1
  20. package/dist/api/shared/framework-manifest.d.ts +7 -6
  21. package/dist/api/shared/framework-manifest.js +6 -11
  22. package/dist/api/shared/hal-op-ir.d.ts +294 -685
  23. package/dist/api/shared/hal-op-ir.js +31 -65
  24. package/dist/api/shared/index.d.ts +0 -4
  25. package/dist/api/shared/index.js +1 -7
  26. package/dist/api/shared/ir-core.d.ts +6 -0
  27. package/dist/api/shared/ir-declarations.d.ts +4 -0
  28. package/dist/api/shared/ir.d.ts +1 -3
  29. package/dist/api/shared/platform-strategy.d.ts +14 -37
  30. package/dist/api/shared/polyfill-helper-registry.js +0 -5
  31. package/dist/api/shared/polyfill-types.js +0 -27
  32. package/dist/api/shared/promise-runtime.js +3 -3
  33. package/dist/api/shared/spdx-licenses.d.ts +12 -10
  34. package/dist/api/shared/spdx-licenses.js +1 -1
  35. package/dist/api/shared/toolchain-types.d.ts +1 -1
  36. package/dist/api/shared/toolchain-types.js +3 -3
  37. package/dist/api/shared/types.d.ts +0 -3
  38. package/dist/api/shared/validate-framework-manifest.js +13 -33
  39. package/dist/board-catalog/dts-reader.d.ts +220 -0
  40. package/dist/board-catalog/dts-reader.js +1052 -0
  41. package/dist/board-catalog/index.d.ts +11 -0
  42. package/dist/board-catalog/index.js +12 -0
  43. package/dist/board-catalog/pinconfig.d.ts +48 -0
  44. package/dist/board-catalog/pinconfig.js +263 -0
  45. package/dist/board-catalog/sdk.d.ts +82 -0
  46. package/dist/board-catalog/sdk.js +180 -0
  47. package/dist/board-catalog/store.d.ts +196 -0
  48. package/dist/board-catalog/store.js +363 -0
  49. package/dist/board-catalog/types.d.ts +233 -0
  50. package/dist/board-catalog/types.js +7 -0
  51. package/dist/board-catalog/walker.d.ts +112 -0
  52. package/dist/board-catalog/walker.js +1101 -0
  53. package/dist/cli-utils.d.ts +2 -2
  54. package/dist/cli-utils.js +5 -5
  55. package/dist/cli.js +219 -242
  56. package/dist/config-loader.d.ts +14 -19
  57. package/dist/config-loader.js +240 -78
  58. package/dist/config-schema.d.ts +3 -5
  59. package/dist/config-schema.js +8 -8
  60. package/dist/contract/board-generator.d.ts +4 -4
  61. package/dist/contract/board-generator.js +16 -5
  62. package/dist/contract/contract-parser.d.ts +27 -0
  63. package/dist/contract/contract-parser.js +76 -0
  64. package/dist/contract/index.d.ts +1 -1
  65. package/dist/contract/index.js +70 -21
  66. package/dist/create/active-board-catalog.d.ts +9 -0
  67. package/dist/create/active-board-catalog.js +94 -0
  68. package/dist/create/board-catalog.generated.d.ts +96 -0
  69. package/dist/create/board-catalog.generated.js +128737 -0
  70. package/dist/create/board-search.d.ts +23 -0
  71. package/dist/create/board-search.js +88 -0
  72. package/dist/create/editor-integration.d.ts +4 -2
  73. package/dist/create/editor-integration.js +41 -2
  74. package/dist/create/framework-catalog.d.ts +57 -9
  75. package/dist/create/framework-catalog.js +76 -57
  76. package/dist/create/index.d.ts +5 -11
  77. package/dist/create/index.js +3 -8
  78. package/dist/create/mcu-target.d.ts +39 -0
  79. package/dist/create/mcu-target.js +80 -0
  80. package/dist/create/pack-targets.d.ts +19 -0
  81. package/dist/create/pack-targets.js +56 -0
  82. package/dist/create/scaffold.d.ts +4 -7
  83. package/dist/create/scaffold.js +19 -109
  84. package/dist/create/templates.d.ts +32 -5
  85. package/dist/create/templates.js +130 -116
  86. package/dist/create/wizard.d.ts +12 -1
  87. package/dist/create/wizard.js +168 -38
  88. package/dist/debug/preprocessor.d.ts +5 -0
  89. package/dist/debug/preprocessor.js +31 -7
  90. package/dist/diagnostics/diagnostics-report.d.ts +1 -1
  91. package/dist/diagnostics/diagnostics-report.js +3 -11
  92. package/dist/diagnostics/json-schema.d.ts +1 -1
  93. package/dist/diagnostics/md-writer.js +2 -2
  94. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  95. package/dist/diagnostics/mermaid-builder.js +1 -1
  96. package/dist/emit/cpp-emitter.js +4 -1
  97. package/dist/emit/emitters/class-emitter.js +32 -4
  98. package/dist/emit/emitters/emitter-context.d.ts +11 -0
  99. package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
  100. package/dist/emit/emitters/function-emitter-impl.js +33 -17
  101. package/dist/emit/emitters/output-finalizer.d.ts +7 -0
  102. package/dist/emit/emitters/output-finalizer.js +31 -23
  103. package/dist/emit/emitters/setup.js +125 -63
  104. package/dist/emit/emitters/top-level-prep.js +1 -1
  105. package/dist/emit/emitters/type-decl-emitter.js +13 -8
  106. package/dist/emit/emitters/ui-emitter.js +1 -1
  107. package/dist/emit/expression-renderer.d.ts +3 -3
  108. package/dist/emit/expression-renderer.js +17 -7
  109. package/dist/emit/route-hal-op.js +17 -48
  110. package/dist/emit/snprintf-helpers.d.ts +1 -3
  111. package/dist/emit/snprintf-helpers.js +1 -39
  112. package/dist/emit/statement-renderer.d.ts +0 -4
  113. package/dist/emit/statement-renderer.js +54 -45
  114. package/dist/emit/utils/async-state-machine.d.ts +16 -9
  115. package/dist/emit/utils/async-state-machine.js +56 -54
  116. package/dist/emit/utils/comment-helpers.d.ts +0 -8
  117. package/dist/emit/utils/comment-helpers.js +1 -1
  118. package/dist/emit/utils/cpp-helpers.d.ts +2 -2
  119. package/dist/emit/utils/cpp-helpers.js +2 -2
  120. package/dist/emit/utils/hal-op-cpp-type.js +4 -6
  121. package/dist/emit/utils/index.d.ts +3 -3
  122. package/dist/emit/utils/index.js +3 -3
  123. package/dist/emit/utils/type-inference.d.ts +1 -29
  124. package/dist/emit/utils/type-inference.js +0 -75
  125. package/dist/framework-package.d.ts +3 -0
  126. package/dist/framework-package.js +23 -6
  127. package/dist/framework-registry.d.ts +5 -5
  128. package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
  129. package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
  130. package/dist/frameworks/native/index.d.ts +3 -0
  131. package/dist/frameworks/native/index.js +15 -0
  132. package/dist/frameworks/native/native-compile.d.ts +17 -0
  133. package/dist/frameworks/native/native-compile.js +170 -0
  134. package/dist/frameworks/native/native-config.d.ts +41 -0
  135. package/dist/frameworks/native/native-config.js +7 -0
  136. package/dist/frameworks/native/strategy.d.ts +97 -0
  137. package/dist/frameworks/native/strategy.js +664 -0
  138. package/dist/ir/board-resolver.d.ts +18 -23
  139. package/dist/ir/board-resolver.js +43 -401
  140. package/dist/ir/build-ir-state.d.ts +3 -0
  141. package/dist/ir/build-ir-state.js +42 -0
  142. package/dist/ir/build-ir.d.ts +1 -1
  143. package/dist/ir/build-ir.js +40 -42
  144. package/dist/ir/declaration-builders.js +1 -0
  145. package/dist/ir/expression-to-ir.js +17 -10
  146. package/dist/ir/hal/hal-emitter.d.ts +0 -2
  147. package/dist/ir/hal/hal-emitter.js +92 -41
  148. package/dist/ir/hal/hal-parser.d.ts +5 -5
  149. package/dist/ir/hal/hal-parser.js +128 -36
  150. package/dist/ir/hal/hal-plugins.d.ts +0 -1
  151. package/dist/ir/hal/hal-plugins.js +425 -716
  152. package/dist/ir/heap-analysis.js +2 -41
  153. package/dist/ir/identifier-collector.js +32 -20
  154. package/dist/ir/network-validation.js +7 -11
  155. package/dist/ir/ownership-analysis.js +75 -13
  156. package/dist/ir/peripheral-usage.d.ts +8 -2
  157. package/dist/ir/peripheral-usage.js +78 -149
  158. package/dist/ir/peripheral-validation.js +23 -3
  159. package/dist/ir/pin-capability-validation.d.ts +12 -0
  160. package/dist/ir/pin-capability-validation.js +61 -97
  161. package/dist/ir/pin-mode-validation.js +5 -5
  162. package/dist/ir/program-analysis.d.ts +8 -15
  163. package/dist/ir/program-analysis.js +140 -323
  164. package/dist/ir/pulldown-validation.js +11 -4
  165. package/dist/ir/resource-analysis.js +15 -3
  166. package/dist/ir/statement-to-ir.js +0 -6
  167. package/dist/ir/timing-validation.js +1 -1
  168. package/dist/ir/transformers/array-methods.js +11 -5
  169. package/dist/ir/transformers/call-statement.js +20 -87
  170. package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
  171. package/dist/ir/transformers/callback-context-registry.js +8 -16
  172. package/dist/ir/transformers/control-flow.js +0 -13
  173. package/dist/ir/transformers/expressions.js +10 -9
  174. package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
  175. package/dist/ir/transformers/hal-call-resolver.js +24 -10
  176. package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
  177. package/dist/ir/transformers/hal-emit-helpers.js +2 -2
  178. package/dist/ir/transformers/namespace-methods.js +1 -1
  179. package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
  180. package/dist/ir/transformers/ui-callback-lowering.js +0 -24
  181. package/dist/ir/transformers/variables.js +338 -42
  182. package/dist/ir/try-catch-validation.js +4 -3
  183. package/dist/ir/type-resolution.js +15 -4
  184. package/dist/ir/validation-orchestrator.js +0 -9
  185. package/dist/libdef/cpp-to-decl.d.ts +0 -9
  186. package/dist/libdef/cpp-to-decl.js +0 -72
  187. package/dist/libdef/registry.d.ts +0 -1
  188. package/dist/libdef/registry.js +1 -24
  189. package/dist/library/init.d.ts +1 -1
  190. package/dist/library/init.js +137 -137
  191. package/dist/library-packages.d.ts +1 -1
  192. package/dist/library-packages.js +1 -1
  193. package/dist/mapping/peripheral-names.js +9 -4
  194. package/dist/mapping/source-map.d.ts +3 -4
  195. package/dist/mapping/source-map.js +12 -18
  196. package/dist/orchestrator/graph-builder.d.ts +3 -3
  197. package/dist/orchestrator/graph-builder.js +13 -12
  198. package/dist/orchestrator/type-checker.d.ts +3 -3
  199. package/dist/orchestrator/type-checker.js +3 -3
  200. package/dist/platform/async-runtime.js +1 -1
  201. package/dist/platform/coop-scheduler-runtime.js +2 -2
  202. package/dist/platform/generic-debug-codegen.d.ts +36 -0
  203. package/dist/platform/generic-debug-codegen.js +185 -0
  204. package/dist/platform/generic-strategy.d.ts +3 -4
  205. package/dist/platform/generic-strategy.js +5 -80
  206. package/dist/platform/index.js +1 -1
  207. package/dist/platform/registry.d.ts +2 -2
  208. package/dist/platform/registry.js +2 -2
  209. package/dist/safety/asil-decorators.d.ts +32 -0
  210. package/dist/safety/asil-decorators.js +32 -0
  211. package/dist/safety/authoring.d.ts +85 -0
  212. package/dist/safety/authoring.js +61 -0
  213. package/dist/safety/engine.d.ts +3 -0
  214. package/dist/safety/engine.js +83 -0
  215. package/dist/safety/hal/ops.d.ts +48 -0
  216. package/dist/safety/hal/ops.js +46 -0
  217. package/dist/safety/iso26262/analyze.d.ts +13 -0
  218. package/dist/safety/iso26262/analyze.js +29 -0
  219. package/dist/safety/iso26262/asil.d.ts +10 -0
  220. package/dist/safety/iso26262/asil.js +22 -0
  221. package/dist/safety/iso26262/collect.d.ts +14 -0
  222. package/dist/safety/iso26262/collect.js +137 -0
  223. package/dist/safety/iso26262/goto-checker.d.ts +10 -0
  224. package/dist/safety/iso26262/goto-checker.js +72 -0
  225. package/dist/safety/iso26262/heap-checker.d.ts +7 -0
  226. package/dist/safety/iso26262/heap-checker.js +57 -0
  227. package/dist/safety/iso26262/init-checker.d.ts +13 -0
  228. package/dist/safety/iso26262/init-checker.js +127 -0
  229. package/dist/safety/iso26262/loop-checker.d.ts +7 -0
  230. package/dist/safety/iso26262/loop-checker.js +86 -0
  231. package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
  232. package/dist/safety/iso26262/recursion-checker.js +134 -0
  233. package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
  234. package/dist/safety/iso26262/sidecar-writer.js +13 -0
  235. package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
  236. package/dist/safety/passes/pinMode-intercept.js +60 -0
  237. package/dist/safety/runtime/mode-table.d.ts +5 -0
  238. package/dist/safety/runtime/mode-table.js +55 -0
  239. package/dist/safety/runtime/polyfills.d.ts +6 -0
  240. package/dist/safety/runtime/polyfills.js +12 -0
  241. package/dist/safety/runtime/result-struct.d.ts +11 -0
  242. package/dist/safety/runtime/result-struct.js +98 -0
  243. package/dist/safety/runtime/safe-int.d.ts +33 -0
  244. package/dist/safety/runtime/safe-int.js +170 -0
  245. package/dist/safety/runtime/safe-traits.d.ts +4 -0
  246. package/dist/safety/runtime/safe-traits.js +95 -0
  247. package/dist/safety/runtime/safe-variable.d.ts +39 -0
  248. package/dist/safety/runtime/safe-variable.js +226 -0
  249. package/dist/safety/runtime/vote.d.ts +8 -0
  250. package/dist/safety/runtime/vote.js +92 -0
  251. package/dist/safety/runtime/write-verify.d.ts +8 -0
  252. package/dist/safety/runtime/write-verify.js +71 -0
  253. package/dist/safety/safe-int-types.d.ts +45 -0
  254. package/dist/safety/safe-int-types.js +25 -0
  255. package/dist/safety/safe-variable-types.d.ts +29 -0
  256. package/dist/safety/safe-variable-types.js +19 -0
  257. package/dist/safety/safety-bridge.d.ts +5 -6
  258. package/dist/safety/safety-bridge.js +15 -28
  259. package/dist/safety/sidecar-bridge.d.ts +1 -3
  260. package/dist/safety/sidecar-bridge.js +3 -20
  261. package/dist/safety/specifiers.d.ts +6 -0
  262. package/dist/safety/specifiers.js +14 -0
  263. package/dist/safety-hook.d.ts +8 -7
  264. package/dist/safety-hook.js +12 -13
  265. package/dist/testing.d.ts +4 -5
  266. package/dist/testing.js +5 -5
  267. package/dist/transpile/resolution.d.ts +1 -1
  268. package/dist/transpile/resolution.js +18 -9
  269. package/dist/transpile/test-pins.d.ts +18 -0
  270. package/dist/transpile/test-pins.js +142 -0
  271. package/dist/transpile.d.ts +1 -2
  272. package/dist/transpile.js +36 -30
  273. package/dist/types.d.ts +26 -26
  274. package/dist/ui-hook.d.ts +1 -1
  275. package/dist/utils/cli.d.ts +2 -2
  276. package/dist/utils/cli.js +92 -154
  277. package/dist/utils/fs.d.ts +3 -4
  278. package/dist/utils/fs.js +4 -5
  279. package/dist/utils/strings.d.ts +0 -1
  280. package/dist/utils/strings.js +0 -8
  281. package/dist/utils/ui.d.ts +5 -2
  282. package/dist/utils/ui.js +13 -3
  283. package/package.json +158 -157
  284. package/dist/add-preset.d.ts +0 -4
  285. package/dist/add-preset.js +0 -74
  286. package/dist/api/shared/async-symbol-detector.d.ts +0 -11
  287. package/dist/api/shared/async-symbol-detector.js +0 -140
  288. package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
  289. package/dist/api/shared/native-display-op-resolver.js +0 -64
  290. package/dist/create/board-checklist.d.ts +0 -2
  291. package/dist/create/board-checklist.js +0 -52
  292. package/dist/create/board-codegen.d.ts +0 -9
  293. package/dist/create/board-codegen.js +0 -238
  294. package/dist/create/board-generators.d.ts +0 -12
  295. package/dist/create/board-generators.js +0 -651
  296. package/dist/create/board-spec.d.ts +0 -181
  297. package/dist/create/board-spec.js +0 -214
  298. package/dist/install/framework-catalog.d.ts +0 -53
  299. package/dist/install/framework-catalog.js +0 -107
  300. package/dist/install/handle-install.d.ts +0 -35
  301. package/dist/install/handle-install.js +0 -177
  302. package/dist/install/index.d.ts +0 -4
  303. package/dist/install/index.js +0 -3
  304. package/dist/ir/pin-state-tracking.d.ts +0 -58
  305. package/dist/ir/pin-state-tracking.js +0 -182
  306. package/dist/ir/pwm-timer-sharing.d.ts +0 -4
  307. package/dist/ir/pwm-timer-sharing.js +0 -94
  308. package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
  309. package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
  310. package/dist/libdef/component-decls.d.ts +0 -2
  311. package/dist/libdef/component-decls.js +0 -6
  312. package/dist/libdef/component-discovery.d.ts +0 -43
  313. package/dist/libdef/component-discovery.js +0 -83
  314. package/dist/theme-tokens.d.ts +0 -22
  315. package/dist/theme-tokens.js +0 -172
@@ -311,36 +311,40 @@ export function emitFunctions(ctx) {
311
311
  // Drive the UI runtime each frame. Fires only in the driver function when
312
312
  // a UI is mounted (entryHasUI). Uses a separate gate from the async pump
313
313
  // so a pure-UI program (no async/timers) still animates. When the strategy
314
- // provides hostEventLoop, the per-frame work runs inside a while loop so a
315
- // host target (SDL) can pump events and present between ticks. Emitted
316
- // AFTER the function's init statements (ui_init/display_init/touch_init)
317
- // so initialization precedes the loop.
318
- if (entryHasUI() && fn.name === asyncDriverFn) {
319
- const loop = strategy.hostEventLoop?.();
320
- if (loop) {
321
- appendSourceLine(ctx, ` bool ${loop.flagName} = true;`);
322
- appendSourceLine(ctx, ` while (${loop.continueCondition}) {`);
323
- appendSourceLine(ctx, ` ${loop.preIteration}`);
314
+ // provides hostEventLoop, the per-frame work ui_tick AND the async-task
315
+ // injections below runs inside a while loop so a driver that is called
316
+ // once (a main() entrypoint: SDL native, Zephyr's scheduler loop) can pump
317
+ // events, tick the UI, and advance async tasks every frame. (A repeatedly-
318
+ // called driver like Arduino's loop() provides no hostEventLoop and the
319
+ // work stays flat.) Emitted AFTER the function's init statements
320
+ // (ui_init/display_init/touch_init) so initialization precedes the loop.
321
+ const hostLoop = entryHasUI() && fn.name === asyncDriverFn
322
+ ? strategy.hostEventLoop?.()
323
+ : undefined;
324
+ if (hostLoop) {
325
+ appendSourceLine(ctx, ` bool ${hostLoop.flagName} = true;`);
326
+ appendSourceLine(ctx, ` while (${hostLoop.continueCondition}) {`);
327
+ if (hostLoop.preIteration) {
328
+ appendSourceLine(ctx, ` ${hostLoop.preIteration}`);
324
329
  }
330
+ }
331
+ if (entryHasUI() && fn.name === asyncDriverFn) {
325
332
  appendSourceLine(ctx, ` uint32_t __tc_ui_now = static_cast<uint32_t>(${strategy.currentTimeMillis()});`);
326
333
  appendSourceLine(ctx, " static uint32_t __tc_ui_last_tick = __tc_ui_now;");
327
334
  appendSourceLine(ctx, " uint32_t __tc_ui_delta = __tc_ui_now - __tc_ui_last_tick;");
328
335
  appendSourceLine(ctx, " __tc_ui_last_tick = __tc_ui_now;");
329
336
  appendSourceLine(ctx, " if (__tc_ui_delta > 250) __tc_ui_delta = 250;");
330
337
  appendSourceLine(ctx, " ui_tick(static_cast<uint16_t>(__tc_ui_delta));");
331
- if (loop) {
332
- appendSourceLine(ctx, ` ${loop.postIteration}`);
333
- appendSourceLine(ctx, ` }`);
334
- }
335
338
  }
336
339
  if (isLoopDriver && (hasPromiseRuntime || asyncTaskClasses.length > 0 || usesTimers)) {
337
340
  const taskNames = asyncTaskClasses.map(t => t.taskVarName);
338
341
  const asyncConfig = strategy.getAsyncRuntimeConfig();
339
342
  asyncConfig.hasPromiseRuntime = hasPromiseRuntime;
340
343
  asyncConfig.hasTimers = usesTimers;
344
+ const injectionIndent = hostLoop ? " " : " ";
341
345
  // Cooperative scheduler path (Phase 0): when opted in, replace the flat
342
346
  // per-frame pump sequence with a single CoopSched dispatch. The work
343
- // units mirror exactly what the strategy's asyncLoopInjection() would
347
+ // units mirror exactly what a strategy's asyncLoopInjection() would
344
348
  // have emitted (so Zephyr, which omits the microtask pump, still omits
345
349
  // it), but they are dispatched in priority order and budget-bounded.
346
350
  if (asyncConfig.enablePriority || asyncConfig.enableTimeBudget) {
@@ -354,16 +358,22 @@ export function emitFunctions(ctx) {
354
358
  // No work units: fall back to the strategy's own injection.
355
359
  const injectionLines = strategy.asyncLoopInjection(taskNames, asyncConfig);
356
360
  for (const line of injectionLines)
357
- appendSourceLine(ctx, ` ${line}`);
361
+ appendSourceLine(ctx, `${injectionIndent}${line}`);
358
362
  }
359
363
  }
360
364
  else {
361
365
  const injectionLines = strategy.asyncLoopInjection(taskNames, asyncConfig);
362
366
  for (const line of injectionLines) {
363
- appendSourceLine(ctx, ` ${line}`);
367
+ appendSourceLine(ctx, `${injectionIndent}${line}`);
364
368
  }
365
369
  }
366
370
  }
371
+ if (hostLoop) {
372
+ if (hostLoop.postIteration) {
373
+ appendSourceLine(ctx, ` ${hostLoop.postIteration}`);
374
+ }
375
+ appendSourceLine(ctx, " }");
376
+ }
367
377
  if (isLoopDriver && lastIsReturn) {
368
378
  appendRenderedStatement(ctx, lastStmt, " ", functionScope);
369
379
  }
@@ -400,6 +410,12 @@ export function emitFunctions(ctx) {
400
410
  export function emitFunctionForwardDeclarations(ctx) {
401
411
  const { strategy, effectiveEmitMode } = ctx;
402
412
  const excludedNames = new Set(strategy.forwardDeclarationExclusions?.() ?? []);
413
+ // Async-method starters: the owning class's method body calls these before
414
+ // the task class (and this definition) exists later in the TU. The class
415
+ // forward declarations emitted nearby make the <Class>* parameter valid.
416
+ for (const starter of ctx.asyncMethodStarters ?? []) {
417
+ appendSourceLine(ctx, starter.proto);
418
+ }
403
419
  // Callback forward declarations (no IRAM_ATTR — ESP-IDF's attribute uses
404
420
  // __COUNTER__, so decl+def would get conflicting .iram1.N sections).
405
421
  if (effectiveEmitMode !== "split") {
@@ -7,4 +7,11 @@ export declare function emitPreamble(ctx: EmitterContext): void;
7
7
  * from task bodies are already declared.
8
8
  */
9
9
  export declare function emitAsyncTaskClasses(ctx: EmitterContext): void;
10
+ /**
11
+ * Emit async-METHOD task classes + instances + starter definitions. Must run
12
+ * AFTER emitClasses (segments dereference _owner->field on the now-complete
13
+ * owning class) and BEFORE emitFunctions (the loop pump references the
14
+ * instances; the starter definitions call .start on them).
15
+ */
16
+ export declare function emitAsyncMethodTasks(ctx: EmitterContext): void;
10
17
  export declare function finalizeOutput(ctx: EmitterContext): GeneratedOutputs;
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { writeText, readText } from "../../utils/fs.js";
3
3
  import { makeGeneratedMap, writeSourceMap } from "../../mapping/source-map.js";
4
- import { dedupe, hasConsoleCalls, resolveTranspiledModuleInclude } from "../utils/index.js";
4
+ import { dedupe, resolveTranspiledModuleInclude } from "../utils/index.js";
5
5
  import { appendHeaderLine } from "./line-appender.js";
6
6
  import { runSelfCheck } from "../compliance/rule-engine.js";
7
7
  import { renderRegistryJson } from "../compliance/deviation-writer.js";
@@ -78,24 +78,6 @@ export function emitPreamble(ctx) {
78
78
  }
79
79
  // Async task classes are emitted later (emitAsyncTaskClasses) AFTER top-level
80
80
  // globals so references like WIFI_SSID inside the state machine compile.
81
- if (strategy.needsVectorOverload() && hasConsoleCalls(program, strategy) && (programAnalysis.usesVectorTypes || programAnalysis.hasArrayInObjectLiteral)) {
82
- appendSourceLineLocal(ctx, "template <typename T>");
83
- appendSourceLineLocal(ctx, "std::ostream& operator<<(std::ostream& os, const std::vector<T>& values)");
84
- appendSourceLineLocal(ctx, "{");
85
- appendSourceLineLocal(ctx, ' os << "[";');
86
- appendSourceLineLocal(ctx, " for (size_t i = 0; i < values.size(); ++i)");
87
- appendSourceLineLocal(ctx, " {");
88
- appendSourceLineLocal(ctx, " if (i > 0)");
89
- appendSourceLineLocal(ctx, " {");
90
- appendSourceLineLocal(ctx, ' os << ", ";');
91
- appendSourceLineLocal(ctx, " }");
92
- appendSourceLineLocal(ctx, " os << values[i];");
93
- appendSourceLineLocal(ctx, " }");
94
- appendSourceLineLocal(ctx, ' os << "]";');
95
- appendSourceLineLocal(ctx, " return os;");
96
- appendSourceLineLocal(ctx, "}");
97
- appendSourceLineLocal(ctx, "");
98
- }
99
81
  if (programAnalysis.hasGenerators) {
100
82
  appendSourceLineLocal(ctx, "#include <coroutine>");
101
83
  appendSourceLineLocal(ctx, "#include <optional>");
@@ -140,12 +122,38 @@ export function emitAsyncTaskClasses(ctx) {
140
122
  const { asyncTaskClasses } = ctx;
141
123
  if (asyncTaskClasses.length === 0)
142
124
  return;
143
- for (const { classDef, instanceDecl } of asyncTaskClasses) {
144
- for (const line of classDef.split("\n")) {
125
+ for (const t of asyncTaskClasses) {
126
+ // Async-METHOD tasks move to emitAsyncMethodTasks (after the user
127
+ // classes): their segment bodies dereference `_owner->field`, which
128
+ // needs the owning class COMPLETE — at this point only the forward
129
+ // declaration exists.
130
+ if (t.starterDef !== undefined)
131
+ continue;
132
+ for (const line of t.classDef.split("\n")) {
133
+ appendSourceLineLocal(ctx, line);
134
+ }
135
+ appendSourceLineLocal(ctx, "");
136
+ appendSourceLineLocal(ctx, t.instanceDecl);
137
+ appendSourceLineLocal(ctx, "");
138
+ }
139
+ }
140
+ /**
141
+ * Emit async-METHOD task classes + instances + starter definitions. Must run
142
+ * AFTER emitClasses (segments dereference _owner->field on the now-complete
143
+ * owning class) and BEFORE emitFunctions (the loop pump references the
144
+ * instances; the starter definitions call .start on them).
145
+ */
146
+ export function emitAsyncMethodTasks(ctx) {
147
+ const { asyncTaskClasses } = ctx;
148
+ for (const t of asyncTaskClasses) {
149
+ if (t.starterDef === undefined)
150
+ continue;
151
+ for (const line of t.classDef.split("\n")) {
145
152
  appendSourceLineLocal(ctx, line);
146
153
  }
147
154
  appendSourceLineLocal(ctx, "");
148
- appendSourceLineLocal(ctx, instanceDecl);
155
+ appendSourceLineLocal(ctx, t.instanceDecl);
156
+ appendSourceLineLocal(ctx, t.starterDef);
149
157
  appendSourceLineLocal(ctx, "");
150
158
  }
151
159
  }
@@ -330,7 +338,7 @@ export function finalizeOutput(ctx) {
330
338
  }
331
339
  // Sidecar deviation registry — written for both warn and strict modes
332
340
  // alongside the emitted artifact (mirrors how .thcppmap.json sits next
333
- // to the .cpp/.ino today).
341
+ // to the .cpp today).
334
342
  const toolVersion = options.toolVersion ?? "unknown";
335
343
  const registryPath = path.join(outDir, `${baseName}.autosar-deviations.json`);
336
344
  writeText(registryPath, renderRegistryJson(ctx.compliance, path.basename(sourcePath), toolVersion));
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { filterPolyfillHelpers, isStringEnum } from "../../api/shared/index.js";
3
3
  import { hasSafetyHook, requireSafetyHook } from "../../safety-hook.js";
4
+ import { isSafetyImportSpecifier } from "../../safety/specifiers.js";
4
5
  import { analyzeProgram } from "../../ir/program-analysis.js";
5
6
  import { collectStatementIdentifiers } from "../../ir/identifier-collector.js";
6
7
  import { ensureDir } from "../../utils/fs.js";
@@ -240,6 +241,11 @@ function extractShimMacro(lines, macroName) {
240
241
  return lines.slice(ifndefIdx, endIdx + 1);
241
242
  }
242
243
  export function buildEmitterContext(program, options) {
244
+ // Framework strategies that resolve per-program chip data (Zephyr's chip
245
+ // view reconstructs from the generated manifest) must do it before any
246
+ // body rendering — hal-op lowering reads their cached descriptor.
247
+ options.strategy
248
+ ?.prepareChip?.(program, options.platformContext);
243
249
  const largeEnumNames = new Set();
244
250
  const enumNames = new Set();
245
251
  const stringEnumNames = new Set();
@@ -313,23 +319,6 @@ export function buildEmitterContext(program, options) {
313
319
  })();
314
320
  const programAnalysis = analyzeProgram(program, strategy);
315
321
  if (options.platformContext) {
316
- // The UI runtime's per-frame tick calls millis() (injected by the emitter,
317
- // not present in user source), so a mounted UI needs the native millis ISR
318
- // even when usesNativeTiming is false. OR entryHasUI() into the flag the
319
- // AVR strategy reads when gating native_millis, so framework-avr doesn't
320
- // drop the Timer0 ISR from a UI program.
321
- if (!programAnalysis.usesNativeTiming && entryHasUI()) {
322
- programAnalysis.usesNativeTiming = true;
323
- }
324
- // The UI runtime header calls constrain() in the progress/range node draw
325
- // and touch-slider paths (injected by the emitter, not present in user
326
- // source), so a mounted UI needs the constrain polyfill even when
327
- // usesConstrain is false. OR entryHasUI() into the flag the native
328
- // strategy reads when gating the constrain helper, mirroring the
329
- // usesNativeTiming handling above.
330
- if (!programAnalysis.usesConstrain && entryHasUI()) {
331
- programAnalysis.usesConstrain = true;
332
- }
333
322
  options.platformContext.analysis = programAnalysis;
334
323
  if (!options.platformContext.architecture && program.boardConstants) {
335
324
  options.platformContext.architecture = program.boardConstants.get("architecture");
@@ -368,16 +357,11 @@ export function buildEmitterContext(program, options) {
368
357
  if (!programAnalysis.usedPolyfillHelpers.has('__tc_StaticArray')) {
369
358
  filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "static_array");
370
359
  }
371
- const usesTimers = programAnalysis.usedPolyfillHelpers.has('__tc_setInterval') ||
372
- programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
373
- if (!usesTimers) {
374
- filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "timer_methods");
375
- }
376
360
  // Safety polyfills (mode table + voter + SafeVariable + SafeInt). Gated on
377
- // programUsesSafety(program) so they ONLY appear when the sketch actually
361
+ // programUsesSafety(program) so they ONLY appear when the program actually
378
362
  // references a safety construct — emitting them unconditionally (just because
379
363
  // @typecad/safety is installed and registered its hook) leaks ~200 lines of
380
- // polyfill into every sketch, and SafeInt's `return *this` chaining methods
364
+ // polyfill into every program, and SafeInt's `return *this` chaining methods
381
365
  // tripped byte-identity / lowering assertions that expect no `this` token.
382
366
  // programUsesSafety walks the IR for any safety.* hal-op.
383
367
  if (isEntryFile && hasSafetyHook() && programUsesSafety(program)) {
@@ -393,8 +377,7 @@ export function buildEmitterContext(program, options) {
393
377
  // minimal-libc targets (Zephyr) where the Promise runtime does not.
394
378
  const coopConfig = strategy.getAsyncRuntimeConfig?.();
395
379
  const hasCoopWork = program.functions.some(fn => fn.isAsync) ||
396
- programAnalysis.usedPolyfillHelpers.has('__tc_setInterval') ||
397
- programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
380
+ program.classes.some(c => c.methods.some(m => m.isAsync));
398
381
  if (isEntryFile && coopConfig && hasCoopWork) {
399
382
  const coop = buildCoopSchedulerPolyfill(program, coopConfig, strategy.currentTimeMillis());
400
383
  if (coop)
@@ -404,8 +387,13 @@ export function buildEmitterContext(program, options) {
404
387
  const emittedPolyfills = allPolyfills.length > 0
405
388
  ? emitPolyfillBoilerplate(allPolyfills)
406
389
  : undefined;
407
- const hasAsyncRuntime = program.functions.some(fn => fn.isAsync);
390
+ const hasAsyncRuntime = program.functions.some(fn => fn.isAsync) || program.classes.some(c => c.methods.some(m => m.isAsync));
408
391
  const hasPromiseRuntime = nativePolyfills.some((p) => p.id === "async_runtime" && p.hasPromiseRuntime === true);
392
+ // Native timer capability comes from the strategy's async config, not from
393
+ // scanning the program: platforms either provide native timer callbacks for
394
+ // the async runtime (host/native) or they do not (Zephyr — periodic work is
395
+ // a Thread or a Counter there).
396
+ const usesTimers = strategy.getAsyncRuntimeConfig?.()?.hasTimers === true;
409
397
  if (!isNpmPackage) {
410
398
  includes.push(...strategy.forcedIncludes(program, options.platformContext));
411
399
  Object.assign(symbolMap, strategy.symbolAliases(program, options.platformContext));
@@ -424,16 +412,6 @@ export function buildEmitterContext(program, options) {
424
412
  if (!programAnalysis.usesDateNow) {
425
413
  shimLines = shimLines.filter(l => !l.includes('namespace Date'));
426
414
  }
427
- // The UI runtime's per-frame tick uses millis() (injected by the emitter,
428
- // not authored in user source), so keep the millis() shim when a UI is
429
- // mounted even if the source-level analysis didn't flag usesMillis. The
430
- // async runtime and the setInterval/setTimeout scheduler also poll
431
- // millis() without any user-source millis() call (Async.sleep lowers to a
432
- // raw hal-op the timing scanners can't see) — mirror the usesNativeTiming
433
- // derivation and keep the shim for those hidden consumers too.
434
- if (!programAnalysis.usesMillis && !programAnalysis.hasAsync && programAnalysis.timerCallCount === 0 && !entryHasUI()) {
435
- shimLines = shimLines.filter(l => !l.includes('millis()'));
436
- }
437
415
  // Strip the nullish helper FUNCTIONS (not the CUTTLEFISH_UNDEFINED macro)
438
416
  // when the file doesn't actually emit cuttlefish_nullish(...) calls. A
439
417
  // file that only references `null`/`undefined` literals needs just the
@@ -445,6 +423,14 @@ export function buildEmitterContext(program, options) {
445
423
  // cuttlefish_nullish( call (a more reliable post-emit check than the
446
424
  // per-file pre-emit analysis flag). The logic here governs the .cpp
447
425
  // source shim only.
426
+ // The runtime clock (__tc_now_ms) is gated by the Zephyr strategy at
427
+ // emission; native emits its definition unconditionally. Strip it from
428
+ // programs that can never read the clock (no Time calls, async, timers,
429
+ // scheduler, or mounted UI) so the definition doesn't leak into the
430
+ // emitted header.
431
+ if (!programAnalysis.usesWallClock && !programAnalysis.hasAsync && !entryHasUI() && !hasPromiseRuntime) {
432
+ shimLines = shimLines.filter(l => !l.includes('__tc_now_ms'));
433
+ }
448
434
  if (!programAnalysis.usesNullishHelper) {
449
435
  const filtered = [];
450
436
  for (let i = 0; i < shimLines.length; i++) {
@@ -458,9 +444,6 @@ export function buildEmitterContext(program, options) {
458
444
  if (!programAnalysis.usesNum) {
459
445
  shimLines = filterShimBlock(shimLines, 'struct __tc_Num {', '} Num;');
460
446
  }
461
- if (!programAnalysis.usesTiming) {
462
- shimLines = filterShimBlock(shimLines, 'struct __tc_Timing {', '} Timing;');
463
- }
464
447
  if (!programAnalysis.usesWDT) {
465
448
  shimLines = filterShimBlock(shimLines, '#include <avr/wdt.h>', '} WDT;');
466
449
  }
@@ -473,9 +456,7 @@ export function buildEmitterContext(program, options) {
473
456
  // these filters are no-ops there. The strategies also self-gate on the
474
457
  // same flags; this is the defensive backstop (mirrors how usesWDT/etc.
475
458
  // backstop the strategy-side gating above).
476
- // Keep the UART block when console calls are present (console_log etc.
477
- // call _uart_* functions) even if no direct uart.* HAL ops are used.
478
- if (!programAnalysis.usesUart && !programAnalysis.hasConsoleCalls) {
459
+ if (!programAnalysis.usesUart) {
479
460
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_BEGIN', '// CUTTLEFISH_UART_END');
480
461
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_EXT_BEGIN', '// CUTTLEFISH_UART_EXT_END');
481
462
  }
@@ -485,14 +466,8 @@ export function buildEmitterContext(program, options) {
485
466
  if (!programAnalysis.usesI2C) {
486
467
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TWI_BEGIN', '// CUTTLEFISH_TWI_END');
487
468
  }
488
- if (!programAnalysis.usesEEPROM && !programAnalysis.usesPreferences) {
489
- shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_EEPROM_BEGIN', '// CUTTLEFISH_EEPROM_END');
490
- }
491
- if (!programAnalysis.usesTone) {
492
- shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TONE_BEGIN', '// CUTTLEFISH_TONE_END');
493
- }
494
469
  // framework-esp32 native peripheral driver shims. Same defensive-backstop
495
- // pattern as the UART/SPI/TWI/EEPROM/tone blocks above. framework-arduino's
470
+ // pattern as the UART/SPI/TWI/EEPROM blocks above. framework-arduino's
496
471
  // and framework-avr's shimLines contain none of these markers, so these
497
472
  // filters are no-ops there.
498
473
  if (!programAnalysis.usesGPIO) {
@@ -507,9 +482,6 @@ export function buildEmitterContext(program, options) {
507
482
  if (!programAnalysis.usesDAC) {
508
483
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_DAC_BEGIN', '// CUTTLEFISH_DAC_END');
509
484
  }
510
- if (!programAnalysis.usesPower) {
511
- shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_POWER_BEGIN', '// CUTTLEFISH_POWER_END');
512
- }
513
485
  if (!programAnalysis.usesWdt) {
514
486
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WDT_BEGIN', '// CUTTLEFISH_WDT_END');
515
487
  }
@@ -608,7 +580,7 @@ export function buildEmitterContext(program, options) {
608
580
  // time; the package emits NO C++ module/header. Register the imported
609
581
  // symbols so references resolve, but skip the #include generation.
610
582
  if (imported.moduleSpecifier === "@typecad/ui" ||
611
- imported.moduleSpecifier === "@typecad/safety") {
583
+ isSafetyImportSpecifier(imported.moduleSpecifier)) {
612
584
  for (const symbol of imported.namedImports) {
613
585
  symbolMap[symbol] = symbol;
614
586
  }
@@ -702,11 +674,11 @@ export function buildEmitterContext(program, options) {
702
674
  // Map a function's original TS name to its emitted C++ name, applying the
703
675
  // strategy's user-function renames. The entrypoint sentinel
704
676
  // `__cuttlefish_entrypoint__` maps to the strategy's entrypoint name
705
- // (`main` on native, `setup` on Arduino); every other name is routed through
677
+ // (per-target entrypoint names); every other name is routed through
706
678
  // `strategy.mapFunctionName`, which is where a target renames a user
707
679
  // function that would otherwise collide with a C++/framework reserved name
708
- // — e.g. Arduino renames a user `function main()` to `cuttlefish_main`,
709
- // because Arduino has no `main()` (the entrypoints are the auto-generated
680
+ // — e.g. Targets without a user `main()` rename one to `cuttlefish_main`;
681
+ // because this target has no user `main()` (the entrypoints are the auto-generated
710
682
  // `setup()`/`loop()`), and a file-scope `static void main()` collides with
711
683
  // C++'s required `int main()` signature. Call-site renames are applied
712
684
  // uniformly in StatementRenderer.renderCall (the single call-rendering
@@ -879,7 +851,7 @@ export function buildEmitterContext(program, options) {
879
851
  // method/ctor parameter, or a top-level binding. Previously only local
880
852
  // var_decls were registered (in class-emitter), which ran too late for the
881
853
  // free-function pass — leaving `hero->alive` unrewritten and failing g++.
882
- // See SUPPORT_MATRIX §4.3 (demo #4 fix).
854
+ // (demo #4 fix.)
883
855
  // `classAccessorNames` is built at the outer scope (not inside a nested
884
856
  // block) so it can also be passed to the expression renderer as
885
857
  // `typeAccessorNames` — the type-keyed fallback used when a getter access's
@@ -989,6 +961,7 @@ export function buildEmitterContext(program, options) {
989
961
  const mapFunctionNameLocal = (originalName) => statementRenderer.mapFunctionName(originalName);
990
962
  const asyncFunctionMappedNames = new Set(program.functions.filter((fn) => fn.isAsync).map((fn) => mapFunctionNameLocal(fn.originalName)));
991
963
  const asyncTaskClasses = [];
964
+ const asyncMethodStarters = [];
992
965
  if (hasAsyncRuntime) {
993
966
  for (const fn of program.functions) {
994
967
  if (fn.isAsync) {
@@ -1014,21 +987,103 @@ export function buildEmitterContext(program, options) {
1014
987
  // Use renderWithPrelude so snprintf buffer declarations (e.g.
1015
988
  // char __cuttlefish_str_N[...]; snprintf(...)) are emitted before
1016
989
  // the statement that references them. Without this, template-literal
1017
- // console.log inside async functions loses the buffer declaration.
990
+ // string building inside async functions loses the buffer declaration.
1018
991
  const { prelude, statement } = contextRenderer.renderWithPrelude(stmt, forHeader, calleeTransformer);
1019
992
  if (prelude.length > 0) {
1020
993
  return prelude.join("\n") + "\n" + statement;
1021
994
  }
1022
995
  return statement;
996
+ }, {
997
+ onUnsupportedAwait: (callee, span) => {
998
+ emitDiagnostics.push({
999
+ severity: "error",
1000
+ code: "await-unsupported-call",
1001
+ message: `await ${callee}(...) has no cooperative lowering here — the call would be dropped. Await is supported on Time.sleep, HAL ops (wifi.join / wifi.scan / http.send), ui.onTap(), and pin-edge waits.`,
1002
+ ...(span ? { source: `${callee}(...)` } : {}),
1003
+ });
1004
+ },
1023
1005
  });
1024
1006
  asyncTaskClasses.push({ ...task, taskVarName: `${fn.originalName}Task` });
1025
1007
  }
1026
1008
  }
1009
+ // Async METHODS on user classes: the body becomes an owner-bound task
1010
+ // (this->x renders as _owner->x inside segments); the in-class method
1011
+ // body is replaced by a call to a forward-declared starter that binds
1012
+ // the receiver and arms the machine (see class-emitter.ts).
1013
+ for (const cls of program.classes) {
1014
+ for (const method of cls.methods) {
1015
+ if (!method.isAsync)
1016
+ continue;
1017
+ const fnName = `${cls.name}_${method.name}`;
1018
+ if (method.isStatic) {
1019
+ emitDiagnostics.push({
1020
+ severity: "error",
1021
+ code: "async-method-static",
1022
+ message: `static async method ${cls.name}.${method.name}() is not supported — hoist it to a free async function instead.`,
1023
+ });
1024
+ continue;
1025
+ }
1026
+ if (method.parameters.length > 0) {
1027
+ emitDiagnostics.push({
1028
+ severity: "error",
1029
+ code: "async-method-params",
1030
+ message: `async method ${cls.name}.${method.name}() takes parameters, which the cooperative state machine cannot capture — make it parameterless.`,
1031
+ });
1032
+ continue;
1033
+ }
1034
+ if (method.returnType !== "void" && !String(method.returnType).startsWith("Promise")) {
1035
+ emitDiagnostics.push({
1036
+ severity: "error",
1037
+ code: "async-method-return",
1038
+ message: `async method ${cls.name}.${method.name}() returns ${method.returnType}; async methods lower to fire-and-forget tasks and must return void (Promise<void>).`,
1039
+ });
1040
+ continue;
1041
+ }
1042
+ const task = generateAsyncTaskClass(fnName, method.statements, strategy, knownFunctionReturnTypes, (stmt, forHeader, strategy, pointerVarTypes, calleeTransformer, knownReturnTypes) => {
1043
+ const contextRenderer = new StatementRenderer({
1044
+ strategy,
1045
+ boardConstants: program.boardConstants,
1046
+ classNameMap,
1047
+ enumNames,
1048
+ stringEnumNames,
1049
+ largeEnumNames,
1050
+ knownFunctionReturnTypes: knownReturnTypes ?? knownFunctionReturnTypes,
1051
+ knownVariableTypes: topLevelScope.knownVariableTypes,
1052
+ globalPointerVarTypes,
1053
+ namespaceNames,
1054
+ snprintfCounter,
1055
+ stringVarNames,
1056
+ varAccessorNames,
1057
+ typeAccessorNames: classAccessorNames,
1058
+ pointerVarTypes,
1059
+ diagnostics: emitDiagnostics,
1060
+ });
1061
+ const { prelude, statement } = contextRenderer.renderWithPrelude(stmt, forHeader, calleeTransformer);
1062
+ const body = prelude.length > 0 ? prelude.join("\n") + "\n" + statement : statement;
1063
+ // The task class is not the receiver: rebind member accesses from
1064
+ // the owning instance to the task's _owner pointer. The renderer
1065
+ // emits every this-form as `this->`; a rendered string literal
1066
+ // containing that token would be rewritten too (accepted edge).
1067
+ return body.replace(/this->/g, "_owner->");
1068
+ }, {
1069
+ ownerClassName: cls.name,
1070
+ onUnsupportedAwait: (callee, span) => {
1071
+ emitDiagnostics.push({
1072
+ severity: "error",
1073
+ code: "await-unsupported-call",
1074
+ message: `await ${callee}(...) has no cooperative lowering here — the call would be dropped. Await is supported on Time.sleep, HAL ops (wifi.join / wifi.scan / http.send), ui.onTap(), and pin-edge waits.`,
1075
+ ...(span ? { source: `${callee}(...)` } : {}),
1076
+ });
1077
+ },
1078
+ });
1079
+ asyncTaskClasses.push({ ...task, taskVarName: `${fnName}Task` });
1080
+ asyncMethodStarters.push({
1081
+ proto: `void __tc_async_start_${fnName}(${cls.name}* owner);`,
1082
+ });
1083
+ }
1084
+ }
1027
1085
  }
1028
1086
  // Additional includes computed from analysis
1029
- if (strategy.needsIostream() && programAnalysis.hasConsoleCalls) {
1030
- includes.push("<iostream>");
1031
- }
1032
1087
  const usesVectorTypes = programAnalysis.usesVectorTypes || programAnalysis.hasArrayInObjectLiteral;
1033
1088
  if (programAnalysis.usesStdString && strategy.needsStdString()) {
1034
1089
  includes.push("<string>");
@@ -1105,7 +1160,13 @@ export function buildEmitterContext(program, options) {
1105
1160
  // include in the AVR profile (which leaked the header into every AVR
1106
1161
  // program, even ones that never touch the watchdog like `led.toggle()`).
1107
1162
  if (programUsesWdt(program)) {
1108
- includes.push("<avr/wdt.h>");
1163
+ // Strategy-vetoed: framework-zephyr lowers wdt.* to the Zephyr watchdog
1164
+ // driver (<zephyr/drivers/watchdog.h>, forced under usesWDT) and must not
1165
+ // carry the AVR-only header into a Zephyr build.
1166
+ const wdtInc = strategy.filterRequiredIncludes
1167
+ ? strategy.filterRequiredIncludes(["<avr/wdt.h>"])
1168
+ : ["<avr/wdt.h>"];
1169
+ includes.push(...wdtInc);
1109
1170
  }
1110
1171
  // Placeholder defaults for fields that are computed later by other phases
1111
1172
  const noopFixPointer = (c) => c;
@@ -1188,6 +1249,7 @@ export function buildEmitterContext(program, options) {
1188
1249
  boardConstants: program.boardConstants,
1189
1250
  restParamFunctions: program.restParamFunctions,
1190
1251
  asyncTaskClasses,
1252
+ asyncMethodStarters,
1191
1253
  asyncFunctionOriginalNames,
1192
1254
  asyncFunctionMappedNames,
1193
1255
  hasAsyncRuntime,
@@ -411,7 +411,7 @@ export function runTopLevelPreprocessing(ctx) {
411
411
  // sees runtime mutations — e.g. `const reached = descended` freezes `reached`
412
412
  // at `descended`'s initial value because both are emitted as file-scope globals
413
413
  // initialized before main(), while the `descended = depth` assignments run
414
- // inside main(). See SUPPORT_MATRIX §6.1.
414
+ // inside main().
415
415
  {
416
416
  // Collect every variable name that is the target of an assignment anywhere
417
417
  // in the program (top-level statements + function/method bodies). Such
@@ -2,6 +2,7 @@ import { emitCommentLines, isRuntimeExpression } from "../utils/index.js";
2
2
  import { appendSourceLine, appendHeaderLine, appendRenderedStatement } from "./line-appender.js";
3
3
  import { escapeCppKeyword } from "../../utils/strings.js";
4
4
  import { isStringEnum } from "../../api/shared/index.js";
5
+ import { parsedIsVector } from "../../api/shared/cpp-type-ir.js";
5
6
  import { resolveEnumValues, narrowestEnumUnderlying } from "../utils/cpp-helpers.js";
6
7
  export function emitTypeDeclarations(ctx) {
7
8
  const { program, strategy, effectiveEmitMode, reservedNames, emittedTopLevelStatements, topLevelScope } = ctx;
@@ -249,14 +250,18 @@ export function emitTypeDeclarations(ctx) {
249
250
  const typeAlreadyConst = cppType.trimStart().startsWith("const");
250
251
  const constPrefix = isConst && !typeAlreadyConst ? "const " : "";
251
252
  const varName = escapeCppKeyword(statement.name, platformReservedNames);
252
- // Array-typed top-level consts (e.g. `export const ARR: T[] = [...]`)
253
- // lower with cppType "auto" and a `{ kind: "array", elementType }`
254
- // initializer. They emit as C-style arrays (`T name[] = {...}`), so the
255
- // matching extern is `extern const T name[];` not a scalar extern and
256
- // not a std::vector extern. Without this, cross-file consumers fail with
257
- // "was not declared in this scope" (the definition lives in the .cpp
258
- // only). Mirror renderVarDecl's element-type fallback for "auto".
259
- if (cppType === "auto" && statement.initializer && statement.initializer.kind === "array") {
253
+ // Array-typed top-level consts (e.g. `export const ARR: T[] = [...]`
254
+ // and `new Uint8Array(N)` buffers) lower with an `{ kind: "array",
255
+ // elementType }` initializer typed arrays carry a pointer cppType
256
+ // (`uint8_t*`) even though renderVarDecl emits the DEFINITION as a
257
+ // C-style array (`T name[] = {...}`). The matching extern must use the
258
+ // same array form regardless of cppType a pointer extern conflicts
259
+ // with the array definition. Vector-typed arrays are the exception: the
260
+ // definition emits `std::vector<T> name = {...}`, so the extern stays a
261
+ // vector extern. Without this, cross-file consumers fail with "was not
262
+ // declared in this scope" (the definition lives in the .cpp only).
263
+ // Mirror renderVarDecl's element-type fallback for "auto".
264
+ if (statement.initializer && statement.initializer.kind === "array" && !parsedIsVector(cppType)) {
260
265
  const elemType = statement.initializer.elementType && statement.initializer.elementType !== "auto"
261
266
  ? strategy.normalizeCppType(statement.initializer.elementType)
262
267
  : strategy.defaultNumericType(ctx.compliance.isEnabled() ? ctx.compliance : undefined);
@@ -163,7 +163,7 @@ export function emitUIRuntime(ctx) {
163
163
  if (profile) {
164
164
  const adapter = generateDisplayAdapter(profile, ctx.strategy);
165
165
  // Prepend includes to the very front — Arduino's auto-prototyper scans
166
- // the whole .ino and generates prototypes that reference GFXcanvas16
166
+ // the whole translation unit and generates prototypes that reference GFXcanvas16
167
167
  // etc. before any #include, so the GFX header must come first.
168
168
  ctx.sourceLines.unshift(adapter.includes);
169
169
  // Color-depth preamble: UI_COLOR_T is referenced by adapter draw
@@ -188,8 +188,8 @@ export declare class ExpressionRenderer {
188
188
  * String-enum operands are passed through unchanged (they're `const char*`).
189
189
  *
190
190
  * This is the assignment/argument-site counterpart to the operator-level
191
- * enum wrapping in renderBinary. SUPPORT_MATRIX §1.7: enum values used as
192
- * integers need an explicit cast in C++.
191
+ * enum wrapping in renderBinary. Enum values used as integers need an
192
+ * explicit cast in C++.
193
193
  */
194
194
  renderEnumSafeValue(expr: ExpressionIR, knownVariableTypes?: Map<string, KnownVariableInfo>): string;
195
195
  /**
@@ -206,7 +206,7 @@ export declare class ExpressionRenderer {
206
206
  * operand → `static_cast<EnumType>(value)` (int → enum). C++ `enum
207
207
  * class` does not implicitly convert FROM an integral storage type
208
208
  * either, so reading an `int`/`uint8_t` cell back into an enum-typed
209
- * local needs the reverse cast. SUPPORT_MATRIX §1.7.
209
+ * local needs the reverse cast.
210
210
  *
211
211
  * This closes the "enum↔integral storage boundary" family — the previous
212
212
  * point-specific casts handled enum-as-array-index (demo #28 E) and