@typecad/cuttlefish 0.1.0-alpha.2 → 1.0.0-alpha.11

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 (224) hide show
  1. package/README.md +10 -10
  2. package/dist/api/board-types.d.ts +1 -1
  3. package/dist/api/config.d.ts +38 -2
  4. package/dist/api/index.d.ts +1 -1
  5. package/dist/api/schema/types.d.ts +15 -0
  6. package/dist/api/shared/async-runtime-static.d.ts +12 -1
  7. package/dist/api/shared/async-runtime-static.js +69 -24
  8. package/dist/api/shared/async-symbol-detector.d.ts +11 -0
  9. package/dist/api/shared/async-symbol-detector.js +140 -0
  10. package/dist/api/shared/async-types.d.ts +24 -0
  11. package/dist/api/shared/coop-scheduler.d.ts +60 -0
  12. package/dist/api/shared/coop-scheduler.js +149 -0
  13. package/dist/api/shared/display-adapter.d.ts +4 -3
  14. package/dist/api/shared/display-adapter.js +15 -118
  15. package/dist/api/shared/display-adapters/sdl.js +38 -31
  16. package/dist/api/shared/display-profile.d.ts +47 -25
  17. package/dist/api/shared/display-profile.js +31 -164
  18. package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
  19. package/dist/api/shared/framework-manifest-registry.js +27 -0
  20. package/dist/api/shared/framework-manifest.d.ts +595 -0
  21. package/dist/api/shared/framework-manifest.js +193 -0
  22. package/dist/api/shared/glcdfont.d.ts +12 -0
  23. package/dist/api/shared/glcdfont.js +124 -0
  24. package/dist/api/shared/graphics-strategy.d.ts +37 -0
  25. package/dist/api/shared/hal-op-ir.d.ts +764 -1
  26. package/dist/api/shared/hal-op-ir.js +129 -1
  27. package/dist/api/shared/index.d.ts +22 -2
  28. package/dist/api/shared/index.js +26 -1
  29. package/dist/api/shared/ir-core.d.ts +4 -0
  30. package/dist/api/shared/ir-declarations.d.ts +6 -0
  31. package/dist/api/shared/native-display-op-resolver.d.ts +10 -0
  32. package/dist/api/shared/native-display-op-resolver.js +64 -0
  33. package/dist/api/shared/platform-strategy.d.ts +119 -2
  34. package/dist/api/shared/polyfill-helper-registry.js +18 -1
  35. package/dist/api/shared/promise-runtime.d.ts +1 -1
  36. package/dist/api/shared/promise-runtime.js +95 -13
  37. package/dist/api/shared/spdx-licenses.d.ts +139 -0
  38. package/dist/api/shared/spdx-licenses.js +385 -0
  39. package/dist/api/shared/toolchain-types.d.ts +17 -0
  40. package/dist/api/shared/types.d.ts +8 -0
  41. package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
  42. package/dist/api/shared/validate-framework-manifest.js +494 -0
  43. package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
  44. package/dist/api/shared/worker-runtime-polyfill.js +34 -0
  45. package/dist/api/shared/worker-runtime.d.ts +69 -0
  46. package/dist/api/shared/worker-runtime.js +163 -0
  47. package/dist/cli-utils.d.ts +1 -0
  48. package/dist/cli-utils.js +3 -1
  49. package/dist/cli.js +249 -32
  50. package/dist/config-loader.d.ts +7 -2
  51. package/dist/config-loader.js +23 -3
  52. package/dist/config-schema.d.ts +112 -70
  53. package/dist/config-schema.js +14 -0
  54. package/dist/contract/board-generator.d.ts +17 -0
  55. package/dist/contract/board-generator.js +57 -0
  56. package/dist/contract/contract-parser.d.ts +217 -0
  57. package/dist/contract/contract-parser.js +224 -0
  58. package/dist/contract/index.d.ts +22 -0
  59. package/dist/contract/index.js +68 -0
  60. package/dist/create/board-codegen.js +4 -4
  61. package/dist/create/board-generators.js +4 -5
  62. package/dist/create/board-spec.d.ts +72 -75
  63. package/dist/create/board-spec.js +0 -1
  64. package/dist/create/framework-catalog.d.ts +64 -0
  65. package/dist/create/framework-catalog.js +136 -0
  66. package/dist/create/index.d.ts +5 -1
  67. package/dist/create/index.js +3 -1
  68. package/dist/create/init-scaffold.d.ts +12 -3
  69. package/dist/create/init-scaffold.js +49 -14
  70. package/dist/create/init-templates.d.ts +4 -0
  71. package/dist/create/init-templates.js +271 -17
  72. package/dist/create/init-wizard.js +41 -27
  73. package/dist/create/install-deps.d.ts +32 -0
  74. package/dist/create/install-deps.js +46 -0
  75. package/dist/debug/preprocessor.js +178 -39
  76. package/dist/debug/types.d.ts +23 -0
  77. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  78. package/dist/diagnostics/mermaid-builder.js +34 -24
  79. package/dist/emit/compliance/arxml-writer.d.ts +11 -0
  80. package/dist/emit/compliance/arxml-writer.js +34 -0
  81. package/dist/emit/compliance/compliance-context.d.ts +57 -0
  82. package/dist/emit/compliance/compliance-context.js +113 -0
  83. package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
  84. package/dist/emit/compliance/deviation-ledger.js +47 -0
  85. package/dist/emit/compliance/deviation-writer.d.ts +30 -0
  86. package/dist/emit/compliance/deviation-writer.js +37 -0
  87. package/dist/emit/compliance/index.d.ts +7 -0
  88. package/dist/emit/compliance/index.js +6 -0
  89. package/dist/emit/compliance/rule-engine.d.ts +13 -0
  90. package/dist/emit/compliance/rule-engine.js +101 -0
  91. package/dist/emit/compliance/rules.d.ts +16 -0
  92. package/dist/emit/compliance/rules.js +191 -0
  93. package/dist/emit/compliance/types.d.ts +59 -0
  94. package/dist/emit/compliance/types.js +8 -0
  95. package/dist/emit/cpp-emitter.js +4 -3
  96. package/dist/emit/emitters/class-emitter.js +6 -1
  97. package/dist/emit/emitters/emitter-context.d.ts +28 -1
  98. package/dist/emit/emitters/function-emitter-impl.js +153 -53
  99. package/dist/emit/emitters/line-appender.js +19 -0
  100. package/dist/emit/emitters/line-marker.d.ts +38 -0
  101. package/dist/emit/emitters/line-marker.js +39 -0
  102. package/dist/emit/emitters/output-finalizer.d.ts +6 -0
  103. package/dist/emit/emitters/output-finalizer.js +101 -12
  104. package/dist/emit/emitters/setup.d.ts +17 -0
  105. package/dist/emit/emitters/setup.js +372 -30
  106. package/dist/emit/emitters/top-level-prep.js +8 -0
  107. package/dist/emit/emitters/type-decl-emitter.js +28 -3
  108. package/dist/emit/emitters/ui-emitter.js +33 -9
  109. package/dist/emit/expression-renderer.d.ts +1 -1
  110. package/dist/emit/expression-renderer.js +91 -6
  111. package/dist/emit/route-hal-op.js +18 -5
  112. package/dist/emit/snprintf-helpers.js +15 -4
  113. package/dist/emit/statement-renderer.d.ts +10 -0
  114. package/dist/emit/statement-renderer.js +34 -5
  115. package/dist/emit/utils/async-state-machine.js +221 -125
  116. package/dist/emit/utils/hal-op-cpp-type.d.ts +6 -0
  117. package/dist/emit/utils/hal-op-cpp-type.js +40 -0
  118. package/dist/framework-package.js +2 -0
  119. package/dist/framework-registry.d.ts +17 -0
  120. package/dist/install/framework-catalog.d.ts +53 -0
  121. package/dist/install/framework-catalog.js +107 -0
  122. package/dist/install/handle-install.d.ts +35 -0
  123. package/dist/install/handle-install.js +177 -0
  124. package/dist/install/index.d.ts +4 -0
  125. package/dist/install/index.js +3 -0
  126. package/dist/ir/adc-range-validation.js +40 -26
  127. package/dist/ir/build-ir-state.d.ts +1 -0
  128. package/dist/ir/build-ir-state.js +14 -0
  129. package/dist/ir/build-ir.js +9 -5
  130. package/dist/ir/call-graph.js +16 -0
  131. package/dist/ir/expression-to-ir.js +207 -6
  132. package/dist/ir/feature-registry.js +7 -25
  133. package/dist/ir/function-builder.js +22 -0
  134. package/dist/ir/hal/hal-emitter.d.ts +5 -2
  135. package/dist/ir/hal/hal-emitter.js +50 -18
  136. package/dist/ir/hal/hal-parser.d.ts +6 -0
  137. package/dist/ir/hal/hal-parser.js +81 -0
  138. package/dist/ir/hal/hal-plugins.js +733 -1
  139. package/dist/ir/identifier-collector.js +35 -0
  140. package/dist/ir/interrupt-analysis.d.ts +5 -1
  141. package/dist/ir/interrupt-analysis.js +16 -19
  142. package/dist/ir/memory-budget-validation.js +1 -0
  143. package/dist/ir/network-validation.d.ts +4 -0
  144. package/dist/ir/network-validation.js +184 -0
  145. package/dist/ir/ownership-analysis.js +39 -1
  146. package/dist/ir/peripheral-ownership.js +5 -0
  147. package/dist/ir/peripheral-validation.d.ts +1 -1
  148. package/dist/ir/peripheral-validation.js +6 -3
  149. package/dist/ir/pin-alias-conflict.d.ts +1 -1
  150. package/dist/ir/pin-alias-conflict.js +2 -1
  151. package/dist/ir/pin-capability-validation.js +71 -32
  152. package/dist/ir/pin-mode-validation.d.ts +2 -2
  153. package/dist/ir/pin-mode-validation.js +54 -18
  154. package/dist/ir/pin-safety.d.ts +1 -1
  155. package/dist/ir/pin-safety.js +2 -1
  156. package/dist/ir/program-analysis.d.ts +81 -0
  157. package/dist/ir/program-analysis.js +429 -0
  158. package/dist/ir/pulldown-validation.d.ts +1 -1
  159. package/dist/ir/pulldown-validation.js +2 -1
  160. package/dist/ir/pwm-timer-sharing.d.ts +1 -1
  161. package/dist/ir/pwm-timer-sharing.js +2 -1
  162. package/dist/ir/render-expr.js +11 -0
  163. package/dist/ir/resource-analysis.js +2 -0
  164. package/dist/ir/timer0-pwm-timing-conflict.d.ts +1 -1
  165. package/dist/ir/timer0-pwm-timing-conflict.js +2 -1
  166. package/dist/ir/timing-validation.d.ts +6 -1
  167. package/dist/ir/timing-validation.js +61 -13
  168. package/dist/ir/transformers/call-statement.js +105 -0
  169. package/dist/ir/transformers/expressions.js +62 -0
  170. package/dist/ir/transformers/hal-call-resolver.js +21 -0
  171. package/dist/ir/transformers/hal-emit-helpers.js +1 -1
  172. package/dist/ir/transformers/namespace-methods.js +17 -12
  173. package/dist/ir/transformers/ui-reactive.js +2 -2
  174. package/dist/ir/transformers/variables.js +137 -19
  175. package/dist/ir/try-catch-validation.js +2 -0
  176. package/dist/ir/type-resolution.js +20 -2
  177. package/dist/ir/ui-element-auto-wire.js +7 -5
  178. package/dist/ir/unit-suspicion-validation.js +9 -7
  179. package/dist/ir/utils/map-statements.d.ts +4 -0
  180. package/dist/ir/utils/map-statements.js +79 -0
  181. package/dist/ir/validation-orchestrator.js +15 -8
  182. package/dist/ir/worker-analysis.d.ts +10 -0
  183. package/dist/ir/worker-analysis.js +261 -0
  184. package/dist/libdef/c-to-decl.d.ts +27 -0
  185. package/dist/libdef/c-to-decl.js +397 -0
  186. package/dist/libdef/component-decls.d.ts +2 -0
  187. package/dist/libdef/component-decls.js +6 -0
  188. package/dist/libdef/component-discovery.d.ts +43 -0
  189. package/dist/libdef/component-discovery.js +83 -0
  190. package/dist/libdef/cpp-to-decl.d.ts +9 -0
  191. package/dist/libdef/cpp-to-decl.js +72 -0
  192. package/dist/libdef/registry.js +5 -2
  193. package/dist/lint-cache.d.ts +59 -0
  194. package/dist/lint-cache.js +257 -0
  195. package/dist/orchestrator/graph-builder.js +14 -9
  196. package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
  197. package/dist/platform/coop-scheduler-runtime.js +41 -0
  198. package/dist/platform/generic-strategy.d.ts +15 -3
  199. package/dist/platform/generic-strategy.js +49 -4
  200. package/dist/safety/safety-bridge.d.ts +11 -0
  201. package/dist/safety/safety-bridge.js +48 -0
  202. package/dist/safety/sidecar-bridge.d.ts +5 -0
  203. package/dist/safety/sidecar-bridge.js +22 -0
  204. package/dist/safety-hook.d.ts +79 -0
  205. package/dist/safety-hook.js +35 -0
  206. package/dist/testing.d.ts +8 -2
  207. package/dist/testing.js +9 -2
  208. package/dist/transpile.d.ts +3 -0
  209. package/dist/transpile.js +191 -49
  210. package/dist/types.d.ts +22 -3
  211. package/dist/ui-hook.d.ts +17 -3
  212. package/dist/utils/cli.js +123 -2
  213. package/dist/utils/fs.d.ts +13 -0
  214. package/dist/utils/fs.js +50 -0
  215. package/dist/utils/ui.js +3 -1
  216. package/package.json +23 -3
  217. package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
  218. package/dist/api/shared/display-adapters/eink-mono.js +0 -53
  219. package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
  220. package/dist/api/shared/display-adapters/ssd1309.js +0 -136
  221. package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
  222. package/dist/api/shared/display-adapters/st7796.js +0 -132
  223. package/dist/ir/heap-array-validation.d.ts +0 -24
  224. package/dist/ir/heap-array-validation.js +0 -29
