nomen-lang 0.6.6 → 0.6.7

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.
Files changed (2) hide show
  1. package/dist/index.mjs +525 -171
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -7147,14 +7147,37 @@ function call_result_type(node) {
7147
7147
  * so `null == 0` conflates them. Nullable `string` (in-band fat zero) and
7148
7148
  * nullable `func`/class values (null pointer) keep their in-band null and
7149
7149
  * are NOT nullable scalars.
7150
+ *
7151
+ * The same holds for a nullable SIMPLE enum (`Color?`) or bitset
7152
+ * (`Permissions?`): the in-band zero (the first case's tag 0 / an empty
7153
+ * bitset) is a real value, so `null` needs the companion flag. Enum-with-data
7154
+ * and generic template enums are NOT nullable scalars — the checker rejects
7155
+ * `?` on enums with associated data, so every nullable enum that reaches the
7156
+ * backends is simple.
7150
7157
  */
7151
- /** True if `type` is a nullable built-in scalar (`bool?`, `int?`, `float?`, …). */
7152
- function is_nullable_scalar_type(type) {
7158
+ /** True if the TYPE NAME is a built-in scalar, simple enum, or bitset — the
7159
+ * shapes that get the `<slot>_has` flag when nullable. */
7160
+ function is_nullable_scalar_name(name, tables) {
7161
+ if (!name) return false;
7162
+ const info = get_built_in_type(name);
7163
+ if (info) return info.kind !== "string" && info.kind !== "func";
7164
+ const enum_node = tables.enums.findLast((e) => (e.name === name || e.source_name === name) && !e.is_generic);
7165
+ if (enum_node) return !enum_node.has_associated_data;
7166
+ return tables.bitsets.some((b) => b.name === name || b.source_name === name);
7167
+ }
7168
+ /** True if `type` is a nullable built-in scalar, simple enum, or bitset. */
7169
+ function is_nullable_scalar_type(type, tables) {
7153
7170
  if (!type?.is_nullable) return false;
7154
7171
  if (type.is_array || type.is_ref || type.storage_kind === "view") return false;
7155
7172
  if (type.is_pointer) return false;
7156
- const info = type.name ? get_built_in_type(type.name) : void 0;
7157
- return !!info && info.kind !== "string" && info.kind !== "func";
7173
+ return is_nullable_scalar_name(type.name, tables);
7174
+ }
7175
+ /** True if `type` is a nullable struct (see nullable_struct.ts) or nullable scalar. */
7176
+ function is_nullable_flagged_type(type, status) {
7177
+ if (!type?.is_nullable) return false;
7178
+ if (type.name === "string") return false;
7179
+ if (type.name === "func") return false;
7180
+ return is_nullable_scalar_type(type, status) || !!status.structs.find((s) => s.name === type.name && !s.is_class && !s.is_simple_type);
7158
7181
  }
7159
7182
  //#endregion
7160
7183
  //#region ../src/build_common/nullable_struct.ts
@@ -8084,7 +8107,7 @@ var AssignmentNode = class extends BaseNode {
8084
8107
  super("assign", start);
8085
8108
  this.left_value = left_value;
8086
8109
  this.right_value = right_value;
8087
- this.operator = operator;
8110
+ this.operator = operator === "=" ? void 0 : operator;
8088
8111
  }
8089
8112
  };
8090
8113
  //#endregion
@@ -12011,7 +12034,7 @@ function get_struct_size(name, status) {
12011
12034
  size = align_to(size, get_type_alignment(field.type, status));
12012
12035
  size += get_type_size(field.type, status);
12013
12036
  if (is_nullable_struct_type(field.type, status)) size += 8;
12014
- else if (is_nullable_scalar_type(field.type)) size += 1;
12037
+ else if (is_nullable_scalar_type(field.type, status)) size += 1;
12015
12038
  }
12016
12039
  size = align_to(size, 8);
12017
12040
  return size;
@@ -12063,7 +12086,7 @@ function get_field_offset_of_fields(fields, field_name, status) {
12063
12086
  if (field.name === field_name) return offset;
12064
12087
  offset += get_type_size(field.type, status);
12065
12088
  if (is_nullable_struct_type(field.type, status)) offset += 8;
12066
- else if (is_nullable_scalar_type(field.type)) offset += 1;
12089
+ else if (is_nullable_scalar_type(field.type, status)) offset += 1;
12067
12090
  }
12068
12091
  return offset;
12069
12092
  }
@@ -17729,7 +17752,7 @@ function owning_buffer_element_aarch64(node, status) {
17729
17752
  const elem_name = node.name.substring(7);
17730
17753
  const elem = status.structs.find((s) => s.name === elem_name && !s.is_simple_type && !s.is_generic);
17731
17754
  if (!elem || elem.is_class) return void 0;
17732
- if (!has_string_fields(elem, status)) return void 0;
17755
+ if (!has_string_fields(elem, status) && !struct_needs_destroy(elem, status)) return void 0;
17733
17756
  return elem;
17734
17757
  }
17735
17758
  /**
@@ -17875,6 +17898,75 @@ function collect_string_fields(elem, status, base_offset = 0) {
17875
17898
  return result;
17876
17899
  }
17877
17900
  /**
17901
+ * Collect the (offset, size) pairs of every owned CONTAINER field (`List<T>`/
17902
+ * `Buffer<T>`/… mono with a `#destroy`) the element's teardown would reclaim.
17903
+ * The owning store_T/replace_T memcpy the source's bytes into the slot, so
17904
+ * zeroing these fields of the SOURCE completes the ownership transfer: the
17905
+ * slot is the sole owner and the source's own teardown (when it still runs)
17906
+ * finds zeroed fields. Nested value struct fields recurse so their container
17907
+ * leaves are zeroed at their own flat offsets; their string fields are
17908
+ * untouched (the slot owns the strdup'd copies via collect_string_fields).
17909
+ * Mirrors the C backend's `emit_transfer_owning_fields`. `view T`/nullable
17910
+ * fields are skipped; class/enum payload fields are left alone (recorded in
17911
+ * FOLLOWUP.md).
17912
+ */
17913
+ function collect_transfer_fields(elem, status, base_offset = 0) {
17914
+ const result = [];
17915
+ let offset = 8;
17916
+ for (const field of elem.fields) {
17917
+ const size = get_type_size(field.type, status);
17918
+ if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable) {
17919
+ const field_struct = resolve_struct_type(field.type, status);
17920
+ if (field_struct && !field_struct.is_class) {
17921
+ if (has_destroy(field_struct)) result.push({
17922
+ offset: base_offset + offset,
17923
+ size
17924
+ });
17925
+ else if (struct_needs_destroy(field_struct, status)) result.push(...collect_transfer_fields(field_struct, status, base_offset + offset));
17926
+ }
17927
+ }
17928
+ offset += size;
17929
+ }
17930
+ return result;
17931
+ }
17932
+ /**
17933
+ * Emit `memset(&src.field, 0, size)` for every owned field collected by
17934
+ * `collect_transfer_fields`. `src` is the register holding the SOURCE struct
17935
+ * address (x2 inline / x21 standalone for store_T). Clobbers x0-x2.
17936
+ */
17937
+ function emit_transfer_owning_fields$1(status, src, fields) {
17938
+ for (const { offset, size } of fields) {
17939
+ emit_asm(status, `add x0, ${src}, #${offset}\n`);
17940
+ emit_asm(status, `mov x1, #0\n`);
17941
+ emit_asm(status, `mov x2, #${size}\n`);
17942
+ emit_asm(status, `bl _memset\n`);
17943
+ }
17944
+ }
17945
+ /**
17946
+ * Emit the deep-copy half of owning load_T into the sret buffer (x8): for
17947
+ * each `List<T>` field whose mono has a `copy` method, replace the
17948
+ * shallow-copied field with `<List_T>_copy(&slot.field)` (sret → the sret
17949
+ * buffer's field; self == x8 is safe — the copy reads its input before the
17950
+ * final result write). Mirrors the C backend's `emit_load_deep_copy_fields`.
17951
+ * `sret` is the register holding the returned struct's address; `slot` holds
17952
+ * the source slot's address (the copy reads from the slot, writes to sret).
17953
+ */
17954
+ function emit_load_deep_copy_fields$1(elem, status, sret, slot) {
17955
+ let offset = 8;
17956
+ for (const field of elem.fields) {
17957
+ const size = get_type_size(field.type, status);
17958
+ if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable && field.type.name !== "string") {
17959
+ const field_struct = resolve_struct_type(field.type, status);
17960
+ if (field_struct && !field_struct.is_class && field_struct.name.startsWith("List_") && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
17961
+ emit_asm(status, `add x8, ${sret}, #${offset}\n`);
17962
+ emit_asm(status, `add x0, ${slot}, #${offset}\n`);
17963
+ emit_asm(status, `bl ${field_struct.name}_copy\n`);
17964
+ }
17965
+ }
17966
+ offset += size;
17967
+ }
17968
+ }
17969
+ /**
17878
17970
  * Emit `slot.field = strdup(src.field)` guarded against a NULL source field
17879
17971
  * (e.g. JsonTree's "no text" sentinel): a NULL field is copied as NULL —
17880
17972
  * strdup(NULL) would crash. `src`/`dst` are the register names holding the
@@ -17912,6 +18004,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
17912
18004
  emit_owning_shift_T(elem, status, "x0");
17913
18005
  return true;
17914
18006
  }
18007
+ if (func_name === "load") {
18008
+ emit_owning_load_T(elem, status, "x0");
18009
+ return true;
18010
+ }
17915
18011
  return false;
17916
18012
  }
17917
18013
  const enum_elem = owning_buffer_enum_element_aarch64(struct_node, status);
@@ -17973,7 +18069,7 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
17973
18069
  function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
17974
18070
  const elem = owning_buffer_element_aarch64(node, status);
17975
18071
  if (elem) {
17976
- if (func_name !== "store" && func_name !== "replace" && func_name !== "shift" && func_name !== "modify") return false;
18072
+ if (func_name !== "store" && func_name !== "replace" && func_name !== "shift" && func_name !== "modify" && func_name !== "load") return false;
17977
18073
  emit_owning_standalone_struct(elem, func_name, status);
17978
18074
  return true;
17979
18075
  }
@@ -18032,6 +18128,7 @@ function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
18032
18128
  function emit_owning_standalone_struct(elem, func_name, status) {
18033
18129
  const T_SIZE = get_struct_size(elem.name, status);
18034
18130
  const string_fields = collect_string_fields(elem, status);
18131
+ const transfer_fields = collect_transfer_fields(elem, status);
18035
18132
  if (func_name === "shift") {
18036
18133
  emit_owning_shift_T(elem, status, "x19");
18037
18134
  return true;
@@ -18040,6 +18137,23 @@ function emit_owning_standalone_struct(elem, func_name, status) {
18040
18137
  emit_owning_modify_T(elem, status, T_SIZE, string_fields);
18041
18138
  return true;
18042
18139
  }
18140
+ if (func_name === "load") {
18141
+ emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
18142
+ emit_asm(status, `stp x22, x23, [sp, #-16]!\n`);
18143
+ emit_asm(status, `ldr x20, [x19, #8]\n`);
18144
+ emit_asm(status, `mov x9, #${T_SIZE}\n`);
18145
+ emit_asm(status, `madd x21, x1, x9, x20\n`);
18146
+ emit_asm(status, `mov x22, x8\n`);
18147
+ emit_asm(status, `mov x0, x22\n`);
18148
+ emit_asm(status, `mov x1, x21\n`);
18149
+ emit_asm(status, `mov x2, x9\n`);
18150
+ emit_asm(status, `bl _memcpy\n`);
18151
+ emit_load_deep_copy_fields$1(elem, status, "x22", "x21");
18152
+ emit_asm(status, `mov x0, x22\n`);
18153
+ emit_asm(status, `ldp x22, x23, [sp], #16\n`);
18154
+ emit_asm(status, `ldp x20, x21, [sp], #16\n`);
18155
+ return true;
18156
+ }
18043
18157
  emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
18044
18158
  emit_asm(status, `str x22, [sp, #-16]!\n`);
18045
18159
  emit_asm(status, `ldr x20, [x19, #8]\n`);
@@ -18077,6 +18191,7 @@ function emit_owning_standalone_struct(elem, func_name, status) {
18077
18191
  }
18078
18192
  if (string_fields.length) emit_asm(status, `add sp, sp, #${Math.ceil(string_fields.length * 8 / 16) * 16}\n`);
18079
18193
  } else for (const { offset: foff } of string_fields) emit_strdup_field(status, "x21", "x20", foff, status.label_counter = (status.label_counter ?? 0) + 1);
18194
+ emit_transfer_owning_fields$1(status, "x21", transfer_fields);
18080
18195
  emit_asm(status, `ldr x22, [sp], #16\n`);
18081
18196
  emit_asm(status, `ldp x20, x21, [sp], #16\n`);
18082
18197
  return true;
@@ -18278,9 +18393,35 @@ function emit_owning_shift_T(elem, status, self_reg) {
18278
18393
  emit_asm(status, `ldr x22, [sp], #16\n`);
18279
18394
  emit_asm(status, `ldp x20, x21, [sp], #16\n`);
18280
18395
  }
18396
+ /**
18397
+ * Specialized owning-value-struct load_T (inline context: x0 = self, x1 = i,
18398
+ * x8 = sret). The caller's scope-exit destroy ALWAYS reclaims a struct
18399
+ * local's container fields, so the returned copy must own independent
18400
+ * containers — a shallow return would double-free the slot's buffer at the
18401
+ * caller's teardown. String fields stay borrowed (struct-local teardown only
18402
+ * frees RECORDED heap strings, so a strdup here would leak).
18403
+ */
18404
+ function emit_owning_load_T(elem, status, self_reg) {
18405
+ const T_SIZE = get_struct_size(elem.name, status);
18406
+ emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
18407
+ emit_asm(status, `stp x22, x23, [sp, #-16]!\n`);
18408
+ emit_asm(status, `ldr x20, [${self_reg}, #8]\n`);
18409
+ emit_asm(status, `mov x9, #${T_SIZE}\n`);
18410
+ emit_asm(status, `madd x21, x1, x9, x20\n`);
18411
+ emit_asm(status, `mov x22, x8\n`);
18412
+ emit_asm(status, `mov x0, x22\n`);
18413
+ emit_asm(status, `mov x1, x21\n`);
18414
+ emit_asm(status, `mov x2, x9\n`);
18415
+ emit_asm(status, `bl _memcpy\n`);
18416
+ emit_load_deep_copy_fields$1(elem, status, "x22", "x21");
18417
+ emit_asm(status, `mov x0, x22\n`);
18418
+ emit_asm(status, `ldp x22, x23, [sp], #16\n`);
18419
+ emit_asm(status, `ldp x20, x21, [sp], #16\n`);
18420
+ }
18281
18421
  function emit_owning_store_T(elem, status) {
18282
18422
  const T_SIZE = get_struct_size(elem.name, status);
18283
18423
  const string_fields = collect_string_fields(elem, status);
18424
+ const transfer_fields = collect_transfer_fields(elem, status);
18284
18425
  emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
18285
18426
  emit_asm(status, `str x22, [sp, #-16]!\n`);
18286
18427
  emit_asm(status, `ldr x20, [x0, #8]\n`);
@@ -18312,12 +18453,14 @@ function emit_owning_store_T(elem, status) {
18312
18453
  emit_asm(status, `${lbl}:\n`);
18313
18454
  }
18314
18455
  if (string_fields.length) emit_asm(status, `add sp, sp, #${Math.ceil(string_fields.length * 8 / 16) * 16}\n`);
18456
+ emit_transfer_owning_fields$1(status, "x21", transfer_fields);
18315
18457
  emit_asm(status, `ldr x22, [sp], #16\n`);
18316
18458
  emit_asm(status, `ldp x20, x21, [sp], #16\n`);
18317
18459
  }
