@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
@@ -0,0 +1,127 @@
1
+ import { asilLevel, ASIL_THRESHOLDS } from "./asil.js";
2
+ /** B5: Init completeness (ISO 26262 Part 6, §7.4.5 — ASIL D).
3
+ *
4
+ * Checks that every identifier used in a safety-critical function (ASIL D)
5
+ * was initialized in setup() or at file scope. An uninitialized variable
6
+ * in a safety-critical path is a potential source of undefined behavior.
7
+ *
8
+ * Approach: collect identifiers assigned/declared in setup() and top-level
9
+ * (the "init set"), then walk each ASIL D function's statements looking
10
+ * for identifiers used before any local assignment. Reports each as a
11
+ * warning. */
12
+ export function checkInitCompleteness(program, _ctx) {
13
+ const diags = [];
14
+ // Collect the "init set": identifiers declared/assigned in setup() and
15
+ // top-level statements. These are considered initialized.
16
+ const initSet = new Set();
17
+ // Top-level var_decls and assignments
18
+ for (const stmt of program.topLevelStatements) {
19
+ collectAssignedNames(stmt, initSet);
20
+ }
21
+ // setup() statements
22
+ const setupFn = program.functions.find(fn => fn.originalName === "setup");
23
+ if (setupFn) {
24
+ for (const stmt of setupFn.statements) {
25
+ collectAssignedNames(stmt, initSet);
26
+ }
27
+ }
28
+ // Check each ASIL D function for use of identifiers not in the init set
29
+ for (const fn of program.functions) {
30
+ if (fn.originalName === "setup" || fn.originalName === "loop")
31
+ continue;
32
+ if (asilLevel(fn.decorators) < ASIL_THRESHOLDS.dynamicAlloc)
33
+ continue;
34
+ // Collect locally-declared names in this function (params + local vars)
35
+ const localNames = new Set();
36
+ for (const param of fn.parameters) {
37
+ localNames.add(param.name);
38
+ }
39
+ // Walk statements — look for identifiers used before local declaration
40
+ for (const stmt of fn.statements) {
41
+ checkUsedBeforeDeclared(stmt, fn.originalName, initSet, localNames, diags);
42
+ }
43
+ }
44
+ return diags;
45
+ }
46
+ /** Collect names that are declared or assigned in a statement tree. */
47
+ function collectAssignedNames(stmt, names) {
48
+ if (!stmt || typeof stmt !== "object")
49
+ return;
50
+ if (stmt.kind === "var_decl" && typeof stmt.name === "string") {
51
+ names.add(stmt.name);
52
+ }
53
+ if (stmt.kind === "assign" && typeof stmt.target === "string") {
54
+ names.add(stmt.target);
55
+ }
56
+ if (stmt.kind === "update" && typeof stmt.target === "string") {
57
+ names.add(stmt.target);
58
+ }
59
+ for (const key of Object.keys(stmt)) {
60
+ const val = stmt[key];
61
+ if (Array.isArray(val)) {
62
+ for (const item of val) {
63
+ if (item && typeof item === "object" && typeof item.kind === "string") {
64
+ collectAssignedNames(item, names);
65
+ }
66
+ }
67
+ }
68
+ else if (val && typeof val === "object" && typeof val.kind === "string") {
69
+ collectAssignedNames(val, names);
70
+ }
71
+ }
72
+ }
73
+ /** Check if a statement uses identifiers that were never initialized. */
74
+ function checkUsedBeforeDeclared(stmt, fnName, initSet, localNames, diags) {
75
+ if (!stmt || typeof stmt !== "object")
76
+ return;
77
+ // Check identifiers used in expressions
78
+ if (stmt.kind === "identifier" && typeof stmt.value === "string") {
79
+ const name = stmt.value;
80
+ // Skip if it's initialized at file scope/setup, or declared locally,
81
+ // or it's a known built-in (skip common names).
82
+ if (!initSet.has(name) && !localNames.has(name) && !isBuiltin(name)) {
83
+ diags.push({
84
+ severity: "warning",
85
+ code: "ISO26262_B5_INIT_COMPLETENESS",
86
+ message: `Variable '${name}' used in ${fnName}() may not be initialized in setup() or at file scope`,
87
+ hint: "ASIL D requires all variables in safety-critical paths to be initialized before use. Initialize in setup() or declare at file scope.",
88
+ });
89
+ }
90
+ }
91
+ // Recurse
92
+ for (const key of Object.keys(stmt)) {
93
+ const val = stmt[key];
94
+ if (Array.isArray(val)) {
95
+ for (const item of val) {
96
+ if (item && typeof item === "object" && typeof item.kind === "string") {
97
+ checkUsedBeforeDeclared(item, fnName, initSet, localNames, diags);
98
+ }
99
+ }
100
+ }
101
+ else if (val && typeof val === "object" && typeof val.kind === "string") {
102
+ checkUsedBeforeDeclared(val, fnName, initSet, localNames, diags);
103
+ }
104
+ }
105
+ }
106
+ function isBuiltin(name) {
107
+ // Common names that don't need explicit init
108
+ const builtins = new Set([
109
+ "true", "false", "null", "undefined", "undefined_t",
110
+ "console", "Math", "Timing", "Serial", "WDT", "Preferences",
111
+ "delay", "millis", "micros", "delayMicroseconds",
112
+ "HIGH", "LOW", "INPUT", "OUTPUT", "INPUT_PULLUP", "INPUT_PULLDOWN",
113
+ "LED_BUILTIN", "Serial",
114
+ // Cuttlefish internal names
115
+ "__tc_", "SafeReadResult", "SafeWriteResult", "SafeVariable", "SafeInt",
116
+ "safe", "volatile",
117
+ ]);
118
+ if (builtins.has(name))
119
+ return true;
120
+ // Skip names starting with __ (cuttlefish internal)
121
+ if (name.startsWith("__"))
122
+ return true;
123
+ // Skip all-caps names (likely macros/constants)
124
+ if (name === name.toUpperCase() && name.length > 1)
125
+ return true;
126
+ return false;
127
+ }
@@ -0,0 +1,7 @@
1
+ import type { ProgramIR, Diagnostic } from "../../api/index.js";
2
+ import type { SafetyTransformContext } from "../../safety-hook.js";
3
+ /** B3: Unbounded loop detection (ISO 26262 Part 6, §7.4.10 — ASIL C+).
4
+ *
5
+ * Only checks functions decorated with @asilC or higher.
6
+ * Lower ASIL levels and QM functions are skipped. */
7
+ export declare function checkUnboundedLoops(program: ProgramIR, _ctx: SafetyTransformContext): Diagnostic[];
@@ -0,0 +1,86 @@
1
+ import { asilLevel, ASIL_THRESHOLDS } from "./asil.js";
2
+ /** B3: Unbounded loop detection (ISO 26262 Part 6, §7.4.10 — ASIL C+).
3
+ *
4
+ * Only checks functions decorated with @asilC or higher.
5
+ * Lower ASIL levels and QM functions are skipped. */
6
+ export function checkUnboundedLoops(program, _ctx) {
7
+ const diags = [];
8
+ for (const fn of program.functions) {
9
+ if (asilLevel(fn.decorators) < ASIL_THRESHOLDS.unboundedLoop)
10
+ continue;
11
+ for (const stmt of fn.statements) {
12
+ scanForUnboundedLoop(stmt, fn.originalName, diags);
13
+ }
14
+ }
15
+ for (const cls of program.classes) {
16
+ for (const method of cls.methods) {
17
+ if (!method.name)
18
+ continue;
19
+ if (asilLevel(method.decorators) < ASIL_THRESHOLDS.unboundedLoop)
20
+ continue;
21
+ for (const stmt of method.statements) {
22
+ scanForUnboundedLoop(stmt, method.name, diags);
23
+ }
24
+ }
25
+ }
26
+ return diags;
27
+ }
28
+ function scanForUnboundedLoop(stmt, fnName, diags) {
29
+ if (!stmt || typeof stmt !== "object")
30
+ return;
31
+ if (stmt.kind === "while") {
32
+ const cond = stmt.condition;
33
+ if (isConstantTrue(cond)) {
34
+ diags.push({
35
+ severity: "warning",
36
+ code: "ISO26262_B3_UNBOUNDED_LOOP",
37
+ message: `Unbounded while(true) in ${fnName}()`,
38
+ hint: "Use a bounded loop counter or a timeout guard to ensure termination.",
39
+ });
40
+ }
41
+ }
42
+ if (stmt.kind === "for" && !stmt.condition) {
43
+ diags.push({
44
+ severity: "warning",
45
+ code: "ISO26262_B3_UNBOUNDED_LOOP",
46
+ message: `Unbounded for(;;) in ${fnName}()`,
47
+ hint: "Use a bounded loop counter or a timeout guard to ensure termination.",
48
+ });
49
+ }
50
+ if (stmt.kind === "do_while") {
51
+ const cond = stmt.condition;
52
+ if (isConstantTrue(cond)) {
53
+ diags.push({
54
+ severity: "warning",
55
+ code: "ISO26262_B3_UNBOUNDED_LOOP",
56
+ message: `Unbounded do-while(true) in ${fnName}()`,
57
+ hint: "Use a bounded loop counter or a timeout guard to ensure termination.",
58
+ });
59
+ }
60
+ }
61
+ for (const key of Object.keys(stmt)) {
62
+ const val = stmt[key];
63
+ if (Array.isArray(val)) {
64
+ for (const item of val) {
65
+ if (item && typeof item === "object" && typeof item.kind === "string") {
66
+ scanForUnboundedLoop(item, fnName, diags);
67
+ }
68
+ }
69
+ }
70
+ else if (val && typeof val === "object" && typeof val.kind === "string") {
71
+ scanForUnboundedLoop(val, fnName, diags);
72
+ }
73
+ }
74
+ }
75
+ function isConstantTrue(expr) {
76
+ if (!expr)
77
+ return false;
78
+ if (expr.kind === "raw" && typeof expr.value === "string") {
79
+ return /^(true|1)$/.test(expr.value.trim());
80
+ }
81
+ if (expr.kind === "boolean" && expr.value === true)
82
+ return true;
83
+ if (expr.kind === "number" && expr.value !== 0)
84
+ return true;
85
+ return false;
86
+ }
@@ -0,0 +1,9 @@
1
+ import type { ProgramIR, Diagnostic } from "../../api/index.js";
2
+ import type { SafetyTransformContext } from "../../safety-hook.js";
3
+ /** B1: No recursion (ISO 26262 Part 6, Table 8 — ASIL B+).
4
+ *
5
+ * Only checks functions decorated with @asilB or higher.
6
+ * QM/ASIL A functions are skipped.
7
+ *
8
+ * Builds a function call graph and runs DFS cycle detection. */
9
+ export declare function checkRecursion(program: ProgramIR, _ctx: SafetyTransformContext): Diagnostic[];
@@ -0,0 +1,134 @@
1
+ import { asilLevel, ASIL_THRESHOLDS } from "./asil.js";
2
+ /** B1: No recursion (ISO 26262 Part 6, Table 8 — ASIL B+).
3
+ *
4
+ * Only checks functions decorated with @asilB or higher.
5
+ * QM/ASIL A functions are skipped.
6
+ *
7
+ * Builds a function call graph and runs DFS cycle detection. */
8
+ export function checkRecursion(program, _ctx) {
9
+ const diags = [];
10
+ // Build function set (only ASIL-annotated functions are checked)
11
+ const asilFnNames = new Set();
12
+ for (const fn of program.functions) {
13
+ if (asilLevel(fn.decorators) >= ASIL_THRESHOLDS.recursion) {
14
+ asilFnNames.add(fn.originalName);
15
+ }
16
+ }
17
+ for (const cls of program.classes) {
18
+ for (const method of cls.methods) {
19
+ if (method.name && asilLevel(method.decorators) >= ASIL_THRESHOLDS.recursion) {
20
+ asilFnNames.add(method.name);
21
+ }
22
+ }
23
+ }
24
+ if (asilFnNames.size === 0)
25
+ return diags;
26
+ // Build call graph (all functions, not just ASIL — callees matter)
27
+ const callGraph = buildFunctionCallGraph(program);
28
+ // DFS cycle detection — only report cycles involving ASIL functions
29
+ const WHITE = 0, GRAY = 1, BLACK = 2;
30
+ const color = new Map();
31
+ for (const name of callGraph.keys()) {
32
+ color.set(name, WHITE);
33
+ }
34
+ for (const entry of callGraph.keys()) {
35
+ if (color.get(entry) === WHITE) {
36
+ const path = [];
37
+ dfs(entry, callGraph, color, path, (cycle) => {
38
+ // Only report if any function in the cycle is ASIL-annotated
39
+ if (cycle.some(name => asilFnNames.has(name))) {
40
+ diags.push({
41
+ severity: "error",
42
+ code: "ISO26262_B1_RECURSION",
43
+ message: `Recursion detected: ${cycle.join(" → ")}`,
44
+ hint: "ASIL B+ requires bounded call depth. Refactor to an iterative loop or use a fixed-size memoization table.",
45
+ });
46
+ }
47
+ });
48
+ }
49
+ }
50
+ return diags;
51
+ }
52
+ function buildFunctionCallGraph(program) {
53
+ const graph = new Map();
54
+ const allFnNames = new Set();
55
+ for (const fn of program.functions) {
56
+ allFnNames.add(fn.originalName);
57
+ }
58
+ for (const cls of program.classes) {
59
+ for (const method of cls.methods) {
60
+ if (method.name)
61
+ allFnNames.add(method.name);
62
+ }
63
+ }
64
+ for (const fn of program.functions) {
65
+ const name = fn.originalName;
66
+ const deps = new Set();
67
+ for (const stmt of fn.statements) {
68
+ collectCallTargets(stmt, allFnNames, deps);
69
+ }
70
+ graph.set(name, deps);
71
+ }
72
+ for (const cls of program.classes) {
73
+ for (const method of cls.methods) {
74
+ if (!method.name)
75
+ continue;
76
+ const deps = new Set();
77
+ for (const stmt of method.statements) {
78
+ collectCallTargets(stmt, allFnNames, deps);
79
+ }
80
+ const existing = graph.get(method.name);
81
+ if (existing) {
82
+ for (const d of deps)
83
+ existing.add(d);
84
+ }
85
+ else {
86
+ graph.set(method.name, deps);
87
+ }
88
+ }
89
+ }
90
+ return graph;
91
+ }
92
+ function collectCallTargets(stmt, allFnNames, deps) {
93
+ if (!stmt || typeof stmt !== "object")
94
+ return;
95
+ if (stmt.kind === "call" && typeof stmt.callee === "string" && allFnNames.has(stmt.callee)) {
96
+ deps.add(stmt.callee);
97
+ }
98
+ if (stmt.kind === "method-call" && typeof stmt.callee === "string" && allFnNames.has(stmt.callee)) {
99
+ deps.add(stmt.callee);
100
+ }
101
+ for (const key of Object.keys(stmt)) {
102
+ const val = stmt[key];
103
+ if (Array.isArray(val)) {
104
+ for (const item of val) {
105
+ if (item && typeof item === "object" && typeof item.kind === "string") {
106
+ collectCallTargets(item, allFnNames, deps);
107
+ }
108
+ }
109
+ }
110
+ else if (val && typeof val === "object" && typeof val.kind === "string") {
111
+ collectCallTargets(val, allFnNames, deps);
112
+ }
113
+ }
114
+ }
115
+ function dfs(node, graph, color, path, onCycle) {
116
+ color.set(node, 1); // GRAY
117
+ path.push(node);
118
+ const neighbors = graph.get(node);
119
+ if (neighbors) {
120
+ for (const next of neighbors) {
121
+ const nextColor = color.get(next);
122
+ if (nextColor === 1) {
123
+ const cycleStart = path.indexOf(next);
124
+ const cycle = path.slice(cycleStart).concat(next);
125
+ onCycle(cycle);
126
+ }
127
+ else if (nextColor === 0) {
128
+ dfs(next, graph, color, path, onCycle);
129
+ }
130
+ }
131
+ }
132
+ path.pop();
133
+ color.set(node, 2); // BLACK
134
+ }
@@ -0,0 +1,25 @@
1
+ import type { SafetyMetadata } from "../../safety-hook.js";
2
+ /** Sidecar JSON schema for the safety metadata artifact. */
3
+ export interface SafetySidecar {
4
+ schemaVersion: string;
5
+ standard: string;
6
+ tool: string;
7
+ toolVersion: string;
8
+ generatedAt: string;
9
+ safetyFunctions: Array<{
10
+ name: string;
11
+ asilLevel: string;
12
+ source?: {
13
+ tsFile: string;
14
+ tsLine: number;
15
+ };
16
+ mechanisms: string[];
17
+ rules: Record<string, "pass" | {
18
+ severity: string;
19
+ message: string;
20
+ }>;
21
+ }>;
22
+ }
23
+ /** Render safety metadata as a sidecar JSON string.
24
+ * Pure function — does not touch the filesystem. */
25
+ export declare function renderSafetySidecar(metadata: SafetyMetadata, toolVersion: string): string;
@@ -0,0 +1,13 @@
1
+ /** Render safety metadata as a sidecar JSON string.
2
+ * Pure function — does not touch the filesystem. */
3
+ export function renderSafetySidecar(metadata, toolVersion) {
4
+ const payload = {
5
+ schemaVersion: "1.0.0",
6
+ standard: "ISO 26262 Part 6",
7
+ tool: "cuttlefish (safety)",
8
+ toolVersion,
9
+ generatedAt: new Date().toISOString(),
10
+ safetyFunctions: metadata.functions,
11
+ };
12
+ return JSON.stringify(payload, null, 2);
13
+ }
@@ -0,0 +1,3 @@
1
+ import type { ProgramIR } from "../../api/index.js";
2
+ import type { SafetyTransformContext } from "../../safety-hook.js";
3
+ export declare function pinModeInterceptPass(program: ProgramIR, ctx: SafetyTransformContext): ProgramIR;
@@ -0,0 +1,60 @@
1
+ // The post-build IR transform. Walks the built ProgramIR and injects a
2
+ // safety.record_pin_mode companion statement after every pin-configuring
3
+ // gpio op — gpio.configure (the thin GPIO constructor / set re-configure)
4
+ // and gpio.read_cfg (the fused guarded-configure + read). This makes the
5
+ // runtime mode table authoritative regardless of how the user configured
6
+ // the pin.
7
+ //
8
+ // v3: the thin HAL replaced gpio.set_mode's Arduino mode strings with
9
+ // gpio.configure's flag tokens ("GPIO.OUTPUT | GPIO.PULL_UP"). The flags
10
+ // field carries token text at IR time; configure-shaped flag sets map onto
11
+ // the TrackedMode enum, anything else (a runtime expression, an open-drain
12
+ // combo with no tracked equivalent) records Unknown so the voter still sees
13
+ // a fresh table entry rather than a stale mode.
14
+ //
15
+ // Produces a NEW ProgramIR via mapProgramStatements; does not mutate the input.
16
+ // See docs/superpowers/specs/2026-07-27-safety-package-part-a-design.md.
17
+ import { mapProgramStatements } from "../../api/index.js";
18
+ import { mapFlagTokens } from "../hal/ops.js";
19
+ /** The gpio ops that (re)configure a pin's direction/flags. */
20
+ const PIN_MODE_OPS = new Set(["gpio.configure", "gpio.read_cfg"]);
21
+ export function pinModeInterceptPass(program, ctx) {
22
+ if (!ctx.safetyInUse)
23
+ return program;
24
+ return mapProgramStatements(program, (stmt) => {
25
+ const companions = collectPinModeCompanions(stmt);
26
+ return companions.length === 0 ? [stmt] : [stmt, ...companions];
27
+ });
28
+ }
29
+ /** If stmt is a hal-op statement carrying a pin-configuring gpio op, return a
30
+ * singleton array containing the companion record_pin_mode statement;
31
+ * else []. */
32
+ function collectPinModeCompanions(stmt) {
33
+ if (stmt.kind !== "hal-op")
34
+ return [];
35
+ const op = stmt.operation;
36
+ if (typeof op.operation !== "string")
37
+ return [];
38
+ if (!PIN_MODE_OPS.has(op.operation))
39
+ return [];
40
+ // gpio.configure / gpio.read_cfg carry { pin: number; flags: string }
41
+ // where flags is flag-token text ("GPIO.INPUT | GPIO.PULL_UP") or an
42
+ // unresolved runtime expression.
43
+ const pinModeOp = op;
44
+ if (typeof pinModeOp.pin !== "number" || typeof pinModeOp.flags !== "string")
45
+ return [];
46
+ return [makeCompanion(pinModeOp.pin, mapFlagTokens(pinModeOp.flags))];
47
+ }
48
+ function makeCompanion(pin, mode) {
49
+ const companionOp = {
50
+ operation: "safety.record_pin_mode",
51
+ pin,
52
+ mode,
53
+ };
54
+ return {
55
+ kind: "hal-op",
56
+ operation: companionOp,
57
+ returns_value: false,
58
+ sourceSpan: { startLine: 0, startColumn: 0, filePath: "<safety-companion>" },
59
+ };
60
+ }
@@ -0,0 +1,5 @@
1
+ import type { RuntimePolyfillIR } from "../../api/index.js";
2
+ /** The pin-mode tracking table + accessors. Tree-shaken out unless
3
+ * __tc_safety_record_pin_mode appears in the program (registered in
4
+ * POLYFILL_HELPER_MAP). */
5
+ export declare function modeTablePolyfill(): RuntimePolyfillIR;
@@ -0,0 +1,55 @@
1
+ import { emitResultStructs } from "./result-struct.js";
2
+ /** The pin-mode tracking table + accessors. Tree-shaken out unless
3
+ * __tc_safety_record_pin_mode appears in the program (registered in
4
+ * POLYFILL_HELPER_MAP). */
5
+ export function modeTablePolyfill() {
6
+ return {
7
+ kind: "polyfill",
8
+ id: "safety_mode_table",
9
+ domain: "embedded",
10
+ requiredIncludes: [],
11
+ forwardDeclarations: [],
12
+ helperStructs: [],
13
+ helperFunctions: [
14
+ emitResultStructs(),
15
+ `
16
+ namespace __tc_safety {
17
+ constexpr uint32_t TC_SAFETY_PIN_TABLE_SIZE = 255U;
18
+
19
+ enum class TrackedMode : uint32_t {
20
+ Unknown = 0x00000000U,
21
+ Input = 0x5A5A5A5AU,
22
+ Output = 0xA5A5A5A5U,
23
+ InputPullup = 0x3C3C3C3CU,
24
+ InputPulldown = 0xC3C3C3C3U,
25
+ };
26
+
27
+ // Raw array: unavoidable for O(1) pin-indexed lookup in an embedded shim.
28
+ // Zero-initialized namespace-scope static storage (Unknown = 0U) satisfies
29
+ // M3-2-1 (const-init) and M3-2-4 (trivial static init); no knownPatterns
30
+ // deviation needed (verified in Part A v1).
31
+ TrackedMode g_pin_mode_table[TC_SAFETY_PIN_TABLE_SIZE];
32
+
33
+ inline void record_pin_mode(uint32_t pin, uint32_t mode) {
34
+ if (pin < TC_SAFETY_PIN_TABLE_SIZE) {
35
+ g_pin_mode_table[pin] = static_cast<TrackedMode>(mode);
36
+ }
37
+ }
38
+
39
+ inline TrackedMode get_pin_mode(uint32_t pin) {
40
+ if (pin < TC_SAFETY_PIN_TABLE_SIZE) {
41
+ return g_pin_mode_table[pin];
42
+ }
43
+ return TrackedMode::Unknown;
44
+ }
45
+ } // namespace __tc_safety
46
+
47
+ inline void __tc_safety_record_pin_mode(uint32_t pin, uint32_t mode) {
48
+ __tc_safety::record_pin_mode(pin, mode);
49
+ }
50
+ `.trim(),
51
+ ],
52
+ shimMacros: [],
53
+ dependencies: [],
54
+ };
55
+ }
@@ -0,0 +1,6 @@
1
+ import type { RuntimePolyfillIR } from "../../api/index.js";
2
+ /** All safety runtime polyfills, in dependency order (mode table before voter
3
+ * and write-verify, safe variable and safe int are standalone). Returned by
4
+ * the safety hook's buildPolyfills(); called from buildEmitterContext so each
5
+ * polyfill participates in usage-based tree-shaking via filterPolyfillHelpers. */
6
+ export declare function buildSafetyPolyfills(): RuntimePolyfillIR[];
@@ -0,0 +1,12 @@
1
+ import { modeTablePolyfill } from "./mode-table.js";
2
+ import { voterPolyfill } from "./vote.js";
3
+ import { safeVariablePolyfill } from "./safe-variable.js";
4
+ import { safeIntPolyfill } from "./safe-int.js";
5
+ import { writeVerifyPolyfill } from "./write-verify.js";
6
+ /** All safety runtime polyfills, in dependency order (mode table before voter
7
+ * and write-verify, safe variable and safe int are standalone). Returned by
8
+ * the safety hook's buildPolyfills(); called from buildEmitterContext so each
9
+ * polyfill participates in usage-based tree-shaking via filterPolyfillHelpers. */
10
+ export function buildSafetyPolyfills() {
11
+ return [modeTablePolyfill(), voterPolyfill(), writeVerifyPolyfill(), safeVariablePolyfill(), safeIntPolyfill()];
12
+ }
@@ -0,0 +1,11 @@
1
+ /** Returns the C++ enum + struct definitions for the safety runtime.
2
+ *
3
+ * These are emitted by the polyfill as the SINGLE canonical source.
4
+ * @typecad/safety is skipped by the graph-builder (like @typecad/ui), so
5
+ * its TypeScript source is never parsed into the user program's IR — the
6
+ * type-decl emitter never sees the enums. The polyfill provides the
7
+ * definitions that both the voter and the user's switch statements
8
+ * reference. Works in both single-file and split-file emit modes because
9
+ * the polyfill is inlined into the entry file's source, visible to all
10
+ * functions in that translation unit. */
11
+ export declare function emitResultStructs(): string;
@@ -0,0 +1,98 @@
1
+ // C++ source strings for the safety result struct + fault enums. Emitted by
2
+ // the mode-table polyfill (Task 9) BEFORE the voter, so the voter can return
3
+ // a SafeReadResult.
4
+ //
5
+ // AUTOSAR C++14 compliance:
6
+ // - enum class (A7-2-1)
7
+ // - fixed-width types (A3-9-1): uint8_t
8
+ // - no C-style casts (M5-0-7), no malloc (A18-5-10)
9
+ // - the raw pin table in mode-table.ts is zero-initialized namespace-scope
10
+ // static storage, satisfying M3-2-1/M3-2-4. No knownPatterns deviation is
11
+ // expected; if a rule fires anyway, add one (see
12
+ // docs/superpowers/plans/2026-07-27-safety-package-part-a.md Task 17).
13
+ /** Returns the C++ enum + struct definitions for the safety runtime.
14
+ *
15
+ * These are emitted by the polyfill as the SINGLE canonical source.
16
+ * @typecad/safety is skipped by the graph-builder (like @typecad/ui), so
17
+ * its TypeScript source is never parsed into the user program's IR — the
18
+ * type-decl emitter never sees the enums. The polyfill provides the
19
+ * definitions that both the voter and the user's switch statements
20
+ * reference. Works in both single-file and split-file emit modes because
21
+ * the polyfill is inlined into the entry file's source, visible to all
22
+ * functions in that translation unit. */
23
+ export function emitResultStructs() {
24
+ return `
25
+ enum class SafetyStatus : uint32_t {
26
+ Ok = 0x5A5A5A5AU,
27
+ Fault = 0xA5A5A5A5U,
28
+ };
29
+
30
+ enum class SafetyFaultCategory : uint32_t {
31
+ Ok = 0x3C3C3C3CU,
32
+ Signal = 0x5A5A5A5AU,
33
+ Integrity = 0xA5A5A5A5U,
34
+ Timing = 0xC3C3C3C3U,
35
+ System = 0x55AA55AAU,
36
+ Configuration = 0xAA55AA55U,
37
+ };
38
+
39
+ enum class SafetyFaultCode : uint32_t {
40
+ Ok = 0x3C3C3C3CU,
41
+ VoteDisagreement = 0x5A5A5A5AU,
42
+ StuckHigh = 0xA5A5A5A5U,
43
+ StuckLow = 0xC3C3C3C3U,
44
+ PinModeMismatch = 0x55AA55AAU,
45
+ PinModeUnknown = 0xAA55AA55U,
46
+ WriteMismatch = 0x3C5AA5C3U,
47
+ };
48
+
49
+ struct SafeReadResult {
50
+ SafetyStatus status;
51
+ uint32_t value;
52
+ SafetyFaultCategory category;
53
+ SafetyFaultCode code;
54
+
55
+ // Chainable result handlers. Each calls the handler at runtime when its
56
+ // condition matches, then returns *this so chains compose and the result
57
+ // stays inspectable afterward. Template F&& accepts the inline C++ lambdas
58
+ // cuttlefish renders — no std::function, no heap allocation.
59
+ template <typename F>
60
+ SafeReadResult& ok(F&& handler) noexcept {
61
+ if (status == SafetyStatus::Ok) { handler(*this); }
62
+ return *this;
63
+ }
64
+ template <typename F>
65
+ SafeReadResult& fail(F&& handler) noexcept {
66
+ if (status != SafetyStatus::Ok) { handler(*this); }
67
+ return *this;
68
+ }
69
+ // fault is an alias of fail (no std::forward — avoids the <utility> header
70
+ // AVR lacks; the handler is called once immediately, so perfect forwarding
71
+ // is not load-bearing).
72
+ template <typename F>
73
+ SafeReadResult& fault(F&& handler) noexcept { return fail(handler); }
74
+ template <typename F>
75
+ SafeReadResult& always(F&& handler) noexcept { handler(*this); return *this; }
76
+ };
77
+
78
+ struct SafeWriteResult {
79
+ SafetyStatus status;
80
+ SafetyFaultCode code;
81
+
82
+ template <typename F>
83
+ SafeWriteResult& ok(F&& handler) noexcept {
84
+ if (status == SafetyStatus::Ok) { handler(*this); }
85
+ return *this;
86
+ }
87
+ template <typename F>
88
+ SafeWriteResult& fail(F&& handler) noexcept {
89
+ if (status != SafetyStatus::Ok) { handler(*this); }
90
+ return *this;
91
+ }
92
+ template <typename F>
93
+ SafeWriteResult& fault(F&& handler) noexcept { return fail(handler); }
94
+ template <typename F>
95
+ SafeWriteResult& always(F&& handler) noexcept { handler(*this); return *this; }
96
+ };
97
+ `.trim();
98
+ }