@@ -1,13 +1,16 @@
1
1
  import path from "node:path";
2
2
  import { filterPolyfillHelpers, isStringEnum } from "../../api/shared/index.js";
3
+ import { hasSafetyHook, requireSafetyHook } from "../../safety-hook.js";
3
4
  import { analyzeProgram } from "../../ir/program-analysis.js";
4
5
  import { collectStatementIdentifiers } from "../../ir/identifier-collector.js";
5
6
  import { ensureDir } from "../../utils/fs.js";
6
7
  import { resolveImport } from "../../libdef/registry.js";
7
8
  import { emitPolyfillBoilerplate } from "../native-helpers-emitter.js";
8
9
  import { resolveStrategy } from "../../platform/registry.js";
10
+ import { buildCoopSchedulerPolyfill } from "../../platform/coop-scheduler-runtime.js";
9
11
  import { getLoadedFramework } from "../../framework-registry.js";
10
12
  import { entryHasUI } from "../../ui-hook.js";
13
+ import { ComplianceContext } from "../compliance/compliance-context.js";
11
14
  import { createEmissionScopeState, statementNeedsSnprintf, } from "../snprintf-helpers.js";
12
15
  import { ExpressionRenderer } from "../expression-renderer.js";
13
16
  import { StatementRenderer } from "../statement-renderer.js";
