nested-array-field-bug.mod 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. MODULE NestedArrayFieldBug;
  2. (* Repro: a RECORD field that is a nested ARRAY OF ARRAY OF CHAR, accessed
  3. as `arr[i].field[j][k]`, is miscompiled by V3 -> segfault (rc 139).
  4. Observed 2026-10-05. Expected exit 42; observed 139.
  5. Found while adding Lower's procedure-formal name table (a module-level
  6. ARRAY OF RECORD with a `pname : ARRAY [0..31] OF ARRAY [0..63] OF
  7. CHAR` field): the V3-built compiler crashed (SIGABRT) on every input.
  8. Flattening the field to `ARRAY [0..2047] OF CHAR` fixed that crash.
  9. STATUS (2026-10-05):
  10. - The *stride* half is FIXED (commit v3-fix-arrayrec-stride):
  11. QbeGen.ArrBodyItems now uses SymTab.TypeSize for record/class/set
  12. array elements, so an ARRAY OF record has correct element size and
  13. flat field access works (see tests/t_recarr.mod, exit 42).
  14. - This nested case is FIXED: ArrBodyItems now expands array-of-record
  15. elements per element (EmitRec) when the record (recursively) has a
  16. nested-array field, and ArrData emits the matching per-element
  17. sub-descriptors (EmitRecStatics). Plain record arrays (no nested
  18. array field) stay a compact zero blob, so AST.arena does not
  19. balloon. Regression test: tests/t_nestarr.mod (exit 42).
  20. Compare with docs/wip/local-array-bug.mod (local arrays) and the
  21. Stage-A array-of-record stride fix (QbeGen.ElemSize). *)
  22. VAR ExitCode : INTEGER;
  23. CONST MaxName = 63; MaxProc = 4;
  24. TYPE PE = RECORD
  25. name : ARRAY [0..MaxName] OF CHAR;
  26. npar : CARDINAL;
  27. pname : ARRAY [0..3] OF ARRAY [0..MaxName] OF CHAR; (* nested *)
  28. END;
  29. VAR procs : ARRAY [0..MaxProc-1] OF PE;
  30. nProcs : CARDINAL;
  31. PROCEDURE Reset;
  32. BEGIN nProcs := 0 END Reset;
  33. PROCEDURE NoteProc (name : ARRAY OF CHAR);
  34. VAR i : CARDINAL;
  35. BEGIN
  36. IF nProcs >= MaxProc THEN RETURN END;
  37. i := 0;
  38. WHILE (i < MaxName) AND (name[i] # CHR(0)) DO
  39. procs[nProcs].name[i] := name[i]; INC(i)
  40. END;
  41. procs[nProcs].name[i] := CHR(0);
  42. procs[nProcs].npar := 0; INC(nProcs)
  43. END NoteProc;
  44. PROCEDURE NoteParam (name : ARRAY OF CHAR);
  45. VAR j, i : CARDINAL;
  46. BEGIN
  47. IF nProcs = 0 THEN RETURN END;
  48. j := procs[nProcs-1].npar;
  49. i := 0;
  50. WHILE (i < MaxName) AND (name[i] # CHR(0)) DO
  51. procs[nProcs-1].pname[j][i] := name[i]; INC(i)
  52. END;
  53. procs[nProcs-1].pname[j][i] := CHR(0);
  54. procs[nProcs-1].npar := j + 1
  55. END NoteParam;
  56. BEGIN
  57. Reset; NoteProc("Add"); NoteParam("a");
  58. IF (nProcs = 1) AND (procs[0].pname[0][0] = "a")
  59. THEN ExitCode := 42 ELSE ExitCode := 1 END
  60. END NestedArrayFieldBug.