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
@@ -25,8 +25,14 @@ import HandlerTestUtils from "./handlerTestUtils";
25
25
  function createMockContext(
26
26
  overrides: Partial<IAssignmentContext> = {},
27
27
  ): IAssignmentContext {
28
+ // Default resolved values based on first identifier
29
+ const identifiers = overrides.identifiers ?? ["flags", "Running"];
30
+ const resolvedTarget = overrides.resolvedTarget ?? identifiers[0];
31
+ const resolvedBaseIdentifier =
32
+ overrides.resolvedBaseIdentifier ?? identifiers[0];
33
+
28
34
  return {
29
- identifiers: ["flags", "Running"],
35
+ identifiers,
30
36
  subscripts: [],
31
37
  isCompound: false,
32
38
  cnextOp: "<-",
@@ -44,6 +50,8 @@ function createMockContext(
44
50
  isSimpleIdentifier: false,
45
51
  isSimpleThisAccess: false,
46
52
  isSimpleGlobalAccess: false,
53
+ resolvedTarget,
54
+ resolvedBaseIdentifier,
47
55
  ...overrides,
48
56
  } as IAssignmentContext;
49
57
  }
@@ -81,9 +89,9 @@ describe("BitmapHandlers", () => {
81
89
  )?.[1];
82
90
 
83
91
  it("generates single-bit read-modify-write", () => {
84
- CodeGenState.typeRegistry = new Map([
92
+ HandlerTestUtils.setupMockTypeRegistry([
85
93
  ["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
86
- ]) as any;
94
+ ]);
87
95
  HandlerTestUtils.setupMockSymbols({
88
96
  bitmapFields: new Map([
89
97
  ["StatusFlags", new Map([["Running", { offset: 0, width: 1 }]])],
@@ -99,9 +107,9 @@ describe("BitmapHandlers", () => {
99
107
  });
100
108
 
101
109
  it("generates single-bit write with correct offset", () => {
102
- CodeGenState.typeRegistry = new Map([
110
+ HandlerTestUtils.setupMockTypeRegistry([
103
111
  ["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
104
- ]) as any;
112
+ ]);
105
113
  HandlerTestUtils.setupMockSymbols({
106
114
  bitmapFields: new Map([
107
115
  ["StatusFlags", new Map([["Active", { offset: 3, width: 1 }]])],
@@ -117,9 +125,9 @@ describe("BitmapHandlers", () => {
117
125
  });
118
126
 
119
127
  it("throws on unknown bitmap field", () => {
120
- CodeGenState.typeRegistry = new Map([
128
+ HandlerTestUtils.setupMockTypeRegistry([
121
129
  ["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
122
- ]) as any;
130
+ ]);
123
131
  HandlerTestUtils.setupMockSymbols({
124
132
  bitmapFields: new Map([["StatusFlags", new Map()]]),
125
133
  });
@@ -133,9 +141,9 @@ describe("BitmapHandlers", () => {
133
141
  });
134
142
 
135
143
  it("throws on compound assignment", () => {
136
- CodeGenState.typeRegistry = new Map([
144
+ HandlerTestUtils.setupMockTypeRegistry([
137
145
  ["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
138
- ]) as any;
146
+ ]);
139
147
  HandlerTestUtils.setupMockSymbols({
140
148
  bitmapFields: new Map([
141
149
  ["StatusFlags", new Map([["Running", { offset: 0, width: 1 }]])],
@@ -152,9 +160,9 @@ describe("BitmapHandlers", () => {
152
160
  });
153
161
 
154
162
  it("validates bitmap field literal", () => {
155
- CodeGenState.typeRegistry = new Map([
163
+ HandlerTestUtils.setupMockTypeRegistry([
156
164
  ["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
157
- ]) as any;
165
+ ]);
158
166
  HandlerTestUtils.setupMockSymbols({
159
167
  bitmapFields: new Map([
160
168
  ["StatusFlags", new Map([["Running", { offset: 0, width: 1 }]])],
@@ -180,9 +188,9 @@ describe("BitmapHandlers", () => {
180
188
  )?.[1];
181
189
 
182
190
  it("generates multi-bit read-modify-write with mask", () => {
183
- CodeGenState.typeRegistry = new Map([
191
+ HandlerTestUtils.setupMockTypeRegistry([
184
192
  ["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
185
- ]) as any;
193
+ ]);
186
194
  HandlerTestUtils.setupMockSymbols({
187
195
  bitmapFields: new Map([
188
196
  ["StatusFlags", new Map([["Mode", { offset: 4, width: 3 }]])],
@@ -202,9 +210,9 @@ describe("BitmapHandlers", () => {
202
210
  });
203
211
 
204
212
  it("generates correct mask for 2-bit field", () => {
205
- CodeGenState.typeRegistry = new Map([
213
+ HandlerTestUtils.setupMockTypeRegistry([
206
214
  ["config", { bitmapTypeName: "Config", baseType: "u8" }],
207
- ]) as any;
215
+ ]);
208
216
  HandlerTestUtils.setupMockSymbols({
209
217
  bitmapFields: new Map([
210
218
  ["Config", new Map([["Priority", { offset: 0, width: 2 }]])],
@@ -228,9 +236,9 @@ describe("BitmapHandlers", () => {
228
236
  )?.[1];
229
237
 
230
238
  it("generates array element bitmap field assignment", () => {
231
- CodeGenState.typeRegistry = new Map([
239
+ HandlerTestUtils.setupMockTypeRegistry([
232
240
  ["flagsArray", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
233
- ]) as any;
241
+ ]);
234
242
  HandlerTestUtils.setupMockGenerator({
235
243
  generateExpression: vi.fn().mockReturnValue("i"),
236
244
  });
@@ -258,9 +266,9 @@ describe("BitmapHandlers", () => {
258
266
  )?.[1];
259
267
 
260
268
  it("generates struct member bitmap field assignment", () => {
261
- CodeGenState.typeRegistry = new Map([
269
+ HandlerTestUtils.setupMockTypeRegistry([
262
270
  ["device", { baseType: "Device" }],
263
- ]) as any;
271
+ ]);
264
272
  HandlerTestUtils.setupMockSymbols({
265
273
  bitmapFields: new Map([
266
274
  ["StatusFlags", new Map([["Active", { offset: 2, width: 1 }]])],
@@ -16,8 +16,15 @@ import HandlerTestUtils from "./handlerTestUtils";
16
16
  function createMockContext(
17
17
  overrides: Partial<IAssignmentContext> = {},
18
18
  ): IAssignmentContext {
19
+ // Default resolved values based on first identifier
20
+ const identifiers = overrides.identifiers ?? ["GPIO7", "DR_SET"];
21
+ const resolvedTarget =
22
+ overrides.resolvedTarget ?? `${identifiers.join("_")}[LED_BIT]`;
23
+ const resolvedBaseIdentifier =
24
+ overrides.resolvedBaseIdentifier ?? identifiers[0];
25
+
19
26
  return {
20
- identifiers: ["GPIO7", "DR_SET"],
27
+ identifiers,
21
28
  subscripts: [{ mockValue: "LED_BIT" } as never],
22
29
  isCompound: false,
23
30
  cnextOp: "<-",
@@ -34,6 +41,8 @@ function createMockContext(
34
41
  isSimpleIdentifier: false,
35
42
  isSimpleThisAccess: false,
36
43
  isSimpleGlobalAccess: false,
44
+ resolvedTarget,
45
+ resolvedBaseIdentifier,
37
46
  ...overrides,
38
47
  } as IAssignmentContext;
39
48
  }
@@ -16,8 +16,14 @@ import HandlerTestUtils from "./handlerTestUtils";
16
16
  function createMockContext(
17
17
  overrides: Partial<IAssignmentContext> = {},
18
18
  ): IAssignmentContext {
19
+ // Default resolved values based on first identifier
20
+ const identifiers = overrides.identifiers ?? ["counter"];
21
+ const resolvedTarget = overrides.resolvedTarget ?? identifiers[0];
22
+ const resolvedBaseIdentifier =
23
+ overrides.resolvedBaseIdentifier ?? identifiers[0];
24
+
19
25
  return {
20
- identifiers: ["counter"],
26
+ identifiers,
21
27
  subscripts: [],
22
28
  isCompound: true,
23
29
  cnextOp: "+<-",
@@ -34,6 +40,8 @@ function createMockContext(
34
40
  isSimpleIdentifier: true,
35
41
  isSimpleThisAccess: false,
36
42
  isSimpleGlobalAccess: false,
43
+ resolvedTarget,
44
+ resolvedBaseIdentifier,
37
45
  ...overrides,
38
46
  } as IAssignmentContext;
39
47
  }
@@ -63,9 +71,9 @@ describe("SpecialHandlers", () => {
63
71
  specialHandlers.find(([kind]) => kind === AssignmentKind.ATOMIC_RMW)?.[1];
64
72
 
65
73
  it("delegates to generateAtomicRMW for simple identifier", () => {
66
- CodeGenState.typeRegistry = new Map([
74
+ HandlerTestUtils.setupMockTypeRegistry([
67
75
  ["counter", { baseType: "u32", isAtomic: true }],
68
- ]) as any;
76
+ ]);
69
77
  const generateAtomicRMW = vi.fn().mockReturnValue("LDREX/STREX pattern");
70
78
  const generateAssignmentTarget = vi.fn().mockReturnValue("counter");
71
79
  HandlerTestUtils.setupMockGenerator({
@@ -76,18 +84,23 @@ describe("SpecialHandlers", () => {
76
84
 
77
85
  const result = getHandler()!(ctx);
78
86
 
79
- expect(generateAtomicRMW).toHaveBeenCalledWith("counter", "+=", "1", {
80
- baseType: "u32",
81
- isAtomic: true,
82
- });
87
+ expect(generateAtomicRMW).toHaveBeenCalledWith(
88
+ "counter",
89
+ "+=",
90
+ "1",
91
+ expect.objectContaining({
92
+ baseType: "u32",
93
+ isAtomic: true,
94
+ }),
95
+ );
83
96
  expect(result).toBe("LDREX/STREX pattern");
84
97
  });
85
98
 
86
99
  it("handles this.member atomic variable", () => {
87
100
  CodeGenState.currentScope = "Motor";
88
- CodeGenState.typeRegistry = new Map([
101
+ HandlerTestUtils.setupMockTypeRegistry([
89
102
  ["Motor_count", { baseType: "u32", isAtomic: true }],
90
- ]) as any;
103
+ ]);
91
104
  const generateAtomicRMW = vi.fn().mockReturnValue("atomic result");
92
105
  const generateAssignmentTarget = vi.fn().mockReturnValue("Motor_count");
93
106
  HandlerTestUtils.setupMockGenerator({
@@ -102,17 +115,22 @@ describe("SpecialHandlers", () => {
102
115
 
103
116
  const result = getHandler()!(ctx);
104
117
 
105
- expect(generateAtomicRMW).toHaveBeenCalledWith("Motor_count", "+=", "1", {
106
- baseType: "u32",
107
- isAtomic: true,
108
- });
118
+ expect(generateAtomicRMW).toHaveBeenCalledWith(
119
+ "Motor_count",
120
+ "+=",
121
+ "1",
122
+ expect.objectContaining({
123
+ baseType: "u32",
124
+ isAtomic: true,
125
+ }),
126
+ );
109
127
  expect(result).toBe("atomic result");
110
128
  });
111
129
 
112
130
  it("handles global.member atomic variable", () => {
113
- CodeGenState.typeRegistry = new Map([
131
+ HandlerTestUtils.setupMockTypeRegistry([
114
132
  ["globalCounter", { baseType: "u32", isAtomic: true }],
115
- ]) as any;
133
+ ]);
116
134
  const generateAtomicRMW = vi.fn().mockReturnValue("global atomic result");
117
135
  const generateAssignmentTarget = vi.fn().mockReturnValue("globalCounter");
118
136
  HandlerTestUtils.setupMockGenerator({
@@ -131,9 +149,9 @@ describe("SpecialHandlers", () => {
131
149
  });
132
150
 
133
151
  it("handles subtract operation", () => {
134
- CodeGenState.typeRegistry = new Map([
152
+ HandlerTestUtils.setupMockTypeRegistry([
135
153
  ["counter", { baseType: "u32", isAtomic: true }],
136
- ]) as any;
154
+ ]);
137
155
  const generateAtomicRMW = vi.fn().mockReturnValue("atomic sub");
138
156
  const generateAssignmentTarget = vi.fn().mockReturnValue("counter");
139
157
  HandlerTestUtils.setupMockGenerator({
@@ -163,9 +181,9 @@ describe("SpecialHandlers", () => {
163
181
  )?.[1];
164
182
 
165
183
  it("generates clamp add helper for u8", () => {
166
- CodeGenState.typeRegistry = new Map([
184
+ HandlerTestUtils.setupMockTypeRegistry([
167
185
  ["saturated", { baseType: "u8", overflowBehavior: "clamp" }],
168
- ]) as any;
186
+ ]);
169
187
  const generateAssignmentTarget = vi.fn().mockReturnValue("saturated");
170
188
  HandlerTestUtils.setupMockGenerator({
171
189
  generateAssignmentTarget,
@@ -182,9 +200,9 @@ describe("SpecialHandlers", () => {
182
200
  });
183
201
 
184
202
  it("generates clamp sub helper for u16", () => {
185
- CodeGenState.typeRegistry = new Map([
203
+ HandlerTestUtils.setupMockTypeRegistry([
186
204
  ["value", { baseType: "u16", overflowBehavior: "clamp" }],
187
- ]) as any;
205
+ ]);
188
206
  const generateAssignmentTarget = vi.fn().mockReturnValue("value");
189
207
  HandlerTestUtils.setupMockGenerator({
190
208
  generateAssignmentTarget,
@@ -203,9 +221,9 @@ describe("SpecialHandlers", () => {
203
221
  });
204
222
 
205
223
  it("generates clamp mul helper for u32", () => {
206
- CodeGenState.typeRegistry = new Map([
224
+ HandlerTestUtils.setupMockTypeRegistry([
207
225
  ["result", { baseType: "u32", overflowBehavior: "clamp" }],
208
- ]) as any;
226
+ ]);
209
227
  const generateAssignmentTarget = vi.fn().mockReturnValue("result");
210
228
  HandlerTestUtils.setupMockGenerator({
211
229
  generateAssignmentTarget,
@@ -224,9 +242,9 @@ describe("SpecialHandlers", () => {
224
242
  });
225
243
 
226
244
  it("uses native arithmetic for float types", () => {
227
- CodeGenState.typeRegistry = new Map([
245
+ HandlerTestUtils.setupMockTypeRegistry([
228
246
  ["f", { baseType: "f32", overflowBehavior: "clamp" }],
229
- ]) as any;
247
+ ]);
230
248
  const generateAssignmentTarget = vi.fn().mockReturnValue("f");
231
249
  HandlerTestUtils.setupMockGenerator({
232
250
  generateAssignmentTarget,
@@ -243,9 +261,9 @@ describe("SpecialHandlers", () => {
243
261
  });
244
262
 
245
263
  it("uses native arithmetic for f64 type", () => {
246
- CodeGenState.typeRegistry = new Map([
264
+ HandlerTestUtils.setupMockTypeRegistry([
247
265
  ["d", { baseType: "f64", overflowBehavior: "clamp" }],
248
- ]) as any;
266
+ ]);
249
267
  const generateAssignmentTarget = vi.fn().mockReturnValue("d");
250
268
  HandlerTestUtils.setupMockGenerator({
251
269
  generateAssignmentTarget,
@@ -262,9 +280,9 @@ describe("SpecialHandlers", () => {
262
280
  });
263
281
 
264
282
  it("falls back to native for unsupported operators", () => {
265
- CodeGenState.typeRegistry = new Map([
283
+ HandlerTestUtils.setupMockTypeRegistry([
266
284
  ["value", { baseType: "u32", overflowBehavior: "clamp" }],
267
- ]) as any;
285
+ ]);
268
286
  const generateAssignmentTarget = vi.fn().mockReturnValue("value");
269
287
  HandlerTestUtils.setupMockGenerator({
270
288
  generateAssignmentTarget,
@@ -284,9 +302,9 @@ describe("SpecialHandlers", () => {
284
302
 
285
303
  it("handles this.member with clamp", () => {
286
304
  CodeGenState.currentScope = "Motor";
287
- CodeGenState.typeRegistry = new Map([
305
+ HandlerTestUtils.setupMockTypeRegistry([
288
306
  ["Motor_speed", { baseType: "u8", overflowBehavior: "clamp" }],
289
- ]) as any;
307
+ ]);
290
308
  const generateAssignmentTarget = vi.fn().mockReturnValue("Motor_speed");
291
309
  HandlerTestUtils.setupMockGenerator({
292
310
  generateAssignmentTarget,
@@ -305,9 +323,9 @@ describe("SpecialHandlers", () => {
305
323
  });
306
324
 
307
325
  it("handles global.member with clamp", () => {
308
- CodeGenState.typeRegistry = new Map([
326
+ HandlerTestUtils.setupMockTypeRegistry([
309
327
  ["globalValue", { baseType: "i16", overflowBehavior: "clamp" }],
310
- ]) as any;
328
+ ]);
311
329
  const generateAssignmentTarget = vi.fn().mockReturnValue("globalValue");
312
330
  HandlerTestUtils.setupMockGenerator({
313
331
  generateAssignmentTarget,
@@ -16,14 +16,22 @@ import HandlerTestUtils from "./handlerTestUtils";
16
16
  function createMockContext(
17
17
  overrides: Partial<IAssignmentContext> = {},
18
18
  ): IAssignmentContext {
19
+ // Default resolved values based on first identifier
20
+ const identifiers = overrides.identifiers ?? ["testVar"];
21
+ const resolvedTarget = overrides.resolvedTarget ?? identifiers[0];
22
+ const resolvedBaseIdentifier =
23
+ overrides.resolvedBaseIdentifier ?? identifiers[0];
24
+
19
25
  return {
20
- identifiers: ["testVar"],
26
+ identifiers,
21
27
  subscripts: [],
22
28
  isCompound: false,
23
29
  cnextOp: "<-",
24
30
  cOp: "=",
25
31
  generatedValue: '"hello"',
26
32
  targetCtx: {} as never,
33
+ resolvedTarget,
34
+ resolvedBaseIdentifier,
27
35
  ...overrides,
28
36
  } as IAssignmentContext;
29
37
  }
@@ -141,15 +141,16 @@ function createTypeInfo(overrides: Partial<TTypeInfo> = {}): TTypeInfo {
141
141
  }
142
142
 
143
143
  /**
144
- * Set up CodeGenState.typeRegistry with typed entries.
144
+ * Set up CodeGenState type registry with typed entries.
145
145
  * Entries only need to specify the fields relevant to the test.
146
+ * Uses setVariableTypeInfo to properly populate the registry.
146
147
  */
147
148
  function setupMockTypeRegistry(
148
149
  entries: Array<[string, Partial<TTypeInfo>]>,
149
150
  ): void {
150
- CodeGenState.typeRegistry = new Map(
151
- entries.map(([name, partial]) => [name, createTypeInfo(partial)]),
152
- );
151
+ for (const [name, partial] of entries) {
152
+ CodeGenState.setVariableTypeInfo(name, createTypeInfo(partial));
153
+ }
153
154
  }
154
155
 
155
156
  export default class HandlerTestUtils {
@@ -17,6 +17,7 @@ import IGeneratorInput from "../IGeneratorInput";
17
17
  import IGeneratorState from "../IGeneratorState";
18
18
  import IOrchestrator from "../IOrchestrator";
19
19
  import CallExprUtils from "./CallExprUtils";
20
+ import CodeGenState from "../../../../state/CodeGenState";
20
21
 
21
22
  /**
22
23
  * Issue #304: Wrap argument with static_cast if it's a C++ enum class
@@ -80,7 +81,7 @@ const _generateCFunctionArg = (
80
81
  // Issue #322: If getExpressionType returns null (e.g., for this.member),
81
82
  // fall back to looking up the generated code in the type registry
82
83
  if (!argType && !argCode.startsWith("&")) {
83
- const typeInfo = input.typeRegistry.get(argCode);
84
+ const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
84
85
  if (typeInfo) {
85
86
  argType = typeInfo.baseType;
86
87
  }
@@ -119,8 +120,15 @@ const _shouldPassByValue = (
119
120
  funcExpr,
120
121
  idx,
121
122
  );
122
- const isSmallPrimitive =
123
- isCrossFile && CallExprUtils.isSmallPrimitiveType(targetParam.baseType);
123
+
124
+ // Issue #786: For cross-file calls, check if parameter is a known primitive type.
125
+ // Known primitives (u8-u64, i8-i64, bool) should always be pass-by-value.
126
+ // This handles the case where local passByValueParams isn't populated for cross-file functions.
127
+ const isCrossFilePrimitive =
128
+ isCrossFile &&
129
+ CallExprUtils.isKnownPrimitiveType(targetParam.baseType) &&
130
+ !orchestrator.isStructType(targetParam.baseType) &&
131
+ !CallExprUtils.isStringType(targetParam.baseType);
124
132
 
125
133
  // Issue #551: Unknown types (external enums, typedefs) use pass-by-value
126
134
  const isUnknownType =
@@ -129,13 +137,13 @@ const _shouldPassByValue = (
129
137
  !CallExprUtils.isStringType(targetParam.baseType) &&
130
138
  !isFloatParam &&
131
139
  !isEnumParam &&
132
- !isSmallPrimitive;
140
+ !isCrossFilePrimitive;
133
141
 
134
142
  return (
135
143
  isFloatParam ||
136
144
  isEnumParam ||
137
145
  isPrimitivePassByValue ||
138
- isSmallPrimitive ||
146
+ isCrossFilePrimitive ||
139
147
  isUnknownType
140
148
  );
141
149
  };
@@ -259,7 +267,7 @@ const generateSafeDivMod = (
259
267
  }
260
268
 
261
269
  // Look up the type of the output parameter
262
- const typeInfo = input.typeRegistry.get(outputArgId);
270
+ const typeInfo = CodeGenState.getVariableTypeInfo(outputArgId);
263
271
  if (!typeInfo) {
264
272
  throw new Error(
265
273
  `Cannot determine type of output parameter '${outputArgId}' for ${funcName}`,
@@ -27,6 +27,7 @@ import SubscriptClassifier from "../../subscript/SubscriptClassifier";
27
27
  import TYPE_WIDTH from "../../types/TYPE_WIDTH";
28
28
  import C_TYPE_WIDTH from "../../types/C_TYPE_WIDTH";
29
29
  import TTypeInfo from "../../types/TTypeInfo";
30
+ import CodeGenState from "../../../../state/CodeGenState";
30
31
 
31
32
  // ========================================================================
32
33
  // Tracking State
@@ -67,7 +68,7 @@ const initializeTrackingState = (
67
68
  : false;
68
69
 
69
70
  const primaryBaseType = rootIdentifier
70
- ? input.typeRegistry.get(rootIdentifier)?.baseType
71
+ ? CodeGenState.getVariableTypeInfo(rootIdentifier)?.baseType
71
72
  : undefined;
72
73
  const currentStructType =
73
74
  primaryBaseType && orchestrator.isKnownStruct(primaryBaseType)
@@ -162,7 +163,7 @@ const generatePostfixExpression = (
162
163
  }
163
164
 
164
165
  const primaryTypeInfo = rootIdentifier
165
- ? input.typeRegistry.get(rootIdentifier)
166
+ ? CodeGenState.getVariableTypeInfo(rootIdentifier)
166
167
  : undefined;
167
168
 
168
169
  const tracking = initializeTrackingState(
@@ -364,7 +365,7 @@ const handleGlobalPrefix = (
364
365
  }
365
366
 
366
367
  // Issue #612: Set currentStructType for global struct variables
367
- const globalTypeInfo = input.typeRegistry.get(memberName);
368
+ const globalTypeInfo = CodeGenState.getVariableTypeInfo(memberName);
368
369
  if (globalTypeInfo && orchestrator.isKnownStruct(globalTypeInfo.baseType)) {
369
370
  tracking.currentStructType = globalTypeInfo.baseType;
370
371
  }
@@ -396,7 +397,7 @@ const handleThisScopeLength = (
396
397
 
397
398
  tracking.result = `${state.currentScope}_${memberName}`;
398
399
  tracking.resolvedIdentifier = tracking.result;
399
- const resolvedTypeInfo = input.typeRegistry.get(tracking.result);
400
+ const resolvedTypeInfo = CodeGenState.getVariableTypeInfo(tracking.result);
400
401
  if (
401
402
  resolvedTypeInfo &&
402
403
  orchestrator.isKnownStruct(resolvedTypeInfo.baseType)
@@ -422,7 +423,9 @@ const resolveStringTypeInfo = (
422
423
  orchestrator: IOrchestrator,
423
424
  ): TTypeInfo | undefined => {
424
425
  const identifier = tracking.resolvedIdentifier ?? rootIdentifier;
425
- const typeInfo = identifier ? input.typeRegistry.get(identifier) : undefined;
426
+ const typeInfo = identifier
427
+ ? CodeGenState.getVariableTypeInfo(identifier)
428
+ : undefined;
426
429
  if (typeInfo?.isString) {
427
430
  return typeInfo;
428
431
  }
@@ -650,7 +653,7 @@ const generateLengthProperty = (
650
653
 
651
654
  // Fall back to checking the current resolved identifier's type
652
655
  const typeInfo = ctx.resolvedIdentifier
653
- ? input.typeRegistry.get(ctx.resolvedIdentifier)
656
+ ? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
654
657
  : undefined;
655
658
 
656
659
  if (!typeInfo) {
@@ -931,7 +934,7 @@ const generateBitLengthProperty = (
931
934
 
932
935
  // Get type info for the resolved identifier
933
936
  const typeInfo = ctx.resolvedIdentifier
934
- ? input.typeRegistry.get(ctx.resolvedIdentifier)
937
+ ? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
935
938
  : undefined;
936
939
 
937
940
  if (!typeInfo) {
@@ -1185,7 +1188,7 @@ const generateByteLengthProperty = (
1185
1188
 
1186
1189
  // Get type info for the resolved identifier
1187
1190
  const typeInfo = ctx.resolvedIdentifier
1188
- ? input.typeRegistry.get(ctx.resolvedIdentifier)
1191
+ ? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
1189
1192
  : undefined;
1190
1193
 
1191
1194
  if (!typeInfo) {
@@ -1251,10 +1254,10 @@ const generateStructFieldElementCount = (
1251
1254
  */
1252
1255
  const generateTypeInfoElementCount = (
1253
1256
  ctx: IExplicitLengthContext,
1254
- input: IGeneratorInput,
1257
+ _input: IGeneratorInput,
1255
1258
  ): string => {
1256
1259
  const typeInfo = ctx.resolvedIdentifier
1257
- ? input.typeRegistry.get(ctx.resolvedIdentifier)
1260
+ ? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
1258
1261
  : undefined;
1259
1262
 
1260
1263
  if (!typeInfo) {
@@ -1350,7 +1353,7 @@ const generateCharCountProperty = (
1350
1353
 
1351
1354
  // Get type info
1352
1355
  const typeInfo = ctx.resolvedIdentifier
1353
- ? input.typeRegistry.get(ctx.resolvedIdentifier)
1356
+ ? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
1354
1357
  : undefined;
1355
1358
 
1356
1359
  if (!typeInfo) {
@@ -1484,7 +1487,7 @@ const tryBitmapFieldAccess = (
1484
1487
  if (!ctx.rootIdentifier) {
1485
1488
  return null;
1486
1489
  }
1487
- const typeInfo = input.typeRegistry.get(ctx.rootIdentifier);
1490
+ const typeInfo = CodeGenState.getVariableTypeInfo(ctx.rootIdentifier);
1488
1491
  if (!typeInfo?.isBitmap || !typeInfo.bitmapTypeName) {
1489
1492
  return null;
1490
1493
  }
@@ -1525,7 +1528,7 @@ const tryScopeMemberAccess = (
1525
1528
  output.result = fullName;
1526
1529
  output.resolvedIdentifier = fullName;
1527
1530
  if (!input.symbols!.knownEnums.has(fullName)) {
1528
- const resolvedTypeInfo = input.typeRegistry.get(fullName);
1531
+ const resolvedTypeInfo = CodeGenState.getVariableTypeInfo(fullName);
1529
1532
  if (
1530
1533
  resolvedTypeInfo &&
1531
1534
  orchestrator.isKnownStruct(resolvedTypeInfo.baseType)
@@ -1569,7 +1572,7 @@ const tryKnownScopeAccess = (
1569
1572
  const output = initializeMemberOutput(ctx);
1570
1573
  output.result = `${ctx.result}${orchestrator.getScopeSeparator(ctx.isCppAccessChain)}${ctx.memberName}`;
1571
1574
  output.resolvedIdentifier = output.result;
1572
- const resolvedTypeInfo = input.typeRegistry.get(output.result);
1575
+ const resolvedTypeInfo = CodeGenState.getVariableTypeInfo(output.result);
1573
1576
  if (
1574
1577
  resolvedTypeInfo &&
1575
1578
  orchestrator.isKnownStruct(resolvedTypeInfo.baseType)
@@ -1920,11 +1923,11 @@ const checkRegisterAccess = (
1920
1923
  */
1921
1924
  const getIdentifierTypeInfo = (
1922
1925
  ctx: ISubscriptAccessContext,
1923
- input: IGeneratorInput,
1926
+ _input: IGeneratorInput,
1924
1927
  ): TTypeInfo | undefined => {
1925
1928
  const identifierToCheck = ctx.resolvedIdentifier || ctx.rootIdentifier;
1926
1929
  return identifierToCheck
1927
- ? input.typeRegistry.get(identifierToCheck)
1930
+ ? CodeGenState.getVariableTypeInfo(identifierToCheck)
1928
1931
  : undefined;
1929
1932
  };
1930
1933
 
@@ -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", () => {