From aee478adb064fc652061423f800b637fef592176 Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 16:06:32 +0330 Subject: conformance: runtime index errors now end with an 'at :N in main' trace line --- tests/conformance/3-expressions/dyn_array_const_index_warning.zc | 2 +- tests/conformance/3-expressions/dyn_array_index_negative.zc | 2 +- tests/conformance/3-expressions/dyn_array_index_out_of_range.zc | 2 +- tests/conformance/3-expressions/dyn_array_pop_empty.zc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests/conformance/3-expressions') diff --git a/tests/conformance/3-expressions/dyn_array_const_index_warning.zc b/tests/conformance/3-expressions/dyn_array_const_index_warning.zc index 36890d7..9639128 100644 --- a/tests/conformance/3-expressions/dyn_array_const_index_warning.zc +++ b/tests/conformance/3-expressions/dyn_array_const_index_warning.zc @@ -1,4 +1,4 @@ -// expect: zc: line 5: warning: out-of-bounds access: index 2 is outside length 2 (storage declared or allocated on line 4)/zy: index 2 out of range (length 2)/ +// expect: zc: line 5: warning: out-of-bounds access: index 2 is outside length 2 (storage declared or allocated on line 4)/zy: index 2 out of range (length 2)/ at :5 in main/ include rt; fn main() { int[?] a = {1, 2}; diff --git a/tests/conformance/3-expressions/dyn_array_index_negative.zc b/tests/conformance/3-expressions/dyn_array_index_negative.zc index c87e786..023951e 100644 --- a/tests/conformance/3-expressions/dyn_array_index_negative.zc +++ b/tests/conformance/3-expressions/dyn_array_index_negative.zc @@ -1,4 +1,4 @@ -// expect: zy: index -1 out of range (length 2)/ +// expect: zy: index -1 out of range (length 2)/ at :7 in main/ include rt; void pn(int x) { rt.put_n(x); rt.put_c(10); } fn main() { diff --git a/tests/conformance/3-expressions/dyn_array_index_out_of_range.zc b/tests/conformance/3-expressions/dyn_array_index_out_of_range.zc index eff8061..e613b70 100644 --- a/tests/conformance/3-expressions/dyn_array_index_out_of_range.zc +++ b/tests/conformance/3-expressions/dyn_array_index_out_of_range.zc @@ -1,4 +1,4 @@ -// expect: 2/zy: index 2 out of range (length 2)/ +// expect: 2/zy: index 2 out of range (length 2)/ at :8 in main/ include rt; void pn(int x) { rt.put_n(x); rt.put_c(10); } fn main() { diff --git a/tests/conformance/3-expressions/dyn_array_pop_empty.zc b/tests/conformance/3-expressions/dyn_array_pop_empty.zc index 6f5fdd0..ce08fca 100644 --- a/tests/conformance/3-expressions/dyn_array_pop_empty.zc +++ b/tests/conformance/3-expressions/dyn_array_pop_empty.zc @@ -1,4 +1,4 @@ -// expect: zy: index -1 out of range (length 0)/ +// expect: zy: index -1 out of range (length 0)/ at :6 in main/ include rt; void pn(int x) { rt.put_n(x); rt.put_c(10); } fn main() { -- cgit v1.2.3 From 3e71e980c611ac9f00565564bd1e647194f74321 Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 16:07:12 +0330 Subject: closures: allow 24 parameters, like functions (was 19) --- src/compiler/expr.zc | 4 ++-- tests/conformance/3-expressions/closure_24_parameters.zc | 6 ++++++ tests/langerrors/closure_params_25.zc | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 tests/conformance/3-expressions/closure_24_parameters.zc create mode 100644 tests/langerrors/closure_params_25.zc (limited to 'tests/conformance/3-expressions') diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc index 3d63e4b..c8cb11a 100644 --- a/src/compiler/expr.zc +++ b/src/compiler/expr.zc @@ -647,7 +647,7 @@ word closure_layout(int closure_index, int count) { // variable in. ctx_push / ctx_pop save and restore the enclosing function's // compiler state around the body. int closure() { - int np = 0; char*[20] pn; word[20] pty; int hop, entry, i, t, envl, ncap, c, result, declared = 0, tags = 0, tag, lt, lfl = tline; word[20] ptags; + int np = 0; char*[24] pn; word[24] pty; int hop, entry, i, t, envl, ncap, c, result, declared = 0, tags = 0, tag, lt, lfl = tline; word[24] ptags; word expected = closure_expected; closure_expected = 0; reserve_closures(nclo + 1); if (o3on) serr("closures can't be made inside an o3 region"); @@ -668,8 +668,8 @@ int closure() { if (expected && expected[0] == TY_FN && !tag && (isptr(pty[np]) || pty[np][0] == TY_DYN || pty[np][0] == TY_FN)) ptags[np] = 1; pnoffs[np] = toff; pnplaces[np] = tpack(); rs_note(RS_PARAM, toff); pn[np] = name(); np++; - if (np >= 20) { die("too many parameters"); break; } if (!accept(s_comma)) break; + if (np >= 24) { die("too many parameters"); break; } // 24, like a function } } expect(s_rp); diff --git a/tests/conformance/3-expressions/closure_24_parameters.zc b/tests/conformance/3-expressions/closure_24_parameters.zc new file mode 100644 index 0000000..e12d258 --- /dev/null +++ b/tests/conformance/3-expressions/closure_24_parameters.zc @@ -0,0 +1,6 @@ +// expect: 300/ +// spec 3.15.1 and 5.2.2: a closure takes up to 24 parameters, like a function +include rt; +void pn(int x) { rt.put_n(x); rt.put_c(10); } +fn main() { let f = fn(int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8,int a9,int a10,int a11,int a12,int a13,int a14,int a15,int a16,int a17,int a18,int a19,int a20,int a21,int a22,int a23,int a24) { return a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15+a16+a17+a18+a19+a20+a21+a22+a23+a24; }; pn(f(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24)); return 0; } +// zc0: skip diff --git a/tests/langerrors/closure_params_25.zc b/tests/langerrors/closure_params_25.zc new file mode 100644 index 0000000..0444007 --- /dev/null +++ b/tests/langerrors/closure_params_25.zc @@ -0,0 +1,3 @@ +// expect: too many parameters +include rt; +fn main() { let f = fn(int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8,int a9,int a10,int a11,int a12,int a13,int a14,int a15,int a16,int a17,int a18,int a19,int a20,int a21,int a22,int a23,int a24, int a25) { return 0; }; return 0; } -- cgit v1.2.3 From b6f4d415c5fae9bfe9243ab0044135ce65492665 Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 16:17:42 +0330 Subject: ternary: an int side and a float side convert the int on its own path only (appendix b.2.5) --- src/compiler/expr.zc | 10 +++++++--- tests/conformance/3-expressions/ternary_int_float_variables.zc | 8 ++++++++ tests/conformance/known.txt | 3 +-- 3 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 tests/conformance/3-expressions/ternary_int_float_variables.zc (limited to 'tests/conformance/3-expressions') diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc index ffb90a7..95ce67b 100644 --- a/src/compiler/expr.zc +++ b/src/compiler/expr.zc @@ -1727,7 +1727,7 @@ int storekeep(int t) { // restored for the second side) and then joined, so a variable can't be given // away on one side only. both sides are fresh objects, or neither is. int ternary(int c) { - int a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta, ba, bb; word temp_a, temp_b, mask_a, mask_b; + int a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta, ba, bb, conv, skip, back; word temp_a, temp_b, mask_a, mask_b; rval(); plainuse(c); ftest(); next(); @@ -1735,6 +1735,7 @@ int ternary(int c) { s0 = snap_push(); ca = expr(); rval(); ta = xty; ba = xbor; temp_a = xaf; mask_a = effect_mask(ca); if (ca != C_FRESH) plainuse(ca); + conv = -1; if (isnum(ta) && !isfl(ta) && ta != tword) { conv = hole(); op(O_JMP); back = here; setlit(conv, back); } // a jump to the next word, redirected below if the other side is a float (appendix b.2.5) livea = live; sa = snap_push(); b = hole(); op(O_JMP); expect(s_colon); @@ -1742,11 +1743,14 @@ int ternary(int c) { snap_restore(s0); live = live0; cb = expr(); rval(); bb = xbor; temp_b = xaf; mask_b = effect_mask(cb); if (cb != C_FRESH) plainuse(cb); + if (isfl(ta) && !isfl(xty)) { op(OP_I2F); xfloat = 1; xty = tfloat; } // on the second side's path only join2(sa, livea, b, live, n0, "ends the two sides of ?: in different states"); snap_pop(s0); - if (isfl(ta) && !isfl(xty)) { op(OP_I2F); xfloat = 1; xty = tfloat; } + if (conv >= 0 && isfl(xty)) { // the first side was an integer: convert it out of line + skip = hole(); op(O_JMP); setlit(conv, here); op(OP_I2F); lit(back); op(O_JMP); setlit(skip, here); + } if (!compat(ta, xty) && !compat(xty, ta)) tcerr("the two sides of ?:", ta, xty); - if (ta != tword && !isfl(ta)) xty = ta; + if (ta != tword && !isfl(ta) && !(conv >= 0 && isfl(xty))) xty = ta; else if (isfl(ta) || isfl(xty)) { xty = tfloat; xfloat = 1; } if_value = 0; effect_value = mask_a | mask_b; diff --git a/tests/conformance/3-expressions/ternary_int_float_variables.zc b/tests/conformance/3-expressions/ternary_int_float_variables.zc new file mode 100644 index 0000000..4bd2d57 --- /dev/null +++ b/tests/conformance/3-expressions/ternary_int_float_variables.zc @@ -0,0 +1,8 @@ +// expect: 3.000000/0.500000/3.000000/0.500000/ +// spec 3.5 and appendix b.2.5: ?: with an int side and a float side gives a float on both paths, also in o3 +include rt; +fn pf(float f) { asm { f @ #1220 } rt.put_c(10); return 0; } +float pick(int k, int i, float f) { return k ? i : f; } +o3 float pick3(int k, int i, float f) { return k ? i : f; } +fn main() { pf(pick(1, 3, 0.5)); pf(pick(0, 3, 0.5)); pf(pick3(1, 3, 0.5)); pf(pick3(0, 3, 0.5)); return 0; } +// zc0: skip diff --git a/tests/conformance/known.txt b/tests/conformance/known.txt index 8415b18..5ff2ec0 100644 --- a/tests/conformance/known.txt +++ b/tests/conformance/known.txt @@ -2,9 +2,8 @@ # class a: self-hosted compiler bug; b: zc0 deviation (appendix a/b); c: spec issue to settle # appendix b.2.8 (a global named in an initializer is its address) is documented behavior per 5.3.2 and 3.14.2: no entry 4-statements/o3_outside_function_ordinary_block.zc zy c top-level o3 { } is rejected; spec 4.10.2 says it is an ordinary block -5-declarations/enum_member_names_earlier_b2.zc zy a appendix b.2.2 confirmed: enum members can't name earlier members +5-declarations/enum_member_names_earlier_b2.zc zy c appendix b.2.2 confirmed, but spec 2.8.1 documents it (members are defined at the closing brace); not fixed, like b.2.8 the spec body wins until decided 5-declarations/fn_prototype_unnamed_params_b13.zc zy c spec 5.2.1 says prototype parameter names are required, and a type name alone is read as an untyped parameter of that name (paramstart); b.2.13 is a spec choice, not fixed -9-appendix-b/b2_05_ternary_int_float.zc zy a appendix b.2.5 confirmed 9-appendix-b/b2_06_bool_normalises.zc zy a appendix b.2.6 confirmed 9-appendix-b/b2_10_recovery_hides_errors.zc zy c appendix b.2.10 confirmed; by design per 8.2 9-appendix-b/b2_11_unterminated_comment.zc zy a appendix b.2.11 confirmed -- cgit v1.2.3 From 82f8b735f966be69f9892350fb0a719f3fc436f1 Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 16:51:03 +0330 Subject: as: the type after as is read greedily in both compilers; x as int * y is a syntax error (appendix b.2.12 settled, no compiler change) --- docs/SPEC.md | 4 ++-- .../3-expressions/cast_as_then_multiply_parenthesised.zc | 5 +++++ tests/conformance/9-appendix-b/b2_12_as_int_star.zc | 6 +++--- tests/conformance/known.txt | 1 - 4 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 tests/conformance/3-expressions/cast_as_then_multiply_parenthesised.zc (limited to 'tests/conformance/3-expressions') diff --git a/docs/SPEC.md b/docs/SPEC.md index cc94451..30d547d 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -361,7 +361,7 @@ a = b = 7 // assigns both, value 7 ``` `&&` and `||` are lower than `|`. this matches c. -3.2.3 `as` is a postfix operator: it applies to the operand to its left and binds tighter than every binary operator, so `a + b as T` is `a + (b as T)`. a type after `as` takes following stars: `x as int * y` parses `int*` then fails. parenthesise. +3.2.3 `as` is a postfix operator: it applies to the operand to its left and binds tighter than every binary operator, so `a + b as T` is `a + (b as T)`. the type after `as` is read greedily, stars included (`x as char**`), in both compilers: `x as int * y` reads `int*` and then is a syntax error; write `(x as int) * y`. 3.2.4 the condition of `?:` is a `binary` expression (so `a = b ? c : d` is `a = (b ? c : d)`), the middle operand and the last operand are full `expr` (assignments allowed). `a ? b : c = 5` therefore parses as `a ? b : (c = 5)`: with `a` 1 the assignment does not run. @@ -1115,7 +1115,7 @@ each item gives a minimal program, the expected behavior (per this spec) and the 9. fixed (5.12.7): `include std;` inside a function body gave `return outside a function`; it is now error: include is top level only. 10. a syntax error hides errors in the statements skipped by recovery: `int a = ; int b = 3 +;` reports only the first error (8.2). by design, but easy to miss. 11. fixed (1.2.2): an unterminated `/* ...` comment was accepted silently; it is error: unterminated comment. (an unterminated string literal is still only reported as a later syntax error.) -12. `x as int * y` parses `int*` as the type (3.2.3). +12. settled (3.2.3): `x as int * y` parses `int*` as the type. this is the rule (the type is read greedily, as zc0 does too), so the program is a syntax error. 13. prototypes need parameter names: `int f(int);` => `expected a name, found ')'`. 14. `len(s)` on a `char*` returns word 0 of the string when `typecheck` is off. 15. duplicate `case` labels are not diagnosed (4.7.2). diff --git a/tests/conformance/3-expressions/cast_as_then_multiply_parenthesised.zc b/tests/conformance/3-expressions/cast_as_then_multiply_parenthesised.zc new file mode 100644 index 0000000..ab11217 --- /dev/null +++ b/tests/conformance/3-expressions/cast_as_then_multiply_parenthesised.zc @@ -0,0 +1,5 @@ +// expect: 6/ +// spec 3.2.3: (x as int) * y multiplies +include rt; +void pn(int x) { rt.put_n(x); rt.put_c(10); } +fn main() { int x = 2; int y = 3; pn((x as int) * y); return 0; } diff --git a/tests/conformance/9-appendix-b/b2_12_as_int_star.zc b/tests/conformance/9-appendix-b/b2_12_as_int_star.zc index fc12792..da54802 100644 --- a/tests/conformance/9-appendix-b/b2_12_as_int_star.zc +++ b/tests/conformance/9-appendix-b/b2_12_as_int_star.zc @@ -1,6 +1,6 @@ -// expect: 6/ -// spec appendix b.2.12: x as int * y should multiply +// expect error: expected ')', found 'y' +// spec 3.2.3 and appendix b.2.12: the type after as is read greedily, so x as int * y reads int* and then fails; write (x as int) * y include rt; void pn(int x) { rt.put_n(x); rt.put_c(10); } fn main() { int x = 2; int y = 3; pn(x as int * y); return 0; } -// zc0: skip +// zc0: error diff --git a/tests/conformance/known.txt b/tests/conformance/known.txt index f666106..7cb703e 100644 --- a/tests/conformance/known.txt +++ b/tests/conformance/known.txt @@ -4,7 +4,6 @@ 4-statements/o3_outside_function_ordinary_block.zc zy c top-level o3 { } is rejected; spec 4.10.2 says it is an ordinary block 5-declarations/fn_prototype_unnamed_params_b13.zc zy c spec 5.2.1 says prototype parameter names are required, and a type name alone is read as an untyped parameter of that name (paramstart); b.2.13 is a spec choice, not fixed 9-appendix-b/b2_10_recovery_hides_errors.zc zy c appendix b.2.10 confirmed; by design per 8.2 -9-appendix-b/b2_12_as_int_star.zc zy c appendix b.2.12 confirmed, but spec 3.2.3 documents it (parenthesise) 9-appendix-b/b2_14_len_char_ptr_typecheck_off.zc zy c appendix b.2.14 confirmed, but spec 3.13.5 leaves it unspecified 9-appendix-b/b2_15_duplicate_case.zc zy c appendix b.2.15 confirmed; 4.7.2 says duplicates are allowed 9-appendix-b/b2_16_local_const_in_const_expr.zc zy c appendix b.2.16 confirmed; 3.14.2 documents it -- cgit v1.2.3