c-next 0.1.68 → 0.1.70

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 (62) hide show
  1. package/package.json +1 -1
  2. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +240 -204
  3. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +693 -0
  4. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +86 -5
  5. package/src/transpiler/{output/codegen → logic/analysis}/helpers/AssignmentTargetExtractor.ts +1 -1
  6. package/src/transpiler/{output/codegen → logic/analysis}/helpers/ChildStatementCollector.ts +1 -1
  7. package/src/transpiler/{output/codegen → logic/analysis}/helpers/StatementExpressionCollector.ts +1 -1
  8. package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/AssignmentTargetExtractor.test.ts +2 -2
  9. package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/ChildStatementCollector.test.ts +2 -2
  10. package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/StatementExpressionCollector.test.ts +2 -2
  11. package/src/transpiler/output/codegen/CodeGenerator.ts +160 -742
  12. package/src/transpiler/output/codegen/TypeRegistrationUtils.ts +4 -6
  13. package/src/transpiler/output/codegen/TypeResolver.ts +2 -2
  14. package/src/transpiler/output/codegen/TypeValidator.ts +7 -7
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +2 -2
  16. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +29 -1
  17. package/src/transpiler/output/codegen/__tests__/TypeRegistrationUtils.test.ts +36 -51
  18. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +20 -17
  19. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +4 -6
  20. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +4 -2
  21. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +1 -1
  22. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +1 -1
  23. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +9 -9
  24. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +12 -12
  25. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +11 -11
  26. package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +49 -0
  27. package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +15 -0
  28. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +30 -17
  29. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +25 -18
  30. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +19 -8
  31. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +3 -3
  32. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +4 -4
  33. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +5 -5
  34. package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +9 -1
  35. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +41 -26
  36. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +29 -37
  37. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +27 -19
  38. package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +10 -1
  39. package/src/transpiler/output/codegen/assignment/handlers/__tests__/SpecialHandlers.test.ts +51 -33
  40. package/src/transpiler/output/codegen/assignment/handlers/__tests__/StringHandlers.test.ts +9 -1
  41. package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +5 -4
  42. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +14 -6
  43. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +19 -16
  44. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +21 -4
  45. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +15 -2
  46. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +2 -1
  47. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +2 -2
  48. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +3 -3
  49. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +1 -1
  50. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +6 -1
  51. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +2 -2
  52. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +1 -1
  53. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +7 -7
  54. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +7 -7
  55. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +2 -2
  56. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +4 -4
  57. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +2 -2
  58. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +5 -5
  59. package/src/transpiler/state/CodeGenState.ts +157 -5
  60. package/src/transpiler/state/__tests__/CodeGenState.test.ts +274 -6
  61. /package/src/transpiler/{output/codegen → logic/analysis}/helpers/TransitiveModificationPropagator.ts +0 -0
  62. /package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/TransitiveModificationPropagator.test.ts +0 -0
@@ -9,7 +9,7 @@
9
9
  * - Property access (.length, .capacity, .size)
10
10
  */
11
11
 
12
- import { describe, it, expect, vi } from "vitest";
12
+ import { describe, it, expect, vi, beforeEach } from "vitest";
13
13
  import generatePostfixExpression from "../PostfixExpressionGenerator";
14
14
  import type IGeneratorInput from "../../IGeneratorInput";
15
15
  import type IGeneratorState from "../../IGeneratorState";
@@ -18,6 +18,7 @@ import type ICodeGenSymbols from "../../../../../types/ICodeGenSymbols";
18
18
  import type TTypeInfo from "../../../types/TTypeInfo";
19
19
  import type TParameterInfo from "../../../types/TParameterInfo";
20
20
  import * as Parser from "../../../../../logic/parser/grammar/CNextParser";
21
+ import CodeGenState from "../../../../../state/CodeGenState";
21
22
 
22
23
  // ========================================================================
23
24
  // Test Helpers - Mock Symbols