18318
18460
  function emit_owning_replace_T(elem, status) {
18319
18461
  const T_SIZE = get_struct_size(elem.name, status);
18320
18462
  const string_fields = collect_string_fields(elem, status);
18463
+ const transfer_fields = collect_transfer_fields(elem, status);
18321
18464
  emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
18322
18465
  emit_asm(status, `str x22, [sp, #-16]!\n`);
18323
18466
  emit_asm(status, `ldr x20, [x0, #8]\n`);
@@ -18332,6 +18475,7 @@ function emit_owning_replace_T(elem, status) {
18332
18475
  emit_asm(status, `mov x2, x22\n`);
18333
18476
  emit_asm(status, `bl _memcpy\n`);
18334
18477
  for (const { offset: foff } of string_fields) emit_strdup_field(status, "x21", "x20", foff, status.label_counter = (status.label_counter ?? 0) + 1);
18478
+ emit_transfer_owning_fields$1(status, "x21", transfer_fields);
18335
18479
  emit_asm(status, `ldr x22, [sp], #16\n`);
18336
18480
  emit_asm(status, `ldp x20, x21, [sp], #16\n`);
18337
18481
  }
@@ -19996,7 +20140,7 @@ function build_function_node$1(node, status) {
19996
20140
  status.code = "";
19997
20141
  node.return_type = resolve_mono_type(node.return_type, status);
19998
20142
  const return_struct = !node.return_type.is_view && !node.return_type.is_array && (!!status.structs.find((s) => s.name === node.return_type.name && !s.is_simple_type && !s.is_class) || get_enum_sret_size(node.return_type.name, status) !== void 0);
19999
- const nullable_scalar_ret = is_nullable_scalar_type(node.return_type) && !node.return_type.is_ref;
20143
+ const nullable_scalar_ret = is_nullable_scalar_type(node.return_type, status) && !node.return_type.is_ref;
20000
20144
  const uses_sret = return_struct || nullable_scalar_ret;
20001
20145
  if (uses_sret) status.struct_return_buffer = "x8";
20002
20146
  status.function_return_type = node.return_type;
@@ -20083,7 +20227,7 @@ function build_function_node$1(node, status) {
20083
20227
  first_pass_slot += 2;
20084
20228
  continue;
20085
20229
  }
20086
- if (is_nullable_scalar_type(param.type)) {
20230
+ if (is_nullable_scalar_type(param.type, status)) {
20087
20231
  first_pass_slot += 2;
20088
20232
  continue;
20089
20233
  }
@@ -20250,7 +20394,7 @@ function build_function_node$1(node, status) {
20250
20394
  param_idx += 2;
20251
20395
  continue;
20252
20396
  }
20253
- if (is_nullable_scalar_type(param.type) && !param.type.is_ref && !param.type.is_array) {
20397
+ if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref && !param.type.is_array) {
20254
20398
  const size = aarch64_size(param.type.name);
20255
20399
  const offset = allocate_stack_space(status, size, size);
20256
20400
  status.stack_offsets.set(param.name, offset);
@@ -20666,7 +20810,7 @@ function init_enum_shorthand_field_default(node, field, base_reg, status) {
20666
20810
  * struct base register (`x0` for auto-init, `x19` for custom-init).
20667
20811
  */
20668
20812
  function init_nullable_field_default(node, field, base_reg, status) {
20669
- const scalar_nullable = is_nullable_scalar_type(field.type);
20813
+ const scalar_nullable = is_nullable_scalar_type(field.type, status);
20670
20814
  if (!is_nullable_struct_type(field.type, status) && !scalar_nullable) return false;
20671
20815
  if (!field.value) return false;
20672
20816
  const offset = get_field_offset(node.name, field.name, status);
@@ -21008,7 +21152,7 @@ function build_init_function(node, status) {
21008
21152
  const slot = ctor_slot;
21009
21153
  const field_is_view = !!field.type.is_view && !field.type.is_array;
21010
21154
  const field_is_fat_string = field.type.name === "string" && !field.type.is_view && !field.type.is_ref && !field.type.is_array;
21011
- const field_is_nullable_scalar = is_nullable_scalar_type(field.type);
21155
+ const field_is_nullable_scalar = is_nullable_scalar_type(field.type, status);
21012
21156
  if (field_is_view || field_is_fat_string || field_is_nullable_scalar) ctor_slot += 2;
21013
21157
  else ctor_slot += 1;
21014
21158
  let src_reg;
@@ -21186,9 +21330,9 @@ function build_init_function(node, status) {
21186
21330
  if (node.fields.find((nf) => nf.name === field.name)) continue;
21187
21331
  const offset = get_field_offset(node.name, field.name, status);
21188
21332
  const val = field.value.node_type === "value" ? field.value.value : void 0;
21189
- if (val === "null" && (is_nullable_scalar_type(field.type) || is_nullable_struct_type(field.type, status))) {
21333
+ if (val === "null" && (is_nullable_scalar_type(field.type, status) || is_nullable_struct_type(field.type, status))) {
21190
21334
  const has_off = get_field_has_offset(node.name, field.name, status);
21191
- if (is_nullable_scalar_type(field.type)) emit_asm(status, `strb wzr, [x19, #${has_off}]\n`);
21335
+ if (is_nullable_scalar_type(field.type, status)) emit_asm(status, `strb wzr, [x19, #${has_off}]\n`);
21192
21336
  else emit_asm(status, `str xzr, [x19, #${has_off}]\n`);
21193
21337
  continue;
21194
21338
  }
@@ -21329,7 +21473,7 @@ function build_custom_init_function(node, func, status) {
21329
21473
  param_idx += 2;
21330
21474
  continue;
21331
21475
  }
21332
- if (is_nullable_scalar_type(param.type) && !param.type.is_ref && !param.type.is_array) {
21476
+ if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref && !param.type.is_array) {
21333
21477
  const size = aarch64_size(param.type.name);
21334
21478
  const offset = allocate_stack_space(status, size, size);
21335
21479
  status.stack_offsets.set(param.name, offset);
@@ -21620,7 +21764,7 @@ function build_struct_functions$1(node, status) {
21620
21764
  status.function_view_params = /* @__PURE__ */ new Set();
21621
21765
  status.struct_return_buffer = void 0;
21622
21766
  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) || get_enum_sret_size(func.return_type?.name, status) !== void 0);
21623
- const nullable_scalar_ret = !!func.return_type && is_nullable_scalar_type(func.return_type) && !func.return_type.is_ref;
21767
+ const nullable_scalar_ret = !!func.return_type && is_nullable_scalar_type(func.return_type, status) && !func.return_type.is_ref;
21624
21768
  const uses_sret = return_struct || nullable_scalar_ret;
21625
21769
  let return_buffer_stack_offset;
21626
21770
  status.current_function_name = func.name;
@@ -21647,7 +21791,7 @@ function build_struct_functions$1(node, status) {
21647
21791
  slot_idx += 2;
21648
21792
  continue;
21649
21793
  }
21650
- if (is_nullable_scalar_type(param.type) && !param.type.is_ref) {
21794
+ if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref) {
21651
21795
  if (param.declaration === "var") status.function_param_vars.add(param.name);
21652
21796
  slot_idx += 2;
21653
21797
  continue;
@@ -21730,7 +21874,7 @@ function build_struct_functions$1(node, status) {
21730
21874
  second_slot_idx += 2;
21731
21875
  continue;
21732
21876
  }
21733
- if (is_nullable_scalar_type(param.type) && !param.type.is_ref) {
21877
+ if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref) {
21734
21878
  const size = aarch64_size(param.type.name);
21735
21879
  const offset = allocate_stack_space(status, size, size);
21736
21880
  status.stack_offsets.set(param.name, offset);
@@ -23189,11 +23333,11 @@ function build_constructor_params(fc, param_regs, status) {
23189
23333
  let nullable_scalar = false;
23190
23334
  if (fc.nullable_param_indices?.includes(i)) {
23191
23335
  const cp = ctor_callee_params?.[i];
23192
- if (cp && is_nullable_scalar_type(cp.type)) nullable_scalar = true;
23193
- else if (is_nullable_scalar_type(p.type)) nullable_scalar = true;
23336
+ if (cp && is_nullable_scalar_type(cp.type, status)) nullable_scalar = true;
23337
+ else if (is_nullable_scalar_type(p.type, status)) nullable_scalar = true;
23194
23338
  else if (!cp) {
23195
23339
  const field = (status.structs.find((s) => s.name === fc.name && !s.is_simple_type)?.fields.filter((f) => f.value == null))?.[i];
23196
- if (field && is_nullable_scalar_type(field.type)) nullable_scalar = true;
23340
+ if (field && is_nullable_scalar_type(field.type, status)) nullable_scalar = true;
23197
23341
  }
23198
23342
  }
23199
23343
  const is_pair = pt === "string" || lit || is_view || nullable_scalar;
@@ -23220,7 +23364,7 @@ function build_constructor_params(fc, param_regs, status) {
23220
23364
  if (arg.node_type === "value" && arg.value === "null") {
23221
23365
  emit_asm(status, `mov x0, #0\n`);
23222
23366
  emit_asm(status, `mov x1, #0\n`);
23223
- } else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
23367
+ } else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
23224
23368
  const vname = arg.value;
23225
23369
  emit_var_load(status, "x0", vname, aarch64_size(arg.type.name));
23226
23370
  emit_var_load(status, "x1", has_flag_name(vname), 8);
@@ -23610,7 +23754,7 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
23610
23754
  emit_var_store(status, "x0", node.name, 8);
23611
23755
  return;
23612
23756
  }
23613
- if (is_nullable_scalar_type(node.type)) {
23757
+ if (is_nullable_scalar_type(node.type, status)) {
23614
23758
  if (status.function_return_label) {
23615
23759
  const offset = allocate_stack_space(status, 16, 16);
23616
23760
  status.stack_offsets.set(node.name, offset);
@@ -23621,14 +23765,23 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
23621
23765
  }
23622
23766
  const init_value = node.value;
23623
23767
  const init_type = call_result_type(init_value) ?? type_from_value_node$1(init_value);
23624
- const value_is_nullable_call = init_value.node_type === "func_call" && is_nullable_scalar_type(init_type);
23625
- if (init_value.node_type === "access" && is_nullable_scalar_type(init_type)) {
23768
+ const value_is_nullable_call = init_value.node_type === "func_call" && is_nullable_scalar_type(init_type, status);
23769
+ const value_is_nullable_method = init_value.node_type === "access" && init_value.access.node_type === "access_func" && is_nullable_scalar_type(init_type, status);
23770
+ const value_is_nullable_field = init_value.node_type === "access" && init_value.access.node_type === "access_field" && is_nullable_scalar_type(init_type, status);
23771
+ if (value_is_nullable_method) {
23626
23772
  emit_init_value$1(init_value, nir_init, status);
23627
23773
  ensure_newline(status);
23628
23774
  emit_asm(status, `ldr x9, [x0]\n`);
23629
23775
  emit_asm(status, `str x9, [x29, #${offset}]\n`);
23630
23776
  emit_asm(status, `ldr x9, [x0, #8]\n`);
23631
23777
  emit_asm(status, `str x9, [x29, #${offset + 8}]\n`);
23778
+ } else if (value_is_nullable_field) {
23779
+ emit_init_value$1(init_value, nir_init, status);
23780
+ ensure_newline(status);
23781
+ emit_var_store(status, "x0", node.name, 8);
23782
+ load_nullable_has(init_value, "x9", status);
23783
+ ensure_newline(status);
23784
+ emit_asm(status, `str x9, [x29, #${offset + 8}]\n`);
23632
23785
  } else if (value_is_nullable_call) {
23633
23786
  const old_buffer = status.struct_return_buffer;
23634
23787
  const old_buffer_var = status.struct_return_buffer_var;
@@ -23650,8 +23803,13 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
23650
23803
  }
23651
23804
  return;
23652
23805
  }
23653
- emit_data(status, `${node.name}: .space 16\n`);
23654
- status.stack_offsets.set(has_flag_name(node.name), -1);
23806
+ const global_is_null = !node.value || node.value.node_type === "value" && node.value.value === "null";
23807
+ if (global_is_null) emit_data(status, `${node.name}: .space 8\n`);
23808
+ else {
23809
+ emit_data(status, `${node.name}: ${aarch64_type(node.type.name)} ${get_raw_value$1(node.value, status)}\n`);
23810
+ if (aarch64_size(node.type.name) < 8) emit_data(status, `.p2align 3\n`);
23811
+ }
23812
+ emit_data(status, `${has_flag_name(node.name)}: .quad ${global_is_null ? 0 : 1}\n`);
23655
23813
  return;
23656
23814
  }
23657
23815
  const directive = aarch64_type(node.type.name);
@@ -26115,7 +26273,7 @@ function build_return_node$1(node, status, nir_value) {
26115
26273
  hoist_field_overrides(node.value, build_node, status);
26116
26274
  if (node.from_inline) return;
26117
26275
  const returns_nullable_struct = is_nullable_struct_type(status.function_return_type, status);
26118
- const returns_nullable_scalar = is_nullable_scalar_type(status.function_return_type);
26276
+ const returns_nullable_scalar = is_nullable_scalar_type(status.function_return_type, status);
26119
26277
  if ((returns_nullable_struct || returns_nullable_scalar) && (!node.value || node.value.node_type === "value" && node.value.value === "null")) {
26120
26278
  if (status.return_buffer_stack_offset !== void 0) {
26121
26279
  const struct_size = get_struct_size(status.function_return_type.name, status);
@@ -26305,7 +26463,7 @@ function build_return_node$1(node, status, nir_value) {
26305
26463
  if (status.return_buffer_stack_offset !== void 0) emit_asm(status, `ldr x8, [x29, #${status.return_buffer_stack_offset}]\n`);
26306
26464
  emit_typed_store(status, "x0", "x8", 0, aarch64_size(status.function_return_type.name));
26307
26465
  const value_type = node.value ? type_from_value_node$1(node.value) : void 0;
26308
- if (node.value && value_type?.is_nullable && is_nullable_scalar_type(value_type)) {
26466
+ if (node.value && value_type?.is_nullable && is_nullable_scalar_type(value_type, status)) {
26309
26467
  load_nullable_has(node.value, "x9", status);
26310
26468
  ensure_newline(status);
26311
26469
  emit_asm(status, `str x9, [x8, #8]\n`);
@@ -29935,7 +30093,7 @@ function build_function_call_node$1(node, status) {
29935
30093
  if (!node.nullable_param_indices?.includes(i)) continue;
29936
30094
  const arg_type = node.params[i].type;
29937
30095
  const callee_param = node.resolved_function?.params?.[i];
29938
- if (is_nullable_scalar_type(arg_type) || callee_param && is_nullable_scalar_type(callee_param.type)) nullable_scalar_arg_set.add(i);
30096
+ if (is_nullable_scalar_type(arg_type, status) || callee_param && is_nullable_scalar_type(callee_param.type, status)) nullable_scalar_arg_set.add(i);
29939
30097
  }
29940
30098
  const arg_slot = [];
29941
30099
  let total_slots = 0;
@@ -30033,7 +30191,7 @@ function build_function_call_node$1(node, status) {
30033
30191
  if (arg.node_type === "value" && arg.value === "null") {
30034
30192
  emit_asm(status, `mov x0, #0\n`);
30035
30193
  emit_asm(status, `mov x1, #0\n`);
30036
- } else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
30194
+ } else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
30037
30195
  const vname = arg.value;
30038
30196
  emit_var_load(status, "x0", vname, aarch64_size(param_type.name));
30039
30197
  emit_var_load(status, "x1", has_flag_name(vname), 8);
@@ -30147,7 +30305,7 @@ function build_function_call_node$1(node, status) {
30147
30305
  } else if (!is_struct && node.type?.name && !status.call_x8_preset) {
30148
30306
  const return_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
30149
30307
  const return_enum_size = get_enum_sret_size(node.type?.name, status);
30150
- const nullable_scalar_ret = is_nullable_scalar_type(node.type);
30308
+ const nullable_scalar_ret = is_nullable_scalar_type(node.type, status);
30151
30309
  if (return_struct || return_enum_size !== void 0 || nullable_scalar_ret) {
30152
30310
  const nullable_ret = is_nullable_struct_type(node.type, status);
30153
30311
  const struct_size = return_struct ? get_struct_size(node.type.name, status) : nullable_scalar_ret ? 8 : return_enum_size;
@@ -30203,7 +30361,7 @@ function build_function_call_node$1(node, status) {
30203
30361
  if (!is_struct && node.type?.name && !status.call_x8_preset) {
30204
30362
  const return_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
30205
30363
  const return_enum_size = get_enum_sret_size(node.type?.name, status);
30206
- const nullable_scalar_ret = is_nullable_scalar_type(node.type);
30364
+ const nullable_scalar_ret = is_nullable_scalar_type(node.type, status);
30207
30365
  if (return_struct || return_enum_size !== void 0 || nullable_scalar_ret) {
30208
30366
  const temp_name = `_call_ret_${temp_counter - 1}`;
30209
30367
  emit_asm(status, `add x0, x29, #${status.stack_offsets.get(temp_name)}\n`);
@@ -31078,7 +31236,7 @@ function build_struct_body(node, status) {
31078
31236
  status.code += `];\n`;
31079
31237
  } else {
31080
31238
  status.code += `${field_c_type(field.type, status)} ${field.name};\n`;
31081
- if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) status.code += `unsigned char ${has_flag_name(field.name)};\n`;
31239
+ if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) status.code += `unsigned char ${has_flag_name(field.name)};\n`;
31082
31240
  }
31083
31241
  for (let traitName of node.traits) {
31084
31242
  const trait = status.traits.find((n) => n.name === traitName);
@@ -31210,8 +31368,8 @@ function build_operation_node$1(node, status) {
31210
31368
  build_node$1(node.right_value, status);
31211
31369
  status.code += `); })`;
31212
31370
  ns_tmp_counter++;
31213
- } else if (is_nullable_scalar_type(left_type)) {
31214
- if (node.left_value.node_type === "func_call" && is_nullable_scalar_type(node.left_value.type)) {
31371
+ } else if (is_nullable_scalar_type(left_type, status)) {
31372
+ if (node.left_value.node_type === "func_call" && is_nullable_scalar_type(node.left_value.type, status)) {
31215
31373
  const id = ns_tmp_counter++;
31216
31374
  const flag = `_nsh_${id}`;
31217
31375
  const val = `_nsv_${id}`;
@@ -31273,8 +31431,8 @@ function build_operation_node$1(node, status) {
31273
31431
  else status.code += `((`;
31274
31432
  build_node$1(nullable_side, status);
31275
31433
  status.code += `.ptr != 0))`;
31276
- } else if (is_nullable_scalar_type(type_from_value_node$1(nullable_side)) && type?.is_nullable && type.name !== "string") {
31277
- if (nullable_side.node_type === "func_call" && is_nullable_scalar_type(nullable_side.type)) {
31434
+ } else if (is_nullable_scalar_type(type_from_value_node$1(nullable_side), status) && type?.is_nullable && type.name !== "string") {
31435
+ if (nullable_side.node_type === "func_call" && is_nullable_scalar_type(nullable_side.type, status)) {
31278
31436
  const id = ns_tmp_counter++;
31279
31437
  const flag = `_nsh_${id}`;
31280
31438
  const val = `_nsv_${id}`;
@@ -31438,7 +31596,7 @@ function is_null_comparison(node) {
31438
31596
  }
31439
31597
  function is_nullable_struct_side(node, status) {
31440
31598
  const t = type_from_value_node$1(node);
31441
- return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
31599
+ return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
31442
31600
  }
31443
31601
  /**
31444
31602
  * Build the companion `_has` flag expression for a nullable-struct or
@@ -31456,8 +31614,8 @@ function is_nullable_struct_side(node, status) {
31456
31614
  function build_nullable_has(node, status) {
31457
31615
  if (node.node_type === "value") {
31458
31616
  const name = node.value;
31459
- if (status.function_ref_params?.has(name) && (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type))) return has_flag_name(name);
31460
- if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) return has_flag_name(name);
31617
+ if (status.function_ref_params?.has(name) && (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status))) return has_flag_name(name);
31618
+ if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) return has_flag_name(name);
31461
31619
  }
31462
31620
  const saved = begin_code_scratch(status);
31463
31621
  build_node$1(node, status);
@@ -33122,7 +33280,7 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
33122
33280
  if (rhs_is_null) status.code += `${flag} = 0`;
33123
33281
  else {
33124
33282
  const call_ret = call_result_type(node.right_value);
33125
- if (!!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret))) {
33283
+ if (!!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret, status))) {
33126
33284
  const old = status.current_nullable_call_flag;
33127
33285
  status.current_nullable_call_flag = flag;
33128
33286
  status.code += `${lhs_expr} = `;
@@ -33592,11 +33750,11 @@ function lhs_nullable_struct_type(node, status) {
33592
33750
  if (node.left_value.node_type === "value") {
33593
33751
  const name = node.left_value.value;
33594
33752
  const t = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
33595
- return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
33753
+ return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
33596
33754
  }
33597
33755
  if (node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field") {
33598
33756
  const field_type = node.left_value.access.type;
33599
- return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type);
33757
+ return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type, status);
33600
33758
  }
33601
33759
  return false;
33602
33760
  }
@@ -33609,7 +33767,7 @@ function lhs_nullable_struct_type(node, status) {
33609
33767
  function nullable_value_flag_expr(value, status) {
33610
33768
  const t = type_from_value_node$1(value);
33611
33769
  if (!t?.is_nullable) return void 0;
33612
- if (!is_nullable_struct_type(t, status) && !is_nullable_scalar_type(t)) return void 0;
33770
+ if (!is_nullable_struct_type(t, status) && !is_nullable_scalar_type(t, status)) return void 0;
33613
33771
  if (value.node_type === "value") return has_flag_name(value.value);
33614
33772
  if (value.node_type === "access" && value.access.node_type === "access_field") {
33615
33773
  const saved = begin_code_scratch(status);
@@ -34340,32 +34498,37 @@ function build_declaration_node(node, status, nir_init, nir_swap) {
34340
34498
  status.stack_array_lengths.set(safe_name, len_text);
34341
34499
  }
34342
34500
  }
34343
- if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) {
34501
+ if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) {
34344
34502
  const flag = has_flag_name(safe_name);
34345
34503
  const is_null = !node.value || node.value.node_type === "value" && node.value.value === "null";
34346
- if (!status.current_function && is_nullable_scalar_type(node.type)) {
34347
- status.code += `;\nunsigned char ${flag} = ${is_null ? 0 : 1}`;
34348
- if (is_null) return;
34349
- } else {
34350
- status.code += `;\nunsigned char ${flag} = 0`;
34351
- if (node.value && !is_null) {
34352
- const call_ret = call_result_type(node.value);
34353
- const value_is_nullable_call = !!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret));
34354
- status.code += `;\n${safe_name} = `;
34355
- if (value_is_nullable_call) {
34356
- const old = status.current_nullable_call_flag;
34357
- status.current_nullable_call_flag = flag;
34358
- emit_init_value(node.value, nir_init, status);
34359
- status.current_nullable_call_flag = old;
34360
- record_call_init_string_fields(node, status);
34361
- } else {
34362
- const rhs_flag = nullable_value_flag_expr(node.value, status);
34363
- emit_init_value(node.value, nir_init, status);
34364
- status.code += `;\n${flag} = ${rhs_flag ?? "1"}`;
34365
- }
34504
+ if (!status.current_function && is_nullable_scalar_type(node.type, status)) {
34505
+ if (is_null) {
34506
+ status.code += `;\nunsigned char ${flag} = 0`;
34507
+ return;
34366
34508
  }
34509
+ status.code += ` = `;
34510
+ emit_init_value(node.value, nir_init, status);
34511
+ status.code += `;\nunsigned char ${flag} = 1`;
34367
34512
  return;
34368
34513
  }
34514
+ status.code += `;\nunsigned char ${flag} = 0`;
34515
+ if (node.value && !is_null) {
34516
+ const call_ret = call_result_type(node.value);
34517
+ const value_is_nullable_call = !!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret, status));
34518
+ status.code += `;\n${safe_name} = `;
34519
+ if (value_is_nullable_call) {
34520
+ const old = status.current_nullable_call_flag;
34521
+ status.current_nullable_call_flag = flag;
34522
+ emit_init_value(node.value, nir_init, status);
34523
+ status.current_nullable_call_flag = old;
34524
+ record_call_init_string_fields(node, status);
34525
+ } else {
34526
+ const rhs_flag = nullable_value_flag_expr(node.value, status);
34527
+ emit_init_value(node.value, nir_init, status);
34528
+ status.code += `;\n${flag} = ${rhs_flag ?? "1"}`;
34529
+ }
34530
+ }
34531
+ return;
34369
34532
  }
34370
34533
  if (node.value) {
34371
34534
  const is_heap_literal_init = node.is_heap_array_literal || node.type.storage_kind === "heap_array" && (node.value.node_type === "array" || node.value.node_type === "range");
@@ -34982,7 +35145,7 @@ function build_return_node(node, status, nir_value) {
34982
35145
  emit_nursery_joins_on_return_c(status);
34983
35146
  reclaim_all_c_scopes(status);
34984
35147
  status.code += `*${ret_has} = 0;\n`;
34985
- if (status.function_return_type && is_nullable_scalar_type(status.function_return_type)) status.code += `return (${c_type(status.function_return_type.name)})0;\n`;
35148
+ if (status.function_return_type && is_nullable_scalar_type(status.function_return_type, status)) status.code += `return (${c_type(status.function_return_type.name)})0;\n`;
34986
35149
  else status.code += `return (struct ${status.function_return_type.name}){0};\n`;
34987
35150
  return;
34988
35151
  }
@@ -35215,7 +35378,7 @@ function nullable_flag_expr_of_value(value, status) {
35215
35378
  if (!value) return void 0;
35216
35379
  const t = type_from_value_node$1(value);
35217
35380
  if (!t?.is_nullable) return void 0;
35218
- if (!(is_nullable_struct_type(t, status) || is_nullable_scalar_type(t))) return void 0;
35381
+ if (!(is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status))) return void 0;
35219
35382
  if (value.node_type === "value") return has_flag_name(value.value);
35220
35383
  if (value.node_type === "access") {
35221
35384
  const saved = begin_code_scratch(status);
@@ -35657,20 +35820,78 @@ function has_owning_fields(node, status) {
35657
35820
  }
35658
35821
  /**
35659
35822
  * The names of Buffer methods that get specialized for owning value structs.
35660
- * load_T and move_T are NOT specialized: load_T returns a shallow copy (a
35661
- * borrow — the caller's struct local is not destroyed, so no double-free);
35662
- * move_T transfers ownership (the slot is zeroed, the caller takes the
35663
- * pointers).
35823
+ * load_T IS specialized when the element has owning container fields (a
35824
+ * `List<T>` member): the returned copy must own independent containers, or
35825
+ * the caller's scope-exit destroy would free the slot's buffer (double-free
35826
+ * at container teardown). String fields stay BORROWED on load (struct-local
35827
+ * teardown only frees RECORDED heap strings, so strdup'ing them would leak);
35828
+ * container fields have no such borrow convention — a struct local's
35829
+ * container fields are ALWAYS destroyed — so they must be deep-copied.
35830
+ * move_T is NOT specialized: it transfers ownership (the slot is zeroed, the
35831
+ * caller takes the pointers).
35664
35832
  */
35665
35833
  const OWNING_BUFFER_METHODS = /* @__PURE__ */ new Set([
35666
35834
  "store",
35667
35835
  "replace",
35836
+ "load",
35668
35837
  "modify",
35669
35838
  "shift",
35670
35839
  "destroy",
35671
35840
  "#destroy"
35672
35841
  ]);
35673
35842
  /**
35843
+ * Emit the transfer half of owning store_T/replace_T: the slot bytes were
35844
+ * already memcpy'd from the source, so zeroing each owned CONTAINER field
35845
+ * of the SOURCE completes the move — the slot is the sole owner, and the
35846
+ * source's own teardown (when it still runs: an unstamped generic-body
35847
+ * local) finds zeroed fields and reclaims nothing. Nested value struct
35848
+ * fields recurse so their container leaves are zeroed at their own paths;
35849
+ * their string fields are untouched (the slot aliases them — the pre-existing
35850
+ * nested-string convention). Class/enum payload fields are left alone
35851
+ * (recorded in FOLLOWUP.md).
35852
+ */
35853
+ function emit_transfer_owning_fields(elem, src, status, prefix = "") {
35854
+ for (const field of elem.fields) {
35855
+ if (field.type.is_ref || field.type.is_view) continue;
35856
+ if (field.type.is_array || field.type.is_nullable) continue;
35857
+ const field_struct = resolve_struct_type(field.type, status);
35858
+ if (!field_struct || field_struct.is_class) continue;
35859
+ const src_field = `${prefix}${src}${arrow(src)}${field.name}`;
35860
+ if (has_destroy(field_struct)) status.code += `memset((void*)&${src_field}, 0, sizeof(${src_field}));\n`;
35861
+ else if (struct_needs_destroy(field_struct, status)) emit_transfer_owning_fields(field_struct, src_field, status, "");
35862
+ }
35863
+ }
35864
+ /**
35865
+ * Emit the deep-copy half of owning load_T: for each field whose type has a
35866
+ * monomorphized `copy` helper that yields an independent value (a `List<T>`
35867
+ * field — `<List_T>_copy` deep-copies slots per the owning store contract —
35868
+ * or an enum-with-data field whose string payloads `<E>_copy` duplicates),
35869
+ * overwrite the shallow-copied bytes with an owned copy. Fields without a
35870
+ * copyable shape (nested owning user structs, class fields) stay shallow —
35871
+ * recorded in FOLLOWUP.md ("remaining shallow container-field shapes").
35872
+ */
35873
+ function emit_load_deep_copy_fields(elem, dst, status) {
35874
+ for (const field of elem.fields) {
35875
+ if (field.type.is_ref || field.type.is_view || field.type.is_array) continue;
35876
+ if (field.type.is_nullable) continue;
35877
+ if (field.type.name === "string") continue;
35878
+ const field_struct = resolve_struct_type(field.type, status);
35879
+ if (field_struct && !field_struct.is_class && field_struct.name.startsWith("List_") && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
35880
+ status.code += `${dst}.${field.name} = ${field_struct.name}_copy(&${dst}.${field.name});\n`;
35881
+ continue;
35882
+ }
35883
+ const field_enum = status.enums.find((e) => e.name === field.type.name && e.has_associated_data);
35884
+ if (field_enum && enum_copy_is_deep(field_enum, status)) status.code += `${dst}.${field.name} = ${field_enum.name}_copy(${dst}.${field.name});\n`;
35885
+ }
35886
+ }
35887
+ /** Whether `<E>_copy` yields a fully independent value: no case payload is a
35888
+ * class/trait reference (those are shared pointers the copy cannot
35889
+ * duplicate — mirroring owning_buffer_enum_element's gate). */
35890
+ function enum_copy_is_deep(elem, status) {
35891
+ for (const c of elem.cases) for (const p of c.params) if (status.structs.find((s) => s.name === p.type.name)?.is_class || status.traits.find((t) => t.name === p.type.name)) return false;
35892
+ return true;
35893
+ }
35894
+ /**
35674
35895
  * A `Buffer<string>` owns an independent heap copy of each slot (strdup on
35675
35896
  * store_T, free+strdup on replace_T, per-slot free on #destroy). The element
35676
35897
  * is a primitive, so this is a separate path from the value-struct
@@ -35833,6 +36054,13 @@ function emit_owning_buffer_body(func_name, elem, status) {
35833
36054
  status.code += `struct ${elem.name} _old = _slots[i];\n`;
35834
36055
  status.code += `_slots[i] = (*val);\n`;
35835
36056
  emit_deep_copy_fields(elem, "_slots[i]", "val", status, "_old");
36057
+ emit_transfer_owning_fields(elem, "val", status);
36058
+ return true;
36059
+ }
36060
+ if (func_name === "load") {
36061
+ status.code += `struct ${elem.name} _loaded = ((struct ${elem.name} *)(unsigned long long)self->data)[i];\n`;
36062
+ emit_load_deep_copy_fields(elem, "_loaded", status);
36063
+ status.code += `return _loaded;\n`;
35836
36064
  return true;
35837
36065
  }
35838
36066
  if (func_name === "modify") {
@@ -35847,6 +36075,7 @@ function emit_owning_buffer_body(func_name, elem, status) {
35847
36075
  status.code += `${elem.name}_destroy(&_slots[i]);\n`;
35848
36076
  status.code += `_slots[i] = (*val);\n`;
35849
36077
  emit_deep_copy_fields(elem, "_slots[i]", "val", status);
36078
+ emit_transfer_owning_fields(elem, "val", status);
35850
36079
  return true;
35851
36080
  }
35852
36081
  if (func_name === "shift") {
@@ -35873,12 +36102,13 @@ function emit_owning_buffer_body(func_name, elem, status) {
35873
36102
  return false;
35874
36103
  }
35875
36104
  /**
35876
- * For each string field on the element struct, overwrite the shallow-copied
35877
- * pointer in `dst` with a fresh strdup of `src`'s field. This breaks the
35878
- * pointer aliasing between the source and the slot so each can be destroyed
35879
- * independently. For nested owning value struct fields, recursively call the
35880
- * nested struct's destroy + deep-copy (the nested destroy frees the
35881
- * shallow-copied owned sub-fields, then the deep-copy re-strdup's them).
36105
+ * For each DIRECT string field on the element struct, overwrite the
36106
+ * shallow-copied pointer in `dst` with a fresh strdup of `src`'s field. This
36107
+ * breaks the pointer aliasing between the source and the slot so each can be
36108
+ * destroyed independently. Nested value struct fields are NOT walked: their
36109
+ * string fields stay aliased (the pre-existing convention — a spliced
36110
+ * `move` donor has no teardown, so the slot is the sole owner), and their
36111
+ * container leaves are transferred by `emit_transfer_owning_fields`.
35882
36112
  *
35883
36113
  * `old_expr` (optional) is the slot's PRE-copy value expression. When the
35884
36114
  * source field pointer equals the old slot field pointer, the slot already
@@ -35896,12 +36126,6 @@ function emit_deep_copy_fields(elem, dst, src, status, old_expr) {
35896
36126
  const src_field = `${src}${arrow(src)}${field.name}`;
35897
36127
  if (old_expr) status.code += `${dst}.${field.name} = (${src_field}.ptr && ${src_field}.ptr != ${old_expr}.${field.name}.ptr) ? nomen_str_dup(${src_field}) : ${src_field};\n`;
35898
36128
  else status.code += `${dst}.${field.name} = ${src_field}.ptr ? nomen_str_dup(${src_field}) : ${src_field};\n`;
35899
- } else if (field.type.name && !field.type.is_array) {
35900
- const field_struct = status.structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_generic);
35901
- if (field_struct && !field_struct.is_class && struct_needs_destroy(field_struct, status)) {
35902
- status.code += `${field_struct.name}_destroy(&${dst}.${field.name});\n`;
35903
- emit_deep_copy_fields(field_struct, `${dst}.${field.name}`, `${src}${arrow(src)}${field.name}`, status);
35904
- }
35905
36129
  }
35906
36130
  }
35907
36131
  }
@@ -36049,7 +36273,7 @@ function build_struct_node(node, status) {
36049
36273
  const ctor_return = is_class ? `struct ${node.name}*` : `struct ${node.name}`;
36050
36274
  const field_params = node.fields.filter((f) => f.value == null).map((f) => {
36051
36275
  let decl = c_param_decl(f.type, f.name, status);
36052
- if (is_nullable_struct_type(f.type, status) || is_nullable_scalar_type(f.type)) decl += `, unsigned char ${has_flag_name(f.name)}`;
36276
+ if (is_nullable_struct_type(f.type, status) || is_nullable_scalar_type(f.type, status)) decl += `, unsigned char ${has_flag_name(f.name)}`;
36053
36277
  return decl;
36054
36278
  }).join(", ");
36055
36279
  const ctor_sig = (init) => {
@@ -36060,7 +36284,7 @@ function build_struct_node(node, status) {
36060
36284
  is_ref: p.is_ref || p.type.is_ref,
36061
36285
  declaration: p.declaration
36062
36286
  });
36063
- if (!p.is_variadic && (is_nullable_struct_type(p.type, status) || is_nullable_scalar_type(p.type))) decl += `, unsigned char ${has_flag_name(p.name)}`;
36287
+ if (!p.is_variadic && (is_nullable_struct_type(p.type, status) || is_nullable_scalar_type(p.type, status))) decl += `, unsigned char ${has_flag_name(p.name)}`;
36064
36288
  return decl;
36065
36289
  }).join(", ");
36066
36290
  const label = is_overloaded(node, "#init") ? mangled_label(init, node.name) : `${node.name}_init`;
@@ -36088,7 +36312,7 @@ function build_struct_node(node, status) {
36088
36312
  build_node$1(field.value, status);
36089
36313
  status.code += `; self${accessor}${field.name} = (nomen_view){ _p.ptr, _p.len }; }\n`;
36090
36314
  } else status.code += `self${accessor}${field.name} = (nomen_view){0};\n`;
36091
- } else if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) {
36315
+ } else if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) {
36092
36316
  if (field.value.node_type === "value" && field.value.value === "null") status.code += `self${accessor}${has_flag_name(field.name)} = 0;\n`;
36093
36317
  else {
36094
36318
  status.code += `self${accessor}${field.name} = `;
@@ -36166,7 +36390,7 @@ function build_struct_node(node, status) {
36166
36390
  status.code += `; ${object_name}${accessor}${field.name} = (nomen_view){ _p.ptr, _p.len }; }\n`;
36167
36391
  } else status.code += `${object_name}${accessor}${field.name} = (nomen_view){0};\n`;
36168
36392
  } else if (field.type.storage_kind === "stack_array" && field.type.length) status.code += `memcpy(${object_name}${accessor}${field.name}, ${field.name}, sizeof(${object_name}${accessor}${field.name}));\n`;
36169
- else if ((is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) && field.value) {
36393
+ else if ((is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) && field.value) {
36170
36394
  if (field.value.node_type === "value" && field.value.value === "null") status.code += `${object_name}${accessor}${has_flag_name(field.name)} = 0;\n`;
36171
36395
  else {
36172
36396
  status.code += `${object_name}${accessor}${field.name} = `;
@@ -36176,7 +36400,7 @@ function build_struct_node(node, status) {
36176
36400
  } else if (is_nullable_struct_type(field.type, status)) {
36177
36401
  status.code += `${object_name}${accessor}${field.name} = *${field.name};\n`;
36178
36402
  status.code += `${object_name}${accessor}${has_flag_name(field.name)} = ${has_flag_name(field.name)};\n`;
36179
- } else if (is_nullable_scalar_type(field.type)) {
36403
+ } else if (is_nullable_scalar_type(field.type, status)) {
36180
36404
  status.code += `${object_name}${accessor}${field.name} = ${field.name};\n`;
36181
36405
  status.code += `${object_name}${accessor}${has_flag_name(field.name)} = ${has_flag_name(field.name)};\n`;
36182
36406
  } else if (!field.type.is_ref && !field.type.is_view && status.enums.find((e) => e.name === field.type.name && e.has_associated_data)) {
@@ -36314,7 +36538,7 @@ function build_struct_functions(node, status, skip_init = false) {
36314
36538
  status.force_heap_strings = scan_force_heap_strings(func.statements ?? [], status.structs);
36315
36539
  status.function_return_type = func.return_type;
36316
36540
  const old_nullable_ret_has = status.nullable_ret_has_param;
36317
- if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type)) status.nullable_ret_has_param = "_ret_has";
36541
+ if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type, status)) status.nullable_ret_has_param = "_ret_has";
36318
36542
  else status.nullable_ret_has_param = void 0;
36319
36543
  const self_param = func.params[0]?.is_self_param ? func.params[0] : null;
36320
36544
  status.self_is_ref = !!self_param?.is_ref || self_param?.declaration === "var";
@@ -36371,9 +36595,9 @@ function build_struct_functions(node, status, skip_init = false) {
36371
36595
  for (let i = 0; i < func.params.length; i++) {
36372
36596
  if (i > 0) status.code += ", ";
36373
36597
  build_parameter_node(func.params[i], status);
36374
- if (!func.params[i].is_self_param && (is_nullable_struct_type(func.params[i].type, status) || is_nullable_scalar_type(func.params[i].type))) status.code += `, unsigned char ${has_flag_name(c_function_name(func.params[i].name))}`;
36598
+ if (!func.params[i].is_self_param && (is_nullable_struct_type(func.params[i].type, status) || is_nullable_scalar_type(func.params[i].type, status))) status.code += `, unsigned char ${has_flag_name(c_function_name(func.params[i].name))}`;
36375
36599
  }
36376
- if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type)) {
36600
+ if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type, status)) {
36377
36601
  if (func.params.length > 0) status.code += `, `;
36378
36602
  status.code += `unsigned char *_ret_has`;
36379
36603
  }
@@ -37134,9 +37358,9 @@ function build_function_node(node, status) {
37134
37358
  first_param = false;
37135
37359
  if (node.params[i].is_variadic) status.code += `long _${node.params[i].name}_len, `;
37136
37360
  build_parameter_node(node.params[i], status);
37137
- if ((is_nullable_struct_type(node.params[i].type, status) || is_nullable_scalar_type(node.params[i].type)) && !node.params[i].is_self_param) status.code += `, unsigned char ${has_flag_name(c_function_name(node.params[i].name))}`;
37361
+ if ((is_nullable_struct_type(node.params[i].type, status) || is_nullable_scalar_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))}`;
37138
37362
  }
37139
- if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type)) {
37363
+ if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type, status)) {
37140
37364
  if (!first_param) status.code += ", ";
37141
37365
  status.code += `unsigned char *_ret_has`;
37142
37366
  }
@@ -37189,7 +37413,7 @@ function build_function_node(node, status) {
37189
37413
  const old_variable_types = status.variable_types;
37190
37414
  status.variable_types = /* @__PURE__ */ new Map();
37191
37415
  const old_nullable_ret_has = status.nullable_ret_has_param;
37192
- if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type)) status.nullable_ret_has_param = "_ret_has";
37416
+ if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type, status)) status.nullable_ret_has_param = "_ret_has";
37193
37417
  else status.nullable_ret_has_param = void 0;
37194
37418
  const old_function_name = status.current_function_name;
37195
37419
  status.current_function_name = emission_label(node);
@@ -37576,7 +37800,7 @@ function build_function_call_node(node, status) {
37576
37800
  const lambda_ret_c = lambda_arg_temps.size > 0 ? c_return_type(node.type, status) : void 0;
37577
37801
  const lambda_ret_tmp = lambda_ret_c && lambda_ret_c !== "void" ? next_lambda_ret_temp() : void 0;
37578
37802
  if (lambda_ret_tmp) status.code += `${lambda_ret_c} ${lambda_ret_tmp} = `;
37579
- const saved_call = (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) && !status.current_nullable_call_flag ? begin_code_scratch(status) : void 0;
37803
+ const saved_call = (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) && !status.current_nullable_call_flag ? begin_code_scratch(status) : void 0;
37580
37804
  let wrap_tmp;
37581
37805
  status.code += `${func_name}(${is_closure_callee ? "NULL" : ""}`;
37582
37806
  if (is_closure_callee && node.params.length > 0) status.code += ", ";
@@ -37617,7 +37841,7 @@ function build_function_call_node(node, status) {
37617
37841
  }
37618
37842
  if (node.nullable_param_indices?.includes(i) && node.params[i].node_type === "value" && node.params[i].value === "null") {
37619
37843
  const callee_null_param_type = callee_params?.[i]?.type ?? param_type;
37620
- if (is_nullable_scalar_type(callee_null_param_type)) status.code += `0, 0`;
37844
+ if (is_nullable_scalar_type(callee_null_param_type, status)) status.code += `0, 0`;
37621
37845
  else status.code += `(void *)&(struct ${mono_type_name(callee_null_param_type)}){0}, 0`;
37622
37846
  continue;
37623
37847
  }
@@ -37703,7 +37927,7 @@ function build_function_call_node(node, status) {
37703
37927
  emit_nullable_arg_flag(node.params[i], status);
37704
37928
  }
37705
37929
  }
37706
- if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) {
37930
+ if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) {
37707
37931
  const flag_name = status.current_nullable_call_flag;
37708
37932
  const arg_list_empty = node.params.length === 0 && !is_closure_callee;
37709
37933
  if (flag_name) status.code += `${arg_list_empty ? "" : ", "}&${flag_name}`;
@@ -37812,13 +38036,13 @@ function emit_nullable_arg_flag(arg, status) {
37812
38036
  }
37813
38037
  if (arg.node_type === "value") {
37814
38038
  const vn = arg;
37815
- if (is_nullable_struct_type(vn.type, status) || is_nullable_scalar_type(vn.type)) status.code += `${has_flag_name(vn.value)}`;
38039
+ if (is_nullable_struct_type(vn.type, status) || is_nullable_scalar_type(vn.type, status)) status.code += `${has_flag_name(vn.value)}`;
37816
38040
  else status.code += `1`;
37817
38041
  return;
37818
38042
  }
37819
38043
  if (arg.node_type === "access" && arg.access.node_type === "access_field") {
37820
38044
  const t = type_from_value_node$1(arg);
37821
- if (is_nullable_struct_type(t, status) || is_nullable_scalar_type(t)) {
38045
+ if (is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status)) {
37822
38046
  const saved = begin_code_scratch(status);
37823
38047
  build_node$1(arg, status);
37824
38048
  const expr = end_code_scratch(status, saved);
@@ -38620,13 +38844,13 @@ function build_access_node$1(node, status) {
38620
38844
  build_node$1(access_func.params[i], status);
38621
38845
  status.suppress_dereference = false;
38622
38846
  }
38623
- if (target_param && !target_param.is_self_param && (is_nullable_struct_type(target_param.type, status) || is_nullable_scalar_type(target_param.type))) {
38847
+ if (target_param && !target_param.is_self_param && (is_nullable_struct_type(target_param.type, status) || is_nullable_scalar_type(target_param.type, status))) {
38624
38848
  status.code += `, `;
38625
38849
  emit_nullable_arg_flag(access_func.params[i], status);
38626
38850
  }
38627
38851
  }
38628
38852
  const method_ret_type = (target_method ?? trait_default_func)?.return_type ?? access_func.type;
38629
- if (is_nullable_struct_type(method_ret_type, status) || is_nullable_scalar_type(method_ret_type)) {
38853
+ if (is_nullable_struct_type(method_ret_type, status) || is_nullable_scalar_type(method_ret_type, status)) {
38630
38854
  const flag_name = status.current_nullable_call_flag;
38631
38855
  status.code += `, `;
38632
38856
  status.code += flag_name ? `&${flag_name}` : `(unsigned char *)&(unsigned char){0}`;
@@ -40371,11 +40595,11 @@ function is_nullable_struct_assignment(node, status) {
40371
40595
  if (node.left_value.node_type === "value") {
40372
40596
  const name = node.left_value.value;
40373
40597
  const t = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
40374
- return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
40598
+ return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
40375
40599
  }
40376
40600
  if (node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field") {
40377
40601
  const field_type = node.left_value.access.type;
40378
- return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type);
40602
+ return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type, status);
40379
40603
  }
40380
40604
  return false;
40381
40605
  }
@@ -40430,7 +40654,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
40430
40654
  const flag_name = has_flag_name(name);
40431
40655
  const decl_type = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
40432
40656
  const type_name = decl_type?.name || "";
40433
- const is_scalar = is_nullable_scalar_type(decl_type);
40657
+ const is_scalar = is_nullable_scalar_type(decl_type, status);
40434
40658
  const scalar_size = is_scalar ? aarch64_size(type_name) : 0;
40435
40659
  const reclaimable = !(node.right_value.node_type === "value" && node.right_value.value === name) && !!status.heap_string_fields?.size && Array.from(status.heap_string_fields).some((k) => k.startsWith(`${name}.`));
40436
40660
  if (rhs_is_null) {
@@ -40449,9 +40673,11 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
40449
40673
  }
40450
40674
  }
40451
40675
  };
40452
- const value_is_nullable_call = node.right_value.node_type === "func_call" && (is_nullable_struct_type(node.right_value.type, status) || is_nullable_scalar_type(node.right_value.type));
40676
+ const value_is_nullable_call = node.right_value.node_type === "func_call" && (is_nullable_struct_type(node.right_value.type, status) || is_nullable_scalar_type(node.right_value.type, status));
40453
40677
  const rhs_value_type = call_result_type(node.right_value) ?? type_from_value_node$1(node.right_value);
40454
- if (node.right_value.node_type === "access" && rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type))) {
40678
+ const value_is_nullable_access = node.right_value.node_type === "access" && rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type, status));
40679
+ const rhs_is_field_read = value_is_nullable_access && is_nullable_scalar_type(rhs_value_type, status) && node.right_value.access.node_type === "access_field";
40680
+ if (value_is_nullable_access && !rhs_is_field_read) {
40455
40681
  if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
40456
40682
  retarget_records();
40457
40683
  emit_rhs_value(node.right_value, nir_rhs, status);
@@ -40468,6 +40694,17 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
40468
40694
  emit_var_store(status, "x9", flag_name, 8);
40469
40695
  return;
40470
40696
  }
40697
+ if (rhs_is_field_read) {
40698
+ if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
40699
+ retarget_records();
40700
+ emit_rhs_value(node.right_value, nir_rhs, status);
40701
+ ensure_newline(status);
40702
+ emit_var_store(status, "x0", name, scalar_size);
40703
+ load_nullable_has(node.right_value, "x9", status);
40704
+ ensure_newline(status);
40705
+ emit_var_store(status, "x9", flag_name, 8);
40706
+ return;
40707
+ }
40471
40708
  if (value_is_nullable_call) {
40472
40709
  if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
40473
40710
  retarget_records();
@@ -40484,7 +40721,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
40484
40721
  status.call_x8_preset = old_preset;
40485
40722
  return;
40486
40723
  }
40487
- const rhs_is_nullable_value = !!rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type));
40724
+ const rhs_is_nullable_value = !!rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type, status));
40488
40725
  let rhs_flag_slot;
40489
40726
  if (rhs_is_nullable_value) {
40490
40727
  load_nullable_has(node.right_value, "x9", status);
@@ -40521,7 +40758,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
40521
40758
  const field_offset = get_field_offset(target_type.name, field_name, status);
40522
40759
  const has_offset = get_field_has_offset(target_type.name, field_name, status);
40523
40760
  const struct_size = get_struct_size(type_name, status);
40524
- const field_is_scalar = is_nullable_scalar_type(field_access.type);
40761
+ const field_is_scalar = is_nullable_scalar_type(field_access.type, status);
40525
40762
  get_source_address(access.target, status);
40526
40763
  ensure_newline(status);
40527
40764
  emit_asm(status, `str x0, [sp, #-16]!\n`);
@@ -40532,7 +40769,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
40532
40769
  return;
40533
40770
  }
40534
40771
  const rhs_t = call_result_type(node.right_value) ?? type_from_value_node$1(node.right_value);
40535
- const rhs_is_nullable_value = !!rhs_t?.is_nullable && (is_nullable_struct_type(rhs_t, status) || is_nullable_scalar_type(rhs_t));
40772
+ const rhs_is_nullable_value = !!rhs_t?.is_nullable && (is_nullable_struct_type(rhs_t, status) || is_nullable_scalar_type(rhs_t, status));
40536
40773
  let rhs_flag_slot;
40537
40774
  if (rhs_is_nullable_value) {
40538
40775
  load_nullable_has(node.right_value, "x10", status);
@@ -42357,7 +42594,7 @@ function build_operation_node(node, status) {
42357
42594
  if (node.op === "??") {
42358
42595
  const left_t = type_from_value_node$1(node.left_value);
42359
42596
  const left_is_nullable_struct = is_nullable_struct_type(left_t, status);
42360
- const left_is_nullable_scalar = is_nullable_scalar_type(left_t);
42597
+ const left_is_nullable_scalar = is_nullable_scalar_type(left_t, status);
42361
42598
  if (left_is_nullable_scalar && node.left_value.node_type === "func_call") {
42362
42599
  build_operand(node.left_value, "x0", status);
42363
42600
  ensure_newline(status);
@@ -42409,11 +42646,11 @@ function build_operation_node(node, status) {
42409
42646
  if ((node.op === "==" || node.op === "!=") && is_null_literal$1(node.left_value) !== is_null_literal$1(node.right_value)) {
42410
42647
  const nullable_flagged_side = (n) => {
42411
42648
  const t = type_from_value_node$1(n);
42412
- return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
42649
+ return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
42413
42650
  };
42414
42651
  const nullable_side = nullable_flagged_side(node.left_value) ? node.left_value : node.right_value;
42415
42652
  const side_type = type_from_value_node$1(nullable_side);
42416
- if (is_nullable_struct_type(side_type, status) || is_nullable_scalar_type(side_type)) {
42653
+ if (is_nullable_struct_type(side_type, status) || is_nullable_scalar_type(side_type, status)) {
42417
42654
  load_nullable_has(nullable_side, "x1", status);
42418
42655
  ensure_newline(status);
42419
42656
  emit_asm(status, `cmp x1, #0\n`);
@@ -42931,7 +43168,7 @@ function load_nullable_has(node, target_reg, status) {
42931
43168
  get_source_address(access.target, status);
42932
43169
  ensure_newline(status);
42933
43170
  if (target_reg !== "x0") emit_asm(status, `mov ${target_reg}, x0\n`);
42934
- const scalar_field = is_nullable_scalar_type(access.access.type);
43171
+ const scalar_field = is_nullable_scalar_type(access.access.type, status);
42935
43172
  const wreg = target_reg.replace("x", "w");
42936
43173
  const load_op = scalar_field ? `ldrb ${wreg}` : `ldr ${target_reg}`;
42937
43174
  if (has_off === 0) emit_asm(status, `${load_op}, [${target_reg}]\n`);
@@ -45133,7 +45370,7 @@ function build_access_method(node, access_func, status) {
45133
45370
  const access_callee_params = access_callee_method?.params?.filter((p) => !p.is_self_param);
45134
45371
  const return_struct = !access_func.type.is_view && !access_func.type.is_array && (!!status.structs.find((s) => s.name === access_func.type.name && !s.is_simple_type && !s.is_class) || get_enum_sret_size(access_func.type.name, status) !== void 0);
45135
45372
  const method_ret_type = access_callee_method?.return_type ?? access_func.type;
45136
- const nullable_scalar_ret = is_nullable_scalar_type(method_ret_type) && !method_ret_type.is_ref;
45373
+ const nullable_scalar_ret = is_nullable_scalar_type(method_ret_type, status) && !method_ret_type.is_ref;
45137
45374
  let temp_addr = "";
45138
45375
  let temp_offset = 0;
45139
45376
  if (return_struct || nullable_scalar_ret) {
@@ -45253,7 +45490,7 @@ function build_access_method(node, access_func, status) {
45253
45490
  for (let i = 0; i < access_func.params.length; i++) {
45254
45491
  if ((access_func.ref_param_indices ?? []).includes(i)) continue;
45255
45492
  const cp = access_callee_params?.[i];
45256
- if (cp && is_nullable_scalar_type(cp.type) || is_nullable_scalar_type(access_func.params[i].type)) nullable_scalar_arg_set.add(i);
45493
+ if (cp && is_nullable_scalar_type(cp.type, status) || is_nullable_scalar_type(access_func.params[i].type, status)) nullable_scalar_arg_set.add(i);
45257
45494
  }
45258
45495
  const arg_slot = [];
45259
45496
  let total_arg_slots = 0;
@@ -45308,7 +45545,7 @@ function build_access_method(node, access_func, status) {
45308
45545
  if (arg.node_type === "value" && arg.value === "null") {
45309
45546
  emit_asm(status, `mov x0, #0\n`);
45310
45547
  emit_asm(status, `mov x1, #0\n`);
45311
- } else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
45548
+ } else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
45312
45549
  const vname = arg.value;
45313
45550
  emit_var_load(status, "x0", vname, aarch64_size(arg.type.name));
45314
45551
  emit_var_load(status, "x1", has_flag_name(vname), 8);
@@ -49572,13 +49809,33 @@ function can_class_int_coerce(target, value, status) {
49572
49809
  }
49573
49810
  //#endregion
49574
49811
  //#region ../src/check/utils/check_type_exists.ts
49812
+ /**
49813
+ * Containers whose elements live in raw T-sized slots (the `#arch` generic
49814
+ * bodies size each slot from the type name alone). A flagged-nullable
49815
+ * element type (`List<int?>`) has no per-slot null flag there, so `0`/`false`
49816
+ * would read back as null — rejected instead of silently misbehaving.
49817
+ * Element types with an in-band null (`string?`, class/trait `?`) stay legal.
49818
+ */
49819
+ const RAW_ELEMENT_CONTAINERS = /* @__PURE__ */ new Set([
49820
+ "Array",
49821
+ "Buffer",
49822
+ "List",
49823
+ "Set",
49824
+ "Map",
49825
+ "Graph",
49826
+ "LinkedList",
49827
+ "Tree",
49828
+ "Arena"
49829
+ ]);
49575
49830
  function check_type_exists(type, status, start) {
49831
+ if (type.is_nullable && type.is_array && !type.is_ref && !type.is_pointer && is_nullable_scalar_name(type.name, status)) add_error(status, `Nullable element type '${type.name}?' is not supported in an array: element storage has no per-element null flag`, start);
49576
49832
  if (type.name === "tuple" && type.tuple_types?.length) {
49577
49833
  let ok = true;
49578
49834
  for (const elem of type.tuple_types) if (!check_type_exists(elem, status, start)) ok = false;
49579
49835
  return ok;
49580
49836
  }
49581
49837
  if (type.name === "anon_enum" && type.enum_cases?.length) {
49838
+ if (type.is_nullable && type.enum_cases.some((c) => c.types.length > 0)) add_error(status, `Nullable type '${type_name(type)}' is not supported: enums with associated data have no separate null flag`, start);
49582
49839
  let ok = true;
49583
49840
  for (const c of type.enum_cases) for (const t of c.types) if (!check_type_exists(t, status, start)) ok = false;
49584
49841
  return ok;
@@ -49595,19 +49852,26 @@ function check_type_exists(type, status, start) {
49595
49852
  }
49596
49853
  }
49597
49854
  type.name = declared.name;
49855
+ if (type.is_nullable) {
49856
+ if (status.enums.findLast((e) => (e.name === declared.name || e.source_name === declared.name) && !e.is_generic)?.has_associated_data) add_error(status, `Nullable type '${type_name(type)}' is not supported: enums with associated data have no separate null flag`, start);
49857
+ }
49598
49858
  } else if (!status.types.includes(type.name)) {
49599
49859
  add_error(status, `Unknown type: ${type_name(type)}`, start);
49600
49860
  return false;
49601
49861
  }
49602
49862
  if (type.tuple_types) for (const elem of type.tuple_types) check_type_exists(elem, status, start);
49603
- if (type.type_args) for (const arg of type.type_args) {
49604
- if (arg.name === "func") {
49605
- add_error(status, `func types cannot be used as type arguments ('${type.name}<...>')`, start);
49606
- continue;
49863
+ if (type.type_args) {
49864
+ if (RAW_ELEMENT_CONTAINERS.has(type.name)) {
49865
+ for (const arg of type.type_args) if (is_nullable_flagged_type(arg, status)) add_error(status, `Nullable element type '${type_name(arg)}' is not supported in '${type.name}<...>': container element storage has no per-element null flag`, start);
49607
49866
  }
49608
- check_type_exists(arg, status, start);
49609
- }
49610
- else {
49867
+ for (const arg of type.type_args) {
49868
+ if (arg.name === "func") {
49869
+ add_error(status, `func types cannot be used as type arguments ('${type.name}<...>')`, start);
49870
+ continue;
49871
+ }
49872
+ check_type_exists(arg, status, start);
49873
+ }
49874
+ } else {
49611
49875
  const struct = status.structs.findLast((s) => s.name === type.name);
49612
49876
  if (struct?.is_generic) {
49613
49877
  if (!struct.type_params.every((tp) => status.type_params.includes(tp))) add_error(status, `Generic type '${type.name}' requires type arguments (expected <${struct.type_params.join(", ")}>)`, start);
@@ -52486,20 +52750,57 @@ function build_hash(struct, fields) {
52486
52750
  }
52487
52751
  /**
52488
52752
  * Whether `struct` is eligible for a synthesized `copy` method: a value
52489
- * struct whose only ownership is its DIRECT string fields — the same set
52490
- * the pass-by-value argument convention covers. A struct that additionally
52491
- * owns non-string heap (a resource-releasing `#destroy`, a class field, a
52492
- * nested requiring-move struct) cannot be deep-copied by "byte copy +
52493
- * strdup the strings" — its copy stays unwritten (move/swap remains the
52494
- * escape hatch) — and a struct with no string fields is already soundly
52495
- * byte-copyable, so it needs no method.
52753
+ * struct whose every field is independently deep-copyable — direct string
52754
+ * fields (strdup'd), scalars/arrays (byte copies), `List<T>` fields whose
52755
+ * element is itself deep-copyable (the library `List.copy` deep-copies slots
52756
+ * via the owning store contract), and nested copyable structs (recursing
52757
+ * through their own `copy`). Class fields, enum-with-data fields (their
52758
+ * stores transfer payload ownership), class/trait-carrying `List` elements,
52759
+ * and resource-releasing `#destroy`s cannot be deep-copied by synthesis —
52760
+ * the copy stays unwritten (move/swap remains the escape hatch) — and a
52761
+ * struct with nothing to deep-copy (no string, `List`, or nested-copyable
52762
+ * field) is already soundly byte-copyable, so it needs no method.
52496
52763
  */
52497
52764
  function struct_is_copyable(struct, status) {
52498
52765
  if (struct.is_class || struct.is_generic || struct.is_simple_type) return false;
52499
- if (direct_string_fields(struct).length === 0) return false;
52500
- if (struct_owns_non_string_heap(struct, status, /* @__PURE__ */ new Set())) return false;
52766
+ if (struct_owns_resource(struct, status, /* @__PURE__ */ new Set())) return false;
52767
+ if (!struct_needs_copy(struct, status)) return false;
52501
52768
  return struct_copy_ctor_args(struct) !== null;
52502
52769
  }
52770
+ /** Whether any field of `struct` actually needs the synthesized deep copy:
52771
+ * a direct string field, a deep-copyable `List<T>` field, or a nested
52772
+ * copyable struct field. A struct of flat values byte-copies soundly
52773
+ * without a method. */
52774
+ function struct_needs_copy(struct, status) {
52775
+ if (direct_string_fields(struct).length > 0) return true;
52776
+ for (const field of struct.fields) {
52777
+ if (field.type.is_ref || field.type.is_view || field.type.is_array) continue;
52778
+ if (field_copy_call(field.type, status)) return true;
52779
+ }
52780
+ return false;
52781
+ }
52782
+ /**
52783
+ * Whether `struct` owns a resource a synthesized copy CANNOT duplicate: a
52784
+ * `#destroy` that releases something (a raw block / extern free), or a field
52785
+ * whose type has no deep-copy story (a class instance, a `List` of
52786
+ * class/trait elements, a nested struct with such a field). Fields that DO
52787
+ * have a deep-copy story (strings, nested copyables, copyable `List`s,
52788
+ * payload-copyable enums) don't block synthesis. Mirrors the old
52789
+ * `struct_owns_non_string_heap` gate but understands container deep copies.
52790
+ */
52791
+ function struct_owns_resource(struct, status, visited) {
52792
+ if (visited.has(struct.name)) return false;
52793
+ visited.add(struct.name);
52794
+ const destroy = struct.functions.find((f) => f.name === "#destroy");
52795
+ if (destroy && destroy_releases(destroy)) return true;
52796
+ for (const field of struct.fields) {
52797
+ if (field.type.is_ref || field.type.is_view) continue;
52798
+ if (field.type.name === "string" && !field.type.is_array) continue;
52799
+ if (field.type.is_nullable) return true;
52800
+ if (!field_copyable(field.type, status)) return true;
52801
+ }
52802
+ return false;
52803
+ }
52503
52804
  /**
52504
52805
  * How a synthesized `copy` constructs the fresh value: the zero-argument
52505
52806
  * `#init` overload when one exists (auto or user), else the bodyless AUTO
@@ -52515,14 +52816,49 @@ function struct_copy_ctor_args(struct) {
52515
52816
  if (!auto) return null;
52516
52817
  return auto.params.filter((p) => !p.is_self_param).map((p) => p.name);
52517
52818
  }
52518
- /** Whether `type` names a struct that has (or will get) a `copy` method.
52519
- * Order-independent: it re-evaluates the eligibility predicate instead of
52520
- * looking for an already-synthesized method, so an outer struct nested in
52521
- * the same pre-pass resolves a later-declared field struct. */
52522
- function field_type_is_copyable(type, status) {
52819
+ /** Whether `type` has (or will get) a deep-copy story: a struct with a
52820
+ * synthesized `copy`, a `List<T>` whose element is deep-copyable (the
52821
+ * library `List.copy` deep-copies slots via the owning store contract), or
52822
+ * a primitive / plain (payload-free) enum. Order-independent: it
52823
+ * re-evaluates the eligibility predicate instead of looking for an
52824
+ * already-synthesized method, so an outer struct nested in the same
52825
+ * pre-pass resolves a later-declared field struct. */
52826
+ function field_copyable(type, status) {
52523
52827
  if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
52828
+ if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
52829
+ const nested_struct = status.structs.find((s) => s.name === type.name);
52830
+ if (nested_struct) {
52831
+ if (nested_struct.is_simple_type) return true;
52832
+ return struct_is_copyable(nested_struct, status);
52833
+ }
52834
+ const en = status.enums.find((e) => e.name === type.name);
52835
+ if (en) return !en.has_associated_data;
52836
+ return !status.traits.some((t) => t.name === type.name);
52837
+ }
52838
+ /** Whether a `List<T>` element type is deep-copyable (see
52839
+ * `field_copyable`). */
52840
+ function element_copyable(type, status) {
52841
+ if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
52842
+ if (type.name === "string") return true;
52843
+ if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
52524
52844
  const nested = status.structs.find((s) => s.name === type.name);
52525
- return !!nested && struct_is_copyable(nested, status);
52845
+ if (nested) {
52846
+ if (nested.is_simple_type) return true;
52847
+ return !nested.is_class && struct_is_copyable(nested, status);
52848
+ }
52849
+ const en = status.enums.find((e) => e.name === type.name);
52850
+ if (en) return !en.has_associated_data;
52851
+ return !status.traits.some((t) => t.name === type.name);
52852
+ }
52853
+ /** Whether `build_copy` must route the field through a `.copy()` call (a
52854
+ * nested copyable struct, or a `List<T>` field): everything else byte
52855
+ * copies through plain field assignment (strings strdup via the field-write
52856
+ * path, primitives/plain enums are flat values). */
52857
+ function field_copy_call(type, status) {
52858
+ if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
52859
+ if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
52860
+ const nested_struct = status.structs.find((s) => s.name === type.name);
52861
+ return !!nested_struct && !nested_struct.is_simple_type && struct_is_copyable(nested_struct, status);
52526
52862
  }
52527
52863
  /**
52528
52864
  * Synthesize `pub func copy(self) -> Struct` for owning value structs: a
@@ -52538,8 +52874,8 @@ function build_copy(struct, status) {
52538
52874
  const ctor = new FunctionCallNode(-1, struct.name, new Type(struct.name), ctor_args.map((name) => field_access("self", name)));
52539
52875
  const statements = [new DeclarationNode(-1, "private", "var", "c", new Type(struct.name), ctor)];
52540
52876
  for (const field of struct.fields) {
52541
- const right = field_type_is_copyable(field.type, status) ? method_call(field_access("self", field.name), "copy") : field_access("self", field.name);
52542
- statements.push(new AssignmentNode(-1, field_access("c", field.name), right, "="));
52877
+ const right = field_copy_call(field.type, status) ? method_call(field_access("self", field.name), "copy") : field_access("self", field.name);
52878
+ statements.push(new AssignmentNode(-1, field_access("c", field.name), right));
52543
52879
  }
52544
52880
  statements.push(new ReturnNode(-1, new ValueNode(-1, "c")));
52545
52881
  return new FunctionNode(-1, "pub", "copy", new Type(struct.name), [self_param(struct)], statements);
@@ -53618,7 +53954,10 @@ function monomorphize(generic_struct, type_args, status) {
53618
53954
  mono_field.type = constructed;
53619
53955
  }
53620
53956
  }
53621
- substitute_raw_in_node(mono_field.value, substitution, status.structs, void 0, status.traits, status.enums);
53957
+ substitute_raw_in_node(mono_field.value, substitution, status.structs, void 0, status.traits, {
53958
+ enums: status.enums,
53959
+ bitsets: status.bitsets
53960
+ });
53622
53961
  }
53623
53962
  mono_field.is_library = field.is_library;
53624
53963
  mono_field.is_readonly = field.is_readonly;
@@ -53653,7 +53992,10 @@ function monomorphize(generic_struct, type_args, status) {
53653
53992
  for (const func of generic_struct.functions) {
53654
53993
  if (func.name === "#init") continue;
53655
53994
  const cloned = clone_node(func);
53656
- substitute_raw_types(cloned, substitution, status.structs, status.traits, status.enums);
53995
+ substitute_raw_types(cloned, substitution, status.structs, status.traits, {
53996
+ enums: status.enums,
53997
+ bitsets: status.bitsets
53998
+ });
53657
53999
  fold_substituted_constant_ifs(cloned.statements);
53658
54000
  rename_local_labels(cloned, mono_name);
53659
54001
  const retype_roots = [...cloned.statements, ...cloned.allocations ?? []];
@@ -53666,7 +54008,10 @@ function monomorphize(generic_struct, type_args, status) {
53666
54008
  param.type = substitute_type(param.type, substitution);
53667
54009
  substitute_param_signature(param, substitution);
53668
54010
  if (param.type.type_args?.length) param.type = materialize_generic_enum_type(param.type, status);
53669
- if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits, status.enums);
54011
+ if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits, {
54012
+ enums: status.enums,
54013
+ bitsets: status.bitsets
54014
+ });
53670
54015
  }
53671
54016
  retype_param_references(retype_roots, cloned.params);
53672
54017
  retype_local_references(retype_roots);
@@ -53694,7 +54039,10 @@ function monomorphize(generic_struct, type_args, status) {
53694
54039
  if (variadic_init_unsupported(generic_init)) continue;
53695
54040
  if (!generic_init.statements.some((s) => s.node_type !== "raw")) continue;
53696
54041
  const cloned = clone_node(generic_init);
53697
- substitute_raw_types(cloned, substitution, status.structs, status.traits, status.enums);
54042
+ substitute_raw_types(cloned, substitution, status.structs, status.traits, {
54043
+ enums: status.enums,
54044
+ bitsets: status.bitsets
54045
+ });
53698
54046
  fold_substituted_constant_ifs(cloned.statements);
53699
54047
  rename_local_labels(cloned, mono_name);
53700
54048
  cloned.return_type = new Type(mono_name);
@@ -53702,7 +54050,10 @@ function monomorphize(generic_struct, type_args, status) {
53702
54050
  for (const param of cloned.params) {
53703
54051
  param.type = substitute_type(param.type, substitution);
53704
54052
  substitute_param_signature(param, substitution);
53705
- if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits, status.enums);
54053
+ if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits, {
54054
+ enums: status.enums,
54055
+ bitsets: status.bitsets
54056
+ });
53706
54057
  }
53707
54058
  const cloned_self = cloned.params.find((p) => p.is_self_param);
53708
54059
  if (cloned_self) cloned_self.type = new Type(mono_name);
@@ -54207,7 +54558,7 @@ function derive_annotations_for_access_func(access_node, fc, status) {
54207
54558
  const p = func.params[i];
54208
54559
  if (p.is_self_param) continue;
54209
54560
  if (!p.type?.is_nullable) continue;
54210
- if (status.structs.find((s) => s.name === p.type.name && !s.is_class) || is_nullable_scalar_type(p.type)) nullable_indices.push(i - (self_offset >= 0 ? self_offset + 1 : 0));
54561
+ if (status.structs.find((s) => s.name === p.type.name && !s.is_class) || is_nullable_scalar_type(p.type, status)) nullable_indices.push(i - (self_offset >= 0 ? self_offset + 1 : 0));
54211
54562
  }
54212
54563
  if (nullable_indices.length > 0) fc.nullable_param_indices = nullable_indices;
54213
54564
  if (func.return_constraint && !fc.return_bounds) {
@@ -54586,7 +54937,7 @@ function fold_substituted_constant_ifs(statements) {
54586
54937
  statements.splice(i, 1, ...kept);
54587
54938
  }
54588
54939
  }
54589
- function substitute_raw_types(func, substitution, structs, traits, enums) {
54940
+ function substitute_raw_types(func, substitution, structs, traits, tables) {
54590
54941
  const deref_params = /* @__PURE__ */ new Set();
54591
54942
  for (const param of func.params) {
54592
54943
  const resolved = substitution.get(param.type.name);
@@ -54595,7 +54946,7 @@ function substitute_raw_types(func, substitution, structs, traits, enums) {
54595
54946
  const s = structs.find((x) => x.name === resolved);
54596
54947
  if (s && !s.is_simple_type && !s.is_class) deref_params.add(param.name);
54597
54948
  }
54598
- for (const stmt of func.statements) substitute_raw_in_node(stmt, substitution, structs, deref_params, traits, enums);
54949
+ for (const stmt of func.statements) substitute_raw_in_node(stmt, substitution, structs, deref_params, traits, tables);
54599
54950
  }
54600
54951
  function rename_local_labels(node, prefix) {
54601
54952
  if (node.node_type === "raw") {
@@ -54628,7 +54979,7 @@ function raw_type_alignment(name) {
54628
54979
  function raw_align_to(offset, alignment) {
54629
54980
  return Math.ceil(offset / alignment) * alignment;
54630
54981
  }
54631
- function raw_type_size(name, structs, enums) {
54982
+ function raw_type_size(name, structs, tables) {
54632
54983
  switch (name) {
54633
54984
  case "bool":
54634
54985
  case "int8":
@@ -54639,13 +54990,13 @@ function raw_type_size(name, structs, enums) {
54639
54990
  case "int32":
54640
54991
  case "uint32": return 4;
54641
54992
  }
54642
- const enum_node = enums?.find((e) => e.name === name && !e.is_generic);
54993
+ const enum_node = tables.enums.find((e) => e.name === name && !e.is_generic);
54643
54994
  if (enum_node) {
54644
54995
  if (!enum_node.has_associated_data) return 8;
54645
54996
  let max_payload = 0;
54646
54997
  for (const c of enum_node.cases) {
54647
54998
  let case_size = 0;
54648
- for (const p of c.params) case_size += raw_type_size(p.type.name, structs, enums);
54999
+ for (const p of c.params) case_size += raw_type_size(p.type.name, structs, tables);
54649
55000
  max_payload = Math.max(max_payload, case_size);
54650
55001
  }
54651
55002
  return 8 + Math.ceil(max_payload / 8) * 8;
@@ -54660,9 +55011,9 @@ function raw_type_size(name, structs, enums) {
54660
55011
  size += 16;
54661
55012
  continue;
54662
55013
  }
54663
- size += raw_type_size(field.type.name, structs, enums);
55014
+ size += raw_type_size(field.type.name, structs, tables);
54664
55015
  if (field.type.is_nullable && structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_class)) size += 8;
54665
- else if (is_nullable_scalar_type(field.type)) size += 1;
55016
+ else if (is_nullable_scalar_type(field.type, tables)) size += 1;
54666
55017
  }
54667
55018
  return raw_align_to(size, 8);
54668
55019
  }
@@ -54707,12 +55058,15 @@ function raw_block_is_pure_asm(value) {
54707
55058
  function is_reference_type_arg(t, structs, traits) {
54708
55059
  return !!traits.some((tr) => tr.name === t.name) || !!structs.find((s) => s.name === t.name && s.is_class);
54709
55060
  }
54710
- function substitute_raw_in_node(node, substitution, structs, deref_params = /* @__PURE__ */ new Set(), traits = [], enums = []) {
55061
+ function substitute_raw_in_node(node, substitution, structs, deref_params = /* @__PURE__ */ new Set(), traits = [], tables = {
55062
+ enums: [],
55063
+ bitsets: []
55064
+ }) {
54711
55065
  if (node.node_type === "value") {
54712
55066
  const vn = node;
54713
55067
  for (const [param, type] of substitution) {
54714
55068
  if (vn.value === `${param}_SIZE`) {
54715
- vn.value = String(raw_type_size(type, structs, enums));
55069
+ vn.value = String(raw_type_size(type, structs, tables));
54716
55070
  vn.type = new Type("int", true);
54717
55071
  return;
54718
55072
  }
@@ -54737,7 +55091,7 @@ function substitute_raw_in_node(node, substitution, structs, deref_params = /* @
54737
55091
  else if (struct_node) c_type_name = `struct ${type}`;
54738
55092
  else c_type_name = raw_c_type_name(type);
54739
55093
  value = value.replace(new RegExp(`\\b${param}\\b`, "g"), c_type_name);
54740
- const size = raw_type_size(type, structs, enums);
55094
+ const size = raw_type_size(type, structs, tables);
54741
55095
  const size_expr = raw_block_is_pure_asm(value) ? String(size) : `sizeof(${c_type_name})`;
54742
55096
  value = value.replace(new RegExp(`\\b${param}_SIZE\\b`, "g"), size_expr);
54743
55097
  value = value.replace(new RegExp(`\\b${param}_destroy\\b`, "g"), `${type}_destroy`);
@@ -54773,22 +55127,22 @@ function substitute_raw_in_node(node, substitution, structs, deref_params = /* @
54773
55127
  if (any_node.func_return_type?.name) any_node.func_return_type = substitute_type(any_node.func_return_type, substitution);
54774
55128
  }
54775
55129
  if (any_node.allocations && Array.isArray(any_node.allocations)) {
54776
- for (const child of any_node.allocations) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, enums);
55130
+ for (const child of any_node.allocations) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
54777
55131
  }
54778
55132
  if (any_node.statements && Array.isArray(any_node.statements)) {
54779
- for (const child of any_node.statements) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, enums);
55133
+ for (const child of any_node.statements) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
54780
55134
  }
54781
55135
  if (any_node.params && Array.isArray(any_node.params)) {
54782
- for (const child of any_node.params) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, enums);
55136
+ for (const child of any_node.params) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
54783
55137
  }
54784
- if (any_node.value && any_node.value.node_type) substitute_raw_in_node(any_node.value, substitution, structs, deref_params, traits, enums);
54785
- if (any_node.left_value?.node_type) substitute_raw_in_node(any_node.left_value, substitution, structs, deref_params, traits, enums);
54786
- if (any_node.right_value?.node_type) substitute_raw_in_node(any_node.right_value, substitution, structs, deref_params, traits, enums);
54787
- if (any_node.target?.node_type) substitute_raw_in_node(any_node.target, substitution, structs, deref_params, traits, enums);
54788
- if (any_node.access?.node_type) substitute_raw_in_node(any_node.access, substitution, structs, deref_params, traits, enums);
54789
- if (any_node.swap?.node_type) substitute_raw_in_node(any_node.swap, substitution, structs, deref_params, traits, enums);
54790
- if (any_node.condition?.node_type) substitute_raw_in_node(any_node.condition, substitution, structs, deref_params, traits, enums);
54791
- if (any_node.index?.node_type) substitute_raw_in_node(any_node.index, substitution, structs, deref_params, traits, enums);
55138
+ if (any_node.value && any_node.value.node_type) substitute_raw_in_node(any_node.value, substitution, structs, deref_params, traits, tables);
55139
+ if (any_node.left_value?.node_type) substitute_raw_in_node(any_node.left_value, substitution, structs, deref_params, traits, tables);
55140
+ if (any_node.right_value?.node_type) substitute_raw_in_node(any_node.right_value, substitution, structs, deref_params, traits, tables);
55141
+ if (any_node.target?.node_type) substitute_raw_in_node(any_node.target, substitution, structs, deref_params, traits, tables);
55142
+ if (any_node.access?.node_type) substitute_raw_in_node(any_node.access, substitution, structs, deref_params, traits, tables);
55143
+ if (any_node.swap?.node_type) substitute_raw_in_node(any_node.swap, substitution, structs, deref_params, traits, tables);
55144
+ if (any_node.condition?.node_type) substitute_raw_in_node(any_node.condition, substitution, structs, deref_params, traits, tables);
55145
+ if (any_node.index?.node_type) substitute_raw_in_node(any_node.index, substitution, structs, deref_params, traits, tables);
54792
55146
  }
54793
55147
  function specialize_function(generic_func, call_node, status) {
54794
55148
  const substitution = /* @__PURE__ */ new Map();
@@ -55807,7 +56161,7 @@ function check_function_call(node, status, func, target_type, self_value, self_p
55807
56161
  for (let [i, param] of node.params.entries()) {
55808
56162
  const func_param = func.params[i + self_offset];
55809
56163
  if (func_param && func_param.type.is_nullable && !func_param.is_self_param && !func_param.type.is_ref && !func_param.type.is_array) {
55810
- if (status.structs.find((st) => st.name === func_param.type.name && !st.is_class && !st.is_simple_type) || is_nullable_scalar_type(func_param.type)) {
56164
+ if (status.structs.find((st) => st.name === func_param.type.name && !st.is_class && !st.is_simple_type) || is_nullable_scalar_type(func_param.type, status)) {
55811
56165
  if (!node.nullable_param_indices) node.nullable_param_indices = [];
55812
56166
  if (!node.nullable_param_indices.includes(i)) node.nullable_param_indices.push(i);
55813
56167
  }