c-next 0.2.16 → 0.2.17

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 (56) hide show
  1. package/README.md +16 -0
  2. package/dist/index.js +1347 -414
  3. package/dist/index.js.map +3 -3
  4. package/grammar/CNext.g4 +4 -0
  5. package/package.json +5 -1
  6. package/src/transpiler/Transpiler.ts +90 -22
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
  9. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  10. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  11. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  12. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  13. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  14. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
  15. package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
  16. package/src/transpiler/logic/symbols/SymbolTable.ts +17 -2
  17. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +6 -4
  18. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  19. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  20. package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
  21. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
  22. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
  23. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  24. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
  25. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
  26. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
  27. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  28. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
  29. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  30. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  31. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  32. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  33. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  34. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
  35. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  36. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  37. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  38. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  39. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  40. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  41. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
  42. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  43. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  44. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  45. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  46. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  47. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  48. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
  49. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  50. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  51. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  52. package/src/transpiler/state/CodeGenState.ts +71 -6
  53. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  54. package/src/utils/LiteralUtils.ts +23 -0
  55. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  56. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -6,14 +6,16 @@ import * as Parser from "../../../parser/grammar/CNextParser";
6
6
  import CNEXT_TO_C_TYPE_MAP from "../../../../../utils/constants/TypeMappings";
7
7
 
8
8
  /**
9
- * Resolve scoped type (this.Type) to full name.
9
+ * Common interface for type contexts that share the same type accessors.
10
+ * Both TypeContext and ArrayTypeContext have these methods.
10
11
  */