@@ -64,10 +65,17 @@ function createMockInput(overrides?: {
64
65
  symbols?: ICodeGenSymbols;
65
66
  typeRegistry?: Map<string, TTypeInfo>;
66
67
  }): IGeneratorInput {
68
+ // Also populate CodeGenState with the type registry entries
69
+ // This is needed because PostfixExpressionGenerator now uses CodeGenState directly
70
+ const typeRegistry = overrides?.typeRegistry ?? new Map<string, TTypeInfo>();
71
+ for (const [name, info] of typeRegistry) {
72
+ CodeGenState.setVariableTypeInfo(name, info);
73
+ }
74
+
67
75
  return {
68
76
  symbolTable: null,
69
77
  symbols: overrides?.symbols ?? createMockSymbols(),
70
- typeRegistry: overrides?.typeRegistry ?? new Map(),
78
+ typeRegistry,
71
79
  functionSignatures: new Map(),
72
80
  knownFunctions: new Set(),
73
81
  knownStructs: new Set(),
@@ -290,6 +298,11 @@ function createMockPostfixExpressionContext(
290
298
  // ========================================================================
291
299
 
292
300
  describe("PostfixExpressionGenerator", () => {
301
+ // Reset CodeGenState before each test to avoid state pollution
302
+ beforeEach(() => {
303
+ CodeGenState.reset();
304
+ });
305
+
293
306
  describe("basic expression generation", () => {
294
307
  it("generates simple identifier", () => {
295
308
  const ctx = createMockPostfixExpressionContext("x", []);
@@ -134,9 +134,10 @@ class ArrayInitHelper {
134
134
  }
135
135
 
136
136
  // Update type registry with inferred size for .length support
137
- const existingType = CodeGenState.typeRegistry.get(name);
137
+ const existingType = CodeGenState.getVariableTypeInfo(name);
138
138
  if (existingType) {
139
139
  existingType.arrayDimensions = [CodeGenState.lastArrayInitCount];
140
+ CodeGenState.setVariableTypeInfo(name, existingType);
140
141
  }
141
142
 
142
143
  return `[${CodeGenState.lastArrayInitCount}]`;
@@ -77,7 +77,7 @@ class AssignmentExpectedTypeResolver {
77
77
  * Resolve expected type for a simple identifier target.
78
78
  */
79
79
  private static resolveForSimpleIdentifier(id: string): IExpectedTypeResult {
80
- const typeInfo = CodeGenState.typeRegistry.get(id);
80
+ const typeInfo = CodeGenState.getVariableTypeInfo(id);
81
81
  if (!typeInfo) {
82
82
  return { expectedType: null, assignmentContext: null };
83
83
  }
@@ -107,7 +107,7 @@ class AssignmentExpectedTypeResolver {
107
107
  }
108
108
 
109
109
  const rootName = identifiers[0];
110
- const rootTypeInfo = CodeGenState.typeRegistry.get(rootName);
110
+ const rootTypeInfo = CodeGenState.getVariableTypeInfo(rootName);
111
111
 
112
112
  if (!rootTypeInfo || !CodeGenState.isKnownStruct(rootTypeInfo.baseType)) {
113
113
  return { expectedType: null, assignmentContext: null };
@@ -120,7 +120,7 @@ class AssignmentValidator {
120
120
  const shadowName = `__bits_${id}`;
121
121
  CodeGenState.floatShadowCurrent.delete(shadowName);
122
122
 
123
- const targetTypeInfo = CodeGenState.typeRegistry.get(id);
123
+ const targetTypeInfo = CodeGenState.getVariableTypeInfo(id);
124
124
  if (!targetTypeInfo) {
125
125
  return;
126
126
  }
@@ -172,7 +172,7 @@ class AssignmentValidator {
172
172
  }
173
173
 
174
174
  // ADR-036: Compile-time bounds checking
175
- const typeInfo = CodeGenState.typeRegistry.get(arrayName);
175
+ const typeInfo = CodeGenState.getVariableTypeInfo(arrayName);
176
176
  if (typeInfo?.isArray && typeInfo.arrayDimensions) {
177
177
  TypeValidator.checkArrayBounds(
178
178
  arrayName,
@@ -217,7 +217,7 @@ class AssignmentValidator {
217
217
  }
218
218
 
219
219
  // ADR-029: Validate callback field assignments with nominal typing
220
- const rootTypeInfo = CodeGenState.typeRegistry.get(rootName);
220
+ const rootTypeInfo = CodeGenState.getVariableTypeInfo(rootName);
221
221
  if (rootTypeInfo && CodeGenState.isKnownStruct(rootTypeInfo.baseType)) {
222
222
  const structType = rootTypeInfo.baseType;
223
223
  const callbackFieldKey = `${structType}.${memberName}`;
@@ -78,7 +78,7 @@ class EnumAssignmentValidator {
78
78
 
79
79
  // Check if it's a variable of primitive integer type
80
80
  if (/^[a-zA-Z_]\w*$/.exec(text)) {
81
- const typeInfo = CodeGenState.typeRegistry.get(text);
81
+ const typeInfo = CodeGenState.getVariableTypeInfo(text);
82
82
  if (
83
83
  typeInfo &&
84
84
  !typeInfo.isEnum &&
@@ -89,7 +89,12 @@ class MemberSeparatorResolver {
89
89
  // Scope member access: Sensor.buffer -> Sensor_buffer
90
90
  // Works with or without global. prefix (both are valid syntax)
91
91
  if (deps.isKnownScope(identifierChain[0])) {
92
- deps.validateCrossScopeVisibility(identifierChain[0], memberName);
92
+ // Issue #779: Skip cross-scope validation for scoped register access
93
+ // Board.GPIO where Board_GPIO is a known register is valid
94
+ const scopedRegisterName = `${identifierChain[0]}_${memberName}`;
95
+ if (!deps.isKnownRegister(scopedRegisterName)) {
96
+ deps.validateCrossScopeVisibility(identifierChain[0], memberName);
97
+ }
93
98
  return "_";
94
99
  }
95
100
 
@@ -325,7 +325,7 @@ class StringDeclHelper {
325
325
  const arraySize = CodeGenState.lastArrayInitCount;
326
326
 
327
327
  // Update type registry with inferred size
328
- CodeGenState.typeRegistry.set(name, {
328
+ CodeGenState.setVariableTypeInfo(name, {
329
329
  baseType: "char",
330
330
  bitWidth: 8,
331
331
  isArray: true,
@@ -531,7 +531,7 @@ class StringDeclHelper {
531
531
  callbacks.requireStringInclude();
532
532
 
533
533
  // Register in type registry with inferred capacity
534
- CodeGenState.typeRegistry.set(name, {
534
+ CodeGenState.setVariableTypeInfo(name, {
535
535
  baseType: "char",
536
536
  bitWidth: 8,
537
537
  isArray: true,
@@ -56,7 +56,7 @@ describe("ArrayInitHelper", () => {
56
56
 
57
57
  it("handles size inference with array initializer", () => {
58
58
  // Add existing type to registry
59
- CodeGenState.typeRegistry.set("arr", {
59
+ CodeGenState.setVariableTypeInfo("arr", {
60
60
  baseType: "u8",
61
61
  bitWidth: 8,
62
62
  isArray: true,
@@ -68,7 +68,7 @@ describe("AssignmentExpectedTypeResolver", () => {
68
68
  describe("resolve()", () => {
69
69
  describe("simple identifier", () => {
70
70
  it("should resolve expected type for known variable", () => {
71
- CodeGenState.typeRegistry.set("counter", {
71
+ CodeGenState.setVariableTypeInfo("counter", {
72
72
  baseType: "u32",
73
73
  bitWidth: 32,
74
74
  isArray: false,
@@ -87,7 +87,7 @@ describe("AssignmentExpectedTypeResolver", () => {
87
87
  });
88
88
 
89
89
  it("should use specified overflow behavior", () => {
90
- CodeGenState.typeRegistry.set("counter", {
90
+ CodeGenState.setVariableTypeInfo("counter", {
91
91
  baseType: "u8",
92
92
  bitWidth: 8,
93
93
  isArray: false,
@@ -113,7 +113,7 @@ describe("AssignmentExpectedTypeResolver", () => {
113
113
 
114
114
  describe("member access", () => {
115
115
  it("should resolve expected type for struct field", () => {
116
- CodeGenState.typeRegistry.set("config", {
116
+ CodeGenState.setVariableTypeInfo("config", {
117
117
  baseType: "Config",
118
118
  bitWidth: 0,
119
119
  isArray: false,
@@ -128,7 +128,7 @@ describe("AssignmentExpectedTypeResolver", () => {
128
128
  });
129
129
 
130
130
  it("should walk nested struct chain", () => {
131
- CodeGenState.typeRegistry.set("app", {
131
+ CodeGenState.setVariableTypeInfo("app", {
132
132
  baseType: "App",
133
133
  bitWidth: 0,
134
134
  isArray: false,
@@ -144,7 +144,7 @@ describe("AssignmentExpectedTypeResolver", () => {
144
144
  });
145
145
 
146
146
  it("should return null for non-struct root", () => {
147
- CodeGenState.typeRegistry.set("counter", {
147
+ CodeGenState.setVariableTypeInfo("counter", {
148
148
  baseType: "u32",
149
149
  bitWidth: 32,
150
150
  isArray: false,
@@ -158,7 +158,7 @@ describe("AssignmentExpectedTypeResolver", () => {
158
158
  });
159
159
 
160
160
  it("should return null for unknown field", () => {
161
- CodeGenState.typeRegistry.set("config", {
161
+ CodeGenState.setVariableTypeInfo("config", {
162
162
  baseType: "Config",
163
163
  bitWidth: 0,
164
164
  isArray: false,
@@ -175,7 +175,7 @@ describe("AssignmentExpectedTypeResolver", () => {
175
175
 
176
176
  describe("array access", () => {
177
177
  it("should return null for array access target", () => {
178
- CodeGenState.typeRegistry.set("arr", {
178
+ CodeGenState.setVariableTypeInfo("arr", {
179
179
  baseType: "u32",
180
180
  bitWidth: 32,
181
181
  isArray: true,
@@ -141,7 +141,7 @@ describe("AssignmentValidator", () => {
141
141
  });
142
142
 
143
143
  it("should validate enum assignment for enum-typed variable", () => {
144
- CodeGenState.typeRegistry.set("status", {
144
+ CodeGenState.setVariableTypeInfo("status", {
145
145
  baseType: "Status",
146
146
  bitWidth: 8,
147
147
  isArray: false,
@@ -165,7 +165,7 @@ describe("AssignmentValidator", () => {
165
165
  });
166
166
 
167
167
  it("should validate integer assignment for integer-typed variable", () => {
168
- CodeGenState.typeRegistry.set("counter", {
168
+ CodeGenState.setVariableTypeInfo("counter", {
169
169
  baseType: "u32",
170
170
  bitWidth: 32,
171
171
  isArray: false,
@@ -190,7 +190,7 @@ describe("AssignmentValidator", () => {
190
190
  });
191
191
 
192
192
  it("should pass isCompound flag to integer validation", () => {
193
- CodeGenState.typeRegistry.set("counter", {
193
+ CodeGenState.setVariableTypeInfo("counter", {
194
194
  baseType: "u32",
195
195
  bitWidth: 32,
196
196
  isArray: false,
@@ -215,7 +215,7 @@ describe("AssignmentValidator", () => {
215
215
  });
216
216
 
217
217
  it("should rethrow validation error with line:column prefix", () => {
218
- CodeGenState.typeRegistry.set("counter", {
218
+ CodeGenState.setVariableTypeInfo("counter", {
219
219
  baseType: "u8",
220
220
  bitWidth: 8,
221
221
  isArray: false,
@@ -240,7 +240,7 @@ describe("AssignmentValidator", () => {
240
240
  });
241
241
 
242
242
  it("should handle non-Error validation exceptions", () => {
243
- CodeGenState.typeRegistry.set("counter", {
243
+ CodeGenState.setVariableTypeInfo("counter", {
244
244
  baseType: "u8",
245
245
  bitWidth: 8,
246
246
  isArray: false,
@@ -298,7 +298,7 @@ describe("AssignmentValidator", () => {
298
298
  });
299
299
 
300
300
  it("should check array bounds for array with dimensions", () => {
301
- CodeGenState.typeRegistry.set("arr", {
301
+ CodeGenState.setVariableTypeInfo("arr", {
302
302
  baseType: "u8",
303
303
  bitWidth: 8,
304
304
  isConst: false,
@@ -374,7 +374,7 @@ describe("AssignmentValidator", () => {
374
374
  });
375
375
 
376
376
  it("should validate callback assignment for callback field", () => {
377
- CodeGenState.typeRegistry.set("handler", {
377
+ CodeGenState.setVariableTypeInfo("handler", {
378
378
  baseType: "Handler",
379
379
  bitWidth: 0,
380
380
  isArray: false,
@@ -275,7 +275,7 @@ describe("EnumAssignmentValidator", () => {
275
275
  });
276
276
 
277
277
  it("returns true for integer-typed variable", () => {
278
- CodeGenState.typeRegistry.set("count", {
278
+ CodeGenState.setVariableTypeInfo("count", {
279
279
  baseType: "u32",
280
280
  bitWidth: 32,
281
281
  isArray: false,
@@ -290,7 +290,7 @@ describe("EnumAssignmentValidator", () => {
290
290
  });
291
291
 
292
292
  it("returns false for enum-typed variable", () => {
293
- CodeGenState.typeRegistry.set("state", {
293
+ CodeGenState.setVariableTypeInfo("state", {
294
294
  baseType: "State",
295
295
  bitWidth: 0,
296
296
  isArray: false,
@@ -246,7 +246,7 @@ describe("StringDeclHelper", () => {
246
246
  expect(result.handled).toBe(true);
247
247
  expect(result.code).toBe('const char greeting[6] = "Hello";');
248
248
  expect(requireStringInclude).toHaveBeenCalled();
249
- expect(CodeGenState.typeRegistry.get("greeting")).toMatchObject({
249
+ expect(CodeGenState.getVariableTypeInfo("greeting")).toMatchObject({
250
250
  baseType: "char",
251
251
  isString: true,
252
252
  stringCapacity: 5,
@@ -821,9 +821,9 @@ describe("StringDeclHelper", () => {
821
821
  expect(result.handled).toBe(true);
822
822
  expect(result.code).toContain("[2]"); // Inferred size
823
823
  expect(result.code).toContain("[11]"); // capacity + 1
824
- expect(CodeGenState.typeRegistry.get("labels")?.arrayDimensions).toEqual([
825
- 2, 11,
826
- ]);
824
+ expect(
825
+ CodeGenState.getVariableTypeInfo("labels")?.arrayDimensions,
826
+ ).toEqual([2, 11]);
827
827
  });
828
828
 
829
829
  it("generates string array without initializer (zero-init)", () => {
@@ -41,7 +41,7 @@ export default class EnumTypeResolver {
41
41
 
42
42
  // Check if it's a simple identifier that's an enum variable
43
43
  if (/^[a-zA-Z_]\w*$/.exec(text)) {
44
- const typeInfo = CodeGenState.typeRegistry.get(text);
44
+ const typeInfo = CodeGenState.getVariableTypeInfo(text);
45
45
  if (typeInfo?.isEnum && typeInfo.enumTypeName) {
46
46
  return typeInfo.enumTypeName;
47
47
  }
@@ -146,7 +146,7 @@ export default class EnumTypeResolver {
146
146
  }
147
147
  const varName = parts[1];
148
148
  const scopedVarName = `${CodeGenState.currentScope}_${varName}`;
149
- const typeInfo = CodeGenState.typeRegistry.get(scopedVarName);
149
+ const typeInfo = CodeGenState.getVariableTypeInfo(scopedVarName);
150
150
  if (typeInfo?.isEnum && typeInfo.enumTypeName) {
151
151
  return typeInfo.enumTypeName;
152
152
  }
@@ -121,7 +121,7 @@ describe("EnumTypeResolver", () => {
121
121
  CodeGenState.symbols = createMockSymbols({
122
122
  knownEnums: new Set(["State"]),
123
123
  });
124
- CodeGenState.typeRegistry.set("currentState", {
124
+ CodeGenState.setVariableTypeInfo("currentState", {
125
125
  baseType: "State",
126
126
  bitWidth: 0,
127
127
  isArray: false,
@@ -135,7 +135,7 @@ describe("EnumTypeResolver", () => {
135
135
  });
136
136
 
137
137
  it("returns null for non-enum variable", () => {
138
- CodeGenState.typeRegistry.set("count", {
138
+ CodeGenState.setVariableTypeInfo("count", {
139
139
  baseType: "u32",
140
140
  bitWidth: 32,
141
141
  isArray: false,
@@ -190,7 +190,7 @@ describe("EnumTypeResolver", () => {
190
190
  CodeGenState.symbols = createMockSymbols({
191
191
  knownEnums: new Set(["Motor_State"]),
192
192
  });
193
- CodeGenState.typeRegistry.set("Motor_current", {
193
+ CodeGenState.setVariableTypeInfo("Motor_current", {
194
194
  baseType: "Motor_State",
195
195
  bitWidth: 0,
196
196
  isArray: false,
@@ -261,7 +261,7 @@ describe("EnumTypeResolver", () => {
261
261
  CodeGenState.symbols = createMockSymbols({
262
262
  knownEnums: new Set(["EValueId"]),
263
263
  });
264
- CodeGenState.typeRegistry.set("input", {
264
+ CodeGenState.setVariableTypeInfo("input", {
265
265
  baseType: "TInput",
266
266
  bitWidth: 0,
267
267
  isArray: false,
@@ -280,7 +280,7 @@ describe("EnumTypeResolver", () => {
280
280
  symbolTable.addStructField("TInput", "count", "u32");
281
281
  CodeGenState.symbolTable = symbolTable;
282
282
  CodeGenState.symbols = createMockSymbols();
283
- CodeGenState.typeRegistry.set("input", {
283
+ CodeGenState.setVariableTypeInfo("input", {
284
284
  baseType: "TInput",
285
285
  bitWidth: 0,
286
286
  isArray: false,
@@ -30,6 +30,8 @@ import ICallbackTypeInfo from "../output/codegen/types/ICallbackTypeInfo";
30
30
  import ITargetCapabilities from "../output/codegen/types/ITargetCapabilities";
31
31
  import TOverflowBehavior from "../output/codegen/types/TOverflowBehavior";
32
32
  import TYPE_WIDTH from "../output/codegen/types/TYPE_WIDTH";
33
+ import ESymbolKind from "../../utils/types/ESymbolKind";
34
+ import ESourceLanguage from "../../utils/types/ESourceLanguage";
33
35
 
34
36
  /**
35
37
  * Default target capabilities (safe fallback)
@@ -101,8 +103,12 @@ export default class CodeGenState {
101
103
  // TYPE TRACKING
102
104
  // ===========================================================================
103
105
 
104
- /** Track variable types for bit access, .length, and type inference */
105
- static typeRegistry: Map<string, TTypeInfo> = new Map();
106
+ /**
107
+ * Track variable types for bit access, .length, and type inference.
108
+ * PRIVATE: Use getVariableTypeInfo()/setVariableTypeInfo() instead.
109
+ * This ensures cross-file variables from SymbolTable are also found.
110
+ */
111
+ private static typeRegistry: Map<string, TTypeInfo> = new Map();
106
112
 
107
113
  /** Bug #8: Compile-time const values for array size resolution */
108
114
  static constValues: Map<string, number> = new Map();
@@ -187,7 +193,7 @@ export default class CodeGenState {
187
193
  static localArrays: Set<string> = new Set();
188
194
 
189
195
  /** Scope member names: scope -> Set of member names */
190
- static scopeMembers: Map<string, Set<string>> = new Map();
196
+ private static scopeMembers: Map<string, Set<string>> = new Map();
191
197
 
192
198
  /** Float bit indexing: declared shadow variables */
193
199
  static floatBitShadows: Set<string> = new Set();
@@ -451,9 +457,121 @@ export default class CodeGenState {
451
457
 
452
458
  /**
453
459
  * Get type info for a variable.
460
+ * Checks local typeRegistry first, then falls back to SymbolTable
461
+ * for cross-file variables from included .cnx files.
462
+ *
463
+ * Issue #786: This unified lookup ensures cross-file variables
464
+ * (defined in included files) are found even before code generation
465
+ * registers them locally.
466
+ */
467
+ static getVariableTypeInfo(name: string): TTypeInfo | undefined {
468
+ // First check the local type registry (current file's variables)
469
+ const localInfo = this.typeRegistry.get(name);
470
+ if (localInfo) {
471
+ return localInfo;
472
+ }
473
+
474
+ // Fall back to SymbolTable for cross-file C-Next variables only.
475
+ // C/C++ header symbols don't have complete type info (e.g., isArray),
476
+ // so we only use C-Next symbols from SymbolTable.
477
+ const symbol = this.symbolTable.getSymbol(name);
478
+ if (
479
+ symbol?.kind === ESymbolKind.Variable &&
480
+ symbol.type &&
481
+ symbol.sourceLanguage === ESourceLanguage.CNext
482
+ ) {
483
+ return this.convertSymbolToTypeInfo(symbol);
484
+ }
485
+
486
+ return undefined;
487
+ }
488
+
489
+ /**
490
+ * Legacy alias for getVariableTypeInfo.
491
+ * @deprecated Use getVariableTypeInfo() instead
454
492
  */
455
493
  static getTypeInfo(name: string): TTypeInfo | undefined {
456
- return this.typeRegistry.get(name);
494
+ return this.getVariableTypeInfo(name);
495
+ }
496
+
497
+ /**
498
+ * Check if a variable type is registered (locally or in SymbolTable).
499
+ */
500
+ static hasVariableTypeInfo(name: string): boolean {
501
+ if (this.typeRegistry.has(name)) {
502
+ return true;
503
+ }
504
+ const symbol = this.symbolTable.getSymbol(name);
505
+ return (
506
+ symbol?.kind === ESymbolKind.Variable &&
507
+ symbol.type !== undefined &&
508
+ symbol.sourceLanguage === ESourceLanguage.CNext
509
+ );
510
+ }
511
+
512
+ /**
513
+ * Set variable type info in the local registry.
514
+ */
515
+ static setVariableTypeInfo(name: string, info: TTypeInfo): void {
516
+ this.typeRegistry.set(name, info);
517
+ }
518
+
519
+ /**
520
+ * Delete variable type info from the local registry.
521
+ */
522
+ static deleteVariableTypeInfo(name: string): void {
523
+ this.typeRegistry.delete(name);
524
+ }
525
+
526
+ /**
527
+ * Get a read-only view of the local type registry.
528
+ * Used for passing to helper functions that need to iterate over types.
529
+ * Note: This only returns locally registered types, not cross-file symbols.
530
+ */
531
+ static getTypeRegistryView(): ReadonlyMap<string, TTypeInfo> {
532
+ return this.typeRegistry;
533
+ }
534
+
535
+ /**
536
+ * Convert an ISymbol to TTypeInfo for unified type lookups.
537
+ * Used when looking up cross-file variables from SymbolTable.
538
+ */
539
+ private static convertSymbolToTypeInfo(symbol: {
540
+ type?: string;
541
+ isArray?: boolean;
542
+ arrayDimensions?: string[];
543
+ isConst?: boolean;
544
+ isAtomic?: boolean;
545
+ }): TTypeInfo {
546
+ const rawType = symbol.type || "unknown";
547
+
548
+ // Parse string<N> type pattern
549
+ const stringPattern = /^string<(\d+)>$/;
550
+ const stringMatch = stringPattern.exec(rawType);
551
+ const isString = stringMatch !== null;
552
+ const stringCapacity = stringMatch
553
+ ? Number.parseInt(stringMatch[1], 10)
554
+ : undefined;
555
+ // Use "char" for string types to match local convention (StringDeclHelper.ts)
556
+ const baseType = isString ? "char" : rawType;
557
+
558
+ const isEnum = this.isKnownEnum(baseType);
559
+
560
+ return {
561
+ baseType,
562
+ // Use bitWidth 8 for strings (char), otherwise lookup from TYPE_WIDTH
563
+ bitWidth: isString ? 8 : TYPE_WIDTH[baseType] || 0,
564
+ isArray: symbol.isArray || false,
565
+ arrayDimensions: symbol.arrayDimensions
566
+ ?.map((d) => Number.parseInt(d, 10))
567
+ .filter((n) => !Number.isNaN(n)),
568
+ isConst: symbol.isConst || false,
569
+ isAtomic: symbol.isAtomic || false,
570
+ isEnum,
571
+ enumTypeName: isEnum ? baseType : undefined,
572
+ isString,
573
+ stringCapacity,
574
+ };
457
575
  }
458
576
 
459
577
  /**
@@ -463,6 +581,26 @@ export default class CodeGenState {
463
581
  return this.modifiedParameters.get(funcName)?.has(paramName) ?? false;
464
582
  }
465
583
 
584
+ /**
585
+ * Compute unmodified parameters for all functions on-demand.
586
+ * Returns a map of function name -> Set of parameter names NOT modified.
587
+ * Computed from functionSignatures and modifiedParameters (no cached state).
588
+ */
589
+ static getUnmodifiedParameters(): Map<string, Set<string>> {
590
+ const result = new Map<string, Set<string>>();
591
+ for (const [funcName, signature] of this.functionSignatures) {
592
+ const modifiedSet = this.modifiedParameters.get(funcName);
593
+ const unmodified = new Set<string>();
594
+ for (const param of signature.parameters) {
595
+ if (!modifiedSet?.has(param.name)) {
596
+ unmodified.add(param.name);
597
+ }
598
+ }
599
+ result.set(funcName, unmodified);
600
+ }
601
+ return result;
602
+ }
603
+
466
604
  /**
467
605
  * Check if a parameter should pass by value.
468
606
  */
@@ -542,6 +680,20 @@ export default class CodeGenState {
542
680
  return this.scopeMembers.get(scopeName);
543
681
  }
544
682
 
683
+ /**
684
+ * Set members of a scope.
685
+ */
686
+ static setScopeMembers(scopeName: string, members: Set<string>): void {
687
+ this.scopeMembers.set(scopeName, members);
688
+ }
689
+
690
+ /**
691
+ * Get all scope members (for IGeneratorState).
692
+ */
693
+ static getAllScopeMembers(): ReadonlyMap<string, ReadonlySet<string>> {
694
+ return this.scopeMembers;
695
+ }
696
+
545
697
  /**
546
698
  * Check if an identifier is a member of the current scope.
547
699
  */
@@ -751,7 +903,7 @@ export default class CodeGenState {
751
903
  * Register a variable type.
752
904
  */
753
905
  static registerType(name: string, info: TTypeInfo): void {
754
- this.typeRegistry.set(name, info);
906
+ this.setVariableTypeInfo(name, info);
755
907
  }
756
908
 
757
909
  /**