c-next 0.1.69 → 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 (54) 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 +35 -607
  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 +5 -5
  15. package/src/transpiler/output/codegen/__tests__/TypeRegistrationUtils.test.ts +36 -51
  16. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +20 -17
  17. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +3 -3
  18. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +1 -1
  19. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +1 -1
  20. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +1 -1
  21. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +9 -9
  22. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +12 -12
  23. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +11 -11
  24. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +23 -17
  25. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +2 -2
  26. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +3 -3
  27. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +3 -3
  28. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +4 -4
  29. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +5 -5
  30. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +23 -25
  31. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +20 -36
  32. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +18 -18
  33. package/src/transpiler/output/codegen/assignment/handlers/__tests__/SpecialHandlers.test.ts +42 -32
  34. package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +5 -4
  35. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +14 -6
  36. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +19 -16
  37. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +21 -4
  38. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +15 -2
  39. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +2 -1
  40. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +2 -2
  41. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +3 -3
  42. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +1 -1
  43. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +2 -2
  44. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +1 -1
  45. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +7 -7
  46. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +7 -7
  47. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +2 -2
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +4 -4
  49. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +2 -2
  50. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +5 -5
  51. package/src/transpiler/state/CodeGenState.ts +122 -4
  52. package/src/transpiler/state/__tests__/CodeGenState.test.ts +269 -1
  53. /package/src/transpiler/{output/codegen → logic/analysis}/helpers/TransitiveModificationPropagator.ts +0 -0
  54. /package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/TransitiveModificationPropagator.test.ts +0 -0
@@ -1,10 +1,12 @@
1
- import { describe, it, expect, vi } from "vitest";
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
  import generateFunctionCall from "../CallExprGenerator";
3
3
  import IGeneratorInput from "../../IGeneratorInput";
4
4
  import IGeneratorState from "../../IGeneratorState";
5
5
  import IOrchestrator from "../../IOrchestrator";
6
6
  import * as Parser from "../../../../../logic/parser/grammar/CNextParser";
7
7
  import ESymbolKind from "../../../../../../utils/types/ESymbolKind";
8
+ import CodeGenState from "../../../../../state/CodeGenState";
9
+ import TTypeInfo from "../../../types/TTypeInfo";
8
10
 
9
11
  // ========================================================================
10
12
  // Test Helpers
@@ -27,10 +29,18 @@ function createMockArgListContext(
27
29
  function createMockInput(
28
30
  overrides: Partial<IGeneratorInput> = {},
29
31
  ): IGeneratorInput {
32
+ // Also populate CodeGenState with the type registry entries
33
+ // This is needed because CallExprGenerator now uses CodeGenState directly
34
+ const typeRegistry =
35
+ (overrides.typeRegistry as Map<string, TTypeInfo>) ?? new Map();
36
+ for (const [name, info] of typeRegistry) {
37
+ CodeGenState.setVariableTypeInfo(name, info);
38
+ }
39
+
30
40
  return {
31
41
  symbols: null,
32
42
  symbolTable: null,
33
- typeRegistry: new Map(),
43
+ typeRegistry,
34
44
  functionSignatures: new Map(),
35
45
  knownFunctions: new Set(),
36
46
  knownStructs: new Set(),
@@ -94,6 +104,11 @@ function createMockOrchestrator(
94
104
  // ========================================================================
95
105
 
96
106
  describe("CallExprGenerator", () => {
107
+ // Reset CodeGenState before each test to avoid state pollution
108
+ beforeEach(() => {
109
+ CodeGenState.reset();
110
+ });
111
+
97
112
  describe("empty function call", () => {
98
113
  it("generates call with no arguments when argCtx is null", () => {
99
114
  const input = createMockInput();
@@ -756,7 +771,7 @@ describe("CallExprGenerator", () => {
756
771
  });
757
772
 
758
773
  describe("cross-file function calls (Issue #315)", () => {
759
- it("looks up parameter info from SymbolTable when no local signature", () => {
774
+ it("looks up parameter info from SymbolTable and passes primitives by value (Issue #786)", () => {
760
775
  const argExprs = [createMockExpressionContext("myVal")];
761
776
  const argCtx = createMockArgListContext(argExprs);
762
777
  const symbolTable = {
@@ -777,6 +792,7 @@ describe("CallExprGenerator", () => {
777
792
  isCNextFunction: vi.fn(() => true),
778
793
  isFloatType: vi.fn(() => false),
779
794
  isParameterPassByValue: vi.fn(() => false),
795
+ isStructType: vi.fn(() => false),
780
796
  });
781
797
 
782
798
  const result = generateFunctionCall(
@@ -788,7 +804,8 @@ describe("CallExprGenerator", () => {
788
804
  );
789
805
 
790
806
  expect(symbolTable.getOverloads).toHaveBeenCalledWith("crossFileFunc");
791
- expect(result.code).toBe("crossFileFunc(&myVal)");
807
+ // Issue #786: Primitive types like u32 are now passed by value for cross-file calls
808
+ expect(result.code).toBe("crossFileFunc(myVal)");
792
809
  });
793
810
 
794
811
  it("passes small primitive by value for cross-file functions", () => {
@@ -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 &&
@@ -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();
@@ -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
  /**
@@ -785,7 +903,7 @@ export default class CodeGenState {
785
903
  * Register a variable type.
786
904
  */
787
905
  static registerType(name: string, info: TTypeInfo): void {
788
- this.typeRegistry.set(name, info);
906
+ this.setVariableTypeInfo(name, info);
789
907
  }
790
908
 
791
909
  /**