@@ -32,12 +35,183 @@ function filterShimBlock(lines, startMarker, endMarker) {
32
35
  return filtered;
33
36
  }
34
37
  /**
35
- * Extract a `#ifndef MACRO ... #endif` include-guard block from a shim line
36
- * list. Used to emit just the macro definition (e.g. CUTTLEFISH_UNDEFINED) in
37
- * non-entry files of a split compilation, where the full helper shim belongs
38
- * to the entry file but the macro token is still referenced here.
39
- * Returns an empty array if no matching guard is found.
38
+ * Detect whether a program uses the watchdog timer. The HAL resolver lowers
39
+ * `WDT.enable/reset/disable` calls to structured `wdt.*` hal-ops, which the
40
+ * setup emitter then renders as bare `wdt_enable/wdt_reset/wdt_disable` calls
41
+ * (or constant-folded `wdt_enable(WDTO_*)` macros). All of those require
42
+ * `<avr/wdt.h>`, so the include is gated on this check instead of being forced
43
+ * into every AVR program.
44
+ *
45
+ * `programAnalysis.usesWDT` is NOT used here: it scans `WDT.`-prefixed
46
+ * callees and raw-code hal-ops, but the structured `wdt.*` ops carry a typed
47
+ * operation name with no raw code, so the analysis misses them.
48
+ *
49
+ * The recursion mirrors `collectStatementIdentifiers` (identifier-collector.ts)
50
+ * — the canonical IR walker — so every compound statement shape is covered.
40
51
  */
