From 0b7a396cc5375afdeea1716126f8dd1a0efea870 Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 16:08:50 +0330 Subject: unary: u*x, m*x, d*x are not recasts when the tag names a global variable either --- tests/conformance/1-source/ident_tags_global_glued_star.zc | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/conformance/1-source/ident_tags_global_glued_star.zc (limited to 'tests/conformance/1-source') diff --git a/tests/conformance/1-source/ident_tags_global_glued_star.zc b/tests/conformance/1-source/ident_tags_global_glued_star.zc new file mode 100644 index 0000000..516f9a3 --- /dev/null +++ b/tests/conformance/1-source/ident_tags_global_glued_star.zc @@ -0,0 +1,8 @@ +// expect: 12/10/ +// spec 1.6.3: u*x and m*x are not recasts when u or m is a visible global variable +include rt; +void pn(int x) { rt.put_n(x); rt.put_c(10); } +int u = 3; +int m = 5; +fn main() { int d = 4; int k = 2; pn(u*d); pn(m*k); return 0; } +// zc0: skip -- cgit v1.2.3 From 2f2597bd753fa04edc8cf51ffe9fd324965224d2 Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 16:21:01 +0330 Subject: zc0: u*x, m*x, d*x are products when u, m or d is a visible local or global (spec 1.6.3) --- src/tools/zc0.c | 13 ++++++++----- tests/conformance/1-source/ident_tags_global_glued_star.zc | 1 - tests/conformance/known.txt | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'tests/conformance/1-source') diff --git a/src/tools/zc0.c b/src/tools/zc0.c index 6c20e2a..e673eee 100644 --- a/src/tools/zc0.c +++ b/src/tools/zc0.c @@ -238,12 +238,8 @@ static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; } static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } } static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; } /* u, m and d are ordinary names. u*x, m*x and d*x are recasts when the star touches - the letter, which the raw character after the name tells: u * x is a product, and + the letter and no variable of that name is visible, which the raw character after the name tells: u * x is a product, and so is u*=x. the current token is the name, so pos is just past it. */ -static int isrecast(void) -{ - return tk == T_ID && ts[1] == 0 && (ts[0] == 'u' || ts[0] == 'm' || ts[0] == 'd') && src[pos] == '*' && src[pos + 1] != '='; -} /* ---- symbols ---- */ typedef struct { char name[64]; int kind, sig, defd, str; int64_t val; } Sym; /* str: a global holding a byte string, or a function returning one */ @@ -284,6 +280,13 @@ static void fnref(const char *n) /* push a function address, fixing it up later typedef struct { char name[64]; int slot, arr, flt, str, dstr; } Loc; /* dstr: declared bstr, so always a byte string */ static Loc lo[1024]; static int nlo, nslot, infn; static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; } +static int isrecast(void) +{ + Sym *g; + if (!(tk == T_ID && ts[1] == 0 && (ts[0] == 'u' || ts[0] == 'm' || ts[0] == 'd') && src[pos] == '*' && src[pos + 1] != '=')) return 0; + if (findl(ts)) return 0; /* a visible variable named u, m or d: a product (spec 1.6.3) */ + g = find(ts); return !(g && (g->kind == K_GVAR || g->kind == K_GARR)); +} static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } } /* ---- operator tables ---- */ diff --git a/tests/conformance/1-source/ident_tags_global_glued_star.zc b/tests/conformance/1-source/ident_tags_global_glued_star.zc index 516f9a3..92abad4 100644 --- a/tests/conformance/1-source/ident_tags_global_glued_star.zc +++ b/tests/conformance/1-source/ident_tags_global_glued_star.zc @@ -5,4 +5,3 @@ void pn(int x) { rt.put_n(x); rt.put_c(10); } int u = 3; int m = 5; fn main() { int d = 4; int k = 2; pn(u*d); pn(m*k); return 0; } -// zc0: skip diff --git a/tests/conformance/known.txt b/tests/conformance/known.txt index 6232672..4d949fa 100644 --- a/tests/conformance/known.txt +++ b/tests/conformance/known.txt @@ -11,7 +11,6 @@ 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 -1-source/ident_tags_as_variables_glued_star.zc zc0 b u*d with visible u and d is read as a recast (no code); zy follows 1.6.3 2-types/conv_argument_and_return.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a) 2-types/conv_assignment_to_float.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a) 2-types/conv_f32_rounds.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a) -- cgit v1.2.3 From 87dba496160abf20557dbf102b7bc9ce4078c0d9 Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 16:25:31 +0330 Subject: Revert "zc0: u*x, m*x, d*x are products when u, m or d is a visible local or global (spec 1.6.3)" This reverts commit 2f2597bd753fa04edc8cf51ffe9fd324965224d2. --- src/tools/zc0.c | 13 +++++-------- tests/conformance/1-source/ident_tags_global_glued_star.zc | 1 + tests/conformance/known.txt | 1 + 3 files changed, 7 insertions(+), 8 deletions(-) (limited to 'tests/conformance/1-source') diff --git a/src/tools/zc0.c b/src/tools/zc0.c index 81561ce..2261661 100644 --- a/src/tools/zc0.c +++ b/src/tools/zc0.c @@ -238,8 +238,12 @@ static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; } static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } } static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; } /* u, m and d are ordinary names. u*x, m*x and d*x are recasts when the star touches - the letter and no variable of that name is visible, which the raw character after the name tells: u * x is a product, and + the letter, which the raw character after the name tells: u * x is a product, and so is u*=x. the current token is the name, so pos is just past it. */ +static int isrecast(void) +{ + return tk == T_ID && ts[1] == 0 && (ts[0] == 'u' || ts[0] == 'm' || ts[0] == 'd') && src[pos] == '*' && src[pos + 1] != '='; +} /* ---- symbols ---- */ typedef struct { char name[64]; int kind, sig, defd, str; int64_t val; } Sym; /* str: a global holding a byte string, or a function returning one */ @@ -280,13 +284,6 @@ static void fnref(const char *n) /* push a function address, fixing it up later typedef struct { char name[64]; int slot, arr, flt, str, dstr; } Loc; /* dstr: declared bstr, so always a byte string */ static Loc lo[1024]; static int nlo, nslot, infn; static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; } -static int isrecast(void) -{ - Sym *g; - if (!(tk == T_ID && ts[1] == 0 && (ts[0] == 'u' || ts[0] == 'm' || ts[0] == 'd') && src[pos] == '*' && src[pos + 1] != '=')) return 0; - if (findl(ts)) return 0; /* a visible variable named u, m or d: a product (spec 1.6.3) */ - g = find(ts); return !(g && (g->kind == K_GVAR || g->kind == K_GARR)); -} static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } } /* ---- operator tables ---- */ diff --git a/tests/conformance/1-source/ident_tags_global_glued_star.zc b/tests/conformance/1-source/ident_tags_global_glued_star.zc index 92abad4..516f9a3 100644 --- a/tests/conformance/1-source/ident_tags_global_glued_star.zc +++ b/tests/conformance/1-source/ident_tags_global_glued_star.zc @@ -5,3 +5,4 @@ void pn(int x) { rt.put_n(x); rt.put_c(10); } int u = 3; int m = 5; fn main() { int d = 4; int k = 2; pn(u*d); pn(m*k); return 0; } +// zc0: skip diff --git a/tests/conformance/known.txt b/tests/conformance/known.txt index df4c94b..22a1e3d 100644 --- a/tests/conformance/known.txt +++ b/tests/conformance/known.txt @@ -11,6 +11,7 @@ 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 +1-source/ident_tags_as_variables_glued_star.zc zc0 b u*d with visible u and d is read as a recast (no code); zy follows 1.6.3 2-types/conv_argument_and_return.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a) 2-types/conv_assignment_to_float.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a) 2-types/conv_f32_rounds.zc zc0 b types are read and dropped: no conversion, no sized cut (appendix a) -- cgit v1.2.3 From ccbc77c2d3fa348d0de6f32fc540468401e6880b Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 16:50:41 +0330 Subject: lexer: a block comment that is never closed is 'unterminated comment' (appendix b.2.11) --- docs/SPEC.md | 4 ++-- src/compiler/lex.zc | 2 ++ tests/conformance/1-source/comment_block_unclosed.zc | 3 ++- tests/conformance/9-appendix-b/b2_11_unterminated_comment.zc | 2 +- tests/conformance/known.txt | 1 - tests/langerrors/unterminated_comment_docs.zc | 4 ++++ 6 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 tests/langerrors/unterminated_comment_docs.zc (limited to 'tests/conformance/1-source') diff --git a/docs/SPEC.md b/docs/SPEC.md index 529b5bf..cc94451 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -24,7 +24,7 @@ this is the reference for the zc language. it is written from `src/compiler/comp 1.2.1 `//` starts a comment that runs to the end of the line. -1.2.2 `/* ... */` is a block comment. block comments do not nest. a block comment that is never closed runs to the end of the unit and is not an error. +1.2.2 `/* ... */` is a block comment. block comments do not nest. a block comment that is never closed is error: unterminated comment. 1.2.3 comments are not tokens, but `docs on;` (5.14) and `hints` (4.12) read them: `//` lines directly above a declaration document it, and `//:` lines are implementation notes that document nothing. an inline block comment right before a numeric literal marks the literal as intentional for the magic-number hint. neither changes the generated code. @@ -1114,7 +1114,7 @@ each item gives a minimal program, the expected behavior (per this spec) and the 8. not a bug: a global initializer that names another global uses the other global's address (5.3.2, 3.14.2): `int a = 5; int b = a + 1;` gives `b` the value `&a + 1`. 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. an unterminated `/* ...` comment is accepted silently (1.2.2); an unterminated string literal is only reported as a later syntax error. +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). 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. diff --git a/src/compiler/lex.zc b/src/compiler/lex.zc index d06b81b..2763333 100644 --- a/src/compiler/lex.zc +++ b/src/compiler/lex.zc @@ -428,6 +428,7 @@ int docblock(int nl) { doc_char(c); if (c == 10) bol = 1; } + if (ch < 0) { tline = line; serr("unterminated comment"); } while (dn && (dbuf[dn - 1] == 32 || dbuf[dn - 1] == 9 || dbuf[dn - 1] == 10 || dbuf[dn - 1] == 13)) dn--; doc_char(10); dlast = cl; docpending = 1; if (adj) dn = 0; @@ -489,6 +490,7 @@ int lex1() { if (docs_on) { docblock(nl); tglue = 0; continue; } nextch(); while (ch >= 0) { if (ch == '*') { nextch(); if (ch == '/') break; } else nextch(); } + if (ch < 0) { tline = line; serr("unterminated comment"); } // a /* with no */ (spec 1.2.2) nextch(); tglue = 0; continue; } tline = line; toff = cpos() - 1; tbuf[0] = '/'; oplex(1); return 0; diff --git a/tests/conformance/1-source/comment_block_unclosed.zc b/tests/conformance/1-source/comment_block_unclosed.zc index 4ae34e7..cb410f9 100644 --- a/tests/conformance/1-source/comment_block_unclosed.zc +++ b/tests/conformance/1-source/comment_block_unclosed.zc @@ -1,4 +1,5 @@ -// expect: 4/ +// expect error: unterminated comment +// spec 1.2.2: a block comment that is never closed is an error include rt; fn main() { rt.put_n(4); rt.put_c(10); return 0; } /* never closed: rt.put_n(9); diff --git a/tests/conformance/9-appendix-b/b2_11_unterminated_comment.zc b/tests/conformance/9-appendix-b/b2_11_unterminated_comment.zc index e6e6bbf..ce4b236 100644 --- a/tests/conformance/9-appendix-b/b2_11_unterminated_comment.zc +++ b/tests/conformance/9-appendix-b/b2_11_unterminated_comment.zc @@ -1,4 +1,4 @@ -// expect error: unterminated +// expect error: unterminated comment // spec appendix b.2.11: an unterminated /* is accepted silently include rt; void pn(int x) { rt.put_n(x); rt.put_c(10); } diff --git a/tests/conformance/known.txt b/tests/conformance/known.txt index 8550c07..f666106 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_11_unterminated_comment.zc zy c appendix b.2.11 confirmed, but spec 1.2.2 says an unclosed block comment is not an error 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 diff --git a/tests/langerrors/unterminated_comment_docs.zc b/tests/langerrors/unterminated_comment_docs.zc new file mode 100644 index 0000000..02f4335 --- /dev/null +++ b/tests/langerrors/unterminated_comment_docs.zc @@ -0,0 +1,4 @@ +// expect: unterminated comment +docs on; +fn main() { return 0; } +/* never closed, with docs on -- cgit v1.2.3