nomen-lang 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +869 -54
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -9989,10 +9989,13 @@ function is_compiler_temp_name$2(name) {
|
|
|
9989
9989
|
* the generic call builder), since monomorphized tuple inits route through
|
|
9990
9990
|
* each of them.
|
|
9991
9991
|
*/
|
|
9992
|
-
function mark_tuple_literal_move_owners(node, status) {
|
|
9992
|
+
function mark_tuple_literal_move_owners(node, status, on_move) {
|
|
9993
9993
|
if (!node.type?.name?.startsWith("_Tuple_")) return;
|
|
9994
9994
|
if (!status.moved) status.moved = /* @__PURE__ */ new Set();
|
|
9995
|
-
for (const p of node.params) if (p.node_type === "value" && p.is_moved && typeof p.value === "string")
|
|
9995
|
+
for (const p of node.params) if (p.node_type === "value" && p.is_moved && typeof p.value === "string") {
|
|
9996
|
+
status.moved.add(p.value);
|
|
9997
|
+
on_move?.(p.value);
|
|
9998
|
+
}
|
|
9996
9999
|
}
|
|
9997
10000
|
//#endregion
|
|
9998
10001
|
//#region ../src/build_common/string_escapes.ts
|
|
@@ -16045,6 +16048,28 @@ function emit_var_store(status, reg, name, size) {
|
|
|
16045
16048
|
}
|
|
16046
16049
|
}
|
|
16047
16050
|
}
|
|
16051
|
+
/**
|
|
16052
|
+
* Save the fat (x0, x1) return pair at [x29, #offset]. The stp/ldp simm7
|
|
16053
|
+
* scaled range tops out at +504 — split into the 12-bit-scaled str/ldr
|
|
16054
|
+
* halves beyond it.
|
|
16055
|
+
*/
|
|
16056
|
+
function emit_return_pair_save(status, offset) {
|
|
16057
|
+
if (offset + 8 > 504) {
|
|
16058
|
+
emit_asm(status, `str x0, [x29, #${offset}]\n`);
|
|
16059
|
+
emit_asm(status, `str x1, [x29, #${offset + 8}]\n`);
|
|
16060
|
+
return;
|
|
16061
|
+
}
|
|
16062
|
+
emit_asm(status, `stp x0, x1, [x29, #${offset}]\n`);
|
|
16063
|
+
}
|
|
16064
|
+
/** Restore the fat (x0, x1) return pair from [x29, #offset]. */
|
|
16065
|
+
function emit_return_pair_restore(status, offset) {
|
|
16066
|
+
if (offset + 8 > 504) {
|
|
16067
|
+
emit_asm(status, `ldr x0, [x29, #${offset}]\n`);
|
|
16068
|
+
emit_asm(status, `ldr x1, [x29, #${offset + 8}]\n`);
|
|
16069
|
+
return;
|
|
16070
|
+
}
|
|
16071
|
+
emit_asm(status, `ldp x0, x1, [x29, #${offset}]\n`);
|
|
16072
|
+
}
|
|
16048
16073
|
//#endregion
|
|
16049
16074
|
//#region ../src/build_aarch64/slp_pair.ts
|
|
16050
16075
|
/**
|
|
@@ -16725,6 +16750,61 @@ function try_emit_slp_pair(index, ctx_ast, ctx_stmts, status) {
|
|
|
16725
16750
|
return 2;
|
|
16726
16751
|
}
|
|
16727
16752
|
//#endregion
|
|
16753
|
+
//#region ../src/build_common/box_clone.ts
|
|
16754
|
+
/**
|
|
16755
|
+
* The load/transfer deep-copy story for `Box<T>` FIELDS on mv struct elements
|
|
16756
|
+
* of mv containers (2026-10-12, the Valen-comparison repro): a
|
|
16757
|
+
* `List<Ship>` whose `Ship` holds `var Box<Engine> engine` used to come back
|
|
16758
|
+
* from `load_T` with the element's box SHALLOW — the copy's scope-exit
|
|
16759
|
+
* destroy freed the slot's shared wrapper (SIGTRAP on the next use). `Box<T>`
|
|
16760
|
+
* joins the deep-copy/transfer set: `load_T` clones the wrapper
|
|
16761
|
+
* (`<Box_T>_box_clone` — malloc + byte copy + fresh copies of the pointee's
|
|
16762
|
+
* owned fields), and `store_T`/`replace_T` transfer it (the donor's field is
|
|
16763
|
+
* zeroed — the slot is the sole owner). The clone helper is emitted lazily
|
|
16764
|
+
* per backend (this module carries only the shared predicates).
|
|
16765
|
+
*/
|
|
16766
|
+
/** The clone helper's symbol for a Box mono (`Box_Engine` → `Box_Engine_box_clone`). */
|
|
16767
|
+
function box_clone_fn_name(box_mono_name) {
|
|
16768
|
+
return `${box_mono_name}_box_clone`;
|
|
16769
|
+
}
|
|
16770
|
+
/**
|
|
16771
|
+
* The monomorphized `Box<T>` wrapper when `type` is a box field type, else
|
|
16772
|
+
* undefined. Shape-checked through `is_box_type` (single `__value` field,
|
|
16773
|
+
* compiler-stamped storage class), so a USER struct named `Box_*` is not
|
|
16774
|
+
* mistaken for the wrapper.
|
|
16775
|
+
*/
|
|
16776
|
+
function box_field_mono(type, table) {
|
|
16777
|
+
if (!type.name || type.is_ref || type.is_view || type.is_array) return void 0;
|
|
16778
|
+
const mono = resolve_struct_type(type, table);
|
|
16779
|
+
if (!mono || !mono.is_class) return void 0;
|
|
16780
|
+
return is_box_type(mono.name, table.structs) ? mono : void 0;
|
|
16781
|
+
}
|
|
16782
|
+
/** The `__value` field's type (the pointee). The wrapper is shape-checked to
|
|
16783
|
+
* have exactly one field. */
|
|
16784
|
+
function box_pointee_type(box_mono) {
|
|
16785
|
+
return box_mono.fields[0]?.type;
|
|
16786
|
+
}
|
|
16787
|
+
/** Whether `<E>_copy` yields a fully independent value: no case payload is a
|
|
16788
|
+
* class/trait reference (those are shared pointers the copy cannot
|
|
16789
|
+
* duplicate — a cloned blob sharing one would free it twice at teardown). */
|
|
16790
|
+
function enum_copy_is_deep$1(elem, table) {
|
|
16791
|
+
for (const c of elem.cases) for (const p of c.params) if (table.structs.find((s) => s.name === p.type.name)?.is_class || table.traits?.some((t) => t.name === p.type.name)) return false;
|
|
16792
|
+
return true;
|
|
16793
|
+
}
|
|
16794
|
+
/**
|
|
16795
|
+
* Whether the pointee's plain `string` fields are heap-owned by the wrapper —
|
|
16796
|
+
* the same gate the box CONSTRUCTOR uses when normalizing the incoming value
|
|
16797
|
+
* (build_declaration_node's boxing path): an OWNING pointee's strings are
|
|
16798
|
+
* uniformly heap (transferred as-is), and a pointee without a user `#destroy`
|
|
16799
|
+
* that needs the auto destroy had its rodata/borrow fields strdup'd at
|
|
16800
|
+
* boxing. Anything else (a non-owning pointee with a user `#destroy`) may
|
|
16801
|
+
* still hold borrowed bytes the wrapper destroy never frees — the clone must
|
|
16802
|
+
* NOT strdup those (it would leak per clone).
|
|
16803
|
+
*/
|
|
16804
|
+
function box_pointee_strings_are_heap(pointee, table) {
|
|
16805
|
+
return owns_strings_uniformly(pointee) || !has_destroy(pointee) && struct_needs_auto_destroy(pointee, table);
|
|
16806
|
+
}
|
|
16807
|
+
//#endregion
|
|
16728
16808
|
//#region ../src/build_common/temp_anchor_consolidation.ts
|
|
16729
16809
|
/**
|
|
16730
16810
|
* Shared OWNERSHIP decision for hoisted call-temporary consolidation.
|
|
@@ -16804,6 +16884,20 @@ function emit_borrow_carrier_owned_free(status, elem, decl_name) {
|
|
|
16804
16884
|
for (const field of elem.fields) {
|
|
16805
16885
|
const size = get_type_size(field.type, status);
|
|
16806
16886
|
if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable && field.type.name !== "string") {
|
|
16887
|
+
const box_mono = box_field_mono(field.type, status);
|
|
16888
|
+
if (box_mono) {
|
|
16889
|
+
emit_var_address(status, "x0", decl_name);
|
|
16890
|
+
emit_asm(status, `ldr x0, [x0, #${offset}]\n`);
|
|
16891
|
+
const skip = `.Lskip_bcc_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
16892
|
+
emit_asm(status, `cbz x0, ${skip}\n`);
|
|
16893
|
+
emit_asm(status, `str x0, [sp, #-16]!\n`);
|
|
16894
|
+
emit_asm(status, `bl ${box_mono.name}_destroy\n`);
|
|
16895
|
+
emit_asm(status, `ldr x0, [sp], #16\n`);
|
|
16896
|
+
emit_free(status);
|
|
16897
|
+
emit_asm(status, `${skip}:\n`);
|
|
16898
|
+
offset += size;
|
|
16899
|
+
continue;
|
|
16900
|
+
}
|
|
16807
16901
|
const field_struct = resolve_struct_type(field.type, status);
|
|
16808
16902
|
if (field_struct && !field_struct.is_class && is_load_deep_copy_container(field_struct.name) && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
16809
16903
|
emit_var_address(status, "x0", decl_name);
|
|
@@ -16906,6 +17000,38 @@ function exit_scope_frame(status, old) {
|
|
|
16906
17000
|
if (saved_offsets) status.stack_offsets = saved_offsets;
|
|
16907
17001
|
status.register_allocations = status.register_allocations_frames?.pop();
|
|
16908
17002
|
}
|
|
17003
|
+
/**
|
|
17004
|
+
* Reclaim a freshly-built OWNED call result sitting in the result registers —
|
|
17005
|
+
* the `while` POST-STATEMENT (update) position, where the value is created and
|
|
17006
|
+
* discarded in the same expression and nothing else can have moved it:
|
|
17007
|
+
*
|
|
17008
|
+
* - `string` (non-view): the fat pair's ptr half is in x0 — free it.
|
|
17009
|
+
* - class instance: the heap pointer is in x0 — `<T>_destroy`, then free
|
|
17010
|
+
* (the pointer parks on the stack across the destroy call).
|
|
17011
|
+
* - owning value struct: the call landed the blob in its `_call_ret_N` sret
|
|
17012
|
+
* temp with the slot ADDRESS in x0 — `<T>_destroy` walks it in place; the
|
|
17013
|
+
* stack temp itself needs no free.
|
|
17014
|
+
*
|
|
17015
|
+
* Mirrors the C backend's statement-expression capture in
|
|
17016
|
+
* build_while_loop_node and the checker's bare-statement `_disc_N` hoist
|
|
17017
|
+
* (which cannot run in the update position — the update must re-evaluate per
|
|
17018
|
+
* iteration). Clears last_result_is_heap: the value was consumed here.
|
|
17019
|
+
*/
|
|
17020
|
+
function emit_discarded_owned_result_free(status, type) {
|
|
17021
|
+
const resolved_name = resolve_struct_name(type.name, type.type_args, status);
|
|
17022
|
+
const struct_type = is_struct_type$4(resolved_name, status) || is_struct_type$4(type.name, status);
|
|
17023
|
+
if (type.name === "string") emit_free(status);
|
|
17024
|
+
else if (struct_type?.is_class) {
|
|
17025
|
+
const skip = `.Lskip_upd_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
17026
|
+
emit_asm(status, `cbz x0, ${skip}\n`);
|
|
17027
|
+
emit_asm(status, `str x0, [sp, #-16]!\n`);
|
|
17028
|
+
emit_asm(status, `bl ${resolved_name}_destroy\n`);
|
|
17029
|
+
emit_asm(status, `ldr x0, [sp], #16\n`);
|
|
17030
|
+
emit_free(status);
|
|
17031
|
+
emit_asm(status, `${skip}:\n`);
|
|
17032
|
+
} else if (struct_type) emit_asm(status, `bl ${resolved_name}_destroy\n`);
|
|
17033
|
+
status.last_result_is_heap = false;
|
|
17034
|
+
}
|
|
16909
17035
|
/** Every declaration frame a `return` must clean: enclosing scopes first,
|
|
16910
17036
|
* the current (innermost) frame last — matching fall-through cleanup order. */
|
|
16911
17037
|
function all_scope_frames(status) {
|
|
@@ -16940,6 +17066,12 @@ function mark_heap_string(status, name) {
|
|
|
16940
17066
|
if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
|
|
16941
17067
|
status.heap_strings.add(name);
|
|
16942
17068
|
if (status.heap_cleanup_stack?.length) status.heap_cleanup_stack[status.heap_cleanup_stack.length - 1].heap_strings.add(name);
|
|
17069
|
+
if (status.function_return_label && !status.mv_owns_flag?.has(name)) {
|
|
17070
|
+
const flag_offset = allocate_stack_space(status, 8, 8);
|
|
17071
|
+
emit_asm(status, `str xzr, [x29, #${flag_offset}]\n`);
|
|
17072
|
+
if (!status.mv_owns_flag) status.mv_owns_flag = /* @__PURE__ */ new Map();
|
|
17073
|
+
status.mv_owns_flag.set(name, flag_offset);
|
|
17074
|
+
}
|
|
16943
17075
|
}
|
|
16944
17076
|
function anchor_heap_pointer(status, var_name, frame_index, is_nullable) {
|
|
16945
17077
|
const offset = allocate_stack_space(status, 8, 8);
|
|
@@ -17254,6 +17386,135 @@ function release_records_for_scope_exit(status, decl, moved) {
|
|
|
17254
17386
|
}
|
|
17255
17387
|
release_heap_string_fields(status, decl.name, decl.type.name, decl.type.is_nullable);
|
|
17256
17388
|
}
|
|
17389
|
+
/**
|
|
17390
|
+
* Scope-exit / return cleanup for a declaration that the build-time `moved`
|
|
17391
|
+
* set says was moved out. For an owning `mv` value-struct local the move may
|
|
17392
|
+
* be CONDITIONAL (inside a branch/loop): the local still owns its value on the
|
|
17393
|
+
* path that skips the move, so the destroy is guarded on the local's runtime
|
|
17394
|
+
* "moved" flag (0 = owns, 1 = moved out). On the not-moved path the struct's
|
|
17395
|
+
* string fields (the `#init`-strdup'd default included) are freed; on the
|
|
17396
|
+
* moved path the callee already reclaimed them and the guard skips. Without a
|
|
17397
|
+
* flag (params, temporaries, non-owning structs) the records are released as
|
|
17398
|
+
* before.
|
|
17399
|
+
*/
|
|
17400
|
+
function emit_moved_local_cleanup(status, decl) {
|
|
17401
|
+
const owns_flag = status.mv_owns_flag?.get(decl.name);
|
|
17402
|
+
if (owns_flag !== void 0 && decl.type.name === "string" && !decl.type.is_array && status.heap_strings?.has(decl.name)) {
|
|
17403
|
+
const skip = `.Lskip_mvowns_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
17404
|
+
emit_asm(status, `ldr x9, [x29, #${owns_flag}]\n`);
|
|
17405
|
+
emit_asm(status, `cbnz x9, ${skip}\n`);
|
|
17406
|
+
emit_var_load(status, "x0", decl.name, 8);
|
|
17407
|
+
emit_free(status);
|
|
17408
|
+
emit_asm(status, `${skip}:\n`);
|
|
17409
|
+
return;
|
|
17410
|
+
}
|
|
17411
|
+
const resolved = resolve_decl_struct(decl, status);
|
|
17412
|
+
if (owns_flag !== void 0 && resolved?.struct_type.is_owning === true && !resolved.struct_type.name.startsWith("_Tuple_") && !resolved.struct_type.name.startsWith("_Anon_")) {
|
|
17413
|
+
const skip = `.Lskip_mvowns_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
17414
|
+
emit_asm(status, `ldr x9, [x29, #${owns_flag}]\n`);
|
|
17415
|
+
emit_asm(status, `cbnz x9, ${skip}\n`);
|
|
17416
|
+
const had = !!status.moved?.has(decl.name);
|
|
17417
|
+
status.moved?.delete(decl.name);
|
|
17418
|
+
emit_destroy_for_decl(status, decl.name, resolved.type_name, void 0, resolved.type_args, decl.type.is_nullable);
|
|
17419
|
+
if (had) status.moved?.add(decl.name);
|
|
17420
|
+
emit_asm(status, `${skip}:\n`);
|
|
17421
|
+
return;
|
|
17422
|
+
}
|
|
17423
|
+
release_heap_string_fields(status, decl.name, decl.type.name, decl.type.is_nullable);
|
|
17424
|
+
}
|
|
17425
|
+
/**
|
|
17426
|
+
* Emit the reclaim for a heap-registered ARRAY declaration: an owned
|
|
17427
|
+
* fat-string array (`heap_string_arrays` — heap or inline storage), a
|
|
17428
|
+
* class-element array (`heap_class_arrays`), or a heap `Array<T>` buffer
|
|
17429
|
+
* (`heap_array_vars`, with `heap_owned_string_arrays` marking owned
|
|
17430
|
+
* fat-string elements freed slot-by-slot before the buffer). Returns true
|
|
17431
|
+
* when the name was registered and the reclaim was emitted.
|
|
17432
|
+
*
|
|
17433
|
+
* This is the RETURN-path variant of emit_destroy_for_scope's arms: a
|
|
17434
|
+
* `return` runs its own declaration cleanup and then jumps straight to the
|
|
17435
|
+
* function's return label, so the scope-exit walk's copy of the reclaim
|
|
17436
|
+
* never executes on that path — a hoisted `Array<T>` literal temp (or a
|
|
17437
|
+
* call-result buffer) leaked on every return that exits its scope. The
|
|
17438
|
+
* labels are counter-suffixed: the fall-through scope-exit walk (unreachable
|
|
17439
|
+
* after a `return`, but still emitted) may carry the same decl's reclaim,
|
|
17440
|
+
* and duplicating `.Lhosa_${name}` broke the assembler.
|
|
17441
|
+
*/
|
|
17442
|
+
function emit_heap_array_decl_destroy(status, decl_name) {
|
|
17443
|
+
const label_id = status.label_counter = (status.label_counter ?? 0) + 1;
|
|
17444
|
+
if (status.heap_string_arrays?.has(decl_name)) {
|
|
17445
|
+
const registered = status.heap_string_arrays.get(decl_name);
|
|
17446
|
+
const len = Math.abs(registered);
|
|
17447
|
+
if (registered > 0) {
|
|
17448
|
+
for (let j = 0; j < len; j++) {
|
|
17449
|
+
emit_var_load(status, "x0", decl_name, 8);
|
|
17450
|
+
emit_asm(status, `ldr x0, [x0, #${8 + j * 16}]\n`);
|
|
17451
|
+
const skip = `.Lskip_hsa_${decl_name}_${label_id}_${j}`;
|
|
17452
|
+
emit_asm(status, `cbz x0, ${skip}\n`);
|
|
17453
|
+
emit_free(status);
|
|
17454
|
+
emit_asm(status, `${skip}:\n`);
|
|
17455
|
+
}
|
|
17456
|
+
emit_var_load(status, "x0", decl_name, 8);
|
|
17457
|
+
emit_free(status);
|
|
17458
|
+
} else for (let j = 0; j < len; j++) {
|
|
17459
|
+
emit_var_address(status, "x0", decl_name);
|
|
17460
|
+
emit_asm(status, `ldr x0, [x0, #${j * 16}]\n`);
|
|
17461
|
+
const skip = `.Lskip_hsa_${decl_name}_${label_id}_${j}`;
|
|
17462
|
+
emit_asm(status, `cbz x0, ${skip}\n`);
|
|
17463
|
+
emit_free(status);
|
|
17464
|
+
emit_asm(status, `${skip}:\n`);
|
|
17465
|
+
}
|
|
17466
|
+
return true;
|
|
17467
|
+
}
|
|
17468
|
+
if (status.heap_class_arrays?.has(decl_name)) {
|
|
17469
|
+
emit_asm(status, `str x19, [sp, #-16]!\n`);
|
|
17470
|
+
emit_asm(status, `str x20, [sp, #-16]!\n`);
|
|
17471
|
+
emit_var_load(status, "x0", decl_name, 8);
|
|
17472
|
+
emit_asm(status, `mov x19, x0\n`);
|
|
17473
|
+
emit_asm(status, `ldr x20, [x19]\n`);
|
|
17474
|
+
emit_asm(status, `add x19, x19, #8\n`);
|
|
17475
|
+
const skip = `.Lskip_cls_${decl_name}_${label_id}`;
|
|
17476
|
+
const label = `.Lcls_${decl_name}_${label_id}`;
|
|
17477
|
+
emit_asm(status, `${label}:\n`);
|
|
17478
|
+
emit_asm(status, `cbz x20, ${skip}\n`);
|
|
17479
|
+
emit_asm(status, `ldr x0, [x19]\n`);
|
|
17480
|
+
emit_free(status);
|
|
17481
|
+
emit_asm(status, `add x19, x19, #8\n`);
|
|
17482
|
+
emit_asm(status, `sub x20, x20, #1\n`);
|
|
17483
|
+
emit_asm(status, `cbnz x20, ${label}\n`);
|
|
17484
|
+
emit_asm(status, `${skip}:\n`);
|
|
17485
|
+
emit_var_load(status, "x0", decl_name, 8);
|
|
17486
|
+
emit_free(status);
|
|
17487
|
+
emit_asm(status, `ldr x20, [sp], #16\n`);
|
|
17488
|
+
emit_asm(status, `ldr x19, [sp], #16\n`);
|
|
17489
|
+
return true;
|
|
17490
|
+
}
|
|
17491
|
+
if (status.heap_array_vars?.has(decl_name)) {
|
|
17492
|
+
emit_asm(status, `str x19, [sp, #-16]!\n`);
|
|
17493
|
+
emit_asm(status, `str x20, [sp, #-16]!\n`);
|
|
17494
|
+
if (status.heap_owned_string_arrays?.has(decl_name)) {
|
|
17495
|
+
emit_var_load(status, "x0", decl_name, 8);
|
|
17496
|
+
emit_asm(status, `mov x19, x0\n`);
|
|
17497
|
+
emit_asm(status, `ldr x20, [x19]\n`);
|
|
17498
|
+
emit_asm(status, `add x19, x19, #8\n`);
|
|
17499
|
+
const loop = `.Lhosa_${decl_name}_${label_id}`;
|
|
17500
|
+
const done = `.Lhosa_done_${decl_name}_${label_id}`;
|
|
17501
|
+
emit_asm(status, `${loop}:\n`);
|
|
17502
|
+
emit_asm(status, `cbz x20, ${done}\n`);
|
|
17503
|
+
emit_asm(status, `ldr x0, [x19]\n`);
|
|
17504
|
+
emit_free(status);
|
|
17505
|
+
emit_asm(status, `add x19, x19, #16\n`);
|
|
17506
|
+
emit_asm(status, `sub x20, x20, #1\n`);
|
|
17507
|
+
emit_asm(status, `b ${loop}\n`);
|
|
17508
|
+
emit_asm(status, `${done}:\n`);
|
|
17509
|
+
}
|
|
17510
|
+
emit_var_load(status, "x0", decl_name, 8);
|
|
17511
|
+
emit_free(status);
|
|
17512
|
+
emit_asm(status, `ldr x20, [sp], #16\n`);
|
|
17513
|
+
emit_asm(status, `ldr x19, [sp], #16\n`);
|
|
17514
|
+
return true;
|
|
17515
|
+
}
|
|
17516
|
+
return false;
|
|
17517
|
+
}
|
|
17257
17518
|
function emit_destroy_for_decl(status, decl_name, decl_type_name, addr_offset, type_args, is_nullable) {
|
|
17258
17519
|
const moved = status.moved ?? /* @__PURE__ */ new Set();
|
|
17259
17520
|
release_records_for_scope_exit(status, {
|
|
@@ -17265,6 +17526,7 @@ function emit_destroy_for_decl(status, decl_name, decl_type_name, addr_offset, t
|
|
|
17265
17526
|
}
|
|
17266
17527
|
}, moved);
|
|
17267
17528
|
if (moved.has(decl_name)) return;
|
|
17529
|
+
if (addr_offset === void 0 && emit_heap_array_decl_destroy(status, decl_name)) return;
|
|
17268
17530
|
if (decl_type_name === "func" && status.stack_offsets?.has(decl_name)) {
|
|
17269
17531
|
const skip = `.Lclosure_free_done_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
17270
17532
|
const no_destroy = `.Lclosure_no_destroy_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
@@ -17568,7 +17830,10 @@ function emit_destroy_for_scope(status, declarations_before) {
|
|
|
17568
17830
|
for (let i = declarations_before; i < status.scoped_declarations.length; i++) {
|
|
17569
17831
|
const decl = status.scoped_declarations[i];
|
|
17570
17832
|
release_records_for_scope_exit(status, decl, moved);
|
|
17571
|
-
if (moved.has(decl.name))
|
|
17833
|
+
if (moved.has(decl.name)) {
|
|
17834
|
+
emit_moved_local_cleanup(status, decl);
|
|
17835
|
+
continue;
|
|
17836
|
+
}
|
|
17572
17837
|
const scope_enum = status.enums.find((e) => e.name === decl.type.name && e.has_associated_data);
|
|
17573
17838
|
if (scope_enum && !decl.type.is_array) {
|
|
17574
17839
|
emit_enum_payload_frees(status, scope_enum.name, decl.name);
|
|
@@ -17674,7 +17939,10 @@ function emit_destroy_for_scope(status, declarations_before) {
|
|
|
17674
17939
|
for (let i = declarations_before; i < status.scoped_declarations.length; i++) {
|
|
17675
17940
|
const decl = status.scoped_declarations[i];
|
|
17676
17941
|
release_records_for_scope_exit(status, decl, moved);
|
|
17677
|
-
if (moved.has(decl.name))
|
|
17942
|
+
if (moved.has(decl.name)) {
|
|
17943
|
+
emit_moved_local_cleanup(status, decl);
|
|
17944
|
+
continue;
|
|
17945
|
+
}
|
|
17678
17946
|
const scope_enum = status.enums.find((e) => e.name === decl.type.name && e.has_associated_data);
|
|
17679
17947
|
if (scope_enum && !decl.type.is_array) {
|
|
17680
17948
|
emit_enum_payload_frees(status, scope_enum.name, decl.name);
|
|
@@ -17799,11 +18067,31 @@ function emit_cleanup_to_loop_depth(status) {
|
|
|
17799
18067
|
}
|
|
17800
18068
|
const moved = status.moved ?? /* @__PURE__ */ new Set();
|
|
17801
18069
|
for (const entry of scope.struct_decls) {
|
|
17802
|
-
if (moved.has(entry.name))
|
|
18070
|
+
if (moved.has(entry.name)) {
|
|
18071
|
+
if (status.mv_owns_flag?.get(entry.name) !== void 0) emit_moved_local_cleanup(status, {
|
|
18072
|
+
name: entry.name,
|
|
18073
|
+
type: {
|
|
18074
|
+
name: entry.type_name,
|
|
18075
|
+
type_args: entry.type_args,
|
|
18076
|
+
is_nullable: entry.is_nullable
|
|
18077
|
+
}
|
|
18078
|
+
});
|
|
18079
|
+
continue;
|
|
18080
|
+
}
|
|
17803
18081
|
emit_destroy_for_decl(status, entry.name, entry.type_name, void 0, entry.type_args, entry.is_nullable);
|
|
17804
18082
|
}
|
|
17805
18083
|
for (const name of scope.heap_strings) {
|
|
17806
|
-
if (moved.has(name))
|
|
18084
|
+
if (moved.has(name)) {
|
|
18085
|
+
const owns_flag = status.mv_owns_flag?.get(name);
|
|
18086
|
+
if (owns_flag === void 0) continue;
|
|
18087
|
+
const skip = `.Lskip_mvowns_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
18088
|
+
emit_asm(status, `ldr x9, [x29, #${owns_flag}]\n`);
|
|
18089
|
+
emit_asm(status, `cbnz x9, ${skip}\n`);
|
|
18090
|
+
emit_var_load(status, "x0", name, 8);
|
|
18091
|
+
emit_free(status);
|
|
18092
|
+
emit_asm(status, `${skip}:\n`);
|
|
18093
|
+
continue;
|
|
18094
|
+
}
|
|
17807
18095
|
if (status.heap_string_arrays?.has(name)) {
|
|
17808
18096
|
const registered = status.heap_string_arrays.get(name);
|
|
17809
18097
|
const len = Math.abs(registered);
|
|
@@ -17838,6 +18126,22 @@ function consolidate_temp_anchors$1(status, call_node, result_type_name) {
|
|
|
17838
18126
|
if (find_anchor_slot(status, pname) === void 0) continue;
|
|
17839
18127
|
if (!status.moved) status.moved = /* @__PURE__ */ new Set();
|
|
17840
18128
|
status.moved.add(pname);
|
|
18129
|
+
mark_local_moved_flag(status, pname);
|
|
18130
|
+
}
|
|
18131
|
+
}
|
|
18132
|
+
/**
|
|
18133
|
+
* Set an owning `mv` value-struct local's runtime moved flag (1 = moved out)
|
|
18134
|
+
* if it has one. Called from every move-marking path (call args, returns,
|
|
18135
|
+
* closure captures, tuple move owners) so the flag-guarded scope-exit destroy
|
|
18136
|
+
* skips the moved path and still reclaims the not-moved path.
|
|
18137
|
+
*/
|
|
18138
|
+
function mark_local_moved_flag(status, name) {
|
|
18139
|
+
const flag = status.mv_owns_flag?.get(name);
|
|
18140
|
+
if (flag !== void 0) {
|
|
18141
|
+
emit_asm(status, `str x9, [sp, #-16]!\n`);
|
|
18142
|
+
emit_asm(status, `mov x9, #1\n`);
|
|
18143
|
+
emit_asm(status, `str x9, [x29, #${flag}]\n`);
|
|
18144
|
+
emit_asm(status, `ldr x9, [sp], #16\n`);
|
|
17841
18145
|
}
|
|
17842
18146
|
}
|
|
17843
18147
|
function mark_moved_if_struct(value, status, opts) {
|
|
@@ -17856,10 +18160,12 @@ function mark_moved_if_struct(value, status, opts) {
|
|
|
17856
18160
|
if (is_struct || is_enum_with_data || is_trait) {
|
|
17857
18161
|
if (!status.moved) status.moved = /* @__PURE__ */ new Set();
|
|
17858
18162
|
status.moved.add(var_name);
|
|
18163
|
+
mark_local_moved_flag(status, var_name);
|
|
17859
18164
|
}
|
|
17860
18165
|
if (opts?.for_return && status.heap_strings?.has(var_name)) {
|
|
17861
18166
|
if (!status.moved) status.moved = /* @__PURE__ */ new Set();
|
|
17862
18167
|
status.moved.add(var_name);
|
|
18168
|
+
mark_local_moved_flag(status, var_name);
|
|
17863
18169
|
}
|
|
17864
18170
|
}
|
|
17865
18171
|
/**
|
|
@@ -20428,6 +20734,135 @@ function value_number_loops(body, ast_list, status, mutate_ast) {
|
|
|
20428
20734
|
};
|
|
20429
20735
|
}
|
|
20430
20736
|
//#endregion
|
|
20737
|
+
//#region ../src/build_aarch64/utils/box_clone.ts
|
|
20738
|
+
/**
|
|
20739
|
+
* Emit the `<Box_T>_box_clone` helper for a `Box<T>` mono — the aarch64 twin
|
|
20740
|
+
* of the C backend's ensure_box_clone (see build_c/utils/box_clone.ts for the
|
|
20741
|
+
* rationale). x0 = src wrapper (NULL-safe), returns the clone in x0: malloc +
|
|
20742
|
+
* byte copy + fresh copies of every pointee field the wrapper's destroy would
|
|
20743
|
+
* reclaim (plain strings, deep-copyable containers, nested boxes,
|
|
20744
|
+
* payload-copyable enums). Class/trait-referencing state stays SHARED — the
|
|
20745
|
+
* pre-existing bounded gap recorded in FOLLOWUP.md.
|
|
20746
|
+
*
|
|
20747
|
+
* The DEFINITION buffers into `status.box_clone_definitions` (flushed at file
|
|
20748
|
+
* scope by build.ts after the statement walk — the call sites are
|
|
20749
|
+
* mid-function-body: the specialized load_T emitters), deduped per TU via
|
|
20750
|
+
* `status.emitted_box_clones`. Returns the helper's symbol.
|
|
20751
|
+
*/
|
|
20752
|
+
function ensure_box_clone_aarch64(box_mono, status) {
|
|
20753
|
+
if (!status.emitted_box_clones) status.emitted_box_clones = /* @__PURE__ */ new Set();
|
|
20754
|
+
if (status.emitted_box_clones.has(box_mono.name)) return box_clone_fn_name(box_mono.name);
|
|
20755
|
+
status.emitted_box_clones.add(box_mono.name);
|
|
20756
|
+
const fn = box_clone_fn_name(box_mono.name);
|
|
20757
|
+
const T_SIZE = get_struct_size(box_mono.name, status);
|
|
20758
|
+
const scratch = { ...status };
|
|
20759
|
+
scratch.asm_code_state = void 0;
|
|
20760
|
+
scratch.code = "";
|
|
20761
|
+
scratch.box_clone_definitions = void 0;
|
|
20762
|
+
scratch.label_counter = status.label_counter ?? 0;
|
|
20763
|
+
const null_label = `.Lbox_clone_null_${scratch.label_counter = (scratch.label_counter ?? 0) + 1}`;
|
|
20764
|
+
emit_asm(scratch, `.p2align 2\n`);
|
|
20765
|
+
emit_asm(scratch, `${fn}:\n`);
|
|
20766
|
+
if (status.emit_mode === "system" && status.platform !== "windows") {
|
|
20767
|
+
emit_asm(scratch, `.globl _${fn}\n`);
|
|
20768
|
+
emit_asm(scratch, `_${fn} = ${fn}\n`);
|
|
20769
|
+
}
|
|
20770
|
+
emit_asm(scratch, `cbz x0, ${null_label}\n`);
|
|
20771
|
+
emit_asm(scratch, `stp x29, x30, [sp, #-16]!\n`);
|
|
20772
|
+
emit_asm(scratch, `stp x20, x21, [sp, #-16]!\n`);
|
|
20773
|
+
emit_asm(scratch, `mov x20, x0\n`);
|
|
20774
|
+
emit_asm(scratch, `mov x0, #${T_SIZE}\n`);
|
|
20775
|
+
emit_malloc(scratch);
|
|
20776
|
+
emit_asm(scratch, `mov x21, x0\n`);
|
|
20777
|
+
emit_asm(scratch, `mov x0, x21\n`);
|
|
20778
|
+
emit_asm(scratch, `mov x1, x20\n`);
|
|
20779
|
+
emit_asm(scratch, `mov x2, #${T_SIZE}\n`);
|
|
20780
|
+
emit_asm(scratch, `bl _memcpy\n`);
|
|
20781
|
+
const pointee_type = box_pointee_type(box_mono);
|
|
20782
|
+
if (pointee_type) emit_pointee_owned_copies_asm(scratch, pointee_type, "x21", get_field_offset(box_mono.name, "__value", status), /* @__PURE__ */ new Set());
|
|
20783
|
+
emit_asm(scratch, `mov x0, x21\n`);
|
|
20784
|
+
emit_asm(scratch, `ldp x20, x21, [sp], #16\n`);
|
|
20785
|
+
emit_asm(scratch, `ldp x29, x30, [sp], #16\n`);
|
|
20786
|
+
emit_asm(scratch, `ret\n`);
|
|
20787
|
+
emit_asm(scratch, `${null_label}:\n`);
|
|
20788
|
+
emit_asm(scratch, `ret\n`);
|
|
20789
|
+
status.label_counter = scratch.label_counter;
|
|
20790
|
+
status.box_clone_definitions = (status.box_clone_definitions ?? "") + (scratch.box_clone_definitions ?? "") + scratch.code;
|
|
20791
|
+
return fn;
|
|
20792
|
+
}
|
|
20793
|
+
/**
|
|
20794
|
+
* Give the wrapper clone at `[base + acc]` fresh copies of every field its
|
|
20795
|
+
* destroy would free (x21 must stay valid: every arm clobbers only
|
|
20796
|
+
* caller-saved registers). Recurses through nested value-struct fields; a
|
|
20797
|
+
* nested Box field clones through its own helper.
|
|
20798
|
+
*/
|
|
20799
|
+
function emit_pointee_owned_copies_asm(scratch, type, base, acc, visiting) {
|
|
20800
|
+
if (!type.name || type.is_ref || type.is_view || type.is_array || type.is_nullable) return;
|
|
20801
|
+
if (type.name === "string") {
|
|
20802
|
+
emit_asm(scratch, `ldr x0, [${base}, #${acc}]\n`);
|
|
20803
|
+
const skip = `.Lbcs_${scratch.label_counter = (scratch.label_counter ?? 0) + 1}`;
|
|
20804
|
+
emit_asm(scratch, `cbz x0, ${skip}\n`);
|
|
20805
|
+
emit_strdup(scratch);
|
|
20806
|
+
emit_asm(scratch, `str x0, [${base}, #${acc}]\n`);
|
|
20807
|
+
emit_asm(scratch, `${skip}:\n`);
|
|
20808
|
+
return;
|
|
20809
|
+
}
|
|
20810
|
+
const field_enum = scratch.enums.find((e) => e.name === type.name && e.has_associated_data);
|
|
20811
|
+
if (field_enum) {
|
|
20812
|
+
if (enum_copy_is_deep$1(field_enum, scratch)) emit_enum_payload_strdups_at(scratch, field_enum.name, base, acc);
|
|
20813
|
+
return;
|
|
20814
|
+
}
|
|
20815
|
+
const pointee = resolve_struct_type(type, scratch);
|
|
20816
|
+
if (!pointee || pointee.is_class) return;
|
|
20817
|
+
if (visiting.has(pointee.name)) return;
|
|
20818
|
+
visiting.add(pointee.name);
|
|
20819
|
+
const strings_are_heap = box_pointee_strings_are_heap(pointee, scratch);
|
|
20820
|
+
for (const field of clone_fields$1(pointee, scratch)) {
|
|
20821
|
+
const offset = acc + get_field_offset_of_fields(clone_fields$1(pointee, scratch), field.name, scratch);
|
|
20822
|
+
if (field.type.is_ref || field.type.is_view || field.type.is_array || field.type.is_nullable) continue;
|
|
20823
|
+
const nested_box = box_field_mono(field.type, scratch);
|
|
20824
|
+
if (nested_box) {
|
|
20825
|
+
const nested_fn = ensure_box_clone_aarch64(nested_box, scratch);
|
|
20826
|
+
emit_asm(scratch, `ldr x0, [${base}, #${offset}]\n`);
|
|
20827
|
+
const skip = `.Lbcc_${scratch.label_counter = (scratch.label_counter ?? 0) + 1}`;
|
|
20828
|
+
emit_asm(scratch, `cbz x0, ${skip}\n`);
|
|
20829
|
+
emit_asm(scratch, `bl ${nested_fn}\n`);
|
|
20830
|
+
emit_asm(scratch, `str x0, [${base}, #${offset}]\n`);
|
|
20831
|
+
emit_asm(scratch, `${skip}:\n`);
|
|
20832
|
+
continue;
|
|
20833
|
+
}
|
|
20834
|
+
if (field.type.name === "string" && strings_are_heap) {
|
|
20835
|
+
emit_asm(scratch, `ldr x0, [${base}, #${offset}]\n`);
|
|
20836
|
+
const skip = `.Lbcs_${scratch.label_counter = (scratch.label_counter ?? 0) + 1}`;
|
|
20837
|
+
emit_asm(scratch, `cbz x0, ${skip}\n`);
|
|
20838
|
+
emit_strdup(scratch);
|
|
20839
|
+
emit_asm(scratch, `str x0, [${base}, #${offset}]\n`);
|
|
20840
|
+
emit_asm(scratch, `${skip}:\n`);
|
|
20841
|
+
continue;
|
|
20842
|
+
}
|
|
20843
|
+
const field_struct = resolve_struct_type(field.type, scratch);
|
|
20844
|
+
if (field_struct && !field_struct.is_class && is_load_deep_copy_container(field_struct.name) && struct_needs_destroy(field_struct, scratch) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
20845
|
+
emit_asm(scratch, `add x8, ${base}, #${offset}\n`);
|
|
20846
|
+
emit_asm(scratch, `add x0, ${base}, #${offset}\n`);
|
|
20847
|
+
emit_asm(scratch, `bl ${field_struct.name}_copy\n`);
|
|
20848
|
+
continue;
|
|
20849
|
+
}
|
|
20850
|
+
const nested_enum = scratch.enums.find((e) => e.name === field.type.name && e.has_associated_data);
|
|
20851
|
+
if (nested_enum && enum_copy_is_deep$1(nested_enum, scratch)) {
|
|
20852
|
+
emit_enum_payload_strdups_at(scratch, nested_enum.name, base, offset);
|
|
20853
|
+
continue;
|
|
20854
|
+
}
|
|
20855
|
+
emit_pointee_owned_copies_asm(scratch, field.type, base, offset, visiting);
|
|
20856
|
+
}
|
|
20857
|
+
}
|
|
20858
|
+
/** The fields the pointee's destroy reclaims: an OWNING struct's
|
|
20859
|
+
* trait-declared fields are uniformly heap (the ctor strdup'd their
|
|
20860
|
+
* defaults) and join the walk, mirroring emit_field_destroys'
|
|
20861
|
+
* layout_fields merge. */
|
|
20862
|
+
function clone_fields$1(pointee, status) {
|
|
20863
|
+
return pointee.is_class || owns_strings_uniformly(pointee) ? layout_fields(pointee, status) : pointee.fields;
|
|
20864
|
+
}
|
|
20865
|
+
//#endregion
|
|
20431
20866
|
//#region ../src/build_aarch64/utils/owning_buffer_specialize.ts
|
|
20432
20867
|
/**
|
|
20433
20868
|
* Detect whether a monomorphized struct is a `Buffer_<T>` whose element type
|
|
@@ -20635,6 +21070,14 @@ function collect_transfer_fields(elem, status, base_offset = 0) {
|
|
|
20635
21070
|
let offset = 8;
|
|
20636
21071
|
for (const field of elem.fields) {
|
|
20637
21072
|
const size = get_type_size(field.type, status);
|
|
21073
|
+
if (box_field_mono(field.type, status)) {
|
|
21074
|
+
result.push({
|
|
21075
|
+
offset: base_offset + offset,
|
|
21076
|
+
size
|
|
21077
|
+
});
|
|
21078
|
+
offset += size;
|
|
21079
|
+
continue;
|
|
21080
|
+
}
|
|
20638
21081
|
if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable) {
|
|
20639
21082
|
const field_struct = resolve_struct_type(field.type, status);
|
|
20640
21083
|
if (field_struct && !field_struct.is_class) {
|
|
@@ -20680,6 +21123,16 @@ function emit_load_deep_copy_fields$1(elem, status, sret, slot) {
|
|
|
20680
21123
|
continue;
|
|
20681
21124
|
}
|
|
20682
21125
|
if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable && field.type.name !== "string") {
|
|
21126
|
+
const box_mono = box_field_mono(field.type, status);
|
|
21127
|
+
if (box_mono) {
|
|
21128
|
+
const clone_fn = ensure_box_clone_aarch64(box_mono, status);
|
|
21129
|
+
emit_asm(status, `add x0, ${sret}, #${offset}\n`);
|
|
21130
|
+
emit_asm(status, `ldr x0, [x0]\n`);
|
|
21131
|
+
emit_asm(status, `bl ${clone_fn}\n`);
|
|
21132
|
+
emit_asm(status, `str x0, [${sret}, #${offset}]\n`);
|
|
21133
|
+
offset += size;
|
|
21134
|
+
continue;
|
|
21135
|
+
}
|
|
20683
21136
|
const field_struct = resolve_struct_type(field.type, status);
|
|
20684
21137
|
if (field_struct && !field_struct.is_class && is_load_deep_copy_container(field_struct.name) && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
20685
21138
|
emit_asm(status, `add x8, ${sret}, #${offset}\n`);
|
|
@@ -22886,6 +23339,8 @@ function build_function_node$1(node, status) {
|
|
|
22886
23339
|
status.trait_class_frames = void 0;
|
|
22887
23340
|
const old_moved = status.moved;
|
|
22888
23341
|
status.moved = void 0;
|
|
23342
|
+
const old_mv_owns_flag = status.mv_owns_flag;
|
|
23343
|
+
status.mv_owns_flag = void 0;
|
|
22889
23344
|
status.access_pins = void 0;
|
|
22890
23345
|
status.forwarded_param_inits = void 0;
|
|
22891
23346
|
const param_regs = [
|
|
@@ -23453,7 +23908,7 @@ function build_function_node$1(node, status) {
|
|
|
23453
23908
|
let return_save;
|
|
23454
23909
|
if (need_guard || need_save) {
|
|
23455
23910
|
return_save = allocate_stack_space(status, returns_fat_pair ? 16 : 8);
|
|
23456
|
-
if (returns_fat_pair)
|
|
23911
|
+
if (returns_fat_pair) emit_return_pair_save(status, return_save);
|
|
23457
23912
|
else emit_asm(status, `str x0, [x29, #${return_save}]\n`);
|
|
23458
23913
|
}
|
|
23459
23914
|
for (const [name, info] of moved_param_save_slots) {
|
|
@@ -23481,7 +23936,7 @@ function build_function_node$1(node, status) {
|
|
|
23481
23936
|
if (need_guard) emit_asm(status, `${keep_prefix}_${name}:\n`);
|
|
23482
23937
|
}
|
|
23483
23938
|
if (need_guard || need_save) {
|
|
23484
|
-
if (returns_fat_pair)
|
|
23939
|
+
if (returns_fat_pair) emit_return_pair_restore(status, return_save);
|
|
23485
23940
|
else emit_asm(status, `ldr x0, [x29, #${return_save}]\n`);
|
|
23486
23941
|
}
|
|
23487
23942
|
}
|
|
@@ -23520,6 +23975,7 @@ function build_function_node$1(node, status) {
|
|
|
23520
23975
|
}
|
|
23521
23976
|
status.scoped_declarations = old_scoped_declarations;
|
|
23522
23977
|
status.moved = old_moved;
|
|
23978
|
+
status.mv_owns_flag = old_mv_owns_flag;
|
|
23523
23979
|
status.heap_string_fields = old_heap_string_fields;
|
|
23524
23980
|
status.heap_strings = old_heap_strings;
|
|
23525
23981
|
status.heap_string_arrays = old_heap_string_arrays;
|
|
@@ -23797,6 +24253,8 @@ function build_destroy_function(node, func, status) {
|
|
|
23797
24253
|
status.trait_class_frames = void 0;
|
|
23798
24254
|
const old_moved = status.moved;
|
|
23799
24255
|
status.moved = void 0;
|
|
24256
|
+
const old_mv_owns_flag = status.mv_owns_flag;
|
|
24257
|
+
status.mv_owns_flag = void 0;
|
|
23800
24258
|
const old_heap_string_arrays = status.heap_string_arrays;
|
|
23801
24259
|
status.heap_string_arrays = void 0;
|
|
23802
24260
|
const old_heap_class_arrays = status.heap_class_arrays;
|
|
@@ -23874,6 +24332,7 @@ function build_destroy_function(node, func, status) {
|
|
|
23874
24332
|
status.heap_strings = old_heap_strings;
|
|
23875
24333
|
status.trait_class_frames = old_trait_class_frames;
|
|
23876
24334
|
status.moved = old_moved;
|
|
24335
|
+
status.mv_owns_flag = old_mv_owns_flag;
|
|
23877
24336
|
status.heap_string_arrays = old_heap_string_arrays;
|
|
23878
24337
|
status.heap_class_arrays = old_heap_class_arrays;
|
|
23879
24338
|
status.heap_array_vars = old_heap_array_vars;
|
|
@@ -24230,6 +24689,8 @@ function build_custom_init_function(node, func, status) {
|
|
|
24230
24689
|
status.trait_class_frames = void 0;
|
|
24231
24690
|
const old_moved = status.moved;
|
|
24232
24691
|
status.moved = void 0;
|
|
24692
|
+
const old_mv_owns_flag = status.mv_owns_flag;
|
|
24693
|
+
status.mv_owns_flag = void 0;
|
|
24233
24694
|
const old_heap_string_arrays = status.heap_string_arrays;
|
|
24234
24695
|
status.heap_string_arrays = void 0;
|
|
24235
24696
|
const old_heap_class_arrays = status.heap_class_arrays;
|
|
@@ -24547,6 +25008,7 @@ function build_custom_init_function(node, func, status) {
|
|
|
24547
25008
|
status.heap_strings = old_heap_strings;
|
|
24548
25009
|
status.trait_class_frames = old_trait_class_frames;
|
|
24549
25010
|
status.moved = old_moved;
|
|
25011
|
+
status.mv_owns_flag = old_mv_owns_flag;
|
|
24550
25012
|
status.heap_string_arrays = old_heap_string_arrays;
|
|
24551
25013
|
status.heap_class_arrays = old_heap_class_arrays;
|
|
24552
25014
|
status.heap_array_vars = old_heap_array_vars;
|
|
@@ -24578,6 +25040,8 @@ function build_struct_functions$1(node, status) {
|
|
|
24578
25040
|
status.trait_class_frames = void 0;
|
|
24579
25041
|
const old_moved = status.moved;
|
|
24580
25042
|
status.moved = void 0;
|
|
25043
|
+
const old_mv_owns_flag = status.mv_owns_flag;
|
|
25044
|
+
status.mv_owns_flag = void 0;
|
|
24581
25045
|
const old_heap_string_arrays = status.heap_string_arrays;
|
|
24582
25046
|
status.heap_string_arrays = void 0;
|
|
24583
25047
|
const old_heap_class_arrays = status.heap_class_arrays;
|
|
@@ -24941,7 +25405,7 @@ function build_struct_functions$1(node, status) {
|
|
|
24941
25405
|
let return_save;
|
|
24942
25406
|
if (ret_is_class || need_save) {
|
|
24943
25407
|
return_save = allocate_stack_space(status, returns_fat_pair ? 16 : 8);
|
|
24944
|
-
if (returns_fat_pair)
|
|
25408
|
+
if (returns_fat_pair) emit_return_pair_save(status, return_save);
|
|
24945
25409
|
else emit_asm(status, `str x0, [x29, #${return_save}]\n`);
|
|
24946
25410
|
}
|
|
24947
25411
|
for (const [name, info] of moved_param_save_slots) {
|
|
@@ -24969,7 +25433,7 @@ function build_struct_functions$1(node, status) {
|
|
|
24969
25433
|
if (ret_is_class) emit_asm(status, `${keep_prefix}_${name}:\n`);
|
|
24970
25434
|
}
|
|
24971
25435
|
if (ret_is_class || need_save) {
|
|
24972
|
-
if (returns_fat_pair)
|
|
25436
|
+
if (returns_fat_pair) emit_return_pair_restore(status, return_save);
|
|
24973
25437
|
else emit_asm(status, `ldr x0, [x29, #${return_save}]\n`);
|
|
24974
25438
|
}
|
|
24975
25439
|
}
|
|
@@ -24992,6 +25456,7 @@ function build_struct_functions$1(node, status) {
|
|
|
24992
25456
|
status.heap_strings = old_heap_strings;
|
|
24993
25457
|
status.trait_class_frames = old_trait_class_frames;
|
|
24994
25458
|
status.moved = old_moved;
|
|
25459
|
+
status.mv_owns_flag = old_mv_owns_flag;
|
|
24995
25460
|
status.function_return_type = old_function_return_type;
|
|
24996
25461
|
status.heap_string_arrays = old_heap_string_arrays;
|
|
24997
25462
|
status.heap_class_arrays = old_heap_class_arrays;
|
|
@@ -28159,7 +28624,7 @@ function build_function_call_node$1(node, status) {
|
|
|
28159
28624
|
}
|
|
28160
28625
|
}
|
|
28161
28626
|
const is_struct = status.structs.find((s) => s.name === node.name && !s.is_simple_type);
|
|
28162
|
-
mark_tuple_literal_move_owners(node, status);
|
|
28627
|
+
mark_tuple_literal_move_owners(node, status, (name) => mark_local_moved_flag(status, name));
|
|
28163
28628
|
const func_name = is_struct ? node.mangled_name ?? `${node.name}_init` : emission_label(node.resolved_function ?? node);
|
|
28164
28629
|
if (is_struct?.is_class) {
|
|
28165
28630
|
const inner = box_ctor_direct_ctor_arg(node, status.structs);
|
|
@@ -28860,7 +29325,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
28860
29325
|
const finalized = status.moved ?? /* @__PURE__ */ new Set();
|
|
28861
29326
|
for (const decl of all_scope_frames(status).flat()) {
|
|
28862
29327
|
if (finalized.has(decl.name)) {
|
|
28863
|
-
|
|
29328
|
+
emit_moved_local_cleanup(status, decl);
|
|
28864
29329
|
continue;
|
|
28865
29330
|
}
|
|
28866
29331
|
if (is_field_struct_borrow(decl)) continue;
|
|
@@ -28880,7 +29345,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
28880
29345
|
const finalized = status.moved ?? /* @__PURE__ */ new Set();
|
|
28881
29346
|
for (const decl of all_scope_frames(status).flat()) {
|
|
28882
29347
|
if (finalized.has(decl.name)) {
|
|
28883
|
-
|
|
29348
|
+
emit_moved_local_cleanup(status, decl);
|
|
28884
29349
|
continue;
|
|
28885
29350
|
}
|
|
28886
29351
|
if (is_field_struct_borrow(decl)) continue;
|
|
@@ -29283,7 +29748,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
29283
29748
|
emit_nursery_joins_on_return_a64(status);
|
|
29284
29749
|
for (const decl of all_scope_frames(status).flat()) {
|
|
29285
29750
|
if (finalized.has(decl.name)) {
|
|
29286
|
-
|
|
29751
|
+
emit_moved_local_cleanup(status, decl);
|
|
29287
29752
|
continue;
|
|
29288
29753
|
}
|
|
29289
29754
|
if (is_field_struct_borrow(decl)) continue;
|
|
@@ -29342,6 +29807,42 @@ function build_switch_node$1(node, status, nir) {
|
|
|
29342
29807
|
exit_scope_frame(status, old_scoped_declarations);
|
|
29343
29808
|
}
|
|
29344
29809
|
//#endregion
|
|
29810
|
+
//#region ../src/build_common/discarded_update_result.ts
|
|
29811
|
+
/**
|
|
29812
|
+
* The owned result type of a `while` POST-STATEMENT (update) expression whose
|
|
29813
|
+
* discarded value must be reclaimed once per iteration.
|
|
29814
|
+
*
|
|
29815
|
+
* This is the update-position twin of the checker's bare-statement `_disc_N`
|
|
29816
|
+
* hoist (`hoist_discarded_class_results`) — but the hoist itself cannot run
|
|
29817
|
+
* here: the update re-evaluates on every iteration, so wrapping it in a
|
|
29818
|
+
* one-shot declaration would run the call before the first condition check
|
|
29819
|
+
* and change loop semantics. Instead, each backend captures the fresh result
|
|
29820
|
+
* into a per-iteration temp and frees it in place.
|
|
29821
|
+
*
|
|
29822
|
+
* Predicate mirrors the hoist's: a free-function call (`func_call`) whose
|
|
29823
|
+
* result type is a class or an owning value struct (`string` included), or a
|
|
29824
|
+
* method call whose callee stamps `owned_return` (`mv out T`). A view / ref /
|
|
29825
|
+
* array result is a borrow — never an owner — and a nullable VALUE struct's
|
|
29826
|
+
* sret-plus-flag shape has no in-place free, so both stay out.
|
|
29827
|
+
*/
|
|
29828
|
+
function discarded_owned_result_type(node, table) {
|
|
29829
|
+
let expr = node;
|
|
29830
|
+
while (expr.node_type === "grouped") expr = expr.value;
|
|
29831
|
+
const is_call = expr.node_type === "func_call";
|
|
29832
|
+
const is_method = expr.node_type === "access" && expr.access.node_type === "access_func";
|
|
29833
|
+
if (!is_call && !is_method) return void 0;
|
|
29834
|
+
const access_func = is_method ? expr.access : null;
|
|
29835
|
+
if (access_func && !access_func.owned_return) return void 0;
|
|
29836
|
+
const t = is_call ? expr.type : access_func.type;
|
|
29837
|
+
if (!t?.name || t.is_view || t.is_ref || t.is_array) return void 0;
|
|
29838
|
+
if (t.name === "string") return t;
|
|
29839
|
+
const struct = resolve_struct_type(t, table);
|
|
29840
|
+
if (!struct) return void 0;
|
|
29841
|
+
if (struct.is_class) return t;
|
|
29842
|
+
if (t.is_nullable) return void 0;
|
|
29843
|
+
if (struct_needs_destroy(struct, table)) return t;
|
|
29844
|
+
}
|
|
29845
|
+
//#endregion
|
|
29345
29846
|
//#region ../src/build_aarch64/build_while_loop_node.ts
|
|
29346
29847
|
let label_counter = 0;
|
|
29347
29848
|
function reset_label_counter() {
|
|
@@ -29456,7 +29957,12 @@ function build_while_loop_node$1(node, status, nir, vector, unroll) {
|
|
|
29456
29957
|
build_block_with_cursor$1(node, nir?.body, status);
|
|
29457
29958
|
if (node.update) {
|
|
29458
29959
|
emit_asm(status, `${continue_label}:\n`);
|
|
29960
|
+
const owned_update = discarded_owned_result_type(node.update, status);
|
|
29459
29961
|
build_node(node.update, status);
|
|
29962
|
+
if (owned_update) {
|
|
29963
|
+
ensure_newline(status);
|
|
29964
|
+
emit_discarded_owned_result_free(status, owned_update);
|
|
29965
|
+
}
|
|
29460
29966
|
ensure_newline(status);
|
|
29461
29967
|
}
|
|
29462
29968
|
emit_asm(status, `b ${start_label}\n`);
|
|
@@ -32136,6 +32642,102 @@ function end_code_scratch(status, saved) {
|
|
|
32136
32642
|
return scratch;
|
|
32137
32643
|
}
|
|
32138
32644
|
//#endregion
|
|
32645
|
+
//#region ../src/build_c/utils/box_clone.ts
|
|
32646
|
+
/**
|
|
32647
|
+
* Emit the `<Box_T>_box_clone` helper for a `Box<T>` mono (STRUCT_PLAN_3
|
|
32648
|
+
* Box-in-container fix): malloc a fresh wrapper, byte-copy the pointee, then
|
|
32649
|
+
* give the clone independent copies of every field the wrapper's destroy
|
|
32650
|
+
* would reclaim (the same field set `build_auto_destroy` walks — plain
|
|
32651
|
+
* strings, deep-copyable containers, nested boxes, payload-copyable enums).
|
|
32652
|
+
* Class/trait-referencing state (a raw class field, an enum with reference
|
|
32653
|
+
* payloads) cannot be duplicated and stays SHARED — the pre-existing bounded
|
|
32654
|
+
* gap, recorded in FOLLOWUP.md.
|
|
32655
|
+
*
|
|
32656
|
+
* The DEFINITION buffers into `status.box_clone_definitions` (flushed at file
|
|
32657
|
+
* scope by build_root_node — C may not nest a function definition inside the
|
|
32658
|
+
* struct-method body being emitted) with the prototype in headers, so
|
|
32659
|
+
* definition order never matters. Deduped per TU via
|
|
32660
|
+
* `status.emitted_box_clones`. Returns the helper's symbol.
|
|
32661
|
+
*/
|
|
32662
|
+
function ensure_box_clone(box_mono, status) {
|
|
32663
|
+
if (!status.emitted_box_clones) status.emitted_box_clones = /* @__PURE__ */ new Set();
|
|
32664
|
+
if (status.emitted_box_clones.has(box_mono.name)) return box_clone_fn_name(box_mono.name);
|
|
32665
|
+
status.emitted_box_clones.add(box_mono.name);
|
|
32666
|
+
const fn = box_clone_fn_name(box_mono.name);
|
|
32667
|
+
status.headers += `struct ${box_mono.name} *${fn}(struct ${box_mono.name} *);\n`;
|
|
32668
|
+
const out = [];
|
|
32669
|
+
out.push(`struct ${box_mono.name} *${fn}(struct ${box_mono.name} *src)\n{\n`);
|
|
32670
|
+
out.push(`if (!src) return 0;\n`);
|
|
32671
|
+
out.push(`struct ${box_mono.name} *_dst = (struct ${box_mono.name} *)malloc(sizeof(struct ${box_mono.name}));\n`);
|
|
32672
|
+
out.push(`*_dst = *src;\n`);
|
|
32673
|
+
const pointee_type = box_pointee_type(box_mono);
|
|
32674
|
+
if (pointee_type) emit_pointee_owned_copies("_dst->__value", pointee_type, status, /* @__PURE__ */ new Set(), out);
|
|
32675
|
+
out.push(`return _dst;\n`);
|
|
32676
|
+
out.push(`}\n`);
|
|
32677
|
+
status.box_clone_definitions = (status.box_clone_definitions ?? "") + out.join("");
|
|
32678
|
+
return fn;
|
|
32679
|
+
}
|
|
32680
|
+
/**
|
|
32681
|
+
* Give the wrapper clone at C lvalue `lv` fresh copies of every field its
|
|
32682
|
+
* destroy would free. Recurses through nested value-struct fields; a nested
|
|
32683
|
+
* Box field clones through its own helper (the same emit point, deduped).
|
|
32684
|
+
*/
|
|
32685
|
+
function emit_pointee_owned_copies(lv, type, status, visiting, out) {
|
|
32686
|
+
if (!type.name || type.is_ref || type.is_view || type.is_array || type.is_nullable) return;
|
|
32687
|
+
if (type.name === "string") {
|
|
32688
|
+
out.push(`${lv} = ${lv}.ptr ? nomen_str_dup(${lv}) : ${lv};\n`);
|
|
32689
|
+
return;
|
|
32690
|
+
}
|
|
32691
|
+
const field_enum = status.enums.find((e) => e.name === type.name && e.has_associated_data);
|
|
32692
|
+
if (field_enum) {
|
|
32693
|
+
if (enum_copy_is_deep$1(field_enum, status)) out.push(`${lv} = ${field_enum.name}_copy(${lv});\n`);
|
|
32694
|
+
return;
|
|
32695
|
+
}
|
|
32696
|
+
const pointee = resolve_struct_type(type, status);
|
|
32697
|
+
if (!pointee || pointee.is_class) return;
|
|
32698
|
+
if (visiting.has(pointee.name)) return;
|
|
32699
|
+
visiting.add(pointee.name);
|
|
32700
|
+
const strings_are_heap = box_pointee_strings_are_heap(pointee, status);
|
|
32701
|
+
for (const field of clone_fields(pointee, status)) {
|
|
32702
|
+
if (field.type.is_ref || field.type.is_view || field.type.is_array || field.type.is_nullable) continue;
|
|
32703
|
+
const flv = `${lv}.${field.name}`;
|
|
32704
|
+
const nested_box = box_field_mono(field.type, status);
|
|
32705
|
+
if (nested_box) {
|
|
32706
|
+
const nested_fn = ensure_box_clone(nested_box, status);
|
|
32707
|
+
out.push(`${flv} = ${nested_fn}(${flv});\n`);
|
|
32708
|
+
continue;
|
|
32709
|
+
}
|
|
32710
|
+
if (field.type.name === "string" && !field.type.is_array && strings_are_heap) {
|
|
32711
|
+
out.push(`${flv} = ${flv}.ptr ? nomen_str_dup(${flv}) : ${flv};\n`);
|
|
32712
|
+
continue;
|
|
32713
|
+
}
|
|
32714
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
32715
|
+
if (field_struct && !field_struct.is_class && is_load_deep_copy_container(field_struct.name) && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
32716
|
+
out.push(`${flv} = ${field_struct.name}_copy(&${flv});\n`);
|
|
32717
|
+
continue;
|
|
32718
|
+
}
|
|
32719
|
+
const nested_enum = status.enums.find((e) => e.name === field.type.name && e.has_associated_data);
|
|
32720
|
+
if (nested_enum && enum_copy_is_deep$1(nested_enum, status)) {
|
|
32721
|
+
out.push(`${flv} = ${nested_enum.name}_copy(${flv});\n`);
|
|
32722
|
+
continue;
|
|
32723
|
+
}
|
|
32724
|
+
emit_pointee_owned_copies(flv, field.type, status, visiting, out);
|
|
32725
|
+
}
|
|
32726
|
+
}
|
|
32727
|
+
/** The fields the pointee's destroy reclaims: an OWNING struct's
|
|
32728
|
+
* trait-declared fields are uniformly heap (the ctor strdup'd their
|
|
32729
|
+
* defaults) and join the walk, mirroring `class_destroy_fields`. */
|
|
32730
|
+
function clone_fields(pointee, status) {
|
|
32731
|
+
if (!owns_strings_uniformly(pointee) || !pointee.traits.length) return pointee.fields;
|
|
32732
|
+
const extra = [];
|
|
32733
|
+
for (const trait_name of pointee.traits) {
|
|
32734
|
+
const trait = status.traits.find((t) => t.name === trait_name);
|
|
32735
|
+
if (!trait) continue;
|
|
32736
|
+
for (const f of trait.fields) if (!pointee.fields.find((nf) => nf.name === f.name)) extra.push(f);
|
|
32737
|
+
}
|
|
32738
|
+
return extra.length ? [...pointee.fields, ...extra] : pointee.fields;
|
|
32739
|
+
}
|
|
32740
|
+
//#endregion
|
|
32139
32741
|
//#region ../src/build_c/utils/owning_buffer_specialize.ts
|
|
32140
32742
|
/**
|
|
32141
32743
|
* Detect whether a monomorphized struct is a `Buffer_<T>` whose element type
|
|
@@ -32214,10 +32816,14 @@ const OWNING_BUFFER_METHODS = /* @__PURE__ */ new Set([
|
|
|
32214
32816
|
function emit_transfer_owning_fields(elem, src, status, prefix = "") {
|
|
32215
32817
|
for (const field of elem.fields) {
|
|
32216
32818
|
if (field.type.is_ref || field.type.is_view) continue;
|
|
32819
|
+
const src_field = `${prefix}${src}${arrow(src)}${field.name}`;
|
|
32820
|
+
if (box_field_mono(field.type, status)) {
|
|
32821
|
+
status.code += `memset((void*)&${src_field}, 0, sizeof(${src_field}));\n`;
|
|
32822
|
+
continue;
|
|
32823
|
+
}
|
|
32217
32824
|
if (field.type.is_array || field.type.is_nullable) continue;
|
|
32218
32825
|
const field_struct = resolve_struct_type(field.type, status);
|
|
32219
32826
|
if (!field_struct || field_struct.is_class) continue;
|
|
32220
|
-
const src_field = `${prefix}${src}${arrow(src)}${field.name}`;
|
|
32221
32827
|
if (has_destroy(field_struct)) status.code += `memset((void*)&${src_field}, 0, sizeof(${src_field}));\n`;
|
|
32222
32828
|
else if (struct_needs_destroy(field_struct, status)) emit_transfer_owning_fields(field_struct, src_field, status, "");
|
|
32223
32829
|
}
|
|
@@ -32241,6 +32847,12 @@ function emit_load_deep_copy_fields(elem, dst, status) {
|
|
|
32241
32847
|
if (field.type.is_ref || field.type.is_view || field.type.is_array) continue;
|
|
32242
32848
|
if (field.type.is_nullable) continue;
|
|
32243
32849
|
if (field.type.name === "string") continue;
|
|
32850
|
+
const box_mono = box_field_mono(field.type, status);
|
|
32851
|
+
if (box_mono) {
|
|
32852
|
+
const clone_fn = ensure_box_clone(box_mono, status);
|
|
32853
|
+
status.code += `${dst}.${field.name} = ${clone_fn}(${dst}.${field.name});\n`;
|
|
32854
|
+
continue;
|
|
32855
|
+
}
|
|
32244
32856
|
const field_struct = resolve_struct_type(field.type, status);
|
|
32245
32857
|
if (field_struct && !field_struct.is_class && is_load_deep_copy_container(field_struct.name) && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
32246
32858
|
status.code += `${dst}.${field.name} = ${field_struct.name}_copy(&${dst}.${field.name});\n`;
|
|
@@ -32266,6 +32878,11 @@ function emit_load_owned_copies_destroy(status, elem, var_expr) {
|
|
|
32266
32878
|
if (field.type.is_ref || field.type.is_view || field.type.is_array) continue;
|
|
32267
32879
|
if (field.type.is_nullable) continue;
|
|
32268
32880
|
if (field.type.name === "string") continue;
|
|
32881
|
+
const box_mono = box_field_mono(field.type, status);
|
|
32882
|
+
if (box_mono) {
|
|
32883
|
+
status.code += `if (${var_expr}.${field.name}) { ${box_mono.name}_destroy(${var_expr}.${field.name}); free(${var_expr}.${field.name}); }\n`;
|
|
32884
|
+
continue;
|
|
32885
|
+
}
|
|
32269
32886
|
const field_struct = resolve_struct_type(field.type, status);
|
|
32270
32887
|
if (field_struct && !field_struct.is_class && is_load_deep_copy_container(field_struct.name) && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
32271
32888
|
status.code += `${field_struct.name}_destroy(&${var_expr}.${field.name});\n`;
|
|
@@ -32723,6 +33340,10 @@ function build_auto_free(status) {
|
|
|
32723
33340
|
* (those are scope-exit-only concerns handled by build_auto_free).
|
|
32724
33341
|
*/
|
|
32725
33342
|
function free_scoped_declarations(status, decls, persist_string_field_records = false) {
|
|
33343
|
+
if (status.pending_mv_owns_sets?.length) {
|
|
33344
|
+
status.code += status.pending_mv_owns_sets.join("\n") + "\n";
|
|
33345
|
+
status.pending_mv_owns_sets.length = 0;
|
|
33346
|
+
}
|
|
32726
33347
|
let commented = false;
|
|
32727
33348
|
if (status.heap_string_fields?.size) for (const dec of decls) {
|
|
32728
33349
|
if (dec.moved_value_struct_param) {
|
|
@@ -32824,6 +33445,7 @@ function free_scoped_declarations(status, decls, persist_string_field_records =
|
|
|
32824
33445
|
const is_heap_string_assign = !!status.heap_strings?.has(dec.name);
|
|
32825
33446
|
const dec_struct = status.structs.find((s) => s.name === dec.type.name);
|
|
32826
33447
|
const is_class_var = !!dec_struct?.is_class;
|
|
33448
|
+
const mv_owns_flag = status.c_mv_owns_flags?.get(dec.name);
|
|
32827
33449
|
const trait_class_trait = dec.trait_class_trait;
|
|
32828
33450
|
if (trait_class_trait !== void 0 && !is_destructured_field_access) {
|
|
32829
33451
|
if (!commented) {
|
|
@@ -32840,7 +33462,8 @@ function free_scoped_declarations(status, decls, persist_string_field_records =
|
|
|
32840
33462
|
status.code += "\n// Auto-free\n";
|
|
32841
33463
|
commented = true;
|
|
32842
33464
|
}
|
|
32843
|
-
status.code += `free(${cname}.ptr)
|
|
33465
|
+
if (mv_owns_flag !== void 0) status.code += `if (!${mv_owns_flag}) { free(${cname}.ptr); }\n`;
|
|
33466
|
+
else status.code += `free(${cname}.ptr);\n`;
|
|
32844
33467
|
}
|
|
32845
33468
|
if (!is_destructured_field_access && !dec.type.is_array) {
|
|
32846
33469
|
const enum_node = status.enums.find((e) => e.name === dec.type.name);
|
|
@@ -32866,7 +33489,8 @@ function free_scoped_declarations(status, decls, persist_string_field_records =
|
|
|
32866
33489
|
if (cls) {
|
|
32867
33490
|
const destroy_call = has_destroy_fn ? `${mono_cls_name}_destroy(${cname}); ` : "";
|
|
32868
33491
|
const alias_flag = status.c_alias_owns_flags?.get(dec.name);
|
|
32869
|
-
if (
|
|
33492
|
+
if (mv_owns_flag !== void 0) status.code += `if (!${mv_owns_flag}) { ${destroy_call}free(${cname}); }\n`;
|
|
33493
|
+
else if (alias_flag !== void 0) status.code += `if (${alias_flag}) { ${destroy_call}free(${cname}); }\n`;
|
|
32870
33494
|
else if (dec.type.is_nullable) status.code += `if (${cname}) { ${destroy_call}free(${cname}); }\n`;
|
|
32871
33495
|
else status.code += `${destroy_call}free(${cname});\n`;
|
|
32872
33496
|
} else status.code += `free(${cname});\n`;
|
|
@@ -32879,7 +33503,11 @@ function free_scoped_declarations(status, decls, persist_string_field_records =
|
|
|
32879
33503
|
status.code += "\n// Auto-free\n";
|
|
32880
33504
|
commented = true;
|
|
32881
33505
|
}
|
|
32882
|
-
|
|
33506
|
+
if (mv_owns_flag !== void 0) {
|
|
33507
|
+
status.code += `if (!${mv_owns_flag}) { `;
|
|
33508
|
+
emit_struct_destroys(status, struct_type, cname);
|
|
33509
|
+
status.code += `}\n`;
|
|
33510
|
+
} else emit_struct_destroys(status, struct_type, cname);
|
|
32883
33511
|
}
|
|
32884
33512
|
}
|
|
32885
33513
|
if (dec.trait_class_trait === void 0 && !!status.traits.find((t) => t.name === dec.type.name) && !is_destructured_field_access && !dec.type.is_array && dec.value) {
|
|
@@ -34441,7 +35069,12 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
34441
35069
|
release_recorded_string_fields(status, move_struct_type, lhs_name);
|
|
34442
35070
|
if (struct_needs_destroy_by_name(move_struct_type.name, status)) emit_struct_destroys(status, move_struct_type, lhs_name);
|
|
34443
35071
|
}
|
|
34444
|
-
|
|
35072
|
+
const mv_donor_name = rhs.value;
|
|
35073
|
+
const mv_donor_flag = status.c_mv_owns_flags?.get(mv_donor_name);
|
|
35074
|
+
if (!!mv_donor_flag && move_struct_type?.is_owning === true && !move_struct_type.name.startsWith("_Tuple_") && !move_struct_type.name.startsWith("_Anon_") && !node.swap) {
|
|
35075
|
+
if (!status.pending_mv_owns_sets) status.pending_mv_owns_sets = [];
|
|
35076
|
+
status.pending_mv_owns_sets.push(`${mv_donor_flag} = 1;`);
|
|
35077
|
+
} else splice_decl_from_c_scopes(status, mv_donor_name);
|
|
34445
35078
|
} else if (rhs_is_base_literal) {
|
|
34446
35079
|
const struct_type = lhs_mono_struct ?? lhs_struct;
|
|
34447
35080
|
if (struct_type) {
|
|
@@ -35298,6 +35931,19 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
35298
35931
|
if (node.type?.name === "string" && !node.type.is_array) {
|
|
35299
35932
|
if (!status.owned_string_vars) status.owned_string_vars = /* @__PURE__ */ new Set();
|
|
35300
35933
|
status.owned_string_vars.add(safe_name);
|
|
35934
|
+
if (!node.is_module_init) {
|
|
35935
|
+
const flag = `_mv_owns_${safe_name}`;
|
|
35936
|
+
status.code += `unsigned char ${flag} = 0;\n`;
|
|
35937
|
+
if (!status.c_mv_owns_flags) status.c_mv_owns_flags = /* @__PURE__ */ new Map();
|
|
35938
|
+
status.c_mv_owns_flags.set(node.name, flag);
|
|
35939
|
+
}
|
|
35940
|
+
}
|
|
35941
|
+
const mv_decl_struct = node.type?.name ? status.structs.find((s) => s.name === mono_type_name(node.type) && !s.is_simple_type && !s.is_generic) : void 0;
|
|
35942
|
+
if (mv_decl_struct?.is_owning === true && !mv_decl_struct.name.startsWith("_Tuple_") && !mv_decl_struct.name.startsWith("_Anon_") && !node.type.is_nullable && struct_needs_destroy_by_name(mv_decl_struct.name, status)) {
|
|
35943
|
+
const flag = `_mv_owns_${safe_name}`;
|
|
35944
|
+
status.code += `unsigned char ${flag} = 0;\n`;
|
|
35945
|
+
if (!status.c_mv_owns_flags) status.c_mv_owns_flags = /* @__PURE__ */ new Map();
|
|
35946
|
+
status.c_mv_owns_flags.set(node.name, flag);
|
|
35301
35947
|
}
|
|
35302
35948
|
} else {
|
|
35303
35949
|
if (!status.class_alias_vars) status.class_alias_vars = /* @__PURE__ */ new Set();
|
|
@@ -36159,16 +36805,20 @@ function build_return_node(node, status, nir_value) {
|
|
|
36159
36805
|
}
|
|
36160
36806
|
}
|
|
36161
36807
|
let returned_value_decl;
|
|
36808
|
+
let moved_string_flag;
|
|
36162
36809
|
let returned_recorded_string_fields;
|
|
36163
36810
|
if (node.value.node_type === "value") {
|
|
36164
36811
|
const value = node.value.value;
|
|
36165
36812
|
returned_value_decl = find_decl_across_scopes(value, status);
|
|
36166
|
-
|
|
36167
|
-
|
|
36168
|
-
const
|
|
36169
|
-
|
|
36170
|
-
frame.
|
|
36171
|
-
|
|
36813
|
+
moved_string_flag = returned_value_decl && returned_value_decl.type?.name === "string" && !returned_value_decl.type?.is_array && !returned_value_decl.type?.is_view ? status.c_mv_owns_flags?.get(value) : void 0;
|
|
36814
|
+
if (!moved_string_flag) {
|
|
36815
|
+
const frames = [status.scoped_declarations, ...status.c_scope_stack ?? []];
|
|
36816
|
+
for (const frame of frames) {
|
|
36817
|
+
const di = frame.indexOf(returned_value_decl);
|
|
36818
|
+
if (di !== -1) {
|
|
36819
|
+
frame.splice(di, 1);
|
|
36820
|
+
break;
|
|
36821
|
+
}
|
|
36172
36822
|
}
|
|
36173
36823
|
}
|
|
36174
36824
|
if (status.heap_string_fields?.size) {
|
|
@@ -36232,6 +36882,7 @@ function build_return_node(node, status, nir_value) {
|
|
|
36232
36882
|
status.code += `${old_return_assign} = `;
|
|
36233
36883
|
emit_return_value(node.value, nir_value, status);
|
|
36234
36884
|
status.code += `;\n`;
|
|
36885
|
+
if (moved_string_flag) status.code += `${moved_string_flag} = 1;\n`;
|
|
36235
36886
|
reclaim_all_c_scopes(status);
|
|
36236
36887
|
} else if ((status.function_return_type?.name ?? node.type?.name) === "void" && !node.type?.is_array) {
|
|
36237
36888
|
emit_allocations(node.value, status);
|
|
@@ -36379,6 +37030,7 @@ function build_return_node(node, status, nir_value) {
|
|
|
36379
37030
|
status.code += `_return_val.${field.name} = nomen_str_dup(_return_val.${field.name});\n`;
|
|
36380
37031
|
}
|
|
36381
37032
|
}
|
|
37033
|
+
if (moved_string_flag) status.code += `${moved_string_flag} = 1;\n`;
|
|
36382
37034
|
emit_nursery_joins_on_return_c(status);
|
|
36383
37035
|
reclaim_all_c_scopes(status);
|
|
36384
37036
|
status.code += `return _return_val;\n`;
|
|
@@ -36545,13 +37197,41 @@ function build_while_loop_node(node, status, nir) {
|
|
|
36545
37197
|
}
|
|
36546
37198
|
return true;
|
|
36547
37199
|
};
|
|
37200
|
+
const build_update_expr = () => {
|
|
37201
|
+
if (!node.update) return;
|
|
37202
|
+
const owned = discarded_owned_result_type(node.update, status);
|
|
37203
|
+
if (!owned) {
|
|
37204
|
+
build_node$1(node.update, status);
|
|
37205
|
+
return;
|
|
37206
|
+
}
|
|
37207
|
+
const mono = mono_type_name(owned);
|
|
37208
|
+
const struct = status.structs.find((s) => s.name === mono && !s.is_generic);
|
|
37209
|
+
const free_fn = status.audit ? "nomen_free_wrap" : "free";
|
|
37210
|
+
const tmp = `_upd_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
37211
|
+
status.code += `({ `;
|
|
37212
|
+
if (struct?.is_class) {
|
|
37213
|
+
status.code += `struct ${mono} *${tmp} = `;
|
|
37214
|
+
build_node$1(node.update, status);
|
|
37215
|
+
status.code += `; if (${tmp}) { ${mono}_destroy(${tmp}); ${free_fn}(${tmp}); }; `;
|
|
37216
|
+
} else if (mono === "string") {
|
|
37217
|
+
status.code += `nomen_string ${tmp} = `;
|
|
37218
|
+
build_node$1(node.update, status);
|
|
37219
|
+
status.code += `; ${free_fn}(${tmp}.ptr); `;
|
|
37220
|
+
} else if (struct) {
|
|
37221
|
+
status.code += `struct ${mono} ${tmp} = `;
|
|
37222
|
+
build_node$1(node.update, status);
|
|
37223
|
+
status.code += `; `;
|
|
37224
|
+
emit_struct_destroys(status, struct, tmp);
|
|
37225
|
+
}
|
|
37226
|
+
status.code += ` })`;
|
|
37227
|
+
};
|
|
36548
37228
|
if (node.update) {
|
|
36549
37229
|
status.code += `for (; `;
|
|
36550
37230
|
const has_reeval = emit_condition_reeval();
|
|
36551
37231
|
build_condition(node.condition, status);
|
|
36552
37232
|
if (has_reeval) status.code += `; })`;
|
|
36553
37233
|
status.code += `; `;
|
|
36554
|
-
|
|
37234
|
+
build_update_expr();
|
|
36555
37235
|
status.code += `) {\n`;
|
|
36556
37236
|
} else {
|
|
36557
37237
|
status.code += `while (`;
|
|
@@ -36766,6 +37446,10 @@ function with_semicolon_tail(status, node) {
|
|
|
36766
37446
|
status.code += status.pending_string_releases.join("\n") + "\n";
|
|
36767
37447
|
status.pending_string_releases.length = 0;
|
|
36768
37448
|
}
|
|
37449
|
+
if (status.pending_mv_owns_sets?.length) {
|
|
37450
|
+
status.code += status.pending_mv_owns_sets.join("\n") + "\n";
|
|
37451
|
+
status.pending_mv_owns_sets.length = 0;
|
|
37452
|
+
}
|
|
36769
37453
|
}
|
|
36770
37454
|
/**
|
|
36771
37455
|
* NIR-level EXPRESSION emission (phase 4, canonical-IR stage 2+ — the C
|
|
@@ -37788,6 +38472,8 @@ function build_function_node(node, status) {
|
|
|
37788
38472
|
status.deferred_frees = [];
|
|
37789
38473
|
const old_alias_owns_flags = status.c_alias_owns_flags;
|
|
37790
38474
|
status.c_alias_owns_flags = /* @__PURE__ */ new Map();
|
|
38475
|
+
const old_mv_owns_flags = status.c_mv_owns_flags;
|
|
38476
|
+
status.c_mv_owns_flags = /* @__PURE__ */ new Map();
|
|
37791
38477
|
const old_alias_decl_frames = status.alias_decl_frames;
|
|
37792
38478
|
status.alias_decl_frames = /* @__PURE__ */ new Map();
|
|
37793
38479
|
const old_class_alias_vars = status.class_alias_vars;
|
|
@@ -37812,6 +38498,8 @@ function build_function_node(node, status) {
|
|
|
37812
38498
|
status.stack_array_lengths = void 0;
|
|
37813
38499
|
const old_moved = status.moved;
|
|
37814
38500
|
status.moved = /* @__PURE__ */ new Set();
|
|
38501
|
+
const old_pending_mv_owns = status.pending_mv_owns_sets;
|
|
38502
|
+
status.pending_mv_owns_sets = void 0;
|
|
37815
38503
|
const old_borrow_only = status.c_borrow_only_strings;
|
|
37816
38504
|
status.c_borrow_only_strings = scan_borrow_only_strings(node);
|
|
37817
38505
|
const old_force_heap = status.force_heap_strings;
|
|
@@ -38028,6 +38716,8 @@ function build_function_node(node, status) {
|
|
|
38028
38716
|
status.c_borrow_only_strings = old_borrow_only;
|
|
38029
38717
|
status.force_heap_strings = old_force_heap;
|
|
38030
38718
|
status.c_alias_owns_flags = old_alias_owns_flags;
|
|
38719
|
+
status.c_mv_owns_flags = old_mv_owns_flags;
|
|
38720
|
+
status.pending_mv_owns_sets = old_pending_mv_owns;
|
|
38031
38721
|
status.alias_decl_frames = old_alias_decl_frames;
|
|
38032
38722
|
status.class_alias_vars = old_class_alias_vars;
|
|
38033
38723
|
status.class_alias_source_map = old_class_alias_source_map;
|
|
@@ -38676,12 +39366,14 @@ function build_function_call_node(node, status) {
|
|
|
38676
39366
|
if (tname && status.enums.find((e) => e.name === tname && e.has_associated_data)) continue;
|
|
38677
39367
|
const decl_struct = decl_hit ? status.structs.find((s) => s.name === tname && !s.is_simple_type) : void 0;
|
|
38678
39368
|
const is_value_struct = !!decl_struct && !decl_struct.is_class;
|
|
38679
|
-
|
|
39369
|
+
const prefix = `${vname}.`;
|
|
39370
|
+
const box_ctor_transfer = is_box_type(node.name, status.structs);
|
|
39371
|
+
const callee_move_param = !!node.resolved_function?.params?.filter((p) => !p.is_self_param)[idx]?.is_moved;
|
|
39372
|
+
const transfer = box_ctor_transfer || (node.move_owned_param_indices?.includes(idx) ?? false) || callee_move_param;
|
|
39373
|
+
const owns_flag = status.c_mv_owns_flags?.get(vname);
|
|
39374
|
+
const use_flag = !!owns_flag && is_value_struct && transfer && decl_struct?.is_owning === true && !decl_struct.name.startsWith("_Tuple_") && !decl_struct.name.startsWith("_Anon_");
|
|
39375
|
+
if (decl_hit && !use_flag) decl_hit.frame.splice(decl_hit.index, 1);
|
|
38680
39376
|
if (is_value_struct) {
|
|
38681
|
-
const prefix = `${vname}.`;
|
|
38682
|
-
const box_ctor_transfer = is_box_type(node.name, status.structs);
|
|
38683
|
-
const callee_move_param = !!node.resolved_function?.params?.filter((p) => !p.is_self_param)[idx]?.is_moved;
|
|
38684
|
-
const transfer = box_ctor_transfer || (node.move_owned_param_indices?.includes(idx) ?? false) || callee_move_param;
|
|
38685
39377
|
for (const key of Array.from(status.heap_string_fields ?? [])) if (key.startsWith(prefix)) {
|
|
38686
39378
|
if (!transfer) {
|
|
38687
39379
|
if (!status.pending_string_releases) status.pending_string_releases = [];
|
|
@@ -38690,6 +39382,11 @@ function build_function_call_node(node, status) {
|
|
|
38690
39382
|
status.heap_string_fields.delete(key);
|
|
38691
39383
|
}
|
|
38692
39384
|
}
|
|
39385
|
+
if (use_flag) {
|
|
39386
|
+
if (!status.pending_mv_owns_sets) status.pending_mv_owns_sets = [];
|
|
39387
|
+
status.pending_mv_owns_sets.push(`${owns_flag} = 1;`);
|
|
39388
|
+
continue;
|
|
39389
|
+
}
|
|
38693
39390
|
if (!status.moved) status.moved = /* @__PURE__ */ new Set();
|
|
38694
39391
|
status.moved.add(vname);
|
|
38695
39392
|
}
|
|
@@ -39852,6 +40549,10 @@ function build_root_node(node, status) {
|
|
|
39852
40549
|
status.code += status.closure_definitions;
|
|
39853
40550
|
status.closure_definitions = void 0;
|
|
39854
40551
|
}
|
|
40552
|
+
if (status.box_clone_definitions) {
|
|
40553
|
+
status.code += status.box_clone_definitions;
|
|
40554
|
+
status.box_clone_definitions = void 0;
|
|
40555
|
+
}
|
|
39855
40556
|
if ((/\bobjc_msgSend\b|\bobjc_getClass\b|\bsel_registerName\b/.test(status.code) || /\bobjc_msgSend\b|\bobjc_getClass\b|\bsel_registerName\b/.test(status.headers)) && (status.platform === "macos" || status.platform === "ios")) status.headers = "#import <Foundation/Foundation.h>\n#include <objc/runtime.h>\n#include <objc/message.h>\n" + (status.platform === "macos" ? `#import <Cocoa/Cocoa.h>\n` : `#import <UIKit/UIKit.h>\n`) + status.headers;
|
|
39856
40557
|
status.headers += "void **_get_trait_func(void **obj, int trait_index, int func_index);\n";
|
|
39857
40558
|
if (status.emit_mode !== "user") status.code += `
|
|
@@ -40070,6 +40771,10 @@ function build_node$1(node, status, with_semicolon = false) {
|
|
|
40070
40771
|
status.code += status.pending_string_releases.join("\n") + "\n";
|
|
40071
40772
|
status.pending_string_releases.length = 0;
|
|
40072
40773
|
}
|
|
40774
|
+
if (status.pending_mv_owns_sets?.length) {
|
|
40775
|
+
status.code += status.pending_mv_owns_sets.join("\n") + "\n";
|
|
40776
|
+
status.pending_mv_owns_sets.length = 0;
|
|
40777
|
+
}
|
|
40073
40778
|
}
|
|
40074
40779
|
}
|
|
40075
40780
|
//#endregion
|
|
@@ -41248,6 +41953,7 @@ function emit_capturing_closure_value_a64(fn, status) {
|
|
|
41248
41953
|
if (cap.is_move && !(cap.type.name === "string" && !cap.type.is_view && !cap.type.is_array)) {
|
|
41249
41954
|
if (!status.moved) status.moved = /* @__PURE__ */ new Set();
|
|
41250
41955
|
status.moved.add(cap.name);
|
|
41956
|
+
mark_local_moved_flag(status, cap.name);
|
|
41251
41957
|
const prefix = `${cap.name}.`;
|
|
41252
41958
|
for (const key of Array.from(status.heap_string_fields ?? [])) if (key.startsWith(prefix)) status.heap_string_fields.delete(key);
|
|
41253
41959
|
}
|
|
@@ -41666,7 +42372,7 @@ function emit_struct_constructor_to_slot(fc, slot_addr, status) {
|
|
|
41666
42372
|
* so register slots start at x1 — overflow begins at the 8th arg (slot 8).
|
|
41667
42373
|
*/
|
|
41668
42374
|
function build_constructor_params(fc, param_regs, status) {
|
|
41669
|
-
mark_tuple_literal_move_owners(fc, status);
|
|
42375
|
+
mark_tuple_literal_move_owners(fc, status, (name) => mark_local_moved_flag(status, name));
|
|
41670
42376
|
const variadic_idx = fc.variadic_param_index;
|
|
41671
42377
|
if (variadic_idx !== void 0 && fc.params[variadic_idx] && fc.params[variadic_idx].node_type === "array") {
|
|
41672
42378
|
const arr = fc.params[variadic_idx];
|
|
@@ -42988,6 +43694,12 @@ function build_declaration_node(node, status, nir_init, nir_swap) {
|
|
|
42988
43694
|
const offset = allocate_stack_space(status, total_size);
|
|
42989
43695
|
status.stack_offsets.set(node.name, offset);
|
|
42990
43696
|
if (nullable_struct) status.stack_offsets.set(has_flag_name(node.name), offset + struct_size);
|
|
43697
|
+
if (struct_type?.is_owning === true && !nullable_struct && !struct_type.name.startsWith("_Tuple_") && !struct_type.name.startsWith("_Anon_") && struct_needs_destroy(struct_type, status)) {
|
|
43698
|
+
const owns_flag_offset = allocate_stack_space(status, 8, 8);
|
|
43699
|
+
emit_asm(status, `str xzr, [x29, #${owns_flag_offset}]\n`);
|
|
43700
|
+
if (!status.mv_owns_flag) status.mv_owns_flag = /* @__PURE__ */ new Map();
|
|
43701
|
+
status.mv_owns_flag.set(node.name, owns_flag_offset);
|
|
43702
|
+
}
|
|
42991
43703
|
} else if (node.is_module_init) {
|
|
42992
43704
|
emit_data(status, ".data\n");
|
|
42993
43705
|
emit_data(status, `.p2align 3\n${node.name}: .space ${total_size}\n`);
|
|
@@ -43731,10 +44443,27 @@ function emit_rhs_value(rhs, nir, status) {
|
|
|
43731
44443
|
* `a = b swap <rep>` / `var X b = mv f swap <rep>`): the expression-seam
|
|
43732
44444
|
* version of the historical `build_node(swap)`. Both callers normalize the
|
|
43733
44445
|
* trailing newline immediately after, so the helper does it.
|
|
44446
|
+
*
|
|
44447
|
+
* The replacement is an ordinary expression COMPOSED INTO the enclosing
|
|
44448
|
+
* statement — never the enclosing function's return value — so a
|
|
44449
|
+
* value-struct-returning call in it (most commonly the fresh `T()` a swap
|
|
44450
|
+
* constructs) must build into a fresh stack temp, not the sret buffer.
|
|
44451
|
+
* `status.struct_return_buffer` is left set for the WHOLE body of an
|
|
44452
|
+
* sret-returning function, so without this it pointed the replacement's
|
|
44453
|
+
* destination at the caller's saved sret pointer and built the value through
|
|
44454
|
+
* it — corrupting the caller's frame (and, in the allmark parser's
|
|
44455
|
+
* `result.arena = mv inline_state.arena swap Arena<...>()`, the returned
|
|
44456
|
+
* structure's node arena, which later crashed in `#destroy`).
|
|
43734
44457
|
*/
|
|
43735
44458
|
function emit_swap_value(swap, nir, status) {
|
|
44459
|
+
const old_buffer = status.struct_return_buffer;
|
|
44460
|
+
const old_offset = status.return_buffer_stack_offset;
|
|
44461
|
+
status.struct_return_buffer = void 0;
|
|
44462
|
+
status.return_buffer_stack_offset = void 0;
|
|
43736
44463
|
if (nir && nir.node === swap) emit_expr_from_nir$1(nir, status);
|
|
43737
44464
|
else build_node(swap, status);
|
|
44465
|
+
status.struct_return_buffer = old_buffer;
|
|
44466
|
+
status.return_buffer_stack_offset = old_offset;
|
|
43738
44467
|
ensure_newline(status);
|
|
43739
44468
|
}
|
|
43740
44469
|
function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
@@ -44650,6 +45379,11 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
44650
45379
|
if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
|
|
44651
45380
|
status.heap_strings.add(name);
|
|
44652
45381
|
} else if (explicit_move_source !== void 0 && status.heap_strings?.delete(explicit_move_source)) status.heap_strings.add(name);
|
|
45382
|
+
if (lhs_is_heap && size === 16 && !status.heap_strings?.has(name)) {
|
|
45383
|
+
emit_strdup_string(status);
|
|
45384
|
+
if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
|
|
45385
|
+
status.heap_strings.add(name);
|
|
45386
|
+
}
|
|
44653
45387
|
emit_asm(status, `\n`);
|
|
44654
45388
|
emit_var_store(status, "x0", name, size);
|
|
44655
45389
|
}
|
|
@@ -50388,6 +51122,10 @@ function build(root, options = {}) {
|
|
|
50388
51122
|
status.borrow_returning_functions = scan_borrow_returning_functions(root);
|
|
50389
51123
|
status.inline_functions = scan_inline_candidates(root);
|
|
50390
51124
|
build_node(root, status);
|
|
51125
|
+
if (status.box_clone_definitions) {
|
|
51126
|
+
status.code += status.box_clone_definitions;
|
|
51127
|
+
status.box_clone_definitions = void 0;
|
|
51128
|
+
}
|
|
50391
51129
|
const emitted_trait_destroys = /* @__PURE__ */ new Set();
|
|
50392
51130
|
for (const trait of status.traits) {
|
|
50393
51131
|
if (emitted_trait_destroys.has(trait.name)) continue;
|
|
@@ -52696,6 +53434,50 @@ function enum_owns_payloads(en, status, visited) {
|
|
|
52696
53434
|
for (const c of en.cases) for (const p of c.params) if (type_owns_heap(p.type, status)) return true;
|
|
52697
53435
|
return false;
|
|
52698
53436
|
}
|
|
53437
|
+
/**
|
|
53438
|
+
* The parameter of the enclosing function named `name`, if any (a bare-name
|
|
53439
|
+
* donor lookup — mirrors the field-store rule's enclosing-func resolution).
|
|
53440
|
+
*/
|
|
53441
|
+
function enclosing_function_param(status, name) {
|
|
53442
|
+
if (!name) return void 0;
|
|
53443
|
+
return status.stack.findLast((n) => n.node_type === "func")?.params.find((p) => p.name === name && !p.is_self_param);
|
|
53444
|
+
}
|
|
53445
|
+
/**
|
|
53446
|
+
* The B3 borrow contract for a BARE (non-`mv`) parameter of an owning mv
|
|
53447
|
+
* struct type: the call boundary does NOT materialize a private deep copy
|
|
53448
|
+
* (that is the pass-by-value convention's row, which declared-`mv` structs
|
|
53449
|
+
* are excluded from), so the parameter's storage aliases the caller's heap
|
|
53450
|
+
* fields and the caller keeps ownership. The callee may READ the parameter
|
|
53451
|
+
* but must never transfer ownership out of it — any transfer (`mv p` into a
|
|
53452
|
+
* declaration/assignment/owning parameter, with or without a swap: the swap
|
|
53453
|
+
* revalidates the callee's copy, not the caller's original; a by-value
|
|
53454
|
+
* `return p`; an implicit-mv boxing) hands the destination ownership the
|
|
53455
|
+
* caller's scope-exit destroy still reclaims (a double free). The fix at the
|
|
53456
|
+
* use site is to declare the parameter `mv` — which forces the call-site
|
|
53457
|
+
* `mv` keyword and invalidates the donor — or to `.copy()` it.
|
|
53458
|
+
*
|
|
53459
|
+
* Flagged only when the aliased bytes actually carry owned heap:
|
|
53460
|
+
* - a declared-`mv` struct whose fields transitively own heap (a string
|
|
53461
|
+
* field, a resource-releasing `#destroy`, a class/nested-owning field) —
|
|
53462
|
+
* a scalar-field `mv struct` aliases harmlessly and keeps the plain
|
|
53463
|
+
* by-value convention;
|
|
53464
|
+
* - an undeclared struct owning non-string heap (its locals are
|
|
53465
|
+
* auto-destroyed, and the boundary is an alias, not a copy).
|
|
53466
|
+
*
|
|
53467
|
+
* Exempt: `mv`/`ref`/`view`/`self` parameters, non-struct types, simple
|
|
53468
|
+
* types (`string` is a declared mv struct, but a string parameter's boundary
|
|
53469
|
+
* materializes a private deep copy, so moving one out never aliases), and
|
|
53470
|
+
* undeclared string-only structs (the pass-by-value deep copy).
|
|
53471
|
+
*/
|
|
53472
|
+
function borrowed_param_transfer_error(param, status) {
|
|
53473
|
+
if (!param || param.is_moved || param.is_self_param) return void 0;
|
|
53474
|
+
if (param.type.is_ref || param.type.is_view) return void 0;
|
|
53475
|
+
if (!param.type.name) return void 0;
|
|
53476
|
+
const s = status.structs.find((st) => st.name === param.type.name && !st.is_simple_type && !st.is_class);
|
|
53477
|
+
if (!s) return void 0;
|
|
53478
|
+
if (!(s.is_owning ? struct_owns_heap(s, status, /* @__PURE__ */ new Set()) : struct_owns_non_string_heap(s, status, /* @__PURE__ */ new Set()))) return void 0;
|
|
53479
|
+
return `cannot transfer ownership out of parameter '${param.name}' — a bare parameter of mv struct type '${param.type.name}' is a borrow; declare it 'mv' (and pass it with 'mv' at the call site), or .copy() it`;
|
|
53480
|
+
}
|
|
52699
53481
|
//#endregion
|
|
52700
53482
|
//#region ../src/check/utils/captures.ts
|
|
52701
53483
|
/**
|
|
@@ -55749,9 +56531,13 @@ function check_declaration_node(decl, status) {
|
|
|
55749
56531
|
} else add_error(status, `cannot copy a trait handle — use 'mv ${src_name}' to transfer it, or box a fresh instance`, decl.value.start);
|
|
55750
56532
|
} else if (src_sv && (src_sv.type.is_ref || src_sv.type.is_view)) add_error(status, boxing_owns_error(), decl.value.start);
|
|
55751
56533
|
else if (src_sv && is_value_struct_conformer(src_sv.type.name, decl.type.name, status)) {
|
|
55752
|
-
|
|
55753
|
-
if (
|
|
55754
|
-
|
|
56534
|
+
const boxing_param_error = borrowed_param_transfer_error(enclosing_function_param(status, src_name), status);
|
|
56535
|
+
if (boxing_param_error) add_error(status, boxing_param_error, decl.value.start);
|
|
56536
|
+
else {
|
|
56537
|
+
if (!decl.value.is_moved) decl.value.is_moved = true;
|
|
56538
|
+
if (!status.moved_variables) status.moved_variables = /* @__PURE__ */ new Set();
|
|
56539
|
+
status.moved_variables.add(src_name);
|
|
56540
|
+
}
|
|
55755
56541
|
}
|
|
55756
56542
|
} else if (is_trait_type(decl.type.name, status) && !decl.type.is_array && !decl.type.is_ref && !decl.type.is_view && decl.value.node_type !== "func_call" && decl.value.node_type !== "value") {
|
|
55757
56543
|
if (is_value_struct_conformer(type_from_value_node(decl.value, status).name, decl.type.name, status)) add_error(status, boxing_owns_error(), decl.value.start);
|
|
@@ -55814,12 +56600,16 @@ function check_declaration_node(decl, status) {
|
|
|
55814
56600
|
} else if (check_node(decl.swap, status)) check_type_and_value_match(field_type, type_from_value_node(decl.swap, status), void 0, status, decl.swap.start, "swap");
|
|
55815
56601
|
}
|
|
55816
56602
|
}
|
|
55817
|
-
if (decl.value?.node_type === "value" && decl.value.is_moved
|
|
55818
|
-
|
|
55819
|
-
|
|
55820
|
-
|
|
55821
|
-
if (
|
|
55822
|
-
|
|
56603
|
+
if (decl.value?.node_type === "value" && decl.value.is_moved) {
|
|
56604
|
+
const transfer_error = borrowed_param_transfer_error(enclosing_function_param(status, decl.value.value), status);
|
|
56605
|
+
if (transfer_error) add_error(status, transfer_error, decl.value.start);
|
|
56606
|
+
else if (!decl.swap) {
|
|
56607
|
+
if (status.values.findLast((v) => v.name === decl.value.value)?.is_ref_binding) add_error(status, `'${decl.value.value}' is a ref — mv the owner instead`, decl.value.start);
|
|
56608
|
+
else {
|
|
56609
|
+
enforce_root_not_frozen(decl.value.value, status, decl.value.start);
|
|
56610
|
+
if (!status.moved_variables) status.moved_variables = /* @__PURE__ */ new Set();
|
|
56611
|
+
status.moved_variables.add(decl.value.value);
|
|
56612
|
+
}
|
|
55823
56613
|
}
|
|
55824
56614
|
}
|
|
55825
56615
|
check_constraint(decl, status);
|
|
@@ -60350,9 +61140,13 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
60350
61140
|
else if (!func_param.is_moved && has_mov_keyword) add_error(status, `Unexpected 'mv' keyword for non-mv parameter '${func_param.name}'`, param.start);
|
|
60351
61141
|
if (has_mov_keyword && param_value && !node.swap_params?.has(i)) {
|
|
60352
61142
|
if (func_param.type.name && (is_class_type$1(func_param.type.name, status) || !!status.traits.find((t) => t.name === func_param.type.name) || func_param.is_moved && is_owning_struct_type(func_param.type, status))) {
|
|
60353
|
-
|
|
60354
|
-
if (
|
|
60355
|
-
|
|
61143
|
+
const transfer_error = borrowed_param_transfer_error(enclosing_function_param(status, param_value), status);
|
|
61144
|
+
if (transfer_error) add_error(status, transfer_error, param.start);
|
|
61145
|
+
else {
|
|
61146
|
+
enforce_root_not_frozen(param_value, status, param.start);
|
|
61147
|
+
if (!status.moved_variables) status.moved_variables = /* @__PURE__ */ new Set();
|
|
61148
|
+
status.moved_variables.add(param_value);
|
|
61149
|
+
}
|
|
60356
61150
|
}
|
|
60357
61151
|
}
|
|
60358
61152
|
if (has_mov_keyword && !node.swap_params?.has(i) && param.node_type === "access" && param.access.node_type === "access_field") {
|
|
@@ -60381,6 +61175,8 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
60381
61175
|
if (swap_expr) {
|
|
60382
61176
|
if (!has_mov_keyword) add_error(status, `swap requires mv keyword`, swap_expr.start);
|
|
60383
61177
|
else {
|
|
61178
|
+
const swap_param_error = borrowed_param_transfer_error(enclosing_function_param(status, param_value), status);
|
|
61179
|
+
if (swap_param_error) add_error(status, swap_param_error, param.start);
|
|
60384
61180
|
check_node(swap_expr, status);
|
|
60385
61181
|
check_type_and_value_match(param_type, type_from_value_node(swap_expr, status), void 0, status, swap_expr.start, "swap");
|
|
60386
61182
|
}
|
|
@@ -61678,7 +62474,11 @@ function check_tuple_element_ownership(array, status) {
|
|
|
61678
62474
|
const t = type_from_value_node(value, status);
|
|
61679
62475
|
if (!t.name) continue;
|
|
61680
62476
|
if (is_owning_struct_type(t, status)) {
|
|
61681
|
-
|
|
62477
|
+
const transfer_error = borrowed_param_transfer_error(enclosing_function_param(status, vn.value), status);
|
|
62478
|
+
if (transfer_error) {
|
|
62479
|
+
add_error(status, transfer_error, value.start);
|
|
62480
|
+
rejected = true;
|
|
62481
|
+
} else if (vn.is_moved || vn.literal_last_use_move) {
|
|
61682
62482
|
vn.is_moved = true;
|
|
61683
62483
|
if (!status.moved_variables) status.moved_variables = /* @__PURE__ */ new Set();
|
|
61684
62484
|
status.moved_variables.add(vn.value);
|
|
@@ -61953,9 +62753,13 @@ function check_assignment_node(assign, status) {
|
|
|
61953
62753
|
return false;
|
|
61954
62754
|
}
|
|
61955
62755
|
if (src_sv && is_value_struct_conformer(src_sv.type.name, trait_name, status)) {
|
|
61956
|
-
|
|
61957
|
-
if (
|
|
61958
|
-
|
|
62756
|
+
const boxing_param_error = borrowed_param_transfer_error(enclosing_function_param(status, src_name), status);
|
|
62757
|
+
if (boxing_param_error) add_error(status, boxing_param_error, assign.right_value.start);
|
|
62758
|
+
else {
|
|
62759
|
+
if (!assign.right_value.is_moved) assign.right_value.is_moved = true;
|
|
62760
|
+
if (!status.moved_variables) status.moved_variables = /* @__PURE__ */ new Set();
|
|
62761
|
+
status.moved_variables.add(src_name);
|
|
62762
|
+
}
|
|
61959
62763
|
}
|
|
61960
62764
|
} else if (assign.right_value.node_type !== "func_call") {
|
|
61961
62765
|
if (is_value_struct_conformer(type_from_value_node(assign.right_value, status).name, trait_name, status)) {
|
|
@@ -62033,12 +62837,16 @@ function check_assignment_node(assign, status) {
|
|
|
62033
62837
|
const raw = assign.right_value.value;
|
|
62034
62838
|
if (!(typeof raw === "string" && (raw.startsWith("\"") && raw.endsWith("\"") || raw === "null")) && rhs_type.name && is_owning_struct_type(rhs_type, status)) add_error(status, rhs_type.name === "string" ? `cannot copy a string by value — it owns its bytes; use 'mv' to transfer it, or .copy() for a deep copy` : `cannot copy '${rhs_type.name}' by value — it owns heap resources; use .copy() or mv`, assign.right_value.start);
|
|
62035
62839
|
}
|
|
62036
|
-
if (assign.right_value.node_type === "value" && assign.right_value.is_moved
|
|
62037
|
-
|
|
62038
|
-
|
|
62039
|
-
|
|
62040
|
-
if (
|
|
62041
|
-
|
|
62840
|
+
if (assign.right_value.node_type === "value" && assign.right_value.is_moved) {
|
|
62841
|
+
const transfer_error = borrowed_param_transfer_error(enclosing_function_param(status, assign.right_value.value), status);
|
|
62842
|
+
if (transfer_error) add_error(status, transfer_error, assign.right_value.start);
|
|
62843
|
+
else if (!assign.swap) {
|
|
62844
|
+
if (status.values.findLast((v) => v.name === assign.right_value.value)?.is_ref_binding) add_error(status, `'${assign.right_value.value}' is a ref — mv the owner instead`, assign.right_value.start);
|
|
62845
|
+
else {
|
|
62846
|
+
enforce_root_not_frozen(assign.right_value.value, status, assign.right_value.start);
|
|
62847
|
+
if (!status.moved_variables) status.moved_variables = /* @__PURE__ */ new Set();
|
|
62848
|
+
status.moved_variables.add(assign.right_value.value);
|
|
62849
|
+
}
|
|
62042
62850
|
}
|
|
62043
62851
|
}
|
|
62044
62852
|
if (assign.right_value.node_type === "value") {
|
|
@@ -63196,6 +64004,13 @@ function check_return_node(ret, status) {
|
|
|
63196
64004
|
if (param && !param.is_self_param && (param.is_ref || param.type.is_ref) && param_struct) add_error(status, `cannot return '${param.name}' — a 'ref' borrow escapes implicitly; declare the parameter 'mv' to transfer it`, ret.value.start);
|
|
63197
64005
|
}
|
|
63198
64006
|
}
|
|
64007
|
+
if (func && ret.type.name && !func.return_type.is_view && !func.return_type.is_ref) {
|
|
64008
|
+
const value_node = get_inner_value_node(ret.value);
|
|
64009
|
+
if (value_node?.node_type === "value") {
|
|
64010
|
+
const transfer_error = borrowed_param_transfer_error(func.params.find((p) => p.name === value_node.value), status);
|
|
64011
|
+
if (transfer_error) add_error(status, transfer_error, ret.value.start);
|
|
64012
|
+
}
|
|
64013
|
+
}
|
|
63199
64014
|
if (func && borrow_depth_of(ret.value, status) !== void 0) {
|
|
63200
64015
|
const ret_value_name = value_from_value_node(ret.value);
|
|
63201
64016
|
const sv = ret_value_name !== void 0 ? status.values.findLast((v) => v.name === ret_value_name) : void 0;
|