52
+ function programUsesWdt(program) {
53
+ const visitStatement = (stmt) => {
54
+ if (stmt.kind === "hal-op") {
55
+ const opName = stmt.operation?.operation;
56
+ if (typeof opName === "string" && opName.startsWith("wdt."))
57
+ return true;
58
+ }
59
+ switch (stmt.kind) {
60
+ case "block":
61
+ case "labeled":
62
+ return visit(stmt.body);
63
+ case "if":
64
+ return visit(stmt.thenBranch) || visit(stmt.elseBranch ?? []);
65
+ case "for":
66
+ return visit(stmt.body)
67
+ || (stmt.initializer ? visitStatement(stmt.initializer) : false);
68
+ case "while":
69
+ case "do_while":
70
+ case "for_of":
71
+ case "for_in":
72
+ return visit(stmt.body);
73
+ case "switch":
74
+ return stmt.cases.some((c) => visit(c.body ?? []));
75
+ case "try":
76
+ return visit(stmt.tryBlock) || visit(stmt.catchBlock ?? []) || visit(stmt.finallyBlock ?? []);
77
+ default:
78
+ return false;
79
+ }
80
+ };
81
+ const visit = (statements) => {
82
+ if (!statements)
83
+ return false;
84
+ for (const stmt of statements) {
85
+ if (visitStatement(stmt))
86
+ return true;
87
+ }
88
+ return false;
89
+ };
90
+ if (visit(program.topLevelStatements))
91
+ return true;
92
+ for (const fn of program.functions ?? []) {
93
+ if (visit(fn.statements))
94
+ return true;
95
+ }
96
+ for (const cls of program.classes ?? []) {
97
+ for (const m of cls.methods ?? [])
98
+ if (visit(m.statements))
99
+ return true;
100
+ for (const g of cls.getters ?? [])
101
+ if (visit(g.statements))
102
+ return true;
103
+ for (const s of cls.setters ?? [])
104
+ if (visit(s.statements))
105
+ return true;
106
+ if (cls.constructor && visit(cls.constructor.statements))
107
+ return true;
108
+ }
109
+ return false;
110
+ }
111
+ /**
112
+ * Detect whether a program uses @typecad/safety. The safety transform pass
113
+ * injects safety.* ops when in use; strategies consult this to decide
114
+ * whether to emit the __tc_gpio_read shim that the safety voter calls.
115
+ * Mirrors programUsesWdt() above (same inline walker pattern).
116
+ *
117
+ * `hal-expr` (the expression-position safety.read_safe lowering produced
118
+ * by `const r = safe.read(pin)`) lives in expression trees, not at statement
119
+ * level — but it's carried inside a var_decl statement whose initializer is
120
+ * the hal-expr, and the var_decl gets visited as a top-level statement. We
121
+ * detect both: statement-level `hal-op` operations, and any statement whose
122
+ * rendered initializer text mentions safety.* (a defensive catch for the
123
+ * hal-expr-in-initializer case). The latter is rarely needed because the
124
+ * voter's read_safe op is also captured at the call site, but kept for
125
+ * robustness.
126
+ */
127
+ export function programUsesSafety(program) {
128
+ // Defensive: some test fixtures and partial-program callers pass a minimal
129
+ // object (e.g. `{} as any`). Treat a missing iterable field as "no safety
130
+ // ops" rather than crashing — matches how the existing ArduinoStrategy path
131
+ // behaves when given a partial program.
132
+ if (!program)
133
+ return false;
134
+ const visitStatement = (stmt) => {
135
+ if (stmt.kind === "hal-op") {
136
+ const opName = stmt.operation?.operation;
137
+ if (typeof opName === "string" && opName.startsWith("safety."))
138
+ return true;
139
+ }
140
+ switch (stmt.kind) {
141
+ case "block":
142
+ case "labeled":
143
+ return visit(stmt.body);
144
+ case "if":
145
+ return visit(stmt.thenBranch) || visit(stmt.elseBranch ?? []);
146
+ case "for":
147
+ return visit(stmt.body)
148
+ || (stmt.initializer ? visitStatement(stmt.initializer) : false);
149
+ case "while":
150
+ case "do_while":
151
+ case "for_of":
152
+ case "for_in":
153
+ return visit(stmt.body);
154
+ case "switch":
155
+ return stmt.cases.some((c) => visit(c.body ?? []));
156
+ case "try":
157
+ return visit(stmt.tryBlock) || visit(stmt.catchBlock ?? []) || visit(stmt.finallyBlock ?? []);
158
+ default:
159
+ return false;
160
+ }
161
+ };
162
+ const visit = (statements) => {
163
+ if (!statements)
164
+ return false;
165
+ for (const stmt of statements) {
166
+ if (visitStatement(stmt))
167
+ return true;
168
+ }
169
+ return false;
170
+ };
171
+ if (visit(program.topLevelStatements))
172
+ return true;
173
+ for (const fn of program.functions ?? []) {
174
+ if (visit(fn.statements))
175
+ return true;
176
+ }
177
+ for (const cls of program.classes ?? []) {
178
+ for (const m of cls.methods ?? [])
179
+ if (visit(m.statements))
180
+ return true;
181
+ for (const g of cls.getters ?? [])
182
+ if (visit(g.statements))
183
+ return true;
184
+ for (const s of cls.setters ?? [])
185
+ if (visit(s.statements))
186
+ return true;
187
+ if (cls.constructor && visit(cls.constructor.statements))
188
+ return true;
189
+ }
190
+ // SafeInt<T> / SafeVariable<T> usage does not produce safety.* hal-ops
191
+ // (they are plain C++ template types), so the op-walk above misses them.
192
+ // Detect via the var_decl cppType: any declaration whose type starts with
193
+ // "SafeInt" or "SafeVariable" needs the corresponding polyfill to ship.
194
+ const usesSafeWrapperType = (statements) => {
195
+ if (!statements)
196
+ return false;
197
+ for (const stmt of statements) {
198
+ if (stmt.kind === "var_decl") {
199
+ const cppType = stmt.cppType;
200
+ if (typeof cppType === "string" && (cppType.startsWith("SafeInt") || cppType.startsWith("SafeVariable"))) {
201
+ return true;
202
+ }
203
+ }
204
+ }
205
+ return false;
206
+ };
207
+ if (usesSafeWrapperType(program.topLevelStatements))
208
+ return true;
209
+ for (const fn of program.functions ?? []) {
210
+ if (usesSafeWrapperType(fn.statements))
211
+ return true;
212
+ }
213
+ return false;
214
+ }
41
215
  function extractShimMacro(lines, macroName) {
42
216
  const ifndefIdx = lines.findIndex(l => {
43
217
  const trimmed = l.trim();
@@ -100,9 +274,29 @@ export function buildEmitterContext(program, options) {
100
274
  stringEnumNames.add(name);
101
275
  }
102
276
  }
277
+ // When @typecad/safety is active, register its enum names so the expression
278
+ // renderer uses :: (C++ enum-class scope resolution) instead of . (TS dot
279
+ // access) for member access like SafetyFaultCategory.Configuration. The
280
+ // graph-builder skips @typecad/safety (its source isn't parsed), so these
281
+ // enum names never enter program.enums — we register them manually here.
282
+ if (hasSafetyHook()) {
283
+ enumNames.add("SafetyFaultCategory");
284
+ enumNames.add("SafetyFaultCode");
285
+ enumNames.add("SafetyStatus");
286
+ }
103
287
  const strategy = options.strategy ?? resolveStrategy(options.target ?? "generic");
104
288
  strategy.setLargeEnumNames?.(largeEnumNames);
105
- const reservedNames = strategy.reservedNames();
289
+ // When @typecad/safety is active, `safe` is a compile-time-only construct
290
+ // (its methods are intercepted at IR-build time). Its top-level var_decl
291
+ // (from the parsed safety module source) must NOT be emitted as a C++
292
+ // _safe_t struct — add it to reservedNames so the top-level-prep filter
293
+ // strips it. Mirrors how platform reserved names (HIGH, LOW, etc.) are
294
+ // kept out of user-code emission.
295
+ let reservedNames = new Set(strategy.reservedNames());
296
+ if (hasSafetyHook()) {
297
+ reservedNames = new Set(reservedNames);
298
+ reservedNames.add("safe");
299
+ }
106
300
  ensureDir(options.outDir);
107
301
  const classNameMap = (() => {
108
302
  try {
@@ -118,6 +312,14 @@ export function buildEmitterContext(program, options) {
118
312
  })();
119
313
  const programAnalysis = analyzeProgram(program, strategy);
120
314
  if (options.platformContext) {
315
+ // The UI runtime's per-frame tick calls millis() (injected by the emitter,
316
+ // not present in user source), so a mounted UI needs the native millis ISR
317
+ // even when usesNativeTiming is false. OR entryHasUI() into the flag the
318
+ // AVR strategy reads when gating native_millis, so framework-avr doesn't
319
+ // drop the Timer0 ISR from a UI program.
320
+ if (!programAnalysis.usesNativeTiming && entryHasUI()) {
321
+ programAnalysis.usesNativeTiming = true;
322
+ }
121
323
  options.platformContext.analysis = programAnalysis;
122
324
  if (!options.platformContext.architecture && program.boardConstants) {
123
325
  options.platformContext.architecture = program.boardConstants.get("architecture");
@@ -144,14 +346,50 @@ export function buildEmitterContext(program, options) {
144
346
  ? (strategy.generateNativePolyfills?.(program, options.platformContext) ?? [])
145
347
  : (strategy.generateNativePolyfills?.(program, options.platformContext) ?? []);
146
348
  let filteredNativePolyfills = filterPolyfillHelpers(nativePolyfills, programAnalysis.usedPolyfillHelpers);
147
- if (!programAnalysis.hasThrowStatements) {
349
+ // cuttlefish_halt: keep when the program throws (throw lowers to cuttlefish_halt)
350
+ // OR explicitly calls cuttlefish_halt in lowered raw text.
351
+ if (!programAnalysis.hasThrowStatements && !programAnalysis.usesHalt) {
148
352
  filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "cuttlefish_halt");
149
353
  }
354
+ // static_array: the __tc_StaticArray struct has no __tc_*() call pattern, so
355
+ // filterPolyfillHelpers' per-function tree-shaker can't drop it (it sees no
356
+ // extractable name). Gate at the polyfill-id level instead: keep only when
357
+ // __tc_StaticArray genuinely appears in used code.
358
+ if (!programAnalysis.usedPolyfillHelpers.has('__tc_StaticArray')) {
359
+ filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "static_array");
360
+ }
150
361
  const usesTimers = programAnalysis.usedPolyfillHelpers.has('__tc_setInterval') ||
151
362
  programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
152
363
  if (!usesTimers) {
153
364
  filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "timer_methods");
154
365
  }
366
+ // Safety polyfills (mode table + voter + SafeVariable + SafeInt). Gated on
367
+ // programUsesSafety(program) so they ONLY appear when the sketch actually
368
+ // references a safety construct — emitting them unconditionally (just because
369
+ // @typecad/safety is installed and registered its hook) leaks ~200 lines of
370
+ // polyfill into every sketch, and SafeInt's `return *this` chaining methods
371
+ // tripped byte-identity / lowering assertions that expect no `this` token.
372
+ // programUsesSafety walks the IR for any safety.* hal-op.
373
+ if (isEntryFile && hasSafetyHook() && programUsesSafety(program)) {
374
+ const safetyPolyfills = filterPolyfillHelpers(requireSafetyHook().buildPolyfills?.() ?? [], programAnalysis.usedPolyfillHelpers);
375
+ filteredNativePolyfills = [...filteredNativePolyfills, ...safetyPolyfills];
376
+ }
377
+ // Cooperative scheduler polyfill (Phase 0). Injected centrally when the
378
+ // strategy opts into priority/time-budget scheduling AND there is per-frame
379
+ // work to schedule (async tasks, the std::function microtask pump, or the
380
+ // cooperative timer pump). The polyfill carries only the no-STL CoopSched
381
+ // namespace; per-program trampoline registration is emitted by the function
382
+ // emitter around the driver's loop injection. STL-free so it links on
383
+ // minimal-libc targets (Zephyr) where the Promise runtime does not.
384
+ const coopConfig = strategy.getAsyncRuntimeConfig?.();
385
+ const hasCoopWork = program.functions.some(fn => fn.isAsync) ||
386
+ programAnalysis.usedPolyfillHelpers.has('__tc_setInterval') ||
387
+ programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
388
+ if (isEntryFile && coopConfig && hasCoopWork) {
389
+ const coop = buildCoopSchedulerPolyfill(program, coopConfig, strategy.currentTimeMillis());
390
+ if (coop)
391
+ filteredNativePolyfills = [...filteredNativePolyfills, coop];
392
+ }
155
393
  const allPolyfills = filteredNativePolyfills;
156
394
  const emittedPolyfills = allPolyfills.length > 0
157
395
  ? emitPolyfillBoilerplate(allPolyfills)
@@ -161,23 +399,15 @@ export function buildEmitterContext(program, options) {
161
399
  if (!isNpmPackage) {
162
400
  includes.push(...strategy.forcedIncludes(program, options.platformContext));
163
401
  Object.assign(symbolMap, strategy.symbolAliases(program, options.platformContext));
164
- if (isEntryFile) {
165
- shimLines = [...strategy.shimLines(program, options.platformContext)];
166
- }
167
- else {
168
- // Non-entry files in split compilation: always emit the full shim
169
- // block. A class method body that uses `??` lowers to a
170
- // cuttlefish_nullish(...) call, but the helper lives in the entry
171
- // file's shim which isn't visible from this header. We used to gate
172
- // this on programAnalysis.usesNullish, but that flag is unreliable:
173
- // the analysis runs before the expression renderer, which can
174
- // introduce cuttlefish_nullish calls (e.g. for default-param
175
- // destructuring, or `??` inside class methods on abstract bases)
176
- // that the analysis pass didn't see, leaving the header referencing
177
- // an undeclared helper. The shim is fully idempotent (#ifndef
178
- // guards), so emitting it unconditionally is safe and cheap.
179
- shimLines = [...strategy.shimLines(program, options.platformContext)];
180
- }
402
+ // Both entry and non-entry files get the strategy's shim lines, then the
403
+ // Layer B filters below strip unused blocks based on programAnalysis. For
404
+ // non-entry (split-file) headers, the per-file pre-render analysis can miss
405
+ // renderer-introduced calls (e.g. cuttlefish_nullish from `??` lowering);
406
+ // output-finalizer.ts handles that case with a post-render text scan that
407
+ // re-injects the nullish helper shim into a header whose emitted lines
408
+ // actually contain a cuttlefish_nullish( call. So gating here is safe the
409
+ // post-render injection covers the one unreliable pre-render signal.
410
+ shimLines = [...strategy.shimLines(program, options.platformContext)];
181
411
  if (!programAnalysis.usesStringConversion) {
182
412
  shimLines = shimLines.filter(l => !l.includes('std::string String('));
183
413
  }
@@ -223,6 +453,87 @@ export function buildEmitterContext(program, options) {
223
453
  if (!programAnalysis.usesStrPtr) {
224
454
  shimLines = filterShimBlock(shimLines, '#ifndef CUTTLEFISH_STR_BUF_SIZE', 'inline size_t (strlen)(const __tc_str_ptr& s) { return ::strlen(s.buf); }');
225
455
  }
456
+ // framework-avr native peripheral driver shims. Each block carries a
457
+ // CUTTLEFISH_*_BEGIN/END marker pair; strip the ones the program doesn't
458
+ // use. framework-arduino's shimLines contain none of these markers, so
459
+ // these filters are no-ops there. The strategies also self-gate on the
460
+ // same flags; this is the defensive backstop (mirrors how usesWDT/etc.
461
+ // backstop the strategy-side gating above).
462
+ // Keep the UART block when console calls are present (console_log etc.
463
+ // call _uart_* functions) even if no direct uart.* HAL ops are used.
464
+ if (!programAnalysis.usesUart && !programAnalysis.hasConsoleCalls) {
465
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_BEGIN', '// CUTTLEFISH_UART_END');
466
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_EXT_BEGIN', '// CUTTLEFISH_UART_EXT_END');
467
+ }
468
+ if (!programAnalysis.usesSPI) {
469
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_SPI_BEGIN', '// CUTTLEFISH_SPI_END');
470
+ }
471
+ if (!programAnalysis.usesI2C) {
472
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TWI_BEGIN', '// CUTTLEFISH_TWI_END');
473
+ }
474
+ if (!programAnalysis.usesEEPROM && !programAnalysis.usesPreferences) {
475
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_EEPROM_BEGIN', '// CUTTLEFISH_EEPROM_END');
476
+ }
477
+ if (!programAnalysis.usesTone) {
478
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TONE_BEGIN', '// CUTTLEFISH_TONE_END');
479
+ }
480
+ // framework-esp32 native peripheral driver shims. Same defensive-backstop
481
+ // pattern as the UART/SPI/TWI/EEPROM/tone blocks above. framework-arduino's
482
+ // and framework-avr's shimLines contain none of these markers, so these
483
+ // filters are no-ops there.
484
+ if (!programAnalysis.usesGPIO) {
485
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_GPIO_BEGIN', '// CUTTLEFISH_GPIO_END');
486
+ }
487
+ if (!programAnalysis.usesPWM) {
488
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_PWM_BEGIN', '// CUTTLEFISH_PWM_END');
489
+ }
490
+ if (!programAnalysis.usesADC) {
491
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_ADC_BEGIN', '// CUTTLEFISH_ADC_END');
492
+ }
493
+ if (!programAnalysis.usesDAC) {
494
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_DAC_BEGIN', '// CUTTLEFISH_DAC_END');
495
+ }
496
+ if (!programAnalysis.usesPower) {
497
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_POWER_BEGIN', '// CUTTLEFISH_POWER_END');
498
+ }
499
+ if (!programAnalysis.usesWdt) {
500
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WDT_BEGIN', '// CUTTLEFISH_WDT_END');
501
+ }
502
+ if (!programAnalysis.usesInterrupts) {
503
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_INTR_BEGIN', '// CUTTLEFISH_INTR_END');
504
+ }
505
+ if (!programAnalysis.usesPulse) {
506
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_PULSE_BEGIN', '// CUTTLEFISH_PULSE_END');
507
+ }
508
+ if (!programAnalysis.usesShift) {
509
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_SHIFT_BEGIN', '// CUTTLEFISH_SHIFT_END');
510
+ }
511
+ if (!programAnalysis.usesWifi) {
512
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WIFI_BEGIN', '// CUTTLEFISH_WIFI_END');
513
+ }
514
+ else {
515
+ if (!programAnalysis.usesWifiConnect) {
516
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WIFI_CONNECT_BEGIN', '// CUTTLEFISH_WIFI_CONNECT_END');
517
+ }
518
+ if (!programAnalysis.usesWifiConnectBlocking) {
519
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WIFI_CONNECT_BLOCKING_BEGIN', '// CUTTLEFISH_WIFI_CONNECT_BLOCKING_END');
520
+ }
521
+ if (!programAnalysis.usesWifiQuery) {
522
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WIFI_QUERY_BEGIN', '// CUTTLEFISH_WIFI_QUERY_END');
523
+ }
524
+ if (!programAnalysis.usesWifiScan) {
525
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WIFI_SCAN_BEGIN', '// CUTTLEFISH_WIFI_SCAN_END');
526
+ }
527
+ if (!programAnalysis.usesWifiConfig) {
528
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WIFI_CONFIG_BEGIN', '// CUTTLEFISH_WIFI_CONFIG_END');
529
+ }
530
+ }
531
+ if (!programAnalysis.usesHttp) {
532
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_HTTP_BEGIN', '// CUTTLEFISH_HTTP_END');
533
+ }
534
+ if (!programAnalysis.usesPreferences) {
535
+ shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_PREFERENCES_BEGIN', '// CUTTLEFISH_PREFERENCES_END');
536
+ }
226
537
  profileDiagnostics = [...strategy.profileDiagnostics(program, options.platformContext)];
227
538
  }
228
539
  for (const imported of program.imports) {
@@ -258,11 +569,11 @@ export function buildEmitterContext(program, options) {
258
569
  symbolMap[defaultName] = defaultName;
259
570
  continue;
260
571
  }
261
- // Compile-time-only packages (@typecad/ui, @typecad/ui): their calls are
262
- // intercepted at IR-build time; the package emits NO C++ module/header.
263
- // Register the imported symbols so references resolve, but skip the
264
- // #include generation (there is no Ui.h to include).
265
- if (imported.moduleSpecifier === "@typecad/ui" || imported.moduleSpecifier === "@typecad/ui") {
572
+ // Compile-time-only packages: their calls are intercepted at IR-build
573
+ // time; the package emits NO C++ module/header. Register the imported
574
+ // symbols so references resolve, but skip the #include generation.
575
+ if (imported.moduleSpecifier === "@typecad/ui" ||
576
+ imported.moduleSpecifier === "@typecad/safety") {
266
577
  for (const symbol of imported.namedImports) {
267
578
  symbolMap[symbol] = symbol;
268
579
  }
@@ -615,6 +926,10 @@ export function buildEmitterContext(program, options) {
615
926
  knownTopLevelObjectTypes,
616
927
  diagnostics: emitDiagnostics,
617
928
  });
929
+ // Construct the compliance context early so it can be threaded into the
930
+ // renderers (StatementRenderer needs it for A3-9-1 fixed-width integer
931
+ // default). It's also returned in the EmitterContext at the end.
932
+ const compliance = new ComplianceContext(options.autosar ?? "off");
618
933
  const statementRenderer = new StatementRenderer({
619
934
  strategy,
620
935
  boardConstants: program.boardConstants,
@@ -633,6 +948,7 @@ export function buildEmitterContext(program, options) {
633
948
  interfaceFieldTypes,
634
949
  crossModuleClassNames: classNames,
635
950
  diagnostics: emitDiagnostics,
951
+ compliance,
636
952
  });
637
953
  const asyncFunctionOriginalNames = new Set(program.functions.filter((fn) => fn.isAsync).map((fn) => fn.originalName));
638
954
  const mapFunctionNameLocal = (originalName) => statementRenderer.mapFunctionName(originalName);
@@ -715,7 +1031,13 @@ export function buildEmitterContext(program, options) {
715
1031
  includes.push(...emittedPolyfills.includes.map((include) => normalizeInclude(include)));
716
1032
  }
717
1033
  if (program.requiredIncludes) {
718
- includes.push(...program.requiredIncludes);
1034
+ let reqIncludes = [...program.requiredIncludes];
1035
+ // Let the strategy strip stale includes (e.g. framework-avr removes
1036
+ // Arduino library headers it doesn't need — Wire.h, SPI.h, etc.).
1037
+ if (strategy.filterRequiredIncludes) {
1038
+ reqIncludes = strategy.filterRequiredIncludes(reqIncludes);
1039
+ }
1040
+ includes.push(...reqIncludes);
719
1041
  }
720
1042
  // UI text bindings lower to snprintf bodies that need <stdio.h>. Pushed here
721
1043
  // (in buildEmitterContext) rather than in emitUIRuntime because emitPreamble
@@ -739,6 +1061,17 @@ export function buildEmitterContext(program, options) {
739
1061
  if (stringEnumNames.size > 0) {
740
1062
  includes.push(strategy.cstringHeader());
741
1063
  }
1064
+ // `<avr/wdt.h>` is only needed when the program actually uses the watchdog.
1065
+ // The HAL resolver lowers WDT.enable/reset/disable to bare wdt_*() calls
1066
+ // (or constant-folded wdt_enable(WDTO_*) macros), all of which require the
1067
+ // header. Detect the structured `wdt.*` hal-ops directly here rather than
1068
+ // relying on programAnalysis.usesWDT (which misses these ops — they carry
1069
+ // a typed operation name, not raw code the analysis scans) or on a forced
1070
+ // include in the AVR profile (which leaked the header into every AVR
1071
+ // program, even ones that never touch the watchdog like `led.toggle()`).
1072
+ if (programUsesWdt(program)) {
1073
+ includes.push("<avr/wdt.h>");
1074
+ }
742
1075
  // Placeholder defaults for fields that are computed later by other phases
743
1076
  const noopFixPointer = (c) => c;
744
1077
  // Demo #18 Finding B: collect the names of non-exported free functions that
@@ -830,6 +1163,14 @@ export function buildEmitterContext(program, options) {
830
1163
  headerLines: ["#pragma once", ""],
831
1164
  sourceMapEntries: [],
832
1165
  headerMapEntries: [],
1166
+ // GDB debug mode: emit #line markers at appendSourceLine when 'gdb'.
1167
+ // Resolved once from strategy.debugMode(buildTarget); defaults to 'printf'
1168
+ // so all existing paths keep their pre-marker behavior.
1169
+ debugMode: (() => {
1170
+ const buildTarget = options.platformContext?.frameworkData?.buildTarget;
1171
+ return strategy.debugMode?.(buildTarget) ?? 'printf';
1172
+ })(),
1173
+ lastEmittedSource: null,
833
1174
  cArrayVarNames: new Set(),
834
1175
  fnCArrayVarNames: new Map(),
835
1176
  globalPointerVarTypes,
@@ -847,5 +1188,6 @@ export function buildEmitterContext(program, options) {
847
1188
  templateInterfaceNames,
848
1189
  interfaceNamespaceMap,
849
1190
  interfaceFieldTypes,
1191
+ compliance,
850
1192
  };
851
1193
  }
@@ -187,6 +187,12 @@ function collectCallbackFromExpression(expr, callbackFunctions, isrPrefix, count
187
187
  function collectCallbacks(statements, callbackFunctions, isrPrefix, counter) {
188
188
  for (const stmt of statements) {
189
189
  if (stmt.kind === "call") {
190
+ // __EXPR_STMT__ statements carry an expression IR (args[0]) that the
191
+ // statement renderer renders at EMIT time via the expression renderer
192
+ // (which handles lambdas inline via renderLambda). Do NOT hoist lambdas
193
+ // from these — they must stay inline to preserve closure captures.
194
+ if (stmt.callee === "__EXPR_STMT__")
195
+ continue;
190
196
  for (const arg of stmt.args) {
191
197
  collectCallbackFromExpression(arg, callbackFunctions, isrPrefix, counter);
192
198
  }
@@ -597,6 +603,8 @@ export function runTopLevelPreprocessing(ctx) {
597
603
  statements: callbackIR.statements ?? callbackIR.body ?? [],
598
604
  debounceMs: callbackIR.debounceMs,
599
605
  isInterruptHandler: callbackIR.isInterruptHandler,
606
+ ...(callbackIR.returnType ? { returnType: callbackIR.returnType } : {}),
607
+ ...(callbackIR.typedParams ? { typedParams: callbackIR.typedParams } : {}),
600
608
  });
601
609
  replacePlaceholderInAllStatements(filteredTopLevelExecutables, rc.placeholderName, callbackName);
602
610
  for (const fn of mappedFunctions) {
@@ -5,7 +5,27 @@ import { isStringEnum } from "../../api/shared/index.js";
5
5
  import { resolveEnumValues, narrowestEnumUnderlying } from "../utils/cpp-helpers.js";
6
6
  export function emitTypeDeclarations(ctx) {
7
7
  const { program, strategy, effectiveEmitMode, reservedNames, emittedTopLevelStatements, topLevelScope } = ctx;
8
- const normalizeCppTypeForTarget = (cppType) => strategy.normalizeCppType(cppType);
8
+ const normalizeCppTypeForTarget = (cppType) => {
9
+ // A3-9-1: resolve "auto" and substitute "int" with the fixed-width
10
+ // default — but ONLY when autosar is active. When off, fall through to
11
+ // the strategy's normalizeCppType (Arduino returns "auto" for "auto").
12
+ const autosarOn = ctx.compliance.isEnabled() && ctx.compliance.isBanned("A3-9-1");
13
+ if (autosarOn && (cppType === "auto" || cppType === "int")) {
14
+ return strategy.defaultNumericType(ctx.compliance);
15
+ }
16
+ // Also apply the int->int32_t substitution to the inner template argument
17
+ // of safety wrappers (SafeInt<int> -> SafeInt<int32_t>, SafeVariable<int>
18
+ // -> SafeVariable<int32_t>). The whole-type check above misses these
19
+ // because the full string is "Name<int>", not "int".
20
+ if (autosarOn) {
21
+ const wrapperMatch = cppType.match(/^(Safe(?:Int|Variable))<int>$/);
22
+ if (wrapperMatch) {
23
+ const fixedWidth = strategy.defaultNumericType(ctx.compliance);
24
+ return `${wrapperMatch[1]}<${fixedWidth}>`;
25
+ }
26
+ }
27
+ return strategy.normalizeCppType(cppType);
28
+ };
9
29
  // Split-mode header include + forward declarations
10
30
  if (effectiveEmitMode === "split") {
11
31
  appendSourceLine(ctx, `#include \"${ctx.baseName}.h\"`);
@@ -222,7 +242,12 @@ export function emitTypeDeclarations(ctx) {
222
242
  cppType = strategy.normalizeCppType(resolved);
223
243
  }
224
244
  const isConst = statement.storage === "const";
225
- const constPrefix = isConst ? "const " : "";
245
+ // A const-qualified top-level variable whose cppType already begins with
246
+ // `const` (e.g. a string-literal global lowered as `const char*`) would
247
+ // otherwise emit `extern const const char* X;` — a duplicate-const error.
248
+ // Don't prepend the storage qualifier when the type already carries it.
249
+ const typeAlreadyConst = cppType.trimStart().startsWith("const");
250
+ const constPrefix = isConst && !typeAlreadyConst ? "const " : "";
226
251
  const varName = escapeCppKeyword(statement.name, platformReservedNames);
227
252
  // Array-typed top-level consts (e.g. `export const ARR: T[] = [...]`)
228
253
  // lower with cppType "auto" and a `{ kind: "array", elementType }`
@@ -234,7 +259,7 @@ export function emitTypeDeclarations(ctx) {
234
259
  if (cppType === "auto" && statement.initializer && statement.initializer.kind === "array") {
235
260
  const elemType = statement.initializer.elementType && statement.initializer.elementType !== "auto"
236
261
  ? strategy.normalizeCppType(statement.initializer.elementType)
237
- : strategy.defaultNumericType();
262
+ : strategy.defaultNumericType(ctx.compliance.isEnabled() ? ctx.compliance : undefined);
238
263
  appendHeaderLine(ctx, `extern ${constPrefix}${elemType} ${varName}[];`);
239
264
  emitted = true;
240
265
  continue;