diff --git a/.trinity/seals/t27o_T27oRust.json b/.trinity/seals/t27o_T27oRust.json index d83a4cf3e2..322d36886f 100644 --- a/.trinity/seals/t27o_T27oRust.json +++ b/.trinity/seals/t27o_T27oRust.json @@ -2,23 +2,23 @@ "built_by": "t27c-bootstrap@0.5.2+9b556b7c3", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:e61cbefe9d5600f5cce1f93821ae49c128548c11dd97e158d5502bd9aae64691", - "gen_hash_rust": "sha256:ed93b8afe9a382abe392f7972b24f150c8d8596daba8321889acf8875638e74b", - "gen_hash_verilog": "sha256:c55a89b710d4317f1b1c0f0108dc9a3ecc45afe5eeb7bed185f58aae1d88c099", - "gen_hash_zig": "sha256:6e8870fffd8349958695106cb01d9ecc3884eb4d66de37eed098bd8bf1c9fa2d", + "gen_hash_c": "sha256:992a3aae9613f106e5ef96634631f25e3cbaf1924b919db17097a699cac44405", + "gen_hash_rust": "sha256:b69954a3177ccf3a49f2a50a4f444d12f5fb813f90f9cfd9ed5e8e8ff15ba947", + "gen_hash_verilog": "sha256:403012be37fecdf33b1b14c2c6f6d3a2b10dc3f73967c3eebfb2e44a6bfc81a2", + "gen_hash_zig": "sha256:a51ff5fd83d5b31fb956180e66c61d77ad8abbc051af1fbb233e0c92326409da", "module": "T27oRust", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T18:59:54Z", + "sealed_at": "2026-10-18T19:45:24Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:811b808e8cee4a8ab3576a3be5e23832e1bdf139df02dbb356dd313ee5f194c8", + "spec_hash": "sha256:686ee4ab880eca4708d8b9887f380f9f4ea876d2afbb4c47d4029d0944117e69", "spec_path": "specs/tools/t27o/rust_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 90, - "total": 90, + "passed": 101, + "total": 101, "vacuous": 0 }, "toolchain": { diff --git a/gen/rust/tools/t27o/rust_convert.rs b/gen/rust/tools/t27o/rust_convert.rs index ccbd99d425..d744a5993f 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: 90 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 101 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. @@ -108,9 +108,23 @@ pub const CCNT: usize = 50; pub const DCAP: usize = 64; -pub const STATE: usize = 51; +pub const LNPOS: usize = 51; -pub const NAMES: usize = 51; +pub const LNNL: usize = 52; + +pub const STATE: usize = 53; + +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 NAME_SLOTS: usize = 5; @@ -652,6 +666,28 @@ pub fn line_of(src: &[u8], pos: usize) -> usize { return n; } +pub fn line_at(src: &[u8], st: &mut [usize], pos: usize) -> usize { + let mut i: usize = 0; + let mut n: usize = 0; + if (st[(LNPOS) as usize] <= pos) { + i = st[(LNPOS) as usize]; + n = st[(LNNL) as usize]; + } + let mut end: usize = pos; + if (end > src.len()) { + end = src.len(); + } + while (i < end) { + if (src[(i) as usize] == B_NL) { + n = (n + 1); + } + i = (i + 1); + } + st[(LNPOS) as usize] = end; + st[(LNNL) as usize] = n; + return (n + 1); +} + pub fn block_end(src: &[u8], o: usize) -> usize { let mut depth: usize = 0; let mut q: usize = o; @@ -696,9 +732,13 @@ pub fn find_at(src: &[u8], from: usize, c: u8) -> usize { } pub fn find_tok(src: &[u8], from: usize, s: &[u8]) -> usize { + return find_tok_to(src, from, src.len(), s); +} + +pub fn find_tok_to(src: &[u8], from: usize, hi: usize, s: &[u8]) -> usize { let mut depth: usize = 0; let mut q: usize = from; - while (q < src.len()) { + while ((q < src.len()) && (q < hi)) { let e: usize = tok_end(src, q); if ((depth == 0) && word_is(src, q, e, s)) { return q; @@ -903,7 +943,11 @@ pub fn add_name(src: &[u8], st: &mut [usize], kind: usize, lo: usize, hi: usize, k = (k + 1); } let e: usize = (NAMES + (st[(NNAMES) as usize] * NAME_SLOTS)); - if ((e + NAME_SLOTS) > st.len()) { + let mut lim: usize = st.len(); + if (st[(IXON) as usize] == 1) { + lim = st[(IXTOP) as usize]; + } + if ((e + NAME_SLOTS) > lim) { return (); } st[(e) as usize] = lo; @@ -942,6 +986,268 @@ pub fn strloc_in(src: &[u8], st: &mut [usize], a: usize, b: usize) -> bool { return false; } +pub const IXK_ITEM: usize = 0; + +pub const IXK_FN: usize = 1; + +pub const IXK_LET: usize = 2; + +pub const IXK_KINDS: usize = 3; + +pub const IX_SLOTS: usize = 4; + +pub fn ix_hash(src: &[u8], a: usize, b: usize) -> usize { + let mut h: usize = 0; + let mut k: usize = a; + while (k < b) { + h = ((((h * 31) + (src[(k) as usize] as usize)) + 1) % 1000003); + k = (k + 1); + } + return h; +} + +pub fn ix_link(st: &mut [usize], head: usize, e: usize) -> () { + let mut s: usize = st[(head) as usize]; + if (s == 0) { + st[(head) as usize] = e; + return (); + } + while (st[((s + 3)) as usize] != 0) { + s = st[((s + 3)) as usize]; + } + st[((s + 3)) as usize] = e; +} + +pub fn ix_enter(st: &mut [usize], head: usize, lo: usize, hi: usize, q: usize) -> bool { + if (st[(IXTOP) as usize] < ((NAMES + ((st.len() - NAMES) / 2)) + IX_SLOTS)) { + return false; + } + let e: usize = (st[(IXTOP) as usize] - IX_SLOTS); + st[(e) as usize] = lo; + st[((e + 1)) as usize] = hi; + st[((e + 2)) as usize] = q; + st[((e + 3)) as usize] = 0; + ix_link(st, head, e); + st[(IXTOP) as usize] = e; + return true; +} + +pub fn ix_add(src: &[u8], st: &mut [usize], kind: usize, lo: usize, hi: usize, q: usize) -> bool { + if (hi <= lo) { + return true; + } + let head: usize = ((st[(IXB) as usize] + (kind * st[(IXBN) as usize])) + (ix_hash(src, lo, hi) % st[(IXBN) as usize])); + return ix_enter(st, head, lo, hi, q); +} + +pub fn ix_first(src: &[u8], st: &mut [usize], kind: usize, a: usize, b: usize) -> usize { + st[(IXHIT) as usize] = 0; + if (b <= a) { + return 0; + } + let mut s: usize = st[(((st[(IXB) as usize] + (kind * st[(IXBN) as usize])) + (ix_hash(src, a, b) % st[(IXBN) as usize]))) as usize]; + while ((s != 0) && !(same_text(src, st[(s) as usize], st[((s + 1)) as usize], a, b))) { + s = st[((s + 3)) as usize]; + } + st[(IXHIT) as usize] = s; + return s; +} + +pub fn ix_next(src: &[u8], st: &mut [usize], s0: usize, a: usize, b: usize) -> usize { + let mut s: usize = st[((s0 + 3)) as usize]; + while ((s != 0) && !(same_text(src, st[(s) as usize], st[((s + 1)) as usize], a, b))) { + s = st[((s + 3)) as usize]; + } + st[(IXHIT) as usize] = s; + return s; +} + +pub fn item_kw(src: &[u8], q: usize, e: usize) -> bool { + return ((((((word_is(src, q, e, b"struct") || word_is(src, q, e, b"enum")) || word_is(src, q, e, b"type")) || word_is(src, q, e, b"trait")) || word_is(src, q, e, b"union")) || word_is(src, q, e, b"const")) || word_is(src, q, e, b"static")); +} + +pub fn let_at(src: &[u8], q: usize, ra: usize, rb: usize) -> usize { + let mut best: usize = src.len(); + let e: usize = tok_end(src, q); + if word_is(src, q, 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)); + } + let ne: usize = tok_end(src, n); + if same_text(src, n, ne, ra, rb) { + let mut t: usize = skip_ws(src, ne); + if tok_is(src, t, b":") { + t = skip_ws(src, tok_end(src, t)); + while (tok_is(src, t, b"&") || tok_is(src, t, b"mut")) { + t = skip_ws(src, tok_end(src, t)); + } + let te: usize = tok_end(src, t); + if (((t < src.len()) && is_upper(src[(t) as usize])) && tok_is(src, skip_ws(src, te), b"=")) { + best = t; + } + } + if tok_is(src, t, b"=") { + let mut v: usize = skip_ws(src, tok_end(src, t)); + while (tok_is(src, v, b"&") || tok_is(src, v, b"mut")) { + v = skip_ws(src, tok_end(src, v)); + } + let ve: usize = tok_end(src, v); + if ((v < src.len()) && is_upper(src[(v) as usize])) { + let nx: usize = skip_ws(src, ve); + if ((tok_is(src, nx, b"::") || (src[(nx) as usize] == B_LBRACE)) || (src[(nx) as usize] == B_LPAREN)) { + best = v; + } + } + } + } + } + return best; +} + +pub fn index_decls(src: &[u8], st: &mut [usize]) -> () { + st[(IXON) as usize] = 0; + st[(IXHIT) as usize] = 0; + st[(IXTOP) as usize] = st.len(); + if (st.len() < (NAMES + 64)) { + return (); + } + let mut bn: usize = (src.len() / 512); + if (bn > ((st.len() - NAMES) / 64)) { + bn = ((st.len() - NAMES) / 64); + } + if (bn < 1) { + bn = 1; + } + let top: usize = ((st.len() - (IXK_KINDS * bn)) - 1); + let mut z: usize = top; + while (z < st.len()) { + st[(z) as usize] = 0; + z = (z + 1); + } + st[(IXB) as usize] = top; + st[(IXBN) as usize] = bn; + st[(IXTOP) as usize] = top; + let impls: usize = (top + (IXK_KINDS * bn)); + let mut whole: bool = true; + let mut q: usize = skip_ws(src, 0); + while ((q < src.len()) && whole) { + let e: usize = tok_end(src, q); + if item_kw(src, q, e) { + let mut n: usize = skip_ws(src, e); + if !(ix_add(src, st, IXK_ITEM, n, tok_end(src, n), q)) { + whole = false; + } + if ((word_is(src, q, e, b"const") || word_is(src, q, e, b"static")) && tok_is(src, n, b"mut")) { + let m: usize = skip_ws(src, tok_end(src, n)); + if !(ix_add(src, st, IXK_ITEM, m, tok_end(src, m), q)) { + whole = false; + } + } + } else { + if word_is(src, q, e, b"fn") { + let mut n: usize = skip_ws(src, e); + if !(ix_add(src, st, IXK_FN, n, tok_end(src, n), q)) { + whole = false; + } + } else { + if word_is(src, q, 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)); + } + let ne: usize = tok_end(src, n); + if ((let_at(src, q, n, ne) < src.len()) && !(ix_add(src, st, IXK_LET, n, ne, q))) { + whole = false; + } + } else { + if word_is(src, q, e, b"impl") { + let t: usize = skip_ws(src, e); + let o: usize = skip_ws(src, tok_end(src, t)); + let mut lb: usize = 0; + let mut hb: usize = 0; + if ((((t < src.len()) && is_alpha(src[(t) as usize])) && (o < src.len())) && (src[(o) as usize] == B_LBRACE)) { + lb = o; + hb = block_end(src, o); + } + if !(ix_enter(st, impls, lb, hb, q)) { + whole = false; + } + } + } + } + } + q = skip_ws(src, e); + } + if whole { + st[(IXON) as usize] = 1; + } else { + st[(IXTOP) as usize] = st.len(); + } +} + +pub fn lookups_agree(src: &[u8], st: &mut [usize], a: usize, b: usize) -> bool { + let on: usize = st[(IXON) as usize]; + let fs: usize = find_struct(src, st, a, b); + let so: usize = struct_open(src, st, a, b); + let ct: usize = const_type(src, st, a, b); + let fr: usize = fn_ret_type(src, st, a, b); + let rc: u8 = ret_class(src, st, a, b); + let lo: usize = let_owner(src, st, a, b); + let dt: bool = declared_type(src, st, a, b); + let df: bool = declared_fn(src, st, a, b); + st[(IXON) as usize] = 0; + let mut same: bool = (((fs == find_struct(src, st, a, b)) && (so == struct_open(src, st, a, b))) && (ct == const_type(src, st, a, b))); + same = (((same && (fr == fn_ret_type(src, st, a, b))) && (rc == ret_class(src, st, a, b))) && (lo == let_owner(src, st, a, b))); + same = ((same && (dt == declared_type(src, st, a, b))) && (df == declared_fn(src, st, a, b))); + st[(IXON) as usize] = on; + return same; +} + +pub fn owners_agree(src: &[u8], st: &mut [usize], na: usize, nb: usize, ola: usize, ohb: usize) -> bool { + let on: usize = st[(IXON) as usize]; + let m1: bool = method_owner(src, st, na, nb, ola, ohb); + let lo1: usize = st[(OLO) as usize]; + let hi1: usize = st[(OHI) as usize]; + let k1: usize = st[(OKIND) as usize]; + let i1: bool = impl_around(src, st, na); + let ilo1: usize = st[(OLO) as usize]; + let ihi1: usize = st[(OHI) as usize]; + st[(IXON) as usize] = 0; + let m0: bool = method_owner(src, st, na, nb, ola, ohb); + let lo0: usize = st[(OLO) as usize]; + let hi0: usize = st[(OHI) as usize]; + let k0: usize = st[(OKIND) as usize]; + let i0: bool = impl_around(src, st, na); + st[(IXON) as usize] = on; + return (((((m1 == m0) && (i1 == i0)) && (ilo1 == st[(OLO) as usize])) && (ihi1 == st[(OHI) as usize])) && (!(m1) || (((lo1 == lo0) && (hi1 == hi0)) && (k1 == k0)))); +} + +pub fn all_names_agree(src: &[u8], st: &mut [usize]) -> bool { + let mut q: usize = skip_ws(src, 0); + while (q < src.len()) { + let e: usize = tok_end(src, q); + if is_alpha(src[(q) as usize]) { + if !(lookups_agree(src, st, q, e)) { + return false; + } + if !(owners_agree(src, st, q, e, 0, 0)) { + return false; + } + let mut r: usize = skip_ws(src, 0); + while (r < src.len()) { + let re: usize = tok_end(src, r); + if (is_alpha(src[(r) as usize]) && !(owners_agree(src, st, q, e, r, re))) { + return false; + } + r = skip_ws(src, re); + } + } + q = skip_ws(src, e); + } + return true; +} + pub fn record_fns(src: &[u8], st: &mut [usize], lo: usize, hi: usize, olo: usize, ohi: usize) -> () { let mut q: usize = lo; while (q < hi) { @@ -974,38 +1280,52 @@ pub fn use_names(src: &[u8], st: &mut [usize], u: usize, end: usize) -> () { } } -pub fn declared_fn(src: &[u8], a: usize, b: usize) -> bool { +pub fn declared_fn(src: &[u8], st: &mut [usize], a: usize, b: usize) -> bool { + if (st[(IXON) as usize] == 1) { + return (ix_first(src, st, IXK_FN, a, b) != 0); + } 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, b"fn") { - let n: usize = skip_ws(src, e); - if same_text(src, n, tok_end(src, n), a, b) { - return true; - } + if fn_named(src, q, a, b) { + return true; } - q = skip_ws(src, e); + q = skip_ws(src, tok_end(src, q)); } return false; } -pub fn declared_type(src: &[u8], a: usize, b: usize) -> bool { +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)) { + return false; + } + let n: usize = skip_ws(src, e); + return same_text(src, n, tok_end(src, n), a, b); +} + +pub fn declared_type(src: &[u8], st: &mut [usize], a: usize, b: usize) -> bool { if (((word_is(src, a, b, b"Self") || word_is(src, a, b, b"Option")) || word_is(src, a, b, b"Some")) || word_is(src, a, b, b"None")) { return true; } if ((((word_is(src, a, b, b"Result") || word_is(src, a, b, b"Ok")) || word_is(src, a, b, b"Err")) || word_is(src, a, b, b"String")) || word_is(src, a, b, b"Vec")) { return true; } - 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, b"struct") || word_is(src, q, e, b"enum")) || word_is(src, q, e, b"type")) || word_is(src, q, e, b"trait")) || word_is(src, q, e, b"union")) || word_is(src, q, e, b"const")) || word_is(src, q, e, b"static")) { - let n: usize = skip_ws(src, e); - if same_text(src, n, tok_end(src, n), a, b) { + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + if item_named(src, st[((s + 2)) as usize], a, b) { return true; } + s = ix_next(src, st, s, a, b); } - q = skip_ws(src, e); + return false; + } + let mut q: usize = skip_ws(src, 0); + while (q < src.len()) { + if item_named(src, q, a, b) { + return true; + } + q = skip_ws(src, tok_end(src, q)); } return false; } @@ -1048,7 +1368,7 @@ pub fn deps(src: &[u8], st: &mut [usize], lo: usize, hi: usize) -> () { } k0 = (k0 + 1); } - if (((st[(ILO) as usize] != st[(IHI) as usize]) && (st[(ERR) as usize] == 0)) && !(declared_type(src, st[(ILO) as usize], st[(IHI) as usize]))) { + if (((st[(ILO) as usize] != st[(IHI) as usize]) && (st[(ERR) as usize] == 0)) && !(declared_type(src, st, st[(ILO) as usize], st[(IHI) as usize]))) { refuse(st, R_UNDECL, st[(ILO) as usize], st[(ILO) as usize], st[(IHI) as usize]); } if self_bad { @@ -1102,15 +1422,15 @@ pub fn deps(src: &[u8], st: &mut [usize], lo: usize, hi: usize) -> () { if (((st[(ERR) as usize] == 0) && self_bad) && word_is(src, q, e, b"Self")) { let c1: usize = skip_ws(src, e); let c2: usize = skip_ws(src, tok_end(src, c1)); - let self_call: bool = (((((tok_is(src, c1, b"::") && (c2 < src.len())) && is_alpha(src[(c2) as usize])) && !(is_upper(src[(c2) as usize]))) && tok_is(src, skip_ws(src, tok_end(src, c2)), b"(")) && declared_fn(src, c2, tok_end(src, c2))); + let self_call: bool = (((((tok_is(src, c1, b"::") && (c2 < src.len())) && is_alpha(src[(c2) as usize])) && !(is_upper(src[(c2) as usize]))) && tok_is(src, skip_ws(src, tok_end(src, c2)), b"(")) && declared_fn(src, st, c2, tok_end(src, c2))); if !(self_call) { refuse(st, R_DEPENDS, q, q, e); } } - if ((((((((st[(ERR) as usize] == 0) && call) && !(after_dot)) && !(after_path)) && !(after_fn)) && !(is_upper(src[(q) as usize]))) && !(call_keyword(src, q, e))) && !(declared_fn(src, q, e))) { + if ((((((((st[(ERR) as usize] == 0) && call) && !(after_dot)) && !(after_path)) && !(after_fn)) && !(is_upper(src[(q) as usize]))) && !(call_keyword(src, q, e))) && !(declared_fn(src, st, q, e))) { refuse(st, R_UNDECL, q, q, e); } - if ((((((st[(ERR) as usize] == 0) && is_upper(src[(q) as usize])) && !(after_dot)) && !(after_path)) && !(prim(src, q, e))) && !(declared_type(src, q, e))) { + if ((((((st[(ERR) as usize] == 0) && is_upper(src[(q) as usize])) && !(after_dot)) && !(after_path)) && !(prim(src, q, e))) && !(declared_type(src, st, q, e))) { refuse(st, R_UNDECL, q, q, e); } } @@ -1979,111 +2299,134 @@ pub fn no_args(src: &[u8], st: &mut [usize]) -> () { want(src, st, b")"); } -pub fn method_kind(src: &[u8], lo: usize, hi: usize, na: usize, nb: usize) -> 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); - let ne: usize = tok_end(src, n); - let lp: usize = skip_ws(src, ne); - if (((((ne - n) == (nb - na)) && same_at(src, n, ne, na)) && (lp < hi)) && (src[(lp) as usize] == B_LPAREN)) { - let mut s: usize = skip_ws(src, (lp + 1)); - let mut amp: bool = false; - let mut mutable: bool = false; - if ((s < hi) && (src[(s) as usize] == B_AMP)) { - amp = true; - s = skip_ws(src, (s + 1)); - } - if ((s < hi) && (src[(s) as usize] == B_APOS)) { - s = skip_ws(src, tok_end(src, s)); - } - if tok_is(src, s, b"mut") { - mutable = true; - s = skip_ws(src, tok_end(src, s)); - } - if ((tok_is(src, s, b"self") && amp) && mutable) { - return 2; - } - if tok_is(src, s, b"self") { - return 1; +pub fn kind_at(src: &[u8], q: usize, hi: usize, na: usize, nb: usize) -> usize { + let e: usize = tok_end(src, q); + if word_is(src, q, e, b"fn") { + let n: usize = skip_ws(src, e); + let ne: usize = tok_end(src, n); + let lp: usize = skip_ws(src, ne); + if (((((ne - n) == (nb - na)) && same_at(src, n, ne, na)) && (lp < hi)) && (src[(lp) as usize] == B_LPAREN)) { + let mut s: usize = skip_ws(src, (lp + 1)); + let mut amp: bool = false; + let mut mutable: bool = false; + if ((s < hi) && (src[(s) as usize] == B_AMP)) { + amp = true; + s = skip_ws(src, (s + 1)); + } + if ((s < hi) && (src[(s) as usize] == B_APOS)) { + s = skip_ws(src, tok_end(src, s)); + } + if tok_is(src, s, b"mut") { + mutable = true; + s = skip_ws(src, tok_end(src, s)); + } + if ((tok_is(src, s, b"self") && amp) && mutable) { + return 2; + } + if tok_is(src, s, b"self") { + return 1; + } + } + } + return 0; +} + +pub fn method_kind(src: &[u8], st: &mut [usize], lo: usize, hi: usize, na: usize, nb: usize) -> usize { + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_FN, na, nb); + while ((s != 0) && (st[((s + 2)) as usize] < hi)) { + if (st[((s + 2)) as usize] >= lo) { + let k: usize = kind_at(src, st[((s + 2)) as usize], hi, na, nb); + if (k != 0) { + return k; } } + s = ix_next(src, st, s, na, nb); } - q = skip_ws(src, e); + return 0; + } + let mut q: usize = lo; + while (q < hi) { + let k: usize = kind_at(src, q, hi, na, nb); + if (k != 0) { + return k; + } + q = skip_ws(src, tok_end(src, q)); } return 0; } -pub fn let_owner(src: &[u8], ra: usize, rb: usize) -> usize { +pub fn let_owner(src: &[u8], st: &mut [usize], ra: usize, rb: usize) -> usize { let mut best: usize = src.len(); + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_LET, ra, rb); + while ((s != 0) && (st[((s + 2)) as usize] < ra)) { + let v: usize = let_at(src, st[((s + 2)) as usize], ra, rb); + if (v < src.len()) { + best = v; + } + s = ix_next(src, st, s, ra, rb); + } + return best; + } let mut q: usize = skip_ws(src, 0); while ((q < src.len()) && (q < ra)) { - let e: usize = tok_end(src, q); - if word_is(src, q, 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)); - } - let ne: usize = tok_end(src, n); - if same_text(src, n, ne, ra, rb) { - let mut t: usize = skip_ws(src, ne); - if tok_is(src, t, b":") { - t = skip_ws(src, tok_end(src, t)); - while (tok_is(src, t, b"&") || tok_is(src, t, b"mut")) { - t = skip_ws(src, tok_end(src, t)); - } - let te: usize = tok_end(src, t); - if (((t < src.len()) && is_upper(src[(t) as usize])) && tok_is(src, skip_ws(src, te), b"=")) { - best = t; - } - } - if tok_is(src, t, b"=") { - let mut v: usize = skip_ws(src, tok_end(src, t)); - while (tok_is(src, v, b"&") || tok_is(src, v, b"mut")) { - v = skip_ws(src, tok_end(src, v)); - } - let ve: usize = tok_end(src, v); - if ((v < src.len()) && is_upper(src[(v) as usize])) { - let nx: usize = skip_ws(src, ve); - if ((tok_is(src, nx, b"::") || (src[(nx) as usize] == B_LBRACE)) || (src[(nx) as usize] == B_LPAREN)) { - best = v; - } - } - } - } + let v: usize = let_at(src, q, ra, rb); + if (v < src.len()) { + best = v; } - q = skip_ws(src, e); + q = skip_ws(src, tok_end(src, q)); } return best; } +pub fn impl_owner_at(src: &[u8], st: &mut [usize], q: usize, pass: usize, na: usize, nb: usize, ola: usize, ohb: usize, hi: usize) -> bool { + let e: usize = tok_end(src, q); + if !(word_is(src, q, e, b"impl")) { + return false; + } + let t: usize = skip_ws(src, e); + let te: usize = tok_end(src, t); + let o: usize = skip_ws(src, te); + if ((((t >= src.len()) || !(is_alpha(src[(t) as usize]))) || (o >= src.len())) || (src[(o) as usize] != B_LBRACE)) { + return false; + } + if (((pass == 0) && (ola < ohb)) && !(same_text(src, t, te, ola, ohb))) { + return false; + } + let mut end: usize = hi; + if (end == 0) { + end = block_end(src, o); + } + let kind: usize = method_kind(src, st, o, end, na, nb); + if (kind == 0) { + return false; + } + st[(OLO) as usize] = t; + st[(OHI) as usize] = te; + st[(OKIND) as usize] = kind; + return true; +} + pub fn method_owner(src: &[u8], st: &mut [usize], na: usize, nb: usize, ola: usize, ohb: usize) -> bool { let mut pass: usize = 0; while (pass < 2) { - 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, b"impl") { - let t: usize = skip_ws(src, e); - let te: usize = tok_end(src, t); - let o: usize = skip_ws(src, te); - if ((((t < src.len()) && is_alpha(src[(t) as usize])) && (o < src.len())) && (src[(o) as usize] == B_LBRACE)) { - let own: bool = (((pass == 0) && (ola < ohb)) && same_text(src, t, te, ola, ohb)); - if (((pass == 0) && (ola < ohb)) && !(own)) { - q = skip_ws(src, e); - continue; - } - let kind: usize = method_kind(src, o, block_end(src, o), na, nb); - if (kind != 0) { - st[(OLO) as usize] = t; - st[(OHI) as usize] = te; - st[(OKIND) as usize] = kind; - return true; - } + if (st[(IXON) as usize] == 1) { + let mut s: usize = st[((st[(IXB) as usize] + (IXK_KINDS * st[(IXBN) as usize]))) as usize]; + while (s != 0) { + if ((st[((s + 1)) as usize] != 0) && impl_owner_at(src, st, st[((s + 2)) as usize], pass, na, nb, ola, ohb, st[((s + 1)) as usize])) { + return true; } + s = st[((s + 3)) as usize]; + } + } else { + let mut q: usize = skip_ws(src, 0); + while (q < src.len()) { + if impl_owner_at(src, st, q, pass, na, nb, ola, ohb, 0) { + return true; + } + q = skip_ws(src, tok_end(src, q)); } - q = skip_ws(src, e); } if (ola >= ohb) { return false; @@ -2093,49 +2436,102 @@ pub fn method_owner(src: &[u8], st: &mut [usize], na: usize, nb: usize, ola: usi return false; } -pub fn const_type(src: &[u8], a: usize, b: usize) -> usize { +pub fn const_hit(src: &[u8], q: usize, a: usize, b: usize) -> bool { + let e: usize = tok_end(src, q); + if !((word_is(src, q, e, b"const") || word_is(src, q, e, b"static"))) { + return false; + } + let mut n: usize = skip_ws(src, e); + if tok_is(src, n, b"mut") { + n = skip_ws(src, tok_end(src, n)); + } + 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 const_type(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + let mut q: usize = st[((s + 2)) as usize]; + if const_hit(src, q, a, b) { + return const_of(src, q); + } + s = ix_next(src, st, s, a, b); + } + return src.len(); + } 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, b"const") || word_is(src, q, e, b"static")) { - let mut n: usize = skip_ws(src, e); - if tok_is(src, n, b"mut") { - n = skip_ws(src, tok_end(src, n)); - } - let ne: usize = tok_end(src, n); - let c: usize = skip_ws(src, ne); - if (same_text(src, n, ne, a, b) && tok_is(src, c, b":")) { - return skip_ws(src, (c + 1)); - } + if const_hit(src, q, a, b) { + return const_of(src, q); } - q = skip_ws(src, e); + q = skip_ws(src, tok_end(src, q)); } return src.len(); } -pub fn fn_ret_type(src: &[u8], a: usize, b: usize) -> usize { +pub fn const_of(src: &[u8], q: usize) -> usize { + let mut n: usize = skip_ws(src, tok_end(src, q)); + if tok_is(src, n, b"mut") { + n = skip_ws(src, tok_end(src, n)); + } + return skip_ws(src, (skip_ws(src, tok_end(src, n)) + 1)); +} + +pub fn fn_hit(src: &[u8], q: usize, a: usize, b: usize) -> bool { + if !(fn_named(src, q, a, b)) { + return false; + } + return (find_at(src, tok_end(src, skip_ws(src, tok_end(src, q))), B_LPAREN) < src.len()); +} + +pub fn fn_named(src: &[u8], q: usize, a: usize, b: usize) -> bool { + let e: usize = tok_end(src, q); + if !(word_is(src, q, e, b"fn")) { + return false; + } + let n: usize = skip_ws(src, e); + return same_text(src, n, tok_end(src, n), a, b); +} + +pub fn fn_ret_of(src: &[u8], q: usize) -> usize { + let n: usize = skip_ws(src, tok_end(src, q)); + let lp: usize = find_at(src, tok_end(src, n), B_LPAREN); + if (lp >= src.len()) { + return src.len(); + } + let r: usize = skip_ws(src, block_end(src, lp)); + if tok_is(src, r, b"->") { + return skip_ws(src, tok_end(src, r)); + } + return src.len(); +} + +pub fn fn_ret_type(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_FN, a, b); + while (s != 0) { + let mut q: usize = st[((s + 2)) as usize]; + if fn_hit(src, q, a, b) { + return fn_ret_of(src, q); + } + s = ix_next(src, st, s, a, b); + } + return src.len(); + } 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, b"fn") { - let n: usize = skip_ws(src, e); - let ne: usize = tok_end(src, n); - let lp: usize = find_at(src, ne, B_LPAREN); - if (same_text(src, n, ne, a, b) && (lp < src.len())) { - let r: usize = skip_ws(src, block_end(src, lp)); - if tok_is(src, r, b"->") { - return skip_ws(src, tok_end(src, r)); - } - return src.len(); - } + if fn_hit(src, q, a, b) { + return fn_ret_of(src, q); } - q = skip_ws(src, e); + q = skip_ws(src, tok_end(src, q)); } return src.len(); } pub fn unit_result_fn(src: &[u8], st: &mut [usize], a: usize, b: usize) -> bool { - let r: usize = fn_ret_type(src, a, b); + let r: usize = fn_ret_type(src, st, a, b); let re: usize = tok_end(src, r); if ((r >= src.len()) || !((word_is(src, r, re, b"Result") || named(src, st, K_RALIAS, r, re)))) { return false; @@ -2214,7 +2610,7 @@ pub fn literal_class(src: &[u8], st: &mut [usize], q: usize) -> u8 { pub fn name_class(src: &[u8], st: &mut [usize], a: usize, b: usize, field: bool) -> u8 { if field { - let lb: usize = find_struct(src, st[(ILO) as usize], st[(IHI) as usize]); + let lb: usize = find_struct(src, st, st[(ILO) as usize], st[(IHI) as usize]); if (lb >= src.len()) { return C_ANY; } @@ -2231,7 +2627,7 @@ pub fn name_class(src: &[u8], st: &mut [usize], a: usize, b: usize, field: bool) if (d < src.len()) { return type_class(src, st, d); } - let c: usize = const_type(src, a, b); + let c: usize = const_type(src, st, a, b); if (c < src.len()) { return type_class(src, st, c); } @@ -2270,7 +2666,7 @@ pub fn name_class(src: &[u8], st: &mut [usize], a: usize, b: usize, field: bool) } let ie: usize = tok_end(src, init); if tok_is(src, skip_ws(src, ie), b"(") { - let r2: usize = fn_ret_type(src, init, ie); + let r2: usize = fn_ret_type(src, st, init, ie); if (r2 < src.len()) { return type_class(src, st, r2); } @@ -2305,16 +2701,39 @@ pub fn name_class(src: &[u8], st: &mut [usize], a: usize, b: usize, field: bool) return C_ANY; } -pub fn find_struct(src: &[u8], a: usize, b: usize) -> usize { +pub fn struct_hit(src: &[u8], q: usize, a: usize, b: usize, paren: bool) -> 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); + let o: usize = skip_ws(src, ne); + return (same_text(src, n, ne, a, b) && (tok_is(src, o, b"{") || (paren && tok_is(src, o, b"(")))); +} + +pub fn struct_brace(src: &[u8], q: usize) -> usize { + return skip_ws(src, tok_end(src, skip_ws(src, tok_end(src, q)))); +} + +pub fn find_struct(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + let mut q: usize = st[((s + 2)) as usize]; + if struct_hit(src, q, a, b, false) { + return struct_brace(src, q); + } + s = ix_next(src, st, s, a, b); + } + return src.len(); + } let mut q: usize = skip_ws(src, 0); while (q < src.len()) { - let e: usize = tok_end(src, q); - let n: usize = skip_ws(src, e); - let ne: usize = tok_end(src, n); - if ((word_is(src, q, e, b"struct") && same_text(src, n, ne, a, b)) && tok_is(src, skip_ws(src, ne), b"{")) { - return skip_ws(src, ne); + if struct_hit(src, q, a, b, false) { + return struct_brace(src, q); } - q = skip_ws(src, e); + q = skip_ws(src, tok_end(src, q)); } return src.len(); } @@ -2322,7 +2741,7 @@ pub fn find_struct(src: &[u8], a: usize, b: usize) -> usize { 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()) { - d = const_type(src, xa, xe); + d = const_type(src, st, xa, xe); } if (d >= src.len()) { return C_ANY; @@ -2340,7 +2759,7 @@ pub fn field_of_class(src: &[u8], st: &mut [usize], xa: usize, xe: usize, fa: us if ((d >= src.len()) || !(is_upper(src[(d) as usize]))) { return C_ANY; } - let lb: usize = find_struct(src, d, de); + let lb: usize = find_struct(src, st, d, de); if (lb >= src.len()) { return C_ANY; } @@ -2457,7 +2876,7 @@ pub fn recv_class(src: &[u8], st: &mut [usize], ra: usize, rb: usize) -> u8 { if unit_result_fn(src, st, ra, e) { return C_BOOL; } - let r: usize = fn_ret_type(src, ra, e); + let r: usize = fn_ret_type(src, st, ra, e); if (r < src.len()) { return type_class(src, st, r); } @@ -2476,7 +2895,7 @@ pub fn recv_class(src: &[u8], st: &mut [usize], ra: usize, rb: usize) -> u8 { } else { if ((is_alpha(src[(q) as usize]) && !(tok_is(src, prev, b"."))) && !(tok_is(src, prev, b"::"))) { if tok_is(src, skip_ws(src, qe), b"(") { - let r2: usize = fn_ret_type(src, q, qe); + let r2: usize = fn_ret_type(src, st, q, qe); if (r2 < src.len()) { k = type_class(src, st, r2); } @@ -2510,30 +2929,38 @@ pub fn std_bool(src: &[u8], a: usize, b: usize) -> bool { return listed(w, &src[(a) as usize..(b) as usize]); } +pub fn ret_step(src: &[u8], st: &mut [usize], q: usize, a: usize, b: usize, cls: u8) -> u8 { + if !(fn_hit(src, q, a, b)) { + return cls; + } + let r: usize = skip_ws(src, block_end(src, find_at(src, tok_end(src, skip_ws(src, tok_end(src, q))), B_LPAREN))); + if !(tok_is(src, r, b"->")) { + return cls; + } + let c: u8 = type_class(src, st, skip_ws(src, tok_end(src, r))); + if (cls == C_NONE) { + return c; + } + if (cls != c) { + return C_ANY; + } + return cls; +} + pub fn ret_class(src: &[u8], st: &mut [usize], a: usize, b: usize) -> u8 { let mut cls: u8 = C_NONE; + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_FN, a, b); + while (s != 0) { + cls = ret_step(src, st, st[((s + 2)) as usize], a, b, cls); + s = ix_next(src, st, s, a, b); + } + return cls; + } 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, b"fn") { - let n: usize = skip_ws(src, e); - let ne: usize = tok_end(src, n); - let lp: usize = find_at(src, ne, B_LPAREN); - if (same_text(src, n, ne, a, b) && (lp < src.len())) { - let r: usize = skip_ws(src, block_end(src, lp)); - if tok_is(src, r, b"->") { - let c: u8 = type_class(src, st, skip_ws(src, tok_end(src, r))); - if (cls == C_NONE) { - cls = c; - } else { - if (cls != c) { - cls = C_ANY; - } - } - } - } - } - q = skip_ws(src, e); + cls = ret_step(src, st, q, a, b, cls); + q = skip_ws(src, tok_end(src, q)); } return cls; } @@ -2563,19 +2990,19 @@ pub fn lit_at(src: &[u8], q: usize) -> bool { return (((src[(q) as usize] == B_LOWER_B) && ((q + 1) < src.len())) && ((src[((q + 1)) as usize] == B_QUOTE) || (src[((q + 1)) as usize] == B_APOS))); } -pub fn head_struct(src: &[u8], flo: usize, ilo: usize, ihi: usize, xa: usize, xe: usize) -> usize { +pub fn head_struct(src: &[u8], st: &mut [usize], flo: usize, ilo: usize, ihi: usize, xa: usize, xe: usize) -> usize { if word_is(src, xa, xe, b"self") { if (ilo == ihi) { return src.len(); } - return struct_open(src, ilo, ihi); + return struct_open(src, st, ilo, ihi); } let mut d: usize = decl_type(src, flo, xa, xa, xe); if (d >= src.len()) { - d = const_type(src, xa, xe); + d = const_type(src, st, xa, xe); } if (d >= src.len()) { - d = let_owner(src, xa, xe); + d = let_owner(src, st, xa, xe); } if (d >= src.len()) { return src.len(); @@ -2592,7 +3019,7 @@ pub fn head_struct(src: &[u8], flo: usize, ilo: usize, ihi: usize, xa: usize, xe if ((d >= src.len()) || !(is_upper(src[(d) as usize]))) { return src.len(); } - return struct_open(src, d, tok_end(src, d)); + return struct_open(src, st, d, tok_end(src, d)); } pub fn chain_class(src: &[u8], st: &mut [usize], a: usize, b: usize, deep: bool) -> u8 { @@ -2724,7 +3151,7 @@ pub fn chain_class(src: &[u8], st: &mut [usize], a: usize, b: usize, deep: bool) return name_class(src, st, na, nb, false); } if (((kind == CK_FIELD) && (dots == 1)) && plain) { - let o: usize = head_struct(src, st[(FLO) as usize], st[(ILO) as usize], st[(IHI) as usize], xa, xe); + let o: usize = head_struct(src, st, st[(FLO) as usize], st[(ILO) as usize], st[(IHI) as usize], xa, xe); if (o >= src.len()) { return C_ANY; } @@ -2748,7 +3175,7 @@ pub fn chain_class(src: &[u8], st: &mut [usize], a: usize, b: usize, deep: bool) return type_class(src, st, f); } if (kind == CK_PATH) { - let c: usize = const_type(src, na, nb); + let c: usize = const_type(src, st, na, nb); if (c < src.len()) { return type_class(src, st, c); } @@ -3071,8 +3498,8 @@ pub fn unroll_contains(src: &[u8], st: &mut [usize], out: &mut [u8], start: usiz if (((ra < src.len()) && (src[(ra) as usize] == B_LBRACKET)) && (skip_ws(src, block_end(src, ra)) >= rb)) { lb = ra; } - if ((((ra < src.len()) && is_alpha(src[(ra) as usize])) && (skip_ws(src, re) >= rb)) && (const_type(src, ra, re) < src.len())) { - let t: usize = const_type(src, ra, re); + if ((((ra < src.len()) && is_alpha(src[(ra) as usize])) && (skip_ws(src, re) >= rb)) && (const_type(src, st, ra, re) < src.len())) { + let t: usize = const_type(src, st, ra, re); let eq: usize = find_at(src, t, B_EQ); if (eq < src.len()) { let v: usize = skip_ws(src, (eq + 1)); @@ -3223,7 +3650,7 @@ pub fn method(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, ra: us no_args(src, st); return (); } - let mut oa: usize = let_owner(src, ra, rb); + let mut oa: usize = let_owner(src, st, ra, rb); let mut ob: usize = src.len(); if (oa < src.len()) { ob = tok_end(src, oa); @@ -3897,7 +4324,7 @@ pub fn path(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, b0: usize) } 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, ma, mb) || declared_type(src, ma, mb)) { + if (declared_fn(src, st, ma, mb) || declared_type(src, st, ma, mb)) { refuse(st, R_CLASH, ma, ma, mb); return (); } @@ -5479,22 +5906,40 @@ pub fn match_stmt(src: &[u8], st: &mut [usize], out: &mut [u8], mode: u8) -> () want(src, st, b"}"); } +pub fn impl_header(src: &[u8], st: &mut [usize], q: usize) -> () { + let mut t: usize = skip_ws(src, tok_end(src, q)); + if tok_is(src, t, b"<") { + t = skip_ws(src, (find_at(src, (t + 1), B_GT) + 1)); + } + let lbr: usize = find_at(src, t, B_LBRACE); + let fr: usize = find_tok_to(src, t, lbr, b"for"); + if (fr < lbr) { + t = skip_ws(src, tok_end(src, fr)); + } + st[(OLO) as usize] = t; + st[(OHI) as usize] = tok_end(src, t); +} + pub fn impl_around(src: &[u8], st: &mut [usize], r: usize) -> bool { - let mut q: usize = skip_ws(src, 0); let mut found: bool = false; + if (st[(IXON) as usize] == 1) { + let mut last: usize = src.len(); + let mut s: usize = st[((st[(IXB) as usize] + (IXK_KINDS * st[(IXBN) as usize]))) as usize]; + while ((s != 0) && (st[((s + 2)) as usize] < r)) { + last = st[((s + 2)) as usize]; + s = st[((s + 3)) as usize]; + } + if (last < src.len()) { + impl_header(src, st, last); + found = true; + } + return found; + } + let mut q: usize = skip_ws(src, 0); while (q < r) { let e: usize = tok_end(src, q); if word_is(src, q, e, b"impl") { - let mut t: usize = skip_ws(src, e); - if tok_is(src, t, b"<") { - t = skip_ws(src, (find_at(src, (t + 1), B_GT) + 1)); - } - let fr: usize = find_tok(src, t, b"for"); - if (fr < find_at(src, t, B_LBRACE)) { - t = skip_ws(src, tok_end(src, fr)); - } - st[(OLO) as usize] = t; - st[(OHI) as usize] = tok_end(src, t); + impl_header(src, st, q); found = true; } q = skip_ws(src, e); @@ -5509,7 +5954,7 @@ pub fn struct_value(src: &[u8], st: &mut [usize], e: usize) -> bool { let mut n: usize = e; 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"("))) && ((find_struct(src, n, ne) < src.len()) || named(src, st, K_TUPLE, n, ne))) { + if ((is_upper(src[(n) as usize]) && (tok_is(src, nx, b"{") || tok_is(src, nx, b"("))) && ((find_struct(src, st, n, ne) < src.len()) || named(src, st, K_TUPLE, n, ne))) { return true; } if (tok_is(src, nx, b"::") || tok_is(src, nx, b".")) { @@ -5520,15 +5965,15 @@ pub fn struct_value(src: &[u8], st: &mut [usize], e: usize) -> bool { if !(tok_is(src, nx, b"(")) { return false; } - let r: usize = fn_ret_type(src, n, ne); + let r: usize = fn_ret_type(src, st, n, ne); if (r >= src.len()) { return false; } let re: usize = tok_end(src, r); if (word_is(src, r, re, b"Self") && impl_around(src, st, r)) { - return ((find_struct(src, st[(OLO) as usize], st[(OHI) as usize]) < src.len()) || named(src, st, K_TUPLE, st[(OLO) as usize], st[(OHI) as usize])); + return ((find_struct(src, st, st[(OLO) as usize], st[(OHI) as usize]) < src.len()) || named(src, st, K_TUPLE, st[(OLO) as usize], st[(OHI) as usize])); } - return ((find_struct(src, r, re) < src.len()) || named(src, st, K_TUPLE, r, re)); + return ((find_struct(src, st, r, re) < src.len()) || named(src, st, K_TUPLE, r, re)); } pub fn result_arg(src: &[u8], p: usize) -> usize { @@ -5931,7 +6376,8 @@ pub fn put_reason(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { puts(st, out, b" `"); put_text(src, st, out, st[(NLO) as usize], st[(NHI) as usize], 40); puts(st, out, b"` at line "); - put_num(st, out, line_of(src, st[(AT) as usize])); + let at_line: usize = line_at(src, st, st[(AT) as usize]); + put_num(st, out, at_line); } pub fn put_refusal(src: &[u8], st: &mut [usize], out: &mut [u8], what: &[u8], na: usize, nb: usize, pos: usize) -> () { @@ -5940,7 +6386,8 @@ pub fn put_refusal(src: &[u8], st: &mut [usize], out: &mut [u8], what: &[u8], na puts(st, out, b" "); put_text(src, st, out, na, nb, 60); puts(st, out, b" (line "); - put_num(st, out, line_of(src, pos)); + let ref_line: usize = line_at(src, st, pos); + put_num(st, out, ref_line); puts(st, out, b"): "); put_reason(src, st, out); puts(st, out, b"\n\n"); @@ -5956,7 +6403,8 @@ pub fn skip_refused(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, puts(st, out, b"// t27o: refused "); put_text(src, st, out, a, b, 40); puts(st, out, b" (line "); - put_num(st, out, line_of(src, a)); + let item_line: usize = line_at(src, st, a); + put_num(st, out, item_line); puts(st, out, b"): "); put_reason(src, st, out); if (nf > 0) { @@ -7241,17 +7689,24 @@ pub fn put_print(src: &[u8], st: &mut [usize], out: &mut [u8], rk: usize, en: us } } -pub fn struct_open(src: &[u8], a: usize, b: usize) -> usize { +pub fn struct_open(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { + if (st[(IXON) as usize] == 1) { + let mut s: usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + let mut q: usize = st[((s + 2)) as usize]; + if struct_hit(src, q, a, b, true) { + return struct_brace(src, q); + } + s = ix_next(src, st, s, a, b); + } + return src.len(); + } let mut q: usize = skip_ws(src, 0); while (q < src.len()) { - let e: usize = tok_end(src, q); - let n: usize = skip_ws(src, e); - let ne: usize = tok_end(src, n); - let o: usize = skip_ws(src, ne); - if ((word_is(src, q, e, b"struct") && same_text(src, n, ne, a, b)) && (tok_is(src, o, b"{") || tok_is(src, o, b"("))) { - return o; + if struct_hit(src, q, a, b, true) { + return struct_brace(src, q); } - q = skip_ws(src, e); + q = skip_ws(src, tok_end(src, q)); } return src.len(); } @@ -7479,7 +7934,7 @@ pub fn vectors(src: &[u8], st: &mut [usize], out: &mut [u8], na: usize, nb: usiz if (tok_is(src, bq, b"Self") && (st[(ILO) as usize] != st[(IHI) as usize])) { sn = st[(ILO) as usize]; } - so = struct_open(src, sn, tok_end(src, sn)); + so = struct_open(src, st, sn, tok_end(src, sn)); if (so < src.len()) { if !(fields_printable(src, st, so)) { return (); @@ -8688,7 +9143,7 @@ pub fn out_str_return(src: &[u8], st: &mut [usize], out: &mut [u8]) -> bool { return true; } -pub fn struct_text(src: &[u8], ilo: usize, ihi: usize, q: usize) -> bool { +pub fn struct_text(src: &[u8], st: &mut [usize], ilo: usize, ihi: usize, q: usize) -> bool { if ((q >= src.len()) || !(is_upper(src[(q) as usize]))) { return false; } @@ -8701,7 +9156,7 @@ pub fn struct_text(src: &[u8], ilo: usize, ihi: usize, q: usize) -> bool { na = ilo; nb = ihi; } - let o: usize = struct_open(src, na, nb); + let o: usize = struct_open(src, st, na, nb); if (o >= src.len()) { return false; } @@ -8716,7 +9171,7 @@ pub fn struct_text(src: &[u8], ilo: usize, ihi: usize, q: usize) -> bool { return false; } -pub fn ret_has_str(src: &[u8], ilo: usize, ihi: usize, pn: usize) -> bool { +pub fn ret_has_str(src: &[u8], st: &mut [usize], ilo: usize, ihi: usize, pn: usize) -> bool { if !(tok_is(src, pn, b"(")) { return false; } @@ -8729,7 +9184,7 @@ pub fn ret_has_str(src: &[u8], ilo: usize, ihi: usize, pn: usize) -> bool { } let mut q: usize = skip_ws(src, tok_end(src, r)); while ((((q < src.len()) && !(tok_is(src, q, b"{"))) && !(tok_is(src, q, b"where"))) && !(tok_is(src, q, b";"))) { - if (tok_is(src, q, b"str") || struct_text(src, ilo, ihi, q)) { + if (tok_is(src, q, b"str") || struct_text(src, st, ilo, ihi, q)) { return true; } q = skip_ws(src, tok_end(src, q)); @@ -8860,7 +9315,7 @@ pub fn fn_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize, is_te if is_test { st[(NTEST) as usize] = (st[(NTEST) as usize] + 1); } - if (!(is_test) && ret_has_str(src, st[(ILO) as usize], st[(IHI) as usize], pn)) { + if (!(is_test) && ret_has_str(src, st, st[(ILO) as usize], st[(IHI) as usize], pn)) { st[(NEQ) as usize] = 1; } if (st[(MODE) as usize] == M_T27) { @@ -9086,7 +9541,7 @@ pub fn impl_item(src: &[u8], st: &mut [usize], out: &mut [u8], start: usize) -> let ta: usize = st[(P) as usize]; let tb: usize = tok_end(src, ta); let lb: usize = find_at(src, ta, B_LBRACE); - let fr: usize = find_tok(src, ta, b"for"); + let fr: usize = find_tok_to(src, ta, lb, b"for"); if (lb >= src.len()) { st[(ERR) as usize] = 0; skip_refused(src, st, out, start, ipos, tok_end(src, ipos), R_SYNTAX); @@ -9251,6 +9706,7 @@ pub fn result_alias(src: &[u8], t: usize) -> bool { } pub fn prescan(src: &[u8], st: &mut [usize]) -> () { + index_decls(src, st); let mut q: usize = skip_ws(src, 0); while (q < src.len()) { let e: usize = tok_end(src, q); diff --git a/specs/tools/t27o/rust_convert.t27 b/specs/tools/t27o/rust_convert.t27 index 79b4ae3d8c..296265d13d 100644 --- a/specs/tools/t27o/rust_convert.t27 +++ b/specs/tools/t27o/rust_convert.t27 @@ -88,8 +88,9 @@ ; variants with data, labels, &mut of a scalar, `mut self`, unsafe, and if / match / blocks used as a ; value without a type annotation. ; -- -; The caller passes the source, an output buffer and a state vector: STATE slots, then the name table -; (NAME_SLOTS a name, as many as fit). The result is the number of bytes the output needs; the buffer was +; The caller passes the source, an output buffer and a state vector: STATE slots, the index slots (IXHIT .. +; IXTOP), then the name table (NAME_SLOTS a name, as many as fit; the declaration index takes the top of +; the vector, the table stops where it begins). The result is the number of bytes the output needs; the buffer was ; too small when that is above out.len (the bytes past the end are counted, not written). Every byte ; this spec reads or writes is a number, as in specs/tri/t27b/source_text.t27 (#7604): gen-rust reads a ; char literal as a Rust char. @@ -182,8 +183,19 @@ pub const CFB : usize = 49; pub const CCNT : usize = 50; /// The most samples one parameter holds: its fixed ones plus the body-derived ones (FR-003 of #9102). pub const DCAP : usize = 64; -pub const STATE : usize = 51; -pub const NAMES : usize = 51; +/// 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; +/// 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 NAME_SLOTS : usize = 5; pub const F_NONE : usize = 0; @@ -566,6 +578,27 @@ pub fn line_of(src: []const u8, pos: usize) -> usize { return n; } +/// line_of with a cache: the position of the last line asked for is kept in the state with the newlines +/// before it, and a later position counts on from there (the refusals of a pass come in file order, so +/// all of them together walk the file once); an earlier one counts from byte 0, as line_of does. +pub fn line_at(src: []const u8, st: []usize, pos: usize) -> usize { + var i : usize = 0; + var n : usize = 0; + if (st[LNPOS] <= pos) { + i = st[LNPOS]; + n = st[LNNL]; + } + var end : usize = pos; + if (end > src.len) { end = src.len; } + while (i < end) { + if (src[i] == B_NL) { n = n + 1; } + i = i + 1; + } + st[LNPOS] = end; + st[LNNL] = n; + return n + 1; +} + /// Where the bracket that opens at src[o] closes: past its match, every bracket kind counted. pub fn block_end(src: []const u8, o: usize) -> usize { var depth : usize = 0; @@ -604,9 +637,16 @@ pub fn find_at(src: []const u8, from: usize, c: u8) -> usize { /// The token `s` at bracket depth 0 from `from`, before any unmatched closer; src.len when none. pub fn find_tok(src: []const u8, from: usize, s: []const u8) -> usize { + return find_tok_to(src, from, src.len, s); +} + +/// find_tok that reads no token at or past src[hi]: src.len when `s` is not before it. (An impl's header +/// looks for its `for` before the `{`; without the bound the walk ran on to the next top-level `for` of +/// the file, once per impl.) +pub fn find_tok_to(src: []const u8, from: usize, hi: usize, s: []const u8) -> usize { var depth : usize = 0; var q : usize = from; - while (q < src.len) { + while (q < src.len and q < hi) { const e : usize = tok_end(src, q); if (depth == 0 and word_is(src, q, e, s)) { return q; } if (e == q + 1 and is_open(src[q])) { depth = depth + 1; } @@ -786,7 +826,10 @@ pub fn add_name(src: []const u8, st: []usize, kind: usize, lo: usize, hi: usize, k = k + 1; } const e : usize = NAMES + st[NNAMES] * NAME_SLOTS; - if (e + NAME_SLOTS > st.len) { return; } + // the name table stops where the declaration index begins, when one is built + var lim : usize = st.len; + if (st[IXON] == 1) { lim = st[IXTOP]; } + if (e + NAME_SLOTS > lim) { return; } st[e] = lo; st[e + 1] = hi; st[e + 2] = olo; @@ -829,6 +872,246 @@ pub fn strloc_in(src: []const u8, st: []usize, a: usize, b: usize) -> bool { return false; } +// ---------------------------------------------------------------- the declaration index (#10544) + +/// A lookup of a declaration by name used to walk the file token by token from byte 0 and test every +/// keyword it met. The index holds, per name, the position of each keyword token that has that name +/// next to it, in file order, and a lookup runs the very same test on those tokens alone. Three kinds +/// of chain: IXK_ITEM the `struct` `enum` `type` `trait` `union` `const` `static` tokens (by the name +/// that follows; a `const mut` / `static mut` also by the one after `mut`), IXK_FN the `fn` tokens (by +/// the name that follows), IXK_LET the `let` tokens that bind a name to a type (by the name that +/// follows, `mut` skipped). The impls have a chain of their own, in file order, without a name. +pub const IXK_ITEM : usize = 0; +pub const IXK_FN : usize = 1; +pub const IXK_LET : usize = 2; +pub const IXK_KINDS : usize = 3; +/// An entry takes four slots: the text of its name (lo, hi), the position of its keyword token, and the +/// next entry of its chain (0 ends the chain). An impl entry has the `{` of an inherent impl and its end +/// where the name would be (both 0 for an impl that is not `impl Name {`). +pub const IX_SLOTS : usize = 4; + +/// A hash of src[a .. b], kept below 1000003 so that no step overflows. +pub fn ix_hash(src: []const u8, a: usize, b: usize) -> usize { + var h : usize = 0; + var k : usize = a; + while (k < b) { + h = (h * 31 + (src[k] as usize) + 1) % 1000003; + k = k + 1; + } + return h; +} + +/// Link the entry at slot e after the last one of the chain whose first slot is st[head]. +pub fn ix_link(st: []usize, head: usize, e: usize) -> void { + var s : usize = st[head]; + if (s == 0) { + st[head] = e; + return; + } + while (st[s + 3] != 0) { s = st[s + 3]; } + st[s + 3] = e; +} + +/// Take the next entry's four slots from the index's room (it may take half of the free slots, no more) +/// and chain it at st[head]. False when it does not fit. +pub fn ix_enter(st: []usize, head: usize, lo: usize, hi: usize, q: usize) -> bool { + if (st[IXTOP] < NAMES + (st.len - NAMES) / 2 + IX_SLOTS) { return false; } + const e : usize = st[IXTOP] - IX_SLOTS; + st[e] = lo; + st[e + 1] = hi; + st[e + 2] = q; + st[e + 3] = 0; + ix_link(st, head, e); + st[IXTOP] = e; + return true; +} + +/// Enter the keyword token src[q] of chain `kind` under the name src[lo .. hi]. +pub fn ix_add(src: []const u8, st: []usize, kind: usize, lo: usize, hi: usize, q: usize) -> bool { + if (hi <= lo) { return true; } + const head : usize = st[IXB] + kind * st[IXBN] + ix_hash(src, lo, hi) % st[IXBN]; + return ix_enter(st, head, lo, hi, q); +} + +/// The first entry of chain `kind` named src[a .. b], 0 when there is none. Its slot goes to st[IXHIT] +/// (what a test reads to know the index answered); the position of its keyword token is st[slot + 2]. +pub fn ix_first(src: []const u8, st: []usize, kind: usize, a: usize, b: usize) -> usize { + st[IXHIT] = 0; + if (b <= a) { return 0; } + var s : usize = st[st[IXB] + kind * st[IXBN] + ix_hash(src, a, b) % st[IXBN]]; + while (s != 0 and !same_text(src, st[s], st[s + 1], a, b)) { s = st[s + 3]; } + st[IXHIT] = s; + return s; +} + +/// The entry after slot s0 with the same name. +pub fn ix_next(src: []const u8, st: []usize, s0: usize, a: usize, b: usize) -> usize { + var s : usize = st[s0 + 3]; + while (s != 0 and !same_text(src, st[s], st[s + 1], a, b)) { s = st[s + 3]; } + st[IXHIT] = s; + return s; +} + +/// Is the token at src[q] one of the keywords that declare an item? +pub fn item_kw(src: []const u8, q: usize, e: usize) -> bool { + return word_is(src, q, e, "struct") or word_is(src, q, e, "enum") or word_is(src, q, e, "type") or word_is(src, q, e, "trait") or word_is(src, q, e, "union") or word_is(src, q, e, "const") or word_is(src, q, e, "static"); +} + +/// The type token a `let` binds src[ra .. rb] to, when the token at src[q] is that `let`: `let r = +/// Type::new(..)` (a builder chain may follow), `let r = Type {`, `let r: Type =`; src.len otherwise. +pub fn let_at(src: []const u8, q: usize, ra: usize, rb: usize) -> usize { + var best : usize = src.len; + const e : usize = tok_end(src, q); + if (word_is(src, q, e, "let")) { + var n : usize = skip_ws(src, e); + if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } + const ne : usize = tok_end(src, n); + if (same_text(src, n, ne, ra, rb)) { + var t : usize = skip_ws(src, ne); + // `let r: Type = ..` says the type between the colon and the equals + if (tok_is(src, t, ":")) { + t = skip_ws(src, tok_end(src, t)); + while (tok_is(src, t, "&") or tok_is(src, t, "mut")) { t = skip_ws(src, tok_end(src, t)); } + const te : usize = tok_end(src, t); + if (t < src.len and is_upper(src[t]) and tok_is(src, skip_ws(src, te), "=")) { best = t; } + } + if (tok_is(src, t, "=")) { + var v : usize = skip_ws(src, tok_end(src, t)); + while (tok_is(src, v, "&") or tok_is(src, v, "mut")) { v = skip_ws(src, tok_end(src, v)); } + const ve : usize = tok_end(src, v); + if (v < src.len and is_upper(src[v])) { + // `Type::new(..)` names its own type, and so do `Type {` and `Type(` + const nx : usize = skip_ws(src, ve); + if (tok_is(src, nx, "::") or src[nx] == B_LBRACE or src[nx] == B_LPAREN) { best = v; } + } + } + } + } + return best; +} + +/// Walk the file once, the way the lookups did, and enter what they will look for. The index lives at +/// the top of the state vector, the buckets first; the name table stops where it begins. A file with +/// more declarations than fit leaves the index off (IXON 0): the lookups then walk the file as before. +pub fn index_decls(src: []const u8, st: []usize) -> void { + st[IXON] = 0; + st[IXHIT] = 0; + st[IXTOP] = st.len; + if (st.len < NAMES + 64) { return; } + var bn : usize = src.len / 512; + if (bn > (st.len - NAMES) / 64) { bn = (st.len - NAMES) / 64; } + if (bn < 1) { bn = 1; } + const top : usize = st.len - IXK_KINDS * bn - 1; + var z : usize = top; + while (z < st.len) { + st[z] = 0; + z = z + 1; + } + st[IXB] = top; + st[IXBN] = bn; + st[IXTOP] = top; + const impls : usize = top + IXK_KINDS * bn; + var whole : bool = true; + var q : usize = skip_ws(src, 0); + while (q < src.len and whole) { + const e : usize = tok_end(src, q); + if (item_kw(src, q, e)) { + const n : usize = skip_ws(src, e); + if (!ix_add(src, st, IXK_ITEM, n, tok_end(src, n), q)) { whole = false; } + if ((word_is(src, q, e, "const") or word_is(src, q, e, "static")) and tok_is(src, n, "mut")) { + const m : usize = skip_ws(src, tok_end(src, n)); + if (!ix_add(src, st, IXK_ITEM, m, tok_end(src, m), q)) { whole = false; } + } + } else if (word_is(src, q, e, "fn")) { + const n : usize = skip_ws(src, e); + if (!ix_add(src, st, IXK_FN, n, tok_end(src, n), q)) { whole = false; } + } else if (word_is(src, q, e, "let")) { + var n : usize = skip_ws(src, e); + if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } + const ne : usize = tok_end(src, n); + if (let_at(src, q, n, ne) < src.len and !ix_add(src, st, IXK_LET, n, ne, q)) { whole = false; } + } else if (word_is(src, q, e, "impl")) { + const t : usize = skip_ws(src, e); + const o : usize = skip_ws(src, tok_end(src, t)); + var lb : usize = 0; + var hb : usize = 0; + if (t < src.len and is_alpha(src[t]) and o < src.len and src[o] == B_LBRACE) { + lb = o; + hb = block_end(src, o); + } + if (!ix_enter(st, impls, lb, hb, q)) { whole = false; } + } + q = skip_ws(src, e); + } + if (whole) { + st[IXON] = 1; + } else { + st[IXTOP] = st.len; + } +} + +/// Do the lookups answer the same with the index as with the walk of the file, for the name src[a .. b]? +/// (What the index tests compare: the index is switched off for the walk, and back on.) +pub fn lookups_agree(src: []const u8, st: []usize, a: usize, b: usize) -> bool { + const on : usize = st[IXON]; + const fs : usize = find_struct(src, st, a, b); + const so : usize = struct_open(src, st, a, b); + const ct : usize = const_type(src, st, a, b); + const fr : usize = fn_ret_type(src, st, a, b); + const rc : u8 = ret_class(src, st, a, b); + const lo : usize = let_owner(src, st, a, b); + const dt : bool = declared_type(src, st, a, b); + const df : bool = declared_fn(src, st, a, b); + st[IXON] = 0; + var same : bool = fs == find_struct(src, st, a, b) and so == struct_open(src, st, a, b) and ct == const_type(src, st, a, b); + same = same and fr == fn_ret_type(src, st, a, b) and rc == ret_class(src, st, a, b) and lo == let_owner(src, st, a, b); + same = same and dt == declared_type(src, st, a, b) and df == declared_fn(src, st, a, b); + st[IXON] = on; + return same; +} + +/// Do method_owner (the method src[na .. nb], a receiver typed src[ola .. ohb]) and impl_around (the impl +/// around src[na]) answer the same, and leave the same in the state, with the index as with the walk? +pub fn owners_agree(src: []const u8, st: []usize, na: usize, nb: usize, ola: usize, ohb: usize) -> bool { + const on : usize = st[IXON]; + const m1 : bool = method_owner(src, st, na, nb, ola, ohb); + const lo1 : usize = st[OLO]; + const hi1 : usize = st[OHI]; + const k1 : usize = st[OKIND]; + const i1 : bool = impl_around(src, st, na); + const ilo1 : usize = st[OLO]; + const ihi1 : usize = st[OHI]; + st[IXON] = 0; + const m0 : bool = method_owner(src, st, na, nb, ola, ohb); + const lo0 : usize = st[OLO]; + const hi0 : usize = st[OHI]; + const k0 : usize = st[OKIND]; + const i0 : bool = impl_around(src, st, na); + st[IXON] = on; + return m1 == m0 and i1 == i0 and ilo1 == st[OLO] and ihi1 == st[OHI] and (!m1 or (lo1 == lo0 and hi1 == hi0 and k1 == k0)); +} + +/// Do they agree on every name src spells, declared or not -- and every method name against every type +/// name for the impl that owns it? +pub fn all_names_agree(src: []const u8, st: []usize) -> bool { + var q : usize = skip_ws(src, 0); + while (q < src.len) { + const e : usize = tok_end(src, q); + if (is_alpha(src[q])) { + if (!lookups_agree(src, st, q, e)) { return false; } + if (!owners_agree(src, st, q, e, 0, 0)) { return false; } + var r : usize = skip_ws(src, 0); + while (r < src.len) { + const re : usize = tok_end(src, r); + if (is_alpha(src[r]) and !owners_agree(src, st, q, e, r, re)) { return false; } + r = skip_ws(src, re); + } + } + q = skip_ws(src, e); + } + return true; +} + /// Each `fn ` in src[lo .. hi] as a refused member of the type src[olo .. ohi]. pub fn record_fns(src: []const u8, st: []usize, lo: usize, hi: usize, olo: usize, ohi: usize) -> void { var q : usize = lo; @@ -860,33 +1143,42 @@ pub fn use_names(src: []const u8, st: []usize, u: usize, end: usize) -> void { } /// Is there a `fn ` anywhere in the file? -pub fn declared_fn(src: []const u8, a: usize, b: usize) -> bool { +pub fn declared_fn(src: []const u8, st: []usize, a: usize, b: usize) -> bool { + if (st[IXON] == 1) { return ix_first(src, st, IXK_FN, a, b) != 0; } var q : usize = skip_ws(src, 0); while (q < src.len) { - const e : usize = tok_end(src, q); - if (word_is(src, q, e, "fn")) { - const n : usize = skip_ws(src, e); - if (same_text(src, n, tok_end(src, n), a, b)) { return true; } - } - q = skip_ws(src, e); + if (fn_named(src, q, 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); + if (!item_kw(src, q, e)) { return false; } + const n : usize = skip_ws(src, e); + return same_text(src, n, tok_end(src, n), a, b); +} + /// Is the capitalized name src[a .. b] declared in the file (struct, enum, type, trait, union, const, /// static), or one Rust always has (Self, Option, Some, None, Result, Ok, Err, String, Vec; ty refuses the /// last two outside a parameter)? -pub fn declared_type(src: []const u8, a: usize, b: usize) -> bool { +pub fn declared_type(src: []const u8, st: []usize, a: usize, b: usize) -> bool { if (word_is(src, a, b, "Self") or word_is(src, a, b, "Option") or word_is(src, a, b, "Some") or word_is(src, a, b, "None")) { return true; } if (word_is(src, a, b, "Result") or word_is(src, a, b, "Ok") or word_is(src, a, b, "Err") or word_is(src, a, b, "String") or word_is(src, a, b, "Vec")) { return true; } + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + if (item_named(src, st[s + 2], a, b)) { return true; } + s = ix_next(src, st, s, a, b); + } + return false; + } var q : usize = skip_ws(src, 0); while (q < src.len) { - const e : usize = tok_end(src, q); - if (word_is(src, q, e, "struct") or word_is(src, q, e, "enum") or word_is(src, q, e, "type") or word_is(src, q, e, "trait") or word_is(src, q, e, "union") or word_is(src, q, e, "const") or word_is(src, q, e, "static")) { - const n : usize = skip_ws(src, e); - if (same_text(src, n, tok_end(src, n), a, b)) { return true; } - } - q = skip_ws(src, e); + if (item_named(src, q, a, b)) { return true; } + q = skip_ws(src, tok_end(src, q)); } return false; } @@ -934,7 +1226,7 @@ pub fn deps(src: []const u8, st: []usize, lo: usize, hi: usize) -> void { if ((st[s0 + 4] == K_ITEM or st[s0 + 4] == K_EXT) and same_text(src, st[s0], st[s0 + 1], st[ILO], st[IHI])) { self_bad = true; } k0 = k0 + 1; } - if (st[ILO] != st[IHI] and st[ERR] == 0 and !declared_type(src, st[ILO], st[IHI])) { refuse(st, R_UNDECL, st[ILO], st[ILO], st[IHI]); } + if (st[ILO] != st[IHI] and st[ERR] == 0 and !declared_type(src, st, st[ILO], st[IHI])) { refuse(st, R_UNDECL, st[ILO], st[ILO], st[IHI]); } // a member that takes `self` still belongs to the type that is not in the output: its t27 // form would carry a parameter of the missing type. The acceptance here is the self-less // associated fn, which names neither the type nor a field of it. @@ -981,14 +1273,14 @@ pub fn deps(src: []const u8, st: []usize, lo: usize, hi: usize) -> void { if (st[ERR] == 0 and self_bad and word_is(src, q, e, "Self")) { const c1 : usize = skip_ws(src, e); const c2 : usize = skip_ws(src, tok_end(src, c1)); - const self_call : bool = tok_is(src, c1, "::") and c2 < src.len and is_alpha(src[c2]) and !is_upper(src[c2]) and tok_is(src, skip_ws(src, tok_end(src, c2)), "(") and declared_fn(src, c2, tok_end(src, c2)); + const self_call : bool = tok_is(src, c1, "::") and c2 < src.len and is_alpha(src[c2]) and !is_upper(src[c2]) and tok_is(src, skip_ws(src, tok_end(src, c2)), "(") and declared_fn(src, st, c2, tok_end(src, c2)); if (!self_call) { refuse(st, R_DEPENDS, q, q, e); } } - if (st[ERR] == 0 and call and !after_dot and !after_path and !after_fn and !is_upper(src[q]) and !call_keyword(src, q, e) and !declared_fn(src, q, e)) { + if (st[ERR] == 0 and call and !after_dot and !after_path and !after_fn and !is_upper(src[q]) and !call_keyword(src, q, e) and !declared_fn(src, st, q, e)) { refuse(st, R_UNDECL, q, q, e); } // a capitalized name nobody here declares came through a glob `use` or from another file - if (st[ERR] == 0 and is_upper(src[q]) and !after_dot and !after_path and !prim(src, q, e) and !declared_type(src, q, e)) { + if (st[ERR] == 0 and is_upper(src[q]) and !after_dot and !after_path and !prim(src, q, e) and !declared_type(src, st, q, e)) { refuse(st, R_UNDECL, q, q, e); } } @@ -1752,34 +2044,53 @@ pub fn no_args(src: []const u8, st: []usize) -> void { want(src, st, ")"); } +/// The receiver of the `fn (..self` the token at src[q] begins, the name src[na .. nb] and the +/// parameters inside src[.. hi]: 1 for self or &self, 2 for &mut self, 0 when it is not such a method. +pub fn kind_at(src: []const u8, q: usize, hi: usize, na: usize, nb: usize) -> usize { + const e : usize = tok_end(src, q); + if (word_is(src, q, e, "fn")) { + const n : usize = skip_ws(src, e); + const ne : usize = tok_end(src, n); + const lp : usize = skip_ws(src, ne); + if (ne - n == nb - na and same_at(src, n, ne, na) and lp < hi and src[lp] == B_LPAREN) { + var s : usize = skip_ws(src, lp + 1); + var amp : bool = false; + var mutable : bool = false; + if (s < hi and src[s] == B_AMP) { + amp = true; + s = skip_ws(src, s + 1); + } + if (s < hi and src[s] == B_APOS) { s = skip_ws(src, tok_end(src, s)); } + if (tok_is(src, s, "mut")) { + mutable = true; + s = skip_ws(src, tok_end(src, s)); + } + if (tok_is(src, s, "self") and amp and mutable) { return 2; } + if (tok_is(src, s, "self")) { return 1; } + } + } + return 0; +} + /// The receiver of `fn (..self` in src[lo .. hi]: 1 for self or &self, 2 for &mut self, 0 when -/// there is no such method. -pub fn method_kind(src: []const u8, lo: usize, hi: usize, na: usize, nb: usize) -> usize { - 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); - const ne : usize = tok_end(src, n); - const lp : usize = skip_ws(src, ne); - if (ne - n == nb - na and same_at(src, n, ne, na) and lp < hi and src[lp] == B_LPAREN) { - var s : usize = skip_ws(src, lp + 1); - var amp : bool = false; - var mutable : bool = false; - if (s < hi and src[s] == B_AMP) { - amp = true; - s = skip_ws(src, s + 1); - } - if (s < hi and src[s] == B_APOS) { s = skip_ws(src, tok_end(src, s)); } - if (tok_is(src, s, "mut")) { - mutable = true; - s = skip_ws(src, tok_end(src, s)); - } - if (tok_is(src, s, "self") and amp and mutable) { return 2; } - if (tok_is(src, s, "self")) { return 1; } +/// there is no such method. The index holds the `fn` tokens by name, in file order. +pub fn method_kind(src: []const u8, st: []usize, lo: usize, hi: usize, na: usize, nb: usize) -> usize { + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_FN, na, nb); + while (s != 0 and st[s + 2] < hi) { + if (st[s + 2] >= lo) { + const k : usize = kind_at(src, st[s + 2], hi, na, nb); + if (k != 0) { return k; } } + s = ix_next(src, st, s, na, nb); } - q = skip_ws(src, e); + return 0; + } + var q : usize = lo; + while (q < hi) { + const k : usize = kind_at(src, q, hi, na, nb); + if (k != 0) { return k; } + q = skip_ws(src, tok_end(src, q)); } return 0; } @@ -1789,71 +2100,68 @@ pub fn method_kind(src: []const u8, lo: usize, hi: usize, na: usize, nb: usize) /// `let r: Type =`. The last binding wins because Rust shadows: one file binds the same name to many /// types (`let r = HirSimResult::ok(..)` after an earlier `let r = HirResourceEstimate::zero()`), and /// the binding in scope at a use is the one textually before it. -pub fn let_owner(src: []const u8, ra: usize, rb: usize) -> usize { +pub fn let_owner(src: []const u8, st: []usize, ra: usize, rb: usize) -> usize { var best : usize = src.len; + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_LET, ra, rb); + while (s != 0 and st[s + 2] < ra) { + const v : usize = let_at(src, st[s + 2], ra, rb); + if (v < src.len) { best = v; } + s = ix_next(src, st, s, ra, rb); + } + return best; + } var q : usize = skip_ws(src, 0); while (q < src.len and q < ra) { - const e : usize = tok_end(src, q); - if (word_is(src, q, e, "let")) { - var n : usize = skip_ws(src, e); - if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } - const ne : usize = tok_end(src, n); - if (same_text(src, n, ne, ra, rb)) { - var t : usize = skip_ws(src, ne); - // `let r: Type = ..` says the type between the colon and the equals - if (tok_is(src, t, ":")) { - t = skip_ws(src, tok_end(src, t)); - while (tok_is(src, t, "&") or tok_is(src, t, "mut")) { t = skip_ws(src, tok_end(src, t)); } - const te : usize = tok_end(src, t); - if (t < src.len and is_upper(src[t]) and tok_is(src, skip_ws(src, te), "=")) { best = t; } - } - if (tok_is(src, t, "=")) { - var v : usize = skip_ws(src, tok_end(src, t)); - while (tok_is(src, v, "&") or tok_is(src, v, "mut")) { v = skip_ws(src, tok_end(src, v)); } - const ve : usize = tok_end(src, v); - if (v < src.len and is_upper(src[v])) { - // `Type::new(..)` names its own type, and so do `Type {` and `Type(` - const nx : usize = skip_ws(src, ve); - if (tok_is(src, nx, "::") or src[nx] == B_LBRACE or src[nx] == B_LPAREN) { best = v; } - } - } - } - } - q = skip_ws(src, e); + const v : usize = let_at(src, q, ra, rb); + if (v < src.len) { best = v; } + q = skip_ws(src, tok_end(src, q)); } return best; } +/// Does the token at src[q] begin an inherent impl block (`impl Name {`) that has a method src[na .. nb] +/// taking self -- in the first pass, with a receiver type src[ola .. ohb] known, one of that type? Its type +/// goes to st[OLO .. OHI], the receiver kind to st[OKIND]. `hi` is the end of the block when the caller has +/// it, 0 when not. +pub fn impl_owner_at(src: []const u8, st: []usize, q: usize, pass: usize, na: usize, nb: usize, ola: usize, ohb: usize, hi: usize) -> bool { + const e : usize = tok_end(src, q); + if (!word_is(src, q, e, "impl")) { return false; } + const t : usize = skip_ws(src, e); + const te : usize = tok_end(src, t); + const o : usize = skip_ws(src, te); + if (t >= src.len or !is_alpha(src[t]) or o >= src.len or src[o] != B_LBRACE) { return false; } + if (pass == 0 and ola < ohb and !same_text(src, t, te, ola, ohb)) { return false; } + var end : usize = hi; + if (end == 0) { end = block_end(src, o); } + const kind : usize = method_kind(src, st, o, end, na, nb); + if (kind == 0) { return false; } + st[OLO] = t; + st[OHI] = te; + st[OKIND] = kind; + return true; +} + /// The type of the first inherent impl block (`impl Name {`) with a method `name` taking self, into /// st[OLO .. OHI], its receiver kind into st[OKIND]. src[ola .. ohb] is the type a `let` bound the /// receiver to when one is known: the impls of that type are searched first, so two types that share a -/// method name (`passed` of LinkResult and HirSimResult) keep their own methods. +/// method name (`passed` of LinkResult and HirSimResult) keep their own methods. The index holds the +/// impls in file order. pub fn method_owner(src: []const u8, st: []usize, na: usize, nb: usize, ola : usize, ohb : usize) -> bool { var pass : usize = 0; while (pass < 2) { - var q : usize = skip_ws(src, 0); - while (q < src.len) { - const e : usize = tok_end(src, q); - if (word_is(src, q, e, "impl")) { - const t : usize = skip_ws(src, e); - const te : usize = tok_end(src, t); - const o : usize = skip_ws(src, te); - if (t < src.len and is_alpha(src[t]) and o < src.len and src[o] == B_LBRACE) { - const own : bool = pass == 0 and ola < ohb and same_text(src, t, te, ola, ohb); - if (pass == 0 and ola < ohb and !own) { - q = skip_ws(src, e); - continue; - } - const kind : usize = method_kind(src, o, block_end(src, o), na, nb); - if (kind != 0) { - st[OLO] = t; - st[OHI] = te; - st[OKIND] = kind; - return true; - } - } + if (st[IXON] == 1) { + var s : usize = st[st[IXB] + IXK_KINDS * st[IXBN]]; + while (s != 0) { + if (st[s + 1] != 0 and impl_owner_at(src, st, st[s + 2], pass, na, nb, ola, ohb, st[s + 1])) { return true; } + s = st[s + 3]; + } + } else { + var q : usize = skip_ws(src, 0); + while (q < src.len) { + if (impl_owner_at(src, st, q, pass, na, nb, ola, ohb, 0)) { return true; } + q = skip_ws(src, tok_end(src, q)); } - q = skip_ws(src, e); } if (ola >= ohb) { return false; } pass = pass + 1; @@ -1863,46 +2171,92 @@ pub fn method_owner(src: []const u8, st: []usize, na: usize, nb: usize, ola : us // ---------------------------------------------------------------- what a receiver is -/// The type token of the file-level `const` / `static` src[a .. b], src.len when there is none. -pub fn const_type(src: []const u8, a: usize, b: usize) -> usize { +/// Is the token at src[q] a `const` or `static` (an optional `mut` after it) of the name src[a .. b] with a +/// type: `const NAME :`? +pub fn const_hit(src: []const u8, q: usize, a: usize, b: usize) -> bool { + const e : usize = tok_end(src, q); + if (!(word_is(src, q, e, "const") or word_is(src, q, e, "static"))) { return false; } + var n : usize = skip_ws(src, e); + if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } + const ne : usize = tok_end(src, n); + return same_text(src, n, ne, a, b) and tok_is(src, skip_ws(src, ne), ":"); +} + +/// The type token of the file-level `const` / `static` src[a .. b], src.len when there is none. The index +/// holds the keyword tokens by name; with it off the file is walked from byte 0. +pub fn const_type(src: []const u8, st: []usize, a: usize, b: usize) -> usize { + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + const q : usize = st[s + 2]; + if (const_hit(src, q, a, b)) { return const_of(src, q); } + s = ix_next(src, st, s, a, b); + } + return src.len; + } var q : usize = skip_ws(src, 0); while (q < src.len) { - const e : usize = tok_end(src, q); - if (word_is(src, q, e, "const") or word_is(src, q, e, "static")) { - var n : usize = skip_ws(src, e); - if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } - const ne : usize = tok_end(src, n); - const c : usize = skip_ws(src, ne); - if (same_text(src, n, ne, a, b) and tok_is(src, c, ":")) { return skip_ws(src, c + 1); } - } - q = skip_ws(src, e); + if (const_hit(src, q, a, b)) { return const_of(src, q); } + q = skip_ws(src, tok_end(src, q)); } return src.len; } -/// The return type token of the first `fn` src[a .. b] of the file, src.len when it has none. -pub fn fn_ret_type(src: []const u8, a: usize, b: usize) -> usize { +/// The type token of the `const` / `static` at src[q] that const_hit found: the one after its colon. +pub fn const_of(src: []const u8, q: usize) -> usize { + var n : usize = skip_ws(src, tok_end(src, q)); + if (tok_is(src, n, "mut")) { n = skip_ws(src, tok_end(src, n)); } + return skip_ws(src, skip_ws(src, tok_end(src, n)) + 1); +} + +/// Is the token at src[q] a `fn` of the name src[a .. b] that has a `(` to read its parameters from? +pub fn fn_hit(src: []const u8, q: usize, a: usize, b: usize) -> bool { + if (!fn_named(src, q, a, b)) { return false; } + return find_at(src, tok_end(src, skip_ws(src, tok_end(src, q))), B_LPAREN) < src.len; +} + +/// Is the token at src[q] a `fn` of the name src[a .. b]? +pub fn fn_named(src: []const u8, q: usize, a: usize, b: usize) -> bool { + const e : usize = tok_end(src, q); + if (!word_is(src, q, e, "fn")) { return false; } + const n : usize = skip_ws(src, e); + return same_text(src, n, tok_end(src, n), a, b); +} + +/// The return type token of the `fn` at src[q] that fn_hit found: the one after `->`, src.len when it has +/// none. +pub fn fn_ret_of(src: []const u8, q: usize) -> usize { + const n : usize = skip_ws(src, tok_end(src, q)); + const lp : usize = find_at(src, tok_end(src, n), B_LPAREN); + if (lp >= src.len) { return src.len; } + const r : usize = skip_ws(src, block_end(src, lp)); + if (tok_is(src, r, "->")) { return skip_ws(src, tok_end(src, r)); } + return src.len; +} + +/// The return type token of the first `fn` src[a .. b] of the file, src.len when it has none. The index +/// holds the `fn` tokens by name; with it off the file is walked from byte 0. +pub fn fn_ret_type(src: []const u8, st: []usize, a: usize, b: usize) -> usize { + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_FN, a, b); + while (s != 0) { + const q : usize = st[s + 2]; + if (fn_hit(src, q, a, b)) { return fn_ret_of(src, q); } + s = ix_next(src, st, s, a, b); + } + return src.len; + } var q : usize = skip_ws(src, 0); while (q < src.len) { - const e : usize = tok_end(src, q); - if (word_is(src, q, e, "fn")) { - const n : usize = skip_ws(src, e); - const ne : usize = tok_end(src, n); - const lp : usize = find_at(src, ne, B_LPAREN); - if (same_text(src, n, ne, a, b) and lp < src.len) { - const r : usize = skip_ws(src, block_end(src, lp)); - if (tok_is(src, r, "->")) { return skip_ws(src, tok_end(src, r)); } - return src.len; - } - } - q = skip_ws(src, e); + if (fn_hit(src, q, a, b)) { return fn_ret_of(src, q); } + q = skip_ws(src, tok_end(src, q)); } return src.len; } /// Does `fn` src[a .. b] return `Result<(), E>` (or an alias of it), which t27o writes as a bool? pub fn unit_result_fn(src: []const u8, st: []usize, a: usize, b: usize) -> bool { - const r : usize = fn_ret_type(src, a, b); + const r : usize = fn_ret_type(src, st, a, b); const re : usize = tok_end(src, r); if (r >= src.len or !(word_is(src, r, re, "Result") or named(src, st, K_RALIAS, r, re))) { return false; } const lt : usize = skip_ws(src, re); @@ -1968,7 +2322,7 @@ pub fn literal_class(src: []const u8, st: []usize, q: usize) -> u8 { /// of the impl's struct after `self.`, a file-level const, or the literal an untyped `let` binds. pub fn name_class(src: []const u8, st: []usize, a: usize, b: usize, field: bool) -> u8 { if (field) { - const lb : usize = find_struct(src, st[ILO], st[IHI]); + const lb : usize = find_struct(src, st, st[ILO], st[IHI]); if (lb >= src.len) { return C_ANY; } const f : usize = decl_type(src, lb, block_end(src, lb), a, b); if (f >= src.len) { return C_ANY; } @@ -1977,7 +2331,7 @@ pub fn name_class(src: []const u8, st: []usize, a: usize, b: usize, field: bool) if (prim(src, a, b) or word_is(src, a, b, "f32") or word_is(src, a, b, "f64")) { return type_class(src, st, a); } const d : usize = decl_type(src, st[FLO], a, a, b); if (d < src.len) { return type_class(src, st, d); } - const c : usize = const_type(src, a, b); + const c : usize = const_type(src, st, a, b); if (c < src.len) { return type_class(src, st, c); } // `let [mut] name = ;` var q : usize = st[FLO]; @@ -2011,7 +2365,7 @@ pub fn name_class(src: []const u8, st: []usize, a: usize, b: usize, field: bool) if (str_chain(src, st, init, semi)) { return C_STR; } const ie : usize = tok_end(src, init); if (tok_is(src, skip_ws(src, ie), "(")) { - const r2 : usize = fn_ret_type(src, init, ie); + const r2 : usize = fn_ret_type(src, st, init, ie); if (r2 < src.len) { return type_class(src, st, r2); } } } @@ -2044,15 +2398,38 @@ pub fn name_class(src: []const u8, st: []usize, a: usize, b: usize, field: bool) return C_ANY; } -/// The `{` of `struct {` in the file, src.len when there is none. -pub fn find_struct(src: []const u8, a: usize, b: usize) -> usize { +/// Is the token at src[q] the `struct` of `struct {`, the name src[a .. b]? (`(` instead of `{` +/// too when `paren` is set: a tuple struct.) +pub fn struct_hit(src: []const u8, q: usize, a: usize, b: usize, paren: bool) -> 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); + const o : usize = skip_ws(src, ne); + return same_text(src, n, ne, a, b) and (tok_is(src, o, "{") or (paren and tok_is(src, o, "("))); +} + +/// The `{` after the name of the `struct` at src[q] that struct_hit found. +pub fn struct_brace(src: []const u8, q: usize) -> usize { + return skip_ws(src, tok_end(src, skip_ws(src, tok_end(src, q)))); +} + +/// The `{` of `struct {` in the file, src.len when there is none. The index holds the keyword +/// tokens by name; with it off the file is walked from byte 0. +pub fn find_struct(src: []const u8, st: []usize, a: usize, b: usize) -> usize { + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + const q : usize = st[s + 2]; + if (struct_hit(src, q, a, b, false)) { return struct_brace(src, q); } + s = ix_next(src, st, s, a, b); + } + return src.len; + } var q : usize = skip_ws(src, 0); while (q < src.len) { - const e : usize = tok_end(src, q); - const n : usize = skip_ws(src, e); - const ne : usize = tok_end(src, n); - if (word_is(src, q, e, "struct") and same_text(src, n, ne, a, b) and tok_is(src, skip_ws(src, ne), "{")) { return skip_ws(src, ne); } - q = skip_ws(src, e); + if (struct_hit(src, q, a, b, false)) { return struct_brace(src, q); } + q = skip_ws(src, tok_end(src, q)); } return src.len; } @@ -2061,7 +2438,7 @@ pub fn find_struct(src: []const u8, a: usize, b: usize) -> usize { /// `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 { var d : usize = decl_type(src, st[FLO], xa, xa, xe); - if (d >= src.len) { d = const_type(src, xa, xe); } + if (d >= src.len) { d = const_type(src, st, xa, xe); } if (d >= src.len) { return C_ANY; } while (tok_is(src, d, "&") or tok_is(src, d, "&&")) { d = skip_ws(src, tok_end(src, d)); @@ -2070,7 +2447,7 @@ pub fn field_of_class(src: []const u8, st: []usize, xa: usize, xe: usize, fa: us } const de : usize = tok_end(src, d); if (d >= src.len or !is_upper(src[d])) { return C_ANY; } - const lb : usize = find_struct(src, d, de); + const lb : usize = find_struct(src, st, d, de); if (lb >= src.len) { return C_ANY; } const f : usize = decl_type(src, lb, block_end(src, lb), fa, fe); if (f >= src.len) { return C_ANY; } @@ -2163,7 +2540,7 @@ pub fn recv_class(src: []const u8, st: []usize, ra: usize, rb: usize) -> u8 { } if (is_alpha(src[ra]) and tok_is(src, nx, "(") and skip_ws(src, block_end(src, nx)) >= rb) { if (unit_result_fn(src, st, ra, e)) { return C_BOOL; } - const r : usize = fn_ret_type(src, ra, e); + const r : usize = fn_ret_type(src, st, ra, e); if (r < src.len) { return type_class(src, st, r); } return C_ANY; } @@ -2179,7 +2556,7 @@ pub fn recv_class(src: []const u8, st: []usize, ra: usize, rb: usize) -> u8 { k = literal_class(src, st, q); } else if (is_alpha(src[q]) and !tok_is(src, prev, ".") and !tok_is(src, prev, "::")) { if (tok_is(src, skip_ws(src, qe), "(")) { - const r2 : usize = fn_ret_type(src, q, qe); + const r2 : usize = fn_ret_type(src, st, q, qe); if (r2 < src.len) { k = type_class(src, st, r2); } } else { k = name_class(src, st, q, qe, false); @@ -2208,29 +2585,37 @@ pub fn std_bool(src: []const u8, a: usize, b: usize) -> bool { return listed(w, src[a .. b]); } +/// One fn token of ret_class: the class of its return type folded into cls (C_NONE: none yet, C_ANY: they +/// disagree), when src[q] is a `fn` named src[a .. b] with a `->`. +pub fn ret_step(src: []const u8, st: []usize, q: usize, a: usize, b: usize, cls: u8) -> u8 { + if (!fn_hit(src, q, a, b)) { return cls; } + // a fn with no `->` returns `()`, which no `!` can be applied to: a test named like a + // method (`fn is_readonly()` beside `fn is_readonly(&self) -> bool`) says nothing of it + const r : usize = skip_ws(src, block_end(src, find_at(src, tok_end(src, skip_ws(src, tok_end(src, q))), B_LPAREN))); + if (!tok_is(src, r, "->")) { return cls; } + const c : u8 = type_class(src, st, skip_ws(src, tok_end(src, r))); + if (cls == C_NONE) { return c; } + if (cls != c) { return C_ANY; } + return cls; +} + /// The class of what the fns named src[a .. b] in the file return: C_NONE when it declares none that /// returns a value, C_ANY when they disagree or return a type t27o does not class (two impls may share /// a method name). pub fn ret_class(src: []const u8, st: []usize, a: usize, b: usize) -> u8 { var cls : u8 = C_NONE; + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_FN, a, b); + while (s != 0) { + cls = ret_step(src, st, st[s + 2], a, b, cls); + s = ix_next(src, st, s, a, b); + } + return cls; + } var q : usize = skip_ws(src, 0); while (q < src.len) { - const e : usize = tok_end(src, q); - if (word_is(src, q, e, "fn")) { - const n : usize = skip_ws(src, e); - const ne : usize = tok_end(src, n); - const lp : usize = find_at(src, ne, B_LPAREN); - if (same_text(src, n, ne, a, b) and lp < src.len) { - // a fn with no `->` returns `()`, which no `!` can be applied to: a test named like a - // method (`fn is_readonly()` beside `fn is_readonly(&self) -> bool`) says nothing of it - const r : usize = skip_ws(src, block_end(src, lp)); - if (tok_is(src, r, "->")) { - const c : u8 = type_class(src, st, skip_ws(src, tok_end(src, r))); - if (cls == C_NONE) { cls = c; } else if (cls != c) { cls = C_ANY; } - } - } - } - q = skip_ws(src, e); + cls = ret_step(src, st, q, a, b, cls); + q = skip_ws(src, tok_end(src, q)); } return cls; } @@ -2259,14 +2644,14 @@ pub fn lit_at(src: []const u8, q: usize) -> bool { /// The `{` (or the `(` of a tuple struct) of the struct the name src[xa .. xe] is of: `self` is the impl's /// type, any other name the type it is declared with or the one a `let x = Type::new(..)` names; src.len /// when t27o cannot tell. -pub fn head_struct(src: []const u8, flo: usize, ilo: usize, ihi: usize, xa: usize, xe: usize) -> usize { +pub fn head_struct(src: []const u8, st: []usize, flo: usize, ilo: usize, ihi: usize, xa: usize, xe: usize) -> usize { if (word_is(src, xa, xe, "self")) { if (ilo == ihi) { return src.len; } - return struct_open(src, ilo, ihi); + return struct_open(src, st, ilo, ihi); } var d : usize = decl_type(src, flo, xa, xa, xe); - if (d >= src.len) { d = const_type(src, xa, xe); } - if (d >= src.len) { d = let_owner(src, xa, xe); } + if (d >= src.len) { d = const_type(src, st, xa, xe); } + if (d >= src.len) { d = let_owner(src, st, xa, xe); } if (d >= src.len) { return src.len; } while (tok_is(src, d, "&") or tok_is(src, d, "&&")) { d = skip_ws(src, tok_end(src, d)); @@ -2274,7 +2659,7 @@ pub fn head_struct(src: []const u8, flo: usize, ilo: usize, ihi: usize, xa: usiz if (tok_is(src, d, "mut")) { d = skip_ws(src, tok_end(src, d)); } } if (d >= src.len or !is_upper(src[d])) { return src.len; } - return struct_open(src, d, tok_end(src, d)); + return struct_open(src, st, d, tok_end(src, d)); } /// The class of the expression src[a .. b] that has no binary operator at its top: a prefix `!`, `*`, `&` @@ -2373,7 +2758,7 @@ pub fn chain_class(src: []const u8, st: []usize, a: usize, b: usize, deep: bool) if (kind == CK_NAME) { return name_class(src, st, na, nb, false); } // `x.f` and `x.0` of a struct this file declares: the field's declared type if (kind == CK_FIELD and dots == 1 and plain) { - const o : usize = head_struct(src, st[FLO], st[ILO], st[IHI], xa, xe); + const o : usize = head_struct(src, st, st[FLO], st[ILO], st[IHI], xa, xe); if (o >= src.len) { return C_ANY; } if (is_digit(src[na])) { if (src[o] != B_LPAREN or nb != na + 1) { return C_ANY; } @@ -2387,7 +2772,7 @@ pub fn chain_class(src: []const u8, st: []usize, a: usize, b: usize, deep: bool) return type_class(src, st, f); } if (kind == CK_PATH) { - const c : usize = const_type(src, na, nb); + const c : usize = const_type(src, st, na, nb); if (c < src.len) { return type_class(src, st, c); } } return C_ANY; @@ -2689,8 +3074,8 @@ pub fn unroll_contains(src: []const u8, st: []usize, out: []u8, start: usize, ra var lb : usize = src.len; const re : usize = tok_end(src, ra); if (ra < src.len and src[ra] == B_LBRACKET and skip_ws(src, block_end(src, ra)) >= rb) { lb = ra; } - if (ra < src.len and is_alpha(src[ra]) and skip_ws(src, re) >= rb and const_type(src, ra, re) < src.len) { - const t : usize = const_type(src, ra, re); + if (ra < src.len and is_alpha(src[ra]) and skip_ws(src, re) >= rb and const_type(src, st, ra, re) < src.len) { + const t : usize = const_type(src, st, ra, re); const eq : usize = find_at(src, t, B_EQ); if (eq < src.len) { const v : usize = skip_ws(src, eq + 1); @@ -2802,7 +3187,7 @@ pub fn method(src: []const u8, st: []usize, out: []u8, start: usize, ra: usize, return; } // a `let`-bound receiver says which impl its method belongs to (two types may share a method name) - var oa : usize = let_owner(src, ra, rb); + var oa : usize = let_owner(src, st, ra, rb); var ob : usize = src.len; if (oa < src.len) { ob = tok_end(src, oa); } // a path receiver names its own owner before any `let` fallback: `AsmRelocKind::Abs32.to_i8()` @@ -3398,7 +3783,7 @@ pub fn path(src: []const u8, st: []usize, out: []u8, a0: usize, b0: usize) -> vo // 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, ma, mb) or declared_type(src, ma, mb)) { + if (declared_fn(src, st, ma, mb) or declared_type(src, st, ma, mb)) { refuse(st, R_CLASH, ma, ma, mb); return; } @@ -4821,19 +5206,40 @@ pub fn match_stmt(src: []const u8, st: []usize, out: []u8, mode: u8) -> void { want(src, st, "}"); } +/// The type of the `impl` whose header the token at src[q] begins, as src[OLO .. OHI] (the type after +/// `for` when there is one). +pub fn impl_header(src: []const u8, st: []usize, q: usize) -> void { + var t : usize = skip_ws(src, tok_end(src, q)); + if (tok_is(src, t, "<")) { t = skip_ws(src, find_at(src, t + 1, B_GT) + 1); } + const lbr : usize = find_at(src, t, B_LBRACE); + const fr : usize = find_tok_to(src, t, lbr, "for"); + if (fr < lbr) { t = skip_ws(src, tok_end(src, fr)); } + st[OLO] = t; + st[OHI] = tok_end(src, t); +} + /// The type of the impl block around src[r], as src[OLO .. OHI] (the nearest `impl` before it). pub fn impl_around(src: []const u8, st: []usize, r: usize) -> bool { - var q : usize = skip_ws(src, 0); var found : bool = false; + if (st[IXON] == 1) { + // the last `impl` before r decides: each one overwrites what the one before it left + var last : usize = src.len; + var s : usize = st[st[IXB] + IXK_KINDS * st[IXBN]]; + while (s != 0 and st[s + 2] < r) { + last = st[s + 2]; + s = st[s + 3]; + } + if (last < src.len) { + impl_header(src, st, last); + found = true; + } + return found; + } + var q : usize = skip_ws(src, 0); while (q < r) { const e : usize = tok_end(src, q); if (word_is(src, q, e, "impl")) { - var t : usize = skip_ws(src, e); - if (tok_is(src, t, "<")) { t = skip_ws(src, find_at(src, t + 1, B_GT) + 1); } - const fr : usize = find_tok(src, t, "for"); - if (fr < find_at(src, t, B_LBRACE)) { t = skip_ws(src, tok_end(src, fr)); } - st[OLO] = t; - st[OHI] = tok_end(src, t); + impl_header(src, st, q); found = true; } q = skip_ws(src, e); @@ -4848,18 +5254,18 @@ pub fn struct_value(src: []const u8, st: []usize, e: usize) -> bool { var n : usize = e; 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, "(")) and (find_struct(src, n, ne) < src.len or named(src, st, K_TUPLE, n, ne))) { return true; } + if (is_upper(src[n]) and (tok_is(src, nx, "{") or tok_is(src, nx, "(")) and (find_struct(src, st, n, ne) < src.len or named(src, st, K_TUPLE, n, ne))) { return true; } 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 false; } - const r : usize = fn_ret_type(src, n, ne); + const r : usize = fn_ret_type(src, st, n, ne); if (r >= src.len) { return false; } const re : usize = tok_end(src, r); - if (word_is(src, r, re, "Self") and impl_around(src, st, r)) { return find_struct(src, st[OLO], st[OHI]) < src.len or named(src, st, K_TUPLE, st[OLO], st[OHI]); } - return find_struct(src, r, re) < src.len or named(src, st, K_TUPLE, r, re); + if (word_is(src, r, re, "Self") and impl_around(src, st, r)) { return find_struct(src, st, st[OLO], st[OHI]) < src.len or named(src, st, K_TUPLE, st[OLO], st[OHI]); } + return find_struct(src, st, r, re) < src.len or named(src, st, K_TUPLE, r, re); } /// Where the second argument of the assert_eq! / assert_ne! whose first argument starts at src[p] begins @@ -5159,7 +5565,8 @@ pub fn put_reason(src: []const u8, st: []usize, out: []u8) -> void { puts(st, out, " `"); put_text(src, st, out, st[NLO], st[NHI], 40); puts(st, out, "` at line "); - put_num(st, out, line_of(src, st[AT])); + const at_line : usize = line_at(src, st, st[AT]); + put_num(st, out, at_line); } /// The comment that stands where a refused item was. @@ -5169,7 +5576,8 @@ pub fn put_refusal(src: []const u8, st: []usize, out: []u8, what: []const u8, na puts(st, out, " "); put_text(src, st, out, na, nb, 60); puts(st, out, " (line "); - put_num(st, out, line_of(src, pos)); + const ref_line : usize = line_at(src, st, pos); + put_num(st, out, ref_line); puts(st, out, "): "); put_reason(src, st, out); puts(st, out, "\n\n"); @@ -5186,7 +5594,8 @@ pub fn skip_refused(src: []const u8, st: []usize, out: []u8, start: usize, a: us puts(st, out, "// t27o: refused "); put_text(src, st, out, a, b, 40); puts(st, out, " (line "); - put_num(st, out, line_of(src, a)); + const item_line : usize = line_at(src, st, a); + put_num(st, out, item_line); puts(st, out, "): "); put_reason(src, st, out); if (nf > 0) { @@ -6171,15 +6580,20 @@ pub fn put_print(src: []const u8, st: []usize, out: []u8, rk: usize, en: usize, } /// The `{` (or the `(` of a tuple struct) of `struct ` in the file, src.len when there is none. -pub fn struct_open(src: []const u8, a: usize, b: usize) -> usize { +pub fn struct_open(src: []const u8, st: []usize, a: usize, b: usize) -> usize { + if (st[IXON] == 1) { + var s : usize = ix_first(src, st, IXK_ITEM, a, b); + while (s != 0) { + const q : usize = st[s + 2]; + if (struct_hit(src, q, a, b, true)) { return struct_brace(src, q); } + s = ix_next(src, st, s, a, b); + } + return src.len; + } var q : usize = skip_ws(src, 0); while (q < src.len) { - const e : usize = tok_end(src, q); - const n : usize = skip_ws(src, e); - const ne : usize = tok_end(src, n); - const o : usize = skip_ws(src, ne); - if (word_is(src, q, e, "struct") and same_text(src, n, ne, a, b) and (tok_is(src, o, "{") or tok_is(src, o, "("))) { return o; } - q = skip_ws(src, e); + if (struct_hit(src, q, a, b, true)) { return struct_brace(src, q); } + q = skip_ws(src, tok_end(src, q)); } return src.len; } @@ -6379,7 +6793,7 @@ pub fn vectors(src: []const u8, st: []usize, out: []u8, na: usize, nb: usize, pn if (wrapk != 3) { var sn : usize = bq; if (tok_is(src, bq, "Self") and st[ILO] != st[IHI]) { sn = st[ILO]; } - so = struct_open(src, sn, tok_end(src, sn)); + so = struct_open(src, st, sn, tok_end(src, sn)); if (so < src.len) { if (!fields_printable(src, st, so)) { return; } rk = V_STRUCT; @@ -7398,7 +7812,7 @@ pub fn out_str_return(src: []const u8, st: []usize, out: []u8) -> bool { /// Does the struct named by the type token at src[q] (`Self` is the impl's type) have a text field? The /// oracle records one with type_text::same, so a fn that returns the struct makes the spec use it (#10534: /// FieldDesc::new -> Self, whose `name: &'static str` the appended oracle test compares). -pub fn struct_text(src: []const u8, ilo: usize, ihi: usize, q: usize) -> bool { +pub fn struct_text(src: []const u8, st: []usize, ilo: usize, ihi: usize, q: usize) -> bool { if (q >= src.len or !is_upper(src[q])) { return false; } var na : usize = q; var nb : usize = tok_end(src, q); @@ -7407,7 +7821,7 @@ pub fn struct_text(src: []const u8, ilo: usize, ihi: usize, q: usize) -> bool { na = ilo; nb = ihi; } - const o : usize = struct_open(src, na, nb); + const o : usize = struct_open(src, st, na, nb); if (o >= src.len) { return false; } const close : usize = block_end(src, o); var t : usize = skip_ws(src, o + 1); @@ -7420,7 +7834,7 @@ pub fn struct_text(src: []const u8, ilo: usize, ihi: usize, q: usize) -> bool { /// Does the return type of the fn whose parameters open at src[pn] name `str`, an owned String, or a /// struct of this file with a text field? The oracle compares each with type_text::same. -pub fn ret_has_str(src: []const u8, ilo: usize, ihi: usize, pn: usize) -> bool { +pub fn ret_has_str(src: []const u8, st: []usize, ilo: usize, ihi: usize, pn: usize) -> bool { if (!tok_is(src, pn, "(")) { return false; } // an owned String held by an Option or a Result is a text the oracle samples, and so is the // out-buffer form (the fn returns how many bytes it wrote into the caller's buffer) @@ -7429,7 +7843,7 @@ pub fn ret_has_str(src: []const u8, ilo: usize, ihi: usize, pn: usize) -> bool { if (!tok_is(src, r, "->")) { return false; } var q : usize = skip_ws(src, tok_end(src, r)); while (q < src.len and !tok_is(src, q, "{") and !tok_is(src, q, "where") and !tok_is(src, q, ";")) { - if (tok_is(src, q, "str") or struct_text(src, ilo, ihi, q)) { return true; } + if (tok_is(src, q, "str") or struct_text(src, st, ilo, ihi, q)) { return true; } q = skip_ws(src, tok_end(src, q)); } return false; @@ -7540,7 +7954,7 @@ pub fn fn_item(src: []const u8, st: []usize, out: []u8, start: usize, is_test: b st[NOK] = st[NOK] + 1; if (is_test) { st[NTEST] = st[NTEST] + 1; } // the oracle compares a text result with type_text::same, so the spec uses it - if (!is_test and ret_has_str(src, st[ILO], st[IHI], pn)) { st[NEQ] = 1; } + if (!is_test and ret_has_str(src, st, st[ILO], st[IHI], pn)) { st[NEQ] = 1; } if (st[MODE] == M_T27) { puts(st, out, "\n\n"); } else { st[W] = w0; } if (st[MODE] == M_SOURCE and !is_test) { put_item_source(src, st, out, start, item_end(src, start)); @@ -7716,7 +8130,7 @@ pub fn impl_item(src: []const u8, st: []usize, out: []u8, start: usize) -> void const ta : usize = st[P]; const tb : usize = tok_end(src, ta); const lb : usize = find_at(src, ta, B_LBRACE); - const fr : usize = find_tok(src, ta, "for"); + const fr : usize = find_tok_to(src, ta, lb, "for"); if (lb >= src.len) { st[ERR] = 0; skip_refused(src, st, out, start, ipos, tok_end(src, ipos), R_SYNTAX); @@ -7880,9 +8294,11 @@ pub fn result_alias(src: []const u8, t: usize) -> bool { return eq < find_at(src, n, B_SEMI) and tok_is(src, skip_ws(src, eq + 1), "Result"); } -/// Before the first pass: tuple structs, Result aliases and the caller's `//@t27o reuse ` -/// lines go into the name table, so an item that uses one before its declaration reads it too. +/// Before the first pass: the declaration index is built (every lookup of a struct, a const or a fn reads +/// it instead of walking the file); tuple structs, Result aliases and the caller's `//@t27o reuse +/// ` lines go into the name table, so an item that uses one before its declaration reads it too. pub fn prescan(src: []const u8, st: []usize) -> void { + index_decls(src, st); var q : usize = skip_ws(src, 0); while (q < src.len) { const e : usize = tok_end(src, q); @@ -9123,3 +9539,225 @@ test negative_control_a_method_two_types_share_and_a_field_of_an_untyped_local_s assert(out_text_has(&out, n, "refused fn b (line 10): `!` of an operand whose type t27o cannot tell `s.on` at line 10")); assert(!out_text_has(&out, n, "== false")); } + +test the_declaration_index_answers_the_three_lookups_as_the_scans_do { + var st : [1024]usize = [0] ** 1024; + // shadowing: the first `struct A {` is the one with x, the first `fn f` the free one (u32, the impl's + // is bool), and the first `fn g` the impl's -- it has no `->`, though the free `g` returns A + const src : str = "struct A { x: u32 }\nconst K: u32 = 3;\nstatic mut S : u8 = 0;\nconst fn cf() -> u8 { 1 }\nfn f(a: u32) -> u32 { a }\nfn mk() -> Ghost { Ghost { } }\nimpl A {\n fn f(&self) -> bool { true }\n fn g(&self) { }\n}\nstruct A { y: u8 }\nstruct U;\nstruct T(u8);\n// struct Remark { }\nfn rm() -> Remark { Remark }\nfn q() -> Quoted { let s = \"struct Quoted { }\"; s }\nfn g() -> A { A { x: 1 } }\nfn ptr(p: fn(u32) -> u32) -> u32 { p(1) }\n"; + index_decls(src, &st); + assert(st[IXON] == 1); + assert(all_names_agree(src, &st)); + const a : usize = find_tok(src, 0, "A"); + const lb : usize = find_struct(src, &st, a, tok_end(src, a)); + assert(lb < src.len and src[lb] == B_LBRACE and src[lb + 2] == B_LOWER_X); + assert(st[IXHIT] != 0); + const f : usize = find_tok(src, 0, "f"); + const fr : usize = fn_ret_type(src, &st, f, tok_end(src, f)); + assert(word_is(src, fr, tok_end(src, fr), "u32")); + const g : usize = find_tok(src, 0, "g"); + assert(fn_ret_type(src, &st, g, tok_end(src, g)) == src.len); + const mk : usize = find_tok(src, 0, "mk"); + const mr : usize = fn_ret_type(src, &st, mk, tok_end(src, mk)); + assert(word_is(src, mr, tok_end(src, mr), "Ghost")); + const cf : usize = find_tok(src, 0, "cf"); + const cr : usize = fn_ret_type(src, &st, cf, tok_end(src, cf)); + assert(word_is(src, cr, tok_end(src, cr), "u8")); + assert(const_type(src, &st, cf, tok_end(src, cf)) == src.len); + const k : usize = find_tok(src, 0, "K"); + const kt : usize = const_type(src, &st, k, tok_end(src, k)); + assert(word_is(src, kt, tok_end(src, kt), "u32")); + const s : usize = find_tok(src, 0, "S"); + const sy : usize = const_type(src, &st, s, tok_end(src, s)); + assert(word_is(src, sy, tok_end(src, sy), "u8")); +} + +test negative_control_names_that_only_look_declared_are_not_in_the_index { + var st : [1024]usize = [0] ** 1024; + // a unit or tuple struct has no `{`, a struct in a comment or a text is no declaration, a name used + // but never declared is nowhere, `fn(u32) -> u32` is a type, and a const is not a fn nor a struct + const src : str = "struct A { x: u32 }\nconst K: u32 = 3;\nfn mk() -> Ghost { Ghost { } }\nstruct U;\nstruct T(u8);\n// struct Remark { }\nfn rm() -> Remark { Remark }\nfn q() -> Quoted { let s = \"struct Quoted { }\"; s }\nfn ptr(p: fn(u32) -> u32) -> u32 { p(1) }\n"; + index_decls(src, &st); + assert(st[IXON] == 1); + const names : [5]str = ["Ghost", "U", "T", "Remark", "Quoted"]; + var i : usize = 0; + while (i < 5) { + const n : usize = find_tok(src, 0, names[i]); + assert(n < src.len); + assert(find_struct(src, &st, n, tok_end(src, n)) == src.len); + assert(st[IXHIT] == 0); + i = i + 1; + } + const kk : usize = find_tok(src, 0, "K"); + assert(fn_ret_type(src, &st, kk, tok_end(src, kk)) == src.len); + assert(find_struct(src, &st, kk, tok_end(src, kk)) == src.len); + const aa : usize = find_tok(src, 0, "A"); + assert(const_type(src, &st, aa, tok_end(src, aa)) == src.len); + assert(fn_ret_type(src, &st, aa, tok_end(src, aa)) == src.len); + assert(all_names_agree(src, &st)); +} + +test negative_control_a_wrong_entry_makes_the_lookup_disagree_with_its_scan { + var st : [1024]usize = [0] ** 1024; + // the index is what the lookups read: point the entry of `struct A` into the middle of its keyword and + // the check that holds above fails, so it can fail + const src : str = "struct A { x: u32 }\nfn f(a: &A) -> u32 { a.x }\n"; + index_decls(src, &st); + assert(all_names_agree(src, &st)); + const a : usize = find_tok(src, 0, "A"); + assert(find_struct(src, &st, a, tok_end(src, a)) < src.len); + const hit : usize = st[IXHIT]; + assert(hit != 0); + st[hit + 2] = st[hit + 2] + 1; + assert(!lookups_agree(src, &st, a, tok_end(src, a))); + assert(!all_names_agree(src, &st)); +} + +test a_file_with_more_declarations_than_fit_leaves_the_index_off_and_the_scans_answer { + var big : [140]usize = [0] ** 140; + var tiny : [100]usize = [0] ** 100; + const src : str = "struct A { x: u32 }\nconst K: u32 = 3;\nstatic mut S : u8 = 0;\nconst fn cf() -> u8 { 1 }\nfn f(a: u32) -> u32 { a }\nfn mk() -> Ghost { Ghost { } }\nimpl A {\n fn f(&self) -> bool { true }\n fn g(&self) { }\n}\nstruct A { y: u8 }\nfn rm() -> Remark { Remark }\nfn q() -> Quoted { Quoted }\nfn g() -> A { A { x: 1 } }\nfn ptr(p: fn(u32) -> u32) -> u32 { p(1) }\n"; + // 140 slots: the index may take half of what is free, eleven entries do not fit; 100: no room to start + index_decls(src, &big); + assert(big[IXON] == 0); + assert(big[IXTOP] == big.len); + assert(all_names_agree(src, &big)); + index_decls(src, &tiny); + assert(tiny[IXON] == 0); + assert(all_names_agree(src, &tiny)); + const f : usize = find_tok(src, 0, "f"); + const fr : usize = fn_ret_type(src, &big, f, tok_end(src, f)); + assert(word_is(src, fr, tok_end(src, fr), "u32")); + assert(big[IXHIT] == 0); +} + +test the_name_table_stops_where_the_index_begins { + var st : [1024]usize = [0] ** 1024; + const src : str = "struct A { x: u32 }\nconst K: u32 = 3;\nfn f(a: u32) -> u32 { a }\n"; + index_decls(src, &st); + assert(st[IXON] == 1); + assert(st[IXTOP] < st.len); + const a : usize = find_tok(src, 0, "A"); + // far more names than fit: the table fills up to the index and drops the rest, the index is intact + var kind : usize = 100; + while (kind < 400) { + add_name(src, &st, kind, a, tok_end(src, a), 0, 0); + kind = kind + 1; + } + assert(st[NNAMES] > 100); + assert(st[NNAMES] < 300); + assert(NAMES + st[NNAMES] * NAME_SLOTS <= st[IXTOP]); + assert(all_names_agree(src, &st)); + assert(st[IXON] == 1); +} + +test a_conversion_reads_the_same_with_the_index_on_and_off { + var out_on : [2048]u8 = undefined; + var out_off : [2048]u8 = undefined; + var on : [1024]usize = [0] ** 1024; + var off : [100]usize = [0] ** 100; + // a struct field, a const and a fn the bang reads its operands' types from: found by the index in one + // run, by the scans in the other (a state vector of 100 slots has no room for an index) + const src : str = "pub struct Cfg { pub on: bool }\nconst FLAG: bool = true;\nfn ready() -> bool { true }\npub fn go(c: &Cfg) -> bool { !c.on && !ready() && !FLAG }\n"; + const want : str = "pub const Cfg = struct {\n on : bool,\n};\n\nconst FLAG : bool = true;\n\nfn ready() -> bool {\n return true;\n}\n\npub fn go(c: Cfg) -> bool {\n return !c.on and !ready() and !FLAG;\n}\n\n"; + const n : usize = body(src, &out_on, &on); + assert(on[IXON] == 1); + assert(out_is(n, &out_on, 0, want)); + const m : usize = body(src, &out_off, &off); + assert(off[IXON] == 0); + assert(out_is(m, &out_off, 0, want)); + assert(on[NOK] == 2); + assert(off[NOK] == 2); +} + +test line_at_counts_on_from_the_last_position_and_agrees_with_line_of { + var st : [1024]usize = [0] ** 1024; + const src : str = "a\nbb\n\nccc\ndd\n"; + // forward, past the end, then back down: the cache keeps the last position, a position before it + // counts from byte 0, and either way the line is line_of's + var p : usize = 0; + while (p < src.len + 3) { + assert(line_at(src, &st, p) == line_of(src, p)); + p = p + 1; + } + p = src.len + 3; + while (p > 0) { + p = p - 1; + assert(line_at(src, &st, p) == line_of(src, p)); + } + assert(line_at(src, &st, 9) == 4); + assert(line_at(src, &st, 2) == 2); + assert(line_at(src, &st, 9) == 4); + assert(st[LNPOS] == 9); +} + +test negative_control_a_new_pass_starts_the_line_cache_over { + var out : [2048]u8 = undefined; + var st : [1024]usize = [0] ** 1024; + // the cache sits in the slots every pass clears, so a refusal late in the file does not leave a + // position the next pass's first refusal would count from: both lines come out right + const n : usize = body("fn a() -> u32 { 1 }\nfn b(x: u32) -> u32 { let y = || x; y() }\nfn c() { }\n", &out, &st); + assert(out_text_has(&out, n, "refused fn b (line 2): closure")); + assert(st[NFN] == 3); +} + +test find_tok_to_reads_no_token_past_its_bound { + // the `for` of a trait impl header is before the `{`; the `for` of the loop inside the block, and the + // one of the next impl, are not looked for + const src : str = "impl A { fn f() { for i in 0..2 { } } }\nimpl B for C { }\n"; + const a : usize = find_tok(src, 0, "A"); + const lb : usize = find_at(src, a, B_LBRACE); + assert(find_tok_to(src, a, lb, "for") == src.len); + const b : usize = find_tok(src, lb, "B"); + assert(find_tok_to(src, b, find_at(src, b, B_LBRACE), "for") == find_tok(src, b, "for")); + assert(find_tok_to(src, b, find_at(src, b, B_LBRACE), "for") < src.len); +} + +test negative_control_find_tok_still_runs_on_past_a_block_when_no_bound_is_given { + const src : str = "impl A { }\nimpl B for C { }\n"; + const a : usize = find_tok(src, 0, "A"); + // unbounded, the walk from A finds the next impl's `for` (the cost the bound removes); bounded by A's + // own `{` it finds nothing + assert(find_tok(src, a, "for") < src.len); + assert(find_tok_to(src, a, find_at(src, a, B_LBRACE), "for") == src.len); +} + +test the_index_names_the_impl_that_owns_a_method_and_the_last_let_that_types_a_name { + var st : [1024]usize = [0] ** 1024; + // `get` is in both impls: with no receiver type the first impl owns it, with a receiver typed Q the + // second; `p` is bound to P and then to Q, and the use after both is Q's (Rust shadows) + const src : str = "pub struct P { pub n: u32 }\npub struct Q { pub n: u32 }\nimpl P {\n pub fn get(&self) -> u32 { self.n }\n pub fn set(&mut self, v: u32) { self.n = v; }\n}\nimpl Q {\n pub fn get(&self) -> bool { true }\n}\nimpl Default for Q { fn default() -> Q { Q { n: 0 } } }\nfn run() -> bool {\n let p = P { n: 1 };\n let q: Q = Q { n: 2 };\n let r = Q::default();\n let p = Q { n: 3 };\n p.get() && q.get() && r.get()\n}\n"; + index_decls(src, &st); + assert(st[IXON] == 1); + assert(all_names_agree(src, &st)); + var g : usize = 0; + var q : usize = skip_ws(src, 0); + var last_p : usize = 0; + var qty : usize = 0; + while (q < src.len) { + const e : usize = tok_end(src, q); + if (word_is(src, q, e, "get") and g == 0) { g = q; } + if (word_is(src, q, e, "p")) { last_p = q; } + if (word_is(src, q, e, "Q") and qty == 0) { qty = q; } + q = skip_ws(src, e); + } + assert(g > 0 and last_p > 0 and qty > 0); + // no receiver type: the first impl with a `get` taking self is P's + assert(method_owner(src, &st, g, tok_end(src, g), 0, 0)); + assert(word_is(src, st[OLO], st[OHI], "P")); + assert(st[OKIND] == 1); + // a receiver typed Q: Q's impl + assert(method_owner(src, &st, g, tok_end(src, g), qty, tok_end(src, qty))); + assert(word_is(src, st[OLO], st[OHI], "Q")); + // the last `let p` that types p before its use is `let p = Q {` + const lp : usize = let_owner(src, &st, last_p, tok_end(src, last_p)); + assert(lp < src.len and word_is(src, lp, tok_end(src, lp), "Q")); + assert(lp > find_tok(src, 0, "run")); + // a name no `let` types, and a method no impl has + const run : usize = find_tok(src, 0, "run"); + assert(let_owner(src, &st, run, tok_end(src, run)) == src.len); + assert(!method_owner(src, &st, run, tok_end(src, run), 0, 0)); + // the impl around a point inside `impl Default for Q` is the type after the `for` + assert(impl_around(src, &st, src.len - 40)); + assert(word_is(src, st[OLO], st[OHI], "Q")); +}