11
- function resolveScopedType(
12
- scopedTypeCtx: Parser.ScopedTypeContext,
13
- scopeName?: string,
14
- ): string {
15
- const typeName = scopedTypeCtx.IDENTIFIER().getText();
16
- return scopeName ? `${scopeName}_${typeName}` : typeName;
12
+ interface ITypeAccessors {
13
+ primitiveType(): Parser.PrimitiveTypeContext | null;
14
+ userType(): Parser.UserTypeContext | null;
15
+ stringType(): Parser.StringTypeContext | null;
16
+ scopedType(): Parser.ScopedTypeContext | null;
17
+ qualifiedType(): Parser.QualifiedTypeContext | null;
18
+ globalType(): Parser.GlobalTypeContext | null;
17
19
  }
18
20
 
19
21
  /**
@@ -25,19 +27,50 @@ function resolveStringType(stringCtx: Parser.StringTypeContext): string {
25
27
  }
26
28
 
27
29
  /**
28
- * Extract inner type from arrayType context.
30
+ * Dispatch type resolution for contexts that share common type accessors.
31
+ * Handles scoped, qualified, global, primitive, string, and user types.
32
+ * Used by both bare type contexts and array element type contexts.
33
+ *
34
+ * @returns The resolved type name, or null if no matching type accessor found
29
35
  */
30
- function resolveArrayInnerType(arrayTypeCtx: Parser.ArrayTypeContext): string {
31
- if (arrayTypeCtx.primitiveType()) {
32
- return arrayTypeCtx.primitiveType()!.getText();
36
+ function dispatchTypeResolution(
37
+ accessors: ITypeAccessors,
38
+ scopeName?: string,
39
+ ): string | null {
40
+ // Handle this.Type for scoped types (e.g., this.State -> Motor_State)
41
+ if (accessors.scopedType()) {
42
+ const typeName = accessors.scopedType()!.IDENTIFIER().getText();
43
+ return scopeName ? `${scopeName}_${typeName}` : typeName;
44
+ }
45
+
46
+ // Handle global.Type for global types inside scope
47
+ // global.ECategory -> ECategory (just the type name, no scope prefix)
48
+ if (accessors.globalType()) {
49
+ return accessors.globalType()!.IDENTIFIER().getText();
33
50
  }
34
- if (arrayTypeCtx.userType()) {
35
- return arrayTypeCtx.userType()!.getText();
51
+
52
+ // Handle Scope.Type from outside scope (e.g., Motor.State -> Motor_State)
53
+ if (accessors.qualifiedType()) {
54
+ const identifiers = accessors.qualifiedType()!.IDENTIFIER();
55
+ return identifiers.map((id) => id.getText()).join("_");
56
+ }
57
+
58
+ // Handle user-defined types
59
+ if (accessors.userType()) {
60
+ return accessors.userType()!.getText();
36
61
  }
37
- // Fallback for other nested types - strip the dimension part
38
- const text = arrayTypeCtx.getText();
39
- const bracketIdx = text.indexOf("[");
40
- return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
62
+
63
+ // Handle primitive types
64
+ if (accessors.primitiveType()) {
65
+ return accessors.primitiveType()!.getText();
66
+ }
67
+
68
+ // Handle string types - preserve capacity for validation (Issue #139)
69
+ if (accessors.stringType()) {
70
+ return resolveStringType(accessors.stringType()!);
71
+ }
72
+
73
+ return null;
41
74
  }
42
75
 
43
76
  class TypeUtils {
@@ -56,42 +89,23 @@ class TypeUtils {
56
89
  ): string {
57
90
  if (!ctx) return "void";
58
91
 
59
- // Handle this.Type for scoped types (e.g., this.State -> Motor_State)
60
- if (ctx.scopedType()) {
61
- return resolveScopedType(ctx.scopedType()!, scopeName);
62
- }
63
-
64
- // Issue #478: Handle global.Type for global types inside scope
65
- // global.ECategory -> ECategory (just the type name, no scope prefix)
66
- if (ctx.globalType()) {
67
- return ctx.globalType()!.IDENTIFIER().getText();
68
- }
69
-
70
- // Handle Scope.Type from outside scope (e.g., Motor.State -> Motor_State)
71
- if (ctx.qualifiedType()) {
72
- const identifiers = ctx.qualifiedType()!.IDENTIFIER();
73
- return identifiers.map((id) => id.getText()).join("_");
74
- }
75
-
76
- // Handle user-defined types
77
- if (ctx.userType()) {
78
- return ctx.userType()!.getText();
79
- }
80
-
81
- // Handle primitive types
82
- if (ctx.primitiveType()) {
83
- return ctx.primitiveType()!.getText();
84
- }
85
-
86
- // Handle string types - preserve capacity for validation (Issue #139)
87
- if (ctx.stringType()) {
88
- return resolveStringType(ctx.stringType()!);
89
- }
90
-
91
92
  // Handle arrayType: Type[size] - extract the inner type without dimension
92
93
  // The dimension is tracked separately in arrayDimensions
93
94
  if (ctx.arrayType()) {
94
- return resolveArrayInnerType(ctx.arrayType()!);
95
+ const result = dispatchTypeResolution(ctx.arrayType()!, scopeName);
96
+ if (result !== null) {
97
+ return result;
98
+ }
99
+ // Fallback for unrecognized array types - strip the dimension part
100
+ const text = ctx.arrayType()!.getText();
101
+ const bracketIdx = text.indexOf("[");
102
+ return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
103
+ }
104
+
105
+ // Non-array types - dispatch directly
106
+ const result = dispatchTypeResolution(ctx, scopeName);
107
+ if (result !== null) {
108
+ return result;
95
109
  }
96
110
 
97
111
  // Fallback
@@ -726,6 +726,7 @@ export default class CodeGenerator implements IOrchestrator {
726
726
  * Part of IOrchestrator interface.
727
727
  * ADR-045: Used to detect string comparisons and generate strcmp().
728
728
  * Issue #137: Extended to handle array element access (e.g., names[0])
729
+ * Issue #1030: Extended to handle struct member access (e.g., person.name)
729
730
  */
730
731
  isStringExpression(ctx: Parser.RelationalExpressionContext): boolean {
731
732
  const text = ctx.getText();
@@ -743,6 +744,11 @@ export default class CodeGenerator implements IOrchestrator {
743
744
  }
744
745
  }
745
746
 
747
+ // Issue #1030: Check for struct member access (e.g., person.name)
748
+ if (this._isStructMemberStringExpression(text)) {
749
+ return true;
750
+ }
751
+
746
752
  // Issue #137: Check for array element access (e.g., names[0], arr[i])
747
753
  return this._isArrayAccessStringExpression(text);
748
754
  }
@@ -800,6 +806,59 @@ export default class CodeGenerator implements IOrchestrator {
800
806
  );
801
807
  }
802
808
 
809
+ /**
810
+ * Check if struct member access expression evaluates to a string.
811
+ * Issue #1030: Handles patterns like person.name, config.key
812
+ */
813
+ private _isStructMemberStringExpression(text: string): boolean {
814
+ // Pattern: identifier.identifier (simple member access)
815
+ // Must not end with a property that returns a number
816
+ if (
817
+ text.endsWith(".char_count") ||
818
+ text.endsWith(".capacity") ||
819
+ text.endsWith(".size") ||
820
+ text.endsWith(".length") ||
821
+ text.endsWith(".bit_length") ||
822
+ text.endsWith(".byte_length") ||
823
+ text.endsWith(".element_count")
824
+ ) {
825
+ return false;
826
+ }
827
+
828
+ // Match simple struct.member pattern
829
+ const memberMatch = /^([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)$/.exec(text);
830
+ if (!memberMatch) {
831
+ return false;
832
+ }
833
+
834
+ const [, varName, fieldName] = memberMatch;
835
+
836
+ // Get the struct variable's type
837
+ const typeInfo = CodeGenState.getVariableTypeInfo(varName);
838
+ if (!typeInfo) {
839
+ return false;
840
+ }
841
+
842
+ // Get the struct type name - it might be directly the baseType
843
+ // or we might need to look it up by the variable's type
844
+ const structTypeName = typeInfo.baseType;
845
+ if (!structTypeName) {
846
+ return false;
847
+ }
848
+
849
+ // Look up the field type from the struct
850
+ const fieldType = CodeGenState.getStructFieldType(
851
+ structTypeName,
852
+ fieldName,
853
+ );
854
+ if (!fieldType) {
855
+ return false;
856
+ }
857
+
858
+ // Check if the field is a string type (e.g., "string<64>")
859
+ return fieldType.startsWith("string");
860
+ }
861
+
803
862
  /**
804
863
  * Get type of additive expression.
805
864
  * Part of IOrchestrator interface - delegates to private implementation.
@@ -1311,9 +1370,10 @@ export default class CodeGenerator implements IOrchestrator {
1311
1370
  * ADR-017: Handle enum types by initializing to first member
1312
1371
  */
1313
1372
  getZeroInitializer(typeCtx: Parser.TypeContext, isArray: boolean): string {
1314
- // Issue #379: Arrays need element type checking for C++ classes
1373
+ // Issue #379 / #1004: arrays zero-init with the aggregate brace ({} in
1374
+ // C++, {0} in C) regardless of element type.
1315
1375
  if (isArray) {
1316
- return this._getArrayZeroInitializer(typeCtx);
1376
+ return this._getAggregateZeroInitBrace();
1317
1377
  }
1318
1378
 
1319
1379
  // Handle named types (scoped, global, qualified, user)
@@ -1323,11 +1383,10 @@ export default class CodeGenerator implements IOrchestrator {
1323
1383
  if (CodeGenState.symbols!.knownEnums.has(resolved.name)) {
1324
1384
  return this._getEnumZeroValue(resolved.name, resolved.separator);
1325
1385
  }
1326
- // Check if C++ type needing {} (only for userType, not qualified/scoped/global)
1327
- if (resolved.checkCppType && this._needsEmptyBraceInit(resolved.name)) {
1328
- return "{}";
1329
- }
1330
- return "{0}";
1386
+ // Issue #1004: struct/class zero-init. C++ value-initialization ({})
1387
+ // works for every aggregate (including ones whose first field is an
1388
+ // enum, where {0} is an invalid int->enum narrowing); C uses {0}.
1389
+ return this._getAggregateZeroInitBrace();
1331
1390
  }
1332
1391
 
1333
1392
  // Issue #295: C++ template types use value initialization {}
@@ -1335,6 +1394,11 @@ export default class CodeGenerator implements IOrchestrator {
1335
1394
  return "{}";
1336
1395
  }
1337
1396
 
1397
+ // Issue #1019: string<N> types use empty string initializer
1398
+ if (typeCtx.stringType()) {
1399
+ return '""';
1400
+ }
1401
+
1338
1402
  // Primitive types use lookup map
1339
1403
  if (typeCtx.primitiveType()) {
1340
1404
  const primType = typeCtx.primitiveType()!.getText();
@@ -1490,9 +1554,9 @@ export default class CodeGenerator implements IOrchestrator {
1490
1554
  if (p.isArray) {
1491
1555
  // Array parameters: type name[]
1492
1556
  return `${constMod}${p.type} ${p.name}${p.arrayDims}`;
1493
- } else if (p.isPointer) {
1494
- // ADR-006: Non-array, non-callback parameters become pointers
1495
- // In C++ mode, use reference (&) instead of pointer (*)
1557
+ } else if (p.isStruct) {
1558
+ // ADR-006: Struct parameters become pointers (C) or references (C++)
1559
+ // Only struct types get pointer/reference semantics, not primitives
1496
1560
  const ptrOrRef = this.isCppMode() ? "&" : "*";
1497
1561
  return `${constMod}${p.type}${ptrOrRef}`;
1498
1562
  } else {
@@ -1776,7 +1840,17 @@ export default class CodeGenerator implements IOrchestrator {
1776
1840
  }
1777
1841
 
1778
1842
  if (ctx.IDENTIFIER()) {
1779
- return this._resolveIdentifierExpression(ctx.IDENTIFIER()!.getText());
1843
+ const id = ctx.IDENTIFIER()!.getText();
1844
+ // Issue #1011: break/continue are not part of C-Next - use structured conditions
1845
+ // ADR-026 (Status: Rejected) explicitly excludes break/continue from the language
1846
+ if (id === "break" || id === "continue") {
1847
+ const line = ctx.start?.line ?? 0;
1848
+ const col = ctx.start?.column ?? 0;
1849
+ throw new Error(
1850
+ `${line}:${col} error[E0703]: '${id}' is not supported in C-Next - use structured conditions instead`,
1851
+ );
1852
+ }
1853
+ return this._resolveIdentifierExpression(id);
1780
1854
  }
1781
1855
  if (ctx.literal()) {
1782
1856
  return this._generateLiteralExpression(ctx.literal()!);
@@ -2085,15 +2159,6 @@ export default class CodeGenerator implements IOrchestrator {
2085
2159
  return identifiers.join("_");
2086
2160
  }
2087
2161
 
2088
- /**
2089
- * Issue #304: Check if a type name is from a C++ header
2090
- * Used to determine whether to use {} or {0} for initialization.
2091
- * C++ types with constructors may fail with {0} but work with {}.
2092
- */
2093
- private isCppType(typeName: string): boolean {
2094
- return SymbolLookupHelper.isCppType(CodeGenState.symbolTable, typeName);
2095
- }
2096
-
2097
2162
  /**
2098
2163
  * Generate C code from a C-Next program
2099
2164
  * @param tree The parsed C-Next program
@@ -2689,6 +2754,7 @@ export default class CodeGenerator implements IOrchestrator {
2689
2754
  type: string;
2690
2755
  isConst: boolean;
2691
2756
  isPointer: boolean;
2757
+ isStruct: boolean;
2692
2758
  isArray: boolean;
2693
2759
  arrayDims: string;
2694
2760
  }> = [];
@@ -2704,6 +2770,8 @@ export default class CodeGenerator implements IOrchestrator {
2704
2770
 
2705
2771
  // ADR-029: Check if parameter type is itself a callback type
2706
2772
  const isCallbackParam = CodeGenState.callbackTypes.has(typeName);
2773
+ // ADR-006: Check if parameter type is a struct (for pointer/reference semantics)
2774
+ const isStruct = this.isStructType(typeName);
2707
2775
 
2708
2776
  let paramType: string;
2709
2777
  let isPointer: boolean;
@@ -2715,8 +2783,8 @@ export default class CodeGenerator implements IOrchestrator {
2715
2783
  isPointer = false; // Function pointers are already pointers
2716
2784
  } else {
2717
2785
  paramType = this.generateType(param.type());
2718
- // ADR-006: Non-array parameters become pointers
2719
- isPointer = !isArray;
2786
+ // ADR-006: Non-array struct parameters become pointers in C mode
2787
+ isPointer = !isArray && isStruct;
2720
2788
  }
2721
2789
 
2722
2790
  let arrayDims: string;
@@ -2739,6 +2807,7 @@ export default class CodeGenerator implements IOrchestrator {
2739
2807
  type: paramType,
2740
2808
  isConst,
2741
2809
  isPointer,
2810
+ isStruct,
2742
2811
  isArray,
2743
2812
  arrayDims,
2744
2813
  });
@@ -3211,7 +3280,7 @@ export default class CodeGenerator implements IOrchestrator {
3211
3280
  decl += this._getStringCapacityDimension(varDecl.type());
3212
3281
 
3213
3282
  if (varDecl.expression()) {
3214
- decl += ` = ${this.generateExpression(varDecl.expression()!)}`;
3283
+ decl += ` = ${CodeGenState.withDeclarationInit(() => this.generateExpression(varDecl.expression()!))}`;
3215
3284
  } else {
3216
3285
  // ADR-015: Zero initialization for uninitialized scope variables
3217
3286
  decl += ` = ${this.getZeroInitializer(varDecl.type(), isArray)}`;
@@ -3495,8 +3564,9 @@ export default class CodeGenerator implements IOrchestrator {
3495
3564
  );
3496
3565
 
3497
3566
  if (!fieldList) {
3498
- // Empty initializer: Point {} -> (Point){ 0 } or {} for C++ classes
3499
- return isCppClass ? "{}" : `(${castType}){ 0 }`;
3567
+ // Empty initializer: Point {} -> { 0 } in declaration context, (Point){ 0 } elsewhere
3568
+ if (isCppClass) return "{}";
3569
+ return CodeGenState.inDeclarationInit ? "{ 0 }" : `(${castType}){ 0 }`;
3500
3570
  }
3501
3571
 
3502
3572
  // Get field type info for nested initializers
@@ -3507,28 +3577,10 @@ export default class CodeGenerator implements IOrchestrator {
3507
3577
 
3508
3578
  const fields = fieldList.fieldInitializer().map((field) => {
3509
3579
  const fieldName = field.IDENTIFIER().getText();
3510
-
3511
- // Compute expected type for nested initializers
3512
- let fieldType: string | undefined;
3513
- if (structFieldTypes?.has(fieldName)) {
3514
- // Issue #502: Convert underscore format to correct output format
3515
- // C-Next struct fields may store C++ types with _ separator (e.g., SeaDash_Parse_ParseResult)
3516
- // but code generation needs :: for C++ types (e.g., SeaDash::Parse::ParseResult)
3517
- fieldType = structFieldTypes.get(fieldName)!;
3518
- if (fieldType.includes("_")) {
3519
- // Check if this looks like a qualified type (contains _) and convert
3520
- const parts = fieldType.split("_");
3521
- if (parts.length > 1 && this.isCppScopeSymbol(parts[0])) {
3522
- // It's a C++ namespaced type - convert _ to ::
3523
- fieldType = parts.join("::");
3524
- }
3525
- }
3526
- }
3527
-
3580
+ const fieldType = this._resolveFieldType(fieldName, structFieldTypes);
3528
3581
  const value = CodeGenState.withExpectedType(fieldType, () =>
3529
3582
  this.generateExpression(field.expression()),
3530
3583
  );
3531
-
3532
3584
  return { fieldName, value };
3533
3585
  });
3534
3586
 
@@ -3545,6 +3597,23 @@ export default class CodeGenerator implements IOrchestrator {
3545
3597
  // For C-Next/C structs, generate designated initializer
3546
3598
  const fieldInits = fields.map((f) => `.${f.fieldName} = ${f.value}`);
3547
3599
 
3600
+ return this.formatStructInitializer(typeName, castType, fieldInits);
3601
+ }
3602
+
3603
+ private formatStructInitializer(
3604
+ typeName: string,
3605
+ castType: string,
3606
+ fieldInits: string[],
3607
+ ): string {
3608
+ const initializer: string = `{ ${fieldInits.join(", ")} }`;
3609
+
3610
+ // In a declaration initializer context, use plain designated initializer — no type cast
3611
+ // prefix needed, and compound literals are not C99 constant expressions so they fail
3612
+ // at file scope on GCC < 13.
3613
+ if (CodeGenState.inDeclarationInit) {
3614
+ return initializer;
3615
+ }
3616
+
3548
3617
  // Issue #882: In C++ mode, anonymous structs/unions must use plain brace init.
3549
3618
  // Compound literals like (struct { ... }){ ... } create incompatible types in C++
3550
3619
  // because each struct { ... } definition creates a distinct nominal type.
@@ -3552,10 +3621,33 @@ export default class CodeGenerator implements IOrchestrator {
3552
3621
  CodeGenState.cppMode &&
3553
3622
  (typeName.startsWith("struct {") || typeName.startsWith("union {"))
3554
3623
  ) {
3555
- return `{ ${fieldInits.join(", ")} }`;
3624
+ return initializer;
3556
3625
  }
3557
3626
 
3558
- return `(${castType}){ ${fieldInits.join(", ")} }`;
3627
+ if (!CodeGenState.inFunctionBody) {
3628
+ return initializer;
3629
+ }
3630
+
3631
+ return `(${castType})${initializer}`;
3632
+ }
3633
+
3634
+ /**
3635
+ * Resolve the C type string for a named struct field, converting C++ underscore-separated
3636
+ * names to :: notation. Returns undefined if the field is not in the type map.
3637
+ * Issue #502: C-Next stores C++ types with _ separator; codegen needs ::.
3638
+ */
3639
+ private _resolveFieldType(
3640
+ fieldName: string,
3641
+ structFieldTypes: Map<string, string> | undefined,
3642
+ ): string | undefined {
3643
+ if (!structFieldTypes?.has(fieldName)) return undefined;
3644
+ const fieldType = structFieldTypes.get(fieldName)!;
3645
+ if (!fieldType.includes("_")) return fieldType;
3646
+ const parts = fieldType.split("_");
3647
+ if (parts.length > 1 && this.isCppScopeSymbol(parts[0])) {
3648
+ return parts.join("::");
3649
+ }
3650
+ return fieldType;
3559
3651
  }
3560
3652
 
3561
3653
  /**
@@ -3776,6 +3868,8 @@ export default class CodeGenerator implements IOrchestrator {
3776
3868
  forceConst,
3777
3869
  isTypedefStructType: (t) =>
3778
3870
  CodeGenState.symbolTable?.isTypedefStructType(t) ?? false,
3871
+ // Issue #995: Opaque handles should not get auto-const
3872
+ isOpaqueType: (t) => CodeGenState.isOpaqueType(t),
3779
3873
  });
3780
3874
 
3781
3875
  // Use shared builder with C/C++ mode
@@ -4102,31 +4196,13 @@ export default class CodeGenerator implements IOrchestrator {
4102
4196
  // and _generateConstructorDecl have been extracted to VariableDeclHelper.ts
4103
4197
 
4104
4198
  /**
4105
- * Get zero initializer for array types.
4106
- * Issue #379: C++ class arrays must use {} instead of {0}
4199
+ * Brace initializer that zero-initializes an aggregate (struct or array).
4200
+ * Issue #379 / #1004: C++ uses value-initialization ({}), which is valid for
4201
+ * any aggregate element type (POD, struct, class) including enum-first
4202
+ * structs where {0} is an invalid int->enum narrowing; C uses {0}.
4107
4203
  */
4108
- private _getArrayZeroInitializer(typeCtx: Parser.TypeContext): string {
4109
- // Check if element type is a C++ class or template type
4110
- if (typeCtx.userType()) {
4111
- const typeName = typeCtx.userType()!.getText();
4112
- if (this._needsEmptyBraceInit(typeName)) {
4113
- return "{}";
4114
- }
4115
- }
4116
- // Also check C-Next style array type (e.g., CppClass[4]) where
4117
- // the userType is nested inside arrayType.
4118
- if (typeCtx.arrayType()?.userType()) {
4119
- const typeName = typeCtx.arrayType()!.userType()!.getText();
4120
- if (this._needsEmptyBraceInit(typeName)) {
4121
- return "{}";
4122
- }
4123
- }
4124
- // Template types are always C++ classes
4125
- if (typeCtx.templateType()) {
4126
- return "{}";
4127
- }
4128
- // Default: POD arrays use {0}
4129
- return "{0}";
4204
+ private _getAggregateZeroInitBrace(): string {
4205
+ return CodeGenState.cppMode ? "{}" : "{0}";
4130
4206
  }
4131
4207
 
4132
4208
  /**
@@ -4158,20 +4234,19 @@ export default class CodeGenerator implements IOrchestrator {
4158
4234
 
4159
4235
  /**
4160
4236
  * Resolve full type name from any TypeContext variant.
4161
- * Returns { name, separator, checkCppType } or null if not a named type.
4237
+ * Returns { name, separator } or null if not a named type.
4162
4238
  * ADR-016: Handles scoped, global, qualified, and user types
4163
- * checkCppType: only true for userType (original behavior preserved)
4164
4239
  */
4165
4240
  private _resolveTypeNameFromContext(
4166
4241
  typeCtx: Parser.TypeContext,
4167
- ): { name: string; separator: string; checkCppType: boolean } | null {
4242
+ ): { name: string; separator: string } | null {
4168
4243
  // ADR-016: Check for scoped types (this.Type)
4169
4244
  if (typeCtx.scopedType()) {
4170
4245
  const localName = typeCtx.scopedType()!.IDENTIFIER().getText();
4171
4246
  const name = CodeGenState.currentScope
4172
4247
  ? `${CodeGenState.currentScope}_${localName}`
4173
4248
  : localName;
4174
- return { name, separator: "_", checkCppType: false };
4249
+ return { name, separator: "_" };
4175
4250
  }
4176
4251
 
4177
4252
  // Issue #478: Check for global types (global.Type)
@@ -4179,7 +4254,6 @@ export default class CodeGenerator implements IOrchestrator {
4179
4254
  return {
4180
4255
  name: typeCtx.globalType()!.IDENTIFIER().getText(),
4181
4256
  separator: "_",
4182
- checkCppType: false,
4183
4257
  };
4184
4258
  }
4185
4259
 
@@ -4189,7 +4263,7 @@ export default class CodeGenerator implements IOrchestrator {
4189
4263
  const parts = typeCtx.qualifiedType()!.IDENTIFIER();
4190
4264
  const name = this.resolveQualifiedType(parts.map((id) => id.getText()));
4191
4265
  const separator = name.includes("::") ? "::" : "_";
4192
- return { name, separator, checkCppType: false };
4266
+ return { name, separator };
4193
4267
  }
4194
4268
 
4195
4269
  // Check for user-defined types (structs/classes/enums)
@@ -4197,28 +4271,12 @@ export default class CodeGenerator implements IOrchestrator {
4197
4271
  return {
4198
4272
  name: typeCtx.userType()!.getText(),
4199
4273
  separator: "_",
4200
- checkCppType: true,
4201
4274
  };
4202
4275
  }
4203
4276
 
4204
4277
  return null;
4205
4278
  }
4206
4279
 
4207
- /**
4208
- * Check if a type needs empty brace initialization {}.
4209
- * Issue #304: C++ types with constructors may fail with {0}
4210
- * Issue #309: Unknown user types in C++ mode may have non-trivial constructors
4211
- */
4212
- private _needsEmptyBraceInit(typeName: string): boolean {
4213
- // C++ types (external libraries with constructors)
4214
- if (this.isCppType(typeName)) {
4215
- return true;
4216
- }
4217
- // In C++ mode, unknown user types may have non-trivial constructors
4218
- // Known structs (C-Next or C headers) are POD types where {0} works
4219
- return CodeGenState.cppMode && !this.isKnownStruct(typeName);
4220
- }
4221
-
4222
4280
  /**
4223
4281
  * Generate a safe bit mask expression.
4224
4282
  * Avoids undefined behavior when width >= 32 for 32-bit integers.
@@ -4750,8 +4808,7 @@ export default class CodeGenerator implements IOrchestrator {
4750
4808
  // MISRA 10.3: Cast limit macros to target type (they have type 'int')
4751
4809
  const finalCast = CppModeHelper.cast(targetType, `(${expr})`);
4752
4810
  const castMax = CppModeHelper.cast(targetType, maxValue);
4753
- const castMin =
4754
- minValue === "0" ? "0" : CppModeHelper.cast(targetType, minValue);
4811
+ const castMin = CppModeHelper.cast(targetType, minValue);
4755
4812
  return `((${expr}) > ${maxComparison} ? ${castMax} : (${expr}) < ${minComparison} ? ${castMin} : ${finalCast})`;
4756
4813
  }
4757
4814