c-next 0.2.16 → 0.2.18
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.
- package/README.md +18 -2
- package/dist/index.js +8897 -6260
- package/dist/index.js.map +4 -4
- package/grammar/CNext.g4 +12 -0
- package/package.json +4 -2
- package/src/transpiler/Transpiler.ts +376 -48
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
- package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
- package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
- package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
- package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
- package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
- package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
- package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
- package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
- package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
- package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
- package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
- package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
- package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
- package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
- package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
- package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
- package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
- package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
- package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
- package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
- package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
- package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
- package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +91 -22
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/transpiler/types/ICachedFileEntry.ts +7 -0
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/cache/CacheManager.ts +13 -2
- package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
- package/src/utils/constants/TypeConstants.ts +13 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -126,6 +126,8 @@ function createMockVariableDecl(options: {
|
|
|
126
126
|
name: string;
|
|
127
127
|
type: string;
|
|
128
128
|
isConst?: boolean;
|
|
129
|
+
isAtomic?: boolean;
|
|
130
|
+
isVolatile?: boolean;
|
|
129
131
|
initialValue?: string;
|
|
130
132
|
arrayDims?: string[];
|
|
131
133
|
hasStringType?: boolean;
|
|
@@ -142,6 +144,10 @@ function createMockVariableDecl(options: {
|
|
|
142
144
|
options.arrayTypeSize,
|
|
143
145
|
),
|
|
144
146
|
constModifier: () => createMockConstModifier(options.isConst ?? false),
|
|
147
|
+
// Issue #998: atomic and volatile modifiers for scope variables
|
|
148
|
+
// Uses VariableModifierBuilder for consistent handling
|
|
149
|
+
atomicModifier: () => (options.isAtomic ? ({} as unknown) : null),
|
|
150
|
+
volatileModifier: () => (options.isVolatile ? ({} as unknown) : null),
|
|
145
151
|
expression: () =>
|
|
146
152
|
options.initialValue ? createMockExpression(options.initialValue) : null,
|
|
147
153
|
arrayDimension: () =>
|
|
@@ -772,6 +778,86 @@ describe("ScopeGenerator", () => {
|
|
|
772
778
|
expect(result.code).toContain("static Point Canvas_point = 0;");
|
|
773
779
|
expect(result.code).not.toContain("Point*");
|
|
774
780
|
});
|
|
781
|
+
|
|
782
|
+
it("generates atomic variable with volatile modifier (Issue #998)", () => {
|
|
783
|
+
const varDecl = createMockVariableDecl({
|
|
784
|
+
name: "counter",
|
|
785
|
+
type: "u32",
|
|
786
|
+
isAtomic: true,
|
|
787
|
+
initialValue: "0",
|
|
788
|
+
});
|
|
789
|
+
const member = createMockScopeMember({ variableDecl: varDecl });
|
|
790
|
+
const ctx = createMockScopeContext("ISR", [member]);
|
|
791
|
+
const input = createMockInput();
|
|
792
|
+
const state = createMockState();
|
|
793
|
+
const orchestrator = createMockOrchestrator();
|
|
794
|
+
|
|
795
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
796
|
+
|
|
797
|
+
expect(result.code).toContain(
|
|
798
|
+
"static volatile uint32_t ISR_counter = 0;",
|
|
799
|
+
);
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
it("generates public atomic variable with volatile but without static (Issue #998)", () => {
|
|
803
|
+
const varDecl = createMockVariableDecl({
|
|
804
|
+
name: "flag",
|
|
805
|
+
type: "bool",
|
|
806
|
+
isAtomic: true,
|
|
807
|
+
initialValue: "false",
|
|
808
|
+
});
|
|
809
|
+
const member = createMockScopeMember({
|
|
810
|
+
visibility: "public",
|
|
811
|
+
variableDecl: varDecl,
|
|
812
|
+
});
|
|
813
|
+
const ctx = createMockScopeContext("Shared", [member]);
|
|
814
|
+
const input = createMockInput();
|
|
815
|
+
const state = createMockState();
|
|
816
|
+
const orchestrator = createMockOrchestrator();
|
|
817
|
+
|
|
818
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
819
|
+
|
|
820
|
+
expect(result.code).toContain("volatile bool Shared_flag = false;");
|
|
821
|
+
expect(result.code).not.toContain("static volatile bool Shared_flag");
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
it("generates volatile-keyword variable with volatile modifier (Issue #998)", () => {
|
|
825
|
+
const varDecl = createMockVariableDecl({
|
|
826
|
+
name: "reg",
|
|
827
|
+
type: "u8",
|
|
828
|
+
isVolatile: true,
|
|
829
|
+
initialValue: "0",
|
|
830
|
+
});
|
|
831
|
+
const member = createMockScopeMember({ variableDecl: varDecl });
|
|
832
|
+
const ctx = createMockScopeContext("HW", [member]);
|
|
833
|
+
const input = createMockInput();
|
|
834
|
+
const state = createMockState();
|
|
835
|
+
const orchestrator = createMockOrchestrator();
|
|
836
|
+
|
|
837
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
838
|
+
|
|
839
|
+
expect(result.code).toContain("static volatile uint8_t HW_reg = 0;");
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
it("throws error when both atomic and volatile are specified (Issue #998)", () => {
|
|
843
|
+
const varDecl = createMockVariableDecl({
|
|
844
|
+
name: "bad",
|
|
845
|
+
type: "u8",
|
|
846
|
+
isAtomic: true,
|
|
847
|
+
isVolatile: true,
|
|
848
|
+
initialValue: "0",
|
|
849
|
+
startLine: 10,
|
|
850
|
+
});
|
|
851
|
+
const member = createMockScopeMember({ variableDecl: varDecl });
|
|
852
|
+
const ctx = createMockScopeContext("Test", [member]);
|
|
853
|
+
const input = createMockInput();
|
|
854
|
+
const state = createMockState();
|
|
855
|
+
const orchestrator = createMockOrchestrator();
|
|
856
|
+
|
|
857
|
+
expect(() => generateScope(ctx, input, state, orchestrator)).toThrow(
|
|
858
|
+
/Cannot use both 'atomic' and 'volatile' modifiers/,
|
|
859
|
+
);
|
|
860
|
+
});
|
|
775
861
|
});
|
|
776
862
|
|
|
777
863
|
// ========================================================================
|
|
@@ -18,6 +18,7 @@ import IGeneratorInput from "../IGeneratorInput";
|
|
|
18
18
|
import IGeneratorState from "../IGeneratorState";
|
|
19
19
|
import IOrchestrator from "../IOrchestrator";
|
|
20
20
|
import BinaryExprUtils from "./BinaryExprUtils";
|
|
21
|
+
import CodeGenState from "../../../../state/CodeGenState";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Generator context passed to child generators.
|
|
@@ -116,6 +117,7 @@ const generateAndExpr = (
|
|
|
116
117
|
* ADR-001: = becomes == in C
|
|
117
118
|
* ADR-017: Enum type safety validation
|
|
118
119
|
* ADR-045: String comparison via strcmp()
|
|
120
|
+
* Issue #1032: Clear expectedType for comparison operands
|
|
119
121
|
*/
|
|
120
122
|
const generateEqualityExpr = (
|
|
121
123
|
node: Parser.EqualityExpressionContext,
|
|
@@ -151,18 +153,14 @@ const generateEqualityExpr = (
|
|
|
151
153
|
// Generate strcmp for string comparison - needs string.h
|
|
152
154
|
effects.push({ type: "include", header: "string" });
|
|
153
155
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
input,
|
|
163
|
-
state,
|
|
164
|
-
orchestrator,
|
|
165
|
-
);
|
|
156
|
+
// Issue #1032: Clear expectedType for equality comparisons.
|
|
157
|
+
// Use CodeGenState.withoutExpectedType() to clear the global state that
|
|
158
|
+
// generators read via getState(). The passed state is not used for
|
|
159
|
+
// expectedType lookup - generators read from CodeGenState directly.
|
|
160
|
+
const [leftResult, rightResult] = CodeGenState.withoutExpectedType(() => [
|
|
161
|
+
generateRelationalExpr(exprs[0], input, state, orchestrator),
|
|
162
|
+
generateRelationalExpr(exprs[1], input, state, orchestrator),
|
|
163
|
+
]);
|
|
166
164
|
effects.push(...leftResult.effects, ...rightResult.effects);
|
|
167
165
|
|
|
168
166
|
const fullText = node.getText();
|
|
@@ -183,18 +181,29 @@ const generateEqualityExpr = (
|
|
|
183
181
|
// Issue #152: Extract operators in order from parse tree children
|
|
184
182
|
// ADR-001: C-Next uses = for equality, transpile to ==
|
|
185
183
|
const operators = orchestrator.getOperatorsFromChildren(node);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
184
|
+
|
|
185
|
+
// Issue #1032: Clear expectedType for equality comparisons.
|
|
186
|
+
// The U suffix for MISRA 7.2 compliance applies to assignments, not comparisons.
|
|
187
|
+
// Use CodeGenState.withoutExpectedType() to clear the global state that
|
|
188
|
+
// generators read via getState(). The passed state is not used for
|
|
189
|
+
// expectedType lookup - generators read from CodeGenState directly.
|
|
190
|
+
return CodeGenState.withoutExpectedType(() =>
|
|
191
|
+
accumulateBinaryExprs(
|
|
192
|
+
exprs,
|
|
193
|
+
operators,
|
|
194
|
+
"=",
|
|
195
|
+
generateRelationalExpr,
|
|
196
|
+
{ input, state, orchestrator },
|
|
197
|
+
BinaryExprUtils.mapEqualityOperator,
|
|
198
|
+
),
|
|
193
199
|
);
|
|
194
200
|
};
|
|
195
201
|
|
|
196
202
|
/**
|
|
197
203
|
* Generate C code for a relational expression.
|
|
204
|
+
* Issue #1032: Clear expectedType for comparison operands - MISRA 7.2 suffix
|
|
205
|
+
* should not apply to comparisons, only to assignments. This prevents
|
|
206
|
+
* `i32 < 0` from becoming `signedIdx < 0U` which changes comparison semantics.
|
|
198
207
|
*/
|
|
199
208
|
const generateRelationalExpr = (
|
|
200
209
|
node: Parser.RelationalExpressionContext,
|
|
@@ -210,11 +219,21 @@ const generateRelationalExpr = (
|
|
|
210
219
|
|
|
211
220
|
// Issue #152: Extract operators in order from parse tree children
|
|
212
221
|
const operators = orchestrator.getOperatorsFromChildren(node);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
222
|
+
|
|
223
|
+
// Issue #1032: Clear expectedType for relational comparisons.
|
|
224
|
+
// The U suffix for MISRA 7.2 compliance applies to assignments, not comparisons.
|
|
225
|
+
// Comparing `i32 < 0` should NOT generate `signedIdx < 0U` because that
|
|
226
|
+
// changes semantics due to C's integer promotion rules.
|
|
227
|
+
// Use CodeGenState.withoutExpectedType() to clear the global state that
|
|
228
|
+
// generators read via getState(). The passed state is not used for
|
|
229
|
+
// expectedType lookup - generators read from CodeGenState directly.
|
|
230
|
+
return CodeGenState.withoutExpectedType(() =>
|
|
231
|
+
accumulateBinaryExprs(exprs, operators, "<", generateBitwiseOrExpr, {
|
|
232
|
+
input,
|
|
233
|
+
state,
|
|
234
|
+
orchestrator,
|
|
235
|
+
}),
|
|
236
|
+
);
|
|
218
237
|
};
|
|
219
238
|
|
|
220
239
|
/**
|
|
@@ -104,6 +104,29 @@ const _parameterExpectsAddressOf = (
|
|
|
104
104
|
return paramBaseType === argType;
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Resolve an argument expression's C-Next type for the address-of decision.
|
|
109
|
+
* Prefers the expression type, then the codegen variable registry, then the C
|
|
110
|
+
* symbol table — the last covers extern C globals (e.g. an lvgl font
|
|
111
|
+
* `lv_font_montserrat_32` of type `lv_font_t`, Issue #985) whose type only
|
|
112
|
+
* header symbol collection knows. Returns null when the type can't be resolved
|
|
113
|
+
* or the argument is already an address-of expression.
|
|
114
|
+
*/
|
|
115
|
+
const _resolveArgType = (
|
|
116
|
+
e: ExpressionContext,
|
|
117
|
+
argCode: string,
|
|
118
|
+
typeInfoBaseType: string | undefined,
|
|
119
|
+
orchestrator: IOrchestrator,
|
|
120
|
+
): string | null => {
|
|
121
|
+
const exprType = orchestrator.getExpressionType(e);
|
|
122
|
+
if (exprType) return exprType;
|
|
123
|
+
if (argCode.startsWith("&")) return null;
|
|
124
|
+
if (typeInfoBaseType) return typeInfoBaseType;
|
|
125
|
+
const cSymbol = CodeGenState.symbolTable?.getCSymbol(argCode);
|
|
126
|
+
if (cSymbol?.kind === "variable" && !cSymbol.isArray) return cSymbol.type;
|
|
127
|
+
return null;
|
|
128
|
+
};
|
|
129
|
+
|
|
107
130
|
/**
|
|
108
131
|
* Generate argument code for a C/C++ function call.
|
|
109
132
|
* Handles automatic address-of (&) for struct arguments passed to pointer params.
|
|
@@ -148,25 +171,16 @@ const _generateCFunctionArg = (
|
|
|
148
171
|
return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
|
|
149
172
|
}
|
|
150
173
|
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
// Issue #322: If getExpressionType returns null (e.g., for this.member),
|
|
155
|
-
// fall back to looking up the generated code in the type registry
|
|
156
|
-
let isPointerVariable = false;
|
|
174
|
+
// Resolve the argument's type (expression type → variable registry → C symbol
|
|
175
|
+
// table for extern globals) to decide whether it needs address-of.
|
|
157
176
|
const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Issue #895 Bug B: Check if variable was inferred as a pointer
|
|
164
|
-
if (typeInfo?.isPointer) {
|
|
165
|
-
isPointerVariable = true;
|
|
166
|
-
}
|
|
177
|
+
const argType = _resolveArgType(e, argCode, typeInfo?.baseType, orchestrator);
|
|
178
|
+
// Issue #895 Bug B: a variable already inferred as a pointer must not get `&`.
|
|
179
|
+
const isPointerVariable = typeInfo?.isPointer ?? false;
|
|
167
180
|
|
|
168
181
|
// Issue #948: Check if argument is an opaque scope variable (already a pointer)
|
|
169
|
-
|
|
182
|
+
// Issue #996: ...including an element of an opaque-handle array (arr[i])
|
|
183
|
+
const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariableAccess(argCode);
|
|
170
184
|
|
|
171
185
|
// Add & if argument needs address-of to match parameter type.
|
|
172
186
|
// Issue #322: struct types passed to pointer params.
|
|
@@ -284,50 +298,54 @@ const generateFunctionCall = (
|
|
|
284
298
|
// Get function signature once for all arguments
|
|
285
299
|
const sig = input.functionSignatures.get(funcExpr);
|
|
286
300
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
);
|
|
296
|
-
const targetParam = resolved.param;
|
|
297
|
-
|
|
298
|
-
// C/C++ function: use pass-by-value semantics
|
|
299
|
-
if (!isCNextFunc) {
|
|
300
|
-
return _generateCFunctionArg(e, targetParam, input, orchestrator);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// C-Next function: check if target parameter should be passed by value
|
|
304
|
-
if (
|
|
305
|
-
_shouldPassByValue(
|
|
306
|
-
funcExpr,
|
|
301
|
+
// Issue #992: Clear inDeclarationInit for function call arguments — struct
|
|
302
|
+
// initializers inside function args need compound literals, not plain designated initializers.
|
|
303
|
+
const args = CodeGenState.withoutDeclarationInit(() =>
|
|
304
|
+
argExprs
|
|
305
|
+
.map((e, idx) => {
|
|
306
|
+
// Get parameter type info from local signature or cross-file SymbolTable
|
|
307
|
+
const resolved = CallExprUtils.resolveTargetParam(
|
|
308
|
+
sig,
|
|
307
309
|
idx,
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
orchestrator,
|
|
311
|
-
)
|
|
312
|
-
) {
|
|
313
|
-
// Issue #872: Set expectedType for MISRA 7.2 compliance, but suppress bare enum resolution
|
|
314
|
-
const argCode = CodeGenState.withExpectedType(
|
|
315
|
-
targetParam?.baseType,
|
|
316
|
-
() => orchestrator.generateExpression(e),
|
|
317
|
-
true, // suppressEnumResolution
|
|
318
|
-
);
|
|
319
|
-
return wrapWithCppEnumCast(
|
|
320
|
-
argCode,
|
|
321
|
-
e,
|
|
322
|
-
targetParam?.baseType,
|
|
323
|
-
orchestrator,
|
|
310
|
+
funcExpr,
|
|
311
|
+
input.symbolTable,
|
|
324
312
|
);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
313
|
+
const targetParam = resolved.param;
|
|
314
|
+
|
|
315
|
+
// C/C++ function: use pass-by-value semantics
|
|
316
|
+
if (!isCNextFunc) {
|
|
317
|
+
return _generateCFunctionArg(e, targetParam, input, orchestrator);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// C-Next function: check if target parameter should be passed by value
|
|
321
|
+
if (
|
|
322
|
+
_shouldPassByValue(
|
|
323
|
+
funcExpr,
|
|
324
|
+
idx,
|
|
325
|
+
targetParam,
|
|
326
|
+
resolved.isCrossFile,
|
|
327
|
+
orchestrator,
|
|
328
|
+
)
|
|
329
|
+
) {
|
|
330
|
+
// Issue #872: Set expectedType for MISRA 7.2 compliance, but suppress bare enum resolution
|
|
331
|
+
const argCode = CodeGenState.withExpectedType(
|
|
332
|
+
targetParam?.baseType,
|
|
333
|
+
() => orchestrator.generateExpression(e),
|
|
334
|
+
true, // suppressEnumResolution
|
|
335
|
+
);
|
|
336
|
+
return wrapWithCppEnumCast(
|
|
337
|
+
argCode,
|
|
338
|
+
e,
|
|
339
|
+
targetParam?.baseType,
|
|
340
|
+
orchestrator,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Target parameter is pass-by-reference: use & logic
|
|
345
|
+
return orchestrator.generateFunctionArg(e, targetParam?.baseType);
|
|
346
|
+
})
|
|
347
|
+
.join(", "),
|
|
348
|
+
);
|
|
331
349
|
|
|
332
350
|
return { code: `${funcExpr}(${args})`, effects };
|
|
333
351
|
};
|
|
@@ -15,6 +15,7 @@ import TGeneratorEffect from "../TGeneratorEffect";
|
|
|
15
15
|
import IGeneratorInput from "../IGeneratorInput";
|
|
16
16
|
import IGeneratorState from "../IGeneratorState";
|
|
17
17
|
import IOrchestrator from "../IOrchestrator";
|
|
18
|
+
import CodeGenState from "../../../../state/CodeGenState";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Generate C code for an expression (entry point).
|
|
@@ -74,9 +75,15 @@ const generateTernaryExpr = (
|
|
|
74
75
|
orchestrator.validateTernaryConditionNoFunctionCall(condition);
|
|
75
76
|
|
|
76
77
|
// Generate C output - parentheses already present from grammar
|
|
78
|
+
// Issue #992: Clear inDeclarationInit in ternary arms — struct initializers
|
|
79
|
+
// inside ternary branches need compound literals, not plain designated initializers.
|
|
77
80
|
const condCode = orchestrator.generateOrExpr(condition);
|
|
78
|
-
const trueCode =
|
|
79
|
-
|
|
81
|
+
const trueCode = CodeGenState.withoutDeclarationInit(() =>
|
|
82
|
+
orchestrator.generateOrExpr(trueExpr),
|
|
83
|
+
);
|
|
84
|
+
const falseCode = CodeGenState.withoutDeclarationInit(() =>
|
|
85
|
+
orchestrator.generateOrExpr(falseExpr),
|
|
86
|
+
);
|
|
80
87
|
|
|
81
88
|
return { code: `(${condCode}) ? ${trueCode} : ${falseCode}`, effects };
|
|
82
89
|
};
|
|
@@ -155,17 +155,14 @@ const generatePostfixExpression = (
|
|
|
155
155
|
// Issue #895: Callback-compatible params need pointer semantics even in C++ mode
|
|
156
156
|
const forcePointerSemantics = paramInfo?.forcePointerSemantics ?? false;
|
|
157
157
|
|
|
158
|
-
// Issue #
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
} else {
|
|
167
|
-
result = orchestrator.generatePrimaryExpr(primary);
|
|
168
|
-
}
|
|
158
|
+
// Issue #1100: Subscripted parameters resolve through the normal primary
|
|
159
|
+
// expression path (ParameterDereferenceResolver), same as any other
|
|
160
|
+
// parameter reference. This is a no-op for array/struct/string params
|
|
161
|
+
// (already pointer-like, so `buf[idx]` is unaffected), and correctly
|
|
162
|
+
// dereferences a scalar parameter that became a pointer because it's
|
|
163
|
+
// modified elsewhere in the function, so bit access (`v[4]`) reads
|
|
164
|
+
// through the pointer instead of pointer-indexing past it.
|
|
165
|
+
const result: string = orchestrator.generatePrimaryExpr(primary);
|
|
169
166
|
|
|
170
167
|
const primaryTypeInfo = rootIdentifier
|
|
171
168
|
? CodeGenState.getVariableTypeInfo(rootIdentifier)
|
|
@@ -1805,6 +1802,14 @@ const handleBitRangeSubscript = (
|
|
|
1805
1802
|
orchestrator.generateExpression(exprs[1]),
|
|
1806
1803
|
]);
|
|
1807
1804
|
|
|
1805
|
+
// Issue #1094: resolve a const/macro width to its numeric value so the mask is
|
|
1806
|
+
// precomputed (byte-identical to a literal width) instead of a runtime
|
|
1807
|
+
// ((1U << W) - 1) — which is UB at full width (1U << 32) and uses the wrong
|
|
1808
|
+
// base type for >32-bit widths. The "U" suffix matches the literal path, which
|
|
1809
|
+
// generates bit widths under a size_t expectedType.
|
|
1810
|
+
const widthConst = orchestrator.tryEvaluateConstant(exprs[1]);
|
|
1811
|
+
const maskWidth = widthConst === undefined ? width : `${widthConst}U`;
|
|
1812
|
+
|
|
1808
1813
|
const isFloatType =
|
|
1809
1814
|
ctx.primaryTypeInfo?.baseType === "f32" ||
|
|
1810
1815
|
ctx.primaryTypeInfo?.baseType === "f64";
|
|
@@ -1817,13 +1822,19 @@ const handleBitRangeSubscript = (
|
|
|
1817
1822
|
baseType: ctx.primaryTypeInfo!.baseType,
|
|
1818
1823
|
start,
|
|
1819
1824
|
width,
|
|
1825
|
+
maskWidth,
|
|
1820
1826
|
},
|
|
1821
1827
|
state,
|
|
1822
1828
|
orchestrator,
|
|
1823
1829
|
effects,
|
|
1824
1830
|
);
|
|
1825
1831
|
} else {
|
|
1826
|
-
|
|
1832
|
+
// Issue #1094: 64-bit operands need a 64-bit mask base (1ULL / wide hex) so
|
|
1833
|
+
// widths > 32 don't shift past a 32-bit literal's width.
|
|
1834
|
+
const is64BitOperand =
|
|
1835
|
+
ctx.primaryTypeInfo?.baseType === "u64" ||
|
|
1836
|
+
ctx.primaryTypeInfo?.baseType === "i64";
|
|
1837
|
+
const mask = orchestrator.generateBitMask(maskWidth, is64BitOperand);
|
|
1827
1838
|
// Skip shift when start is 0 (either "0" or "0U" with MISRA suffix)
|
|
1828
1839
|
let expr: string;
|
|
1829
1840
|
if (start === "0" || start === "0U") {
|
|
@@ -1861,6 +1872,8 @@ interface IFloatBitRangeContext {
|
|
|
1861
1872
|
baseType: string;
|
|
1862
1873
|
start: string;
|
|
1863
1874
|
width: string;
|
|
1875
|
+
/** Width formatted for mask generation (const-resolved when possible, #1094). */
|
|
1876
|
+
maskWidth: string;
|
|
1864
1877
|
}
|
|
1865
1878
|
|
|
1866
1879
|
/**
|
|
@@ -1892,7 +1905,7 @@ const handleFloatBitRange = (
|
|
|
1892
1905
|
const floatType = getFloatTypeName(ctx.baseType);
|
|
1893
1906
|
const intType = isF64 ? "uint64_t" : "uint32_t";
|
|
1894
1907
|
const shadowName = `__bits_${ctx.rootIdentifier}`;
|
|
1895
|
-
const mask = orchestrator.generateBitMask(ctx.
|
|
1908
|
+
const mask = orchestrator.generateBitMask(ctx.maskWidth, isF64);
|
|
1896
1909
|
|
|
1897
1910
|
const needsDeclaration = !orchestrator.hasFloatBitShadow(shadowName);
|
|
1898
1911
|
if (needsDeclaration) {
|
package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts
CHANGED
|
@@ -1477,4 +1477,48 @@ describe("CallExprGenerator", () => {
|
|
|
1477
1477
|
expect(result.code).toBe("use_handle(my_handle)");
|
|
1478
1478
|
});
|
|
1479
1479
|
});
|
|
1480
|
+
|
|
1481
|
+
describe("inDeclarationInit clearing (Issue #992)", () => {
|
|
1482
|
+
it("clears inDeclarationInit during function argument generation", () => {
|
|
1483
|
+
CodeGenState.inDeclarationInit = true;
|
|
1484
|
+
|
|
1485
|
+
const argExprs = [createMockExpressionContext("myArg")];
|
|
1486
|
+
const argCtx = createMockArgListContext(argExprs);
|
|
1487
|
+
const input = createMockInput();
|
|
1488
|
+
const state = createMockState();
|
|
1489
|
+
|
|
1490
|
+
let flagDuringArg = true;
|
|
1491
|
+
const orchestrator = createMockOrchestrator({
|
|
1492
|
+
isCNextFunction: vi.fn(() => false),
|
|
1493
|
+
generateExpression: vi.fn((ctx: Parser.ExpressionContext) => {
|
|
1494
|
+
flagDuringArg = CodeGenState.inDeclarationInit;
|
|
1495
|
+
return ctx.getText();
|
|
1496
|
+
}),
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
generateFunctionCall("myFunc", argCtx, input, state, orchestrator);
|
|
1500
|
+
|
|
1501
|
+
expect(flagDuringArg).toBe(false);
|
|
1502
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
it("restores inDeclarationInit after argument generation", () => {
|
|
1506
|
+
CodeGenState.inDeclarationInit = true;
|
|
1507
|
+
|
|
1508
|
+
const argExprs = [
|
|
1509
|
+
createMockExpressionContext("a"),
|
|
1510
|
+
createMockExpressionContext("b"),
|
|
1511
|
+
];
|
|
1512
|
+
const argCtx = createMockArgListContext(argExprs);
|
|
1513
|
+
const input = createMockInput();
|
|
1514
|
+
const state = createMockState();
|
|
1515
|
+
const orchestrator = createMockOrchestrator({
|
|
1516
|
+
isCNextFunction: vi.fn(() => false),
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
generateFunctionCall("myFunc", argCtx, input, state, orchestrator);
|
|
1520
|
+
|
|
1521
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
1522
|
+
});
|
|
1523
|
+
});
|
|
1480
1524
|
});
|
package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import expressionGenerators from "../ExpressionGenerator";
|
|
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
|
+
import CodeGenState from "../../../../../state/CodeGenState";
|
|
7
8
|
|
|
8
9
|
// ========================================================================
|
|
9
10
|
// Test Helpers
|
|
@@ -455,6 +456,86 @@ describe("ExpressionGenerator", () => {
|
|
|
455
456
|
});
|
|
456
457
|
});
|
|
457
458
|
|
|
459
|
+
describe("inDeclarationInit clearing (Issue #992)", () => {
|
|
460
|
+
beforeEach(() => {
|
|
461
|
+
CodeGenState.reset();
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it("clears inDeclarationInit in ternary true and false arms", () => {
|
|
465
|
+
CodeGenState.inDeclarationInit = true;
|
|
466
|
+
|
|
467
|
+
const condition = createMockOrExpr("x > 0");
|
|
468
|
+
const trueExpr = createMockOrExpr("a");
|
|
469
|
+
const falseExpr = createMockOrExpr("b");
|
|
470
|
+
const ctx = createMockTernaryContext([condition, trueExpr, falseExpr]);
|
|
471
|
+
|
|
472
|
+
const input = createMockInput();
|
|
473
|
+
const state = createMockState();
|
|
474
|
+
|
|
475
|
+
const flagDuringTrue: boolean[] = [];
|
|
476
|
+
const flagDuringFalse: boolean[] = [];
|
|
477
|
+
|
|
478
|
+
const orchestrator = createMockOrchestrator();
|
|
479
|
+
let callCount = 0;
|
|
480
|
+
(
|
|
481
|
+
orchestrator.generateOrExpr as ReturnType<typeof vi.fn>
|
|
482
|
+
).mockImplementation((ctx: Parser.OrExpressionContext) => {
|
|
483
|
+
callCount++;
|
|
484
|
+
if (callCount === 2) {
|
|
485
|
+
flagDuringTrue.push(CodeGenState.inDeclarationInit);
|
|
486
|
+
} else if (callCount === 3) {
|
|
487
|
+
flagDuringFalse.push(CodeGenState.inDeclarationInit);
|
|
488
|
+
}
|
|
489
|
+
return ctx.getText();
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
expressionGenerators.generateTernaryExpr(
|
|
493
|
+
ctx,
|
|
494
|
+
input,
|
|
495
|
+
state,
|
|
496
|
+
orchestrator,
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
expect(flagDuringTrue).toEqual([false]);
|
|
500
|
+
expect(flagDuringFalse).toEqual([false]);
|
|
501
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("does not affect condition generation", () => {
|
|
505
|
+
CodeGenState.inDeclarationInit = true;
|
|
506
|
+
|
|
507
|
+
const condition = createMockOrExpr("x > 0");
|
|
508
|
+
const trueExpr = createMockOrExpr("a");
|
|
509
|
+
const falseExpr = createMockOrExpr("b");
|
|
510
|
+
const ctx = createMockTernaryContext([condition, trueExpr, falseExpr]);
|
|
511
|
+
|
|
512
|
+
const input = createMockInput();
|
|
513
|
+
const state = createMockState();
|
|
514
|
+
|
|
515
|
+
let flagDuringCondition = false;
|
|
516
|
+
const orchestrator = createMockOrchestrator();
|
|
517
|
+
let callCount = 0;
|
|
518
|
+
(
|
|
519
|
+
orchestrator.generateOrExpr as ReturnType<typeof vi.fn>
|
|
520
|
+
).mockImplementation((ctx: Parser.OrExpressionContext) => {
|
|
521
|
+
callCount++;
|
|
522
|
+
if (callCount === 1) {
|
|
523
|
+
flagDuringCondition = CodeGenState.inDeclarationInit;
|
|
524
|
+
}
|
|
525
|
+
return ctx.getText();
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
expressionGenerators.generateTernaryExpr(
|
|
529
|
+
ctx,
|
|
530
|
+
input,
|
|
531
|
+
state,
|
|
532
|
+
orchestrator,
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
expect(flagDuringCondition).toBe(true);
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
458
539
|
describe("effects", () => {
|
|
459
540
|
it("returns empty effects array for non-ternary", () => {
|
|
460
541
|
const orExpr = createMockOrExpr("value");
|