1717open Alcotest
1818open Kernelscript.Parse
1919open Kernelscript.Type_checker
20+ open Kernelscript.Ir
21+ open Kernelscript.Ebpf_c_codegen
22+ open Kernelscript.Ast
23+
24+ (* * Common test position for IR/codegen tests *)
25+ let test_pos = make_position 1 1 " test.ks"
2026
2127(* * Helper function to parse and evaluate a program with break/continue *)
2228let parse_and_check_break_continue program_text =
@@ -196,6 +202,118 @@ let test_break_evaluation () =
196202 with
197203 | e -> fail (" Failed break evaluation test: " ^ Printexc. to_string e)
198204
205+ (* * Test that verifies the fix for break/continue in unbound loops
206+ This test ensures that callback functions have consistent variable naming
207+ between declarations and usage. Previously, variables were declared as
208+ tmp_X but referenced as val_X, causing compilation errors.
209+ *)
210+ let test_break_continue_unbound_variable_naming () =
211+ let ctx = create_c_context () in
212+
213+ (* Create registers for variables that would be used in break/continue logic *)
214+ let counter_reg = 1 in
215+ let condition_reg = 2 in
216+ let temp_reg = 3 in
217+
218+ (* Create IR values representing loop variables *)
219+ let counter_val = make_ir_value (IRRegister counter_reg) IRU32 test_pos in
220+ let condition_val = make_ir_value (IRRegister condition_reg) IRBool test_pos in
221+ let temp_val = make_ir_value (IRRegister temp_reg) IRU32 test_pos in
222+
223+ (* Create a modulo operation and comparison (similar to the original failing case) *)
224+ let two_val = make_ir_value (IRLiteral (IntLit (2 , None ))) IRU32 test_pos in
225+ let zero_val = make_ir_value (IRLiteral (IntLit (0 , None ))) IRU32 test_pos in
226+
227+ (* Create IR instructions that would be in a bpf_loop callback *)
228+ let mod_expr = make_ir_expr (IRBinOp (counter_val, IRMod , two_val)) IRU32 test_pos in
229+ let mod_assign = make_ir_instruction (IRAssign (temp_val, mod_expr)) test_pos in
230+
231+ let eq_expr = make_ir_expr (IRBinOp (temp_val, IREq , zero_val)) IRBool test_pos in
232+ let eq_assign = make_ir_instruction (IRAssign (condition_val, eq_expr)) test_pos in
233+
234+ (* Create the bpf_loop instruction with these body instructions *)
235+ let start_val = make_ir_value (IRLiteral (IntLit (0 , None ))) IRU32 test_pos in
236+ let end_val = make_ir_value (IRLiteral (IntLit (1000 , None ))) IRU32 test_pos in
237+ let ctx_val = make_ir_value (IRRegister 10 ) (IRPointer (IRU8 , make_bounds_info () )) test_pos in
238+ let body_instructions = [mod_assign; eq_assign] in
239+
240+ let bpf_loop_instr = make_ir_instruction
241+ (IRBpfLoop (start_val, end_val, counter_val, ctx_val, body_instructions))
242+ test_pos in
243+
244+ (* Generate C code for the bpf_loop instruction *)
245+ generate_c_instruction ctx bpf_loop_instr;
246+
247+ (* Get the generated code *)
248+ let _output = String. concat " \n " ctx.output_lines in
249+
250+ (* Extract any pending callbacks (this is where the fix matters) *)
251+ let callback_code = String. concat " \n " ctx.pending_callbacks in
252+
253+ (* Verify that the callback code doesn't contain inconsistent variable naming *)
254+ let has_consistent_naming =
255+ (* Check that if variables are declared as tmp_X, they're also used as tmp_X *)
256+ let tmp_declarations = Str. split (Str. regexp " tmp_[0-9]+" ) callback_code in
257+ let val_usage = Str. split (Str. regexp " val_[0-9]+" ) callback_code in
258+ (* If there are tmp declarations, there shouldn't be val usage in the same callback *)
259+ if List. length tmp_declarations > 1 then
260+ List. length val_usage < = 1 (* Only the split creates one extra element *)
261+ else
262+ true
263+ in
264+
265+ check bool " Variable naming is consistent in callback" true has_consistent_naming;
266+
267+ (* Also verify that the generated code contains a callback function *)
268+ let has_callback = String. length callback_code > 0 in
269+ check bool " Callback function was generated" true has_callback;
270+
271+ (* Additional check: verify no undeclared variable usage *)
272+ let lines = String. split_on_char '\n' callback_code in
273+ let has_undeclared_usage = List. exists (fun line ->
274+ (* Look for patterns like "val_X = " where val_X wasn't declared *)
275+ Str. string_match (Str. regexp " .*val_[0-9]+ =.*" ) line 0 &&
276+ not (Str. string_match (Str. regexp " .*__u32 val_[0-9]+.*" ) line 0 )
277+ ) lines in
278+
279+ check bool " No undeclared variable usage" false has_undeclared_usage
280+
281+ let test_register_hints_respected_in_callbacks () =
282+ let ctx = create_c_context () in
283+
284+ (* Set register hints to use "tmp" prefix *)
285+ Hashtbl. replace ctx.register_name_hints 1 " tmp" ;
286+ Hashtbl. replace ctx.register_name_hints 2 " tmp" ;
287+
288+ (* Create variables with types that would normally use "val" prefix *)
289+ let _val1 = make_ir_value (IRRegister 1 ) IRU32 test_pos in
290+ let _val2 = make_ir_value (IRRegister 2 ) IRU32 test_pos in
291+
292+ (* Generate variable names *)
293+ let name1 = get_meaningful_var_name ctx 1 IRU32 in
294+ let name2 = get_meaningful_var_name ctx 2 IRU32 in
295+
296+ (* With the fix, these should respect the hints and use "tmp" not "val" *)
297+ check string " Register 1 uses hint prefix" " tmp_1" name1;
298+ check string " Register 2 uses hint prefix" " tmp_2" name2
299+
300+ let test_no_hints_uses_type_based_naming () =
301+ let ctx = create_c_context () in
302+
303+ (* Don't set any register hints *)
304+
305+ (* Create variables with types that should use "val" prefix when no hints *)
306+ let _val1 = make_ir_value (IRRegister 1 ) IRU32 test_pos in
307+ let _val2 = make_ir_value (IRRegister 2 ) IRBool test_pos in
308+
309+ (* Generate variable names *)
310+ let name1 = get_meaningful_var_name ctx 1 IRU32 in
311+ let name2 = get_meaningful_var_name ctx 2 IRBool in
312+
313+ (* Without hints, should use type-based naming *)
314+ check string " U32 without hints uses val prefix" " val_1" name1;
315+ check string " Bool without hints uses cond prefix" " cond_2" name2
316+
199317let break_continue_tests = [
200318 " break_statement_parsing" , `Quick , test_break_statement_parsing;
201319 " continue_statement_parsing" , `Quick , test_continue_statement_parsing;
@@ -206,6 +324,9 @@ let break_continue_tests = [
206324 " break_continue_in_nested_conditional" , `Quick , test_break_continue_in_nested_conditional;
207325 " multiple_break_continue_statements" , `Quick , test_multiple_break_continue_statements;
208326 " break_evaluation" , `Quick , test_break_evaluation;
327+ " break_continue_unbound_variable_naming" , `Quick , test_break_continue_unbound_variable_naming;
328+ " register_hints_respected_in_callbacks" , `Quick , test_register_hints_respected_in_callbacks;
329+ " no_hints_uses_type_based_naming" , `Quick , test_no_hints_uses_type_based_naming;
209330]
210331
211332let () =
0 commit comments