From 48c1b95e6451962486d9a74de390e7eb3f6e2613 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 13:44:57 +0700 Subject: [PATCH 1/6] t27o: an all-caps enum variant is Owner.VARIANT and a unit struct value is Name{} -- two spellings t27c rejects (Closes #10905) Class 5 of #10905. path() and pattern() wrote Owner::GF16 as Owner_GF16 whenever the member looked like a const (upper case, digits, _), so the variants F32, GF16, X402, H2, FFN of seven enums named a declaration nobody makes. enum_has asks the file: a member that is a variant of the enum Owner (Self is the impl's type) is Owner.VARIANT; an associated const stays Owner_CONST. A unit struct value was written as the bare type name, which t27c rejects where a value stands ("expected type 'Gw', found 'type'"); unit_struct finds `struct Name;` (the index or the walk, Self is the impl's type) and primary writes Name{}. Tests: unit_hit / unit_struct / enum_has directly (index on and off, Self with and without an impl, an empty enum, a braced and a tuple struct as negative controls), and one body test whose output compiled under t27c test-report and ran under t27b test; an associated const stays Gw_LIMIT. t27b 292 / 292, seal re-saved and verified, cargo check -p tri clean. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oRust.json | 16 ++-- gen/rust/tools/t27o/rust_convert.rs | 75 +++++++++++++++++- specs/tools/t27o/rust_convert.t27 | 113 +++++++++++++++++++++++++++- 3 files changed, 191 insertions(+), 13 deletions(-) diff --git a/.trinity/seals/t27o_T27oRust.json b/.trinity/seals/t27o_T27oRust.json index 6b19cde333..8934957241 100644 --- a/.trinity/seals/t27o_T27oRust.json +++ b/.trinity/seals/t27o_T27oRust.json @@ -2,23 +2,23 @@ "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:27e4105ea1818cd9656f4b2bf72425213254ced23c0be9c3d244710d6fe5edb2", - "gen_hash_rust": "sha256:919dc613502d5c25810b958a036ac8422ea7a836bb513e0e033bf93a03db2036", - "gen_hash_verilog": "sha256:ee8237f39b07d795d71793f91ad289b04601c0cc18d193329ec8a5554173d5f7", - "gen_hash_zig": "sha256:459d4de242bb3c57fcd1d3cdbaf3bf4a6fa7bd5603c400f962178ee1098ac5f5", + "gen_hash_c": "sha256:f08c884a578db89376b851a66caa5994ebb13e44dc6f1e30ec21d1dd778614a5", + "gen_hash_rust": "sha256:8e5dd78e8b1283cdf2b524111318fb4f1b5854ae36409e22624866257f056d63", + "gen_hash_verilog": "sha256:82b00c47b7ea0232a322e2361f7faa57c70a7b9ddfb5d10778fea77fcfb4fcd8", + "gen_hash_zig": "sha256:661188e906f0212e2e20d08b58992e4a2ff65be5ab3fa818965edd251845eeb0", "module": "T27oRust", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T05:31:45Z", + "sealed_at": "2026-10-19T06:39:05Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:b01dc84ffec00c49315231af4ab32b216412dd09298204831a545cd1ab09f9cd", + "spec_hash": "sha256:61a09c15f5ef8778eb36b081bd9146ec411f10c7786b5d414b59e3499172f0ac", "spec_path": "specs/tools/t27o/rust_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 290, - "total": 290, + "passed": 292, + "total": 292, "vacuous": 0 }, "toolchain": { diff --git a/gen/rust/tools/t27o/rust_convert.rs b/gen/rust/tools/t27o/rust_convert.rs index 54e7e46ad0..3b0c43cacd 100644 --- a/gen/rust/tools/t27o/rust_convert.rs +++ b/gen/rust/tools/t27o/rust_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 290 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 292 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -2770,6 +2770,46 @@ pub fn find_struct(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { return src.len(); } +pub fn unit_hit(src: &[u8], q: usize, a: usize, b: usize) -> bool { + let e: usize = tok_end(src, q); + if !(word_is(src, q, e, b"struct")) { + return false; + } + let n: usize = skip_ws(src, e); + let ne: usize = tok_end(src, n); + return (same_text(src, n, ne, a, b) && tok_is(src, skip_ws(src, ne), b";")); +} + +pub fn unit_struct(src: &[u8], st: &mut [usize], a: usize, b: usize) -> bool { + let mut na: usize = a; + let mut nb: usize = b; + if word_is(src, a, b, b"Self") { + na = st[(ILO) as usize]; + nb = st[(IHI) as usize]; + } + if (nb <= na) { + return false; + } + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_ITEM, na, nb); + while (s != 0) { + if unit_hit(src, st[((s + 2)) as usize], na, nb) { + return true; + } + s = ix_next(src, st, s, na, nb); + } + return false; + } + let mut q: usize = skip_ws(src, 0); + while (q < src.len()) { + if unit_hit(src, q, na, nb) { + return true; + } + q = skip_ws(src, tok_end(src, q)); + } + return false; +} + pub fn field_of_class(src: &[u8], st: &mut [usize], xa: usize, xe: usize, fa: usize, fe: usize) -> u8 { let mut d: usize = decl_type(src, st[(FLO) as usize], xa, xa, xe); if (d >= src.len()) { @@ -4591,7 +4631,7 @@ pub fn path(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, b0: usize) return (); } put_owner(src, st, out, a, b); - if upper_const(src, ma, mb) { + if (upper_const(src, ma, mb) && !(enum_has(src, st[(ILO) as usize], st[(IHI) as usize], a, b, ma, mb))) { puts(st, out, b"_"); } else { puts(st, out, b"."); @@ -4982,6 +5022,9 @@ pub fn primary(src: &[u8], st: &mut [usize], out: &mut [u8], ns: bool) -> () { return (); } put_owner(src, st, out, a, b); + if (is_upper(c) && unit_struct(src, st, a, b)) { + puts(st, out, b"{}"); + } } pub fn open_block(src: &[u8], st: &mut [usize], out: &mut [u8]) -> bool { @@ -6346,7 +6389,7 @@ pub fn pattern(src: &[u8], st: &mut [usize], out: &mut [u8], sa: usize, tmp: usi put_scrut(src, st, out, sa, tmp, wrapit); puts(st, out, b" == "); put_owner(src, st, out, a, b); - if upper_const(src, ma, mb) { + if (upper_const(src, ma, mb) && !(enum_has(src, st[(ILO) as usize], st[(IHI) as usize], a, b, ma, mb))) { puts(st, out, b"_"); } else { puts(st, out, b"."); @@ -7559,6 +7602,32 @@ pub fn enum_variant(src: &[u8], lb: usize, k: usize, count: bool) -> usize { return enum_variant(src, lb, (k % n), false); } +pub fn enum_has(src: &[u8], ilo: usize, ihi: usize, oa: usize, ob: usize, ma: usize, mb: usize) -> bool { + let mut a: usize = oa; + let mut b: usize = ob; + if word_is(src, oa, ob, b"Self") { + a = ilo; + b = ihi; + } + if (b <= a) { + return false; + } + let lb: usize = find_enum(src, a, b); + if (lb >= src.len()) { + return false; + } + let n: usize = enum_variant(src, lb, 0, true); + let mut k: usize = 0; + while (k < n) { + let v: usize = enum_variant(src, lb, k, false); + if ((v < src.len()) && same_text(src, v, tok_end(src, v), ma, mb)) { + return true; + } + k = (k + 1); + } + return false; +} + pub fn vlen(src: &[u8], ilo: usize, ihi: usize, kind: usize, tq: usize) -> usize { if (kind == V_ENUM) { let en: usize = enum_at(src, ilo, ihi, tq); diff --git a/specs/tools/t27o/rust_convert.t27 b/specs/tools/t27o/rust_convert.t27 index 755e9dca35..1eafa51598 100644 --- a/specs/tools/t27o/rust_convert.t27 +++ b/specs/tools/t27o/rust_convert.t27 @@ -2470,6 +2470,41 @@ pub fn find_struct(src: []const u8, st: []usize, a: usize, b: usize) -> usize { return src.len; } +/// Is the token at src[q] the `struct` of `struct ;` (a unit struct), the name src[a .. b]? +pub fn unit_hit(src: []const u8, q: usize, a: usize, b: usize) -> bool { + const e : usize = tok_end(src, q); + if (!word_is(src, q, e, "struct")) { return false; } + const n : usize = skip_ws(src, e); + const ne : usize = tok_end(src, n); + return same_text(src, n, ne, a, b) and tok_is(src, skip_ws(src, ne), ";"); +} + +/// Is src[a .. b] a unit struct of this file (`struct Name;`)? `Self` is the impl's type. Its value is +/// written `Name{}`: a bare `Name` is the type, which t27c rejects where a value stands (probe u1 of #10905). +pub fn unit_struct(src: []const u8, st: []usize, a: usize, b: usize) -> bool { + var na : usize = a; + var nb : usize = b; + if (word_is(src, a, b, "Self")) { + na = st[ILO]; + nb = st[IHI]; + } + if (nb <= na) { return false; } + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_ITEM, na, nb); + while (s != 0) { + if (unit_hit(src, st[s + 2], na, nb)) { return true; } + s = ix_next(src, st, s, na, nb); + } + return false; + } + var q : usize = skip_ws(src, 0); + while (q < src.len) { + if (unit_hit(src, q, na, nb)) { return true; } + q = skip_ws(src, tok_end(src, q)); + } + return false; +} + /// The class of the field src[fa .. fe] of src[xa .. xe]'s declared type's struct (`node.value` where /// `node: &Node`), C_ANY when x has no declared struct type or the field is not one of its fields. pub fn field_of_class(src: []const u8, st: []usize, xa: usize, xe: usize, fa: usize, fe: usize) -> u8 { @@ -4050,7 +4085,7 @@ pub fn path(src: []const u8, st: []usize, out: []u8, a0: usize, b0: usize) -> vo return; } put_owner(src, st, out, a, b); - if (upper_const(src, ma, mb)) { puts(st, out, "_"); } else { puts(st, out, "."); } + if (upper_const(src, ma, mb) and !enum_has(src, st[ILO], st[IHI], a, b, ma, mb)) { puts(st, out, "_"); } else { puts(st, out, "."); } put_src(src, st, out, ma, mb); } @@ -4385,6 +4420,7 @@ pub fn primary(src: []const u8, st: []usize, out: []u8, ns: bool) -> void { return; } put_owner(src, st, out, a, b); + if (is_upper(c) and unit_struct(src, st, a, b)) { puts(st, out, "{}"); } } // ---------------------------------------------------------------- statements @@ -5633,7 +5669,7 @@ pub fn pattern(src: []const u8, st: []usize, out: []u8, sa: usize, tmp: usize, w put_scrut(src, st, out, sa, tmp, wrapit); puts(st, out, " == "); put_owner(src, st, out, a, b); - if (upper_const(src, ma, mb)) { puts(st, out, "_"); } else { puts(st, out, "."); } + if (upper_const(src, ma, mb) and !enum_has(src, st[ILO], st[IHI], a, b, ma, mb)) { puts(st, out, "_"); } else { puts(st, out, "."); } put_src(src, st, out, ma, mb); return; } @@ -6606,6 +6642,29 @@ pub fn enum_variant(src: []const u8, lb: usize, k: usize, count: bool) -> usize return enum_variant(src, lb, k % n, false); } +/// Is src[ma .. mb] a variant of the enum src[oa .. ob] declared in this file (`Self` is the impl's type src[ilo .. ihi])? +/// A variant written in capitals (`GF16`, `F32`, `X402`) looks like an associated const to upper_const; +/// it is `Owner.VARIANT`, not `Owner_VARIANT`, which nothing declares (#10905). +pub fn enum_has(src: []const u8, ilo: usize, ihi: usize, oa: usize, ob: usize, ma: usize, mb: usize) -> bool { + var a : usize = oa; + var b : usize = ob; + if (word_is(src, oa, ob, "Self")) { + a = ilo; + b = ihi; + } + if (b <= a) { return false; } + const lb : usize = find_enum(src, a, b); + if (lb >= src.len) { return false; } + const n : usize = enum_variant(src, lb, 0, true); + var k : usize = 0; + while (k < n) { + const v : usize = enum_variant(src, lb, k, false); + if (v < src.len and same_text(src, v, tok_end(src, v), ma, mb)) { return true; } + k = k + 1; + } + return false; +} + /// How many sample values the oracle has for a kind (for an enum, its variants; for an array, its /// element's). ilo .. ihi is the impl's type (an enum's `Self`). pub fn vlen(src: []const u8, ilo: usize, ihi: usize, kind: usize, tq: usize) -> usize { @@ -14318,6 +14377,56 @@ test put_limit_writes_the_extremes_of_an_integer_type { assert(st[W] == 0); } +test unit_hit_unit_struct_and_enum_has_read_a_unit_struct_and_the_variants_of_an_enum { + const src : str = "struct Gw;\nstruct Pair { a: u8 }\nstruct Wrap(u8);\nenum Fmt { F32, #[x] GF16, Plain }\nenum Empty { }\nGw Pair Wrap Fmt Empty GF16 F32 Plain Zz Self"; + assert(unit_hit(src, 0, 100, 102)); + assert(!unit_hit(src, 0, 103, 107)); + assert(!unit_hit(src, 11, 103, 107)); + assert(!unit_hit(src, 33, 108, 112)); + assert(!unit_hit(src, 50, 113, 116)); + assert(!unit_hit(src, 100, 100, 102)); + var st : [1024]usize = [0] ** 1024; + index_decls(src, &st); + assert(st[IXON] == 1); + var mode : usize = 0; + while (mode < 2) { + st[IXON] = 1 - mode; + assert(unit_struct(src, &st, 100, 102)); + assert(!unit_struct(src, &st, 103, 107)); + assert(!unit_struct(src, &st, 108, 112)); + assert(!unit_struct(src, &st, 113, 116)); + assert(!unit_struct(src, &st, 138, 140)); + assert(!unit_struct(src, &st, 141, 145)); + st[ILO] = 100; + st[IHI] = 102; + assert(unit_struct(src, &st, 141, 145)); + st[ILO] = 103; + st[IHI] = 107; + assert(!unit_struct(src, &st, 141, 145)); + st[ILO] = 0; + st[IHI] = 0; + mode = mode + 1; + } + assert(enum_has(src, 0, 0, 113, 116, 128, 131)); + assert(enum_has(src, 0, 0, 113, 116, 123, 127)); + assert(enum_has(src, 0, 0, 113, 116, 132, 137)); + assert(!enum_has(src, 0, 0, 113, 116, 138, 140)); + assert(!enum_has(src, 0, 0, 113, 116, 113, 116)); + assert(!enum_has(src, 0, 0, 117, 122, 123, 127)); + assert(!enum_has(src, 0, 0, 138, 140, 123, 127)); + assert(!enum_has(src, 0, 0, 141, 145, 123, 127)); + assert(enum_has(src, 113, 116, 141, 145, 123, 127)); + assert(!enum_has(src, 117, 122, 141, 145, 123, 127)); +} + +test an_all_caps_variant_is_owner_dot_variant_and_a_unit_struct_value_is_a_literal { + var out : [2048]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub struct Gw;\nimpl Gw {\n pub const LIMIT: u8 = 4;\n pub fn new() -> Self { Self }\n pub fn lim() -> u8 { Gw::LIMIT }\n}\npub struct Pair { a: u8 }\npub enum Fmt { F32, GF16, Plain }\nimpl Fmt {\n pub fn bits(&self) -> u8 { match self { Fmt::F32 => 32, Fmt::GF16 => 16, Fmt::Plain => 8 } }\n pub fn first() -> Self { Self::GF16 }\n}\nfn make() -> Gw { Gw }\nfn is_gf(f: Fmt) -> bool { f == Fmt::GF16 }\n#[test]\nfn t() {\n let g = Gw;\n assert!(is_gf(Fmt::first()));\n assert_eq!(Gw::lim(), 4);\n}", &out, &st); + assert(out_is(n, &out, 0, "pub const Gw = struct {};\n\npub const Gw_LIMIT : u8 = 4;\n\npub fn Gw_new() -> Gw {\n return Gw{};\n}\n\npub fn Gw_lim() -> u8 {\n return Gw_LIMIT;\n}\n\npub const Pair = struct {\n a : u8,\n};\n\npub const Fmt = enum(u32) {\n F32,\n GF16,\n Plain,\n};\n\npub fn Fmt_bits(self: Fmt) -> u8 {\n if (self == Fmt.F32) {\n return 32;\n } else if (self == Fmt.GF16) {\n return 16;\n } else {\n return 8;\n }\n}\n\npub fn Fmt_first() -> Fmt {\n return Fmt.GF16;\n}\n\nfn make() -> Gw {\n return Gw{};\n}\n\nfn is_gf(f: Fmt) -> bool {\n return f == Fmt.GF16;\n}\n\ntest t {\n const g = Gw{};\n assert(is_gf(Fmt_first()));\n assert(Gw_lim() == 4);\n}\n\n")); + assert(st[NOK] == 7); +} + test path_prefix_path_and_the_module_paths_write_a_qualified_name { assert(path_prefix("crate", 0, 5)); assert(path_prefix("self", 0, 4)); From 9fd623f086dce972eca2baaa04eee702a31b01f6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 13:46:52 +0700 Subject: [PATCH 2/6] t27o: a discard in a test is const _ = e; -- the form t27c keeps as Zig's _ = e (Closes #10905) Class 3 of #10905. `let _ = Type::new();` in a Rust test became `_ = e;` in the t27 test block, which t27c writes as `const _ = e;` in Zig, a declaration Zig rejects ("'_' used as an identifier without @\"_\" syntax"); eight BR_OUTPUT specs of the trios rings were blocked on it. t27c's own spelling `const _ = e;` is the discard in both places (W608), so a test block now writes that. A fn body keeps `_ = e;`: its output is byte for byte what it was. The state vector gains INTEST (slot 53, 1 while the body of a #[test] fn is read); STATE, IXHIT .. IXTOP and NAMES move up by one slot, every user of them names the constants. Test: a Rust fn and a #[test] fn side by side, let_stmt with the flag up and down, a used binding as the negative control. t27b 293 / 293, seal re-saved and verified, the output of the new test compiled under t27c test-report and ran under t27b test. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oRust.json | 16 +++++----- gen/rust/tools/t27o/rust_convert.rs | 26 ++++++++++------ specs/tools/t27o/rust_convert.t27 | 46 ++++++++++++++++++++++++----- 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/.trinity/seals/t27o_T27oRust.json b/.trinity/seals/t27o_T27oRust.json index 8934957241..d215b9536a 100644 --- a/.trinity/seals/t27o_T27oRust.json +++ b/.trinity/seals/t27o_T27oRust.json @@ -2,23 +2,23 @@ "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:f08c884a578db89376b851a66caa5994ebb13e44dc6f1e30ec21d1dd778614a5", - "gen_hash_rust": "sha256:8e5dd78e8b1283cdf2b524111318fb4f1b5854ae36409e22624866257f056d63", - "gen_hash_verilog": "sha256:82b00c47b7ea0232a322e2361f7faa57c70a7b9ddfb5d10778fea77fcfb4fcd8", - "gen_hash_zig": "sha256:661188e906f0212e2e20d08b58992e4a2ff65be5ab3fa818965edd251845eeb0", + "gen_hash_c": "sha256:255942c7fc76298e91fc92b6077365b90062e0693ab15d756cb7d8cc4411a4bf", + "gen_hash_rust": "sha256:621ecd385613a6cca58ffb391bd89af037843b8cd0fdcb9e4ce4b6d9c1f29ad4", + "gen_hash_verilog": "sha256:27cb51a4c89d0c0f7da15e62342a70110bc97e043f6b2dbec38ff05d944c6ff0", + "gen_hash_zig": "sha256:10c8d02b8cc1e38cfcffde1cfc08a0d15e761300819419cbe873e45ed6a5c239", "module": "T27oRust", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T06:39:05Z", + "sealed_at": "2026-10-19T06:46:43Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:61a09c15f5ef8778eb36b081bd9146ec411f10c7786b5d414b59e3499172f0ac", + "spec_hash": "sha256:457036006e4429b6271aed6661ab5ef5aa2ab609aac81ef5bc2885c751ceb95a", "spec_path": "specs/tools/t27o/rust_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 292, - "total": 292, + "passed": 293, + "total": 293, "vacuous": 0 }, "toolchain": { diff --git a/gen/rust/tools/t27o/rust_convert.rs b/gen/rust/tools/t27o/rust_convert.rs index 3b0c43cacd..838d4bb5d2 100644 --- a/gen/rust/tools/t27o/rust_convert.rs +++ b/gen/rust/tools/t27o/rust_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 292 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 293 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -112,19 +112,21 @@ pub const LNPOS: usize = 51; pub const LNNL: usize = 52; -pub const STATE: usize = 53; +pub const INTEST: usize = 53; -pub const IXHIT: usize = 53; +pub const STATE: usize = 54; -pub const IXON: usize = 54; +pub const IXHIT: usize = 54; -pub const IXB: usize = 55; +pub const IXON: usize = 55; -pub const IXBN: usize = 56; +pub const IXB: usize = 56; -pub const IXTOP: usize = 57; +pub const IXBN: usize = 57; -pub const NAMES: usize = 58; +pub const IXTOP: usize = 58; + +pub const NAMES: usize = 59; pub const NAME_SLOTS: usize = 5; @@ -5331,7 +5333,11 @@ pub fn let_stmt(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { try_stmt(src, st, out); return (); } - puts(st, out, b"_ = "); + if (st[(INTEST) as usize] == 1) { + puts(st, out, b"const _ = "); + } else { + puts(st, out, b"_ = "); + } expr(src, st, out, false); want(src, st, b";"); puts(st, out, b";"); @@ -9946,7 +9952,9 @@ pub fn fn_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is_te if at(src, st[(P) as usize], b"->") { refuse_here(src, st, R_SYNTAX); } + st[(INTEST) as usize] = 1; block(src, st, out, M_STMT); + st[(INTEST) as usize] = 0; } else { let pa: usize = st[(P) as usize]; let bb: usize = find_at(src, pn, B_LBRACE); diff --git a/specs/tools/t27o/rust_convert.t27 b/specs/tools/t27o/rust_convert.t27 index 1eafa51598..8b515070c0 100644 --- a/specs/tools/t27o/rust_convert.t27 +++ b/specs/tools/t27o/rust_convert.t27 @@ -192,16 +192,19 @@ pub const DCAP : usize = 64; /// The line cache of line_at: the position of the last line asked for, and the newlines before it. pub const LNPOS : usize = 51; pub const LNNL : usize = 52; -pub const STATE : usize = 53; +/// 1 while the body of a `#[test]` fn is read: t27c spells the discard `_ = e;` as `const _ = e;` there, a +/// declaration Zig rejects (#10905), so a test discards with `const _ = e;` and a fn with `_ = e;`. +pub const INTEST : usize = 53; +pub const STATE : usize = 54; /// The declaration index (#10544), slots the passes keep (STATE slots start every pass over): the slot of /// the entry the last lookup found (0 when none), 1 once the index is built and whole, where its buckets /// begin, how many buckets a kind has, and the lowest slot an entry takes (the name table stops there). -pub const IXHIT : usize = 53; -pub const IXON : usize = 54; -pub const IXB : usize = 55; -pub const IXBN : usize = 56; -pub const IXTOP : usize = 57; -pub const NAMES : usize = 58; +pub const IXHIT : usize = 54; +pub const IXON : usize = 55; +pub const IXB : usize = 56; +pub const IXBN : usize = 57; +pub const IXTOP : usize = 58; +pub const NAMES : usize = 59; pub const NAME_SLOTS : usize = 5; pub const F_NONE : usize = 0; @@ -4683,7 +4686,8 @@ pub fn let_stmt(src: []const u8, st: []usize, out: []u8) -> void { try_stmt(src, st, out); return; } - puts(st, out, "_ = "); + // a test block discards with `const _ = e;`: t27c writes a bare `_ = e;` there as `const _ = e;` in Zig + if (st[INTEST] == 1) { puts(st, out, "const _ = "); } else { puts(st, out, "_ = "); } expr(src, st, out, false); want(src, st, ";"); puts(st, out, ";"); @@ -8537,7 +8541,9 @@ pub fn fn_item(src: []const u8, st: []usize, out: []u8, start: usize, is_test: b want(src, st, "("); want(src, st, ")"); if (at(src, st[P], "->")) { refuse_here(src, st, R_SYNTAX); } + st[INTEST] = 1; block(src, st, out, M_STMT); + st[INTEST] = 0; } else { const pa : usize = st[P]; // a method that never names its self writes it self_: t27c's gen drops a discard of a param @@ -14427,6 +14433,30 @@ test an_all_caps_variant_is_owner_dot_variant_and_a_unit_struct_value_is_a_liter assert(st[NOK] == 7); } +test a_discard_in_a_test_is_const_underscore_and_in_a_fn_a_bare_underscore { + var out : [1024]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub struct Unit;\nimpl Unit { pub fn new() -> Self { Unit } }\nfn f(a: u8) -> u8 {\n let _ = Unit::new();\n let _keep = a;\n a\n}\n#[test]\nfn t() {\n let _ = Unit::new();\n let _x = f(1);\n assert_eq!(f(2), 2);\n}", &out, &st); + assert(out_is(n, &out, 0, "pub const Unit = struct {};\n\npub fn Unit_new() -> Unit {\n return Unit{};\n}\n\nfn f(a: u8) -> u8 {\n _ = Unit_new();\n _ = a;\n return a;\n}\n\ntest t {\n const _ = Unit_new();\n const _ = f(1);\n assert(f(2) == 2);\n}\n\n")); + assert(st[INTEST] == 0); + clear(&st); + st[INTEST] = 1; + let_stmt("let _ = a;", &st, &out); + assert(out_is(st[W], &out, 0, "const _ = a;")); + clear(&st); + assert(st[INTEST] == 0); + let_stmt("let _ = a;", &st, &out); + assert(out_is(st[W], &out, 0, "_ = a;")); + clear(&st); + st[INTEST] = 1; + let_stmt("let _unused = f(a);", &st, &out); + assert(out_is(st[W], &out, 0, "const _ = f(a);")); + clear(&st); + st[INTEST] = 1; + let_stmt("let k = 5;", &st, &out); + assert(out_is(st[W], &out, 0, "const k = 5;")); +} + test path_prefix_path_and_the_module_paths_write_a_qualified_name { assert(path_prefix("crate", 0, 5)); assert(path_prefix("self", 0, 4)); From b1756117dd46c4514836fef7c50ae66d3674c9d9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 14:15:12 +0700 Subject: [PATCH 3/6] t27o: an enum with a negative discriminant takes a signed tag, one past u32 a 64-bit tag (Closes #10905) Class 2 of #10905. enum_item wrote every unit enum as enum(u32), so `Neg = -1` was Zig's "type 'u32' cannot represent integer value '-1'" and nine specs (the Trit enums of trinity, trinity-fpga, trios-ternary, trios-tri, and rings 092, 093, 097) were blocked on it. enum_tag reads the discriminants -- the plain integer literals (decimal, 0x, 0o, 0b, _, a suffix) and the variants after them, one more than the one before; an expression leaves the ones after it unknown -- and picks the tag: i32 when one is negative, i64 when a value is also outside i32, u64 when one is past u32 and none is negative, u32 otherwise. t27c and t27b both take enum(i32), enum(i64) and enum(u64) (probed). An enum with no explicit negative or wide value is written exactly as before. Tests: enum_tag over sixteen bodies (an attribute, hex, an expression, an unclosed or missing brace, the i32 and u32 edges, an implicit successor crossing u32::MAX), and one body whose three enums read i32, u64, u32. t27b 295 / 295, seal re-saved and verified. What the nine specs hit next (an enum cast to an integer, arithmetic on enum values) is the last commit. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oRust.json | 16 ++-- gen/rust/tools/t27o/rust_convert.rs | 112 +++++++++++++++++++++++++++- specs/tools/t27o/rust_convert.t27 | 104 +++++++++++++++++++++++++- 3 files changed, 221 insertions(+), 11 deletions(-) diff --git a/.trinity/seals/t27o_T27oRust.json b/.trinity/seals/t27o_T27oRust.json index d215b9536a..6525ac6221 100644 --- a/.trinity/seals/t27o_T27oRust.json +++ b/.trinity/seals/t27o_T27oRust.json @@ -2,23 +2,23 @@ "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:255942c7fc76298e91fc92b6077365b90062e0693ab15d756cb7d8cc4411a4bf", - "gen_hash_rust": "sha256:621ecd385613a6cca58ffb391bd89af037843b8cd0fdcb9e4ce4b6d9c1f29ad4", - "gen_hash_verilog": "sha256:27cb51a4c89d0c0f7da15e62342a70110bc97e043f6b2dbec38ff05d944c6ff0", - "gen_hash_zig": "sha256:10c8d02b8cc1e38cfcffde1cfc08a0d15e761300819419cbe873e45ed6a5c239", + "gen_hash_c": "sha256:3aa101b96f28e5156f366a3ea77fb47a33472fa993ca5db9ec7abb3162b43df5", + "gen_hash_rust": "sha256:655977f5b748ba8e6dfecea877b7ae8780e58bcbe7f6f2c9c573aa2cb131ef71", + "gen_hash_verilog": "sha256:546e54f9c7ed28e91739ad0d85c7db11aa9d16d18193e55ce93df48e76d1af6f", + "gen_hash_zig": "sha256:a79b1949a69b9fa746a8f638ba972500ad26b9c07a290fef41596818d1db9b01", "module": "T27oRust", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T06:46:43Z", + "sealed_at": "2026-10-19T07:14:47Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:457036006e4429b6271aed6661ab5ef5aa2ab609aac81ef5bc2885c751ceb95a", + "spec_hash": "sha256:b24e3460c8eaa984b399e7151b9a851de50e2e81ab52efdd07cde1975156d5aa", "spec_path": "specs/tools/t27o/rust_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 293, - "total": 293, + "passed": 295, + "total": 295, "vacuous": 0 }, "toolchain": { diff --git a/gen/rust/tools/t27o/rust_convert.rs b/gen/rust/tools/t27o/rust_convert.rs index 838d4bb5d2..fd6cdf568d 100644 --- a/gen/rust/tools/t27o/rust_convert.rs +++ b/gen/rust/tools/t27o/rust_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 293 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 295 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -10182,6 +10182,111 @@ pub fn struct_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, i } } +pub const ET_U32: usize = 0; + +pub const ET_I32: usize = 1; + +pub const ET_I64: usize = 2; + +pub const ET_U64: usize = 3; + +pub const ET_NAMES: [&'static str; 4] = ["u32", "i32", "i64", "u64"]; + +pub fn enum_tag(src: &[u8], lb: usize) -> usize { + if ((lb >= src.len()) || (src[(lb) as usize] != B_LBRACE)) { + return ET_U32; + } + let mut stop: usize = block_end(src, lb); + if ((stop > (lb + 1)) && (src[((stop - 1)) as usize] == B_RBRACE)) { + stop = (stop - 1); + } + let mut neg: bool = false; + let mut wide: bool = false; + let mut big: bool = false; + let mut cneg: bool = false; + let mut cmag: usize = 0; + let mut known: bool = true; + let mut first: bool = true; + let mut q: usize = skip_ws(src, (lb + 1)); + while ((q < stop) && !(tok_is(src, q, b"}"))) { + while tok_is(src, q, b"#") { + q = skip_ws(src, block_end(src, skip_ws(src, (q + 1)))); + } + let mut end: usize = find_at(src, q, B_COMMA); + if (end > stop) { + end = stop; + } + let eq: usize = find_at(src, q, B_EQ); + if (eq < end) { + let mut d: usize = skip_ws(src, (eq + 1)); + let mut ng: bool = false; + if tok_is(src, d, b"-") { + ng = true; + d = skip_ws(src, tok_end(src, d)); + } + let de: usize = tok_end(src, d); + let mut m: usize = NUM_REFUSED; + if (((d < end) && is_digit(src[(d) as usize])) && (skip_ws(src, de) >= end)) { + m = num_value(src, d, de); + } + if (m < NUM_REFUSED) { + cneg = (ng && (m > 0)); + cmag = m; + known = true; + } else { + known = false; + } + } else { + if first { + cneg = false; + cmag = 0; + } else { + if (known && cneg) { + cmag = (cmag - 1); + if (cmag == 0) { + cneg = false; + } + } else { + if known { + cmag = (cmag + 1); + } + } + } + } + first = false; + if (known && cneg) { + neg = true; + if (cmag > 2147483648) { + wide = true; + } + } else { + if known { + if (cmag > 2147483647) { + wide = true; + } + if (cmag > 4294967295) { + big = true; + } + } + } + if (end >= stop) { + q = stop; + } else { + q = skip_ws(src, (end + 1)); + } + } + if (neg && wide) { + return ET_I64; + } + if neg { + return ET_I32; + } + if big { + return ET_U64; + } + return ET_U32; +} + pub fn enum_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is_pub: bool) -> () { let kpos: usize = st[(P) as usize]; let w0: usize = st[(W) as usize]; @@ -10201,7 +10306,10 @@ pub fn enum_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is_ puts(st, out, b"const "); } put_ident(src, st, out, na, nb); - puts(st, out, b" = enum(u32) "); + let tag: &'static str = ET_NAMES[(enum_tag(src, st[(P) as usize])) as usize]; + puts(st, out, b" = enum("); + puts(st, out, tag.as_bytes()); + puts(st, out, b") "); if open_block(src, st, out) { while (live(src, st[(P) as usize], st[(ERR) as usize]) && !(at(src, st[(P) as usize], b"}"))) { skip_attrs(src, st); diff --git a/specs/tools/t27o/rust_convert.t27 b/specs/tools/t27o/rust_convert.t27 index 8b515070c0..7ae9d26ca1 100644 --- a/specs/tools/t27o/rust_convert.t27 +++ b/specs/tools/t27o/rust_convert.t27 @@ -8721,6 +8721,80 @@ pub fn struct_item(src: []const u8, st: []usize, out: []u8, start: usize, is_pub if (refused) { add_name(src, st, K_ITEM, na, nb, 0, 0); } } +/// The tag types an enum is written with: u32 (what every unit enum has been), and the one a discriminant +/// asks for when it does not fit u32 -- a negative value needs a signed tag (`Neg = -1` in a u32 enum is +/// Zig's "type 'u32' cannot represent integer value '-1'", #10905), a value past 2^32 a 64-bit one. +pub const ET_U32 : usize = 0; +pub const ET_I32 : usize = 1; +pub const ET_I64 : usize = 2; +pub const ET_U64 : usize = 3; +pub const ET_NAMES : [4]str = ["u32", "i32", "i64", "u64"]; + +/// The tag type of the enum whose body opens at src[lb]: ET_U32 unless a discriminant needs another. The +/// discriminants are the plain integer literals (`= 5`, `= -1`, `= 0x10`) and the variants after them (one +/// more than the one before); a discriminant that is an expression leaves the ones after it unknown, which +/// stay out of the choice. A negative one makes the tag signed (i32, or i64 when a value is outside i32); a +/// value past u32 with none negative makes it u64. +pub fn enum_tag(src: []const u8, lb: usize) -> usize { + if (lb >= src.len or src[lb] != B_LBRACE) { return ET_U32; } + // the `}` that closes the body (past the end when the block is not closed) + var stop : usize = block_end(src, lb); + if (stop > lb + 1 and src[stop - 1] == B_RBRACE) { stop = stop - 1; } + var neg : bool = false; + var wide : bool = false; + var big : bool = false; + var cneg : bool = false; + var cmag : usize = 0; + var known : bool = true; + var first : bool = true; + var q : usize = skip_ws(src, lb + 1); + while (q < stop and !tok_is(src, q, "}")) { + while (tok_is(src, q, "#")) { q = skip_ws(src, block_end(src, skip_ws(src, q + 1))); } + var end : usize = find_at(src, q, B_COMMA); + if (end > stop) { end = stop; } + const eq : usize = find_at(src, q, B_EQ); + if (eq < end) { + var d : usize = skip_ws(src, eq + 1); + var ng : bool = false; + if (tok_is(src, d, "-")) { + ng = true; + d = skip_ws(src, tok_end(src, d)); + } + const de : usize = tok_end(src, d); + var m : usize = NUM_REFUSED; + if (d < end and is_digit(src[d]) and skip_ws(src, de) >= end) { m = num_value(src, d, de); } + if (m < NUM_REFUSED) { + cneg = ng and m > 0; + cmag = m; + known = true; + } else { + known = false; + } + } else if (first) { + cneg = false; + cmag = 0; + } else if (known and cneg) { + cmag = cmag - 1; + if (cmag == 0) { cneg = false; } + } else if (known) { + cmag = cmag + 1; + } + first = false; + if (known and cneg) { + neg = true; + if (cmag > 2147483648) { wide = true; } + } else if (known) { + if (cmag > 2147483647) { wide = true; } + if (cmag > 4294967295) { big = true; } + } + if (end >= stop) { q = stop; } else { q = skip_ws(src, end + 1); } + } + if (neg and wide) { return ET_I64; } + if (neg) { return ET_I32; } + if (big) { return ET_U64; } + return ET_U32; +} + pub fn enum_item(src: []const u8, st: []usize, out: []u8, start: usize, is_pub: bool) -> void { const kpos : usize = st[P]; const w0 : usize = st[W]; @@ -8734,7 +8808,11 @@ pub fn enum_item(src: []const u8, st: []usize, out: []u8, start: usize, is_pub: if (at(src, st[P], "<")) { refuse_here(src, st, R_GENERIC); } if (is_pub) { puts(st, out, "pub const "); } else { puts(st, out, "const "); } put_ident(src, st, out, na, nb); - puts(st, out, " = enum(u32) "); + // (gen-rust reads a `str` array element as &str, a local `str` as bytes) + const tag : str = ET_NAMES[enum_tag(src, st[P])]; + puts(st, out, " = enum("); + puts(st, out, tag); + puts(st, out, ") "); if (open_block(src, st, out)) { while (live(src, st[P], st[ERR]) and !at(src, st[P], "}")) { skip_attrs(src, st); @@ -14457,6 +14535,30 @@ test a_discard_in_a_test_is_const_underscore_and_in_a_fn_a_bare_underscore { assert(out_is(st[W], &out, 0, "const k = 5;")); } +test enum_tag_picks_the_tag_type_the_discriminants_need { + const rows : [16]str = ["{ A, B }", "{ Neg = -1, Zero = 0, Pos = 1 }", "{ A = 5000000000, B }", "{ A = -3000000000 }", "{ A = -1, B = 3000000000 }", "{ A = 2147483648 }", "{ A = -2147483648 }", "{ A = -2, B, C, D }", "{ A = 4294967295, B }", "{ A = FOO, B }", "{ A = 1 << 40, B = -1 }", "{ #[x] Neg = -1 }", "{ A = -0x10 }", "{ }", "A, B", "{ A = -0, B = 7u8 }"]; + const want : [16]usize = [ET_U32, ET_I32, ET_U64, ET_I64, ET_I64, ET_U32, ET_I32, ET_I32, ET_U64, ET_U32, ET_I32, ET_I32, ET_I32, ET_U32, ET_U32, ET_U32]; + var i : usize = 0; + while (i < 16) { + assert(enum_tag(rows[i], 0) == want[i]); + i = i + 1; + } + assert(ET_NAMES[ET_U32].len == 3); + assert(word_is(ET_NAMES[ET_U32], 0, 3, "u32")); + assert(word_is(ET_NAMES[ET_I32], 0, 3, "i32")); + assert(word_is(ET_NAMES[ET_I64], 0, 3, "i64")); + assert(word_is(ET_NAMES[ET_U64], 0, 3, "u64")); + assert(enum_tag("x { Neg = -1 }", 2) == ET_I32); + assert(enum_tag("x { Neg = -1 }", 0) == ET_U32); +} + +test a_negative_discriminant_gives_the_enum_a_signed_tag { + var out : [1024]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub enum Trit { Neg = -1, Zero = 0, Pos = 1 }\npub enum Wide { A = 5000000000, B }\nenum Small { A = 3, B }\nfn v(t: Trit) -> i8 { match t { Trit::Neg => -1, Trit::Zero => 0, Trit::Pos => 1 } }", &out, &st); + assert(out_is(n, &out, 0, "pub const Trit = enum(i32) {\n Neg = -1,\n Zero = 0,\n Pos = 1,\n};\n\npub const Wide = enum(u64) {\n A = 5000000000,\n B,\n};\n\nconst Small = enum(u32) {\n A = 3,\n B,\n};\n\nfn v(t: Trit) -> i8 {\n if (t == Trit.Neg) {\n return -1;\n } else if (t == Trit.Zero) {\n return 0;\n } else {\n return 1;\n }\n}\n\n")); +} + test path_prefix_path_and_the_module_paths_write_a_qualified_name { assert(path_prefix("crate", 0, 5)); assert(path_prefix("self", 0, 4)); From 6e9339eea6ac50f4097c2e108ad752e96fbfdc1b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 14:20:52 +0700 Subject: [PATCH 4/6] t27o: a compound shift is a = a >> n, and the second receiver of a trim is the converted receiver (Closes #10905) Class 4 of #10905, the two parse errors the list showed. `c >>= 1` and `e <<= 1` reached the output as written; t27 has no compound shift ("Unexpected token in expression: Equals", t27c and t27b alike). Five specs (host/dma, host/weight_loader, rings 095, 098, 099) held it in a pow or a CRC loop. expr_stmt now writes `c = c >> 1`, the place copied from what was written (put_again) and the amount wrapped unless it is an atom (`c = c << (k + 1)`). The place is written twice, so one that calls something is refused with the new reason R_SHIFTCALL, never called twice. `path[..i]` in a parse-error: the std table's trim forms write the receiver three times, and the second and third copies came from the Rust text, not from the output -- `path[..i].trim()` kept `..i` in two of them (use_resolve.t27, brace_module). expand now copies the receiver as it was written in the output (the same copy `^` makes), so the receiver reads `path[0 .. i]` in all three. The two tests that pinned `a <<= 2` as the output (stmt, expr_stmt) now pin `a = a << 2`. Tests: a body with both shifts, a place that calls (refused, stub kept), expr_stmt directly (self.x, a sum amount, += and ^= unchanged as controls), put_again incl. a buffer too small, and the trim of `path[..i]` beside a plain `s.trim()`. t27b 298 / 298, seal re-saved and verified; the generated shifts and trims compiled under t27c test-report and ran under t27b test. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oRust.json | 16 ++-- gen/rust/tools/t27o/rust_convert.rs | 66 +++++++++++++---- specs/tools/t27o/rust_convert.t27 | 110 ++++++++++++++++++++++++---- 3 files changed, 153 insertions(+), 39 deletions(-) diff --git a/.trinity/seals/t27o_T27oRust.json b/.trinity/seals/t27o_T27oRust.json index 6525ac6221..624bb2b976 100644 --- a/.trinity/seals/t27o_T27oRust.json +++ b/.trinity/seals/t27o_T27oRust.json @@ -2,23 +2,23 @@ "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:3aa101b96f28e5156f366a3ea77fb47a33472fa993ca5db9ec7abb3162b43df5", - "gen_hash_rust": "sha256:655977f5b748ba8e6dfecea877b7ae8780e58bcbe7f6f2c9c573aa2cb131ef71", - "gen_hash_verilog": "sha256:546e54f9c7ed28e91739ad0d85c7db11aa9d16d18193e55ce93df48e76d1af6f", - "gen_hash_zig": "sha256:a79b1949a69b9fa746a8f638ba972500ad26b9c07a290fef41596818d1db9b01", + "gen_hash_c": "sha256:b6fdfaa74635c400e9d3b0a10d76876e56e70fbc0405381d9315ae390fbf9a23", + "gen_hash_rust": "sha256:40389758272c8c50782690107b02984b447c0157ab88fe416181f303de198398", + "gen_hash_verilog": "sha256:771c52521c7169d81fde28329cdee917eb748a70743106ce5ad1f52621f93b8f", + "gen_hash_zig": "sha256:88df6378e78690bc0308cabfa98c0ae92b9e36fda5a1553de0a41619aa077e4b", "module": "T27oRust", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T07:14:47Z", + "sealed_at": "2026-10-19T07:20:21Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:b24e3460c8eaa984b399e7151b9a851de50e2e81ab52efdd07cde1975156d5aa", + "spec_hash": "sha256:f71b9d17935371c41e6fd93d53230438fd3774880a139158e47923b4ff69fc81", "spec_path": "specs/tools/t27o/rust_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 295, - "total": 295, + "passed": 298, + "total": 298, "vacuous": 0 }, "toolchain": { diff --git a/gen/rust/tools/t27o/rust_convert.rs b/gen/rust/tools/t27o/rust_convert.rs index fd6cdf568d..66e874af30 100644 --- a/gen/rust/tools/t27o/rust_convert.rs +++ b/gen/rust/tools/t27o/rust_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 295 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 298 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -306,6 +306,8 @@ pub const R_LIBM: usize = 37; pub const R_SLICEGEN: usize = 38; +pub const R_SHIFTCALL: usize = 39; + pub const VU_READ: usize = 0; pub const VU_CHANGED: usize = 1; @@ -1624,6 +1626,18 @@ pub fn wrap(st: &mut [usize], out: &mut [u8], from: usize) -> () { puts(st, out, b")"); } +pub fn put_again(st: &mut [usize], out: &mut [u8], from: usize, to: usize) -> () { + let mut i: usize = from; + while (i < to) { + if (i < out.len()) { + put(st, out, out[(i) as usize]); + } else { + put(st, out, 0); + } + i = (i + 1); + } +} + pub fn reserved(src: &[u8], a: usize, b: usize) -> bool { return ((((((((((((((((((((((((((word_is(src, a, b, b"and") || word_is(src, a, b, b"or")) || word_is(src, a, b, b"test")) || word_is(src, a, b, b"var")) || word_is(src, a, b, b"error")) || word_is(src, a, b, b"null")) || word_is(src, a, b, b"undefined")) || word_is(src, a, b, b"switch")) || word_is(src, a, b, b"orelse")) || word_is(src, a, b, b"try")) || word_is(src, a, b, b"defer")) || word_is(src, a, b, b"comptime")) || word_is(src, a, b, b"module")) || word_is(src, a, b, b"invariant")) || word_is(src, a, b, b"bench")) || word_is(src, a, b, b"catch")) || word_is(src, a, b, b"unreachable")) || word_is(src, a, b, b"anytype")) || word_is(src, a, b, b"opaque")) || word_is(src, a, b, b"packed")) || word_is(src, a, b, b"export")) || word_is(src, a, b, b"inline")) || word_is(src, a, b, b"align")) || word_is(src, a, b, b"noreturn")) || word_is(src, a, b, b"errdefer")) || word_is(src, a, b, b"spec")) || word_is(src, a, b, b"void")); } @@ -3657,9 +3671,10 @@ pub fn expand(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, k: usi } i = (i + 1); } - let rpos: usize = pos; + let mut rpos: usize = pos; if ((((cls == C_SLICE) && (st[(TYARR) as usize] == 1)) && (h > 0)) && (STD_CLASS[(k) as usize] != C_ANY)) { insert(st, out, pos, b"&"); + rpos = (pos + 1); } if (nargs == 0) { no_args(src, st); @@ -3698,19 +3713,10 @@ pub fn expand(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, k: usi put_src(src, st, out, tyl, tyh); } else { if (c == B_CARET) { - let mut r: usize = 0; - while (r < rlen) { - let rb2: usize = (rpos + r); - if (rb2 < out.len()) { - put(st, out, out[(rb2) as usize]); - } else { - put(st, out, 0); - } - r = (r + 1); - } + put_again(st, out, rpos, (rpos + rlen)); } else { if ((c == B_HASH) && (ra < rb)) { - put_src(src, st, out, ra, rb); + put_again(st, out, rpos, (rpos + rlen)); } else { put(st, out, c); } @@ -5292,8 +5298,32 @@ pub fn assign_op(src: &[u8], p: usize) -> bool { } pub fn expr_stmt(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + let lo: usize = st[(W) as usize]; expr(src, st, out, false); if (live(src, st[(P) as usize], st[(ERR) as usize]) && assign_op(src, st[(P) as usize])) { + let oa: usize = st[(P) as usize]; + let ob: usize = tok_end(src, oa); + if (word_is(src, oa, ob, b"<<=") || word_is(src, oa, ob, b">>=")) { + let hi: usize = st[(W) as usize]; + if out_has(hi, out, lo, B_LPAREN) { + refuse(st, R_SHIFTCALL, oa, oa, ob); + return (); + } + puts(st, out, b" = "); + put_again(st, out, lo, hi); + if word_is(src, oa, ob, b"<<=") { + puts(st, out, b" << "); + } else { + puts(st, out, b" >> "); + } + step(src, st); + let rs: usize = st[(W) as usize]; + let rl: usize = expr(src, st, out, false); + if ((rl != L_ATOM) && (rl != L_UNARY)) { + wrap(st, out, rs); + } + return (); + } puts(st, out, b" "); put_src(src, st, out, st[(P) as usize], tok_end(src, st[(P) as usize])); puts(st, out, b" "); @@ -7004,10 +7034,14 @@ pub fn put_reason(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { if (c == R_SLICEGEN) { puts(st, out, b"slice contains on"); } else { - if (c == R_SPLICE) { - puts(st, out, b"a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records)"); + if (c == R_SHIFTCALL) { + puts(st, out, b"a compound shift of a place that calls something"); } else { - puts(st, out, b"syntax"); + if (c == R_SPLICE) { + puts(st, out, b"a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records)"); + } else { + puts(st, out, b"syntax"); + } } } } diff --git a/specs/tools/t27o/rust_convert.t27 b/specs/tools/t27o/rust_convert.t27 index 7ae9d26ca1..5154097296 100644 --- a/specs/tools/t27o/rust_convert.t27 +++ b/specs/tools/t27o/rust_convert.t27 @@ -329,6 +329,9 @@ pub const R_LIBM : usize = 37; /// A slice `contains` on a scalar other than u8: bytes_contain is the one body, t27 has no generics to share it, /// and a copy per element type is refused by the duplicate-bodies ratchet (tools/dupe_scan.py). pub const R_SLICEGEN : usize = 38; +/// A compound shift (`a >>= n`, written `a = a >> n` as t27 has no `>>=`) of a place that calls something: the +/// place would be called twice. +pub const R_SHIFTCALL : usize = 39; /// How a fn uses a vec! local: VU_READ only reads it, VU_CHANGED changes it, VU_MOVED hands it on. pub const VU_READ : usize = 0; pub const VU_CHANGED : usize = 1; @@ -1469,6 +1472,15 @@ pub fn wrap(st: []usize, out: []u8, from: usize) -> void { puts(st, out, ")"); } +/// Write what was written at out[from .. to) once more (a byte past the buffer reads as 0). +pub fn put_again(st: []usize, out: []u8, from: usize, to: usize) -> void { + var i : usize = from; + while (i < to) { + if (i < out.len) { put(st, out, out[i]); } else { put(st, out, 0); } + i = i + 1; + } +} + /// A name t27 reserves; the converter writes it with a trailing `_`. pub fn reserved(src: []const u8, a: usize, b: usize) -> bool { return word_is(src, a, b, "and") or word_is(src, a, b, "or") or word_is(src, a, b, "test") or word_is(src, a, b, "var") or word_is(src, a, b, "error") or word_is(src, a, b, "null") or word_is(src, a, b, "undefined") or word_is(src, a, b, "switch") or word_is(src, a, b, "orelse") or word_is(src, a, b, "try") or word_is(src, a, b, "defer") or word_is(src, a, b, "comptime") or word_is(src, a, b, "module") or word_is(src, a, b, "invariant") or word_is(src, a, b, "bench") or word_is(src, a, b, "catch") or word_is(src, a, b, "unreachable") or word_is(src, a, b, "anytype") or word_is(src, a, b, "opaque") or word_is(src, a, b, "packed") or word_is(src, a, b, "export") or word_is(src, a, b, "inline") or word_is(src, a, b, "align") or word_is(src, a, b, "noreturn") or word_is(src, a, b, "errdefer") or word_is(src, a, b, "spec") or word_is(src, a, b, "void"); @@ -3241,10 +3253,14 @@ pub fn expand(src: []const u8, st: []usize, out: []u8, start: usize, k: usize, r } i = i + 1; } - const rpos : usize = pos; + // where the receiver sits in the output (past the `&` an array receiver takes) + var rpos : usize = pos; // an array receiver goes to a slice parameter by address (a row of any class has the receiver itself as its // subject; one of a slice class takes it as the argument) - if (cls == C_SLICE and st[TYARR] == 1 and h > 0 and STD_CLASS[k] != C_ANY) { insert(st, out, pos, "&"); } + if (cls == C_SLICE and st[TYARR] == 1 and h > 0 and STD_CLASS[k] != C_ANY) { + insert(st, out, pos, "&"); + rpos = pos + 1; + } if (nargs == 0) { no_args(src, st); } else { want(src, st, "("); } var used : bool = false; var j : usize = h + 1; @@ -3277,16 +3293,13 @@ pub fn expand(src: []const u8, st: []usize, out: []u8, start: usize, k: usize, r put_src(src, st, out, tyl, tyh); } else if (c == B_CARET) { // `^`: the receiver as it was written in the output, once more (unwrap_or: the test, then the value) - var r : usize = 0; - while (r < rlen) { - const rb2 : usize = rpos + r; - if (rb2 < out.len) { put(st, out, out[rb2]); } else { put(st, out, 0); } - r = r + 1; - } + put_again(st, out, rpos, rpos + rlen); } else if (c == B_HASH and ra < rb) { // a second # of the form writes the receiver again: the trims index their slice with - // trim_lo / trim_hi calls on it -- the receiver is both the slice subject and the argument - put_src(src, st, out, ra, rb); + // trim_lo / trim_hi calls on it -- the receiver is both the slice subject and the argument. It is + // the receiver as written in the output, not the Rust text: `path[..i].trim()` has `path[0 .. i]` + // there, and its `..i` is not t27 (#10905) + put_again(st, out, rpos, rpos + rlen); } else { put(st, out, c); } @@ -4644,8 +4657,28 @@ pub fn assign_op(src: []const u8, p: usize) -> bool { } pub fn expr_stmt(src: []const u8, st: []usize, out: []u8) -> void { + const lo : usize = st[W]; expr(src, st, out, false); if (live(src, st[P], st[ERR]) and assign_op(src, st[P])) { + const oa : usize = st[P]; + const ob : usize = tok_end(src, oa); + // t27 has no `<<=` / `>>=` (t27c: "Unexpected token in expression: Equals"): `a >>= n` is `a = a >> n`. + // The place is written twice, so one that calls something is refused rather than called twice. + if (word_is(src, oa, ob, "<<=") or word_is(src, oa, ob, ">>=")) { + const hi : usize = st[W]; + if (out_has(hi, out, lo, B_LPAREN)) { + refuse(st, R_SHIFTCALL, oa, oa, ob); + return; + } + puts(st, out, " = "); + put_again(st, out, lo, hi); + if (word_is(src, oa, ob, "<<=")) { puts(st, out, " << "); } else { puts(st, out, " >> "); } + step(src, st); + const rs : usize = st[W]; + const rl : usize = expr(src, st, out, false); + if (rl != L_ATOM and rl != L_UNARY) { wrap(st, out, rs); } + return; + } puts(st, out, " "); put_src(src, st, out, st[P], tok_end(src, st[P])); puts(st, out, " "); @@ -6173,6 +6206,8 @@ pub fn put_reason(src: []const u8, st: []usize, out: []u8) -> void { puts(st, out, "libm function not bit-exact"); } else if (c == R_SLICEGEN) { puts(st, out, "slice contains on"); + } else if (c == R_SHIFTCALL) { + puts(st, out, "a compound shift of a place that calls something"); } else if (c == R_SPLICE) { puts(st, out, "a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records)"); } else { @@ -14559,6 +14594,51 @@ test a_negative_discriminant_gives_the_enum_a_signed_tag { assert(out_is(n, &out, 0, "pub const Trit = enum(i32) {\n Neg = -1,\n Zero = 0,\n Pos = 1,\n};\n\npub const Wide = enum(u64) {\n A = 5000000000,\n B,\n};\n\nconst Small = enum(u32) {\n A = 3,\n B,\n};\n\nfn v(t: Trit) -> i8 {\n if (t == Trit.Neg) {\n return -1;\n } else if (t == Trit.Zero) {\n return 0;\n } else {\n return 1;\n }\n}\n\n")); } +test a_compound_shift_is_written_as_an_assignment_and_a_place_that_calls_is_refused { + var out : [1024]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("fn sh(mut c: u32, k: u32) -> u32 {\n c >>= 1;\n c <<= k + 1;\n c\n}\nfn lo(t: &mut [u32], i: usize) {\n t[i] >>= 2;\n}\nfn h(a: usize) -> usize {\n a\n}\nfn g(t: &mut [u32]) {\n t[h(1)] >>= 1;\n}", &out, &st); + assert(out_is(n, &out, 0, "fn sh(c_in: u32, k: u32) -> u32 {\n var c : u32 = c_in;\n c = c >> 1;\n c = c << (k + 1);\n return c;\n}\n\nfn lo(t: []u32, i: usize) -> void {\n t[i] = t[i] >> 2;\n}\n\nfn h(a: usize) -> usize {\n return a;\n}\n\n// t27o: refused fn g (line 12): a compound shift of a place that calls something `>>=` at line 13\n\nfn g(t: []u32) -> void {\n}\n\n")); + clear(&st); + expr_stmt("a >>= 1", &st, &out); + assert(st[ERR] == 0); + assert(out_is(st[W], &out, 0, "a = a >> 1")); + clear(&st); + expr_stmt("self.x <<= 2 + n", &st, &out); + assert(out_is(st[W], &out, 0, "self.x = self.x << (2 + n)")); + clear(&st); + expr_stmt("t[h(1)] <<= 1", &st, &out); + assert(st[ERR] == R_SHIFTCALL); + clear(&st); + expr_stmt("a += 1", &st, &out); + assert(out_is(st[W], &out, 0, "a += 1")); + clear(&st); + expr_stmt("a ^= b << 1", &st, &out); + assert(out_is(st[W], &out, 0, "a ^= b << 1")); +} + +test put_again_writes_the_output_it_wrote_once_more { + var out : [64]u8 = undefined; + var st : [64]usize = [0] ** 64; + puts(&st, &out, "ab[0 .. i]"); + put_again(&st, &out, 2, 10); + assert(out_is(st[W], &out, 0, "ab[0 .. i][0 .. i]")); + put_again(&st, &out, 5, 5); + assert(st[W] == 18); + var tiny : [4]u8 = undefined; + var ts : [8]usize = [0] ** 8; + puts(&ts, &tiny, "abcd"); + put_again(&ts, &tiny, 2, 8); + assert(ts[W] == 10); +} + +test the_second_receiver_of_a_trim_is_the_converted_receiver_not_the_rust_text { + var out : [1024]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("fn head_len(path: &str, i: usize) -> usize {\n path[..i].trim().len()\n}\nfn plain_len(s: &str) -> usize {\n s.trim().len()\n}", &out, &st); + assert(out_is(n, &out, 0, "use tri::t27b::type_text;\n\nfn head_len(path: []const u8, i: usize) -> usize {\n return path[0 .. i][type_text::trim_lo(path[0 .. i], 0, path[0 .. i].len) .. type_text::trim_hi(path[0 .. i], type_text::trim_lo(path[0 .. i], 0, path[0 .. i].len), path[0 .. i].len)].len;\n}\n\nfn plain_len(s: []const u8) -> usize {\n return s[type_text::trim_lo(s, 0, s.len) .. type_text::trim_hi(s, type_text::trim_lo(s, 0, s.len), s.len)].len;\n}\n\n")); +} + test path_prefix_path_and_the_module_paths_write_a_qualified_name { assert(path_prefix("crate", 0, 5)); assert(path_prefix("self", 0, 4)); @@ -15029,7 +15109,7 @@ test open_block_close_block_block_stmts_and_tail_of_write_a_braced_body { test stmt_writes_one_statement_in_each_of_its_forms { const head : str = "struct Node { v: u8 }\nconst C: u32 = 3;\nfn g(x: u32) -> u32 { x }\nfn f(a: u32, b: u32, ok: bool, s: &str, r: u32) { "; const ins : [35]str = ["let k = 5;", "let mut k = 5;", "let k: u8 = 5;", "let k = 5u8;", "let k: u8;", "let _ = a;", "a = a + 1;", "a += 1;", "a <<= 2;", "g(a);", "return a;", "return;", "break;", "continue;", "if a > 1 { g(a); }", "if a > 1 { g(a); } else { g(b); }", "if a > 1 { g(a); } else if a > 2 { g(b); }", "while a < 3 { a += 1; }", "loop { break; }", "for i in 0..4 { g(i); }", "match a { 1 => { g(1); } _ => { g(2); } }", "assert!(a > 1);", "assert_eq!(a, 1);", "assert_ne!(a, b, \"m\");", "const D: u8 = 1;", "let k: u32 = if ok { 1 } else { 2 };", "let a: [u8; 0] = [];", "a + 1", "g(a)", "if ok { 1 } else { 2 }", "x.0 = 1;", "debug_assert!(ok);", "a;", "s;", "if ok { return; }"]; - const outs : [35]str = ["const k = 5;", "var k = 5;", "const k : u8 = 5;", "const k : u8 = 5;", "var k : u8 = undefined;", "_ = a;", "a = a + 1;", "a += 1;", "a <<= 2;", "g(a);", "return a;", "return;", "break;", "continue;", "if (a > 1) {\n g(a);\n }", "if (a > 1) {\n g(a);\n } else {\n g(b);\n }", "if (a > 1) {\n g(a);\n } else if (a > 2) {\n g(b);\n }", "while (a < 3) {\n a += 1;\n }", "while (true) {\n break;\n }", "for i in 0..4 {\n g(i);\n }", "if (a == 1) {\n g(1);\n } else {\n g(2);\n }", "assert(a > 1);", "assert(a == 1);", "assert(a != b);", "const D : u8 = 1;", "var k : u32 = undefined;\n if (ok) {\n k = 1;\n } else {\n k = 2;\n }", "const a : [0]u8 = [];", "a + 1;", "g(a);", "if (ok) {\n 1;\n } else {\n 2;\n }", "x._0 = 1;", "assert(ok);", "a;", "s;", "if (ok) {\n return;\n }"]; + const outs : [35]str = ["const k = 5;", "var k = 5;", "const k : u8 = 5;", "const k : u8 = 5;", "var k : u8 = undefined;", "_ = a;", "a = a + 1;", "a += 1;", "a = a << 2;", "g(a);", "return a;", "return;", "break;", "continue;", "if (a > 1) {\n g(a);\n }", "if (a > 1) {\n g(a);\n } else {\n g(b);\n }", "if (a > 1) {\n g(a);\n } else if (a > 2) {\n g(b);\n }", "while (a < 3) {\n a += 1;\n }", "while (true) {\n break;\n }", "for i in 0..4 {\n g(i);\n }", "if (a == 1) {\n g(1);\n } else {\n g(2);\n }", "assert(a > 1);", "assert(a == 1);", "assert(a != b);", "const D : u8 = 1;", "var k : u32 = undefined;\n if (ok) {\n k = 1;\n } else {\n k = 2;\n }", "const a : [0]u8 = [];", "a + 1;", "g(a);", "if (ok) {\n 1;\n } else {\n 2;\n }", "x._0 = 1;", "assert(ok);", "a;", "s;", "if (ok) {\n return;\n }"]; var buf : [1024]u8 = [0] ** 1024; var st : [1024]usize = [0] ** 1024; var out : [512]u8 = undefined; @@ -15435,7 +15515,7 @@ test let_stmt_let_value_expr_stmt_and_local_const_write_a_binding { assert(st[P] == 9); clear(&st); expr_stmt("a <<= 2;", &st, &out); - assert(out_is(st[W], &out, 0, "a <<= 2")); + assert(out_is(st[W], &out, 0, "a = a << 2")); clear(&st); expr_stmt("g(a);", &st, &out); assert(out_is(st[W], &out, 0, "g(a)")); @@ -15967,13 +16047,13 @@ test put_docs_writes_the_doc_lines_above_an_item_and_nothing_else { } test put_reason_names_the_refusal_and_the_text_it_points_at { - const codes : [38]usize = [R_SYNTAX, R_GENERIC, R_TRAIT, R_CLOSURE, R_TRY, R_MACRO, R_TYPE, R_METHOD, R_TUPLE, R_IFLET, R_ITER, R_PATTERN, R_MUTREF, R_DEREF, R_VALUE, R_LITERAL, R_LABEL, R_ITEM, R_UNSAFE, R_PATH, R_WHERE, R_SELFMUT, R_UPDATE, R_DATA, R_DEPENDS, R_EXTERNAL, R_UNDECL, R_CLASH, R_STRUCTEQ, R_STRUSE, R_SPLICE, R_EMPTY, R_NOTYPE, R_VECMUT, R_VECMOVE, R_VECTYPE, R_LIBM, R_SLICEGEN]; - const why : [38]str = ["syntax", "generics", "trait impl", "closure", "? operator", "macro", "type", "method", "tuple", "if let / while let", "for over an iterator", "match pattern", "&mut of a value", "deref", "block as a value", "literal", "label or break value", "item", "unsafe", "path", "where clause", "mut self", "struct update", "enum variant with data", "uses refused", "uses external", "undeclared", "a used module's name, declared here too (use splices flat, see #7176)", "== of structs", "an owned String outside the out-buffer form", "a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records)", "empty slice with no known element type", "`!` of an operand whose type t27o cannot tell", "a vec! the fn changes (push, resize, assign into, &mut)", "a vec! the fn hands on or returns", "a vec! whose element type or length t27o cannot tell", "libm function not bit-exact", "slice contains on"]; + const codes : [39]usize = [R_SYNTAX, R_GENERIC, R_TRAIT, R_CLOSURE, R_TRY, R_MACRO, R_TYPE, R_METHOD, R_TUPLE, R_IFLET, R_ITER, R_PATTERN, R_MUTREF, R_DEREF, R_VALUE, R_LITERAL, R_LABEL, R_ITEM, R_UNSAFE, R_PATH, R_WHERE, R_SELFMUT, R_UPDATE, R_DATA, R_DEPENDS, R_EXTERNAL, R_UNDECL, R_CLASH, R_STRUCTEQ, R_STRUSE, R_SPLICE, R_EMPTY, R_NOTYPE, R_VECMUT, R_VECMOVE, R_VECTYPE, R_LIBM, R_SLICEGEN, R_SHIFTCALL]; + const why : [39]str = ["syntax", "generics", "trait impl", "closure", "? operator", "macro", "type", "method", "tuple", "if let / while let", "for over an iterator", "match pattern", "&mut of a value", "deref", "block as a value", "literal", "label or break value", "item", "unsafe", "path", "where clause", "mut self", "struct update", "enum variant with data", "uses refused", "uses external", "undeclared", "a used module's name, declared here too (use splices flat, see #7176)", "== of structs", "an owned String outside the out-buffer form", "a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records)", "empty slice with no known element type", "`!` of an operand whose type t27o cannot tell", "a vec! the fn changes (push, resize, assign into, &mut)", "a vec! the fn hands on or returns", "a vec! whose element type or length t27o cannot tell", "libm function not bit-exact", "slice contains on", "a compound shift of a place that calls something"]; var out : [512]u8 = undefined; var st : [64]usize = [0] ** 64; var w : [512]u8 = [0] ** 512; var i : usize = 0; - while (i < 38) { + while (i < 39) { clear(&st); st[ERR] = codes[i]; st[AT] = 3; From f89b53c03a8d30cddcba6bf63b0bf245b0b33ec1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 16:54:05 +0700 Subject: [PATCH 5/6] t27o: a name the output uses is declared in it -- six ways it was not (Closes #10905) Class 1 of #10905, 15 specs that named something t27c cannot find. The causes, each with the spec it came from: - `Type::f(..)` wrote `Type_f(..)` whether or not an impl declared f: `Language::default()` of a #[derive(Default)] (multibots user.rs) named a `Language_default` no one writes. assoc_declared asks the file; a call nothing answers is refused as undeclared. - A last match arm is the else, so `Some(want) => want == ..` bound nothing and `want` was undeclared (prcheck.rs). payload_binder binds `Some(x)` / `Ok(x)` to the scrutinee's value (`const want : T = e.?;`, T from the Option's declared type, so t27c knows it is a text), lets a payload nobody reads go, and refuses any other payload pattern. - `pub extern "C" fn gf16_to_f32` was refused without its name, so the tests of ffi/src/lib.rs that call it kept the call; an `unsafe fn` registered the name `fn`, which refused every fn after it with "uses refused `fn`". qualified_fn finds the name behind unsafe / extern / async / const; the fns of an `extern "C" { .. }` block are recorded too. - A call into a #[path] gen module t27c cannot splice beside type_text: operator_text has a `use` of its own (compiler.rs, #10843), sha256 and its siblings declare a `same` beside type_text's (main.rs, #10556). The refusal now reads the module's path, not the alias it was mounted under, from a list (SPLICE_CLASH). A gen module's const in a match pattern (`t27a::ASM_NO_MNEMONIC =>`) was written `t27a_ASM_NO_MNEMONIC`; pattern uses the same mod_member as an expression. - t27c writes the Zig prelude (the std import, the assert helper) only for a spec with a test block, so an assert in a fn of a testless spec left `__t27_assert_fail` undeclared (trinity_loss tests/loss.rs). A pass that found no test now refuses such an assert (R_NOPRELUDE), through two flags kept in the name table. Tests: every new fn directly (assoc_declared with and without the index, payload_binder over nine arms, qualified_fn / foreign_block / record_foreign, mod_member, splice_clash, need_prelude, bind_type) and a body test per cause whose output compiled under t27c test-report; the older tests that used operator_text as a stand-in module now use a module that splices, and the known-defect pin of #10843 is flipped into its fix. t27b 312 / 312, coverage 374 of 374, seals re-saved and verified. At this commit 25 of the 51 specs compile (user, main, compiler, prcheck, ffi lib, loss among them); SR_ALG_00 and CR_CHAT_06 need the next commit. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oRust.json | 16 +- gen/rust/tools/t27o/rust_convert.rs | 252 +++++++++++-- specs/tools/t27o/rust_convert.t27 | 526 +++++++++++++++++++++++++--- 3 files changed, 721 insertions(+), 73 deletions(-) diff --git a/.trinity/seals/t27o_T27oRust.json b/.trinity/seals/t27o_T27oRust.json index 624bb2b976..6363f53eb7 100644 --- a/.trinity/seals/t27o_T27oRust.json +++ b/.trinity/seals/t27o_T27oRust.json @@ -2,23 +2,23 @@ "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:b6fdfaa74635c400e9d3b0a10d76876e56e70fbc0405381d9315ae390fbf9a23", - "gen_hash_rust": "sha256:40389758272c8c50782690107b02984b447c0157ab88fe416181f303de198398", - "gen_hash_verilog": "sha256:771c52521c7169d81fde28329cdee917eb748a70743106ce5ad1f52621f93b8f", - "gen_hash_zig": "sha256:88df6378e78690bc0308cabfa98c0ae92b9e36fda5a1553de0a41619aa077e4b", + "gen_hash_c": "sha256:b050777eb5e6702d557eb0d0982ee348656d7439f06704b33f133827206f6d6d", + "gen_hash_rust": "sha256:73c0aa1fdb4e309ea3271902c350779bd45dcdab0df91b0f24942fdc895df672", + "gen_hash_verilog": "sha256:9b276398fcdd3b99a62485b3a067459b5cbeeb9707a94645b638b26522c96cf4", + "gen_hash_zig": "sha256:c81dabfe060b13fb6afec509a3fb1057f35163342240662793a7486e9ad8da76", "module": "T27oRust", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T07:20:21Z", + "sealed_at": "2026-10-19T09:52:40Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:f71b9d17935371c41e6fd93d53230438fd3774880a139158e47923b4ff69fc81", + "spec_hash": "sha256:ce9baea9768eb18952e33a36066fbbee71e988b378c47fcb9d60f081883471fa", "spec_path": "specs/tools/t27o/rust_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 298, - "total": 298, + "passed": 312, + "total": 312, "vacuous": 0 }, "toolchain": { diff --git a/gen/rust/tools/t27o/rust_convert.rs b/gen/rust/tools/t27o/rust_convert.rs index 66e874af30..e70863d7f8 100644 --- a/gen/rust/tools/t27o/rust_convert.rs +++ b/gen/rust/tools/t27o/rust_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 298 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 312 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -200,6 +200,10 @@ pub const K_STRFN: usize = 12; pub const K_BYTECHAR: usize = 13; +pub const K_NEEDPRE: usize = 14; + +pub const K_NOPRE: usize = 15; + pub const M_STMT: u8 = 0; pub const M_RET: u8 = 1; @@ -308,6 +312,8 @@ pub const R_SLICEGEN: usize = 38; pub const R_SHIFTCALL: usize = 39; +pub const R_NOPRELUDE: usize = 40; + pub const VU_READ: usize = 0; pub const VU_CHANGED: usize = 1; @@ -1316,6 +1322,36 @@ pub fn declared_fn(src: &[u8], st: &mut [usize], a: usize, b: usize) -> bool { return false; } +pub fn assoc_declared(src: &[u8], st: &mut [usize], ilo: usize, ihi: usize, oa: usize, ob: usize, na: usize, nb: usize) -> bool { + let mut a: usize = oa; + let mut b: usize = ob; + if word_is(src, oa, ob, b"Self") { + a = ilo; + b = ihi; + } + if (b <= a) { + return false; + } + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_FN, na, nb); + while (s != 0) { + if (impl_around(src, st, st[((s + 2)) as usize]) && same_text(src, st[(OLO) as usize], st[(OHI) as usize], a, b)) { + return true; + } + s = ix_next(src, st, s, na, nb); + } + return false; + } + let mut q: usize = skip_ws(src, 0); + while (q < src.len()) { + if ((fn_named(src, q, na, nb) && impl_around(src, st, q)) && same_text(src, st[(OLO) as usize], st[(OHI) as usize], a, b)) { + return true; + } + q = skip_ws(src, tok_end(src, q)); + } + return false; +} + pub fn item_named(src: &[u8], q: usize, a: usize, b: usize) -> bool { let e: usize = tok_end(src, q); if !(item_kw(src, q, e)) { @@ -4525,6 +4561,39 @@ pub fn path_prefix(src: &[u8], a: usize, b: usize) -> bool { return ((((word_is(src, a, b, b"crate") || word_is(src, a, b, b"self")) || word_is(src, a, b, b"super")) || word_is(src, a, b, b"std")) || word_is(src, a, b, b"core")); } +pub const SPLICE_CLASH: [&'static str; 7] = ["compiler/int_type_names", "compiler/operator_text", "tri/crypto/sha256", "tri/crypto/sha512", "tri/crypto/hex", "tri/pr/preflight", "tri/utils/template"]; + +pub fn splice_clash(src: &[u8], lo: usize, hi: usize) -> bool { + let mut k: usize = 0; + while (k < 7) { + let w: &'static str = SPLICE_CLASH[(k) as usize]; + if (((hi >= lo) && ((hi - lo) == w.len())) && word_is(src, lo, hi, w.as_bytes())) { + return true; + } + k = (k + 1); + } + return false; +} + +pub fn mod_member(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usize, ma: usize, mb: usize) -> () { + let s: usize = st[(HIT) as usize]; + if (declared_fn(src, st, ma, mb) || declared_type(src, st, ma, mb)) { + refuse(st, R_CLASH, ma, ma, mb); + return (); + } + if splice_clash(src, st[((s + 2)) as usize], st[((s + 3)) as usize]) { + refuse(st, R_SPLICE, a, a, mb); + return (); + } + st[((s + 4)) as usize] = K_MODUSED; + put_last(src, st, out, st[((s + 2)) as usize], st[((s + 3)) as usize]); + puts(st, out, b"::"); + put_src(src, st, out, ma, mb); + if at(src, st[(P) as usize], b"(") { + call_args(src, st, out); + } +} + pub fn path(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, b0: usize) -> () { let mut a: usize = a0; let mut b: usize = b0; @@ -4609,22 +4678,7 @@ pub fn path(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, b0: usize) return (); } if (named(src, st, K_MODPATH, a, b) || named(src, st, K_MODUSED, a, b)) { - let s: usize = st[(HIT) as usize]; - if (declared_fn(src, st, ma, mb) || declared_type(src, st, ma, mb)) { - refuse(st, R_CLASH, ma, ma, mb); - return (); - } - if word_is(src, st[(s) as usize], st[((s + 1)) as usize], b"int_type_names") { - refuse(st, R_SPLICE, a, a, mb); - return (); - } - st[((s + 4)) as usize] = K_MODUSED; - put_last(src, st, out, st[((s + 2)) as usize], st[((s + 3)) as usize]); - puts(st, out, b"::"); - put_src(src, st, out, ma, mb); - if at(src, st[(P) as usize], b"(") { - call_args(src, st, out); - } + mod_member(src, st, out, a, b, ma, mb); return (); } if !(is_upper(src[(a) as usize])) { @@ -4632,6 +4686,10 @@ pub fn path(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, b0: usize) return (); } if at(src, st[(P) as usize], b"(") { + if !(assoc_declared(src, st, st[(ILO) as usize], st[(IHI) as usize], a, b, ma, mb)) { + refuse(st, R_UNDECL, ma, ma, mb); + return (); + } put_owner(src, st, out, a, b); puts(st, out, b"_"); put_ident(src, st, out, ma, mb); @@ -6422,6 +6480,12 @@ pub fn pattern(src: &[u8], st: &mut [usize], out: &mut [u8], sa: usize, tmp: usi refuse(st, R_DATA, a, a, mb); return (); } + if (named(src, st, K_MODPATH, a, b) || named(src, st, K_MODUSED, a, b)) { + put_scrut(src, st, out, sa, tmp, wrapit); + puts(st, out, b" == "); + mod_member(src, st, out, a, b, ma, mb); + return (); + } put_scrut(src, st, out, sa, tmp, wrapit); puts(st, out, b" == "); put_owner(src, st, out, a, b); @@ -6491,6 +6555,51 @@ pub fn arm_expr(src: &[u8], st: &mut [usize], out: &mut [u8], mode: u8) -> () { } } +pub fn payload_binder(src: &[u8], st: &mut [usize], aa: usize, ab: usize, arrow: usize, ae: usize) -> usize { + let lp: usize = skip_ws(src, ab); + let x: usize = skip_ws(src, (lp + 1)); + let xe: usize = tok_end(src, x); + let rp: usize = skip_ws(src, xe); + if ((((x >= src.len()) || !(is_alpha(src[(x) as usize]))) || !(tok_is(src, rp, b")"))) || (skip_ws(src, (rp + 1)) != arrow)) { + refuse(st, R_PATTERN, aa, aa, ab); + return src.len(); + } + if word_is(src, x, xe, b"_") { + if ((word_is(src, aa, ab, b"Some") || word_is(src, aa, ab, b"Ok")) || word_is(src, aa, ab, b"Err")) { + return src.len(); + } + refuse(st, R_PATTERN, aa, aa, ab); + return src.len(); + } + if word_is(src, aa, ab, b"Err") { + if mentions(src, arrow, ae, x, xe) { + refuse(st, R_PATTERN, x, x, xe); + } + return src.len(); + } + if (word_is(src, aa, ab, b"Some") || word_is(src, aa, ab, b"Ok")) { + return x; + } + refuse(st, R_PATTERN, aa, aa, ab); + return src.len(); +} + +pub fn bind_type(src: &[u8], st: &mut [usize], out: &mut [u8], sa: usize) -> () { + if ((sa >= src.len()) || !(is_alpha(src[(sa) as usize]))) { + return (); + } + let d: usize = decl_type(src, st[(FLO) as usize], sa, sa, tok_end(src, sa)); + if (d >= src.len()) { + return (); + } + let de: usize = tok_end(src, d); + let lt: usize = skip_ws(src, de); + if ((word_is(src, d, de, b"Option") || word_is(src, d, de, b"Result")) && tok_is(src, lt, b"<")) { + puts(st, out, b" : "); + put_elem_ty(src, st, out, skip_ws(src, (lt + 1))); + } +} + pub fn match_stmt(src: &[u8], st: &mut [usize], out: &mut [u8], mode: u8) -> () { let mpos: usize = st[(P) as usize]; step(src, st); @@ -6534,6 +6643,13 @@ pub fn match_stmt(src: &[u8], st: &mut [usize], out: &mut [u8], mode: u8) -> () let single: bool = (skip_ws(src, ab) == arrow); let wild: bool = (single && word_is(src, aa, ab, b"_")); let bind: bool = ((((((single && last) && !(wild)) && (src[(aa) as usize] >= B_LOWER_A)) && (src[(aa) as usize] <= B_LOWER_Z)) && !(word_is(src, aa, ab, b"true"))) && !(word_is(src, aa, ab, b"false"))); + let mut pb: usize = src.len(); + if ((((last && !(first_arm)) && !(single)) && is_alpha(src[(aa) as usize])) && tok_is(src, skip_ws(src, ab), b"(")) { + pb = payload_binder(src, st, aa, ab, arrow, ae); + if bad(st[(ERR) as usize]) { + return (); + } + } if ((wild || bind) || (last && !(first_arm))) { if first_arm { puts(st, out, b"if (true) "); @@ -6560,6 +6676,17 @@ pub fn match_stmt(src: &[u8], st: &mut [usize], out: &mut [u8], mode: u8) -> () } puts(st, out, b"{"); st[(IND) as usize] = (st[(IND) as usize] + 1); + if (pb < src.len()) { + newline(st, out); + puts(st, out, b"const "); + put_ident(src, st, out, pb, tok_end(src, pb)); + if ((tmp == 0) && (skip_ws(src, tok_end(src, sa)) == mb)) { + bind_type(src, st, out, sa); + } + puts(st, out, b" = "); + put_scrut(src, st, out, sa, tmp, wrapit); + puts(st, out, b".?;"); + } if bind { if ((((tmp != 0) || !(is_alpha(src[(sa) as usize]))) || (skip_ws(src, tok_end(src, sa)) != mb)) || !(same_text(src, aa, ab, sa, tok_end(src, sa)))) { newline(st, out); @@ -6824,6 +6951,12 @@ pub fn macro_stmt(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usi take(src, st, b";"); return (); } + if (st[(INTEST) as usize] != 1) { + need_prelude(src, st, a, b); + if bad(st[(ERR) as usize]) { + return (); + } + } puts(st, out, b"assert("); if plain { expr(src, st, out, false); @@ -7037,10 +7170,14 @@ pub fn put_reason(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { if (c == R_SHIFTCALL) { puts(st, out, b"a compound shift of a place that calls something"); } else { - if (c == R_SPLICE) { - puts(st, out, b"a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records)"); + if (c == R_NOPRELUDE) { + puts(st, out, b"an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one)"); } else { - puts(st, out, b"syntax"); + if (c == R_SPLICE) { + puts(st, out, b"a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records)"); + } else { + puts(st, out, b"syntax"); + } } } } @@ -10615,6 +10752,49 @@ pub fn reused(src: &[u8], st: &mut [usize], na: usize, nb: usize) -> bool { return false; } +pub fn qualified_fn(src: &[u8], q: usize) -> usize { + let mut p: usize = q; + while (p < src.len()) { + let e: usize = tok_end(src, p); + if word_is(src, p, e, b"fn") { + return skip_ws(src, e); + } + if !(((((word_is(src, p, e, b"unsafe") || word_is(src, p, e, b"extern")) || word_is(src, p, e, b"async")) || word_is(src, p, e, b"const")) || (src[(p) as usize] == B_QUOTE))) { + return src.len(); + } + p = skip_ws(src, e); + } + return src.len(); +} + +pub fn foreign_block(src: &[u8], q: usize) -> usize { + if !(tok_is(src, q, b"extern")) { + return src.len(); + } + let mut p: usize = skip_ws(src, tok_end(src, q)); + if ((p < src.len()) && (src[(p) as usize] == B_QUOTE)) { + p = skip_ws(src, tok_end(src, p)); + } + if tok_is(src, p, b"{") { + return p; + } + return src.len(); +} + +pub fn record_foreign(src: &[u8], st: &mut [usize], lo: usize, hi: usize) -> () { + let mut q: usize = lo; + while (q < hi) { + let e: usize = tok_end(src, q); + if word_is(src, q, e, b"fn") { + let n: usize = skip_ws(src, e); + if ((n < src.len()) && is_alpha(src[(n) as usize])) { + add_name(src, st, K_FN, n, tok_end(src, n), 0, 0); + } + } + q = skip_ws(src, e); + } +} + pub fn item(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { st[(ERR) as usize] = 0; let start: usize = st[(P) as usize]; @@ -10744,8 +10924,22 @@ pub fn item(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { if tok_is(src, n, b"mut") { n = skip_ws(src, tok_end(src, n)); } - if ((n < src.len()) && is_alpha(src[(n) as usize])) { - add_name(src, st, K_ITEM, n, tok_end(src, n), 0, 0); + let qf: usize = qualified_fn(src, a); + let fb: usize = foreign_block(src, a); + if (qf < src.len()) { + if (st[(ILO) as usize] != st[(IHI) as usize]) { + add_name(src, st, K_MEMBER, qf, tok_end(src, qf), st[(ILO) as usize], st[(IHI) as usize]); + } else { + add_name(src, st, K_FN, qf, tok_end(src, qf), 0, 0); + } + } else { + if (fb < src.len()) { + record_foreign(src, st, fb, block_end(src, fb)); + } else { + if ((n < src.len()) && is_alpha(src[(n) as usize])) { + add_name(src, st, K_ITEM, n, tok_end(src, n), 0, 0); + } + } } skip_refused(src, st, out, start, a, b, R_ITEM); } @@ -10907,6 +11101,17 @@ pub fn one_pass(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize], whole } } +pub fn need_prelude(src: &[u8], st: &mut [usize], a: usize, b: usize) -> () { + if (src.len() == 0) { + return (); + } + if named(src, st, K_NOPRE, 0, 1) { + refuse(st, R_NOPRELUDE, a, a, b); + return (); + } + add_name(src, st, K_NEEDPRE, 0, 1, 0, 0); +} + pub fn passes(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize], whole: bool) -> usize { clear(st); prescan(src, st); @@ -10915,6 +11120,9 @@ pub fn passes(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize], whole: while (grew && (rounds < 16)) { let before: usize = st[(NNAMES) as usize]; one_pass(src, name, out, st, whole); + if ((((src.len() > 0) && (st[(NTEST) as usize] == 0)) && named(src, st, K_NEEDPRE, 0, 1)) && !(named(src, st, K_NOPRE, 0, 1))) { + add_name(src, st, K_NOPRE, 0, 1, 0, 0); + } grew = (st[(NNAMES) as usize] != before); rounds = (rounds + 1); } diff --git a/specs/tools/t27o/rust_convert.t27 b/specs/tools/t27o/rust_convert.t27 index 5154097296..506c6f49f6 100644 --- a/specs/tools/t27o/rust_convert.t27 +++ b/specs/tools/t27o/rust_convert.t27 @@ -257,6 +257,12 @@ pub const K_STRLOC : usize = 11; pub const K_STRFN : usize = 12; /// The byte binding of a `for c in s.chars()` loop of an out-buffer fn. pub const K_BYTECHAR : usize = 13; +/// Flags the passes keep in the table (the name is src[0 .. 1)): K_NEEDPRE once some fn wrote code that Zig compiles only +/// when the spec has a test block (an assert, a comparison of two texts or byte slices: t27c writes the std import +/// and the assert helper with the first test block and no sooner), K_NOPRE once a pass found no test: the +/// next pass refuses that code. +pub const K_NEEDPRE : usize = 14; +pub const K_NOPRE : usize = 15; /// What the tail expression of a block becomes: a statement, a `return`, or an assignment to the /// target src[TLO .. THI]. @@ -332,6 +338,9 @@ pub const R_SLICEGEN : usize = 38; /// A compound shift (`a >>= n`, written `a = a >> n` as t27 has no `>>=`) of a place that calls something: the /// place would be called twice. pub const R_SHIFTCALL : usize = 39; +/// An assert in a fn, or a comparison of two texts, in a spec with no test block: the Zig t27c writes has no std +/// import and no assert helper then (`use of undeclared identifier 'std'`, '__t27_assert_fail'). +pub const R_NOPRELUDE : usize = 40; /// How a fn uses a vec! local: VU_READ only reads it, VU_CHANGED changes it, VU_MOVED hands it on. pub const VU_READ : usize = 0; pub const VU_CHANGED : usize = 1; @@ -1181,6 +1190,35 @@ pub fn declared_fn(src: []const u8, st: []usize, a: usize, b: usize) -> bool { return false; } +/// Does an impl of the type src[oa .. ob] declare `fn ` (src[na .. nb])? `Self` is the impl's type +/// src[ilo .. ihi]. The impl of a `fn` token is the nearest `impl` before it (impl_around), so a free fn after +/// an impl of the type counts as its member: this only refuses a call nothing answers, `Type::default()` of a +/// `#[derive(Default)]` for one, whose `Type_default` no one declares (#10905). It leaves the impl type of the +/// state (OLO .. OHI) where impl_around put it. +pub fn assoc_declared(src: []const u8, st: []usize, ilo: usize, ihi: usize, oa: usize, ob: usize, na: usize, nb: usize) -> bool { + var a : usize = oa; + var b : usize = ob; + if (word_is(src, oa, ob, "Self")) { + a = ilo; + b = ihi; + } + if (b <= a) { return false; } + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_FN, na, nb); + while (s != 0) { + if (impl_around(src, st, st[s + 2]) and same_text(src, st[OLO], st[OHI], a, b)) { return true; } + s = ix_next(src, st, s, na, nb); + } + return false; + } + var q : usize = skip_ws(src, 0); + while (q < src.len) { + if (fn_named(src, q, na, nb) and impl_around(src, st, q) and same_text(src, st[OLO], st[OHI], a, b)) { return true; } + q = skip_ws(src, tok_end(src, q)); + } + return false; +} + /// Is the keyword token at src[q] one that declares the item src[a .. b] (the name right after it)? pub fn item_named(src: []const u8, q: usize, a: usize, b: usize) -> bool { const e : usize = tok_end(src, q); @@ -3992,6 +4030,47 @@ pub fn path_prefix(src: []const u8, a: usize, b: usize) -> bool { return word_is(src, a, b, "crate") or word_is(src, a, b, "self") or word_is(src, a, b, "super") or word_is(src, a, b, "std") or word_is(src, a, b, "core"); } +/// The t27 modules (their path under specs/) a converted spec cannot call into: t27c splices a used module flat +/// and does not splice that module's own `use`, so int_type_names and operator_text leave the `type_text::` of +/// their bodies unresolved (the gap specs/compiler/int_type_names.t27 records, #10843); and a used module that +/// declares a `same` beside type_text's makes `same` ambiguous, so neither resolves (#10556: sha256, sha512, +/// hex, pr/preflight, utils/template -- main.rs and compiler.rs of this repository hit both). +pub const SPLICE_CLASH : [7]str = ["compiler/int_type_names", "compiler/operator_text", "tri/crypto/sha256", "tri/crypto/sha512", "tri/crypto/hex", "tri/pr/preflight", "tri/utils/template"]; + +/// Is src[lo .. hi) the path of a module in SPLICE_CLASH? +pub fn splice_clash(src: []const u8, lo: usize, hi: usize) -> bool { + var k : usize = 0; + while (k < 7) { + const w : str = SPLICE_CLASH[k]; + if (hi >= lo and hi - lo == w.len and word_is(src, lo, hi, w)) { return true; } + k = k + 1; + } + return false; +} + +/// `x::m` where x is a #[path] gen module of the file (HIT is its row, left by `named`): `::m`, the module +/// `use`d, then the call's arguments when a `(` follows. A name this file declares too would shadow m (t27c's use +/// splices flat, see #7176). int_type_names' own t27 says `type_text::`, which t27c cannot resolve when it splices the +/// module into this file (the gap specs/compiler/int_type_names.t27 records): a call that kept this fn would emit a +/// spec whose test-report is BLOCKED, so the call refuses instead. The module is read by its #[path], not by the +/// alias it was mounted under (#10843). +pub fn mod_member(src: []const u8, st: []usize, out: []u8, a: usize, b: usize, ma: usize, mb: usize) -> void { + const s : usize = st[HIT]; + if (declared_fn(src, st, ma, mb) or declared_type(src, st, ma, mb)) { + refuse(st, R_CLASH, ma, ma, mb); + return; + } + if (splice_clash(src, st[s + 2], st[s + 3])) { + refuse(st, R_SPLICE, a, a, mb); + return; + } + st[s + 4] = K_MODUSED; + put_last(src, st, out, st[s + 2], st[s + 3]); + puts(st, out, "::"); + put_src(src, st, out, ma, mb); + if (at(src, st[P], "(")) { call_args(src, st, out); } +} + /// A path that starts with the segment src[a0 .. b0]; st[P] is at `::`. pub fn path(src: []const u8, st: []usize, out: []u8, a0: usize, b0: usize) -> void { var a : usize = a0; @@ -4069,23 +4148,7 @@ pub fn path(src: []const u8, st: []usize, out: []u8, a0: usize, b0: usize) -> vo // a #[path] gen module: `x::f` is `::f`, the module `use`d; a name this file declares too // would shadow it (t27c's use splices flat, see #7176) if (named(src, st, K_MODPATH, a, b) or named(src, st, K_MODUSED, a, b)) { - const s : usize = st[HIT]; - if (declared_fn(src, st, ma, mb) or declared_type(src, st, ma, mb)) { - refuse(st, R_CLASH, ma, ma, mb); - return; - } - // int_type_names' own t27 says `type_text::`, which t27c cannot resolve when it splices the - // module into this file (the gap specs/compiler/int_type_names.t27 records): a call that kept - // this fn would emit a spec whose test-report is BLOCKED, so the call refuses instead - if (word_is(src, st[s], st[s + 1], "int_type_names")) { - refuse(st, R_SPLICE, a, a, mb); - return; - } - st[s + 4] = K_MODUSED; - put_last(src, st, out, st[s + 2], st[s + 3]); - puts(st, out, "::"); - put_src(src, st, out, ma, mb); - if (at(src, st[P], "(")) { call_args(src, st, out); } + mod_member(src, st, out, a, b, ma, mb); return; } // a lower-case first segment is a module: its items live in another file @@ -4094,6 +4157,11 @@ pub fn path(src: []const u8, st: []usize, out: []u8, a0: usize, b0: usize) -> vo return; } if (at(src, st[P], "(")) { + // `Type::f(..)` is `Type_f(..)`: the impl must declare f, or the output names what no one writes + if (!assoc_declared(src, st, st[ILO], st[IHI], a, b, ma, mb)) { + refuse(st, R_UNDECL, ma, ma, mb); + return; + } put_owner(src, st, out, a, b); puts(st, out, "_"); put_ident(src, st, out, ma, mb); @@ -5703,6 +5771,13 @@ pub fn pattern(src: []const u8, st: []usize, out: []u8, sa: usize, tmp: usize, w refuse(st, R_DATA, a, a, mb); return; } + // `t27a::ASM_NO_MNEMONIC =>`: a const of a #[path] gen module reads `t27a::ASM_NO_MNEMONIC`, as in an expression + if (named(src, st, K_MODPATH, a, b) or named(src, st, K_MODUSED, a, b)) { + put_scrut(src, st, out, sa, tmp, wrapit); + puts(st, out, " == "); + mod_member(src, st, out, a, b, ma, mb); + return; + } put_scrut(src, st, out, sa, tmp, wrapit); puts(st, out, " == "); put_owner(src, st, out, a, b); @@ -5755,6 +5830,47 @@ pub fn arm_expr(src: []const u8, st: []usize, out: []u8, mode: u8) -> void { } } +/// The binder `x` of the last arm `Some(x)` / `Ok(x)` that starts at src[aa] (its first token src[aa .. ab), the +/// arm's `=>` at src[arrow], the arm ending at src[ae]): its position, which the else-arm binds to the +/// scrutinee's value. src.len when the arm binds nothing -- `Some(_)`, `Err(_)`, an `Err(e)` the arm never +/// reads. Any other payload pattern is refused here as a match pattern. +pub fn payload_binder(src: []const u8, st: []usize, aa: usize, ab: usize, arrow: usize, ae: usize) -> usize { + const lp : usize = skip_ws(src, ab); + const x : usize = skip_ws(src, lp + 1); + const xe : usize = tok_end(src, x); + const rp : usize = skip_ws(src, xe); + if (x >= src.len or !is_alpha(src[x]) or !tok_is(src, rp, ")") or skip_ws(src, rp + 1) != arrow) { + refuse(st, R_PATTERN, aa, aa, ab); + return src.len; + } + if (word_is(src, x, xe, "_")) { + if (word_is(src, aa, ab, "Some") or word_is(src, aa, ab, "Ok") or word_is(src, aa, ab, "Err")) { return src.len; } + refuse(st, R_PATTERN, aa, aa, ab); + return src.len; + } + if (word_is(src, aa, ab, "Err")) { + if (mentions(src, arrow, ae, x, xe)) { refuse(st, R_PATTERN, x, x, xe); } + return src.len; + } + if (word_is(src, aa, ab, "Some") or word_is(src, aa, ab, "Ok")) { return x; } + refuse(st, R_PATTERN, aa, aa, ab); + return src.len; +} + +/// ` : T` for the binder of `Some(x)` / `Ok(x)` of a match on the name src[sa ..) declared `Option` / `Result`: t27c +/// writes a bare `==` for two texts whose types it cannot see, and Zig rejects it on slices (#10905). +pub fn bind_type(src: []const u8, st: []usize, out: []u8, sa: usize) -> void { + if (sa >= src.len or !is_alpha(src[sa])) { return; } + const d : usize = decl_type(src, st[FLO], sa, sa, tok_end(src, sa)); + if (d >= src.len) { return; } + const de : usize = tok_end(src, d); + const lt : usize = skip_ws(src, de); + if ((word_is(src, d, de, "Option") or word_is(src, d, de, "Result")) and tok_is(src, lt, "<")) { + puts(st, out, " : "); + put_elem_ty(src, st, out, skip_ws(src, lt + 1)); + } +} + /// `match` as an if-chain; Rust checked the match is exhaustive, so its last arm is the `else`. pub fn match_stmt(src: []const u8, st: []usize, out: []u8, mode: u8) -> void { const mpos : usize = st[P]; @@ -5797,6 +5913,15 @@ pub fn match_stmt(src: []const u8, st: []usize, out: []u8, mode: u8) -> void { const single : bool = skip_ws(src, ab) == arrow; const wild : bool = single and word_is(src, aa, ab, "_"); const bind : bool = single and last and !wild and src[aa] >= B_LOWER_A and src[aa] <= B_LOWER_Z and !word_is(src, aa, ab, "true") and !word_is(src, aa, ab, "false"); + // the last arm is the else, so a payload pattern binds nothing there by itself: `Some(x)` / `Ok(x)` is the + // scrutinee's value (`const x = e.?;` below), `Some(_)` / `Err(_)` and an `Err(e)` the arm never reads + // need no binding, any other pattern with a payload is refused (#10905: `Some(want) => want == ..` wrote + // `want` and bound nothing) + var pb : usize = src.len; + if (last and !first_arm and !single and is_alpha(src[aa]) and tok_is(src, skip_ws(src, ab), "(")) { + pb = payload_binder(src, st, aa, ab, arrow, ae); + if (bad(st[ERR])) { return; } + } if (wild or bind or (last and !first_arm)) { if (first_arm) { puts(st, out, "if (true) "); } else { puts(st, out, " else "); } st[P] = arrow; @@ -5813,6 +5938,15 @@ pub fn match_stmt(src: []const u8, st: []usize, out: []u8, mode: u8) -> void { } puts(st, out, "{"); st[IND] = st[IND] + 1; + if (pb < src.len) { + newline(st, out); + puts(st, out, "const "); + put_ident(src, st, out, pb, tok_end(src, pb)); + if (tmp == 0 and skip_ws(src, tok_end(src, sa)) == mb) { bind_type(src, st, out, sa); } + puts(st, out, " = "); + put_scrut(src, st, out, sa, tmp, wrapit); + puts(st, out, ".?;"); + } if (bind) { // the binder may BE the scrutinee's own name -- a catch-all `c => ..` of a match on `c` // -- and `const c = c;` then shadows the local or for-capture holding it, which t27c @@ -6055,6 +6189,10 @@ pub fn macro_stmt(src: []const u8, st: []usize, out: []u8, a: usize, b: usize) - take(src, st, ";"); return; } + if (st[INTEST] != 1) { + need_prelude(src, st, a, b); + if (bad(st[ERR])) { return; } + } puts(st, out, "assert("); if (plain) { expr(src, st, out, false); @@ -6208,8 +6346,10 @@ pub fn put_reason(src: []const u8, st: []usize, out: []u8) -> void { puts(st, out, "slice contains on"); } else if (c == R_SHIFTCALL) { puts(st, out, "a compound shift of a place that calls something"); + } else if (c == R_NOPRELUDE) { + puts(st, out, "an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one)"); } else if (c == R_SPLICE) { - puts(st, out, "a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records)"); + puts(st, out, "a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records)"); } else { puts(st, out, "syntax"); } @@ -9106,6 +9246,42 @@ pub fn reused(src: []const u8, st: []usize, na: usize, nb: usize) -> bool { return false; } +/// The name of the fn that a qualified header begins at src[q] -- `unsafe fn f`, `extern "C" fn f`, `async fn f`, +/// `unsafe extern "C" fn f` -- src.len when the tokens up to the first `fn` are not such qualifiers. +pub fn qualified_fn(src: []const u8, q: usize) -> usize { + var p : usize = q; + while (p < src.len) { + const e : usize = tok_end(src, p); + if (word_is(src, p, e, "fn")) { return skip_ws(src, e); } + if (!(word_is(src, p, e, "unsafe") or word_is(src, p, e, "extern") or word_is(src, p, e, "async") or word_is(src, p, e, "const") or src[p] == B_QUOTE)) { return src.len; } + p = skip_ws(src, e); + } + return src.len; +} + +/// The `{` of an `extern "C" { .. }` block that begins at src[q], src.len when it is not one. +pub fn foreign_block(src: []const u8, q: usize) -> usize { + if (!tok_is(src, q, "extern")) { return src.len; } + var p : usize = skip_ws(src, tok_end(src, q)); + if (p < src.len and src[p] == B_QUOTE) { p = skip_ws(src, tok_end(src, p)); } + if (tok_is(src, p, "{")) { return p; } + return src.len; +} + +/// Each `fn ` in the foreign block src[lo .. hi) as a refused fn of the file: the block is not +/// converted, and its fns are what the file's other items call. +pub fn record_foreign(src: []const u8, st: []usize, lo: usize, hi: usize) -> void { + var q : usize = lo; + while (q < hi) { + const e : usize = tok_end(src, q); + if (word_is(src, q, e, "fn")) { + const n : usize = skip_ws(src, e); + if (n < src.len and is_alpha(src[n])) { add_name(src, st, K_FN, n, tok_end(src, n), 0, 0); } + } + q = skip_ws(src, e); + } +} + pub fn item(src: []const u8, st: []usize, out: []u8) -> void { st[ERR] = 0; const start : usize = st[P]; @@ -9218,7 +9394,16 @@ pub fn item(src: []const u8, st: []usize, out: []u8) -> void { // trait, type, union, static mut, macro_rules ...: its name is refused with it var n : usize = skip_ws(src, b); if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } - if (n < src.len and is_alpha(src[n])) { add_name(src, st, K_ITEM, n, tok_end(src, n), 0, 0); } + const qf : usize = qualified_fn(src, a); + const fb : usize = foreign_block(src, a); + if (qf < src.len) { + // `unsafe fn f`, `extern "C" fn f`: f is refused, not an item named `fn` + if (st[ILO] != st[IHI]) { add_name(src, st, K_MEMBER, qf, tok_end(src, qf), st[ILO], st[IHI]); } else { add_name(src, st, K_FN, qf, tok_end(src, qf), 0, 0); } + } else if (fb < src.len) { + record_foreign(src, st, fb, block_end(src, fb)); + } else if (n < src.len and is_alpha(src[n])) { + add_name(src, st, K_ITEM, n, tok_end(src, n), 0, 0); + } skip_refused(src, st, out, start, a, b, R_ITEM); } } @@ -9367,6 +9552,17 @@ pub fn one_pass(src: []const u8, name: []const u8, out: []u8, st: []usize, whole if (whole) { put_report(st, out); } } +/// Note that the code being written needs the Zig prelude (std, the assert helper), which t27c writes only for a spec with a +/// test block; once a pass has found no test (passes), this refuses it instead. +pub fn need_prelude(src: []const u8, st: []usize, a: usize, b: usize) -> void { + if (src.len == 0) { return; } + if (named(src, st, K_NOPRE, 0, 1)) { + refuse(st, R_NOPRELUDE, a, a, b); + return; + } + add_name(src, st, K_NEEDPRE, 0, 1, 0, 0); +} + /// Passes until no new name is refused: an item that uses a refused one is refused in the next pass, /// in whatever order the file declares them. The table only grows, so this ends. pub fn passes(src: []const u8, name: []const u8, out: []u8, st: []usize, whole: bool) -> usize { @@ -9377,6 +9573,8 @@ pub fn passes(src: []const u8, name: []const u8, out: []u8, st: []usize, whole: while (grew and rounds < 16) { const before : usize = st[NNAMES]; one_pass(src, name, out, st, whole); + // code that needs the prelude and no test in the pass: the next pass refuses it + if (src.len > 0 and st[NTEST] == 0 and named(src, st, K_NEEDPRE, 0, 1) and !named(src, st, K_NOPRE, 0, 1)) { add_name(src, st, K_NOPRE, 0, 1, 0, 0); } grew = st[NNAMES] != before; rounds = rounds + 1; } @@ -9711,8 +9909,8 @@ test a_string_local_of_one_fn_does_not_poison_a_later_fns_name { test a_gen_module_scrutinee_types_the_if_let_binding_as_text { var out : [2048]u8 = undefined; var st : [1024]usize = [0] ** 1024; - const n : usize = body("#[path = \"../gen/rust/compiler/operator_text.rs\"]\nmod operator_text;\n\nfn assign_op_text(extra_op: &str) -> String {\n if let Some(op) = operator_text::compound_binop(extra_op.as_bytes()) {\n return format!(\" {}= \", op);\n }\n let t = extra_op.trim();\n if t.len() >= 2 && t.ends_with('=') && !matches!(t, \"==\" | \"!=\" | \"<=\" | \">=\") {\n return format!(\" {} \", t);\n }\n \" = \".to_string()\n}", &out, &st); - assert(out_is(n, &out, 0, "use tools::t27o::rust_std;\nuse hosting::toolchain;\nuse tri::t27b::type_text;\nuse compiler::operator_text;\n\nfn assign_op_text(extra_op: []const u8, out: []u8) -> usize {\n var t27o_w : usize = 0;\n const op_t27o = operator_text::compound_binop(extra_op);\n if (op_t27o != null) {\n const op = op_t27o.?;\n t27o_w = rust_std::append_text(out, t27o_w, \" \");\n t27o_w = rust_std::append_text(out, t27o_w, op);\n t27o_w = rust_std::append_text(out, t27o_w, \"= \");\n return t27o_w;\n }\n const t = extra_op[type_text::trim_lo(extra_op, 0, extra_op.len) .. type_text::trim_hi(extra_op, type_text::trim_lo(extra_op, 0, extra_op.len), extra_op.len)];\n if (t.len >= 2 and toolchain::ends_with(t, \"=\") and !(type_text::same(t, \"==\") or type_text::same(t, \"!=\") or type_text::same(t, \"<=\") or type_text::same(t, \">=\"))) {\n t27o_w = rust_std::append_text(out, t27o_w, \" \");\n t27o_w = rust_std::append_text(out, t27o_w, t);\n t27o_w = rust_std::append_text(out, t27o_w, \" \");\n return t27o_w;\n }\n t27o_w = rust_std::append_text(out, t27o_w, \" = \");\n return t27o_w;\n}\n\n")); + const n : usize = body("#[path = \"../gen/rust/compiler/op_words.rs\"]\nmod op_words;\n\nfn assign_op_text(extra_op: &str) -> String {\n if let Some(op) = op_words::compound_binop(extra_op.as_bytes()) {\n return format!(\" {}= \", op);\n }\n let t = extra_op.trim();\n if t.len() >= 2 && t.ends_with('=') && !matches!(t, \"==\" | \"!=\" | \"<=\" | \">=\") {\n return format!(\" {} \", t);\n }\n \" = \".to_string()\n}", &out, &st); + assert(out_is(n, &out, 0, "use tools::t27o::rust_std;\nuse hosting::toolchain;\nuse tri::t27b::type_text;\nuse compiler::op_words;\n\nfn assign_op_text(extra_op: []const u8, out: []u8) -> usize {\n var t27o_w : usize = 0;\n const op_t27o = op_words::compound_binop(extra_op);\n if (op_t27o != null) {\n const op = op_t27o.?;\n t27o_w = rust_std::append_text(out, t27o_w, \" \");\n t27o_w = rust_std::append_text(out, t27o_w, op);\n t27o_w = rust_std::append_text(out, t27o_w, \"= \");\n return t27o_w;\n }\n const t = extra_op[type_text::trim_lo(extra_op, 0, extra_op.len) .. type_text::trim_hi(extra_op, type_text::trim_lo(extra_op, 0, extra_op.len), extra_op.len)];\n if (t.len >= 2 and toolchain::ends_with(t, \"=\") and !(type_text::same(t, \"==\") or type_text::same(t, \"!=\") or type_text::same(t, \"<=\") or type_text::same(t, \">=\"))) {\n t27o_w = rust_std::append_text(out, t27o_w, \" \");\n t27o_w = rust_std::append_text(out, t27o_w, t);\n t27o_w = rust_std::append_text(out, t27o_w, \" \");\n return t27o_w;\n }\n t27o_w = rust_std::append_text(out, t27o_w, \" = \");\n return t27o_w;\n}\n\n")); } test decl_type_stops_at_the_fns_end_so_a_later_same_name_param_is_not_its_type { @@ -9723,7 +9921,7 @@ test decl_type_stops_at_the_fns_end_so_a_later_same_name_param_is_not_its_type { // #9098 file typed this fn's `t` as `&TypeInfo`, a class out_format refuses, and the whole-file // run blamed an owned String that was never there. The scan now stops at fn_end, the boundary // the K_STRLOC windows already gave the name table (the test above theirs). - const n : usize = body("#[path = \"../gen/rust/compiler/operator_text.rs\"]\nmod operator_text;\n\nfn assign_op_text(extra_op: &str) -> String {\n if let Some(op) = operator_text::compound_binop(extra_op.as_bytes()) {\n return format!(\" {}= \", op);\n }\n let t = extra_op.trim();\n if t.len() >= 2 && t.ends_with('=') && !matches!(t, \"==\" | \"!=\" | \"<=\" | \">=\") {\n return format!(\" {} \", t);\n }\n \" = \".to_string()\n}\n\nfn type_rank(t: &TypeInfo) -> u8 { 0 }", &out, &st); + const n : usize = body("#[path = \"../gen/rust/compiler/op_words.rs\"]\nmod op_words;\n\nfn assign_op_text(extra_op: &str) -> String {\n if let Some(op) = op_words::compound_binop(extra_op.as_bytes()) {\n return format!(\" {}= \", op);\n }\n let t = extra_op.trim();\n if t.len() >= 2 && t.ends_with('=') && !matches!(t, \"==\" | \"!=\" | \"<=\" | \">=\") {\n return format!(\" {} \", t);\n }\n \" = \".to_string()\n}\n\nfn type_rank(t: &TypeInfo) -> u8 { 0 }", &out, &st); assert(out_text_has(&out, n, "fn assign_op_text(extra_op: []const u8, out: []u8) -> usize {")); assert(out_text_has(&out, n, "t27o_w = rust_std::append_text(out, t27o_w, t);")); assert(!out_text_has(&out, n, "refused fn assign_op_text")); @@ -9737,8 +9935,8 @@ test a_gen_path_attribute_tolerates_other_attributes_before_the_mod { // #[path] attribute's end as the mod's position and the #[allow] between the two broke it, so // gen_mod_name denied the very module that path names. The mod now matches the END of an // unbroken attribute run that carries a gen path, whichever attribute ends it. - const n : usize = body("#[path = \"../gen/rust/compiler/operator_text.rs\"]\n#[allow(dead_code, unused_parens)]\nmod operator_text;\n\nfn assign_op_text(extra_op: &str) -> String {\n if let Some(op) = operator_text::compound_binop(extra_op.as_bytes()) {\n return format!(\" {}= \", op);\n }\n \" = \".to_string()\n}", &out, &st); - assert(out_text_has(&out, n, "use compiler::operator_text;")); + const n : usize = body("#[path = \"../gen/rust/compiler/op_words.rs\"]\n#[allow(dead_code, unused_parens)]\nmod op_words;\n\nfn assign_op_text(extra_op: &str) -> String {\n if let Some(op) = op_words::compound_binop(extra_op.as_bytes()) {\n return format!(\" {}= \", op);\n }\n \" = \".to_string()\n}", &out, &st); + assert(out_text_has(&out, n, "use compiler::op_words;")); assert(out_text_has(&out, n, "const op = op_t27o.?;")); assert(out_text_has(&out, n, "t27o_w = rust_std::append_text(out, t27o_w, op);")); assert(!out_text_has(&out, n, "refused fn assign_op_text")); @@ -9855,7 +10053,7 @@ test a_module_whose_own_use_t27c_cannot_splice_refuses { var out : [1024]u8 = undefined; var st : [1024]usize = [0] ** 1024; const n : usize = body("#[path = \"../../../gen/rust/compiler/int_type_names.rs\"]\nmod int_type_names;\nfn suffix(t: &str) -> bool { int_type_names::is_integer_type_suffix(t.as_bytes()) }", &out, &st); - assert(out_is(n, &out, 0, "// t27o: refused fn suffix (line 3): a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records) `int_type_names::is_integer_type_suffix` at line 3\n\nfn suffix(t: []const u8) -> bool {\n}\n\n")); + assert(out_is(n, &out, 0, "// t27o: refused fn suffix (line 3): a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records) `int_type_names::is_integer_type_suffix` at line 3\n\nfn suffix(t: []const u8) -> bool {\n}\n\n")); assert(st[NOK] == 0); } @@ -10119,11 +10317,12 @@ test a_for_over_a_borrowed_array_literal_is_a_for_over_the_literal { var out2 : [1024]u8 = undefined; var st : [1024]usize = [0] ** 1024; var st2 : [1024]usize = [0] ** 1024; - const n : usize = body("fn t() {\n for bad in &[\"9bad\", \"has space\", \"\"] {\n assert!(bad.len() < 20);\n }\n for &x in &[1u8, 2] { assert!(x > 0); }\n}", &out, &st); - assert(out_is(n, &out, 0, "fn t() -> void {\n const t27o_a2 : [3][]const u8 = [\"9bad\", \"has space\", \"\"];\n for bad in t27o_a2 {\n assert(bad.len < 20);\n }\n const t27o_a5 : [2]u8 = [@as(u8, 1), 2];\n for x in t27o_a5 {\n assert(x > 0);\n }\n}\n\n")); + const n : usize = body("#[test]\nfn t() {\n for bad in &[\"9bad\", \"has space\", \"\"] {\n assert!(bad.len() < 20);\n }\n for &x in &[1u8, 2] { assert!(x > 0); }\n}", &out, &st); + assert(out_is(n, &out, 0, "test t {\n const t27o_a3 : [3][]const u8 = [\"9bad\", \"has space\", \"\"];\n for bad in t27o_a3 {\n assert(bad.len < 20);\n }\n const t27o_a6 : [2]u8 = [@as(u8, 1), 2];\n for x in t27o_a6 {\n assert(x > 0);\n }\n}\n\n")); assert(st[NOK] == 1); + assert(st[NTEST] == 1); // the very text of the loops over the literals themselves, borrowed or not - const n2 : usize = body("fn t() {\n for bad in [\"9bad\", \"has space\", \"\"] {\n assert!(bad.len() < 20);\n }\n for &x in [1u8, 2] { assert!(x > 0); }\n}", &out2, &st2); + const n2 : usize = body("#[test]\nfn t() {\n for bad in [\"9bad\", \"has space\", \"\"] {\n assert!(bad.len() < 20);\n }\n for &x in [1u8, 2] { assert!(x > 0); }\n}", &out2, &st2); assert(out_is(n, &out, 0, out2[0 .. n2])); } @@ -13792,7 +13991,7 @@ test expr_writes_operators_with_the_parentheses_t27_needs { } test expr_writes_texts_ranges_paths_literals_and_cfg { - const head : str = "struct Node { v: u8, name: bool }\nfn g(x: u32) -> u32 { x }\nfn f(a: u32, b: u32, s: &str, n: &Node, v: &[u8]) { "; + const head : str = "struct Node { v: u8, name: bool }\nimpl Node { fn new(v: u8) -> Node { Node { v: v, name: false } } }\nfn g(x: u32) -> u32 { x }\nfn f(a: u32, b: u32, s: &str, n: &Node, v: &[u8]) { "; const ins : [41]str = ["s == \"x\"", "\"x\" == s", "s != \"x\"", "s == r\"x\"", "s == b\"x\"", "v[1..3]", "v[..2]", "v[1..]", "v[..=2]", "v[1..=a]", "v[..]", "s[a..]", "v[a + 1..b]", "u32::MAX", "i8::MIN", "u8::MIN", "i64::MAX", "Node::new(1)", "Node::Kind", "Node::MAX_SIZE", "cmp::max(a, b)", "std::cmp::min(a, b)", "super::g(a)", "self::LIMIT", "crate::g(a)", "Node { v: 1, name }", "Node { }", "[1, 2, 3]", "[0; 4]", "[a; b + 1]", "(a)", "s.len()", "s.is_empty()", "a.wrapping_add(b)", "a.pow(2)", "cfg!(unix)", "cfg!(windows)", "cfg!(not(windows))", "cfg!(all(unix, target_os = \"linux\"))", "cfg!(any(windows, target_os = \"macos\"))", "n.name"]; const outs : [41]str = ["type_text::same(s, \"x\")", "type_text::same(\"x\", s)", "!type_text::same(s, \"x\")", "type_text::same(s, \"x\")", "type_text::same(s, \"x\")", "v[1 .. 3]", "v[0 .. 2]", "v[1 .. v.len]", "v[0 .. 2 + 1]", "v[1 .. a + 1]", "v", "s[a .. s.len]", "v[a + 1 .. b]", "4294967295", "(-128)", "0", "9223372036854775807", "Node_new(1)", "Node.Kind", "Node_MAX_SIZE", "@max(a, b)", "@min(a, b)", "g(a)", "LIMIT", "g(a)", "Node{ .v = 1, .name = name }", "Node{}", "[1, 2, 3]", "[0] ** 4", "[a] ** (b + 1)", "(a)", "s.len", "(s.len == 0)", "(a +% b)", "rust_std::pow_u32(a, 2)", "true", "false", "true", "true", "false", "n.name"]; var buf : [1024]u8 = [0] ** 1024; @@ -14639,6 +14838,221 @@ test the_second_receiver_of_a_trim_is_the_converted_receiver_not_the_rust_text { assert(out_is(n, &out, 0, "use tri::t27b::type_text;\n\nfn head_len(path: []const u8, i: usize) -> usize {\n return path[0 .. i][type_text::trim_lo(path[0 .. i], 0, path[0 .. i].len) .. type_text::trim_hi(path[0 .. i], type_text::trim_lo(path[0 .. i], 0, path[0 .. i].len), path[0 .. i].len)].len;\n}\n\nfn plain_len(s: []const u8) -> usize {\n return s[type_text::trim_lo(s, 0, s.len) .. type_text::trim_hi(s, type_text::trim_lo(s, 0, s.len), s.len)].len;\n}\n\n")); } +test qualified_fn_foreign_block_and_record_foreign_read_a_header_that_is_not_a_plain_fn { + const src : str = "unsafe fn a() ; extern \"C\" fn b() ; async fn c() ; unsafe extern \"C\" fn d() ; struct S ; extern \"C\" { fn e(); fn f(); } extern crate x ; fn plain() ; const unsafe fn g() ;"; + assert(qualified_fn(src, 0) == 10); + assert(qualified_fn(src, 16) == 30); + assert(qualified_fn(src, 36) == 45); + assert(qualified_fn(src, 51) == 72); + assert(qualified_fn(src, 150) == 166); + assert(qualified_fn(src, 137) == 140); + assert(qualified_fn(src, 78) == src.len); + assert(qualified_fn(src, 89) == src.len); + assert(qualified_fn(src, 120) == src.len); + assert(foreign_block(src, 89) == 100); + assert(foreign_block(src, 16) == src.len); + assert(foreign_block(src, 120) == src.len); + assert(foreign_block(src, 78) == src.len); + var st : [256]usize = [0] ** 256; + record_foreign(src, &st, 100, 119); + assert(st[NNAMES] == 2); + assert(named(src, &st, K_FN, 105, 106)); + assert(named(src, &st, K_FN, 113, 114)); + assert(!named(src, &st, K_FN, 10, 11)); +} + +test payload_binder_names_the_value_a_last_arm_some_or_ok_binds_and_refuses_the_other_payloads { + var st : [64]usize = [0] ** 64; + clear(&st); + assert(payload_binder("Some(x) => x", &st, 0, 4, 8, 12) == 5); + assert(st[ERR] == 0); + clear(&st); + assert(payload_binder("Ok(v) => v", &st, 0, 2, 6, 10) == 3); + assert(st[ERR] == 0); + clear(&st); + assert(payload_binder("Some(_) => 1", &st, 0, 4, 8, 12) == 12); + assert(st[ERR] == 0); + clear(&st); + assert(payload_binder("Err(e) => 1", &st, 0, 3, 7, 11) == 11); + assert(st[ERR] == 0); + clear(&st); + payload_binder("Err(e) => e", &st, 0, 3, 7, 11); + assert(st[ERR] == R_PATTERN); + clear(&st); + payload_binder("Some(1) => 1", &st, 0, 4, 8, 12); + assert(st[ERR] == R_PATTERN); + clear(&st); + payload_binder("Foo(x) => x", &st, 0, 3, 7, 11); + assert(st[ERR] == R_PATTERN); + clear(&st); + payload_binder("Foo(_) => 1", &st, 0, 3, 7, 11); + assert(st[ERR] == R_PATTERN); + clear(&st); + payload_binder("Some(a, b) => 1", &st, 0, 4, 11, 15); + assert(st[ERR] == R_PATTERN); +} + +test assoc_declared_asks_whether_an_impl_of_the_type_declares_the_fn { + const src : str = "impl Pt { fn new() -> Pt { Pt {} } } +impl Default for Lang { fn default() -> Lang { Lang::En } } +Pt new Lang default Pt origin Lang new Pt default Self new"; + var st : [1024]usize = [0] ** 1024; + index_decls(src, &st); + var mode : usize = 0; + while (mode < 2) { + st[IXON] = 1 - mode; + assert(assoc_declared(src, &st, 0, 0, 97, 99, 100, 103)); + assert(assoc_declared(src, &st, 0, 0, 104, 108, 109, 116)); + assert(!assoc_declared(src, &st, 0, 0, 117, 119, 120, 126)); + assert(!assoc_declared(src, &st, 0, 0, 127, 131, 132, 135)); + assert(!assoc_declared(src, &st, 0, 0, 136, 138, 139, 146)); + assert(assoc_declared(src, &st, 97, 99, 147, 151, 100, 103)); + assert(!assoc_declared(src, &st, 0, 0, 147, 151, 100, 103)); + mode = mode + 1; + } +} + +test need_prelude_notes_the_need_and_refuses_once_a_pass_found_no_test { + var st : [256]usize = [0] ** 256; + need_prelude("fn f() {}", &st, 0, 2); + assert(st[ERR] == 0); + assert(named("fn f() {}", &st, K_NEEDPRE, 0, 1)); + assert(!named("fn f() {}", &st, K_NOPRE, 0, 1)); + add_name("fn f() {}", &st, K_NOPRE, 0, 1, 0, 0); + need_prelude("fn f() {}", &st, 3, 4); + assert(st[ERR] == R_NOPRELUDE); + assert(st[AT] == 3); + clear(&st); + need_prelude("", &st, 0, 0); + assert(st[NNAMES] == 0); + assert(st[ERR] == 0); +} + +test mod_member_writes_a_gen_module_member_and_refuses_a_clash_or_a_module_it_cannot_splice { + var out : [64]u8 = undefined; + var st : [256]usize = [0] ** 256; + const w1 : str = "t27a::ASM_X(1) isa/t27a"; + add_name(w1, &st, K_MODPATH, 0, 4, 15, 23); + assert(named(w1, &st, K_MODPATH, 0, 4)); + st[P] = 11; + mod_member(w1, &st, &out, 0, 4, 6, 11); + assert(st[ERR] == 0); + assert(out_is(st[W], &out, 0, "t27a::ASM_X(1)")); + assert(named(w1, &st, K_MODUSED, 0, 4)); + clear(&st); + const w4 : str = "t27a::ASM_Y isa/t27a"; + add_name(w4, &st, K_MODPATH, 0, 4, 12, 20); + assert(named(w4, &st, K_MODPATH, 0, 4)); + st[P] = 11; + mod_member(w4, &st, &out, 0, 4, 6, 11); + assert(out_is(st[W], &out, 0, "t27a::ASM_Y")); + var st2 : [256]usize = [0] ** 256; + const w2 : str = "t27a::f(1) isa/t27a fn f() {}"; + add_name(w2, &st2, K_MODPATH, 0, 4, 11, 18); + assert(named(w2, &st2, K_MODPATH, 0, 4)); + st2[P] = 7; + mod_member(w2, &st2, &out, 0, 4, 6, 7); + assert(st2[ERR] == R_CLASH); + var st3 : [256]usize = [0] ** 256; + const w3 : str = "t27a::g(1) compiler/operator_text"; + add_name(w3, &st3, K_MODPATH, 0, 4, 11, 33); + assert(named(w3, &st3, K_MODPATH, 0, 4)); + st3[P] = 7; + mod_member(w3, &st3, &out, 0, 4, 6, 7); + assert(st3[ERR] == R_SPLICE); +} + +test bind_type_types_the_binder_of_an_option_or_a_result_by_its_declared_type { + var out : [64]u8 = undefined; + var st : [64]usize = [0] ** 64; + const src : str = "fn f(e: Option<&str>, r: Result, o: u8, v: Option>) { match e { } match r { } match o { } match q { } }"; + st[FLO] = 0; + bind_type(src, &st, &out, 75); + assert(out_is(st[W], &out, 0, " : []const u8")); + clear(&st); + bind_type(src, &st, &out, 87); + assert(out_is(st[W], &out, 0, " : u8")); + clear(&st); + bind_type(src, &st, &out, 99); + assert(st[W] == 0); + clear(&st); + bind_type(src, &st, &out, 111); + assert(st[W] == 0); + clear(&st); + bind_type(src, &st, &out, src.len); + assert(st[W] == 0); +} + +/// `Lang::default()` of a #[derive(Default)] and `Pt::origin()` name a `Type_f` no one writes (#10905); `Pt::new()` and `Self::new()` are declared. +test an_associated_call_nobody_declares_is_refused_and_one_an_impl_declares_converts { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("#[derive(Default)]\npub enum Lang { #[default] En, Ru }\npub struct Pt { x: u8 }\nimpl Pt {\n pub fn new() -> Pt { Pt { x: 1 } }\n pub fn twin() -> Pt { Self::new() }\n}\npub fn make() -> Lang { Lang::default() }\npub fn ok() -> Pt { Pt::new() }\npub fn bad() -> Pt { Pt::origin() }", &out, &st); + assert(out_is(n, &out, 0, "pub const Lang = enum(u32) {\n En,\n Ru,\n};\n\npub const Pt = struct {\n x : u8,\n};\n\npub fn Pt_new() -> Pt {\n return Pt{ .x = 1 };\n}\n\npub fn Pt_twin() -> Pt {\n return Pt_new();\n}\n\n// t27o: refused fn make (line 8): undeclared `default` at line 8\n\npub fn make() -> Lang {\n}\n\npub fn ok() -> Pt {\n return Pt_new();\n}\n\n// t27o: refused fn bad (line 10): undeclared `origin` at line 10\n\npub fn bad() -> Pt {\n}\n\n")); + assert(st[NOK] == 3); + assert(st[NTEST] == 0); +} + +/// `Some(want) => ..` as the last arm bound nothing, so `want` was undeclared (#10905); the binder takes the Option's type. +test a_last_arm_that_binds_a_payload_binds_it_and_the_other_payload_patterns_are_refused { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub fn pick(e: Option<&str>, a: &str) -> bool {\n match e { None => true, Some(want) => want == a.trim() }\n}\npub fn inc(e: Option) -> u8 {\n match e { None => 0, Some(v) => v + 1 }\n}\npub fn some_one(e: Option) -> u8 {\n match e { None => 0, Some(_) => 1 }\n}\npub fn lit(e: Option) -> u8 {\n match e { None => 0, Some(1) => 1 }\n}\npub fn code(e: Result) -> u8 {\n match e { Ok(_) => 0, Err(c) => c }\n}\npub fn unread(e: Result) -> u8 {\n match e { Ok(v) => v, Err(_) => 9 }\n}", &out, &st); + assert(out_is(n, &out, 0, "use tri::t27b::type_text;\n\npub fn pick(e: ?[]const u8, a: []const u8) -> bool {\n if (e == null) {\n return true;\n } else {\n const want : []const u8 = e.?;\n return want == a[type_text::trim_lo(a, 0, a.len) .. type_text::trim_hi(a, type_text::trim_lo(a, 0, a.len), a.len)];\n }\n}\n\npub fn inc(e: ?u8) -> u8 {\n if (e == null) {\n return 0;\n } else {\n const v : u8 = e.?;\n return v + 1;\n }\n}\n\npub fn some_one(e: ?u8) -> u8 {\n if (e == null) {\n return 0;\n } else {\n return 1;\n }\n}\n\n// t27o: refused fn lit (line 10): match pattern `Some` at line 11\n\npub fn lit(e: ?u8) -> u8 {\n}\n\n// t27o: refused fn code (line 13): match pattern `Ok` at line 14\n\npub fn code(e: ?u8) -> u8 {\n}\n\n// t27o: refused fn unread (line 16): match pattern `Ok` at line 17\n\npub fn unread(e: ?u8) -> u8 {\n}\n\n")); + assert(st[NOK] == 3); + assert(st[NTEST] == 0); +} + +/// `pub extern fn ext` was refused without its name, so its callers kept it (#10905); an `unsafe fn` registered the name `fn`. +test unsafe_and_extern_fns_are_refused_under_their_own_names_and_a_foreign_block_by_its_fns { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub unsafe fn raw(a: u8) -> u8 { a }\n\npub extern \"C\" fn ext(a: u8) -> u8 { a + 2 }\n\nextern \"C\" {\n fn lib_add(a: u8, b: u8) -> u8;\n}\n\npub fn plain(a: u8) -> u8 { a + 1 }\n\npub fn uses_ext() -> u8 { ext(1) + plain(2) }\n\npub fn uses_lib() -> u8 { plain(1) }\n\n#[test]\nfn t() {\n assert_eq!(plain(1), 2);\n assert_eq!(ext(1), 3);\n}", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused unsafe (line 1): item `unsafe` at line 1; 1 fn inside\n\n// t27o: refused extern (line 3): item `extern` at line 3; 1 fn inside\n\n// t27o: refused extern (line 5): item `extern` at line 5; 1 fn inside\n\npub fn plain(a: u8) -> u8 {\n return a + 1;\n}\n\n// t27o: refused fn uses_ext (line 11): uses refused `ext` at line 11\n\npub fn uses_ext() -> u8 {\n}\n\npub fn uses_lib() -> u8 {\n return plain(1);\n}\n\n// t27o: refused fn t (line 16): uses refused `ext` at line 18\n\n")); + assert(st[NOK] == 2); + assert(st[NTEST] == 0); +} + +/// `t27a::ASM_NO_MNEMONIC =>` was written `t27a_ASM_NO_MNEMONIC` (#10905). +test a_match_on_consts_of_a_gen_module_reads_module_colon_colon_const { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("#[path = \"../gen/rust/isa/t27a.rs\"]\nmod t27a;\npub fn text(s: u32) -> &'static str {\n match s {\n t27a::ASM_NO_MNEMONIC => \"no mnemonic\",\n t27a::ASM_BAD_WORD => \"bad word\",\n _ => \"rejected\",\n }\n}", &out, &st); + assert(out_is(n, &out, 0, "use tri::t27b::type_text;\nuse isa::t27a;\n\npub fn text(s: u32) -> []const u8 {\n if (s == t27a::ASM_NO_MNEMONIC) {\n return \"no mnemonic\";\n } else if (s == t27a::ASM_BAD_WORD) {\n return \"bad word\";\n } else {\n return \"rejected\";\n }\n}\n\n")); + assert(st[NOK] == 1); + assert(st[NTEST] == 0); +} + +/// sha256 declares a `same` beside type_text's (#10556), operator_text has a `use` of its own (#10843). +test a_call_into_a_module_t27c_cannot_splice_is_refused { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("#[path = \"../gen/rust/tri/crypto/sha256.rs\"]\nmod sha256;\n#[path = \"../gen/rust/compiler/operator_text.rs\"]\nmod operator_text;\npub fn digest(a: &[u8]) -> bool { sha256::hash(a).len() == 32 }\npub fn op(a: &[u8]) -> bool { operator_text::compound_binop(a).is_some() }\npub fn is_x(a: &str) -> bool { a == \"x\" }", &out, &st); + assert(out_is(n, &out, 0, "use tri::t27b::type_text;\n\n// t27o: refused fn digest (line 5): a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records) `sha256::hash` at line 5\n\npub fn digest(a: []const u8) -> bool {\n}\n\n// t27o: refused fn op (line 6): a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records) `operator_text::compound_binop` at line 6\n\npub fn op(a: []const u8) -> bool {\n}\n\npub fn is_x(a: []const u8) -> bool {\n return type_text::same(a, \"x\");\n}\n\n")); + assert(st[NOK] == 1); + assert(st[NTEST] == 0); +} + +/// t27c writes the std import and the assert helper only with a test block, so without one the fn is refused (#10905: `__t27_assert_fail`). +test an_assert_in_a_fn_needs_a_test_block_in_the_spec { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub fn must(x: u8) { assert!(x > 0); }", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused fn must (line 1): an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one) `assert` at line 1\n\npub fn must(x: u8) -> void {\n}\n\n")); + assert(st[NOK] == 0); + assert(st[NTEST] == 0); +} + +/// The same fn beside a test block is kept. +test an_assert_in_a_fn_stays_when_the_spec_has_a_test { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub fn must(x: u8) { assert!(x > 0); }\n#[test]\nfn t() {\n must(1);\n}", &out, &st); + assert(out_is(n, &out, 0, "pub fn must(x: u8) -> void {\n assert(x > 0);\n}\n\ntest t {\n must(1);\n}\n\n")); + assert(st[NOK] == 2); + assert(st[NTEST] == 1); +} + test path_prefix_path_and_the_module_paths_write_a_qualified_name { assert(path_prefix("crate", 0, 5)); assert(path_prefix("self", 0, 4)); @@ -14661,8 +15075,14 @@ test path_prefix_path_and_the_module_paths_write_a_qualified_name { assert(out_is(st[W], &out, 0, "@max(a, b)")); clear(&st); st[P] = 4; - path("Node::new(1)", &st, &out, 0, 4); + path("Node::new(1)\nimpl Node { fn new(v: u8) -> Node { Node { v: v } } }", &st, &out, 0, 4); assert(out_is(st[W], &out, 0, "Node_new(1)")); + assert(st[ERR] == 0); + // negative control: no impl of Node declares `default`, so the call is refused, not written + clear(&st); + st[P] = 4; + path("Node::default()\nimpl Node { fn new(v: u8) -> Node { Node { v: v } } }", &st, &out, 0, 4); + assert(st[ERR] == R_UNDECL); clear(&st); st[P] = 4; path("Node::Kind", &st, &out, 0, 4); @@ -14846,25 +15266,45 @@ test cfg_eval_reads_a_cfg_predicate_on_the_default_host { assert(!cfg_eval("unix windows", 5)); } -test known_defect_the_splice_refusal_reads_the_alias_not_the_module_path { - // #10843: path refuses a call into int_type_names only when the mod is itself named int_type_names; the - // check reads the alias of the K_MODPATH entry, not the #[path] module. When that is fixed the second - // half flips (R_SPLICE for the alias x too). +test splice_clash_reads_the_module_path_not_the_alias_it_was_mounted_under { + // #10843: path refuses a call into a module t27c cannot splice by the #[path] module, whatever alias the mod has var out : [64]u8 = undefined; var named_ok : [128]usize = [0] ** 128; - const m1 : str = "int_type_names::f(1) tri/t27b/int_type_names"; - add_name(m1, &named_ok, K_MODPATH, 0, 14, 22, 44); + const m1 : str = "int_type_names::f(1) compiler/int_type_names"; + add_name(m1, &named_ok, K_MODPATH, 0, 14, 21, 44); named_ok[P] = 14; path(m1, &named_ok, &out, 0, 14); assert(named_ok[ERR] == R_SPLICE); assert(named_ok[AT] == 0); var aliased : [128]usize = [0] ** 128; - const m2 : str = "x::f(1) tri/t27b/int_type_names"; - add_name(m2, &aliased, K_MODPATH, 0, 1, 8, 31); + const m2 : str = "x::f(1) compiler/operator_text"; + add_name(m2, &aliased, K_MODPATH, 0, 1, 8, 30); aliased[P] = 1; path(m2, &aliased, &out, 0, 1); - assert(aliased[ERR] == 0); - assert(out_is(aliased[W], &out, 0, "int_type_names::f(1)")); + assert(aliased[ERR] == R_SPLICE); + var clean : [128]usize = [0] ** 128; + const m3 : str = "x::f(1) tri/t27b/source_text"; + add_name(m3, &clean, K_MODPATH, 0, 1, 8, 28); + clean[P] = 1; + path(m3, &clean, &out, 0, 1); + assert(clean[ERR] == 0); + assert(out_is(clean[W], &out, 0, "source_text::f(1)")); +} + +test splice_clash_lists_the_modules_t27c_cannot_splice_beside_type_text { + const w : str = "tri/crypto/sha256 tri/crypto/sha512 tri/crypto/hex compiler/int_type_names compiler/operator_text tri/pr/preflight tri/utils/template tri/t27b/type_text tri/crypto/sha2560 crypto/sha256"; + assert(splice_clash(w, 0, 17)); + assert(splice_clash(w, 18, 35)); + assert(splice_clash(w, 36, 50)); + assert(splice_clash(w, 51, 74)); + assert(splice_clash(w, 75, 97)); + assert(splice_clash(w, 98, 114)); + assert(splice_clash(w, 115, 133)); + assert(!splice_clash(w, 134, 152)); + assert(!splice_clash(w, 153, 171)); + assert(!splice_clash(w, 172, 185)); + assert(!splice_clash(w, 5, 5)); + assert(!splice_clash(w, 9, 3)); } test result_value_and_matches_macro_write_ok_err_and_a_pattern_condition { @@ -16047,13 +16487,13 @@ test put_docs_writes_the_doc_lines_above_an_item_and_nothing_else { } test put_reason_names_the_refusal_and_the_text_it_points_at { - const codes : [39]usize = [R_SYNTAX, R_GENERIC, R_TRAIT, R_CLOSURE, R_TRY, R_MACRO, R_TYPE, R_METHOD, R_TUPLE, R_IFLET, R_ITER, R_PATTERN, R_MUTREF, R_DEREF, R_VALUE, R_LITERAL, R_LABEL, R_ITEM, R_UNSAFE, R_PATH, R_WHERE, R_SELFMUT, R_UPDATE, R_DATA, R_DEPENDS, R_EXTERNAL, R_UNDECL, R_CLASH, R_STRUCTEQ, R_STRUSE, R_SPLICE, R_EMPTY, R_NOTYPE, R_VECMUT, R_VECMOVE, R_VECTYPE, R_LIBM, R_SLICEGEN, R_SHIFTCALL]; - const why : [39]str = ["syntax", "generics", "trait impl", "closure", "? operator", "macro", "type", "method", "tuple", "if let / while let", "for over an iterator", "match pattern", "&mut of a value", "deref", "block as a value", "literal", "label or break value", "item", "unsafe", "path", "where clause", "mut self", "struct update", "enum variant with data", "uses refused", "uses external", "undeclared", "a used module's name, declared here too (use splices flat, see #7176)", "== of structs", "an owned String outside the out-buffer form", "a used module's own use t27c does not splice (its type_text:: names stay unresolved, the gap specs/compiler/int_type_names.t27 records)", "empty slice with no known element type", "`!` of an operand whose type t27o cannot tell", "a vec! the fn changes (push, resize, assign into, &mut)", "a vec! the fn hands on or returns", "a vec! whose element type or length t27o cannot tell", "libm function not bit-exact", "slice contains on", "a compound shift of a place that calls something"]; + const codes : [40]usize = [R_SYNTAX, R_GENERIC, R_TRAIT, R_CLOSURE, R_TRY, R_MACRO, R_TYPE, R_METHOD, R_TUPLE, R_IFLET, R_ITER, R_PATTERN, R_MUTREF, R_DEREF, R_VALUE, R_LITERAL, R_LABEL, R_ITEM, R_UNSAFE, R_PATH, R_WHERE, R_SELFMUT, R_UPDATE, R_DATA, R_DEPENDS, R_EXTERNAL, R_UNDECL, R_CLASH, R_STRUCTEQ, R_STRUSE, R_SPLICE, R_EMPTY, R_NOTYPE, R_VECMUT, R_VECMOVE, R_VECTYPE, R_LIBM, R_SLICEGEN, R_SHIFTCALL, R_NOPRELUDE]; + const why : [40]str = ["syntax", "generics", "trait impl", "closure", "? operator", "macro", "type", "method", "tuple", "if let / while let", "for over an iterator", "match pattern", "&mut of a value", "deref", "block as a value", "literal", "label or break value", "item", "unsafe", "path", "where clause", "mut self", "struct update", "enum variant with data", "uses refused", "uses external", "undeclared", "a used module's name, declared here too (use splices flat, see #7176)", "== of structs", "an owned String outside the out-buffer form", "a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records)", "empty slice with no known element type", "`!` of an operand whose type t27o cannot tell", "a vec! the fn changes (push, resize, assign into, &mut)", "a vec! the fn hands on or returns", "a vec! whose element type or length t27o cannot tell", "libm function not bit-exact", "slice contains on", "a compound shift of a place that calls something", "an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one)"]; var out : [512]u8 = undefined; var st : [64]usize = [0] ** 64; var w : [512]u8 = [0] ** 512; var i : usize = 0; - while (i < 39) { + while (i < 40) { clear(&st); st[ERR] = codes[i]; st[AT] = 3; From 8c6cb0bdd1176cbc0596662eeea54b5de717c4eb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:01:08 +0700 Subject: [PATCH 6/6] t27o: what t27c has no form for is refused -- operators and casts on enum, struct, array and text values, borrows, types -- and the arrays and bool methods it reads wrongly are typed (Closes #10905) Class 6 of #10905, the rest of the 55: 26 specs, each blocked by a Rust construct whose t27 spelling t27c rejects. Refused (new reason R_OPERAND, operand_kind says what an operand is): - `a < b`, `a == b` on structs (version.rs SemVer), `==` on arrays (SR_ALG_03, preregistration_seed_lock_final, trios-phd main: assert_eq!(CONST, [..])), `>` on texts (issues.rs open_at), arithmetic on enum values (`Trit::Pos * Trit::Neg`), `-Trit::Pos`, `x as i8` of an enum (rings 092 .. 098, trit.rs x4): t27c has no operator or cast for them. Two enums ordered or compared (`Health.Critical < Health.Healthy`) compile and run in t27c and stay. - `*tag` of a `&[u8; 4]` parameter (firmware.rs) and `&self.session` of an array field (CR_CHAT_04): the parameter is written a slice, the field is an array, and a slice of the fn's own copy of self dangles. - `[]const ?f64` (`&[Option]`, fpga.rs), which t27c prints as a broken parameter list; a struct or enum named like a type t27c renames in its Zig backend (`struct GF16(u16)` is u16 there: golden-float lib.rs). - two texts or byte slices compared with `==` and an assert in a fn, in a spec with no test block: R_NOPRELUDE, as the previous commit does for the assert (SR_ALG_00, CR_CHAT_06, `std` undeclared). Written: - `let cmds = [Cmd::Nop, ..]; for c in cmds`, `let mut scores = [0.0f64; N]; scores[0] = 1.0`, an array borrowed (`&input`) or indexed by a run-time value is a typed array (protocol.rs, red.rs, ring_099, ring_092, fpga.rs); an element type no one gives, for an array that is written, is refused. vec_first_type reads a const's declared type for `[PVT_TEMP_MIN_C, 20i64]`. - `const NAMES: &[&str] = &[..]` is the array `[N][]const u8` (t27c has no value for a slice const: bitnet_bundle.rs), unless the file hands the name whole to a call. - `let mut r = 1.0;` is `var r : f64` when the file never says f32, refused when it does (pow_u64 of rings 095, 098, 099). - `x.validate().is_ok()` of a method that returns Result<(), E> is a bool (mup.rs); `.unwrap()` / `.expect()` of one is refused (ema.rs). enum_open finds an enum through the declaration index (find_enum walked the file once per operator: compiler.rs took 56 s of CPU, now 7 s as before). Tests: every new fn directly (type_kind, operand_kind over 30 operands, operands_ok, for_var_kind, let_array, slice_const, ..) and a body test per construct whose output compiled under t27c test-report; the reasons table test now carries R_OPERAND, R_TYPENAME and R_FLOATLIT. t27b 332 / 332, coverage 395 of 395, seals re-saved and verified. With this commit all 51 Rust specs of the 55 compile under t27c (42 also pass t27b; 9 meet t27b's own gaps: a struct or array const, @max, a repeat count that is a sum, an f64 cast, a u32/u64 mix, a stub reached by name). The 180 of 492 corpus outputs that change all stay valid: no t27c or t27b pass became a failure. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oRust.json | 16 +- gen/rust/tools/t27o/rust_convert.rs | 732 ++++++++++++++++- specs/tools/t27o/rust_convert.t27 | 1133 +++++++++++++++++++++++++-- 3 files changed, 1790 insertions(+), 91 deletions(-) diff --git a/.trinity/seals/t27o_T27oRust.json b/.trinity/seals/t27o_T27oRust.json index 6363f53eb7..70eccd8fb1 100644 --- a/.trinity/seals/t27o_T27oRust.json +++ b/.trinity/seals/t27o_T27oRust.json @@ -2,23 +2,23 @@ "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:b050777eb5e6702d557eb0d0982ee348656d7439f06704b33f133827206f6d6d", - "gen_hash_rust": "sha256:73c0aa1fdb4e309ea3271902c350779bd45dcdab0df91b0f24942fdc895df672", - "gen_hash_verilog": "sha256:9b276398fcdd3b99a62485b3a067459b5cbeeb9707a94645b638b26522c96cf4", - "gen_hash_zig": "sha256:c81dabfe060b13fb6afec509a3fb1057f35163342240662793a7486e9ad8da76", + "gen_hash_c": "sha256:d4ad54395165650f1142310418c8b75baf5da83eb167369360e555618fab54a3", + "gen_hash_rust": "sha256:812e54a96c93a3f0a028bda32358e7bbad97b82296c4e68dbdf8873c869abfeb", + "gen_hash_verilog": "sha256:781df430b1e5fefc13499c7997f25753f6290cdf14dd10856cbb126068fa8cd4", + "gen_hash_zig": "sha256:18a94a5f6a080c4db768f632a788e0b390de811a2525fdbd6537778191b8cd1f", "module": "T27oRust", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T09:52:40Z", + "sealed_at": "2026-10-19T09:59:57Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:ce9baea9768eb18952e33a36066fbbee71e988b378c47fcb9d60f081883471fa", + "spec_hash": "sha256:f95769587da94282b6fb0c99d2e72288c10d5381f0393cbe315e3a334cef7fd1", "spec_path": "specs/tools/t27o/rust_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 312, - "total": 312, + "passed": 332, + "total": 332, "vacuous": 0 }, "toolchain": { diff --git a/gen/rust/tools/t27o/rust_convert.rs b/gen/rust/tools/t27o/rust_convert.rs index e70863d7f8..833a3c4e58 100644 --- a/gen/rust/tools/t27o/rust_convert.rs +++ b/gen/rust/tools/t27o/rust_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 312 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 332 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -312,6 +312,12 @@ pub const R_SLICEGEN: usize = 38; pub const R_SHIFTCALL: usize = 39; +pub const R_OPERAND: usize = 41; + +pub const R_TYPENAME: usize = 42; + +pub const R_FLOATLIT: usize = 43; + pub const R_NOPRELUDE: usize = 40; pub const VU_READ: usize = 0; @@ -1785,7 +1791,12 @@ pub fn param_slice(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: us step(src, st); step(src, st); puts(st, out, b"[]const "); + let ve: usize = st[(P) as usize]; + let wv: usize = st[(W) as usize]; ty(src, st, out); + if (!(bad(st[(ERR) as usize])) && (out_at(st[(W) as usize], out, wv) == B_QMARK)) { + refuse(st, R_TYPE, ve, ve, tok_end(src, ve)); + } close_angle(src, st); return true; } @@ -1845,7 +1856,12 @@ pub fn ty(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { } else { puts(st, out, b"[]const "); } + let es: usize = st[(P) as usize]; + let we: usize = st[(W) as usize]; ty(src, st, out); + if (!(bad(st[(ERR) as usize])) && (out_at(st[(W) as usize], out, we) == B_QMARK)) { + refuse(st, R_TYPE, es, es, tok_end(src, es)); + } if take(src, st, b";") { let close: usize = find_at(src, st[(P) as usize], B_RBRACKET); if (close < src.len()) { @@ -1886,7 +1902,11 @@ pub fn ty(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { if nested_elem(src, st, false) { return (); } + let we2: usize = st[(W) as usize]; ty(src, st, out); + if (!(bad(st[(ERR) as usize])) && (out_at(st[(W) as usize], out, we2) == B_QMARK)) { + refuse(st, R_TYPE, elem, elem, tok_end(src, elem)); + } st[(P) as usize] = after; return (); } @@ -2009,12 +2029,336 @@ pub fn str_tok(src: &[u8], q: usize) -> bool { return (((src[(q) as usize] == B_LOWER_B) && ((q + 1) < src.len())) && (src[((q + 1)) as usize] == B_QUOTE)); } +pub const OK_NONE: usize = 0; + +pub const OK_STRUCT: usize = 1; + +pub const OK_ENUM: usize = 2; + +pub const OK_ARRAY: usize = 3; + +pub const OK_TEXT: usize = 4; + +pub const OK_BYTES: usize = 5; + +pub fn value_end(src: &[u8], q: usize) -> usize { + let mut n: usize = q; + let mut ne: usize = tok_end(src, n); + let mut nx: usize = skip_ws(src, ne); + if (is_upper(src[(n) as usize]) && (tok_is(src, nx, b"{") || tok_is(src, nx, b"("))) { + return skip_ws(src, block_end(src, nx)); + } + if (tok_is(src, nx, b"::") || tok_is(src, nx, b".")) { + n = skip_ws(src, tok_end(src, nx)); + ne = tok_end(src, n); + nx = skip_ws(src, ne); + } + if tok_is(src, nx, b"(") { + return skip_ws(src, block_end(src, nx)); + } + return src.len(); +} + +pub fn type_kind(src: &[u8], st: &mut [usize], d: usize) -> usize { + let mut q: usize = d; + let mut by_ref: bool = false; + while ((q < src.len()) && (((src[(q) as usize] == B_AMP) || tok_is(src, q, b"mut")) || (src[(q) as usize] == B_APOS))) { + if (src[(q) as usize] == B_AMP) { + by_ref = true; + } + q = skip_ws(src, tok_end(src, q)); + } + if (q >= src.len()) { + return OK_NONE; + } + if (src[(q) as usize] == B_LBRACKET) { + if (!(by_ref) && (find_at(src, skip_ws(src, (q + 1)), B_SEMI) < block_end(src, q))) { + return OK_ARRAY; + } + return OK_NONE; + } + if !(is_upper(src[(q) as usize])) { + return OK_NONE; + } + let mut tb: usize = q; + let mut te: usize = tok_end(src, q); + if word_is(src, q, te, b"Self") { + tb = st[(ILO) as usize]; + te = st[(IHI) as usize]; + } + if (te <= tb) { + return OK_NONE; + } + if (enum_open(src, st, tb, te) < src.len()) { + return OK_ENUM; + } + if (((find_struct(src, st, tb, te) < src.len()) || named(src, st, K_TUPLE, tb, te)) || unit_struct(src, st, tb, te)) { + return OK_STRUCT; + } + return OK_NONE; +} + +pub fn let_init_of(src: &[u8], flo: usize, q: usize, na: usize, nb: usize) -> usize { + let mut best: usize = src.len(); + let mut p: usize = flo; + while (p < q) { + let e: usize = tok_end(src, p); + if word_is(src, p, e, b"let") { + let mut n: usize = skip_ws(src, e); + if tok_is(src, n, b"mut") { + n = skip_ws(src, tok_end(src, n)); + } + if same_text(src, n, tok_end(src, n), na, nb) { + let i: usize = init_at(src, p); + if (i < src.len()) { + best = i; + } + } + } + p = skip_ws(src, e); + } + return best; +} + +pub fn for_var_kind(src: &[u8], st: &mut [usize], flo: usize, q: usize, na: usize, nb: usize) -> usize { + let mut it: usize = src.len(); + let mut p: usize = flo; + while (p < q) { + let e: usize = tok_end(src, p); + if word_is(src, p, e, b"for") { + let n: usize = skip_ws(src, e); + let ne: usize = tok_end(src, n); + let k: usize = skip_ws(src, ne); + if (same_text(src, n, ne, na, nb) && tok_is(src, k, b"in")) { + it = skip_ws(src, tok_end(src, k)); + if tok_is(src, it, b"&") { + it = skip_ws(src, tok_end(src, it)); + } + } + } + p = skip_ws(src, e); + } + if (it >= src.len()) { + return OK_NONE; + } + let mut lit: usize = src.len(); + if (src[(it) as usize] == B_LBRACKET) { + lit = it; + } else { + if is_alpha(src[(it) as usize]) { + let ie: usize = tok_end(src, it); + let li: usize = let_init_of(src, flo, it, it, ie); + if ((li < src.len()) && (src[(li) as usize] == B_LBRACKET)) { + lit = li; + } + if (lit >= src.len()) { + let mut d: usize = decl_type(src, flo, it, it, ie); + while ((d < src.len()) && (((src[(d) as usize] == B_AMP) || tok_is(src, d, b"mut")) || (src[(d) as usize] == B_APOS))) { + d = skip_ws(src, tok_end(src, d)); + } + if ((d < src.len()) && (src[(d) as usize] == B_LBRACKET)) { + return type_kind(src, st, skip_ws(src, (d + 1))); + } + if ((d < src.len()) && tok_is(src, d, b"Vec")) { + return type_kind(src, st, skip_ws(src, (skip_ws(src, tok_end(src, d)) + 1))); + } + return OK_NONE; + } + } + } + if (lit >= src.len()) { + return OK_NONE; + } + let e0: usize = skip_ws(src, (lit + 1)); + if ((e0 >= src.len()) || !(is_upper(src[(e0) as usize]))) { + return OK_NONE; + } + return type_kind(src, st, e0); +} + +pub fn let_as_bytes(src: &[u8], flo: usize, q: usize, na: usize, nb: usize) -> bool { + let li: usize = let_init_of(src, flo, q, na, nb); + if (li >= src.len()) { + return false; + } + let semi: usize = find_at(src, li, B_SEMI); + let mut p: usize = li; + while (p < semi) { + let e: usize = tok_end(src, p); + if word_is(src, p, e, b"as_bytes") { + let lp: usize = skip_ws(src, e); + let rp: usize = skip_ws(src, (lp + 1)); + if ((tok_is(src, lp, b"(") && tok_is(src, rp, b")")) && (skip_ws(src, (rp + 1)) >= semi)) { + return true; + } + } + p = skip_ws(src, e); + } + return false; +} + +pub fn operand_kind(src: &[u8], st: &mut [usize], lo: usize, hi: usize) -> usize { + let mut q: usize = lo; + while ((q < hi) && (((src[(q) as usize] == B_AMP) || (src[(q) as usize] == B_STAR)) || tok_is(src, q, b"mut"))) { + q = skip_ws(src, tok_end(src, q)); + } + if (q >= hi) { + return OK_NONE; + } + if ((src[(q) as usize] == B_LPAREN) && (skip_ws(src, block_end(src, q)) >= hi)) { + return operand_kind(src, st, skip_ws(src, (q + 1)), (block_end(src, q) - 1)); + } + if (src[(q) as usize] == B_LBRACKET) { + if (skip_ws(src, block_end(src, q)) >= hi) { + return OK_ARRAY; + } + return OK_NONE; + } + if (str_tok(src, q) || raw_str_at(src, q)) { + if (skip_ws(src, tok_end(src, q)) >= hi) { + return OK_TEXT; + } + return OK_NONE; + } + if !(is_alpha(src[(q) as usize])) { + return OK_NONE; + } + let e: usize = tok_end(src, q); + let nx: usize = skip_ws(src, e); + if (struct_value(src, st, q) && (value_end(src, q) >= hi)) { + return OK_STRUCT; + } + if (is_upper(src[(q) as usize]) && tok_is(src, nx, b"::")) { + let v: usize = skip_ws(src, tok_end(src, nx)); + let ve: usize = tok_end(src, v); + if ((((v < src.len()) && is_alpha(src[(v) as usize])) && (skip_ws(src, ve) >= hi)) && enum_has(src, st, q, e, v, ve)) { + return OK_ENUM; + } + return OK_NONE; + } + if ((tok_is(src, nx, b"[") && (skip_ws(src, block_end(src, nx)) >= hi)) && (find_tok_to(src, (nx + 1), block_end(src, nx), b"..") < src.len())) { + let u0: usize = st[(TYLO) as usize]; + let u1: usize = st[(TYHI) as usize]; + let u2: usize = st[(TYARR) as usize]; + let bc: u8 = recv_class(src, st, q, e); + let mut bk: usize = OK_NONE; + if (bc == C_STR) { + bk = OK_BYTES; + } + if (((bc == C_SLICE) && (st[(TYARR) as usize] != 1)) && word_is(src, st[(TYLO) as usize], st[(TYHI) as usize], b"u8")) { + bk = OK_BYTES; + } + if ((bk == OK_NONE) && let_as_bytes(src, st[(FLO) as usize], q, q, e)) { + bk = OK_BYTES; + } + st[(TYLO) as usize] = u0; + st[(TYHI) as usize] = u1; + st[(TYARR) as usize] = u2; + return bk; + } + let mut f: usize = src.len(); + let mut fe: usize = src.len(); + let mut simple: bool = (nx >= hi); + if (!(simple) && tok_is(src, nx, b".")) { + f = skip_ws(src, (nx + 1)); + fe = tok_end(src, f); + simple = (((f < src.len()) && (is_alpha(src[(f) as usize]) || is_digit(src[(f) as usize]))) && (skip_ws(src, fe) >= hi)); + } + if !(simple) { + return OK_NONE; + } + let t0: usize = st[(TYLO) as usize]; + let t1: usize = st[(TYHI) as usize]; + let t2: usize = st[(TYARR) as usize]; + let mut k: usize = OK_NONE; + if (nx >= hi) { + if word_is(src, q, e, b"self") { + if (((find_struct(src, st, st[(ILO) as usize], st[(IHI) as usize]) < src.len()) || named(src, st, K_TUPLE, st[(ILO) as usize], st[(IHI) as usize])) || unit_struct(src, st, st[(ILO) as usize], st[(IHI) as usize])) { + k = OK_STRUCT; + } + if ((st[(IHI) as usize] > st[(ILO) as usize]) && (enum_open(src, st, st[(ILO) as usize], st[(IHI) as usize]) < src.len())) { + k = OK_ENUM; + } + } else { + let mut d: usize = decl_type(src, st[(FLO) as usize], q, q, e); + if (d >= src.len()) { + d = const_type(src, st, q, e); + } + if (d >= src.len()) { + d = let_owner(src, st, q, e); + } + if (d < src.len()) { + k = type_kind(src, st, d); + } + if (d >= src.len()) { + k = for_var_kind(src, st, st[(FLO) as usize], q, q, e); + } + } + } else { + let lb: usize = head_struct(src, st, st[(FLO) as usize], st[(ILO) as usize], st[(IHI) as usize], q, e); + if (lb < src.len()) { + let mut ft: usize = src.len(); + if is_digit(src[(f) as usize]) { + ft = field_type(src, st, lb, ((src[(f) as usize] - B_ZERO) as usize)); + } else { + ft = decl_type(src, lb, block_end(src, lb), f, fe); + } + if (ft < src.len()) { + k = type_kind(src, st, ft); + } + } + } + let rc: u8 = recv_class(src, st, q, hi); + if (((k == OK_NONE) && (rc == C_SLICE)) && (st[(TYARR) as usize] == 1)) { + k = OK_ARRAY; + } + if ((k == OK_NONE) && (rc == C_STR)) { + k = OK_TEXT; + } + if ((((k == OK_NONE) && (rc == C_SLICE)) && (st[(TYARR) as usize] != 1)) && word_is(src, st[(TYLO) as usize], st[(TYHI) as usize], b"u8")) { + k = OK_BYTES; + } + if (((k == OK_NONE) && (nx >= hi)) && let_as_bytes(src, st[(FLO) as usize], q, q, e)) { + k = OK_BYTES; + } + st[(TYLO) as usize] = t0; + st[(TYHI) as usize] = t1; + st[(TYARR) as usize] = t2; + return k; +} + +pub fn operands_ok(src: &[u8], st: &mut [usize], l0: usize, l1: usize, r0: usize, r1: usize, oa: usize, ob: usize, eq: bool, ord: bool) -> () { + if bad(st[(ERR) as usize]) { + return (); + } + if (((((!(eq) && !(ord)) && !(tok_is(src, l0, b"self"))) && !(tok_is(src, r0, b"self"))) && !((((l0 < src.len()) && is_upper(src[(l0) as usize])) && peek_is(src, l0, b"::")))) && !((((r0 < src.len()) && is_upper(src[(r0) as usize])) && peek_is(src, r0, b"::")))) { + return (); + } + let lk: usize = operand_kind(src, st, l0, l1); + let rk: usize = operand_kind(src, st, r0, r1); + let mut no: bool = ((((lk == OK_STRUCT) || (lk == OK_ARRAY)) || (rk == OK_STRUCT)) || (rk == OK_ARRAY)); + if ((!(eq) && !(ord)) && ((lk == OK_ENUM) || (rk == OK_ENUM))) { + no = true; + } + if (ord && ((((lk == OK_TEXT) || (rk == OK_TEXT)) || (lk == OK_BYTES)) || (rk == OK_BYTES))) { + no = true; + } + if no { + refuse(st, R_OPERAND, oa, oa, ob); + return (); + } + if ((eq && ((lk == OK_TEXT) || (lk == OK_BYTES))) && ((rk == OK_TEXT) || (rk == OK_BYTES))) { + need_prelude(src, st, oa, ob); + } +} + pub fn expr(src: &[u8], st: &mut [usize], out: &mut [u8], ns: bool) -> usize { return expr_bp(src, st, out, L_OR, ns); } pub fn expr_bp(src: &[u8], st: &mut [usize], out: &mut [u8], min: usize, ns: bool) -> usize { let start: usize = st[(W) as usize]; + let sp: usize = st[(P) as usize]; let mut lvl: usize = unary(src, st, out, ns); while !(bad(st[(ERR) as usize])) { let a: usize = st[(P) as usize]; @@ -2025,6 +2369,10 @@ pub fn expr_bp(src: &[u8], st: &mut [usize], out: &mut [u8], min: usize, ns: boo } step(src, st); if (lv == L_AS) { + if (operand_kind(src, st, sp, a) == OK_ENUM) { + refuse(st, R_OPERAND, a, a, b); + return lvl; + } if (lvl != L_ATOM) { wrap(st, out, start); } @@ -2065,10 +2413,12 @@ pub fn expr_bp(src: &[u8], st: &mut [usize], out: &mut [u8], min: usize, ns: boo } puts(st, out, b" "); let rs: usize = st[(W) as usize]; + let rsp: usize = st[(P) as usize]; let rl: usize = expr_bp(src, st, out, (lv + 1), ns); if !(keep_bare(rl, lv, false)) { wrap(st, out, rs); } + operands_ok(src, st, sp, a, rsp, st[(P) as usize], a, b, (word_is(src, a, b, b"==") || word_is(src, a, b, b"!=")), ((lv == L_CMP) && !((word_is(src, a, b, b"==") || word_is(src, a, b, b"!="))))); lvl = lv; } } @@ -2099,11 +2449,49 @@ pub fn array_local(src: &[u8], flo: usize, a: usize, b: usize) -> bool { return false; } +pub fn param_type(src: &[u8], flo: usize, a: usize, b: usize) -> usize { + let lp: usize = find_at(src, flo, B_LPAREN); + if (lp >= src.len()) { + return src.len(); + } + let rp: usize = block_end(src, lp); + let mut q: usize = skip_ws(src, (lp + 1)); + while (q < rp) { + let e: usize = tok_end(src, q); + let c: usize = skip_ws(src, e); + if (same_text(src, q, e, a, b) && tok_is(src, c, b":")) { + return skip_ws(src, tok_end(src, c)); + } + q = skip_ws(src, e); + } + return src.len(); +} + +pub fn array_ref_param(src: &[u8], flo: usize, a: usize, b: usize) -> bool { + let mut d: usize = param_type(src, flo, a, b); + if ((d >= src.len()) || !(tok_is(src, d, b"&"))) { + return false; + } + d = skip_ws(src, tok_end(src, d)); + if ((d < src.len()) && (src[(d) as usize] == B_APOS)) { + d = skip_ws(src, tok_end(src, d)); + } + if tok_is(src, d, b"mut") { + d = skip_ws(src, tok_end(src, d)); + } + if !(tok_is(src, d, b"[")) { + return false; + } + let el: usize = skip_ws(src, (d + 1)); + return (find_at(src, el, B_SEMI) < block_end(src, d)); +} + pub fn unary(src: &[u8], st: &mut [usize], out: &mut [u8], ns: bool) -> usize { if bad(st[(ERR) as usize]) { return L_ATOM; } if take(src, st, b"-") { + let na: usize = st[(P) as usize]; if at(src, st[(P) as usize], b"(") { puts(st, out, b"(0 - "); unary(src, st, out, ns); @@ -2112,6 +2500,9 @@ pub fn unary(src: &[u8], st: &mut [usize], out: &mut [u8], ns: bool) -> usize { } puts(st, out, b"-"); unary(src, st, out, ns); + if (!(bad(st[(ERR) as usize])) && (operand_kind(src, st, na, st[(P) as usize]) == OK_ENUM)) { + refuse(st, R_OPERAND, na, na, tok_end(src, na)); + } return L_UNARY; } if take(src, st, b"!") { @@ -2154,6 +2545,11 @@ pub fn unary(src: &[u8], st: &mut [usize], out: &mut [u8], ns: bool) -> usize { puts(st, out, b"self.*"); return L_ATOM; } + let rp: usize = st[(P) as usize]; + if (((rp < src.len()) && is_alpha(src[(rp) as usize])) && array_ref_param(src, st[(FLO) as usize], rp, tok_end(src, rp))) { + refuse(st, R_OPERAND, rp, rp, tok_end(src, rp)); + return L_ATOM; + } return unary(src, st, out, ns); } if (take(src, st, b"&") || take(src, st, b"&&")) { @@ -2167,6 +2563,10 @@ pub fn unary(src: &[u8], st: &mut [usize], out: &mut [u8], ns: bool) -> usize { if arr { insert(st, out, s, b"&"); } + if (((!(arr) && !(bad(st[(ERR) as usize]))) && (operand_kind(src, st, oa, st[(P) as usize]) == OK_ARRAY)) && (find_at(src, oa, B_DOT) < st[(P) as usize])) { + refuse(st, R_OPERAND, oa, oa, ob); + return l; + } if ((m && !(arr)) && (out_at(st[(W) as usize], out, (st[(W) as usize] - 1)) != B_RBRACKET)) { refuse(st, R_MUTREF, oa, oa, ob); } @@ -3071,6 +3471,23 @@ pub fn recv_class(src: &[u8], st: &mut [usize], ra: usize, rb: usize) -> u8 { } return C_ANY; } + if (is_alpha(src[(ra) as usize]) && tok_is(src, nx, b".")) { + let m: usize = skip_ws(src, (nx + 1)); + let me: usize = tok_end(src, m); + let lp: usize = skip_ws(src, me); + if (((((m < src.len()) && is_alpha(src[(m) as usize])) && tok_is(src, lp, b"(")) && (skip_ws(src, block_end(src, lp)) >= rb)) && unit_result_fn(src, st, m, me)) { + let olo: usize = st[(OLO) as usize]; + let ohi: usize = st[(OHI) as usize]; + let okind: usize = st[(OKIND) as usize]; + let own: bool = method_owner(src, st, m, me, 0, 0); + st[(OLO) as usize] = olo; + st[(OHI) as usize] = ohi; + st[(OKIND) as usize] = okind; + if own { + return C_BOOL; + } + } + } let mut cls: u8 = C_ANY; let mut ilo: usize = 0; let mut ihi: usize = 0; @@ -4036,6 +4453,10 @@ pub fn method(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, ra: us return (); } let cls: u8 = recv_class(src, st, ra, rb); + if ((cls == C_BOOL) && (word_is(src, na, nb, b"unwrap") || word_is(src, na, nb, b"expect"))) { + refuse(st, R_METHOD, na, na, nb); + return (); + } let usz: bool = (st[(TYARR) as usize] == 2); let mut tyl: usize = st[(TYLO) as usize]; let mut tyh: usize = st[(TYHI) as usize]; @@ -4697,7 +5118,7 @@ pub fn path(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, b0: usize) return (); } put_owner(src, st, out, a, b); - if (upper_const(src, ma, mb) && !(enum_has(src, st[(ILO) as usize], st[(IHI) as usize], a, b, ma, mb))) { + if (upper_const(src, ma, mb) && !(enum_has(src, st, a, b, ma, mb))) { puts(st, out, b"_"); } else { puts(st, out, b"."); @@ -5447,10 +5868,161 @@ pub fn let_stmt(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { st[(LETTY) as usize] = 0; return (); } + if (((init < src.len()) && (src[(init) as usize] == B_LBRACKET)) && !(typed)) { + let hi: usize = item_end(src, st[(FLO) as usize]); + let written: bool = array_written(src, block_end(src, init), hi, na, nb); + if (((written || array_iterated(src, block_end(src, init), hi, na, nb)) || array_borrowed(src, block_end(src, init), hi, na, nb)) || array_indexed(src, block_end(src, init), hi, na, nb)) { + if let_array(src, st, out, mutable, na, nb, init, written) { + st[(LETTY) as usize] = 0; + return (); + } + } + } let_value(src, st, out, mutable, na, nb, typed, init); st[(LETTY) as usize] = 0; } +pub fn array_iterated(src: &[u8], lo: usize, hi: usize, na: usize, nb: usize) -> bool { + let mut q: usize = lo; + while (q < hi) { + let e: usize = tok_end(src, q); + if word_is(src, q, e, b"in") { + let mut n: usize = skip_ws(src, e); + if tok_is(src, n, b"&") { + n = skip_ws(src, tok_end(src, n)); + } + if same_text(src, n, tok_end(src, n), na, nb) { + return true; + } + } + q = skip_ws(src, e); + } + return false; +} + +pub fn array_indexed(src: &[u8], lo: usize, hi: usize, na: usize, nb: usize) -> bool { + let mut q: usize = lo; + while (q < hi) { + let e: usize = tok_end(src, q); + if (same_text(src, q, e, na, nb) && tok_is(src, skip_ws(src, e), b"[")) { + let lb: usize = skip_ws(src, e); + let i0: usize = skip_ws(src, (lb + 1)); + let ie: usize = tok_end(src, i0); + if !((((i0 < src.len()) && is_digit(src[(i0) as usize])) && tok_is(src, skip_ws(src, ie), b"]"))) { + return true; + } + } + q = skip_ws(src, e); + } + return false; +} + +pub fn array_borrowed(src: &[u8], lo: usize, hi: usize, na: usize, nb: usize) -> bool { + let mut q: usize = lo; + while (q < hi) { + let e: usize = tok_end(src, q); + if (src[(q) as usize] == B_AMP) { + let mut n: usize = skip_ws(src, e); + if tok_is(src, n, b"mut") { + n = skip_ws(src, tok_end(src, n)); + } + if same_text(src, n, tok_end(src, n), na, nb) { + return true; + } + } + q = skip_ws(src, e); + } + return false; +} + +pub fn array_written(src: &[u8], lo: usize, hi: usize, na: usize, nb: usize) -> bool { + let mut q: usize = lo; + while (q < hi) { + let e: usize = tok_end(src, q); + if (same_text(src, q, e, na, nb) && tok_is(src, skip_ws(src, e), b"[")) { + let c: usize = skip_ws(src, block_end(src, skip_ws(src, e))); + if assign_op(src, c) { + return true; + } + } + q = skip_ws(src, e); + } + return false; +} + +pub fn let_array(src: &[u8], st: &mut [usize], out: &mut [u8], mutable: bool, na: usize, nb: usize, init: usize, written: bool) -> bool { + let close: usize = block_end(src, init); + let rb: usize = (close - 1); + let e0: usize = skip_ws(src, (init + 1)); + if (e0 >= rb) { + return false; + } + let semi: usize = find_at(src, e0, B_SEMI); + let repeat: bool = (semi < rb); + let w0: usize = st[(W) as usize]; + let p0: usize = st[(P) as usize]; + if mutable { + puts(st, out, b"var "); + } else { + puts(st, out, b"const "); + } + put_ident(src, st, out, na, nb); + puts(st, out, b" : ["); + if repeat { + st[(P) as usize] = skip_ws(src, (semi + 1)); + expr(src, st, out, false); + } else { + put_num(st, out, list_count(src, e0, rb)); + } + puts(st, out, b"]"); + let known: bool = put_vec_elem(src, st, out, false, e0, close, item_end(src, st[(FLO) as usize]), na, nb); + if bad(st[(ERR) as usize]) { + return true; + } + if !(known) { + st[(W) as usize] = w0; + st[(P) as usize] = p0; + if written { + refuse(st, R_VECTYPE, init, init, tok_end(src, init)); + return true; + } + return false; + } + puts(st, out, b" = "); + st[(P) as usize] = init; + array_lit(src, st, out); + want(src, st, b";"); + puts(st, out, b";"); + return true; +} + +pub fn bare_float(src: &[u8], st: &mut [usize], p: usize) -> bool { + let mut q: usize = p; + if tok_is(src, q, b"-") { + q = skip_ws(src, tok_end(src, q)); + } + if ((q >= src.len()) || !(is_digit(src[(q) as usize]))) { + return false; + } + let e: usize = tok_end(src, q); + if !(tok_is(src, skip_ws(src, e), b";")) { + return false; + } + return ((suffix_len(src, q, e) == 0) && (literal_class(src, st, q) == C_FLOAT)); +} + +pub fn has_word(src: &[u8], w: &[u8]) -> bool { + let mut q: usize = skip_ws(src, 0); + while (q < src.len()) { + let e: usize = tok_end(src, q); + if word_is(src, q, e, w) { + return true; + } + q = skip_ws(src, e); + } + return false; +} + pub fn let_value(src: &[u8], st: &mut [usize], out: &mut [u8], mutable: bool, na: usize, nb: usize, typed: bool, init: usize) -> () { let vform: bool = ((init < src.len()) && value_form(src, init)); if ((mutable || vform) || (init >= src.len())) { @@ -5459,6 +6031,13 @@ pub fn let_value(src: &[u8], st: &mut [usize], out: &mut [u8], mutable: bool, na puts(st, out, b"const "); } put_ident(src, st, out, na, nb); + if (((!(typed) && mutable) && (init < src.len())) && bare_float(src, st, init)) { + if has_word(src, b"f32") { + refuse(st, R_FLOATLIT, init, init, tok_end(src, init)); + return (); + } + puts(st, out, b" : f64"); + } if typed { step(src, st); puts(st, out, b" : "); @@ -5765,6 +6344,13 @@ pub fn vec_first_type(src: &[u8], st: &mut [usize], out: &mut [u8], e0: usize) - put_owner(src, st, out, t0, te); return true; } + if (is_upper(c) && upper_const(src, t0, te)) { + let ct: usize = const_type(src, st, t0, te); + if (ct < src.len()) { + put_elem_ty(src, st, out, ct); + return !(bad(st[(ERR) as usize])); + } + } if ((is_alpha(c) && !(is_upper(c))) && peek_is(src, t0, b"(")) { let r: usize = ret_word(src, t0, te); if (r < src.len()) { @@ -6489,7 +7075,7 @@ pub fn pattern(src: &[u8], st: &mut [usize], out: &mut [u8], sa: usize, tmp: usi put_scrut(src, st, out, sa, tmp, wrapit); puts(st, out, b" == "); put_owner(src, st, out, a, b); - if (upper_const(src, ma, mb) && !(enum_has(src, st[(ILO) as usize], st[(IHI) as usize], a, b, ma, mb))) { + if (upper_const(src, ma, mb) && !(enum_has(src, st, a, b, ma, mb))) { puts(st, out, b"_"); } else { puts(st, out, b"."); @@ -6964,6 +7550,7 @@ pub fn macro_stmt(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usi let w0: usize = st[(W) as usize]; let la: usize = st[(P) as usize]; let l: usize = expr(src, st, out, false); + let lend: usize = st[(P) as usize]; let lstr: bool = ((out_at(st[(W) as usize], out, w0) == B_QUOTE) || (recv_class(src, st, la, st[(P) as usize]) == C_STR)); want(src, st, b","); if (struct_value(src, st, la) || struct_value(src, st, st[(P) as usize])) { @@ -6990,10 +7577,12 @@ pub fn macro_stmt(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usi puts(st, out, b" != "); } let r0: usize = st[(W) as usize]; + let rsp: usize = st[(P) as usize]; let r: usize = expr(src, st, out, false); if !(keep_bare(r, L_CMP, false)) { wrap(st, out, r0); } + operands_ok(src, st, la, lend, rsp, st[(P) as usize], a, b, true, false); } } if take(src, st, b",") { @@ -7159,7 +7748,7 @@ pub fn put_reason(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { puts(st, out, b"a vec! the fn hands on or returns"); } else { if (c == R_VECTYPE) { - puts(st, out, b"a vec! whose element type or length t27o cannot tell"); + puts(st, out, b"a vec! or array literal whose element type or length t27o cannot tell"); } else { if (c == R_LIBM) { puts(st, out, b"libm function not bit-exact"); @@ -7170,13 +7759,25 @@ pub fn put_reason(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { if (c == R_SHIFTCALL) { puts(st, out, b"a compound shift of a place that calls something"); } else { - if (c == R_NOPRELUDE) { - puts(st, out, b"an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one)"); + if (c == R_OPERAND) { + puts(st, out, b"an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text"); } else { - if (c == R_SPLICE) { - puts(st, out, b"a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records)"); + if (c == R_TYPENAME) { + puts(st, out, b"a type named like one t27c renames (GF16, str, int ..)"); } else { - puts(st, out, b"syntax"); + if (c == R_FLOATLIT) { + puts(st, out, b"a float literal in a var of no type, f32 or f64 left to inference"); + } else { + if (c == R_NOPRELUDE) { + puts(st, out, b"an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one)"); + } else { + if (c == R_SPLICE) { + puts(st, out, b"a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records)"); + } else { + puts(st, out, b"syntax"); + } + } + } } } } @@ -7752,6 +8353,34 @@ pub fn find_enum(src: &[u8], a: usize, b: usize) -> usize { return src.len(); } +pub fn enum_hit(src: &[u8], q: usize, a: usize, b: usize) -> bool { + let e: usize = tok_end(src, q); + if !(word_is(src, q, e, b"enum")) { + return false; + } + let n: usize = skip_ws(src, e); + let ne: usize = tok_end(src, n); + return (same_text(src, n, ne, a, b) && tok_is(src, skip_ws(src, ne), b"{")); +} + +pub fn enum_open(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { + if (st[(IXON) as usize] == 1) { + if (b <= a) { + return src.len(); + } + let mut s: usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + let q: usize = st[((s + 2)) as usize]; + if enum_hit(src, q, a, b) { + return skip_ws(src, tok_end(src, skip_ws(src, tok_end(src, q)))); + } + s = ix_next(src, st, s, a, b); + } + return src.len(); + } + return find_enum(src, a, b); +} + pub fn enum_variant(src: &[u8], lb: usize, k: usize, count: bool) -> usize { let mut n: usize = 0; let mut q: usize = skip_ws(src, (lb + 1)); @@ -7779,17 +8408,17 @@ pub fn enum_variant(src: &[u8], lb: usize, k: usize, count: bool) -> usize { return enum_variant(src, lb, (k % n), false); } -pub fn enum_has(src: &[u8], ilo: usize, ihi: usize, oa: usize, ob: usize, ma: usize, mb: usize) -> bool { +pub fn enum_has(src: &[u8], st: &mut [usize], oa: usize, ob: usize, ma: usize, mb: usize) -> bool { let mut a: usize = oa; let mut b: usize = ob; if word_is(src, oa, ob, b"Self") { - a = ilo; - b = ihi; + a = st[(ILO) as usize]; + b = st[(IHI) as usize]; } if (b <= a) { return false; } - let lb: usize = find_enum(src, a, b); + let lb: usize = enum_open(src, st, a, b); if (lb >= src.len()) { return false; } @@ -10207,6 +10836,69 @@ pub fn fn_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is_te st[(P) as usize] = end; } +pub fn slice_const(src: &[u8], st: &mut [usize], out: &mut [u8], na: usize, nb: usize) -> bool { + let mut t: usize = st[(P) as usize]; + if !(tok_is(src, t, b"&")) { + return false; + } + t = skip_ws(src, tok_end(src, t)); + if ((t < src.len()) && (src[(t) as usize] == B_APOS)) { + t = skip_ws(src, tok_end(src, t)); + } + if !(tok_is(src, t, b"[")) { + return false; + } + let el: usize = skip_ws(src, (t + 1)); + let close: usize = block_end(src, t); + if (find_at(src, el, B_SEMI) < close) { + return false; + } + let eq: usize = find_at(src, close, B_EQ); + if (eq >= src.len()) { + return false; + } + let mut v: usize = skip_ws(src, (eq + 1)); + if tok_is(src, v, b"&") { + v = skip_ws(src, tok_end(src, v)); + } + if ((v >= src.len()) || (src[(v) as usize] != B_LBRACKET)) { + return false; + } + let vc: usize = block_end(src, v); + let e0: usize = skip_ws(src, (v + 1)); + if ((e0 >= (vc - 1)) || (find_at(src, e0, B_SEMI) < (vc - 1))) { + return false; + } + if bare_arg_use(src, na, nb) { + refuse(st, R_TYPE, t, t, tok_end(src, t)); + return true; + } + puts(st, out, b"["); + put_num(st, out, list_count(src, e0, (vc - 1))); + puts(st, out, b"]"); + st[(P) as usize] = el; + if nested_elem(src, st, false) { + return true; + } + ty(src, st, out); + st[(P) as usize] = skip_ws(src, close); + return true; +} + +pub fn bare_arg_use(src: &[u8], na: usize, nb: usize) -> bool { + let mut prev: usize = src.len(); + let mut q: usize = skip_ws(src, 0); + while (q < src.len()) { + let e: usize = tok_end(src, q); + if (((same_text(src, q, e, na, nb) && (q != na)) && (tok_is(src, prev, b"(") || tok_is(src, prev, b","))) && (tok_is(src, skip_ws(src, e), b")") || tok_is(src, skip_ws(src, e), b","))) { + return true; + } + prev = q; + q = skip_ws(src, e); + } + return false; +} + pub fn const_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is_pub: bool) -> () { let kpos: usize = st[(P) as usize]; let w0: usize = st[(W) as usize]; @@ -10232,7 +10924,9 @@ pub fn const_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is put_ident(src, st, out, na, nb); want(src, st, b":"); puts(st, out, b" : "); - ty(src, st, out); + if !(slice_const(src, st, out, na, nb)) { + ty(src, st, out); + } want(src, st, b"="); puts(st, out, b" = "); expr(src, st, out, false); @@ -10262,6 +10956,10 @@ pub fn skip_attrs(src: &[u8], st: &mut [usize]) -> () { } } +pub fn builtin_type_name(src: &[u8], a: usize, b: usize) -> bool { + return (((((((word_is(src, a, b, b"GF16") || word_is(src, a, b, b"gf16")) || word_is(src, a, b, b"str")) || word_is(src, a, b, b"string")) || word_is(src, a, b, b"float")) || word_is(src, a, b, b"double")) || word_is(src, a, b, b"int")) || word_is(src, a, b, b"uint")); +} + pub fn struct_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is_pub: bool) -> () { let kpos: usize = st[(P) as usize]; let w0: usize = st[(W) as usize]; @@ -10272,6 +10970,9 @@ pub fn struct_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, i let na: usize = st[(P) as usize]; let nb: usize = tok_end(src, na); step(src, st); + if builtin_type_name(src, na, nb) { + refuse(st, R_TYPENAME, na, na, nb); + } if is_pub { puts(st, out, b"pub const "); } else { @@ -10468,6 +11169,9 @@ pub fn enum_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is_ let na: usize = st[(P) as usize]; let nb: usize = tok_end(src, na); step(src, st); + if builtin_type_name(src, na, nb) { + refuse(st, R_TYPENAME, na, na, nb); + } if at(src, st[(P) as usize], b"<") { refuse_here(src, st, R_GENERIC); } diff --git a/specs/tools/t27o/rust_convert.t27 b/specs/tools/t27o/rust_convert.t27 index 506c6f49f6..cf01e00c74 100644 --- a/specs/tools/t27o/rust_convert.t27 +++ b/specs/tools/t27o/rust_convert.t27 @@ -338,6 +338,15 @@ pub const R_SLICEGEN : usize = 38; /// A compound shift (`a >>= n`, written `a = a >> n` as t27 has no `>>=`) of a place that calls something: the /// place would be called twice. pub const R_SHIFTCALL : usize = 39; +/// An operator, cast, borrow or deref that t27c has no form for on its operand: a comparison or arithmetic on a +/// struct or an array value, an ordering or arithmetic on an enum value, an ordering of texts, an enum cast to +/// a number, a `*` of an array reference, a `&` of an array field. +pub const R_OPERAND : usize = 41; +/// A struct or enum named like a type t27c's Zig backend renames (GF16 is u16 there, str is []const u8). +pub const R_TYPENAME : usize = 42; +/// A float literal bound to a `var` with no type: t27c writes a `var` of comptime_float, which Zig rejects, and +/// the source leaves f32 or f64 to inference. +pub const R_FLOATLIT : usize = 43; /// An assert in a fn, or a comparison of two texts, in a spec with no test block: the Zig t27c writes has no std /// import and no assert helper then (`use of undeclared identifier 'std'`, '__t27_assert_fail'). pub const R_NOPRELUDE : usize = 40; @@ -1630,7 +1639,11 @@ pub fn param_slice(src: []const u8, st: []usize, out: []u8, a: usize, b: usize) step(src, st); step(src, st); puts(st, out, "[]const "); + const ve : usize = st[P]; + const wv : usize = st[W]; ty(src, st, out); + // `Vec>` is `[]const ?T`, which t27c cannot read + if (!bad(st[ERR]) and out_at(st[W], out, wv) == B_QMARK) { refuse(st, R_TYPE, ve, ve, tok_end(src, ve)); } close_angle(src, st); return true; } @@ -1680,7 +1693,11 @@ pub fn ty(src: []const u8, st: []usize, out: []u8) -> void { const elem_slice : bool = at(src, inner, "[") or tok_is(src, inner, "str"); step(src, st); if (m or elem_slice) { puts(st, out, "[]"); } else { puts(st, out, "[]const "); } + const es : usize = st[P]; + const we : usize = st[W]; ty(src, st, out); + // t27c reads no `[]const ?T` (an optional element); it would print a broken parameter list (#10905) + if (!bad(st[ERR]) and out_at(st[W], out, we) == B_QMARK) { refuse(st, R_TYPE, es, es, tok_end(src, es)); } if (take(src, st, ";")) { const close : usize = find_at(src, st[P], B_RBRACKET); if (close < src.len) { st[P] = close; } @@ -1717,7 +1734,10 @@ pub fn ty(src: []const u8, st: []usize, out: []u8) -> void { const after : usize = skip_ws(src, tok_end(src, st[P])); st[P] = elem; if (nested_elem(src, st, false)) { return; } + const we2 : usize = st[W]; ty(src, st, out); + // nor an array of optionals, `[3]?u8` + if (!bad(st[ERR]) and out_at(st[W], out, we2) == B_QMARK) { refuse(st, R_TYPE, elem, elem, tok_end(src, elem)); } st[P] = after; return; } @@ -1808,6 +1828,252 @@ pub fn str_tok(src: []const u8, q: usize) -> bool { return src[q] == B_LOWER_B and q + 1 < src.len and src[q + 1] == B_QUOTE; } +/// What an operand is, where t27c has no operator or cast for it. +pub const OK_NONE : usize = 0; +pub const OK_STRUCT : usize = 1; +pub const OK_ENUM : usize = 2; +pub const OK_ARRAY : usize = 3; +/// A text: a string literal, or a name or field declared a `&str` / `String` (what a comparison orders by bytes, which +/// t27c has no operator for). +pub const OK_TEXT : usize = 4; +/// A slice of bytes: a name or field declared `&[u8]`, or a range of one or of a text (`x[a..b]`). +pub const OK_BYTES : usize = 5; + +/// Where the struct literal or the call that begins at src[q] ends (past its `}` or `)`), src.len when it is +/// neither (the same walk as struct_value: `Name { .. }`, `Name(..)`, `a::f(..)`, `a.f(..)`). +pub fn value_end(src: []const u8, q: usize) -> usize { + var n : usize = q; + var ne : usize = tok_end(src, n); + var nx : usize = skip_ws(src, ne); + if (is_upper(src[n]) and (tok_is(src, nx, "{") or tok_is(src, nx, "("))) { return skip_ws(src, block_end(src, nx)); } + if (tok_is(src, nx, "::") or tok_is(src, nx, ".")) { + n = skip_ws(src, tok_end(src, nx)); + ne = tok_end(src, n); + nx = skip_ws(src, ne); + } + if (tok_is(src, nx, "(")) { return skip_ws(src, block_end(src, nx)); } + return src.len; +} + +/// The kind of a value of the type written at src[d] (a `&`, `mut` or lifetime before it is skipped): an enum or a +/// struct of this file, an array held by value (`[T; N]`, not behind a `&`); OK_NONE for the rest. +pub fn type_kind(src: []const u8, st: []usize, d: usize) -> usize { + var q : usize = d; + var by_ref : bool = false; + while (q < src.len and (src[q] == B_AMP or tok_is(src, q, "mut") or src[q] == B_APOS)) { + if (src[q] == B_AMP) { by_ref = true; } + q = skip_ws(src, tok_end(src, q)); + } + if (q >= src.len) { return OK_NONE; } + if (src[q] == B_LBRACKET) { + if (!by_ref and find_at(src, skip_ws(src, q + 1), B_SEMI) < block_end(src, q)) { return OK_ARRAY; } + return OK_NONE; + } + if (!is_upper(src[q])) { return OK_NONE; } + var tb : usize = q; + var te : usize = tok_end(src, q); + if (word_is(src, q, te, "Self")) { + tb = st[ILO]; + te = st[IHI]; + } + if (te <= tb) { return OK_NONE; } + if (enum_open(src, st, tb, te) < src.len) { return OK_ENUM; } + if (find_struct(src, st, tb, te) < src.len or named(src, st, K_TUPLE, tb, te) or unit_struct(src, st, tb, te)) { return OK_STRUCT; } + return OK_NONE; +} + +/// The position after the `=` of the last `let [mut] ` before src[q] in the fn that starts at src[flo], src.len +/// when there is none. +pub fn let_init_of(src: []const u8, flo: usize, q: usize, na: usize, nb: usize) -> usize { + var best : usize = src.len; + var p : usize = flo; + while (p < q) { + const e : usize = tok_end(src, p); + if (word_is(src, p, e, "let")) { + var n : usize = skip_ws(src, e); + if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } + if (same_text(src, n, tok_end(src, n), na, nb)) { + const i : usize = init_at(src, p); + if (i < src.len) { best = i; } + } + } + p = skip_ws(src, e); + } + return best; +} + +/// The kind of a `for in ` binding src[na .. nb) read before src[q]: what an element of `iter` is -- the +/// variants or structs a literal or a `let` of one lists, the element type of an array or slice it is declared with. +pub fn for_var_kind(src: []const u8, st: []usize, flo: usize, q: usize, na: usize, nb: usize) -> usize { + var it : usize = src.len; + var p : usize = flo; + while (p < q) { + const e : usize = tok_end(src, p); + if (word_is(src, p, e, "for")) { + const n : usize = skip_ws(src, e); + const ne : usize = tok_end(src, n); + const k : usize = skip_ws(src, ne); + if (same_text(src, n, ne, na, nb) and tok_is(src, k, "in")) { + it = skip_ws(src, tok_end(src, k)); + if (tok_is(src, it, "&")) { it = skip_ws(src, tok_end(src, it)); } + } + } + p = skip_ws(src, e); + } + if (it >= src.len) { return OK_NONE; } + var lit : usize = src.len; + if (src[it] == B_LBRACKET) { + lit = it; + } else if (is_alpha(src[it])) { + const ie : usize = tok_end(src, it); + const li : usize = let_init_of(src, flo, it, it, ie); + if (li < src.len and src[li] == B_LBRACKET) { lit = li; } + if (lit >= src.len) { + // a parameter or typed let: the element type of `[E; N]`, `&[E]` + var d : usize = decl_type(src, flo, it, it, ie); + while (d < src.len and (src[d] == B_AMP or tok_is(src, d, "mut") or src[d] == B_APOS)) { d = skip_ws(src, tok_end(src, d)); } + if (d < src.len and src[d] == B_LBRACKET) { return type_kind(src, st, skip_ws(src, d + 1)); } + if (d < src.len and tok_is(src, d, "Vec")) { return type_kind(src, st, skip_ws(src, skip_ws(src, tok_end(src, d)) + 1)); } + return OK_NONE; + } + } + if (lit >= src.len) { return OK_NONE; } + // the first element of the literal: `Enum::Variant` or `Struct { .. }` + const e0 : usize = skip_ws(src, lit + 1); + if (e0 >= src.len or !is_upper(src[e0])) { return OK_NONE; } + return type_kind(src, st, e0); +} + +/// Is the name src[na .. nb) bound by `let name = .as_bytes();` before src[q] in the fn that starts at src[flo]? +pub fn let_as_bytes(src: []const u8, flo: usize, q: usize, na: usize, nb: usize) -> bool { + const li : usize = let_init_of(src, flo, q, na, nb); + if (li >= src.len) { return false; } + const semi : usize = find_at(src, li, B_SEMI); + var p : usize = li; + while (p < semi) { + const e : usize = tok_end(src, p); + if (word_is(src, p, e, "as_bytes")) { + const lp : usize = skip_ws(src, e); + const rp : usize = skip_ws(src, lp + 1); + if (tok_is(src, lp, "(") and tok_is(src, rp, ")") and skip_ws(src, rp + 1) >= semi) { return true; } + } + p = skip_ws(src, e); + } + return false; +} + +/// The kind of the operand src[lo .. hi): a struct of this file (a literal, a call of a fn returning one, a name or +/// `x.f` declared with one, `self` in its impl), an enum value (`Enum::Variant`, `self` in an enum's impl, a name, +/// field or `for` binding of an enum), an array held by value (a literal, a name, field or const declared +/// `[T; N]`), a text (a literal, a name or field declared a `&str` / `String`); OK_NONE for the rest -- and for any +/// operand that is more than one such atom, which is never guessed. +pub fn operand_kind(src: []const u8, st: []usize, lo: usize, hi: usize) -> usize { + var q : usize = lo; + while (q < hi and (src[q] == B_AMP or src[q] == B_STAR or tok_is(src, q, "mut"))) { q = skip_ws(src, tok_end(src, q)); } + if (q >= hi) { return OK_NONE; } + if (src[q] == B_LPAREN and skip_ws(src, block_end(src, q)) >= hi) { return operand_kind(src, st, skip_ws(src, q + 1), block_end(src, q) - 1); } + if (src[q] == B_LBRACKET) { + if (skip_ws(src, block_end(src, q)) >= hi) { return OK_ARRAY; } + return OK_NONE; + } + if (str_tok(src, q) or raw_str_at(src, q)) { + if (skip_ws(src, tok_end(src, q)) >= hi) { return OK_TEXT; } + return OK_NONE; + } + if (!is_alpha(src[q])) { return OK_NONE; } + const e : usize = tok_end(src, q); + const nx : usize = skip_ws(src, e); + if (struct_value(src, st, q) and value_end(src, q) >= hi) { return OK_STRUCT; } + if (is_upper(src[q]) and tok_is(src, nx, "::")) { + const v : usize = skip_ws(src, tok_end(src, nx)); + const ve : usize = tok_end(src, v); + if (v < src.len and is_alpha(src[v]) and skip_ws(src, ve) >= hi and enum_has(src, st, q, e, v, ve)) { return OK_ENUM; } + return OK_NONE; + } + // `x[a..b]`: the bytes of a text or of a byte slice + if (tok_is(src, nx, "[") and skip_ws(src, block_end(src, nx)) >= hi and find_tok_to(src, nx + 1, block_end(src, nx), "..") < src.len) { + const u0 : usize = st[TYLO]; + const u1 : usize = st[TYHI]; + const u2 : usize = st[TYARR]; + const bc : u8 = recv_class(src, st, q, e); + var bk : usize = OK_NONE; + if (bc == C_STR) { bk = OK_BYTES; } + if (bc == C_SLICE and st[TYARR] != 1 and word_is(src, st[TYLO], st[TYHI], "u8")) { bk = OK_BYTES; } + if (bk == OK_NONE and let_as_bytes(src, st[FLO], q, q, e)) { bk = OK_BYTES; } + st[TYLO] = u0; + st[TYHI] = u1; + st[TYARR] = u2; + return bk; + } + // a name or `x.f` is the one atom whose declared type says what it is + var f : usize = src.len; + var fe : usize = src.len; + var simple : bool = nx >= hi; + if (!simple and tok_is(src, nx, ".")) { + f = skip_ws(src, nx + 1); + fe = tok_end(src, f); + simple = f < src.len and (is_alpha(src[f]) or is_digit(src[f])) and skip_ws(src, fe) >= hi; + } + if (!simple) { return OK_NONE; } + const t0 : usize = st[TYLO]; + const t1 : usize = st[TYHI]; + const t2 : usize = st[TYARR]; + var k : usize = OK_NONE; + if (nx >= hi) { + // one name: `self` of an impl, or a name with a declared type, a const's, a `let`'s own, a `for` binding's + if (word_is(src, q, e, "self")) { + if (find_struct(src, st, st[ILO], st[IHI]) < src.len or named(src, st, K_TUPLE, st[ILO], st[IHI]) or unit_struct(src, st, st[ILO], st[IHI])) { k = OK_STRUCT; } + if (st[IHI] > st[ILO] and enum_open(src, st, st[ILO], st[IHI]) < src.len) { k = OK_ENUM; } + } else { + var d : usize = decl_type(src, st[FLO], q, q, e); + if (d >= src.len) { d = const_type(src, st, q, e); } + if (d >= src.len) { d = let_owner(src, st, q, e); } + if (d < src.len) { k = type_kind(src, st, d); } + if (d >= src.len) { k = for_var_kind(src, st, st[FLO], q, q, e); } + } + } else { + // `x.f`: the field's type in the struct x is of (`self.0` the first type of a tuple struct) + const lb : usize = head_struct(src, st, st[FLO], st[ILO], st[IHI], q, e); + if (lb < src.len) { + var ft : usize = src.len; + if (is_digit(src[f])) { ft = field_type(src, st, lb, (src[f] - B_ZERO) as usize); } else { ft = decl_type(src, lb, block_end(src, lb), f, fe); } + if (ft < src.len) { k = type_kind(src, st, ft); } + } + } + // an array held by value, or a text: a name, `self.f` or `x.f` declared `[T; N]` / `&str` + const rc : u8 = recv_class(src, st, q, hi); + if (k == OK_NONE and rc == C_SLICE and st[TYARR] == 1) { k = OK_ARRAY; } + if (k == OK_NONE and rc == C_STR) { k = OK_TEXT; } + if (k == OK_NONE and rc == C_SLICE and st[TYARR] != 1 and word_is(src, st[TYLO], st[TYHI], "u8")) { k = OK_BYTES; } + if (k == OK_NONE and nx >= hi and let_as_bytes(src, st[FLO], q, q, e)) { k = OK_BYTES; } + st[TYLO] = t0; + st[TYHI] = t1; + st[TYARR] = t2; + return k; +} + +/// Refuse the operator src[oa .. ob] (an equality when `eq`, an ordering when `ord`, otherwise arithmetic) when +/// t27c has no operator for its operands src[l0 .. l1) and src[r0 .. r1): a struct or an array in any of them, +/// an enum in arithmetic, a text in an ordering (equality of texts is type_text::same). +pub fn operands_ok(src: []const u8, st: []usize, l0: usize, l1: usize, r0: usize, r1: usize, oa: usize, ob: usize, eq: bool, ord: bool) -> void { + if (bad(st[ERR])) { return; } + // arithmetic is only looked at for an operand that names an enum value (`Enum::Variant`) or `self`; the + // comparisons look at every operand + if (!eq and !ord and !tok_is(src, l0, "self") and !tok_is(src, r0, "self") and !(l0 < src.len and is_upper(src[l0]) and peek_is(src, l0, "::")) and !(r0 < src.len and is_upper(src[r0]) and peek_is(src, r0, "::"))) { return; } + const lk : usize = operand_kind(src, st, l0, l1); + const rk : usize = operand_kind(src, st, r0, r1); + var no : bool = lk == OK_STRUCT or lk == OK_ARRAY or rk == OK_STRUCT or rk == OK_ARRAY; + // (t27c orders two enum values -- `Health.Critical < Health.Healthy` compiles and runs -- but has no arithmetic on them) + if (!eq and !ord and (lk == OK_ENUM or rk == OK_ENUM)) { no = true; } + if (ord and (lk == OK_TEXT or rk == OK_TEXT or lk == OK_BYTES or rk == OK_BYTES)) { no = true; } + if (no) { + refuse(st, R_OPERAND, oa, oa, ob); + return; + } + // two texts or byte slices compared with `==` are `std.mem.eql` in the Zig t27c writes, which has std only with a test block + if (eq and (lk == OK_TEXT or lk == OK_BYTES) and (rk == OK_TEXT or rk == OK_BYTES)) { need_prelude(src, st, oa, ob); } +} + pub fn expr(src: []const u8, st: []usize, out: []u8, ns: bool) -> usize { return expr_bp(src, st, out, L_OR, ns); } @@ -1816,6 +2082,7 @@ pub fn expr(src: []const u8, st: []usize, out: []u8, ns: bool) -> usize { /// where Rust reads `Name {` as a block, not a struct literal (conditions, scrutinees, ranges). pub fn expr_bp(src: []const u8, st: []usize, out: []u8, min: usize, ns: bool) -> usize { const start : usize = st[W]; + const sp : usize = st[P]; var lvl : usize = unary(src, st, out, ns); while (!bad(st[ERR])) { const a : usize = st[P]; @@ -1824,6 +2091,10 @@ pub fn expr_bp(src: []const u8, st: []usize, out: []u8, min: usize, ns: bool) -> if (lv == 0 or lv < min) { return lvl; } step(src, st); if (lv == L_AS) { + if (operand_kind(src, st, sp, a) == OK_ENUM) { + refuse(st, R_OPERAND, a, a, b); + return lvl; + } if (lvl != L_ATOM) { wrap(st, out, start); } // t27c's `as` takes bool, u8 .. u64, i8 .. i64, usize, f32 and f64 (not i128, u128, isize) const ca : usize = st[P]; @@ -1854,8 +2125,10 @@ pub fn expr_bp(src: []const u8, st: []usize, out: []u8, min: usize, ns: bool) -> } puts(st, out, " "); const rs : usize = st[W]; + const rsp : usize = st[P]; const rl : usize = expr_bp(src, st, out, lv + 1, ns); if (!keep_bare(rl, lv, false)) { wrap(st, out, rs); } + operands_ok(src, st, sp, a, rsp, st[P], a, b, word_is(src, a, b, "==") or word_is(src, a, b, "!="), lv == L_CMP and !(word_is(src, a, b, "==") or word_is(src, a, b, "!="))); lvl = lv; } } @@ -1882,11 +2155,40 @@ pub fn array_local(src: []const u8, flo: usize, a: usize, b: usize) -> bool { return false; } +/// The type token of the parameter src[a .. b] of the fn whose `fn` is at src[flo], src.len when the list has no such +/// parameter. (decl_type keeps the last `name:` before a use, which a struct literal's `tag: *tag` is.) +pub fn param_type(src: []const u8, flo: usize, a: usize, b: usize) -> usize { + const lp : usize = find_at(src, flo, B_LPAREN); + if (lp >= src.len) { return src.len; } + const rp : usize = block_end(src, lp); + var q : usize = skip_ws(src, lp + 1); + while (q < rp) { + const e : usize = tok_end(src, q); + const c : usize = skip_ws(src, e); + if (same_text(src, q, e, a, b) and tok_is(src, c, ":")) { return skip_ws(src, tok_end(src, c)); } + q = skip_ws(src, e); + } + return src.len; +} + +/// Is the name src[a .. b] a parameter of the fn that starts at src[flo] with the type `&[T; N]`? +pub fn array_ref_param(src: []const u8, flo: usize, a: usize, b: usize) -> bool { + var d : usize = param_type(src, flo, a, b); + if (d >= src.len or !tok_is(src, d, "&")) { return false; } + d = skip_ws(src, tok_end(src, d)); + if (d < src.len and src[d] == B_APOS) { d = skip_ws(src, tok_end(src, d)); } + if (tok_is(src, d, "mut")) { d = skip_ws(src, tok_end(src, d)); } + if (!tok_is(src, d, "[")) { return false; } + const el : usize = skip_ws(src, d + 1); + return find_at(src, el, B_SEMI) < block_end(src, d); +} + pub fn unary(src: []const u8, st: []usize, out: []u8, ns: bool) -> usize { if (bad(st[ERR])) { return L_ATOM; } // t27c's Zig backend drops the parentheses of -(x) and !(x) (#6549), so a parenthesized operand // is written as (0 - (x)) and ((x) == false). if (take(src, st, "-")) { + const na : usize = st[P]; if (at(src, st[P], "(")) { puts(st, out, "(0 - "); unary(src, st, out, ns); @@ -1895,6 +2197,8 @@ pub fn unary(src: []const u8, st: []usize, out: []u8, ns: bool) -> usize { } puts(st, out, "-"); unary(src, st, out, ns); + // `-Trit::Pos` is a user `impl Neg`: t27c has no negation of an enum value + if (!bad(st[ERR]) and operand_kind(src, st, na, st[P]) == OK_ENUM) { refuse(st, R_OPERAND, na, na, tok_end(src, na)); } return L_UNARY; } // Rust's `!` is a boolean NOT on a bool and a bitwise NOT on an integer; t27's `!` is the first and @@ -1942,6 +2246,13 @@ pub fn unary(src: []const u8, st: []usize, out: []u8, ns: bool) -> usize { puts(st, out, "self.*"); return L_ATOM; } + // `*r` of a `&[T; N]` parameter is the array by value; the parameter is written a slice, which no field or + // `let` of the array type takes (#10905: `tag: *tag`) + const rp : usize = st[P]; + if (rp < src.len and is_alpha(src[rp]) and array_ref_param(src, st[FLO], rp, tok_end(src, rp))) { + refuse(st, R_OPERAND, rp, rp, tok_end(src, rp)); + return L_ATOM; + } return unary(src, st, out, ns); } if (take(src, st, "&") or take(src, st, "&&")) { @@ -1953,6 +2264,11 @@ pub fn unary(src: []const u8, st: []usize, out: []u8, ns: bool) -> usize { const one : bool = oa < src.len and is_alpha(src[oa]) and st[P] == skip_ws(src, ob); const arr : bool = one and array_local(src, st[FLO], oa, ob); if (arr) { insert(st, out, s, "&"); } + // `&self.f` of an array field would hand out a slice of the fn's own copy of self: no form for it + if (!arr and !bad(st[ERR]) and operand_kind(src, st, oa, st[P]) == OK_ARRAY and find_at(src, oa, B_DOT) < st[P]) { + refuse(st, R_OPERAND, oa, oa, ob); + return l; + } // `&mut x` of anything but an array or a slice range would write through a copy if (m and !arr and out_at(st[W], out, st[W] - 1) != B_RBRACKET) { refuse(st, R_MUTREF, oa, oa, ob); } return l; @@ -2728,6 +3044,23 @@ pub fn recv_class(src: []const u8, st: []usize, ra: usize, rb: usize) -> u8 { if (r < src.len) { return type_class(src, st, r); } return C_ANY; } + // `x.m(..)` of a method this file's impl declares and that returns Result<(), E> is a bool as well: its + // `.is_ok()` reads the bool, not `!= null` (#10905: `valid.validate().is_ok()`) + if (is_alpha(src[ra]) and tok_is(src, nx, ".")) { + const m : usize = skip_ws(src, nx + 1); + const me : usize = tok_end(src, m); + const lp : usize = skip_ws(src, me); + if (m < src.len and is_alpha(src[m]) and tok_is(src, lp, "(") and skip_ws(src, block_end(src, lp)) >= rb and unit_result_fn(src, st, m, me)) { + const olo : usize = st[OLO]; + const ohi : usize = st[OHI]; + const okind : usize = st[OKIND]; + const own : bool = method_owner(src, st, m, me, 0, 0); + st[OLO] = olo; + st[OHI] = ohi; + st[OKIND] = okind; + if (own) { return C_BOOL; } + } + } var cls : u8 = C_ANY; var ilo : usize = 0; var ihi : usize = 0; @@ -3569,6 +3902,11 @@ pub fn method(src: []const u8, st: []usize, out: []u8, start: usize, ra: usize, } if (word_is(src, na, nb, "contains") and unroll_contains(src, st, out, start, ra, rb)) { return; } const cls : u8 = recv_class(src, st, ra, rb); + // a Result<(), E> is a bool here: nothing is left to unwrap, and `.?` of a bool is not t27 (#10905) + if (cls == C_BOOL and (word_is(src, na, nb, "unwrap") or word_is(src, na, nb, "expect"))) { + refuse(st, R_METHOD, na, na, nb); + return; + } const usz : bool = st[TYARR] == 2; var tyl : usize = st[TYLO]; var tyh : usize = st[TYHI]; @@ -4169,7 +4507,7 @@ pub fn path(src: []const u8, st: []usize, out: []u8, a0: usize, b0: usize) -> vo return; } put_owner(src, st, out, a, b); - if (upper_const(src, ma, mb) and !enum_has(src, st[ILO], st[IHI], a, b, ma, mb)) { puts(st, out, "_"); } else { puts(st, out, "."); } + if (upper_const(src, ma, mb) and !enum_has(src, st, a, b, ma, mb)) { puts(st, out, "_"); } else { puts(st, out, "."); } put_src(src, st, out, ma, mb); } @@ -4809,15 +5147,162 @@ pub fn let_stmt(src: []const u8, st: []usize, out: []u8) -> void { st[LETTY] = 0; return; } + // `let cmds = [Cmd::Nop, Cmd::Reset]; for c in cmds { .. }`, `let mut scores = [0.0f64; N]; scores[0] = 1.0;`: + // t27c reads the untyped literal as a tuple, a `for` over it does not resolve at run time and its fields are + // comptime, so an element cannot be written (#10905); the local is a typed array + if (init < src.len and src[init] == B_LBRACKET and !typed) { + const hi : usize = item_end(src, st[FLO]); + const written : bool = array_written(src, block_end(src, init), hi, na, nb); + if (written or array_iterated(src, block_end(src, init), hi, na, nb) or array_borrowed(src, block_end(src, init), hi, na, nb) or array_indexed(src, block_end(src, init), hi, na, nb)) { + if (let_array(src, st, out, mutable, na, nb, init, written)) { + st[LETTY] = 0; + return; + } + } + } let_value(src, st, out, mutable, na, nb, typed, init); st[LETTY] = 0; } +/// Does src[lo .. hi) loop over the name src[na .. nb): `in name` or `in &name`? +pub fn array_iterated(src: []const u8, lo: usize, hi: usize, na: usize, nb: usize) -> bool { + var q : usize = lo; + while (q < hi) { + const e : usize = tok_end(src, q); + if (word_is(src, q, e, "in")) { + var n : usize = skip_ws(src, e); + if (tok_is(src, n, "&")) { n = skip_ws(src, tok_end(src, n)); } + if (same_text(src, n, tok_end(src, n), na, nb)) { return true; } + } + q = skip_ws(src, e); + } + return false; +} + +/// Is the name src[na .. nb) indexed in src[lo .. hi) by anything but a number literal: `name[j]`, `name[i + 1]`? (A tuple +/// takes a literal index at compile time; a run-time one needs the typed array.) +pub fn array_indexed(src: []const u8, lo: usize, hi: usize, na: usize, nb: usize) -> bool { + var q : usize = lo; + while (q < hi) { + const e : usize = tok_end(src, q); + if (same_text(src, q, e, na, nb) and tok_is(src, skip_ws(src, e), "[")) { + const lb : usize = skip_ws(src, e); + const i0 : usize = skip_ws(src, lb + 1); + const ie : usize = tok_end(src, i0); + if (!(i0 < src.len and is_digit(src[i0]) and tok_is(src, skip_ws(src, ie), "]"))) { return true; } + } + q = skip_ws(src, e); + } + return false; +} + +/// Is the name src[na .. nb) borrowed in src[lo .. hi): `&name`, `&mut name`? +pub fn array_borrowed(src: []const u8, lo: usize, hi: usize, na: usize, nb: usize) -> bool { + var q : usize = lo; + while (q < hi) { + const e : usize = tok_end(src, q); + if (src[q] == B_AMP) { + var n : usize = skip_ws(src, e); + if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } + if (same_text(src, n, tok_end(src, n), na, nb)) { return true; } + } + q = skip_ws(src, e); + } + return false; +} + +/// Is an element of the name src[na .. nb) assigned in src[lo .. hi): `name[i] = v`, `name[i] += v`? +pub fn array_written(src: []const u8, lo: usize, hi: usize, na: usize, nb: usize) -> bool { + var q : usize = lo; + while (q < hi) { + const e : usize = tok_end(src, q); + if (same_text(src, q, e, na, nb) and tok_is(src, skip_ws(src, e), "[")) { + const c : usize = skip_ws(src, block_end(src, skip_ws(src, e))); + if (assign_op(src, c)) { return true; } + } + q = skip_ws(src, e); + } + return false; +} + +/// `let [mut] v = [a, b, c];` / `[x; n]` as the typed array `const v : [3]T = [a, b, c];` / `var v : [n]T = [x] ** n;`, +/// T from the first element or from the slice parameter the array is handed to (put_vec_elem). When T is unknown +/// the let goes the way it always did -- unless an element is written (`written`): the untyped tuple would be +/// written into, which Zig refuses, so it is refused as a vec! of unknown type is. st[P] is after the name; init +/// is the `[`. +pub fn let_array(src: []const u8, st: []usize, out: []u8, mutable: bool, na: usize, nb: usize, init: usize, written: bool) -> bool { + const close : usize = block_end(src, init); + const rb : usize = close - 1; + const e0 : usize = skip_ws(src, init + 1); + if (e0 >= rb) { return false; } + const semi : usize = find_at(src, e0, B_SEMI); + const repeat : bool = semi < rb; + const w0 : usize = st[W]; + const p0 : usize = st[P]; + if (mutable) { puts(st, out, "var "); } else { puts(st, out, "const "); } + put_ident(src, st, out, na, nb); + puts(st, out, " : ["); + if (repeat) { + st[P] = skip_ws(src, semi + 1); + expr(src, st, out, false); + } else { + put_num(st, out, list_count(src, e0, rb)); + } + puts(st, out, "]"); + const known : bool = put_vec_elem(src, st, out, false, e0, close, item_end(src, st[FLO]), na, nb); + if (bad(st[ERR])) { return true; } + if (!known) { + st[W] = w0; + st[P] = p0; + if (written) { + refuse(st, R_VECTYPE, init, init, tok_end(src, init)); + return true; + } + return false; + } + puts(st, out, " = "); + st[P] = init; + array_lit(src, st, out); + want(src, st, ";"); + puts(st, out, ";"); + return true; +} + +/// Is src[p .. ) a lone float literal without a suffix, optionally negated, that a `;` follows? +pub fn bare_float(src: []const u8, st: []usize, p: usize) -> bool { + var q : usize = p; + if (tok_is(src, q, "-")) { q = skip_ws(src, tok_end(src, q)); } + if (q >= src.len or !is_digit(src[q])) { return false; } + const e : usize = tok_end(src, q); + if (!tok_is(src, skip_ws(src, e), ";")) { return false; } + return suffix_len(src, q, e) == 0 and literal_class(src, st, q) == C_FLOAT; +} + +/// Does the source name the word `w` anywhere (as a token)? +pub fn has_word(src: []const u8, w: []const u8) -> bool { + var q : usize = skip_ws(src, 0); + while (q < src.len) { + const e : usize = tok_end(src, q); + if (word_is(src, q, e, w)) { return true; } + q = skip_ws(src, e); + } + return false; +} + /// The rest of a `let` (no `?` at its end): its declaration and value. st[P] is after the name. pub fn let_value(src: []const u8, st: []usize, out: []u8, mutable: bool, na: usize, nb: usize, typed: bool, init: usize) -> void { const vform : bool = init < src.len and value_form(src, init); if (mutable or vform or init >= src.len) { puts(st, out, "var "); } else { puts(st, out, "const "); } put_ident(src, st, out, na, nb); + // `let mut x = 1.0;` is Rust's f64 unless something makes it f32; t27c writes a `var` of comptime_float, which + // Zig rejects. A file with no f32 in it leaves nothing to make it one: the type is written. Otherwise refused. + if (!typed and mutable and init < src.len and bare_float(src, st, init)) { + if (has_word(src, "f32")) { + refuse(st, R_FLOATLIT, init, init, tok_end(src, init)); + return; + } + puts(st, out, " : f64"); + } if (typed) { step(src, st); puts(st, out, " : "); @@ -5091,6 +5576,14 @@ pub fn vec_first_type(src: []const u8, st: []usize, out: []u8, e0: usize) -> boo put_owner(src, st, out, t0, te); return true; } + // a const of the file: its declared type (`[PVT_TEMP_MIN_C, -20i64, 0]`) + if (is_upper(c) and upper_const(src, t0, te)) { + const ct : usize = const_type(src, st, t0, te); + if (ct < src.len) { + put_elem_ty(src, st, out, ct); + return !bad(st[ERR]); + } + } if (is_alpha(c) and !is_upper(c) and peek_is(src, t0, "(")) { const r : usize = ret_word(src, t0, te); if (r < src.len) { @@ -5781,7 +6274,7 @@ pub fn pattern(src: []const u8, st: []usize, out: []u8, sa: usize, tmp: usize, w put_scrut(src, st, out, sa, tmp, wrapit); puts(st, out, " == "); put_owner(src, st, out, a, b); - if (upper_const(src, ma, mb) and !enum_has(src, st[ILO], st[IHI], a, b, ma, mb)) { puts(st, out, "_"); } else { puts(st, out, "."); } + if (upper_const(src, ma, mb) and !enum_has(src, st, a, b, ma, mb)) { puts(st, out, "_"); } else { puts(st, out, "."); } put_src(src, st, out, ma, mb); return; } @@ -6200,6 +6693,7 @@ pub fn macro_stmt(src: []const u8, st: []usize, out: []u8, a: usize, b: usize) - const w0 : usize = st[W]; const la : usize = st[P]; const l : usize = expr(src, st, out, false); + const lend : usize = st[P]; const lstr : bool = out_at(st[W], out, w0) == B_QUOTE or recv_class(src, st, la, st[P]) == C_STR; want(src, st, ","); // Zig has no == on structs @@ -6217,8 +6711,11 @@ pub fn macro_stmt(src: []const u8, st: []usize, out: []u8, a: usize, b: usize) - if (!keep_bare(l, L_CMP, true)) { wrap(st, out, w0); } if (eq) { puts(st, out, " == "); } else { puts(st, out, " != "); } const r0 : usize = st[W]; + const rsp : usize = st[P]; const r : usize = expr(src, st, out, false); if (!keep_bare(r, L_CMP, false)) { wrap(st, out, r0); } + // Zig has no == on arrays either (t27c: "operator == not allowed for type '[3]i64'") + operands_ok(src, st, la, lend, rsp, st[P], a, b, true, false); } } if (take(src, st, ",")) { @@ -6339,13 +6836,19 @@ pub fn put_reason(src: []const u8, st: []usize, out: []u8) -> void { } else if (c == R_VECMOVE) { puts(st, out, "a vec! the fn hands on or returns"); } else if (c == R_VECTYPE) { - puts(st, out, "a vec! whose element type or length t27o cannot tell"); + puts(st, out, "a vec! or array literal whose element type or length t27o cannot tell"); } else if (c == R_LIBM) { puts(st, out, "libm function not bit-exact"); } else if (c == R_SLICEGEN) { puts(st, out, "slice contains on"); } else if (c == R_SHIFTCALL) { puts(st, out, "a compound shift of a place that calls something"); + } else if (c == R_OPERAND) { + puts(st, out, "an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text"); + } else if (c == R_TYPENAME) { + puts(st, out, "a type named like one t27c renames (GF16, str, int ..)"); + } else if (c == R_FLOATLIT) { + puts(st, out, "a float literal in a var of no type, f32 or f64 left to inference"); } else if (c == R_NOPRELUDE) { puts(st, out, "an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one)"); } else if (c == R_SPLICE) { @@ -6804,6 +7307,31 @@ pub fn find_enum(src: []const u8, a: usize, b: usize) -> usize { return src.len; } +/// Is the token at src[q] the `enum` of `enum {`, the name src[a .. b]? +pub fn enum_hit(src: []const u8, q: usize, a: usize, b: usize) -> bool { + const e : usize = tok_end(src, q); + if (!word_is(src, q, e, "enum")) { return false; } + const n : usize = skip_ws(src, e); + const ne : usize = tok_end(src, n); + return same_text(src, n, ne, a, b) and tok_is(src, skip_ws(src, ne), "{"); +} + +/// find_enum through the declaration index (the keyword tokens by name) when it is built: the `{` of +/// `enum {`, src.len when there is none. The operand checks ask once per operator. +pub fn enum_open(src: []const u8, st: []usize, a: usize, b: usize) -> usize { + if (st[IXON] == 1) { + if (b <= a) { return src.len; } + var s : usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + const q : usize = st[s + 2]; + if (enum_hit(src, q, a, b)) { return skip_ws(src, tok_end(src, skip_ws(src, tok_end(src, q)))); } + s = ix_next(src, st, s, a, b); + } + return src.len; + } + return find_enum(src, a, b); +} + /// The name of variant k of the enum whose body opens at src[lb] (k counted modulo the variants), or /// with `count` set, how many variants it has. pub fn enum_variant(src: []const u8, lb: usize, k: usize, count: bool) -> usize { @@ -6821,18 +7349,18 @@ pub fn enum_variant(src: []const u8, lb: usize, k: usize, count: bool) -> usize return enum_variant(src, lb, k % n, false); } -/// Is src[ma .. mb] a variant of the enum src[oa .. ob] declared in this file (`Self` is the impl's type src[ilo .. ihi])? +/// Is src[ma .. mb] a variant of the enum src[oa .. ob] declared in this file (`Self` is the impl's type)? /// A variant written in capitals (`GF16`, `F32`, `X402`) looks like an associated const to upper_const; /// it is `Owner.VARIANT`, not `Owner_VARIANT`, which nothing declares (#10905). -pub fn enum_has(src: []const u8, ilo: usize, ihi: usize, oa: usize, ob: usize, ma: usize, mb: usize) -> bool { +pub fn enum_has(src: []const u8, st: []usize, oa: usize, ob: usize, ma: usize, mb: usize) -> bool { var a : usize = oa; var b : usize = ob; if (word_is(src, oa, ob, "Self")) { - a = ilo; - b = ihi; + a = st[ILO]; + b = st[IHI]; } if (b <= a) { return false; } - const lb : usize = find_enum(src, a, b); + const lb : usize = enum_open(src, st, a, b); if (lb >= src.len) { return false; } const n : usize = enum_variant(src, lb, 0, true); var k : usize = 0; @@ -8782,6 +9310,54 @@ pub fn fn_item(src: []const u8, st: []usize, out: []u8, start: usize, is_test: b } /// `const` and `static` (not `static mut`) as `const`; in an impl block as `Type_NAME`. +/// A const or static of the type `&[E]` set from a list `&[a, b, c]` (the type at st[P], the name src[na .. nb)): +/// t27c has no value to give a slice there ("type '[]const u8' does not support array initialization syntax"), so +/// it is the array `[3]E` the list makes -- unless the file hands the name whole to a call, where the array would +/// meet a slice parameter: that is refused. True once the type is written (or refused). +pub fn slice_const(src: []const u8, st: []usize, out: []u8, na: usize, nb: usize) -> bool { + var t : usize = st[P]; + if (!tok_is(src, t, "&")) { return false; } + t = skip_ws(src, tok_end(src, t)); + if (t < src.len and src[t] == B_APOS) { t = skip_ws(src, tok_end(src, t)); } + if (!tok_is(src, t, "[")) { return false; } + const el : usize = skip_ws(src, t + 1); + const close : usize = block_end(src, t); + if (find_at(src, el, B_SEMI) < close) { return false; } + const eq : usize = find_at(src, close, B_EQ); + if (eq >= src.len) { return false; } + var v : usize = skip_ws(src, eq + 1); + if (tok_is(src, v, "&")) { v = skip_ws(src, tok_end(src, v)); } + if (v >= src.len or src[v] != B_LBRACKET) { return false; } + const vc : usize = block_end(src, v); + const e0 : usize = skip_ws(src, v + 1); + if (e0 >= vc - 1 or find_at(src, e0, B_SEMI) < vc - 1) { return false; } + if (bare_arg_use(src, na, nb)) { + refuse(st, R_TYPE, t, t, tok_end(src, t)); + return true; + } + puts(st, out, "["); + put_num(st, out, list_count(src, e0, vc - 1)); + puts(st, out, "]"); + st[P] = el; + if (nested_elem(src, st, false)) { return true; } + ty(src, st, out); + st[P] = skip_ws(src, close); + return true; +} + +/// Is the name src[na .. nb) passed whole as an argument somewhere in the file: `(NAME)`, `(NAME,`, `, NAME)` or `, NAME,`? +pub fn bare_arg_use(src: []const u8, na: usize, nb: usize) -> bool { + var prev : usize = src.len; + var q : usize = skip_ws(src, 0); + while (q < src.len) { + const e : usize = tok_end(src, q); + if (same_text(src, q, e, na, nb) and q != na and (tok_is(src, prev, "(") or tok_is(src, prev, ",")) and (tok_is(src, skip_ws(src, e), ")") or tok_is(src, skip_ws(src, e), ","))) { return true; } + prev = q; + q = skip_ws(src, e); + } + return false; +} + pub fn const_item(src: []const u8, st: []usize, out: []u8, start: usize, is_pub: bool) -> void { const kpos : usize = st[P]; const w0 : usize = st[W]; @@ -8801,7 +9377,7 @@ pub fn const_item(src: []const u8, st: []usize, out: []u8, start: usize, is_pub: put_ident(src, st, out, na, nb); want(src, st, ":"); puts(st, out, " : "); - ty(src, st, out); + if (!slice_const(src, st, out, na, nb)) { ty(src, st, out); } want(src, st, "="); puts(st, out, " = "); expr(src, st, out, false); @@ -8824,6 +9400,13 @@ pub fn skip_attrs(src: []const u8, st: []usize) -> void { } } +/// Is src[a .. b] a type name t27c's Zig backend renames in a type position (t27_array_type_to_zig: GF16 and gf16 +/// are u16, str and string are []const u8, float and double f64, int i32, uint u32)? A struct or enum of that name +/// is every use of it turned into the other type (#10905: `struct GF16(u16)`). +pub fn builtin_type_name(src: []const u8, a: usize, b: usize) -> bool { + return word_is(src, a, b, "GF16") or word_is(src, a, b, "gf16") or word_is(src, a, b, "str") or word_is(src, a, b, "string") or word_is(src, a, b, "float") or word_is(src, a, b, "double") or word_is(src, a, b, "int") or word_is(src, a, b, "uint"); +} + pub fn struct_item(src: []const u8, st: []usize, out: []u8, start: usize, is_pub: bool) -> void { const kpos : usize = st[P]; const w0 : usize = st[W]; @@ -8834,6 +9417,7 @@ pub fn struct_item(src: []const u8, st: []usize, out: []u8, start: usize, is_pub const na : usize = st[P]; const nb : usize = tok_end(src, na); step(src, st); + if (builtin_type_name(src, na, nb)) { refuse(st, R_TYPENAME, na, na, nb); } if (is_pub) { puts(st, out, "pub const "); } else { puts(st, out, "const "); } put_ident(src, st, out, na, nb); puts(st, out, " = struct "); @@ -8980,6 +9564,7 @@ pub fn enum_item(src: []const u8, st: []usize, out: []u8, start: usize, is_pub: const na : usize = st[P]; const nb : usize = tok_end(src, na); step(src, st); + if (builtin_type_name(src, na, nb)) { refuse(st, R_TYPENAME, na, na, nb); } if (at(src, st[P], "<")) { refuse_here(src, st, R_GENERIC); } if (is_pub) { puts(st, out, "pub const "); } else { puts(st, out, "const "); } put_ident(src, st, out, na, nb); @@ -10370,7 +10955,7 @@ test negative_control_an_unknown_element_type_or_length_and_a_vec_that_is_no_let var out : [4096]u8 = undefined; var st : [1024]usize = [0] ** 1024; const n : usize = body("fn bytes(v: &[u8]) -> usize { v.len() }\nfn runtime(n: usize) -> usize {\n let v = vec![0u8; n];\n v.len()\n}\nfn expr_count() {\n let v = vec![0u8; 2 + 2];\n assert_eq!(bytes(&v), 4);\n}\n#[test]\nfn unknown_type() {\n let v = vec![1, 2, 3];\n assert_eq!(v.len(), 3);\n}\n#[test]\nfn a_call_argument_is_not_a_let() {\n let u = bytes(&vec![1u8]);\n}", &out, &st); - assert(out_is(n, &out, 0, "fn bytes(v: []const u8) -> usize {\n return v.len;\n}\n\n// t27o: refused fn runtime (line 2): a vec! whose element type or length t27o cannot tell `vec` at line 3\n\nfn runtime(n: usize) -> usize {\n}\n\n// t27o: refused fn expr_count (line 6): a vec! whose element type or length t27o cannot tell `vec` at line 7\n\nfn expr_count() -> void {\n}\n\n// t27o: refused fn unknown_type (line 11): a vec! whose element type or length t27o cannot tell `vec` at line 12\n\n// t27o: refused fn a_call_argument_is_not_a_let (line 16): macro `vec` at line 17\n\n")); + assert(out_is(n, &out, 0, "fn bytes(v: []const u8) -> usize {\n return v.len;\n}\n\n// t27o: refused fn runtime (line 2): a vec! or array literal whose element type or length t27o cannot tell `vec` at line 3\n\nfn runtime(n: usize) -> usize {\n}\n\n// t27o: refused fn expr_count (line 6): a vec! or array literal whose element type or length t27o cannot tell `vec` at line 7\n\nfn expr_count() -> void {\n}\n\n// t27o: refused fn unknown_type (line 11): a vec! or array literal whose element type or length t27o cannot tell `vec` at line 12\n\n// t27o: refused fn a_call_argument_is_not_a_let (line 16): macro `vec` at line 17\n\n")); assert(st[NOK] == 1); } @@ -14725,16 +15310,36 @@ test unit_hit_unit_struct_and_enum_has_read_a_unit_struct_and_the_variants_of_an st[IHI] = 0; mode = mode + 1; } - assert(enum_has(src, 0, 0, 113, 116, 128, 131)); - assert(enum_has(src, 0, 0, 113, 116, 123, 127)); - assert(enum_has(src, 0, 0, 113, 116, 132, 137)); - assert(!enum_has(src, 0, 0, 113, 116, 138, 140)); - assert(!enum_has(src, 0, 0, 113, 116, 113, 116)); - assert(!enum_has(src, 0, 0, 117, 122, 123, 127)); - assert(!enum_has(src, 0, 0, 138, 140, 123, 127)); - assert(!enum_has(src, 0, 0, 141, 145, 123, 127)); - assert(enum_has(src, 113, 116, 141, 145, 123, 127)); - assert(!enum_has(src, 117, 122, 141, 145, 123, 127)); + mode = 0; + while (mode < 2) { + st[IXON] = 1 - mode; + st[ILO] = 0; + st[IHI] = 0; + assert(enum_has(src, &st, 113, 116, 128, 131)); + assert(enum_has(src, &st, 113, 116, 123, 127)); + assert(enum_has(src, &st, 113, 116, 132, 137)); + assert(!enum_has(src, &st, 113, 116, 138, 140)); + assert(!enum_has(src, &st, 113, 116, 113, 116)); + assert(!enum_has(src, &st, 117, 122, 123, 127)); + assert(!enum_has(src, &st, 138, 140, 123, 127)); + assert(!enum_has(src, &st, 141, 145, 123, 127)); + st[ILO] = 113; + st[IHI] = 116; + assert(enum_has(src, &st, 141, 145, 123, 127)); + st[ILO] = 117; + st[IHI] = 122; + assert(!enum_has(src, &st, 141, 145, 123, 127)); + assert(enum_open(src, &st, 113, 116) == 59); + assert(enum_open(src, &st, 117, 122) == 96); + assert(enum_open(src, &st, 100, 102) == src.len); + assert(enum_open(src, &st, 5, 5) == src.len); + mode = mode + 1; + } + st[ILO] = 0; + st[IHI] = 0; + assert(enum_hit(src, 50, 113, 116)); + assert(!enum_hit(src, 50, 117, 122)); + assert(!enum_hit(src, 0, 113, 116)); } test an_all_caps_variant_is_owner_dot_variant_and_a_unit_struct_value_is_a_literal { @@ -14861,6 +15466,68 @@ test qualified_fn_foreign_block_and_record_foreign_read_a_header_that_is_not_a_p assert(!named(src, &st, K_FN, 10, 11)); } +test value_end_walks_a_struct_literal_or_a_call_to_the_token_after_it { + const src : str = "Pt { x: 1 } Pt(1) mk(2) Self::new(3) a.f(4) name 5"; + assert(value_end(src, 0) == 12); + assert(value_end(src, 12) == 18); + assert(value_end(src, 18) == 24); + assert(value_end(src, 24) == 37); + assert(value_end(src, 37) == 44); + assert(value_end(src, 44) == src.len); +} + +test builtin_type_name_has_word_and_array_iterated_read_a_name { + const src : str = "GF16 gf16 str string float double int uint Node f32 f320"; + assert(builtin_type_name(src, 0, 4)); + assert(builtin_type_name(src, 5, 9)); + assert(builtin_type_name(src, 10, 13)); + assert(builtin_type_name(src, 14, 20)); + assert(builtin_type_name(src, 21, 26)); + assert(builtin_type_name(src, 27, 33)); + assert(builtin_type_name(src, 34, 37)); + assert(builtin_type_name(src, 38, 42)); + assert(!builtin_type_name(src, 43, 47)); + assert(!builtin_type_name(src, 48, 51)); + assert(!builtin_type_name(src, 52, 56)); + assert(has_word(src, "f32")); + assert(has_word(src, "Node")); + assert(!has_word(src, "f3")); + assert(!has_word(src, "no")); + assert(!has_word("", "f32")); + const lp : str = "for c in cmds { } for w in &words { } for n in nums.iter() { } for x in other { } for i in 0..3 { }"; + assert(array_iterated(lp, 0, lp.len, 9, 13)); + assert(array_iterated(lp, 0, lp.len, 28, 33)); + assert(array_iterated(lp, 0, lp.len, 47, 51)); + assert(!array_iterated(lp, 0, lp.len, 86, 87)); + assert(!array_iterated(lp, 34, lp.len, 9, 13)); + assert(!array_iterated(lp, 0, 24, 28, 33)); +} + +test bare_arg_use_finds_a_name_handed_whole_to_a_call { + const src : str = "const NAMES: &[&str] = &[\"a\"]; fn f() { hand(NAMES); g(1, NAMES); h(NAMES, 2); k(x, NAMES, 2); NAMES.len(); NAMES[0]; }"; + assert(bare_arg_use(src, 6, 11)); + const only : str = "const OTHER: &[&str] = &[\"a\"]; fn f() { OTHER.len(); OTHER[0]; for x in OTHER { } }"; + assert(!bare_arg_use(only, 6, 11)); + assert(!bare_arg_use("", 0, 0)); +} + +test param_type_and_array_ref_param_read_the_parameter_list_not_a_struct_literals_label { + const src : str = "fn f(a: &[u8; 4], b: &[u8], c: [u8; 4], d: &mut [u8; 2], e: &'static [u16; 3], g: u8) { Hd { a: *a, g: g }; x(a, b, c, d, e, g) }"; + assert(param_type(src, 0, 97, 98) == 8); + assert(param_type(src, 0, 125, 126) == 82); + assert(param_type(src, 0, 116, 117) == 31); + assert(param_type(src, 0, 88, 90) == src.len); + assert(param_type("x + 1", 0, 0, 1) == 5); + assert(array_ref_param(src, 0, 97, 98)); + assert(array_ref_param(src, 0, 110, 111)); + assert(!array_ref_param(src, 0, 113, 114)); + assert(!array_ref_param(src, 0, 116, 117)); + assert(array_ref_param(src, 0, 119, 120)); + assert(array_ref_param(src, 0, 122, 123)); + assert(!array_ref_param(src, 0, 125, 126)); + assert(!array_ref_param(src, 0, 0, 2)); +} + test payload_binder_names_the_value_a_last_arm_some_or_ok_binds_and_refuses_the_other_payloads { var st : [64]usize = [0] ** 64; clear(&st); @@ -14912,6 +15579,289 @@ Pt new Lang default Pt origin Lang new Pt default Self new"; } } +/// `Lang::default()` of a #[derive(Default)] and `Pt::origin()` name a `Type_f` no one writes (#10905); `Pt::new()` and `Self::new()` are declared. +test an_associated_call_nobody_declares_is_refused_and_one_an_impl_declares_converts { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("#[derive(Default)]\npub enum Lang { #[default] En, Ru }\npub struct Pt { x: u8 }\nimpl Pt {\n pub fn new() -> Pt { Pt { x: 1 } }\n pub fn twin() -> Pt { Self::new() }\n}\npub fn make() -> Lang { Lang::default() }\npub fn ok() -> Pt { Pt::new() }\npub fn bad() -> Pt { Pt::origin() }", &out, &st); + assert(out_is(n, &out, 0, "pub const Lang = enum(u32) {\n En,\n Ru,\n};\n\npub const Pt = struct {\n x : u8,\n};\n\npub fn Pt_new() -> Pt {\n return Pt{ .x = 1 };\n}\n\npub fn Pt_twin() -> Pt {\n return Pt_new();\n}\n\n// t27o: refused fn make (line 8): undeclared `default` at line 8\n\npub fn make() -> Lang {\n}\n\npub fn ok() -> Pt {\n return Pt_new();\n}\n\n// t27o: refused fn bad (line 10): undeclared `origin` at line 10\n\npub fn bad() -> Pt {\n}\n\n")); + assert(st[NOK] == 3); + assert(st[NTEST] == 0); +} + +/// `Some(want) => ..` as the last arm bound nothing, so `want` was undeclared (#10905); the binder takes the Option's type. +test a_last_arm_that_binds_a_payload_binds_it_and_the_other_payload_patterns_are_refused { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub fn pick(e: Option<&str>, a: &str) -> bool {\n match e { None => true, Some(want) => want == a.trim() }\n}\npub fn inc(e: Option) -> u8 {\n match e { None => 0, Some(v) => v + 1 }\n}\npub fn some_one(e: Option) -> u8 {\n match e { None => 0, Some(_) => 1 }\n}\npub fn lit(e: Option) -> u8 {\n match e { None => 0, Some(1) => 1 }\n}\npub fn code(e: Result) -> u8 {\n match e { Ok(_) => 0, Err(c) => c }\n}\npub fn unread(e: Result) -> u8 {\n match e { Ok(v) => v, Err(_) => 9 }\n}", &out, &st); + assert(out_is(n, &out, 0, "use tri::t27b::type_text;\n\npub fn pick(e: ?[]const u8, a: []const u8) -> bool {\n if (e == null) {\n return true;\n } else {\n const want : []const u8 = e.?;\n return want == a[type_text::trim_lo(a, 0, a.len) .. type_text::trim_hi(a, type_text::trim_lo(a, 0, a.len), a.len)];\n }\n}\n\npub fn inc(e: ?u8) -> u8 {\n if (e == null) {\n return 0;\n } else {\n const v : u8 = e.?;\n return v + 1;\n }\n}\n\npub fn some_one(e: ?u8) -> u8 {\n if (e == null) {\n return 0;\n } else {\n return 1;\n }\n}\n\n// t27o: refused fn lit (line 10): match pattern `Some` at line 11\n\npub fn lit(e: ?u8) -> u8 {\n}\n\n// t27o: refused fn code (line 13): match pattern `Ok` at line 14\n\npub fn code(e: ?u8) -> u8 {\n}\n\n// t27o: refused fn unread (line 16): match pattern `Ok` at line 17\n\npub fn unread(e: ?u8) -> u8 {\n}\n\n")); + assert(st[NOK] == 3); + assert(st[NTEST] == 0); +} + +/// `pub extern fn ext` was refused without its name, so its callers kept it (#10905); an `unsafe fn` registered the name `fn`. +test unsafe_and_extern_fns_are_refused_under_their_own_names_and_a_foreign_block_by_its_fns { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub unsafe fn raw(a: u8) -> u8 { a }\n\npub extern \"C\" fn ext(a: u8) -> u8 { a + 2 }\n\nextern \"C\" {\n fn lib_add(a: u8, b: u8) -> u8;\n}\n\npub fn plain(a: u8) -> u8 { a + 1 }\n\npub fn uses_ext() -> u8 { ext(1) + plain(2) }\n\npub fn uses_lib() -> u8 { plain(1) }\n\n#[test]\nfn t() {\n assert_eq!(plain(1), 2);\n assert_eq!(ext(1), 3);\n}", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused unsafe (line 1): item `unsafe` at line 1; 1 fn inside\n\n// t27o: refused extern (line 3): item `extern` at line 3; 1 fn inside\n\n// t27o: refused extern (line 5): item `extern` at line 5; 1 fn inside\n\npub fn plain(a: u8) -> u8 {\n return a + 1;\n}\n\n// t27o: refused fn uses_ext (line 11): uses refused `ext` at line 11\n\npub fn uses_ext() -> u8 {\n}\n\npub fn uses_lib() -> u8 {\n return plain(1);\n}\n\n// t27o: refused fn t (line 16): uses refused `ext` at line 18\n\n")); + assert(st[NOK] == 2); + assert(st[NTEST] == 0); +} + +/// An enum cast or negated or multiplied, a struct or array compared, a text ordered: refused; two enums ordered or equal, ints and a slice length stay. +test operators_t27c_has_no_form_for_are_refused_where_the_operands_are_enums_structs_arrays_or_texts { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub struct Ver { major: u8 }\nimpl Ver { pub fn new(major: u8) -> Ver { Ver { major } } }\npub enum Trit { Neg = -1, Zero = 0, Pos = 1 }\nimpl Trit {\n pub fn value(self) -> i8 { self as i8 }\n pub fn lt(a: Trit, b: Trit) -> bool { a < b }\n pub fn same(a: Trit, b: Trit) -> bool { a == b }\n pub fn flip() -> Trit { -Trit::Pos }\n}\npub const SEEDS: [u64; 3] = [1, 2, 3];\npub fn newer(a: &str, b: &str) -> bool { a > b }\npub fn older() -> bool { Ver::new(1) < Ver::new(2) }\npub fn eq_ver() -> bool { Ver::new(1) == Ver::new(2) }\npub fn seeds_match() -> bool { SEEDS == [1, 2, 3] }\npub fn prod() -> bool { Trit::Pos * Trit::Neg == Trit::Neg }\npub fn count(v: &[u8]) -> bool { v.len() > 2 }\npub fn ints(a: u32, b: u32) -> bool { a < b }", &out, &st); + assert(out_is(n, &out, 0, "pub const Ver = struct {\n major : u8,\n};\n\npub fn Ver_new(major: u8) -> Ver {\n return Ver{ .major = major };\n}\n\npub const Trit = enum(i32) {\n Neg = -1,\n Zero = 0,\n Pos = 1,\n};\n\n// t27o: refused fn value (line 5): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `as` at line 5\n\npub fn Trit_value(self_: Trit) -> i8 {\n}\n\npub fn Trit_lt(a: Trit, b: Trit) -> bool {\n return a < b;\n}\n\npub fn Trit_same(a: Trit, b: Trit) -> bool {\n return a == b;\n}\n\n// t27o: refused fn flip (line 8): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `Trit` at line 8\n\npub fn Trit_flip() -> Trit {\n}\n\npub const SEEDS : [3]u64 = [1, 2, 3];\n\n// t27o: refused fn newer (line 11): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `>` at line 11\n\npub fn newer(a: []const u8, b: []const u8) -> bool {\n}\n\n// t27o: refused fn older (line 12): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `<` at line 12\n\npub fn older() -> bool {\n}\n\n// t27o: refused fn eq_ver (line 13): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `==` at line 13\n\npub fn eq_ver() -> bool {\n}\n\n// t27o: refused fn seeds_match (line 14): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `==` at line 14\n\npub fn seeds_match() -> bool {\n}\n\n// t27o: refused fn prod (line 15): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `*` at line 15\n\npub fn prod() -> bool {\n}\n\npub fn count(v: []const u8) -> bool {\n return v.len > 2;\n}\n\npub fn ints(a: u32, b: u32) -> bool {\n return a < b;\n}\n\n")); + assert(st[NOK] == 5); + assert(st[NTEST] == 0); +} + +/// `[]const ?T`, a struct named GF16, a bare float `var`, and a `&[E]` const set from a list (an array, unless the file hands it whole to a call). +test types_t27c_cannot_read_are_refused_and_float_vars_and_slice_consts_are_typed { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub fn opts(times: &[Option]) -> bool { times.len() == 0 }\npub fn vopts(times: &Vec>) -> bool { times.len() == 0 }\npub struct GF16(u16);\npub fn grow() -> f64 { let mut r = 1.0; r *= 2.0; r }\npub const NAMES: &[&str] = &[\"a\", \"bc\"];\npub const NUMS: &[u8] = &[1, 2];\npub fn first() -> usize { NAMES.len() + NUMS.len() }\npub const NAMES2: &[&str] = &[\"x\"];\npub fn hand(n: &[&str]) -> usize { n.len() }\npub fn pass() -> usize { hand(NAMES2) }", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused fn opts (line 1): type `Option` at line 1\n\n// t27o: refused fn vopts (line 2): type `Option` at line 2\n\n// t27o: refused struct GF16 (line 3): a type named like one t27c renames (GF16, str, int ..) `GF16` at line 3\n\npub fn grow() -> f64 {\n var r : f64 = 1.0;\n r *= 2.0;\n return r;\n}\n\npub const NAMES : [2][]const u8 = [\"a\", \"bc\"];\n\npub const NUMS : [2]u8 = [1, 2];\n\npub fn first() -> usize {\n return NAMES.len + NUMS.len;\n}\n\n// t27o: refused const NAMES2 (line 8): type `[` at line 8\n\npub fn hand(n: [][]const u8) -> usize {\n return n.len;\n}\n\n// t27o: refused fn pass (line 10): uses refused `NAMES2` at line 10\n\npub fn pass() -> usize {\n}\n\n")); + assert(st[NOK] == 3); + assert(st[NTEST] == 0); +} + +/// `let cmds = [Cmd::Nop, ..]; for c in cmds` is a typed array (#10905); an unsuffixed int list has no type to give and is left as it was. +test an_array_local_that_is_looped_over_written_borrowed_or_indexed_is_a_typed_array { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub enum Cmd { Nop, Reset }\nfn sum(v: &[f64]) -> f64 { v.len() as f64 }\n#[test]\nfn loops() {\n let cmds = [Cmd::Nop, Cmd::Reset];\n for c in cmds { assert!(true); }\n let words = [\"a\", \"b\"];\n for w in &words { assert!(w.len() == 1); }\n let nums = [1, 2, 3];\n for n in nums { assert!(n > 0); }\n let only = [Cmd::Nop];\n assert!(only.len() == 1);\n}\n#[test]\nfn writes() {\n let mut scores = [0.0f64; 4];\n scores[0] = 1.0;\n assert!(scores.len() == 4);\n}\n#[test]\nfn borrows() {\n let input = [1.0, 2.0, 3.0];\n assert!(sum(&input) > 0.0);\n let idx = [Cmd::Nop, Cmd::Reset];\n let j = 1;\n let c = &idx[j];\n}", &out, &st); + assert(out_is(n, &out, 0, "pub const Cmd = enum(u32) {\n Nop,\n Reset,\n};\n\nfn sum(v: []const f64) -> f64 {\n return v.len as f64;\n}\n\ntest loops {\n const cmds : [2]Cmd = [Cmd.Nop, Cmd.Reset];\n for c in cmds {\n assert(true);\n }\n const words : [2][]const u8 = [\"a\", \"b\"];\n for w in words {\n assert(w.len == 1);\n }\n const nums = [1, 2, 3];\n for n in nums {\n assert(n > 0);\n }\n const only = [Cmd.Nop];\n assert(only.len == 1);\n}\n\ntest writes {\n var scores : [4]f64 = [@as(f64, 0.0)] ** 4;\n scores[0] = 1.0;\n assert(scores.len == 4);\n}\n\ntest borrows {\n const input : [3]f64 = [1.0, 2.0, 3.0];\n assert(sum(&input) > 0.0);\n const idx : [2]Cmd = [Cmd.Nop, Cmd.Reset];\n const j = 1;\n const c = idx[j];\n}\n\n")); + assert(st[NOK] == 4); + assert(st[NTEST] == 3); +} + +/// The untyped tuple would be written into, which Zig refuses. +test a_written_array_of_no_known_type_is_refused { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("#[test]\nfn writes() {\n let mut plain = [0; 3];\n plain[1] = 2;\n assert!(plain.len() == 3);\n}", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused fn writes (line 2): a vec! or array literal whose element type or length t27o cannot tell `[` at line 3\n\n")); + assert(st[NOK] == 0); + assert(st[NTEST] == 0); +} + +/// `*tag` of a `&[u8; 4]` parameter and `&self.tag` have no t27 form (#10905). +test a_deref_of_an_array_reference_and_a_borrow_of_an_array_field_are_refused { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub struct Hd { tag: [u8; 4], n: u8 }\nimpl Hd {\n pub fn new(tag: &[u8; 4], n: u8) -> Hd { Hd { tag: *tag, n } }\n pub fn tag(&self) -> &[u8; 4] { &self.tag }\n pub fn first(&self) -> u8 { self.tag[0] }\n}", &out, &st); + assert(out_is(n, &out, 0, "pub const Hd = struct {\n tag : [4]u8,\n n : u8,\n};\n\n// t27o: refused fn new (line 3): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `tag` at line 3\n\npub fn Hd_new(tag: []const u8, n: u8) -> Hd {\n}\n\n// t27o: refused fn tag (line 4): an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text `self` at line 4\n\npub fn Hd_tag(self_: Hd) -> []const u8 {\n}\n\npub fn Hd_first(self: Hd) -> u8 {\n return self.tag[0];\n}\n\n")); + assert(st[NOK] == 1); + assert(st[NTEST] == 0); +} + +/// `x.validate().is_ok()` read `!= null` of a bool (#10905); `.unwrap()` of it was `.?`. +test a_method_returning_result_of_unit_is_a_bool_and_unwrap_of_it_is_refused { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub struct Dims { d: u32 }\nimpl Dims {\n pub fn validate(&self) -> Result<(), String> {\n if self.d == 0 { return Err(\"zero\".to_string()); }\n Ok(())\n }\n}\npub fn check(x: &Dims) -> bool { x.validate().is_ok() }\n#[test]\nfn t() {\n let ok = Dims { d: 3 };\n assert!(ok.validate().is_ok());\n let bad = Dims { d: 0 };\n assert!(bad.validate().is_err());\n}\n#[test]\nfn u() {\n let ok = Dims { d: 3 };\n ok.validate().unwrap();\n}", &out, &st); + assert(out_is(n, &out, 0, "pub const Dims = struct {\n d : u32,\n};\n\npub fn Dims_validate(self: Dims) -> bool {\n if (self.d == 0) {\n return false;\n }\n return true;\n}\n\npub fn check(x: Dims) -> bool {\n return Dims_validate(x);\n}\n\ntest t {\n const ok = Dims{ .d = 3 };\n assert(Dims_validate(ok));\n const bad = Dims{ .d = 0 };\n assert((Dims_validate(bad) == false));\n}\n\n// t27o: refused fn u (line 17): method `unwrap` at line 19\n\n")); + assert(st[NOK] == 3); + assert(st[NTEST] == 1); +} + +/// t27c writes the std import and the assert helper only with a test block, so without one these two fns are refused. +test an_assert_or_a_text_comparison_needs_a_test_block_in_the_spec { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub fn same_words(a: &str, b: &str) -> bool { a == b }\npub fn must(x: u8) { assert!(x > 0); }", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused fn same_words (line 1): an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one) `==` at line 1\n\npub fn same_words(a: []const u8, b: []const u8) -> bool {\n}\n\n// t27o: refused fn must (line 2): an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one) `assert` at line 2\n\npub fn must(x: u8) -> void {\n}\n\n")); + assert(st[NOK] == 0); + assert(st[NTEST] == 0); +} + +/// The same two fns beside a test block are kept. +test an_assert_or_a_text_comparison_stays_when_the_spec_has_a_test { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("pub fn same_words(a: &str, b: &str) -> bool { a == b }\npub fn must(x: u8) { assert!(x > 0); }\n#[test]\nfn t() {\n assert!(same_words(\"a\", \"a\"));\n must(1);\n}", &out, &st); + assert(out_is(n, &out, 0, "pub fn same_words(a: []const u8, b: []const u8) -> bool {\n return a == b;\n}\n\npub fn must(x: u8) -> void {\n assert(x > 0);\n}\n\ntest t {\n assert(same_words(\"a\", \"a\"));\n must(1);\n}\n\n")); + assert(st[NOK] == 3); + assert(st[NTEST] == 1); +} + +/// `t27a::ASM_NO_MNEMONIC =>` was written `t27a_ASM_NO_MNEMONIC` (#10905). +test a_match_on_consts_of_a_gen_module_reads_module_colon_colon_const { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("#[path = \"../gen/rust/isa/t27a.rs\"]\nmod t27a;\npub fn text(s: u32) -> &'static str {\n match s {\n t27a::ASM_NO_MNEMONIC => \"no mnemonic\",\n t27a::ASM_BAD_WORD => \"bad word\",\n _ => \"rejected\",\n }\n}", &out, &st); + assert(out_is(n, &out, 0, "use tri::t27b::type_text;\nuse isa::t27a;\n\npub fn text(s: u32) -> []const u8 {\n if (s == t27a::ASM_NO_MNEMONIC) {\n return \"no mnemonic\";\n } else if (s == t27a::ASM_BAD_WORD) {\n return \"bad word\";\n } else {\n return \"rejected\";\n }\n}\n\n")); + assert(st[NOK] == 1); + assert(st[NTEST] == 0); +} + +/// sha256 declares a `same` beside type_text's (#10556), operator_text has a `use` of its own (#10843). +test a_call_into_a_module_t27c_cannot_splice_is_refused { + var out : [4096]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + const n : usize = body("#[path = \"../gen/rust/tri/crypto/sha256.rs\"]\nmod sha256;\n#[path = \"../gen/rust/compiler/operator_text.rs\"]\nmod operator_text;\npub fn digest(a: &[u8]) -> bool { sha256::hash(a).len() == 32 }\npub fn op(a: &[u8]) -> bool { operator_text::compound_binop(a).is_some() }\npub fn is_x(a: &str) -> bool { a == \"x\" }", &out, &st); + assert(out_is(n, &out, 0, "use tri::t27b::type_text;\n\n// t27o: refused fn digest (line 5): a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records) `sha256::hash` at line 5\n\npub fn digest(a: []const u8) -> bool {\n}\n\n// t27o: refused fn op (line 6): a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records) `operator_text::compound_binop` at line 6\n\npub fn op(a: []const u8) -> bool {\n}\n\npub fn is_x(a: []const u8) -> bool {\n return type_text::same(a, \"x\");\n}\n\n")); + assert(st[NOK] == 1); + assert(st[NTEST] == 0); +} + +test type_kind_tells_a_struct_an_enum_and_an_array_held_by_value_from_a_type { + const src : str = "struct Pt { x: u8 }\nenum E { A, B }\nstruct U;\nPt ; &Pt ; E ; &mut E ; [u8; 4] ; &[u8; 4] ; [u8] ; u32 ; U ; Self ; Self"; + var st : [1024]usize = [0] ** 1024; + index_decls(src, &st); + var mode : usize = 0; + while (mode < 2) { + st[IXON] = 1 - mode; + assert(type_kind(src, &st, 46) == 1); + assert(type_kind(src, &st, 51) == 1); + assert(type_kind(src, &st, 57) == 2); + assert(type_kind(src, &st, 61) == 2); + assert(type_kind(src, &st, 70) == 3); + assert(type_kind(src, &st, 80) == 0); + assert(type_kind(src, &st, 91) == 0); + assert(type_kind(src, &st, 98) == 0); + assert(type_kind(src, &st, 104) == 1); + assert(type_kind(src, &st, 61) == 2); + st[ILO] = 25; + st[IHI] = 26; + assert(type_kind(src, &st, 108) == 2); + st[ILO] = 0; + st[IHI] = 0; + assert(type_kind(src, &st, 108) == 0); + assert(type_kind(src, &st, src.len) == 0); + mode = mode + 1; + } +} + +test operand_kind_names_the_struct_enum_array_text_or_bytes_an_operand_is { + const src : str = "struct Pt { x: u8 }\nenum E { A, B }\nstruct U;\nconst LIM: [u8; 2] = [1, 2];\nconst NAME: &str = \"n\";\nfn f(p: Pt, e: &E, a: [u8; 4], b: &[u8; 4], c: &[u8], n: u32, v: Vec, w: &[Pt], s: &str) {\n let cmds = [E::A, E::B];\n let raw = s.as_bytes();\n let t = 5;\n let mk = Pt { x: 1 };\n for c1 in cmds { }\n for c2 in &v { }\n for c3 in w { }\n for c4 in [Pt { x: 1 }] { }\n for c5 in other { }\n p ; e ; a ; b ; c ; n ; s ; t ; mk ; mk.x ; LIM ; NAME ; E::A ; E::Z ; Pt { x: 2 } ; [1, 2] ; \"lit\" ; c1 ; c2 ; c3 ; c4 ; c5 ; raw ; raw[1..3] ; s[1..] ; (p) ; p.x ; n + 1 ; &p ; *e ; \n}"; + var st : [2048]usize = [0] ** 2048; + index_decls(src, &st); + var mode : usize = 0; + while (mode < 2) { + st[IXON] = 1 - mode; + st[FLO] = 99; + assert(operand_kind(src, &st, 415, 416) == 1); + assert(operand_kind(src, &st, 419, 420) == 2); + assert(operand_kind(src, &st, 423, 424) == 3); + assert(operand_kind(src, &st, 427, 428) == 5); + assert(operand_kind(src, &st, 431, 432) == 5); + assert(operand_kind(src, &st, 435, 436) == 0); + assert(operand_kind(src, &st, 439, 440) == 4); + assert(operand_kind(src, &st, 443, 444) == 0); + assert(operand_kind(src, &st, 447, 449) == 1); + assert(operand_kind(src, &st, 452, 456) == 0); + assert(operand_kind(src, &st, 459, 462) == 3); + assert(operand_kind(src, &st, 465, 469) == 4); + assert(operand_kind(src, &st, 472, 476) == 2); + assert(operand_kind(src, &st, 479, 483) == 0); + assert(operand_kind(src, &st, 486, 497) == 1); + assert(operand_kind(src, &st, 500, 506) == 3); + assert(operand_kind(src, &st, 509, 514) == 4); + assert(operand_kind(src, &st, 517, 519) == 2); + assert(operand_kind(src, &st, 522, 524) == 2); + assert(operand_kind(src, &st, 527, 529) == 1); + assert(operand_kind(src, &st, 532, 534) == 1); + assert(operand_kind(src, &st, 537, 539) == 0); + assert(operand_kind(src, &st, 542, 545) == 5); + assert(operand_kind(src, &st, 548, 557) == 5); + assert(operand_kind(src, &st, 560, 566) == 5); + assert(operand_kind(src, &st, 569, 572) == 1); + assert(operand_kind(src, &st, 575, 578) == 0); + assert(operand_kind(src, &st, 581, 586) == 0); + assert(operand_kind(src, &st, 589, 591) == 1); + assert(operand_kind(src, &st, 594, 596) == 2); + mode = mode + 1; + } +} + +test let_init_of_for_var_kind_and_let_as_bytes_read_the_binding_of_a_name_before_its_use { + const src : str = "struct Pt { x: u8 }\nenum E { A, B }\nstruct U;\nconst LIM: [u8; 2] = [1, 2];\nconst NAME: &str = \"n\";\nfn f(p: Pt, e: &E, a: [u8; 4], b: &[u8; 4], c: &[u8], n: u32, v: Vec, w: &[Pt], s: &str) {\n let cmds = [E::A, E::B];\n let raw = s.as_bytes();\n let t = 5;\n let mk = Pt { x: 1 };\n for c1 in cmds { }\n for c2 in &v { }\n for c3 in w { }\n for c4 in [Pt { x: 1 }] { }\n for c5 in other { }\n c1 ; c2 ; c3 ; c4 ; c5 ; nobody ; cmds ; raw ; t ; mk ; nobody ; \n}"; + var st : [2048]usize = [0] ** 2048; + index_decls(src, &st); + assert(let_init_of(src, 99, 449, 449, 453) == 208); + assert(let_init_of(src, 99, 466, 466, 468) == 278); + assert(let_init_of(src, 99, 471, 471, 477) == src.len); + assert(let_init_of(src, 99, 197, 449, 453) == src.len); + assert(for_var_kind(src, &st, 99, 415, 415, 417) == OK_ENUM); + assert(for_var_kind(src, &st, 99, 420, 420, 422) == OK_ENUM); + assert(for_var_kind(src, &st, 99, 425, 425, 427) == OK_STRUCT); + assert(for_var_kind(src, &st, 99, 430, 430, 432) == OK_STRUCT); + assert(for_var_kind(src, &st, 99, 435, 435, 437) == OK_NONE); + assert(for_var_kind(src, &st, 99, 462, 462, 463) == OK_NONE); + assert(let_as_bytes(src, 99, 456, 456, 459)); + assert(!let_as_bytes(src, 99, 462, 462, 463)); + assert(!let_as_bytes(src, 99, 449, 449, 453)); + assert(!let_as_bytes(src, 99, 471, 471, 477)); +} + +test operands_ok_refuses_the_operators_t27c_has_no_form_for_and_notes_the_prelude_a_text_equality_needs { + const src : str = "struct Pt { x: u8 }\nenum E { A, B }\nconst LIM: [u8; 2] = [1, 2];\nconst NAME: &str = \"n\";\nfn f(p: Pt, q: Pt, e: &E, a: [u8; 4], n: u32, m: u32, s: &str, u: &str, c: &[u8]) {\n p ; q ; e ; E::A ; E::B ; a ; LIM ; n ; m ; s ; u ; NAME ; c ; == ; < ; + ; \n}"; + var st : [2048]usize = [0] ** 2048; + index_decls(src, &st); + var mode : usize = 0; + while (mode < 2) { + st[IXON] = 1 - mode; + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 177, 178, 181, 182, 240, 242, true, false); + assert(st[ERR] == R_OPERAND); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 203, 204, 207, 210, 240, 242, true, false); + assert(st[ERR] == R_OPERAND); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 185, 186, 189, 193, 240, 242, true, false); + assert(st[ERR] == 0); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 189, 193, 196, 200, 245, 246, false, true); + assert(st[ERR] == 0); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 189, 193, 196, 200, 249, 250, false, false); + assert(st[ERR] == R_OPERAND); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 213, 214, 217, 218, 249, 250, false, false); + assert(st[ERR] == 0); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 213, 214, 217, 218, 245, 246, false, true); + assert(st[ERR] == 0); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 221, 222, 225, 226, 245, 246, false, true); + assert(st[ERR] == R_OPERAND); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 221, 222, 229, 233, 245, 246, false, true); + assert(st[ERR] == R_OPERAND); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 236, 237, 213, 214, 245, 246, false, true); + assert(st[ERR] == R_OPERAND); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 221, 222, 225, 226, 240, 242, true, false); + assert(st[ERR] == 0); + assert(named(src, &st, K_NEEDPRE, 0, 1)); + clear(&st); + st[FLO] = 89; + operands_ok(src, &st, 213, 214, 217, 218, 240, 242, true, false); + assert(!named(src, &st, K_NEEDPRE, 0, 1)); + add_name(src, &st, K_NOPRE, 0, 1, 0, 0); + operands_ok(src, &st, 221, 222, 225, 226, 240, 242, true, false); + assert(st[ERR] == R_NOPRELUDE); + mode = mode + 1; + } +} + test need_prelude_notes_the_need_and_refuses_once_a_pass_found_no_test { var st : [256]usize = [0] ** 256; need_prelude("fn f() {}", &st, 0, 2); @@ -14962,6 +15912,36 @@ test mod_member_writes_a_gen_module_member_and_refuses_a_clash_or_a_module_it_ca assert(st3[ERR] == R_SPLICE); } +test array_indexed_array_borrowed_and_array_written_read_how_a_local_is_used { + const src : str = "a[j] ; b[0] ; c[1 + 2] ; d[i] = 4 ; e[k] += 1 ; f[2] == 3 ; g = &h ; &mut m ; k"; + assert(array_indexed(src, 0, src.len, 0, 1)); + assert(!array_indexed(src, 0, src.len, 7, 8)); + assert(array_indexed(src, 0, src.len, 14, 15)); + assert(array_indexed(src, 0, src.len, 25, 26)); + assert(!array_indexed(src, 0, src.len, 65, 66)); + assert(!array_indexed(src, 11, src.len, 0, 1)); + assert(array_written(src, 0, src.len, 25, 26)); + assert(array_written(src, 0, src.len, 36, 37)); + assert(!array_written(src, 0, src.len, 48, 49)); + assert(!array_written(src, 0, src.len, 0, 1)); + assert(array_borrowed(src, 0, src.len, 65, 66)); + assert(array_borrowed(src, 0, src.len, 74, 75)); + assert(!array_borrowed(src, 0, src.len, 0, 1)); + assert(!array_borrowed(src, 74, src.len, 65, 66)); +} + +test bare_float_is_a_lone_float_literal_without_a_suffix { + var st : [64]usize = [0] ** 64; + assert(bare_float("1.0;", &st, 0)); + assert(bare_float("-0.5 ;", &st, 0)); + assert(bare_float("2e3;", &st, 0)); + assert(!bare_float("1.5f32;", &st, 0)); + assert(!bare_float("1;", &st, 0)); + assert(!bare_float("2.0 + x;", &st, 0)); + assert(!bare_float("x;", &st, 0)); + assert(!bare_float("", &st, 0)); +} + test bind_type_types_the_binder_of_an_option_or_a_result_by_its_declared_type { var out : [64]u8 = undefined; var st : [64]usize = [0] ** 64; @@ -14983,54 +15963,69 @@ test bind_type_types_the_binder_of_an_option_or_a_result_by_its_declared_type { assert(st[W] == 0); } -/// `Lang::default()` of a #[derive(Default)] and `Pt::origin()` name a `Type_f` no one writes (#10905); `Pt::new()` and `Self::new()` are declared. -test an_associated_call_nobody_declares_is_refused_and_one_an_impl_declares_converts { - var out : [4096]u8 = undefined; - var st : [1024]usize = [0] ** 1024; - const n : usize = body("#[derive(Default)]\npub enum Lang { #[default] En, Ru }\npub struct Pt { x: u8 }\nimpl Pt {\n pub fn new() -> Pt { Pt { x: 1 } }\n pub fn twin() -> Pt { Self::new() }\n}\npub fn make() -> Lang { Lang::default() }\npub fn ok() -> Pt { Pt::new() }\npub fn bad() -> Pt { Pt::origin() }", &out, &st); - assert(out_is(n, &out, 0, "pub const Lang = enum(u32) {\n En,\n Ru,\n};\n\npub const Pt = struct {\n x : u8,\n};\n\npub fn Pt_new() -> Pt {\n return Pt{ .x = 1 };\n}\n\npub fn Pt_twin() -> Pt {\n return Pt_new();\n}\n\n// t27o: refused fn make (line 8): undeclared `default` at line 8\n\npub fn make() -> Lang {\n}\n\npub fn ok() -> Pt {\n return Pt_new();\n}\n\n// t27o: refused fn bad (line 10): undeclared `origin` at line 10\n\npub fn bad() -> Pt {\n}\n\n")); - assert(st[NOK] == 3); - assert(st[NTEST] == 0); -} - -/// `Some(want) => ..` as the last arm bound nothing, so `want` was undeclared (#10905); the binder takes the Option's type. -test a_last_arm_that_binds_a_payload_binds_it_and_the_other_payload_patterns_are_refused { - var out : [4096]u8 = undefined; - var st : [1024]usize = [0] ** 1024; - const n : usize = body("pub fn pick(e: Option<&str>, a: &str) -> bool {\n match e { None => true, Some(want) => want == a.trim() }\n}\npub fn inc(e: Option) -> u8 {\n match e { None => 0, Some(v) => v + 1 }\n}\npub fn some_one(e: Option) -> u8 {\n match e { None => 0, Some(_) => 1 }\n}\npub fn lit(e: Option) -> u8 {\n match e { None => 0, Some(1) => 1 }\n}\npub fn code(e: Result) -> u8 {\n match e { Ok(_) => 0, Err(c) => c }\n}\npub fn unread(e: Result) -> u8 {\n match e { Ok(v) => v, Err(_) => 9 }\n}", &out, &st); - assert(out_is(n, &out, 0, "use tri::t27b::type_text;\n\npub fn pick(e: ?[]const u8, a: []const u8) -> bool {\n if (e == null) {\n return true;\n } else {\n const want : []const u8 = e.?;\n return want == a[type_text::trim_lo(a, 0, a.len) .. type_text::trim_hi(a, type_text::trim_lo(a, 0, a.len), a.len)];\n }\n}\n\npub fn inc(e: ?u8) -> u8 {\n if (e == null) {\n return 0;\n } else {\n const v : u8 = e.?;\n return v + 1;\n }\n}\n\npub fn some_one(e: ?u8) -> u8 {\n if (e == null) {\n return 0;\n } else {\n return 1;\n }\n}\n\n// t27o: refused fn lit (line 10): match pattern `Some` at line 11\n\npub fn lit(e: ?u8) -> u8 {\n}\n\n// t27o: refused fn code (line 13): match pattern `Ok` at line 14\n\npub fn code(e: ?u8) -> u8 {\n}\n\n// t27o: refused fn unread (line 16): match pattern `Ok` at line 17\n\npub fn unread(e: ?u8) -> u8 {\n}\n\n")); - assert(st[NOK] == 3); - assert(st[NTEST] == 0); -} - -/// `pub extern fn ext` was refused without its name, so its callers kept it (#10905); an `unsafe fn` registered the name `fn`. -test unsafe_and_extern_fns_are_refused_under_their_own_names_and_a_foreign_block_by_its_fns { - var out : [4096]u8 = undefined; - var st : [1024]usize = [0] ** 1024; - const n : usize = body("pub unsafe fn raw(a: u8) -> u8 { a }\n\npub extern \"C\" fn ext(a: u8) -> u8 { a + 2 }\n\nextern \"C\" {\n fn lib_add(a: u8, b: u8) -> u8;\n}\n\npub fn plain(a: u8) -> u8 { a + 1 }\n\npub fn uses_ext() -> u8 { ext(1) + plain(2) }\n\npub fn uses_lib() -> u8 { plain(1) }\n\n#[test]\nfn t() {\n assert_eq!(plain(1), 2);\n assert_eq!(ext(1), 3);\n}", &out, &st); - assert(out_is(n, &out, 0, "// t27o: refused unsafe (line 1): item `unsafe` at line 1; 1 fn inside\n\n// t27o: refused extern (line 3): item `extern` at line 3; 1 fn inside\n\n// t27o: refused extern (line 5): item `extern` at line 5; 1 fn inside\n\npub fn plain(a: u8) -> u8 {\n return a + 1;\n}\n\n// t27o: refused fn uses_ext (line 11): uses refused `ext` at line 11\n\npub fn uses_ext() -> u8 {\n}\n\npub fn uses_lib() -> u8 {\n return plain(1);\n}\n\n// t27o: refused fn t (line 16): uses refused `ext` at line 18\n\n")); - assert(st[NOK] == 2); - assert(st[NTEST] == 0); -} - -/// `t27a::ASM_NO_MNEMONIC =>` was written `t27a_ASM_NO_MNEMONIC` (#10905). -test a_match_on_consts_of_a_gen_module_reads_module_colon_colon_const { - var out : [4096]u8 = undefined; - var st : [1024]usize = [0] ** 1024; - const n : usize = body("#[path = \"../gen/rust/isa/t27a.rs\"]\nmod t27a;\npub fn text(s: u32) -> &'static str {\n match s {\n t27a::ASM_NO_MNEMONIC => \"no mnemonic\",\n t27a::ASM_BAD_WORD => \"bad word\",\n _ => \"rejected\",\n }\n}", &out, &st); - assert(out_is(n, &out, 0, "use tri::t27b::type_text;\nuse isa::t27a;\n\npub fn text(s: u32) -> []const u8 {\n if (s == t27a::ASM_NO_MNEMONIC) {\n return \"no mnemonic\";\n } else if (s == t27a::ASM_BAD_WORD) {\n return \"bad word\";\n } else {\n return \"rejected\";\n }\n}\n\n")); - assert(st[NOK] == 1); - assert(st[NTEST] == 0); +test slice_const_writes_a_slice_const_set_from_a_list_as_an_array { + var out : [64]u8 = undefined; + var st : [256]usize = [0] ** 256; + const src : str = "const NAMES: &[&str] = &[\"a\", \"b\"];\nconst REP: &[u8] = &[0; 3];\nconst ARR: [u8; 2] = [1, 2];\nconst HELD: &[&str] = &[\"x\"];\nfn f() { g(HELD); }\nconst U: &'static [u16] = &[1, 2, 3];\nconst E: &[u8] = &[];"; + st[P] = 13; + assert(slice_const(src, &st, &out, 6, 11)); + assert(out_is(st[W], &out, 0, "[2][]const u8")); + assert(st[P] == 21); + clear(&st); + st[P] = 152; + assert(slice_const(src, &st, &out, 149, 150)); + assert(out_is(st[W], &out, 0, "[3]u16")); + clear(&st); + st[P] = 47; + assert(!slice_const(src, &st, &out, 42, 45)); + assert(st[W] == 0); + clear(&st); + st[P] = 75; + assert(!slice_const(src, &st, &out, 70, 73)); + clear(&st); + st[P] = 105; + assert(slice_const(src, &st, &out, 99, 103)); + assert(st[ERR] == R_TYPE); + clear(&st); + st[P] = 190; + assert(!slice_const(src, &st, &out, 187, 188)); } -/// sha256 declares a `same` beside type_text's (#10556), operator_text has a `use` of its own (#10843). -test a_call_into_a_module_t27c_cannot_splice_is_refused { - var out : [4096]u8 = undefined; - var st : [1024]usize = [0] ** 1024; - const n : usize = body("#[path = \"../gen/rust/tri/crypto/sha256.rs\"]\nmod sha256;\n#[path = \"../gen/rust/compiler/operator_text.rs\"]\nmod operator_text;\npub fn digest(a: &[u8]) -> bool { sha256::hash(a).len() == 32 }\npub fn op(a: &[u8]) -> bool { operator_text::compound_binop(a).is_some() }\npub fn is_x(a: &str) -> bool { a == \"x\" }", &out, &st); - assert(out_is(n, &out, 0, "use tri::t27b::type_text;\n\n// t27o: refused fn digest (line 5): a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records) `sha256::hash` at line 5\n\npub fn digest(a: []const u8) -> bool {\n}\n\n// t27o: refused fn op (line 6): a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records) `operator_text::compound_binop` at line 6\n\npub fn op(a: []const u8) -> bool {\n}\n\npub fn is_x(a: []const u8) -> bool {\n return type_text::same(a, \"x\");\n}\n\n")); - assert(st[NOK] == 1); - assert(st[NTEST] == 0); +test let_array_writes_a_typed_array_for_a_list_or_a_repeat_and_falls_back_or_refuses_when_the_type_is_unknown { + var out : [128]u8 = undefined; + var st : [256]usize = [0] ** 256; + const src : str = "enum E { A, B }\nfn f() {\n let c = [E::A, E::B];\n let mut s = [0.0f64; N];\n let u = [1, 2];\n let z = [];\n let w = [1u8; 3];\n}"; + clear(&st); + st[FLO] = 16; + st[P] = 34; + assert(let_array(src, &st, &out, false, 33, 34, 37, false) == true); + assert(out_is(st[W], &out, 0, "const c : [2]E = [E.A, E.B];")); + clear(&st); + st[FLO] = 16; + st[P] = 64; + assert(let_array(src, &st, &out, true, 63, 64, 67, true) == true); + assert(out_is(st[W], &out, 0, "var s : [N]f64 = [@as(f64, 0.0)] ** N;")); + clear(&st); + st[FLO] = 16; + st[P] = 89; + assert(let_array(src, &st, &out, false, 88, 89, 92, false) == false); + assert(st[W] == 0); + clear(&st); + st[FLO] = 16; + st[P] = 89; + assert(let_array(src, &st, &out, false, 88, 89, 92, true) == true); + assert(st[W] == 0); + assert(st[ERR] == R_VECTYPE); + clear(&st); + st[FLO] = 16; + st[P] = 109; + assert(let_array(src, &st, &out, false, 108, 109, 112, false) == false); + assert(st[W] == 0); + clear(&st); + st[FLO] = 16; + st[P] = 125; + assert(let_array(src, &st, &out, false, 124, 125, 128, false) == true); + assert(out_is(st[W], &out, 0, "const w : [3]u8 = [@as(u8, 1)] ** 3;")); } /// t27c writes the std import and the assert helper only with a test block, so without one the fn is refused (#10905: `__t27_assert_fail`). @@ -16487,13 +17482,13 @@ test put_docs_writes_the_doc_lines_above_an_item_and_nothing_else { } test put_reason_names_the_refusal_and_the_text_it_points_at { - const codes : [40]usize = [R_SYNTAX, R_GENERIC, R_TRAIT, R_CLOSURE, R_TRY, R_MACRO, R_TYPE, R_METHOD, R_TUPLE, R_IFLET, R_ITER, R_PATTERN, R_MUTREF, R_DEREF, R_VALUE, R_LITERAL, R_LABEL, R_ITEM, R_UNSAFE, R_PATH, R_WHERE, R_SELFMUT, R_UPDATE, R_DATA, R_DEPENDS, R_EXTERNAL, R_UNDECL, R_CLASH, R_STRUCTEQ, R_STRUSE, R_SPLICE, R_EMPTY, R_NOTYPE, R_VECMUT, R_VECMOVE, R_VECTYPE, R_LIBM, R_SLICEGEN, R_SHIFTCALL, R_NOPRELUDE]; - const why : [40]str = ["syntax", "generics", "trait impl", "closure", "? operator", "macro", "type", "method", "tuple", "if let / while let", "for over an iterator", "match pattern", "&mut of a value", "deref", "block as a value", "literal", "label or break value", "item", "unsafe", "path", "where clause", "mut self", "struct update", "enum variant with data", "uses refused", "uses external", "undeclared", "a used module's name, declared here too (use splices flat, see #7176)", "== of structs", "an owned String outside the out-buffer form", "a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records)", "empty slice with no known element type", "`!` of an operand whose type t27o cannot tell", "a vec! the fn changes (push, resize, assign into, &mut)", "a vec! the fn hands on or returns", "a vec! whose element type or length t27o cannot tell", "libm function not bit-exact", "slice contains on", "a compound shift of a place that calls something", "an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one)"]; + const codes : [43]usize = [R_SYNTAX, R_GENERIC, R_TRAIT, R_CLOSURE, R_TRY, R_MACRO, R_TYPE, R_METHOD, R_TUPLE, R_IFLET, R_ITER, R_PATTERN, R_MUTREF, R_DEREF, R_VALUE, R_LITERAL, R_LABEL, R_ITEM, R_UNSAFE, R_PATH, R_WHERE, R_SELFMUT, R_UPDATE, R_DATA, R_DEPENDS, R_EXTERNAL, R_UNDECL, R_CLASH, R_STRUCTEQ, R_STRUSE, R_SPLICE, R_EMPTY, R_NOTYPE, R_VECMUT, R_VECMOVE, R_VECTYPE, R_LIBM, R_SLICEGEN, R_SHIFTCALL, R_OPERAND, R_TYPENAME, R_FLOATLIT, R_NOPRELUDE]; + const why : [43]str = ["syntax", "generics", "trait impl", "closure", "? operator", "macro", "type", "method", "tuple", "if let / while let", "for over an iterator", "match pattern", "&mut of a value", "deref", "block as a value", "literal", "label or break value", "item", "unsafe", "path", "where clause", "mut self", "struct update", "enum variant with data", "uses refused", "uses external", "undeclared", "a used module's name, declared here too (use splices flat, see #7176)", "== of structs", "an owned String outside the out-buffer form", "a used module t27c cannot splice beside type_text (its own use stays unresolved, or its same clashes with type_text's, the gap specs/compiler/int_type_names.t27 records)", "empty slice with no known element type", "`!` of an operand whose type t27o cannot tell", "a vec! the fn changes (push, resize, assign into, &mut)", "a vec! the fn hands on or returns", "a vec! or array literal whose element type or length t27o cannot tell", "libm function not bit-exact", "slice contains on", "a compound shift of a place that calls something", "an operator, cast, borrow or deref t27 has no form for on an enum, struct, array or text", "a type named like one t27c renames (GF16, str, int ..)", "a float literal in a var of no type, f32 or f64 left to inference", "an assert or a comparison of texts in a spec with no test block (t27c writes the std import and assert helper with the first one)"]; var out : [512]u8 = undefined; var st : [64]usize = [0] ** 64; var w : [512]u8 = [0] ** 512; var i : usize = 0; - while (i < 40) { + while (i < 43) { clear(&st); st[ERR] = codes[i]; st[AT] = 3;