nomen-lang 0.0.17 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1777,6 +1777,11 @@ var AccessFunctionCallNode = class extends BaseNode {
1777
1777
  * FunctionCallNode.nullable_param_indices.
1778
1778
  */
1779
1779
  nullable_param_indices;
1780
+ /**
1781
+ * Indices of arguments whose corresponding callee parameter is a `view T`
1782
+ * (notably `view string`). See FunctionCallNode.view_param_indices.
1783
+ */
1784
+ view_param_indices;
1780
1785
  swap_params;
1781
1786
  variadic_param_name;
1782
1787
  variadic_param_index;
@@ -1828,6 +1833,12 @@ var AccessFunctionCallNode = class extends BaseNode {
1828
1833
  * the programmer is asserting the array is long enough positionally.
1829
1834
  */
1830
1835
  skip_bounds_check;
1836
+ /**
1837
+ * The concrete FunctionNode the checker resolved this method call to.
1838
+ * See FunctionCallNode.resolved_function — used by build-time passes for
1839
+ * callee-signature facts (notably hidden string-length companions).
1840
+ */
1841
+ resolved_function;
1831
1842
  constructor(start, name, type, params, is_static) {
1832
1843
  super("access_func", start);
1833
1844
  this.name = name;
@@ -1901,6 +1912,14 @@ var FunctionCallNode = class extends BaseNode {
1901
1912
  * struct pointer — see ROADBLOCKS "Nullable structs".
1902
1913
  */
1903
1914
  nullable_param_indices;
1915
+ /**
1916
+ * Indices of arguments whose corresponding callee parameter is a `view T`
1917
+ * (notably `view string`). Populated during the check pass when the
1918
+ * callee's signature is resolved. The build uses this to pass the (ptr,
1919
+ * len) pair at the call boundary: a view-typed argument passes through,
1920
+ * an owned `string` argument is wrapped with its strlen.
1921
+ */
1922
+ view_param_indices;
1904
1923
  swap_params;
1905
1924
  variadic_param_name;
1906
1925
  variadic_param_index;
@@ -1934,6 +1953,14 @@ var FunctionCallNode = class extends BaseNode {
1934
1953
  * tag+payload temp), mirroring `Enum.case(args)` access calls.
1935
1954
  */
1936
1955
  is_enum_shorthand;
1956
+ /**
1957
+ * The concrete FunctionNode the checker resolved this call to (plain
1958
+ * function, struct constructor, or monomorphized/specialized copy).
1959
+ * Build-time passes consult it for callee-signature facts that only the
1960
+ * checker can resolve robustly (overloads, monos) — notably the hidden
1961
+ * string-length companion params (`ParameterNode.hidden_len`).
1962
+ */
1963
+ resolved_function;
1937
1964
  constructor(start, name, type, params, is_static) {
1938
1965
  super("func_call", start);
1939
1966
  this.name = name;
@@ -2055,6 +2082,29 @@ function type_from_value_node$1(node) {
2055
2082
  return new Type("");
2056
2083
  }
2057
2084
  //#endregion
2085
+ //#region ../src/build_common/hidden_len.ts
2086
+ /**
2087
+ * Indices of a call's DECLARED arguments whose callee parameter carries the
2088
+ * hidden string-length companion (`ParameterNode.hidden_len`, stamped by the
2089
+ * post-check `stamp_hidden_string_lens` pass). The backends append one
2090
+ * companion argument right after each such argument — `long _<name>_len` on
2091
+ * C, the next AAPCS slot on aarch64 — and the hoist scan treats these args
2092
+ * like `.length` reads so a loop-invariant strlen temp covers them.
2093
+ *
2094
+ * Resolution comes from the checker's `resolved_function` stamp (exact for
2095
+ * overloads and monomorphized copies); an unstamped call site (synthesized
2096
+ * nodes, indirect func-value calls) yields no companions, which matches the
2097
+ * qualification pass — those callees are never stamped `hidden_len`.
2098
+ */
2099
+ function callee_hidden_len_indices(call) {
2100
+ const func = call.resolved_function;
2101
+ if (!func) return [];
2102
+ const self_offset = func.params[0]?.is_self_param ? 1 : 0;
2103
+ const out = [];
2104
+ for (let i = 0; i < call.params.length; i++) if (func.params[i + self_offset]?.hidden_len) out.push(i);
2105
+ return out;
2106
+ }
2107
+ //#endregion
2058
2108
  //#region ../src/build_common/mono_name.ts
2059
2109
  function mono_type_name(type_or_name, type_args) {
2060
2110
  const name = typeof type_or_name === "string" ? type_or_name : type_or_name.name;
@@ -2153,6 +2203,27 @@ var ParameterNode = class extends BaseNode {
2153
2203
  is_variadic_tuple;
2154
2204
  func_params;
2155
2205
  func_return_type;
2206
+ /**
2207
+ * Parallel-length equalities (`a.length == b.length` conjuncts) stripped
2208
+ * from `constraint` at signature-registration time — before any call site
2209
+ * can observe this function, so declaration order never matters (a
2210
+ * forward-referenced callee must not ask its caller to prove a clause no
2211
+ * call site could). The callee's own param check seeds
2212
+ * `status.equal_lengths` from these so its body still gets the assumption.
2213
+ */
2214
+ stripped_length_equalities;
2215
+ /**
2216
+ * True for a by-value `string` param whose function body reads
2217
+ * `<name>.length` (and never rebinds/mutates/escapes it) — stamped by the
2218
+ * post-check pass `stamp_hidden_string_lens`. The backends lower it as TWO
2219
+ * parameters: the `char*` itself plus a trailing companion length
2220
+ * (`long _<name>_len` on C; the next AAPCS slot on aarch64), so the body's
2221
+ * `.length` reads are register/stack loads instead of a per-call `strlen`.
2222
+ * Call sites append the companion argument (a hoisted loop-invariant
2223
+ * strlen temp when one exists — see scan_string_length_hoists). Mirrors
2224
+ * the nullable `_has` companion convention.
2225
+ */
2226
+ hidden_len;
2156
2227
  constructor(start, name, type, default_value, is_self_param, declaration) {
2157
2228
  super("param", start);
2158
2229
  this.name = name;
@@ -2217,6 +2288,12 @@ var FunctionNode = class extends BaseNode {
2217
2288
  type_params = [];
2218
2289
  scope;
2219
2290
  /**
2291
+ * Set once the post-check `stamp_hidden_string_lens` pass has considered
2292
+ * this function — guards against double-stamping when a check pass runs
2293
+ * twice over a shared tree.
2294
+ */
2295
+ hidden_len_stamped;
2296
+ /**
2220
2297
  * Optional contract on the return value, parsed from `out TYPE: constraint`.
2221
2298
  * The placeholder `out` refers to the return value; other identifiers refer
2222
2299
  * to the function's parameters. Used by check_function_call to propagate
@@ -4396,6 +4473,11 @@ function build_function_node$1(node, status) {
4396
4473
  first_pass_slot += 1;
4397
4474
  continue;
4398
4475
  }
4476
+ if (param.type.is_view) {
4477
+ first_pass_slot += 2;
4478
+ continue;
4479
+ }
4480
+ if (param.hidden_len) first_pass_slot++;
4399
4481
  const is_struct_type = !!status.structs.find((s) => s.name === param.type.name && !s.is_simple_type);
4400
4482
  const is_trait_type = !!status.traits.find((t) => t.name === param.type.name);
4401
4483
  const is_enum_with_data = !!status.enums.find((e) => e.name === param.type.name && e.has_associated_data);
@@ -4433,7 +4515,11 @@ function build_function_node$1(node, status) {
4433
4515
  status.ref_class_slots = /* @__PURE__ */ new Map();
4434
4516
  status.function_struct_param_slots = /* @__PURE__ */ new Set();
4435
4517
  const old_variadic_params_aarch64 = status.function_variadic_params;
4518
+ const old_view_params = status.function_view_params;
4436
4519
  status.function_variadic_params = /* @__PURE__ */ new Set();
4520
+ status.function_view_params = /* @__PURE__ */ new Set();
4521
+ const old_hidden_len_params = status.hidden_len_params;
4522
+ status.hidden_len_params = /* @__PURE__ */ new Set();
4437
4523
  status.moved_class_params = /* @__PURE__ */ new Map();
4438
4524
  let moved_param_save_slots = /* @__PURE__ */ new Map();
4439
4525
  if (has_body) {
@@ -4454,6 +4540,22 @@ function build_function_node$1(node, status) {
4454
4540
  }
4455
4541
  param_idx++;
4456
4542
  }
4543
+ if (param.type.is_view) {
4544
+ const offset = allocate_stack_space(status, 16, 16);
4545
+ status.stack_offsets.set(param.name, offset);
4546
+ status.function_view_params.add(param.name);
4547
+ for (const half of [0, 1]) {
4548
+ const p_slot = param_idx + half;
4549
+ if (p_slot < 8) status.code += `str ${param_regs[p_slot]}, [x29, #${offset + half * 8}]\n`;
4550
+ else {
4551
+ const k = p_slot - 8;
4552
+ status.code += `ldr x9, [x29, #${overflow_placeholder(node.name, k)}]\n`;
4553
+ status.code += `str x9, [x29, #${offset + half * 8}]\n`;
4554
+ }
4555
+ }
4556
+ param_idx += 2;
4557
+ continue;
4558
+ }
4457
4559
  if (callee_map.has(param.name)) {
4458
4560
  const reg = callee_map.get(param.name);
4459
4561
  status.function_param_regs.set(param.name, reg);
@@ -4487,6 +4589,19 @@ function build_function_node$1(node, status) {
4487
4589
  }
4488
4590
  }
4489
4591
  param_idx++;
4592
+ if (param.hidden_len) {
4593
+ const len_offset = allocate_stack_space(status, 8, 8);
4594
+ status.stack_offsets.set(`_${param.name}_len`, len_offset);
4595
+ if (!status.hidden_len_params) status.hidden_len_params = /* @__PURE__ */ new Set();
4596
+ status.hidden_len_params.add(param.name);
4597
+ if (param_idx < 8) status.code += `str ${param_regs[param_idx]}, [x29, #${len_offset}]\n`;
4598
+ else {
4599
+ const k = param_idx - 8;
4600
+ status.code += `ldr x9, [x29, #${overflow_placeholder(node.name, k)}]\n`;
4601
+ status.code += `str x9, [x29, #${len_offset}]\n`;
4602
+ }
4603
+ param_idx++;
4604
+ }
4490
4605
  if (param.declaration === "var") status.function_param_vars.add(param.name);
4491
4606
  if (param.type.is_array) {
4492
4607
  if (!param.is_variadic ? array_struct_name(param.type, status) : void 0) {
@@ -4502,6 +4617,7 @@ function build_function_node$1(node, status) {
4502
4617
  for (let i = 0; i < node.params.length; i++) {
4503
4618
  const param = node.params[i];
4504
4619
  if (param.is_variadic) pidx++;
4620
+ if (param.hidden_len) pidx++;
4505
4621
  if (param.is_moved) {
4506
4622
  if (!!status.structs.find((s) => s.name === param.type.name && s.is_class)) {
4507
4623
  const reg = callee_map.get(param.name) ?? param_regs[pidx];
@@ -4627,6 +4743,8 @@ function build_function_node$1(node, status) {
4627
4743
  status.ref_class_slots = old_ref_class_slots;
4628
4744
  status.function_struct_param_slots = old_struct_param_slots;
4629
4745
  status.function_variadic_params = old_variadic_params_aarch64;
4746
+ status.function_view_params = old_view_params;
4747
+ status.hidden_len_params = old_hidden_len_params;
4630
4748
  status.function_return_label = old_return_label;
4631
4749
  status.struct_return_buffer = void 0;
4632
4750
  status.return_buffer_stack_offset = void 0;
@@ -6314,6 +6432,108 @@ function build_swap_params(node, status) {
6314
6432
  }
6315
6433
  }
6316
6434
  //#endregion
6435
+ //#region ../src/build_aarch64/utils/view_value.ts
6436
+ /**
6437
+ * Resolve the struct name of an expression's value by walking the access
6438
+ * chain through the (monomorphized) struct table — bare names via
6439
+ * variable_types / scoped declarations / `self`, field accesses via the
6440
+ * base struct's field type, method calls via the method's return type.
6441
+ * Fallback for cached node types that lost their modifiers (generic bodies
6442
+ * re-checked after monomorphization can carry a stale plain `string`).
6443
+ */
6444
+ function value_struct_name$1(node, status) {
6445
+ if (node.node_type === "value") {
6446
+ const name = node.value;
6447
+ if (name === "self" && status.current_struct) return status.current_struct.name;
6448
+ const vt = status.variable_types?.get(name);
6449
+ if (vt?.name) return vt.name;
6450
+ const decl = status.scoped_declarations.findLast((d) => d.name === name);
6451
+ if (decl?.type?.name) return decl.type.name;
6452
+ return;
6453
+ }
6454
+ if (node.node_type === "access") {
6455
+ const inner = node.access;
6456
+ const base = value_struct_name$1(node.target, status);
6457
+ if (!base) return void 0;
6458
+ const struct = status.structs.find((s) => s.name === base && !s.is_generic);
6459
+ if (!struct) return void 0;
6460
+ if (inner.node_type === "access_field") return struct.fields.find((f) => f.name === inner.name)?.type?.name;
6461
+ if (inner.node_type === "access_func") return struct.functions.find((f) => f.name === inner.name)?.return_type?.name;
6462
+ }
6463
+ }
6464
+ /**
6465
+ * Whether an expression is a `view T` VALUE — a (ptr, len) pair that can be
6466
+ * passed to a `view T` parameter unchanged (loaded from the value's two
6467
+ * stack slots, or produced directly by a view-returning call). Recovers
6468
+ * `is_view` from the cached node type, the declared variable type, the
6469
+ * current function's view params, or (for method calls whose cached type
6470
+ * lost the modifier) the callee's declared return type.
6471
+ */
6472
+ function is_view_value$1(node, status) {
6473
+ if (type_from_value_node$1(node)?.is_view) return true;
6474
+ if (node.node_type === "value") {
6475
+ const name = node.value;
6476
+ if (status.variable_types?.get(name)?.is_view) return true;
6477
+ if (status.function_view_params?.has(name)) return true;
6478
+ }
6479
+ if (node.node_type === "access" && node.access.node_type === "access_func") {
6480
+ const access = node.access;
6481
+ const recv_struct = value_struct_name$1(node.target, status);
6482
+ if ((status.structs.find((s) => s.name === recv_struct && !s.is_generic)?.functions.find((f) => f.name === access.name || f.name === `#${access.name}`))?.return_type?.is_view) return true;
6483
+ }
6484
+ return false;
6485
+ }
6486
+ /**
6487
+ * Emit the (ptr, len) pair for a `view string` argument into x0/x1.
6488
+ * - A view VALUE (a view local/param's two stack slots, or a view-returning
6489
+ * call that leaves the pair in x0/x1) passes through unchanged.
6490
+ * - An owned string expression builds its pointer into x0, then measures it
6491
+ * with strlen — an implicit owned→view borrow; ownership stays with the
6492
+ * caller.
6493
+ */
6494
+ function emit_view_string_arg(arg, status) {
6495
+ if (is_view_value$1(arg, status)) {
6496
+ if (arg.node_type === "value") {
6497
+ const base = status.stack_offsets?.get(arg.value);
6498
+ if (base !== void 0) {
6499
+ status.code += `ldr x0, [x29, #${base}]\n`;
6500
+ status.code += `ldr x1, [x29, #${base + 8}]\n`;
6501
+ return;
6502
+ }
6503
+ }
6504
+ build_node$1(arg, status);
6505
+ if (!status.code.endsWith("\n")) status.code += "\n";
6506
+ return;
6507
+ }
6508
+ build_node$1(arg, status);
6509
+ if (!status.code.endsWith("\n")) status.code += "\n";
6510
+ status.code += `str x0, [sp, #-16]!\n`;
6511
+ status.code += `bl _strlen\n`;
6512
+ status.code += `mov x1, x0\n`;
6513
+ status.code += `ldr x0, [sp], #16\n`;
6514
+ }
6515
+ /**
6516
+ * Materialize a `view string` into an OWNED heap string. The (ptr, len)
6517
+ * pair must be in x0/x1 on entry; on exit x0 holds a malloc'd, null-
6518
+ * terminated copy of exactly len bytes (and the caller must reclaim it —
6519
+ * set last_result_is_heap / mark the target as a heap string). Mirrors the
6520
+ * view `.to_string` builtin sequence.
6521
+ */
6522
+ function emit_view_materialize_owned(status) {
6523
+ status.code += `stp x0, x1, [sp, #-16]!\n`;
6524
+ status.code += `add x0, x1, #1\n`;
6525
+ emit_malloc(status);
6526
+ status.code += `str x0, [sp, #-16]!\n`;
6527
+ status.code += `ldr x1, [sp, #16]\n`;
6528
+ status.code += `ldr x2, [sp, #24]\n`;
6529
+ status.code += `ldr x0, [sp]\n`;
6530
+ status.code += `bl _memcpy\n`;
6531
+ status.code += `ldr x0, [sp]\n`;
6532
+ status.code += `ldr x1, [sp, #24]\n`;
6533
+ status.code += `strb wzr, [x0, x1]\n`;
6534
+ status.code += `add sp, sp, #32\n`;
6535
+ }
6536
+ //#endregion
6317
6537
  //#region ../src/build_aarch64/build_declaration_node.ts
6318
6538
  /**
6319
6539
  * Pass a struct-typed param by address. For ValueNode (variable references)
@@ -6476,10 +6696,13 @@ function build_constructor_params(fc, param_regs, status) {
6476
6696
  const elem_struct = status.structs.find((s) => s.name === elem_type_name && !s.is_simple_type);
6477
6697
  const elem_size = elem_struct ? get_struct_size(elem_type_name, status) : 8;
6478
6698
  const count = arr.values.length;
6479
- const arr_offset = allocate_stack_space(status, Math.max(count, 1) * elem_size, 16);
6699
+ const arr_offset = allocate_stack_space(status, 8 + Math.max(count, 1) * elem_size, 16);
6700
+ const data_base = arr_offset + 8;
6701
+ status.code += `mov x0, #${count}\n`;
6702
+ status.code += `str x0, [x29, #${arr_offset}]\n`;
6480
6703
  for (let j = count - 1; j >= 0; j--) {
6481
6704
  const arg = arr.values[j];
6482
- const slot_offset = arr_offset + j * elem_size;
6705
+ const slot_offset = data_base + j * elem_size;
6483
6706
  if (elem_struct && arg.node_type === "func_call") {
6484
6707
  const tfc = arg;
6485
6708
  const fc_param_regs = [
@@ -6539,7 +6762,7 @@ function build_constructor_params(fc, param_regs, status) {
6539
6762
  emit_int_immediate(status, String(count));
6540
6763
  if (!status.code.endsWith("\n")) status.code += "\n";
6541
6764
  status.code += `str x0, [x29, #${count_slot}]\n`;
6542
- status.code += `add x0, x29, #${arr_offset}\n`;
6765
+ status.code += `add x0, x29, #${data_base}\n`;
6543
6766
  status.code += `str x0, [x29, #${ptr_slot}]\n`;
6544
6767
  const reg_slots = Math.min(total_slots, 7);
6545
6768
  for (let i = 0; i < reg_slots; i++) status.code += `ldr ${param_regs[i]}, [x29, #${slots_base + i * 8}]\n`;
@@ -7414,10 +7637,18 @@ function build_declaration_node$1(node, status) {
7414
7637
  const value_node = node.value;
7415
7638
  const raw = get_raw_value$1(value_node);
7416
7639
  const is_literal = /^(\+|-)?\d+(\.\d+)?$/.test(raw) || raw.startsWith("\"") || raw === "true" || raw === "false";
7417
- if (!!status.function_return_label) {
7640
+ const use_stack = !!status.function_return_label;
7641
+ const is_heap_alias = node.type.name === "string" && !is_literal && status.heap_strings?.has(raw);
7642
+ if (use_stack) {
7418
7643
  const offset = allocate_stack_space(status, size, size);
7419
7644
  status.stack_offsets.set(node.name, offset);
7420
- if (node.type.name === "string" && !is_literal && status.heap_strings?.has(raw)) {
7645
+ if (node.type.name === "string" && !node.type.is_view && is_view_value$1(node.value, status)) {
7646
+ emit_view_string_arg(node.value, status);
7647
+ emit_view_materialize_owned(status);
7648
+ status.code += `str x0, [x29, #${offset}]\n`;
7649
+ mark_heap_string(status, node.name);
7650
+ status.last_result_is_heap = false;
7651
+ } else if (is_heap_alias) {
7421
7652
  emit_var_load(status, "x0", raw, 8);
7422
7653
  emit_strdup(status);
7423
7654
  status.code += `str x0, [x29, #${offset}]\n`;
@@ -7570,6 +7801,14 @@ function build_declaration_node$1(node, status) {
7570
7801
  const offset = allocate_stack_space(status, size, size);
7571
7802
  status.stack_offsets.set(node.name, offset);
7572
7803
  } else emit_data(status, `${node.name}: .space ${size}\n`);
7804
+ if (node.type.name === "string" && !node.type.is_view && is_view_value$1(node.value, status)) {
7805
+ emit_view_string_arg(node.value, status);
7806
+ emit_view_materialize_owned(status);
7807
+ emit_var_store(status, "x0", node.name, size);
7808
+ mark_heap_string(status, node.name);
7809
+ status.last_result_is_heap = false;
7810
+ return;
7811
+ }
7573
7812
  build_node$1(node.value, status);
7574
7813
  emit_var_store(status, "x0", node.name, size);
7575
7814
  check_heap();
@@ -8292,10 +8531,13 @@ function build_function_call_node$1(node, status) {
8292
8531
  const elem_type_name = arr.type.name || "int";
8293
8532
  const elem_struct = status.structs.find((s) => s.name === elem_type_name && !s.is_simple_type);
8294
8533
  const elem_size = elem_struct ? get_struct_size(elem_type_name, status) : 8;
8295
- const arr_offset = allocate_stack_space(status, arr.values.length * elem_size, 16);
8534
+ const arr_offset = allocate_stack_space(status, 8 + Math.max(arr.values.length, 1) * elem_size, 16);
8535
+ const data_base = arr_offset + 8;
8536
+ status.code += `mov x0, #${arr.values.length}\n`;
8537
+ status.code += `str x0, [x29, #${arr_offset}]\n`;
8296
8538
  for (let j = arr.values.length - 1; j >= 0; j--) {
8297
8539
  const arg = arr.values[j];
8298
- const slot_offset = arr_offset + j * elem_size;
8540
+ const slot_offset = data_base + j * elem_size;
8299
8541
  if (elem_struct && arg.node_type === "func_call") {
8300
8542
  const fc = arg;
8301
8543
  const fc_param_regs = [
@@ -8338,16 +8580,29 @@ function build_function_call_node$1(node, status) {
8338
8580
  non_variadic.push({
8339
8581
  node: param,
8340
8582
  is_struct: is_struct_type$2(param_type, status) || is_enum_with_data_type$1(param_type, status),
8341
- is_ref: !!is_ref_param
8583
+ is_ref: !!is_ref_param,
8584
+ is_view: !!node.view_param_indices?.includes(i)
8342
8585
  });
8343
8586
  }
8344
8587
  const nv = non_variadic.length;
8345
- const total_slots = nv + 2;
8588
+ const nv_slot = [];
8589
+ let nv_total = 0;
8590
+ for (let i = 0; i < nv; i++) {
8591
+ nv_slot.push(nv_total);
8592
+ nv_total += non_variadic[i].is_view ? 2 : 1;
8593
+ }
8594
+ const total_slots = nv_total + 2;
8346
8595
  const slots_base = allocate_stack_space(status, total_slots * 8, 16);
8347
- const count_slot = slots_base + nv * 8;
8596
+ const count_slot = slots_base + nv_total * 8;
8348
8597
  const ptr_slot = count_slot + 8;
8349
8598
  for (let i = nv - 1; i >= 0; i--) {
8350
8599
  const ep = non_variadic[i];
8600
+ if (ep.is_view) {
8601
+ emit_view_string_arg(ep.node, status);
8602
+ status.code += `str x0, [x29, #${slots_base + nv_slot[i] * 8}]\n`;
8603
+ status.code += `str x1, [x29, #${slots_base + (nv_slot[i] + 1) * 8}]\n`;
8604
+ continue;
8605
+ }
8351
8606
  if (ep.is_struct) emit_struct_address(ep.node, status);
8352
8607
  else if (ep.is_ref) {
8353
8608
  const ep_name = ep.node.node_type === "value" ? ep.node.value : void 0;
@@ -8356,11 +8611,11 @@ function build_function_call_node$1(node, status) {
8356
8611
  else emit_address_of(ep.node, status);
8357
8612
  } else build_node$1(ep.node, status);
8358
8613
  if (!status.code.endsWith("\n")) status.code += "\n";
8359
- status.code += `str x0, [x29, #${slots_base + i * 8}]\n`;
8614
+ status.code += `str x0, [x29, #${slots_base + nv_slot[i] * 8}]\n`;
8360
8615
  }
8361
8616
  status.code += `mov x0, #${arr.values.length}\n`;
8362
8617
  status.code += `str x0, [x29, #${count_slot}]\n`;
8363
- status.code += `add x0, x29, #${arr_offset}\n`;
8618
+ status.code += `add x0, x29, #${data_base}\n`;
8364
8619
  status.code += `str x0, [x29, #${ptr_slot}]\n`;
8365
8620
  for (let s = 0; s < total_slots; s++) {
8366
8621
  const slot = start_reg + s;
@@ -8381,13 +8636,33 @@ function build_function_call_node$1(node, status) {
8381
8636
  }
8382
8637
  }
8383
8638
  } else {
8384
- const has_args = node.params.length > 0;
8639
+ const view_arg_set = new Set(node.view_param_indices ?? []);
8640
+ const hidden_len_indices = callee_hidden_len_indices(node);
8641
+ const hidden_len_set = new Set(hidden_len_indices);
8642
+ const hidden_len_slots = /* @__PURE__ */ new Map();
8643
+ const arg_slot = [];
8644
+ let total_slots = 0;
8645
+ for (let i = 0; i < node.params.length; i++) {
8646
+ arg_slot.push(total_slots);
8647
+ total_slots += view_arg_set.has(i) ? 2 : 1;
8648
+ if (hidden_len_set.has(i)) {
8649
+ hidden_len_slots.set(i, total_slots);
8650
+ total_slots += 1;
8651
+ }
8652
+ }
8653
+ const has_args = total_slots > 0;
8385
8654
  let args_base = 0;
8386
- if (has_args) args_base = allocate_stack_space(status, node.params.length * 8, 16);
8655
+ if (has_args) args_base = allocate_stack_space(status, total_slots * 8, 16);
8387
8656
  for (let i = node.params.length - 1; i >= 0; i--) {
8388
8657
  const param = node.params[i];
8389
8658
  const param_type = param.type?.name || "";
8390
8659
  const is_ref_param = node.ref_param_indices?.includes(i);
8660
+ if (view_arg_set.has(i)) {
8661
+ emit_view_string_arg(param, status);
8662
+ status.code += `str x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
8663
+ status.code += `str x1, [x29, #${args_base + (arg_slot[i] + 1) * 8}]\n`;
8664
+ continue;
8665
+ }
8391
8666
  const arr_arg_name = param.node_type === "value" ? param.value : void 0;
8392
8667
  if (!!arr_arg_name && !!status.heap_array_vars?.has(arr_arg_name)) build_node$1(param, status);
8393
8668
  else if (param.node_type === "array" && param_type) {
@@ -8445,19 +8720,35 @@ function build_function_call_node$1(node, status) {
8445
8720
  } else if (is_struct_type$2(param_type, status) || is_enum_with_data_type$1(param_type, status)) emit_struct_address(node.params[i], status);
8446
8721
  else build_node$1(node.params[i], status);
8447
8722
  if (!status.code.endsWith("\n")) status.code += "\n";
8448
- status.code += `str x0, [x29, #${args_base + i * 8}]\n`;
8723
+ status.code += `str x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
8724
+ }
8725
+ for (const i of hidden_len_indices) {
8726
+ const arg = node.params[i];
8727
+ let computed = false;
8728
+ if (arg.node_type === "value") {
8729
+ const hoisted = status.string_length_slots?.get(arg.value);
8730
+ if (hoisted !== void 0) {
8731
+ status.code += `ldr x0, [x29, #${hoisted}]\n`;
8732
+ computed = true;
8733
+ }
8734
+ }
8735
+ if (!computed) {
8736
+ status.code += `ldr x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
8737
+ status.code += `bl _strlen\n`;
8738
+ }
8739
+ status.code += `str x0, [x29, #${args_base + hidden_len_slots.get(i) * 8}]\n`;
8449
8740
  }
8450
8741
  if (has_args) {
8451
- for (let i = 0; i < node.params.length; i++) {
8452
- const slot = start_reg + i;
8742
+ for (let s = 0; s < total_slots; s++) {
8743
+ const slot = start_reg + s;
8453
8744
  if (slot >= 8) continue;
8454
8745
  const reg = param_regs[slot];
8455
8746
  if (reg === "x0") continue;
8456
- status.code += `ldr ${reg}, [x29, #${args_base + i * 8}]\n`;
8747
+ status.code += `ldr ${reg}, [x29, #${args_base + s * 8}]\n`;
8457
8748
  }
8458
8749
  if (!is_struct) status.code += `ldr x0, [x29, #${args_base}]\n`;
8459
8750
  }
8460
- overflow_count = Math.max(0, node.params.length - (8 - start_reg));
8751
+ overflow_count = Math.max(0, total_slots - (8 - start_reg));
8461
8752
  if (overflow_count > 0) {
8462
8753
  outgoing_size = Math.ceil(overflow_count * 8 / 16) * 16;
8463
8754
  status.code += `sub sp, sp, #${outgoing_size}\n`;
@@ -8769,9 +9060,13 @@ function static_enum_case(operand, enum_node, status) {
8769
9060
  //#region ../src/build_aarch64/build_operation_node.ts
8770
9061
  let string_counter$1 = 0;
8771
9062
  let coalesce_counter = 0;
9063
+ let view_cmp_counter = 0;
9064
+ let sc_counter = 0;
8772
9065
  function reset_string_counter$1() {
8773
9066
  string_counter$1 = 0;
8774
9067
  coalesce_counter = 0;
9068
+ view_cmp_counter = 0;
9069
+ sc_counter = 0;
8775
9070
  }
8776
9071
  function is_comparison(op) {
8777
9072
  return [
@@ -9052,6 +9347,29 @@ function build_operation_node$1(node, status) {
9052
9347
  status.code += `cset x0, ${node.op === "==" ? "eq" : "ne"}\n`;
9053
9348
  return;
9054
9349
  }
9350
+ if ((node.op === "==" || node.op === "!=") && node.operator_func?.struct_name === "string" && (is_view_value$1(node.left_value, status) || is_view_value$1(node.right_value, status))) {
9351
+ const id = view_cmp_counter++;
9352
+ const len_ne_label = `.Lview_cmp_len_ne_${id}`;
9353
+ const done_label = `.Lview_cmp_done_${id}`;
9354
+ emit_view_string_arg(node.right_value, status);
9355
+ status.code += `stp x0, x1, [sp, #-16]!\n`;
9356
+ emit_view_string_arg(node.left_value, status);
9357
+ status.code += `ldr x2, [sp, #8]\n`;
9358
+ status.code += `cmp x1, x2\n`;
9359
+ status.code += `b.ne ${len_ne_label}\n`;
9360
+ status.code += `mov x2, x1\n`;
9361
+ status.code += `ldr x1, [sp]\n`;
9362
+ status.code += `bl _memcmp\n`;
9363
+ status.code += `cmp x0, #0\n`;
9364
+ status.code += `cset x0, ${node.op === "==" ? "eq" : "ne"}\n`;
9365
+ status.code += `b ${done_label}\n`;
9366
+ status.code += `${len_ne_label}:\n`;
9367
+ status.code += `mov x0, #0\n`;
9368
+ if (node.op === "!=") status.code += `mov x0, #1\n`;
9369
+ status.code += `${done_label}:\n`;
9370
+ status.code += `add sp, sp, #16\n`;
9371
+ return;
9372
+ }
9055
9373
  if (node.operator_func) {
9056
9374
  const left_type = type_from_value_node$1(node.left_value);
9057
9375
  const is_array_op = node.operator_func.struct_name.startsWith("Array") && left_type.is_array;
@@ -9185,6 +9503,25 @@ function build_operation_node$1(node, status) {
9185
9503
  else status.code += `fmov x0, d0\n`;
9186
9504
  return;
9187
9505
  }
9506
+ if (node.op === "&&" || node.op === "||") {
9507
+ const id = sc_counter++;
9508
+ const skip_right = `.Lsc_skip_${id}`;
9509
+ const done = `.Lsc_done_${id}`;
9510
+ build_operand$1(node.left_value, "x0", status);
9511
+ if (!status.code.endsWith("\n")) status.code += "\n";
9512
+ status.code += `cmp x0, #0\n`;
9513
+ status.code += node.op === "&&" ? `b.eq ${skip_right}\n` : `b.ne ${skip_right}\n`;
9514
+ build_operand$1(node.right_value, "x0", status);
9515
+ if (!status.code.endsWith("\n")) status.code += "\n";
9516
+ status.code += `cmp x0, #0\n`;
9517
+ status.code += `cset x0, ne\n`;
9518
+ status.code += `b ${done}\n`;
9519
+ status.code += `${skip_right}:\n`;
9520
+ if (node.op === "&&") status.code += `mov x0, #0\n`;
9521
+ else status.code += `mov x0, #1\n`;
9522
+ status.code += `${done}:\n`;
9523
+ return;
9524
+ }
9188
9525
  const need_spill = !is_simple(node.left_value);
9189
9526
  build_operand$1(node.right_value, "x2", status);
9190
9527
  if (need_spill) {
@@ -9195,19 +9532,7 @@ function build_operation_node$1(node, status) {
9195
9532
  if (!status.code.endsWith("\n")) status.code += "\n";
9196
9533
  if (need_spill) status.code += `ldr x2, [sp], #16\n`;
9197
9534
  const unsigned = is_unsigned_type(node.left_value) || is_unsigned_type(node.right_value);
9198
- if (node.op === "&&") {
9199
- status.code += `cmp x1, #0\n`;
9200
- status.code += `cset x1, ne\n`;
9201
- status.code += `cmp x2, #0\n`;
9202
- status.code += `cset x2, ne\n`;
9203
- status.code += `and x0, x1, x2\n`;
9204
- } else if (node.op === "||") {
9205
- status.code += `cmp x1, #0\n`;
9206
- status.code += `cset x1, ne\n`;
9207
- status.code += `cmp x2, #0\n`;
9208
- status.code += `cset x2, ne\n`;
9209
- status.code += `orr x0, x1, x2\n`;
9210
- } else if (is_comparison(node.op)) {
9535
+ if (is_comparison(node.op)) {
9211
9536
  status.code += `cmp x1, x2\n`;
9212
9537
  status.code += `cset x0, ${map_cmp(node.op, unsigned)}\n`;
9213
9538
  } else {
@@ -9447,6 +9772,12 @@ function build_return_node$1(node, status) {
9447
9772
  if (override_ctor) status.struct_return_buffer = saved_buffer;
9448
9773
  }
9449
9774
  if (!status.code.endsWith("\n")) status.code += "\n";
9775
+ if (status.function_return_type?.is_view && status.function_return_type.name === "string" && node.value && !is_view_value$1(node.value, status)) {
9776
+ status.code += `str x0, [sp, #-16]!\n`;
9777
+ status.code += `bl _strlen\n`;
9778
+ status.code += `mov x1, x0\n`;
9779
+ status.code += `ldr x0, [sp], #16\n`;
9780
+ }
9450
9781
  if (status.function_return_type?.name === "string" && !status.function_return_type?.is_view && node.value.node_type === "value" && node.value.value.length >= 2 && node.value.value.startsWith("\"") && node.value.value.endsWith("\"")) {
9451
9782
  const fn_name = status.current_function_name;
9452
9783
  const mangled = status.current_struct ? `${status.current_struct.name}_${fn_name}` : fn_name;
@@ -9767,7 +10098,7 @@ function build_value_node$1(node, status) {
9767
10098
  }
9768
10099
  //#endregion
9769
10100
  //#region ../src/build_common/scan_string_length_hoists.ts
9770
- const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
10101
+ const NON_IDENTIFIERS$1 = /* @__PURE__ */ new Set([
9771
10102
  "true",
9772
10103
  "false",
9773
10104
  "null",
@@ -9776,12 +10107,12 @@ const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
9776
10107
  "default"
9777
10108
  ]);
9778
10109
  function is_identifier(value) {
9779
- return !!value && !NON_IDENTIFIERS.has(value) && !/^(\+|-)?\d+(\.\d+)?$/.test(value) && !value.startsWith("\"") && !value.startsWith("'");
10110
+ return !!value && !NON_IDENTIFIERS$1.has(value) && !/^(\+|-)?\d+(\.\d+)?$/.test(value) && !value.startsWith("\"") && !value.startsWith("'");
9780
10111
  }
9781
- function root_name(node) {
10112
+ function root_name$1(node) {
9782
10113
  if (!node) return void 0;
9783
10114
  if (node.node_type === "value") return node.value;
9784
- if (node.node_type === "access") return root_name(node.target);
10115
+ if (node.node_type === "access") return root_name$1(node.target);
9785
10116
  }
9786
10117
  /**
9787
10118
  * Find the bare `string` variables whose `.length` is read inside a while
@@ -9868,9 +10199,9 @@ function scan_string_length_hoists(condition, statements, update, status) {
9868
10199
  }
9869
10200
  case "assign": {
9870
10201
  const assign = n;
9871
- const lhs = root_name(assign.left_value);
10202
+ const lhs = root_name$1(assign.left_value);
9872
10203
  if (lhs) invalidated.add(lhs);
9873
- const swap = root_name(assign.swap);
10204
+ const swap = root_name$1(assign.swap);
9874
10205
  if (swap) invalidated.add(swap);
9875
10206
  walk_children(n, in_boundary);
9876
10207
  return;
@@ -9892,11 +10223,20 @@ function scan_string_length_hoists(condition, statements, update, status) {
9892
10223
  case "access_func": {
9893
10224
  const call = n;
9894
10225
  for (const indices of [call.ref_param_indices, call.mov_param_indices]) for (const i of indices ?? []) {
9895
- const arg = root_name(call.params?.[i]);
10226
+ const arg = root_name$1(call.params?.[i]);
9896
10227
  if (arg) invalidated.add(arg);
9897
10228
  }
9898
10229
  if (call.swap_params) {
9899
- for (const [i, swap] of call.swap_params) for (const arg of [root_name(call.params?.[i]), root_name(swap)]) if (arg) invalidated.add(arg);
10230
+ for (const [i, swap] of call.swap_params) for (const arg of [root_name$1(call.params?.[i]), root_name$1(swap)]) if (arg) invalidated.add(arg);
10231
+ }
10232
+ if (!in_boundary && call.resolved_function) {
10233
+ const self_offset = call.resolved_function.params[0]?.is_self_param ? 1 : 0;
10234
+ const params = call.params ?? [];
10235
+ for (let i = 0; i < params.length; i++) {
10236
+ if (!call.resolved_function.params[i + self_offset]?.hidden_len) continue;
10237
+ const arg = params[i];
10238
+ if (arg.node_type === "value" && is_identifier(arg.value) && is_string_target(arg)) candidates.set(arg.value, arg);
10239
+ }
9900
10240
  }
9901
10241
  walk_children(n, in_boundary);
9902
10242
  return;
@@ -10073,6 +10413,7 @@ function build_while_loop_node$1(node, status) {
10073
10413
  if (!status.string_length_slots) status.string_length_slots = /* @__PURE__ */ new Map();
10074
10414
  for (const [name, target] of hoists) {
10075
10415
  if (status.string_length_slots.has(name)) continue;
10416
+ if (status.hidden_len_params?.has(name)) continue;
10076
10417
  const offset = allocate_stack_space(status, 8);
10077
10418
  status.string_length_slots.set(name, offset);
10078
10419
  build_node$1(target, status);
@@ -10317,6 +10658,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
10317
10658
  emit_owning_replace_T(elem, status);
10318
10659
  return true;
10319
10660
  }
10661
+ if (func_name === "shift_T") {
10662
+ emit_owning_shift_T(elem, status, "x0");
10663
+ return true;
10664
+ }
10320
10665
  return false;
10321
10666
  }
10322
10667
  if (owning_buffer_is_string_elem_aarch64(struct_node)) {
@@ -10328,6 +10673,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
10328
10673
  emit_string_replace_T(status, "x0");
10329
10674
  return true;
10330
10675
  }
10676
+ if (func_name === "shift_T") {
10677
+ emit_string_shift_T(status, "x0");
10678
+ return true;
10679
+ }
10331
10680
  }
10332
10681
  return false;
10333
10682
  }
@@ -10341,7 +10690,7 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
10341
10690
  function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
10342
10691
  const elem = owning_buffer_element_aarch64(node, status);
10343
10692
  if (elem) {
10344
- if (func_name !== "store_T" && func_name !== "replace_T") return false;
10693
+ if (func_name !== "store_T" && func_name !== "replace_T" && func_name !== "shift_T") return false;
10345
10694
  emit_owning_standalone_struct(elem, func_name, status);
10346
10695
  return true;
10347
10696
  }
@@ -10354,12 +10703,20 @@ function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
10354
10703
  emit_string_replace_T(status, "x19");
10355
10704
  return true;
10356
10705
  }
10706
+ if (func_name === "shift_T") {
10707
+ emit_string_shift_T(status, "x19");
10708
+ return true;
10709
+ }
10357
10710
  }
10358
10711
  return false;
10359
10712
  }
10360
10713
  function emit_owning_standalone_struct(elem, func_name, status) {
10361
10714
  const T_SIZE = get_struct_size(elem.name, status);
10362
10715
  const string_fields = collect_string_fields(elem, status);
10716
+ if (func_name === "shift_T") {
10717
+ emit_owning_shift_T(elem, status, "x19");
10718
+ return true;
10719
+ }
10363
10720
  status.code += `stp x20, x21, [sp, #-16]!\n`;
10364
10721
  status.code += `str x22, [sp, #-16]!\n`;
10365
10722
  status.code += `ldr x20, [x19, #8]\n`;
@@ -10459,6 +10816,59 @@ function emit_string_replace_T(status, self_reg) {
10459
10816
  status.code += `${done}:\n`;
10460
10817
  status.code += `ldp x20, x21, [sp], #16\n`;
10461
10818
  }
10819
+ /**
10820
+ * Specialized `Buffer<string>` shift_T. `self_reg` is the register holding
10821
+ * the Buffer pointer ("x0" inline, "x19" standalone); x1 = dst, x2 = src.
10822
+ * Move slot src into slot dst — free dst's heap copy, take over src's
10823
+ * pointer, zero src. Exactly one slot owns the string afterwards.
10824
+ */
10825
+ function emit_string_shift_T(status, self_reg) {
10826
+ const done = `.Lstr_sh_done_${status.label_counter = (status.label_counter ?? 0) + 1}`;
10827
+ status.code += `stp x20, x21, [sp, #-16]!\n`;
10828
+ status.code += `ldr x9, [${self_reg}, #8]\n`;
10829
+ status.code += `add x20, x9, x1, lsl #3\n`;
10830
+ status.code += `add x21, x9, x2, lsl #3\n`;
10831
+ status.code += `cmp x20, x21\n`;
10832
+ status.code += `b.eq ${done}\n`;
10833
+ status.code += `ldr x0, [x20]\n`;
10834
+ emit_free(status);
10835
+ status.code += `ldr x9, [x21]\n`;
10836
+ status.code += `str x9, [x20]\n`;
10837
+ status.code += `str xzr, [x21]\n`;
10838
+ status.code += `${done}:\n`;
10839
+ status.code += `ldp x20, x21, [sp], #16\n`;
10840
+ }
10841
+ /**
10842
+ * Specialized owning-value-struct shift_T. `self_reg` holds the Buffer
10843
+ * pointer ("x0" inline, "x19" standalone); x1 = dst, x2 = src (slot indices).
10844
+ * Destroy dst's old value, copy src's bytes over it wholesale (the moved
10845
+ * struct's owned pointers transfer — no deep copy), zero src.
10846
+ */
10847
+ function emit_owning_shift_T(elem, status, self_reg) {
10848
+ const T_SIZE = get_struct_size(elem.name, status);
10849
+ const done = `.Lown_sh_done_${status.label_counter = (status.label_counter ?? 0) + 1}`;
10850
+ status.code += `stp x20, x21, [sp, #-16]!\n`;
10851
+ status.code += `str x22, [sp, #-16]!\n`;
10852
+ status.code += `ldr x9, [${self_reg}, #8]\n`;
10853
+ status.code += `mov x22, #${T_SIZE}\n`;
10854
+ status.code += `madd x20, x1, x22, x9\n`;
10855
+ status.code += `madd x21, x2, x22, x9\n`;
10856
+ status.code += `cmp x20, x21\n`;
10857
+ status.code += `b.eq ${done}\n`;
10858
+ status.code += `mov x0, x20\n`;
10859
+ status.code += `bl ${elem.name}_destroy\n`;
10860
+ status.code += `mov x0, x20\n`;
10861
+ status.code += `mov x1, x21\n`;
10862
+ status.code += `mov x2, x22\n`;
10863
+ status.code += `bl _memcpy\n`;
10864
+ status.code += `mov x0, x21\n`;
10865
+ status.code += `mov x1, #0\n`;
10866
+ status.code += `mov x2, x22\n`;
10867
+ status.code += `bl _memset\n`;
10868
+ status.code += `${done}:\n`;
10869
+ status.code += `ldr x22, [sp], #16\n`;
10870
+ status.code += `ldp x20, x21, [sp], #16\n`;
10871
+ }
10462
10872
  function emit_owning_store_T(elem, status) {
10463
10873
  const T_SIZE = get_struct_size(elem.name, status);
10464
10874
  const string_fields = collect_string_fields(elem, status);
@@ -11192,10 +11602,14 @@ function build_struct_functions$1(node, status) {
11192
11602
  if (needs_x19) callee_idx = 1;
11193
11603
  const old_ref_params = status.function_ref_params;
11194
11604
  const old_ref_class_slots = status.ref_class_slots;
11605
+ const old_view_params = status.function_view_params;
11606
+ const old_hidden_len_params = status.hidden_len_params;
11195
11607
  status.function_param_regs = /* @__PURE__ */ new Map();
11196
11608
  status.function_param_vars = /* @__PURE__ */ new Set();
11197
11609
  status.function_ref_params = /* @__PURE__ */ new Set();
11198
11610
  status.ref_class_slots = /* @__PURE__ */ new Map();
11611
+ status.function_view_params = /* @__PURE__ */ new Set();
11612
+ status.hidden_len_params = /* @__PURE__ */ new Set();
11199
11613
  status.struct_return_buffer = void 0;
11200
11614
  const return_struct = !func.return_type?.is_array && !!status.structs.find((s) => s.name === func.return_type?.name && !s.is_simple_type && !s.is_class);
11201
11615
  let return_buffer_stack_offset;
@@ -11218,6 +11632,10 @@ function build_struct_functions$1(node, status) {
11218
11632
  if (param.type.is_ref) status.function_ref_params.add(param.name);
11219
11633
  continue;
11220
11634
  }
11635
+ if (param.type.is_view) {
11636
+ slot_idx += 2;
11637
+ continue;
11638
+ }
11221
11639
  if ((!!status.structs.find((s) => s.name === param.type.name && !s.is_simple_type) || !!status.enums.find((e) => e.name === param.type.name && e.has_associated_data)) && callee_idx < callee_saved.length) {
11222
11640
  const saved_reg = callee_saved[callee_idx++];
11223
11641
  if (saved_reg !== "x19" || !needs_x19) status.code += `str ${saved_reg}, [sp, #-16]!\n`;
@@ -11235,6 +11653,7 @@ function build_struct_functions$1(node, status) {
11235
11653
  if (!status.class_vars) status.class_vars = /* @__PURE__ */ new Set();
11236
11654
  status.class_vars.add(param.name);
11237
11655
  }
11656
+ if (param.hidden_len) slot_idx++;
11238
11657
  slot_idx++;
11239
11658
  }
11240
11659
  status.code += `sub sp, sp, #${stack_placeholder}\n`;
@@ -11255,6 +11674,22 @@ function build_struct_functions$1(node, status) {
11255
11674
  second_slot_idx += 2;
11256
11675
  continue;
11257
11676
  }
11677
+ if (param.type.is_view) {
11678
+ const offset = allocate_stack_space(status, 16, 16);
11679
+ status.stack_offsets.set(param.name, offset);
11680
+ status.function_view_params.add(param.name);
11681
+ for (const half of [0, 1]) {
11682
+ const p_slot = second_slot_idx + half;
11683
+ if (p_slot < 8) status.code += `str ${param_regs[p_slot]}, [x29, #${offset + half * 8}]\n`;
11684
+ else {
11685
+ const k = p_slot - 8;
11686
+ status.code += `ldr x9, [x29, #${overflow_placeholder(func_label, k)}]\n`;
11687
+ status.code += `str x9, [x29, #${offset + half * 8}]\n`;
11688
+ }
11689
+ }
11690
+ second_slot_idx += 2;
11691
+ continue;
11692
+ }
11258
11693
  if (!(!!status.structs.find((s) => s.name === param.type.name && !s.is_simple_type) || !!status.enums.find((e) => e.name === param.type.name && e.has_associated_data))) {
11259
11694
  const size = aarch64_size(param.type.name);
11260
11695
  const offset = allocate_stack_space(status, size, size);
@@ -11285,6 +11720,20 @@ function build_struct_functions$1(node, status) {
11285
11720
  }
11286
11721
  }
11287
11722
  }
11723
+ if (param.hidden_len) {
11724
+ const len_offset = allocate_stack_space(status, 8, 8);
11725
+ status.stack_offsets.set(`_${param.name}_len`, len_offset);
11726
+ if (!status.hidden_len_params) status.hidden_len_params = /* @__PURE__ */ new Set();
11727
+ status.hidden_len_params.add(param.name);
11728
+ const len_slot = second_slot_idx + 1;
11729
+ if (len_slot < 8) status.code += `str ${param_regs[len_slot]}, [x29, #${len_offset}]\n`;
11730
+ else {
11731
+ const k = len_slot - 8;
11732
+ status.code += `ldr x9, [x29, #${overflow_placeholder(func_label, k)}]\n`;
11733
+ status.code += `str x9, [x29, #${len_offset}]\n`;
11734
+ }
11735
+ second_slot_idx++;
11736
+ }
11288
11737
  second_slot_idx++;
11289
11738
  }
11290
11739
  const moved_param_save_slots = /* @__PURE__ */ new Map();
@@ -11374,6 +11823,8 @@ function build_struct_functions$1(node, status) {
11374
11823
  status.function_param_vars = old_param_vars;
11375
11824
  status.function_ref_params = old_ref_params;
11376
11825
  status.ref_class_slots = old_ref_class_slots;
11826
+ status.function_view_params = old_view_params;
11827
+ status.hidden_len_params = old_hidden_len_params;
11377
11828
  status.function_return_label = old_return_label;
11378
11829
  status.force_heap_strings = old_force_heap;
11379
11830
  status.struct_return_buffer = void 0;
@@ -11814,6 +12265,11 @@ function build_inline_method(struct_node, func, status) {
11814
12265
  const old_heap_cleanup_stack = status.heap_cleanup_stack;
11815
12266
  const old_moved = status.moved;
11816
12267
  const old_outer_scope_declarations = status.outer_scope_declarations;
12268
+ const old_current_struct = status.current_struct;
12269
+ const old_current_function_name = status.current_function_name;
12270
+ const old_return_assign = status.return_assign;
12271
+ const old_join_needs_owned_string = status.join_needs_owned_string;
12272
+ const old_emitted_allocations = status.emitted_allocations;
11817
12273
  const return_label = `.inline_ret_${inline_counter++}`;
11818
12274
  status.function_return_label = return_label;
11819
12275
  status.scoped_declarations = [];
@@ -11824,6 +12280,11 @@ function build_inline_method(struct_node, func, status) {
11824
12280
  status.buffer_data_cache = void 0;
11825
12281
  status.heap_cleanup_stack = [];
11826
12282
  status.moved = /* @__PURE__ */ new Set();
12283
+ status.current_struct = struct_node;
12284
+ status.current_function_name = func.name;
12285
+ status.return_assign = void 0;
12286
+ status.join_needs_owned_string = void 0;
12287
+ status.emitted_allocations = /* @__PURE__ */ new Set();
11827
12288
  if (needs_x19) {
11828
12289
  status.code += `str x19, [sp, #-16]!\n`;
11829
12290
  status.code += `mov x19, x0\n`;
@@ -11881,9 +12342,11 @@ function build_inline_method(struct_node, func, status) {
11881
12342
  status.code += `str x8, [x29, #${return_buffer_stack_offset}]\n`;
11882
12343
  status.return_buffer_stack_offset = return_buffer_stack_offset;
11883
12344
  }
12345
+ const code_length_before_body = status.code.length;
11884
12346
  build_block_node$1(func, status);
11885
12347
  const standalone_return_label = `.return_${struct_node.name}_${func.name.replace(/#/g, "")}`;
11886
- status.code = status.code.replaceAll(`b ${standalone_return_label}`, `b ${return_label}`);
12348
+ const body = status.code.slice(code_length_before_body);
12349
+ status.code = status.code.slice(0, code_length_before_body) + body.replaceAll(`b ${standalone_return_label}\n`, `b ${return_label}\n`);
11887
12350
  status.code += `${return_label}:\n`;
11888
12351
  for (let i = saved_stack_slots.length - 1; i >= 0; i--) status.code += `add sp, sp, #16\n`;
11889
12352
  for (let ci = callee_idx - 1; ci >= 0; ci--) {
@@ -11905,6 +12368,11 @@ function build_inline_method(struct_node, func, status) {
11905
12368
  status.heap_cleanup_stack = old_heap_cleanup_stack;
11906
12369
  status.moved = old_moved;
11907
12370
  status.outer_scope_declarations = old_outer_scope_declarations;
12371
+ status.current_struct = old_current_struct;
12372
+ status.current_function_name = old_current_function_name;
12373
+ status.return_assign = old_return_assign;
12374
+ status.join_needs_owned_string = old_join_needs_owned_string;
12375
+ status.emitted_allocations = old_emitted_allocations;
11908
12376
  }
11909
12377
  let inline_fn_depth = 0;
11910
12378
  const MAX_INLINE_DEPTH = 2;
@@ -11925,6 +12393,11 @@ function build_inline_function(func, status) {
11925
12393
  const old_heap_cleanup_stack = status.heap_cleanup_stack;
11926
12394
  const old_moved = status.moved;
11927
12395
  const old_outer_scope_declarations = status.outer_scope_declarations;
12396
+ const old_current_struct = status.current_struct;
12397
+ const old_current_function_name = status.current_function_name;
12398
+ const old_return_assign = status.return_assign;
12399
+ const old_join_needs_owned_string = status.join_needs_owned_string;
12400
+ const old_emitted_allocations = status.emitted_allocations;
11928
12401
  const return_label = `.inline_fn_ret_${inline_counter++}`;
11929
12402
  status.function_return_label = return_label;
11930
12403
  status.scoped_declarations = [];
@@ -11935,6 +12408,11 @@ function build_inline_function(func, status) {
11935
12408
  status.buffer_data_cache = void 0;
11936
12409
  status.heap_cleanup_stack = [];
11937
12410
  status.moved = /* @__PURE__ */ new Set();
12411
+ status.current_struct = void 0;
12412
+ status.current_function_name = func.name;
12413
+ status.return_assign = void 0;
12414
+ status.join_needs_owned_string = void 0;
12415
+ status.emitted_allocations = /* @__PURE__ */ new Set();
11938
12416
  const param_regs = [
11939
12417
  "x0",
11940
12418
  "x1",
@@ -12004,6 +12482,11 @@ function build_inline_function(func, status) {
12004
12482
  status.heap_cleanup_stack = old_heap_cleanup_stack;
12005
12483
  status.moved = old_moved;
12006
12484
  status.outer_scope_declarations = old_outer_scope_declarations;
12485
+ status.current_struct = old_current_struct;
12486
+ status.current_function_name = old_current_function_name;
12487
+ status.return_assign = old_return_assign;
12488
+ status.join_needs_owned_string = old_join_needs_owned_string;
12489
+ status.emitted_allocations = old_emitted_allocations;
12007
12490
  inline_fn_depth--;
12008
12491
  return true;
12009
12492
  }
@@ -12158,8 +12641,25 @@ function load_nursery_struct_address(target, status) {
12158
12641
  }
12159
12642
  //#endregion
12160
12643
  //#region ../src/build_aarch64/build_access_node.ts
12644
+ /**
12645
+ * Whether `struct_node` is a monomorphized `Array<T>` (`Array_<T>`). The mono
12646
+ * name is unambiguous — the compiler derives it itself (`"Array_" + elem`)
12647
+ * for every `Array<T>` instantiation, so a name match is always an Array mono.
12648
+ */
12649
+ function is_array_mono_struct(struct_node, status) {
12650
+ return !!struct_node && !struct_node.is_generic && struct_node.name.startsWith("Array_") && !!status.structs.find((s) => s.name === struct_node.name);
12651
+ }
12161
12652
  function emit_string_length$1(target, status) {
12162
12653
  if (target.node_type === "value") {
12654
+ const name = target.value;
12655
+ if (name !== "self" && status.hidden_len_params?.has(name)) {
12656
+ const len_slot = status.stack_offsets?.get(`_${name}_len`);
12657
+ if (len_slot !== void 0) {
12658
+ status.last_result_is_heap = false;
12659
+ status.code += `ldr x0, [x29, #${len_slot}]\n`;
12660
+ return;
12661
+ }
12662
+ }
12163
12663
  const slot = status.string_length_slots?.get(target.value);
12164
12664
  if (slot !== void 0) {
12165
12665
  status.last_result_is_heap = false;
@@ -12229,18 +12729,7 @@ function build_view_op(node, access_func, status) {
12229
12729
  if (access_func.name === "to_string" && t.name === "string") {
12230
12730
  status.code += `ldr x0, [x29, #${base}]\n`;
12231
12731
  status.code += `ldr x1, [x29, #${base + 8}]\n`;
12232
- status.code += `stp x0, x1, [sp, #-16]!\n`;
12233
- status.code += `add x0, x1, #1\n`;
12234
- emit_malloc(status);
12235
- status.code += `str x0, [sp, #-16]!\n`;
12236
- status.code += `ldr x1, [sp, #16]\n`;
12237
- status.code += `ldr x2, [sp, #24]\n`;
12238
- status.code += `ldr x0, [sp]\n`;
12239
- status.code += `bl _memcpy\n`;
12240
- status.code += `ldr x0, [sp]\n`;
12241
- status.code += `ldr x1, [sp, #24]\n`;
12242
- status.code += `strb wzr, [x0, x1]\n`;
12243
- status.code += `add sp, sp, #32\n`;
12732
+ emit_view_materialize_owned(status);
12244
12733
  status.last_result_is_heap = true;
12245
12734
  return true;
12246
12735
  }
@@ -12448,6 +12937,14 @@ function build_access_field(node, status) {
12448
12937
  }
12449
12938
  const target_name = node.target.node_type === "value" ? node.target.value : target_type?.name;
12450
12939
  const access_field = node.access;
12940
+ if (access_field.name === "length" && node.target.node_type === "value" && node.target.value === "self" && is_array_mono_struct(status.current_struct, status)) {
12941
+ const self_reg = get_param_reg("self", status);
12942
+ if (self_reg) {
12943
+ if (self_reg !== "x0") status.code += `mov x0, ${self_reg}\n`;
12944
+ status.code += `ldr x0, [x0, #-8]\n`;
12945
+ return;
12946
+ }
12947
+ }
12451
12948
  if (access_field.type?.name === "func") {
12452
12949
  status.code += `adr x0, ${target_type.name}_${access_field.name}\n`;
12453
12950
  return;
@@ -13086,14 +13583,41 @@ function build_access_method(node, access_func, status) {
13086
13583
  "x6",
13087
13584
  "x7"
13088
13585
  ];
13089
- const overflow_count = Math.max(0, access_func.params.length - (8 - start_reg));
13586
+ const view_arg_set = new Set(access_func.view_param_indices ?? []);
13587
+ const hidden_len_indices = callee_hidden_len_indices(access_func);
13588
+ const hidden_len_set = new Set(hidden_len_indices);
13589
+ const hidden_len_slots = /* @__PURE__ */ new Map();
13590
+ const arg_slot = [];
13591
+ let total_arg_slots = 0;
13592
+ for (let i = 0; i < access_func.params.length; i++) {
13593
+ arg_slot.push(total_arg_slots);
13594
+ total_arg_slots += view_arg_set.has(i) ? 2 : 1;
13595
+ if (hidden_len_set.has(i)) {
13596
+ hidden_len_slots.set(i, total_arg_slots);
13597
+ total_arg_slots += 1;
13598
+ }
13599
+ }
13600
+ const overflow_count = Math.max(0, total_arg_slots - (8 - start_reg));
13090
13601
  let overflow_base = 0;
13091
13602
  if (overflow_count > 0) overflow_base = allocate_stack_space(status, overflow_count * 8, 16);
13603
+ const has_view_args = view_arg_set.size > 0;
13604
+ let view_spill_base = 0;
13605
+ if (has_view_args) view_spill_base = allocate_stack_space(status, total_arg_slots * 8, 16);
13606
+ const view_half_store = (j, half) => {
13607
+ const half_slot = start_reg + arg_slot[j] + half;
13608
+ return half_slot >= 8 ? overflow_base + (half_slot - 8) * 8 : view_spill_base + (arg_slot[j] + half) * 8;
13609
+ };
13092
13610
  const ref_class_param_reload = [];
13093
13611
  for (let i = access_func.params.length - 1; i >= 0; i--) {
13094
13612
  const param = access_func.params[i];
13095
13613
  const is_ref_param = access_func.ref_param_indices?.includes(i);
13096
13614
  const param_type = param.type?.name || "";
13615
+ if (view_arg_set.has(i)) {
13616
+ emit_view_string_arg(param, status);
13617
+ status.code += `str x0, [x29, #${view_half_store(i, 0)}]\n`;
13618
+ status.code += `str x1, [x29, #${view_half_store(i, 1)}]\n`;
13619
+ continue;
13620
+ }
13097
13621
  const is_struct = is_struct_type(param_type, status) || is_enum_with_data_type(param_type, status);
13098
13622
  if (is_ref_param) {
13099
13623
  const rp_name = param.node_type === "value" ? param.value : void 0;
@@ -13115,14 +13639,66 @@ function build_access_method(node, access_func, status) {
13115
13639
  if (!status.code.endsWith("\n")) status.code += "\n";
13116
13640
  }
13117
13641
  } else build_node$1(param, status);
13118
- const slot = start_reg + i;
13642
+ const slot = start_reg + arg_slot[i];
13119
13643
  if (!status.code.endsWith("\n")) status.code += "\n";
13120
13644
  if (slot >= 8) status.code += `str x0, [x29, #${overflow_base + (slot - 8) * 8}]\n`;
13121
13645
  else {
13122
- const reg = param_regs[i];
13646
+ const reg = param_regs[arg_slot[i]];
13123
13647
  if (reg && reg !== "x0") status.code += `mov ${reg}, x0\n`;
13124
13648
  }
13125
13649
  }
13650
+ if (has_view_args) for (let j = 0; j < access_func.params.length; j++) {
13651
+ if (!view_arg_set.has(j)) continue;
13652
+ for (const half of [0, 1]) {
13653
+ const half_slot = start_reg + arg_slot[j] + half;
13654
+ if (half_slot >= 8) continue;
13655
+ status.code += `ldr x${half_slot}, [x29, #${view_spill_base + (arg_slot[j] + half) * 8}]\n`;
13656
+ }
13657
+ }
13658
+ if (hidden_len_indices.length > 0) {
13659
+ const needs_strlen = hidden_len_indices.some((i) => {
13660
+ const arg = access_func.params[i];
13661
+ if (arg.node_type !== "value") return true;
13662
+ return !status.string_length_slots?.has(arg.value);
13663
+ });
13664
+ const live_regs = [];
13665
+ if (needs_strlen) {
13666
+ const max_live = Math.min(7, start_reg + total_arg_slots - 1);
13667
+ for (let s = start_reg; s <= max_live; s++) live_regs.push(`x${s}`);
13668
+ }
13669
+ const reg_save_base = needs_strlen ? allocate_stack_space(status, Math.max(live_regs.length, 1) * 8, 16) : 0;
13670
+ const len_save_base = allocate_stack_space(status, hidden_len_indices.length * 8, 16);
13671
+ for (let k = 0; k < live_regs.length; k++) status.code += `str ${live_regs[k]}, [x29, #${reg_save_base + k * 8}]\n`;
13672
+ for (let h = 0; h < hidden_len_indices.length; h++) {
13673
+ const i = hidden_len_indices[h];
13674
+ const arg = access_func.params[i];
13675
+ let computed = false;
13676
+ if (arg.node_type === "value") {
13677
+ const hoisted = status.string_length_slots?.get(arg.value);
13678
+ if (hoisted !== void 0) {
13679
+ status.code += `ldr x9, [x29, #${hoisted}]\n`;
13680
+ status.code += `str x9, [x29, #${len_save_base + h * 8}]\n`;
13681
+ computed = true;
13682
+ }
13683
+ }
13684
+ if (!computed) {
13685
+ const src_slot = start_reg + arg_slot[i];
13686
+ if (src_slot < 8) {
13687
+ const save_index = src_slot - start_reg;
13688
+ status.code += `ldr x0, [x29, #${reg_save_base + save_index * 8}]\n`;
13689
+ } else status.code += `ldr x0, [x29, #${overflow_base + (src_slot - 8) * 8}]\n`;
13690
+ status.code += `bl _strlen\n`;
13691
+ status.code += `str x0, [x29, #${len_save_base + h * 8}]\n`;
13692
+ }
13693
+ }
13694
+ for (let k = live_regs.length - 1; k >= 0; k--) status.code += `ldr ${live_regs[k]}, [x29, #${reg_save_base + k * 8}]\n`;
13695
+ for (let h = 0; h < hidden_len_indices.length; h++) {
13696
+ const dest_slot = start_reg + hidden_len_slots.get(hidden_len_indices[h]);
13697
+ status.code += `ldr x9, [x29, #${len_save_base + h * 8}]\n`;
13698
+ if (dest_slot < 8) status.code += `mov x${dest_slot}, x9\n`;
13699
+ else status.code += `str x9, [x29, #${overflow_base + (dest_slot - 8) * 8}]\n`;
13700
+ }
13701
+ }
13126
13702
  if (!status.code.endsWith("\n")) status.code += "\n";
13127
13703
  if (needs_self_save) status.code += `ldr x0, [sp], #16\n`;
13128
13704
  let outgoing_size = 0;
@@ -14226,6 +14802,84 @@ function has_flag_name(name) {
14226
14802
  return `${name}_has`;
14227
14803
  }
14228
14804
  //#endregion
14805
+ //#region ../src/build_c/utils/view_value.ts
14806
+ /**
14807
+ * Resolve the struct name of an expression's value by walking the access
14808
+ * chain through the (monomorphized) struct table — bare names via
14809
+ * variable_types / scoped declarations / `self`, field accesses via the
14810
+ * base struct's field type, method calls via the method's return type.
14811
+ * Fallback for cached node types that lost their modifiers (generic bodies
14812
+ * re-checked after monomorphization can carry a stale plain `string`).
14813
+ */
14814
+ function value_struct_name(node, status) {
14815
+ if (node.node_type === "value") {
14816
+ const name = node.value;
14817
+ if (name === "self" && status.current_struct) return status.current_struct.name;
14818
+ const vt = status.variable_types?.get(name);
14819
+ if (vt?.name) return vt.name;
14820
+ const decl = status.scoped_declarations.findLast((d) => d.name === name);
14821
+ if (decl?.type?.name) return decl.type.name;
14822
+ return;
14823
+ }
14824
+ if (node.node_type === "access") {
14825
+ const inner = node.access;
14826
+ const base = value_struct_name(node.target, status);
14827
+ if (!base) return void 0;
14828
+ const struct = status.structs.find((s) => s.name === base && !s.is_generic);
14829
+ if (!struct) return void 0;
14830
+ if (inner.node_type === "access_field") return struct.fields.find((f) => f.name === inner.name)?.type?.name;
14831
+ if (inner.node_type === "access_func") return struct.functions.find((f) => f.name === inner.name)?.return_type?.name;
14832
+ }
14833
+ }
14834
+ /**
14835
+ * Whether an expression is a `view T` VALUE at the C level — a nomen_view
14836
+ * (ptr, len) struct expression that can pass through a `view T` parameter
14837
+ * unchanged. Recovers `is_view` from the cached node type, the declared
14838
+ * variable type, the current function's view params, or (for method calls
14839
+ * whose cached type lost the modifier) the callee's declared return type.
14840
+ */
14841
+ function is_view_value(node, status) {
14842
+ if (type_from_value_node$1(node)?.is_view) return true;
14843
+ if (node.node_type === "value") {
14844
+ const name = node.value;
14845
+ if (status.variable_types?.get(name)?.is_view) return true;
14846
+ if (status.function_view_params?.has(name)) return true;
14847
+ }
14848
+ if (node.node_type === "access" && node.access.node_type === "access_func") {
14849
+ const access = node.access;
14850
+ const recv_struct = value_struct_name(node.target, status);
14851
+ if ((status.structs.find((s) => s.name === recv_struct && !s.is_generic)?.functions.find((f) => f.name === access.name || f.name === `#${access.name}`))?.return_type?.is_view) return true;
14852
+ }
14853
+ return false;
14854
+ }
14855
+ /**
14856
+ * Emit a C expression for an argument passed to a `view string` parameter.
14857
+ * A view-typed argument is already a nomen_view — pass it through. An owned
14858
+ * `string` expression is wrapped into a nomen_view carrying its strlen (an
14859
+ * implicit owned→view borrow; the caller keeps ownership of the string).
14860
+ * Statement-expression form evaluates the operand exactly once.
14861
+ */
14862
+ function c_view_string_arg(arg, status) {
14863
+ if (is_view_value(arg, status)) {
14864
+ build_node(arg, status);
14865
+ return;
14866
+ }
14867
+ status.code += `({ char* _p = `;
14868
+ build_node(arg, status);
14869
+ status.code += `; nomen_view _v = { (void*)_p, (long)strlen(_p) }; _v; })`;
14870
+ }
14871
+ /**
14872
+ * Emit a C expression materializing a `view string` into an OWNED heap
14873
+ * string (malloc len+1 / memcpy / null-terminate). Used when a view value
14874
+ * initializes an owned `string` declaration (`const string s = v`) — the
14875
+ * resulting pointer is heap-owned and must be freed at scope exit.
14876
+ */
14877
+ function c_materialize_view_string(arg, status) {
14878
+ status.code += `({ nomen_view _t = `;
14879
+ build_node(arg, status);
14880
+ status.code += `; char* _r = malloc(_t.len + 1); memcpy(_r, _t.ptr, _t.len); _r[_t.len] = 0; _r; })`;
14881
+ }
14882
+ //#endregion
14229
14883
  //#region ../src/build_c/build_operation_node.ts
14230
14884
  function build_operation_node(node, status) {
14231
14885
  if (node.operator_func?.deferred) {
@@ -14332,8 +14986,16 @@ function build_operation_node(node, status) {
14332
14986
  status.code += `)`;
14333
14987
  } else if (node.operator_func) {
14334
14988
  const label = node.operator_func.mangled_name || `${node.operator_func.struct_name}_${node.operator_func.func_name}`;
14335
- const is_string_op = node.type?.name === "string";
14336
14989
  const is_string_cmp = node.op === "==" || node.op === "!=";
14990
+ if (is_string_cmp && node.operator_func.struct_name === "string" && (is_view_value(node.left_value, status) || is_view_value(node.right_value, status))) {
14991
+ status.code += `({ nomen_view _a = `;
14992
+ c_view_string_arg(node.left_value, status);
14993
+ status.code += `, _b = `;
14994
+ c_view_string_arg(node.right_value, status);
14995
+ status.code += `; ((_a.len == _b.len) && (memcmp(_a.ptr, _b.ptr, _a.len) == 0))${node.op === "!=" ? " == 0" : ""}; })`;
14996
+ return;
14997
+ }
14998
+ const is_string_op = node.type?.name === "string";
14337
14999
  const left_temp = (is_string_op || is_string_cmp) && is_owned_heap_temp(node.left_value, status);
14338
15000
  const right_temp = (is_string_op || is_string_cmp) && is_owned_heap_temp(node.right_value, status);
14339
15001
  if (left_temp || right_temp) {
@@ -14587,7 +15249,7 @@ function free_scoped_declarations(status, decls, persist_string_field_records =
14587
15249
  }
14588
15250
  const is_destructured_field_access = dec.value?.node_type === "access" && dec.value.access.node_type === "access_field" && !dec.value.is_moved;
14589
15251
  const is_borrowed_string = is_string_borrow(dec.value) || !!status.string_borrow_vars?.has(dec.name);
14590
- const value_is_heap_string = dec.type.name === "string" && (dec.value?.node_type === "access" && dec.value.access.node_type === "access_func" || dec.value?.node_type === "func_call");
15252
+ const value_is_heap_string = dec.type.name === "string" && (dec.value?.node_type === "access" && dec.value.access.node_type === "access_func" || dec.value?.node_type === "func_call" || !!dec.value && !dec.type.is_view && is_view_value(dec.value, status));
14591
15253
  const dec_value = dec.value;
14592
15254
  const dec_val_is_string_literal = dec.value?.node_type === "value" && dec_value.value.length >= 2 && dec_value.value.startsWith("\"") && dec_value.value.endsWith("\"");
14593
15255
  const dec_val_is_heap_string_var = dec.value?.node_type === "value" && !dec_val_is_string_literal && !!status.scoped_declarations.find((d) => d.name === dec_value.value);
@@ -14865,6 +15527,70 @@ function reclaim_to_loop_body(status) {
14865
15527
  return loopBodyIdx;
14866
15528
  }
14867
15529
  //#endregion
15530
+ //#region ../src/build_c/utils/hidden_len_call.ts
15531
+ function needs_len_materialize(arg) {
15532
+ if (arg.node_type === "value") return false;
15533
+ if (arg.node_type === "access" && arg.access.node_type === "access_field") return false;
15534
+ return true;
15535
+ }
15536
+ /**
15537
+ * Emit the statement-expression prologue materialising every non-lvalue
15538
+ * hidden-len argument once. Must be called immediately BEFORE the call's
15539
+ * label text is emitted; the args loop then substitutes the temps, and
15540
+ * close_hidden_len_wrap() terminates the expression after the call.
15541
+ */
15542
+ function open_hidden_len_wrap(indices, params, status) {
15543
+ const temps = /* @__PURE__ */ new Map();
15544
+ const to_materialize = indices.filter((i) => params[i] && needs_len_materialize(params[i]));
15545
+ if (to_materialize.length === 0) return {
15546
+ temps,
15547
+ open: false
15548
+ };
15549
+ status.code += `({ `;
15550
+ for (const i of to_materialize) {
15551
+ const tmp = `_hlm_${status.label_counter = (status.label_counter ?? 0) + 1}`;
15552
+ status.code += `char* ${tmp} = `;
15553
+ build_node(params[i], status);
15554
+ status.code += `; `;
15555
+ temps.set(i, tmp);
15556
+ }
15557
+ return {
15558
+ temps,
15559
+ open: true
15560
+ };
15561
+ }
15562
+ function close_hidden_len_wrap(wrap, status) {
15563
+ if (wrap.open) status.code += `; })`;
15564
+ }
15565
+ /**
15566
+ * Emit the companion length EXPRESSION for the argument at a hidden-len
15567
+ * index (the caller emits the separating `, `). `temp` is the materialised
15568
+ * pointer temp when one exists (open_hidden_len_wrap).
15569
+ */
15570
+ function emit_hidden_len_expr(arg, temp, status) {
15571
+ if (temp) {
15572
+ status.code += `strlen(${temp})`;
15573
+ return;
15574
+ }
15575
+ if (arg.node_type === "value") {
15576
+ const value = arg.value;
15577
+ if (value.startsWith("\"")) {
15578
+ status.code += `sizeof(${value}) - 1`;
15579
+ return;
15580
+ }
15581
+ const hoisted = status.string_length_temps?.get(value);
15582
+ if (hoisted) {
15583
+ status.code += hoisted;
15584
+ return;
15585
+ }
15586
+ status.code += `strlen(${value})`;
15587
+ return;
15588
+ }
15589
+ status.code += `strlen(`;
15590
+ build_node(arg, status);
15591
+ status.code += `)`;
15592
+ }
15593
+ //#endregion
14868
15594
  //#region ../src/build_c/build_access_node.ts
14869
15595
  /**
14870
15596
  * The C type of a single element of a `view T` slice, used to cast the
@@ -14926,6 +15652,66 @@ function build_vtable_target(node, status) {
14926
15652
  status.code = status.code.substring(0, before);
14927
15653
  status.code += "&" + expr;
14928
15654
  }
15655
+ /**
15656
+ * An Array method's `self` is a `struct Array_<T>*` — header `[0]=_vt`,
15657
+ * `[8]=length`, elements at `+16` — which is exactly what a heap receiver
15658
+ * (`struct Array_<T>*`) already is. A NON-heap receiver (a plain stack C
15659
+ * array `T w[N]`, or a variadic `...T` param lowered to `T *w`) would pass
15660
+ * the bare element pointer, so the callee's `self->length` and the raw
15661
+ * bodies' `(T*)((char*)self + sizeof(*self))` reads land on garbage
15662
+ * (`w[1]`, `w + 2`). When such a receiver with a usable length calls a
15663
+ * method on the mono `Array_<T>` struct, build a statement-expression
15664
+ * temporary carrying a proper header plus a copy of the elements — the
15665
+ * same wrap the binary-operator operand path uses
15666
+ * (build_array_operand_for_call). A `ref self` callee additionally gets
15667
+ * its element copy written back after the call, so mutations through
15668
+ * `self` reach the caller's array.
15669
+ */
15670
+ function array_receiver_wrap(node, target_type, target_method, mono_struct_name, status) {
15671
+ if (!target_type?.is_array || !mono_struct_name.startsWith("Array_")) return void 0;
15672
+ if (node.target.node_type !== "value") return void 0;
15673
+ const target_value = node.target.value;
15674
+ if (target_type.is_array_heap || status.heap_array_vars?.has(target_value)) return void 0;
15675
+ let length;
15676
+ if (status.stack_array_lengths?.has(target_value)) length = status.stack_array_lengths.get(target_value);
15677
+ else if (target_type.length) {
15678
+ const before = status.code.length;
15679
+ build_node(target_type.length, status);
15680
+ length = status.code.substring(before);
15681
+ status.code = status.code.substring(0, before);
15682
+ } else if (status.function_variadic_params?.has(target_value)) length = `_${target_value}_len`;
15683
+ if (!length) return void 0;
15684
+ const before = status.code.length;
15685
+ status.suppress_dereference = true;
15686
+ build_node(node.target, status);
15687
+ status.suppress_dereference = false;
15688
+ const recv = status.code.substring(before);
15689
+ status.code = status.code.substring(0, before);
15690
+ const elem_c = c_type(target_type.name);
15691
+ const id = status.label_counter = (status.label_counter ?? 0) + 1;
15692
+ const wrap = `_arrm_${id}`;
15693
+ const method_self_is_ref = !!target_method?.params?.some((p) => p.is_self_param && (p.is_ref || p.type?.is_ref));
15694
+ const ret = target_method?.return_type;
15695
+ const ret_c = !ret?.name ? "void" : ret.is_array ? "void*" : c_type(ret.name);
15696
+ const bytes = `${length} * sizeof(${elem_c})`;
15697
+ const length_is_literal = /^(\d+L?|0x[0-9a-fA-F]+)$/.test(length.trim());
15698
+ let prefix;
15699
+ let self_expr;
15700
+ if (length_is_literal) {
15701
+ prefix = `({ struct { struct ${mono_struct_name} _h; ${elem_c} _d[${length}]; } ${wrap}; ${wrap}._h._vt = 0; ${wrap}._h.length = ${length}; memcpy(${wrap}._d, ${recv}, ${bytes}); ` + (method_self_is_ref && ret_c !== "void" ? `${ret_c} _arrm_r_${id} = ` : "");
15702
+ self_expr = `(struct ${mono_struct_name}*)&${wrap}`;
15703
+ } else {
15704
+ prefix = `({ char ${wrap}[sizeof(struct ${mono_struct_name}) + ${bytes}]; struct ${mono_struct_name}* _arrm_p_${id} = (struct ${mono_struct_name}*)${wrap}; _arrm_p_${id}->_vt = 0; _arrm_p_${id}->length = ${length}; memcpy(${wrap} + sizeof(struct ${mono_struct_name}), ${recv}, ${bytes}); ` + (method_self_is_ref && ret_c !== "void" ? `${ret_c} _arrm_r_${id} = ` : "");
15705
+ self_expr = `_arrm_p_${id}`;
15706
+ }
15707
+ const copy_back = `memcpy(${recv}, ${length_is_literal ? `${wrap}._d` : `${wrap} + sizeof(struct ${mono_struct_name})`}, ${bytes}); `;
15708
+ const suffix = (method_self_is_ref ? `; ${copy_back}` + (ret_c !== "void" ? `_arrm_r_${id}; ` : `(void)0; `) : "") + `; })`;
15709
+ return {
15710
+ prefix,
15711
+ self_expr,
15712
+ suffix
15713
+ };
15714
+ }
14929
15715
  function build_access_node(node, status) {
14930
15716
  let target_type = type_from_value_node$1(node.target);
14931
15717
  if (!target_type?.name && node.target.node_type === "value") {
@@ -15233,20 +16019,36 @@ function build_access_node(node, status) {
15233
16019
  }
15234
16020
  }
15235
16021
  const label = access_func.mangled_name || trait_default_label || `${mono_struct_name}_${access_func.name.replace(/#/g, "")}`;
16022
+ const array_wrap = array_receiver_wrap(node, method_type, target_method, mono_struct_name, status);
16023
+ if (array_wrap) status.code += array_wrap.prefix;
16024
+ const hidden_len = callee_hidden_len_indices(access_func);
16025
+ const hidden_wrap = open_hidden_len_wrap(hidden_len, access_func.params, status);
15236
16026
  status.code += `${label}(`;
15237
16027
  if (!access_func.is_static) {
15238
- const method_self_is_ref = !!target_method?.params?.some((p) => p.is_self_param && (p.is_ref || p.type?.is_ref));
15239
- if (!built_in_types.includes(method_type?.name || "") || method_self_is_ref) {
15240
- const target_value = node.target.node_type === "value" ? node.target.value : "";
15241
- const target_is_ref_class_param = !!status.ref_class_params?.has(target_value);
15242
- if (!(!!status.function_ref_params?.has(target_value) || !!status.class_vars?.has(target_value) || !!status.heap_array_vars?.has(target_value))) status.code += "&";
15243
- else if (target_is_ref_class_param) {} else status.suppress_dereference = true;
16028
+ if (array_wrap) status.code += array_wrap.self_expr;
16029
+ else {
16030
+ const method_self_is_ref = !!target_method?.params?.some((p) => p.is_self_param && (p.is_ref || p.type?.is_ref));
16031
+ if (!built_in_types.includes(method_type?.name || "") || method_self_is_ref) {
16032
+ const target_value = node.target.node_type === "value" ? node.target.value : "";
16033
+ const target_is_ref_class_param = !!status.ref_class_params?.has(target_value);
16034
+ if (!(!!status.function_ref_params?.has(target_value) || !!status.class_vars?.has(target_value) || !!status.heap_array_vars?.has(target_value))) status.code += "&";
16035
+ else if (target_is_ref_class_param) {} else status.suppress_dereference = true;
16036
+ }
16037
+ build_node(node.target, status);
16038
+ status.suppress_dereference = false;
15244
16039
  }
15245
- build_node(node.target, status);
15246
- status.suppress_dereference = false;
15247
16040
  }
15248
16041
  for (let i = 0; i < access_func.params.length; i++) {
15249
16042
  if (!access_func.is_static || i > 0) status.code += ", ";
16043
+ const hidden_temp = hidden_wrap.temps.get(i);
16044
+ if (hidden_temp) {
16045
+ status.code += `${hidden_temp}, strlen(${hidden_temp})`;
16046
+ continue;
16047
+ }
16048
+ if (access_func.view_param_indices?.includes(i)) {
16049
+ c_view_string_arg(access_func.params[i], status);
16050
+ continue;
16051
+ }
15250
16052
  const param_type = type_from_value_node$1(access_func.params[i]);
15251
16053
  const param_value = access_func.params[i].node_type === "value" ? access_func.params[i].value : "";
15252
16054
  const arg_is_struct_or_trait = !!status.structs.find((s) => s.name === param_type.name && !s.is_simple_type) || !!status.traits.find((t) => t.name === param_type.name) || !!status.class_vars?.has(param_value);
@@ -15267,8 +16069,14 @@ function build_access_node(node, status) {
15267
16069
  build_node(access_func.params[i], status);
15268
16070
  status.suppress_dereference = false;
15269
16071
  } else build_node(access_func.params[i], status);
16072
+ if (hidden_len.includes(i)) {
16073
+ status.code += `, `;
16074
+ emit_hidden_len_expr(access_func.params[i], void 0, status);
16075
+ }
15270
16076
  }
15271
16077
  status.code += ")";
16078
+ close_hidden_len_wrap(hidden_wrap, status);
16079
+ if (array_wrap) status.code += array_wrap.suffix;
15272
16080
  }
15273
16081
  if (access_func.mov_param_indices) for (const idx of access_func.mov_param_indices) {
15274
16082
  const param = access_func.params[idx];
@@ -15310,6 +16118,11 @@ function resolve_access_field_type(node, status) {
15310
16118
  }
15311
16119
  function emit_string_length(target, status) {
15312
16120
  if (target.node_type === "value") {
16121
+ const name = target.value;
16122
+ if (name !== "self" && status.hidden_len_params?.has(c_function_name(name))) {
16123
+ status.code += `_${c_function_name(name)}_len`;
16124
+ return;
16125
+ }
15313
16126
  const temp = status.string_length_temps?.get(target.value);
15314
16127
  if (temp) {
15315
16128
  status.code += temp;
@@ -15834,6 +16647,10 @@ function build_parameter_node(node, status) {
15834
16647
  status.code += `)`;
15835
16648
  return;
15836
16649
  }
16650
+ if (!node.is_self_param && node.type.is_view) {
16651
+ status.code += `nomen_view ${c_function_name(node.name)}`;
16652
+ return;
16653
+ }
15837
16654
  const cls = classify_param(node.type, type_name, {
15838
16655
  is_ref: node.is_ref || node.type.is_ref,
15839
16656
  is_self: node.is_self_param,
@@ -15961,6 +16778,7 @@ function has_string_fields(node, status) {
15961
16778
  const OWNING_BUFFER_METHODS = /* @__PURE__ */ new Set([
15962
16779
  "store_T",
15963
16780
  "replace_T",
16781
+ "shift_T",
15964
16782
  "destroy",
15965
16783
  "#destroy"
15966
16784
  ]);
@@ -15995,6 +16813,15 @@ function emit_owning_buffer_string_body(func_name, status) {
15995
16813
  status.code += `if (val != _old) { free(_old); _slots[i] = val ? strdup(val) : 0; }\n`;
15996
16814
  return true;
15997
16815
  }
16816
+ if (func_name === "shift_T") {
16817
+ status.code += `char** _slots = (char**)self->data;\n`;
16818
+ status.code += `if (dst != src) {\n`;
16819
+ status.code += `free(_slots[dst]);\n`;
16820
+ status.code += `_slots[dst] = _slots[src];\n`;
16821
+ status.code += `_slots[src] = 0;\n`;
16822
+ status.code += `}\n`;
16823
+ return true;
16824
+ }
15998
16825
  if (func_name === "destroy") {
15999
16826
  status.code += `if (self->data) {\n`;
16000
16827
  status.code += `char** _slots = (char**)self->data;\n`;
@@ -16034,6 +16861,15 @@ function emit_owning_buffer_body(func_name, elem, status) {
16034
16861
  emit_deep_copy_fields(elem, "_slots[i]", "val", status);
16035
16862
  return true;
16036
16863
  }
16864
+ if (func_name === "shift_T") {
16865
+ status.code += `${Tptr}_slots = ${Tcast}(unsigned long long)self->data;\n`;
16866
+ status.code += `if (dst != src) {\n`;
16867
+ status.code += `${elem.name}_destroy(&_slots[dst]);\n`;
16868
+ status.code += `_slots[dst] = _slots[src];\n`;
16869
+ status.code += `memset((void*)&_slots[src], 0, sizeof(struct ${elem.name}));\n`;
16870
+ status.code += `}\n`;
16871
+ return true;
16872
+ }
16037
16873
  if (func_name === "destroy") {
16038
16874
  status.code += `if (self->data) {\n`;
16039
16875
  status.code += `${Tptr}_slots = ${Tcast}(unsigned long long)self->data;\n`;
@@ -16356,6 +17192,10 @@ function build_struct_functions(node, status, skip_init = false) {
16356
17192
  const old_borrow_only = status.c_borrow_only_strings;
16357
17193
  const old_return_type = status.function_return_type;
16358
17194
  const old_function_name = status.current_function_name;
17195
+ const old_view_params = status.function_view_params;
17196
+ status.function_view_params = /* @__PURE__ */ new Set();
17197
+ const old_hidden_len_params = status.hidden_len_params;
17198
+ status.hidden_len_params = /* @__PURE__ */ new Set();
16359
17199
  status.current_function_name = func.name;
16360
17200
  status.function_ref_params = /* @__PURE__ */ new Set();
16361
17201
  status.class_vars = /* @__PURE__ */ new Set();
@@ -16367,6 +17207,8 @@ function build_struct_functions(node, status, skip_init = false) {
16367
17207
  const self_param = func.params[0]?.is_self_param ? func.params[0] : null;
16368
17208
  status.self_is_ref = !!self_param?.is_ref || self_param?.declaration === "var";
16369
17209
  for (let param of func.params) {
17210
+ if (param.type.is_view && !param.is_self_param) status.function_view_params.add(c_function_name(param.name));
17211
+ if (param.hidden_len) status.hidden_len_params.add(c_function_name(param.name));
16370
17212
  const param_struct = status.structs.find((s) => s.name === param.type.name);
16371
17213
  const param_trait = status.traits.find((t) => t.name === param.type.name);
16372
17214
  if (param.is_self_param || param_struct && !param_struct.is_simple_type || param_trait || param.is_ref || param.type.is_ref || param.declaration === "var" && param_struct && !param_struct.is_simple_type) {
@@ -16417,6 +17259,7 @@ function build_struct_functions(node, status, skip_init = false) {
16417
17259
  for (let i = 0; i < func.params.length; i++) {
16418
17260
  if (i > 0) status.code += ", ";
16419
17261
  build_parameter_node(func.params[i], status);
17262
+ if (func.params[i].hidden_len) status.code += `, long _${c_function_name(func.params[i].name)}_len`;
16420
17263
  }
16421
17264
  status.code += `)`;
16422
17265
  forward_decl_referenced_types(func, status);
@@ -16446,6 +17289,8 @@ function build_struct_functions(node, status, skip_init = false) {
16446
17289
  status.c_borrow_only_strings = old_borrow_only;
16447
17290
  status.function_return_type = old_return_type;
16448
17291
  status.current_function_name = old_function_name;
17292
+ status.function_view_params = old_view_params;
17293
+ status.hidden_len_params = old_hidden_len_params;
16449
17294
  }
16450
17295
  status.current_struct = old_current_struct;
16451
17296
  for (let traitName of node.traits) {
@@ -16576,6 +17421,7 @@ function build_function_node(node, status) {
16576
17421
  else {
16577
17422
  if (node.return_type.name) {
16578
17423
  if (node.return_type.is_array) status.code += `struct Array_${node.return_type.name}* `;
17424
+ else if (node.return_type.is_view) status.code += `nomen_view `;
16579
17425
  else {
16580
17426
  const mono_return_name = mono_type_name(node.return_type);
16581
17427
  const return_is_class = !!status.structs.find((s) => s.name === mono_return_name && s.is_class);
@@ -16596,6 +17442,7 @@ function build_function_node(node, status) {
16596
17442
  if (node.params[i].is_variadic) status.code += `long _${node.params[i].name}_len, `;
16597
17443
  build_parameter_node(node.params[i], status);
16598
17444
  if (is_nullable_struct_type(node.params[i].type, status) && !node.params[i].is_self_param) status.code += `, unsigned char ${has_flag_name(c_function_name(node.params[i].name))}`;
17445
+ if (node.params[i].hidden_len) status.code += `, long _${c_function_name(node.params[i].name)}_len`;
16599
17446
  }
16600
17447
  if (is_nullable_struct_type(node.return_type, status)) {
16601
17448
  if (!first_param) status.code += ", ";
@@ -16624,6 +17471,10 @@ function build_function_node(node, status) {
16624
17471
  status.ref_class_param_types = /* @__PURE__ */ new Map();
16625
17472
  const old_variadic_params = status.function_variadic_params;
16626
17473
  status.function_variadic_params = /* @__PURE__ */ new Set();
17474
+ const old_hidden_len_params = status.hidden_len_params;
17475
+ status.hidden_len_params = /* @__PURE__ */ new Set();
17476
+ const old_view_params = status.function_view_params;
17477
+ status.function_view_params = /* @__PURE__ */ new Set();
16627
17478
  const old_return_type = status.function_return_type;
16628
17479
  status.function_return_type = node.return_type;
16629
17480
  const old_nullable_ret_has = status.nullable_ret_has_param;
@@ -16633,6 +17484,8 @@ function build_function_node(node, status) {
16633
17484
  status.current_function_name = node.name;
16634
17485
  for (let param of node.params) {
16635
17486
  if (param.is_variadic) status.function_variadic_params.add(c_function_name(param.name));
17487
+ if (param.hidden_len) status.hidden_len_params.add(c_function_name(param.name));
17488
+ if (param.type.is_view && !param.is_self_param) status.function_view_params.add(c_function_name(param.name));
16636
17489
  if (!param.is_variadic ? array_struct_name(param.type, status) : void 0) {
16637
17490
  const pname = c_function_name(param.name);
16638
17491
  if (!status.heap_array_vars) status.heap_array_vars = /* @__PURE__ */ new Set();
@@ -16665,6 +17518,8 @@ function build_function_node(node, status) {
16665
17518
  status.ref_class_params = old_ref_class_params;
16666
17519
  status.ref_class_param_types = old_ref_class_param_types;
16667
17520
  status.function_variadic_params = old_variadic_params;
17521
+ status.hidden_len_params = old_hidden_len_params;
17522
+ status.function_view_params = old_view_params;
16668
17523
  status.function_return_type = old_return_type;
16669
17524
  status.nullable_ret_has_param = old_nullable_ret_has;
16670
17525
  status.current_function_name = old_function_name;
@@ -17378,7 +18233,8 @@ function build_declaration_node(node, status) {
17378
18233
  const val_node = node.value;
17379
18234
  const val_is_string_literal = node.value.node_type === "value" && val_node.value.length >= 2 && val_node.value.startsWith("\"") && val_node.value.endsWith("\"");
17380
18235
  const val_is_heap_string_var = node.value.node_type === "value" && !val_is_string_literal && !!status.scoped_declarations.find((d) => d.name === val_node.value);
17381
- if (node.declaration === "var" && node.type.name === "string" && !node.type.is_view && (val_is_string_literal || val_is_heap_string_var) && !is_borrow_only_string) {
18236
+ if (node.type.name === "string" && !node.type.is_view && is_view_value(node.value, status)) c_materialize_view_string(node.value, status);
18237
+ else if (node.declaration === "var" && node.type.name === "string" && !node.type.is_view && (val_is_string_literal || val_is_heap_string_var) && !is_borrow_only_string) {
17382
18238
  status.code += `strdup(`;
17383
18239
  build_node(node.value, status);
17384
18240
  status.code += `)`;
@@ -17595,10 +18451,17 @@ function build_function_call_node(node, status) {
17595
18451
  }
17596
18452
  const is_struct = status.structs.find((s) => s.name === node.name && !s.is_simple_type);
17597
18453
  const func_name = is_struct ? `${node.name}_init` : c_function_name(node.name);
18454
+ const hidden_len = callee_hidden_len_indices(node);
18455
+ const hidden_wrap = open_hidden_len_wrap(hidden_len, node.params, status);
17598
18456
  status.code += `${func_name}(`;
17599
18457
  const variadic_idx = node.variadic_param_name ? node.params.findIndex((p) => p.node_type === "array") : -1;
17600
18458
  for (let i = 0; i < node.params.length; i++) {
17601
18459
  if (i > 0) status.code += ", ";
18460
+ const hidden_temp = hidden_wrap.temps.get(i);
18461
+ if (hidden_temp) {
18462
+ status.code += `${hidden_temp}, strlen(${hidden_temp})`;
18463
+ continue;
18464
+ }
17602
18465
  if (i === variadic_idx && node.params[i].node_type === "array") {
17603
18466
  const arr = node.params[i];
17604
18467
  status.code += `${arr.values.length}, (${c_type(arr.type.name)}[]){`;
@@ -17620,6 +18483,10 @@ function build_function_call_node(node, status) {
17620
18483
  status.suppress_dereference = false;
17621
18484
  continue;
17622
18485
  }
18486
+ if (node.view_param_indices?.includes(i)) {
18487
+ c_view_string_arg(node.params[i], status);
18488
+ continue;
18489
+ }
17623
18490
  const is_ref_param = node.ref_param_indices?.includes(i);
17624
18491
  const param_is_class = !!status.structs.find((s) => s.name === param_type.name && s.is_class);
17625
18492
  if (is_ref_param && param_is_class) {
@@ -17649,6 +18516,10 @@ function build_function_call_node(node, status) {
17649
18516
  status.code += `, `;
17650
18517
  emit_nullable_arg_flag(node.params[i], status);
17651
18518
  }
18519
+ if (hidden_len.includes(i)) {
18520
+ status.code += `, `;
18521
+ emit_hidden_len_expr(node.params[i], void 0, status);
18522
+ }
17652
18523
  }
17653
18524
  if (is_nullable_struct_type(node.type, status)) {
17654
18525
  const flag_name = status.current_nullable_call_flag;
@@ -17664,6 +18535,7 @@ function build_function_call_node(node, status) {
17664
18535
  }
17665
18536
  }
17666
18537
  status.code += ")";
18538
+ close_hidden_len_wrap(hidden_wrap, status);
17667
18539
  if (node.name.startsWith("_string_interpolate_")) status.interpolate_string_counts.add(node.params.length - 1);
17668
18540
  if (node.swap_params?.size) for (const [idx, swap_expr] of node.swap_params) {
17669
18541
  const source = node.params[idx];
@@ -18072,10 +18944,13 @@ function build_return_node(node, status) {
18072
18944
  }
18073
18945
  }
18074
18946
  if (returns_borrowed_string || returns_string_literal || returns_borrow_var) status.code += `strdup(`;
18947
+ const wrap_view_string_return = !!ret_type.is_view && ret_type.name === "string" && !is_view_value(node.value, status);
18075
18948
  const expr_type_name = node.type?.name || "";
18076
18949
  const expr_is_simple = !status.structs.find((s) => s.name === expr_type_name && !s.is_simple_type);
18077
18950
  if (is_struct && expr_is_simple && !returns_borrowed_string && !ret_type.is_view) status.code += return_is_class ? `(struct ${mono_ret_name}*)` : `(struct ${mono_ret_name})`;
18951
+ if (wrap_view_string_return) status.code += `({ char* _p = `;
18078
18952
  build_node(node.value, status);
18953
+ if (wrap_view_string_return) status.code += `; nomen_view _v = { (void*)_p, (long)strlen(_p) }; _v; })`;
18079
18954
  if (returns_borrowed_string || returns_string_literal || returns_borrow_var) status.code += `)`;
18080
18955
  status.code += `;\n`;
18081
18956
  if (node.value.node_type === "func_call" && node.value.field_overrides?.length) emit_field_overrides("_return_val", node.value, build_node, status, "", ";\n");
@@ -18289,6 +19164,7 @@ function build_while_loop_node(node, status) {
18289
19164
  if (!status.string_length_temps) status.string_length_temps = /* @__PURE__ */ new Map();
18290
19165
  for (const [name, target] of hoists) {
18291
19166
  if (status.string_length_temps.has(name)) continue;
19167
+ if (status.hidden_len_params?.has(c_function_name(name))) continue;
18292
19168
  const temp = `_slh_${status.label_counter = (status.label_counter ?? 0) + 1}`;
18293
19169
  status.string_length_temps.set(name, temp);
18294
19170
  status.code += `const long ${temp} = (long)strlen(`;
@@ -20131,6 +21007,16 @@ function add_error(status, message, start) {
20131
21007
  });
20132
21008
  }
20133
21009
  //#endregion
21010
+ //#region ../src/nodes/set_resolved_function.ts
21011
+ function set_resolved_function(node, func) {
21012
+ Object.defineProperty(node, "resolved_function", {
21013
+ value: func,
21014
+ enumerable: false,
21015
+ writable: true,
21016
+ configurable: true
21017
+ });
21018
+ }
21019
+ //#endregion
20134
21020
  //#region ../src/nodes/clone_node.ts
20135
21021
  function clone_type$2(type) {
20136
21022
  const t = new Type(type.name, type.is_static, type.is_array, type.length ? clone_node(type.length) : void 0);
@@ -20165,10 +21051,13 @@ function clone_node(node) {
20165
21051
  c.type_args = n.type_args?.map(clone_type$2);
20166
21052
  c.ref_param_indices = n.ref_param_indices?.slice();
20167
21053
  c.mov_param_indices = n.mov_param_indices?.slice();
21054
+ c.nullable_param_indices = n.nullable_param_indices?.slice();
21055
+ c.view_param_indices = n.view_param_indices?.slice();
20168
21056
  c.swap_params = n.swap_params ? new Map([...n.swap_params].map(([k, v]) => [k, clone_node(v)])) : void 0;
20169
21057
  c.variadic_param_name = n.variadic_param_name;
20170
21058
  c.variadic_param_index = n.variadic_param_index;
20171
21059
  c.is_enum_shorthand = n.is_enum_shorthand;
21060
+ set_resolved_function(c, n.resolved_function);
20172
21061
  c.allocations = n.allocations?.map(clone_node);
20173
21062
  return c;
20174
21063
  }
@@ -20196,6 +21085,7 @@ function clone_node(node) {
20196
21085
  c.mangled_name = n.mangled_name;
20197
21086
  c.owned_return = n.owned_return;
20198
21087
  c.nullable_param_indices = n.nullable_param_indices?.slice();
21088
+ c.view_param_indices = n.view_param_indices?.slice();
20199
21089
  c.return_bounds = n.return_bounds ? {
20200
21090
  upper: n.return_bounds.upper.slice(),
20201
21091
  lower: n.return_bounds.lower.slice(),
@@ -20207,6 +21097,7 @@ function clone_node(node) {
20207
21097
  c.function_return_type = n.function_return_type ? clone_type$2(n.function_return_type) : void 0;
20208
21098
  c.is_statement = n.is_statement;
20209
21099
  c.skip_bounds_check = n.skip_bounds_check;
21100
+ set_resolved_function(c, n.resolved_function);
20210
21101
  c.allocations = n.allocations?.map(clone_node);
20211
21102
  return c;
20212
21103
  }
@@ -20405,7 +21296,10 @@ function clone_node(node) {
20405
21296
  c.default_value_start = n.default_value_start;
20406
21297
  c.is_variadic = n.is_variadic;
20407
21298
  c.is_ref = n.is_ref;
21299
+ c.is_variadic_tuple = n.is_variadic_tuple;
21300
+ c.hidden_len = n.hidden_len;
20408
21301
  c.constraint = n.constraint ? clone_node(n.constraint) : void 0;
21302
+ c.stripped_length_equalities = n.stripped_length_equalities?.map((e) => ({ ...e }));
20409
21303
  c.func_params = n.func_params?.map((p) => clone_node(p));
20410
21304
  c.func_return_type = n.func_return_type ? clone_type$2(n.func_return_type) : void 0;
20411
21305
  c.allocations = n.allocations?.map(clone_node);
@@ -21111,6 +22005,66 @@ function strip_length_equalities(constraint, param_name, status) {
21111
22005
  });
21112
22006
  }
21113
22007
  /**
22008
+ * Registration-time variant of strip_length_equalities: removes
22009
+ * `<container>.<size> == <container>.<size>` conjuncts from a parameter
22010
+ * constraint and returns them alongside the remaining constraint. Used when a
22011
+ * function signature is REGISTERED — before any call site can observe it — so
22012
+ * a forward-referenced callee never hands its caller a clause no call site
22013
+ * could prove (declaration order must not matter). Bases are validated
22014
+ * against `known_names` (the function's own parameters) instead of
22015
+ * status.values, because at registration time nothing has been pushed into
22016
+ * any scope yet. Does NOT touch status.equal_lengths: the extracted
22017
+ * equalities are returned for the callee's own param check to seed.
22018
+ */
22019
+ function extract_length_equalities_at_registration(constraint, param_name, known_names) {
22020
+ const equalities = [];
22021
+ if (!constraint) return {
22022
+ constraint,
22023
+ equalities
22024
+ };
22025
+ const walk = (node) => {
22026
+ if (node.node_type !== "op") return node;
22027
+ const op = node;
22028
+ if (op.op === "&&") {
22029
+ const left = walk(op.left_value);
22030
+ const right = walk(op.right_value);
22031
+ if (left && right) {
22032
+ if (left === op.left_value && right === op.right_value) return node;
22033
+ return new OperationNode(op.start, "&&", left, right, op.type);
22034
+ }
22035
+ return left ?? right;
22036
+ }
22037
+ if (op.op !== "==") return node;
22038
+ const extract = (side) => {
22039
+ if (side.node_type !== "access") return void 0;
22040
+ const access = side;
22041
+ let field;
22042
+ if (access.access.node_type === "access_field") field = access.access.name;
22043
+ else if (access.access.node_type === "access_func") field = access.access.name;
22044
+ if (!field || !NON_NEGATIVE_FIELDS$1.has(field)) return void 0;
22045
+ if (access.target.node_type !== "value") return void 0;
22046
+ return {
22047
+ base: access.target.value,
22048
+ field
22049
+ };
22050
+ };
22051
+ const lhs = extract(op.left_value);
22052
+ const rhs = extract(op.right_value);
22053
+ if (!lhs || !rhs || lhs.field !== rhs.field) return node;
22054
+ if (lhs.base === rhs.base) return node;
22055
+ for (const base of [lhs.base, rhs.base]) if (base !== param_name && !known_names.has(base)) return node;
22056
+ equalities.push({
22057
+ a: lhs.base,
22058
+ b: rhs.base,
22059
+ field: lhs.field
22060
+ });
22061
+ };
22062
+ return {
22063
+ constraint: walk(constraint),
22064
+ equalities
22065
+ };
22066
+ }
22067
+ /**
21114
22068
  * Extract flow-sensitive bounds from a comparison condition.
21115
22069
  * E.g. `j < list.length` → { var: "j", op: "<", expr: "list.length" }
21116
22070
  * Returns undefined if the condition isn't a simple var-vs-expr comparison.
@@ -22433,6 +23387,7 @@ function is_inside_core_method(status) {
22433
23387
  return false;
22434
23388
  }
22435
23389
  function check_function_call(node, status, func, target_type, self_value, self_path) {
23390
+ set_resolved_function(node, func);
22436
23391
  const access_scope = status.stack.at(-1);
22437
23392
  if (func.name === "#init") {
22438
23393
  const struct_name = target_type?.name || func.return_type.name;
@@ -22498,6 +23453,10 @@ function check_function_call(node, status, func, target_type, self_value, self_p
22498
23453
  if (!node.nullable_param_indices.includes(i)) node.nullable_param_indices.push(i);
22499
23454
  }
22500
23455
  }
23456
+ if (func_param && func_param.type.is_view && !func_param.is_self_param) {
23457
+ if (!node.view_param_indices) node.view_param_indices = [];
23458
+ if (!node.view_param_indices.includes(i)) node.view_param_indices.push(i);
23459
+ }
22501
23460
  const old_expected_type = status.expected_type;
22502
23461
  const old_allow_null = status.allow_null_value;
22503
23462
  if (func_param) {
@@ -24114,6 +25073,15 @@ function gather_structs(block, status) {
24114
25073
  func.scope = status.stack.at(-1) || block;
24115
25074
  func.return_type = materialize_type(func.return_type, status);
24116
25075
  instantiate_generic_type(func.return_type, status);
25076
+ const param_names = new Set(func.params.map((p) => p.name));
25077
+ for (const param of func.params) {
25078
+ if (!param.constraint) continue;
25079
+ const { constraint, equalities } = extract_length_equalities_at_registration(param.constraint, param.name, param_names);
25080
+ if (equalities.length) {
25081
+ param.constraint = constraint;
25082
+ param.stripped_length_equalities = equalities;
25083
+ }
25084
+ }
24117
25085
  status.functions.push(func);
24118
25086
  }
24119
25087
  break;
@@ -24227,6 +25195,10 @@ function check_function_parameter_node(param, status) {
24227
25195
  is_set: true,
24228
25196
  is_null: param.type.is_nullable ? true : void 0
24229
25197
  });
25198
+ if (param.stripped_length_equalities?.length) {
25199
+ if (!status.equal_lengths) status.equal_lengths = [];
25200
+ for (const eq of param.stripped_length_equalities) if (!status.equal_lengths.some((e) => e.field === eq.field && (e.a === eq.a && e.b === eq.b || e.a === eq.b && e.b === eq.a))) status.equal_lengths.push(eq);
25201
+ }
24230
25202
  if (param.constraint) {
24231
25203
  const stripped = strip_length_equalities(param.constraint, param.name, status);
24232
25204
  if (stripped !== param.constraint) {
@@ -24289,6 +25261,15 @@ function check_function_node(func, status) {
24289
25261
  if (func.checked) return;
24290
25262
  func.checked = true;
24291
25263
  status.functions.push(func);
25264
+ const param_names = new Set(func.params.map((p) => p.name));
25265
+ for (const param of func.params) {
25266
+ if (!param.constraint) continue;
25267
+ const { constraint, equalities } = extract_length_equalities_at_registration(param.constraint, param.name, param_names);
25268
+ if (equalities.length) {
25269
+ param.constraint = constraint;
25270
+ param.stripped_length_equalities = equalities;
25271
+ }
25272
+ }
24292
25273
  if (is_generic_func(func)) {
24293
25274
  func.is_generic = true;
24294
25275
  return;
@@ -27711,6 +28692,251 @@ function promote_allocations(node, status) {
27711
28692
  }
27712
28693
  }
27713
28694
  //#endregion
28695
+ //#region ../src/check/stamp_hidden_string_lens.ts
28696
+ /**
28697
+ * Post-check pass implementing the PERF gap 2.4 fix: thread a string's
28698
+ * length through the call boundary so a per-character helper doesn't pay one
28699
+ * `strlen` per CALL.
28700
+ *
28701
+ * For every function whose Nomen-level body reads `<param>.length` on a
28702
+ * by-value `string` param (and never rebinds, mutates, or escapes it), the
28703
+ * param is stamped `hidden_len`. The backends then lower it as TWO
28704
+ * parameters — the `char*` plus a trailing `long _<name>_len` companion
28705
+ * (mirroring the nullable `_has` companion) — and the body's `.length` reads
28706
+ * become loads of that companion instead of `strlen`. Call sites append the
28707
+ * companion argument, preferring a loop-invariant hoisted strlen temp
28708
+ * (scan_string_length_hoists treats such args like `.length` reads), so a
28709
+ * helper called once per line over a large document costs ONE strlen, not
28710
+ * O(lines).
28711
+ *
28712
+ * Conservative exclusions (any of these keeps the plain one-pointer ABI):
28713
+ * - raw blocks anywhere in the body (opaque code may touch the param), or no
28714
+ * body at all
28715
+ * - `#init`/`#destroy`/`init`/`destroy`/`main` and any non-identifier or
28716
+ * `_`/`#`-prefixed name (operators, compiler-generated helpers) — their
28717
+ * call sites use bespoke emission paths
28718
+ * - variadic params anywhere (their call sites pack hidden count/ptr pairs)
28719
+ * - `is_inline` functions (the aarch64 inline splicer)
28720
+ * - trait method declarations/default bodies, and struct methods that
28721
+ * implement a trait method (vtable dispatch uses the trait's declared
28722
+ * signature)
28723
+ * - functions referenced as VALUES anywhere in the program (`var func f =
28724
+ * helper`, `obj.method` as a value, nursery.spawn targets) — indirect
28725
+ * calls use the declared ABI
28726
+ * - params that are ref/mov/view/nullable/variadic/self, or that the body
28727
+ * assigns to, passes ref/mov/swap, mutates through a `var self`/`ref self`
28728
+ * string method, or references across a nested func/async boundary, or
28729
+ * whose companion name `_<name>_len` collides with any declared name in
28730
+ * the body
28731
+ */
28732
+ const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
28733
+ "true",
28734
+ "false",
28735
+ "null",
28736
+ "self",
28737
+ "as",
28738
+ "default"
28739
+ ]);
28740
+ function is_plain_identifier(value) {
28741
+ if (!value) return false;
28742
+ if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(value)) return false;
28743
+ if (value.startsWith("_")) return false;
28744
+ return !NON_IDENTIFIERS.has(value);
28745
+ }
28746
+ function root_name(node) {
28747
+ if (!node) return void 0;
28748
+ if (node.node_type === "value") {
28749
+ const v = node.value;
28750
+ return is_plain_identifier(v) ? v : void 0;
28751
+ }
28752
+ if (node.node_type === "access") return root_name(node.target);
28753
+ }
28754
+ function collect_functions(root, status) {
28755
+ const out = [];
28756
+ const seen = /* @__PURE__ */ new Set();
28757
+ const add = (func, struct, is_trait_func) => {
28758
+ if (seen.has(func)) return;
28759
+ seen.add(func);
28760
+ out.push({
28761
+ func,
28762
+ struct,
28763
+ is_trait_func
28764
+ });
28765
+ };
28766
+ for (const struct of status.structs) for (const func of struct.functions) add(func, struct);
28767
+ for (const trait of status.traits) for (const func of trait.functions) add(func, void 0, true);
28768
+ for (const func of status.functions) add(func);
28769
+ walk_tree(root, (n) => {
28770
+ if (n.node_type === "struct") {
28771
+ const s = n;
28772
+ for (const func of s.functions) add(func, s);
28773
+ } else if (n.node_type === "trait") {
28774
+ const t = n;
28775
+ for (const func of t.functions) add(func, void 0, true);
28776
+ } else if (n.node_type === "func") add(n);
28777
+ });
28778
+ return out;
28779
+ }
28780
+ /** Names of functions referenced as VALUES (func-typed, non-call nodes). */
28781
+ function collect_value_referenced_names(root, status) {
28782
+ const excluded = /* @__PURE__ */ new Set();
28783
+ const visit = (n) => {
28784
+ if (n.type?.name !== "func") return;
28785
+ if (n.node_type === "value") {
28786
+ const v = n.value;
28787
+ if (is_plain_identifier(v)) excluded.add(v);
28788
+ } else if (n.node_type === "access_field") {
28789
+ const name = n.name;
28790
+ if (is_plain_identifier(name)) excluded.add(name);
28791
+ }
28792
+ };
28793
+ walk_tree(root, visit);
28794
+ for (const struct of status.structs) walk_tree(struct, visit);
28795
+ for (const trait of status.traits) walk_tree(trait, visit);
28796
+ for (const func of status.functions) walk_tree(func, visit);
28797
+ return excluded;
28798
+ }
28799
+ function walk_tree(node, visit) {
28800
+ if (!node || typeof node !== "object") return;
28801
+ if (Array.isArray(node)) {
28802
+ for (const item of node) walk_tree(item, visit);
28803
+ return;
28804
+ }
28805
+ const n = node;
28806
+ if (typeof n.node_type !== "string") return;
28807
+ visit(n);
28808
+ const record = n;
28809
+ for (const key of Object.keys(record)) {
28810
+ if (key === "parent" || key === "scope" || key === "node_type") continue;
28811
+ walk_tree(record[key], visit);
28812
+ }
28813
+ }
28814
+ function stamp_hidden_string_lens(root, status) {
28815
+ const value_referenced = collect_value_referenced_names(root, status);
28816
+ const owners = collect_functions(root, status);
28817
+ const string_struct = status.structs.find((s) => s.name === "string");
28818
+ for (const { func, struct, is_trait_func } of owners) {
28819
+ if (func.hidden_len_stamped) continue;
28820
+ func.hidden_len_stamped = true;
28821
+ if (!func.has_body || func.statements.length === 0) continue;
28822
+ if (func.is_inline || is_trait_func) continue;
28823
+ const bare_name = func.name.replace(/^#/, "");
28824
+ if (!is_plain_identifier(func.name)) continue;
28825
+ if ([
28826
+ "main",
28827
+ "init",
28828
+ "destroy"
28829
+ ].includes(bare_name)) continue;
28830
+ if (value_referenced.has(func.name)) continue;
28831
+ if (func.params.some((p) => p.is_variadic || p.is_variadic_tuple)) continue;
28832
+ if (struct && trait_declares_method(struct, func.name, status)) continue;
28833
+ const candidates = /* @__PURE__ */ new Set();
28834
+ for (const param of func.params) if (param_is_hidden_len_candidate(param)) candidates.add(param.name);
28835
+ if (candidates.size === 0) continue;
28836
+ const reads = /* @__PURE__ */ new Set();
28837
+ const invalidated = /* @__PURE__ */ new Set();
28838
+ const declared_names = /* @__PURE__ */ new Set();
28839
+ let has_raw = false;
28840
+ const visit_children = (n, in_boundary) => {
28841
+ const record = n;
28842
+ for (const key of Object.keys(record)) {
28843
+ if (key === "parent" || key === "scope" || key === "node_type") continue;
28844
+ const child = record[key];
28845
+ if (Array.isArray(child)) {
28846
+ for (const item of child) if (item && typeof item === "object" && item.node_type) visit(item, in_boundary);
28847
+ } else if (child && typeof child === "object" && child.node_type) visit(child, in_boundary);
28848
+ }
28849
+ };
28850
+ const visit = (n, in_boundary) => {
28851
+ switch (n.node_type) {
28852
+ case "raw":
28853
+ has_raw = true;
28854
+ return;
28855
+ case "func":
28856
+ case "async_block":
28857
+ visit_children(n, true);
28858
+ return;
28859
+ case "value": {
28860
+ const v = n.value;
28861
+ if (in_boundary && is_plain_identifier(v)) invalidated.add(v);
28862
+ return;
28863
+ }
28864
+ case "access": {
28865
+ const access = n;
28866
+ if (!in_boundary && access.access.node_type === "access_field" && access.access.name === "length" && access.target.node_type === "value") {
28867
+ const name = access.target.value;
28868
+ if (name !== void 0 && candidates.has(name)) reads.add(name);
28869
+ }
28870
+ if (!in_boundary && access.access.node_type === "access_func" && access.target.node_type === "value") {
28871
+ const name = access.target.value;
28872
+ const call = access.access;
28873
+ if (name !== void 0 && candidates.has(name)) {
28874
+ const self_param = (string_struct?.functions.find((f) => f.name === call.name || f.name === `#${call.name}`))?.params?.[0];
28875
+ if (self_param?.is_self_param && (self_param.declaration === "var" || self_param.is_ref)) invalidated.add(name);
28876
+ }
28877
+ }
28878
+ visit_children(n, in_boundary);
28879
+ return;
28880
+ }
28881
+ case "assign": {
28882
+ const assign = n;
28883
+ const lhs = root_name(assign.left_value);
28884
+ if (lhs) invalidated.add(lhs);
28885
+ const swap = root_name(assign.swap);
28886
+ if (swap) invalidated.add(swap);
28887
+ visit_children(n, in_boundary);
28888
+ return;
28889
+ }
28890
+ case "declare":
28891
+ case "param": {
28892
+ const name = n.name;
28893
+ if (name) declared_names.add(name);
28894
+ visit_children(n, in_boundary);
28895
+ return;
28896
+ }
28897
+ case "for": {
28898
+ const item = n.item;
28899
+ if (item?.value) declared_names.add(item.value);
28900
+ visit_children(n, in_boundary);
28901
+ return;
28902
+ }
28903
+ case "func_call":
28904
+ case "access_func": {
28905
+ const call = n;
28906
+ for (const indices of [call.ref_param_indices, call.mov_param_indices]) for (const i of indices ?? []) {
28907
+ const arg = root_name(call.params?.[i]);
28908
+ if (arg) invalidated.add(arg);
28909
+ }
28910
+ if (call.swap_params) {
28911
+ for (const [i, swap] of call.swap_params) for (const arg of [root_name(call.params?.[i]), root_name(swap)]) if (arg) invalidated.add(arg);
28912
+ }
28913
+ visit_children(n, in_boundary);
28914
+ return;
28915
+ }
28916
+ default:
28917
+ visit_children(n, in_boundary);
28918
+ return;
28919
+ }
28920
+ };
28921
+ for (const stmt of func.statements) visit(stmt, false);
28922
+ if (has_raw) continue;
28923
+ for (const name of candidates) {
28924
+ if (!reads.has(name)) continue;
28925
+ if (invalidated.has(name)) continue;
28926
+ if (declared_names.has(`_${name}_len`)) continue;
28927
+ const param = func.params.find((p) => p.name === name);
28928
+ param.hidden_len = true;
28929
+ }
28930
+ }
28931
+ }
28932
+ function trait_declares_method(struct, method_name, status) {
28933
+ for (const trait_name of struct.traits) if (status.traits.find((t) => t.name === trait_name)?.functions.some((f) => f.name === method_name)) return true;
28934
+ return false;
28935
+ }
28936
+ function param_is_hidden_len_candidate(param) {
28937
+ return !param.is_self_param && !param.is_variadic && !param.is_variadic_tuple && !param.is_ref && !param.is_moved && param.type.name === "string" && !param.type.is_array && !param.type.is_ref && !param.type.is_view && !param.type.is_nullable;
28938
+ }
28939
+ //#endregion
27714
28940
  //#region ../src/check/warnings.ts
27715
28941
  /**
27716
28942
  * Lint pass run after a successful check. It walks the AST to surface three
@@ -27913,6 +29139,7 @@ function check(root) {
27913
29139
  mutated_local_names: /* @__PURE__ */ new Set()
27914
29140
  };
27915
29141
  check_node(root, status);
29142
+ stamp_hidden_string_lens(root, status);
27916
29143
  if (!status.errors.length) emit_warnings(root, status);
27917
29144
  return {
27918
29145
  ok: !status.errors.length,