nomen-lang 0.6.5 → 0.6.6
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 +795 -97
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7546,7 +7546,7 @@ function scan_self_string_field_writes(struct, method) {
|
|
|
7546
7546
|
const scan_method = (func) => {
|
|
7547
7547
|
if (visited.has(func.name)) return;
|
|
7548
7548
|
visited.add(func.name);
|
|
7549
|
-
walk$
|
|
7549
|
+
walk$4(func.statements ?? [], (n) => {
|
|
7550
7550
|
if (n.node_type === "assign") {
|
|
7551
7551
|
const lhs = n.left_value;
|
|
7552
7552
|
if (lhs?.node_type !== "access") return;
|
|
@@ -7590,10 +7590,10 @@ function drop_self_written_string_field_records(status, receiver_name, fields) {
|
|
|
7590
7590
|
* statement arrays) — skipping `parent`/`scope` back-references and NOT
|
|
7591
7591
|
* descending INTO nested `func`/`struct`/`trait` declarations (a nested
|
|
7592
7592
|
* function's body is a separate function, not part of this one's writes). */
|
|
7593
|
-
function walk$
|
|
7593
|
+
function walk$4(value, cb) {
|
|
7594
7594
|
if (!value || typeof value !== "object") return;
|
|
7595
7595
|
if (Array.isArray(value)) {
|
|
7596
|
-
for (const item of value) walk$
|
|
7596
|
+
for (const item of value) walk$4(item, cb);
|
|
7597
7597
|
return;
|
|
7598
7598
|
}
|
|
7599
7599
|
const n = value;
|
|
@@ -7602,7 +7602,177 @@ function walk$3(value, cb) {
|
|
|
7602
7602
|
if (is_boundary) return;
|
|
7603
7603
|
for (const key of Object.keys(value)) {
|
|
7604
7604
|
if (key === "parent" || key === "scope" || key === "node_type") continue;
|
|
7605
|
-
walk$
|
|
7605
|
+
walk$4(value[key], cb);
|
|
7606
|
+
}
|
|
7607
|
+
}
|
|
7608
|
+
//#endregion
|
|
7609
|
+
//#region ../src/build_common/has_string_fields.ts
|
|
7610
|
+
/**
|
|
7611
|
+
* Whether any field (recursively, through nested owning value structs)
|
|
7612
|
+
* is an owned `string`. Shared by BOTH backends' owning-Buffer element
|
|
7613
|
+
* specialization (the slot deep-copies strdup'd strings, so a
|
|
7614
|
+
* string-field element is owning even when `struct_needs_destroy`
|
|
7615
|
+
* ignores string-only locals) — the C comment in
|
|
7616
|
+
* owning_buffer_specialize records the divergence this once caused.
|
|
7617
|
+
* `ref` and `view T` fields are skipped: a ref aliases the caller's
|
|
7618
|
+
* storage and a view's byte copy owns nothing.
|
|
7619
|
+
*/
|
|
7620
|
+
function has_string_fields(node, status) {
|
|
7621
|
+
for (const field of node.fields) {
|
|
7622
|
+
if (field.type.is_ref) continue;
|
|
7623
|
+
if (field.type.is_view) continue;
|
|
7624
|
+
if (field.type.name === "string" && !field.type.is_array) return true;
|
|
7625
|
+
const field_struct = status.structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_generic);
|
|
7626
|
+
if (field_struct && !field_struct.is_class && has_string_fields(field_struct, status)) return true;
|
|
7627
|
+
}
|
|
7628
|
+
return false;
|
|
7629
|
+
}
|
|
7630
|
+
/**
|
|
7631
|
+
* The DIRECT `string` fields of a value struct (nested struct fields are not
|
|
7632
|
+
* descended into — nested string fields are outside the per-field ownership
|
|
7633
|
+
* tracking everywhere else too). Used by the return-boundary normalization:
|
|
7634
|
+
* the returned struct's unrecorded fields are strdup'd so the value the
|
|
7635
|
+
* caller receives is uniformly heap-owned.
|
|
7636
|
+
*/
|
|
7637
|
+
function direct_string_fields(node) {
|
|
7638
|
+
return node.fields.filter((f) => !f.type.is_ref && !f.type.is_view && !f.type.is_array && f.type.name === "string");
|
|
7639
|
+
}
|
|
7640
|
+
//#endregion
|
|
7641
|
+
//#region ../src/build_common/scan_ref_string_writes.ts
|
|
7642
|
+
/**
|
|
7643
|
+
* The plain `string` fields of `func`'s `ref` value-struct parameters that the
|
|
7644
|
+
* function stores through with a DIRECT top-level statement — transitively
|
|
7645
|
+
* through top-level calls that forward the same param as their own `ref`
|
|
7646
|
+
* argument (callees resolved via the checker's `resolved_function` stamps).
|
|
7647
|
+
*
|
|
7648
|
+
* Both backends' string-field store paths strdup every non-heap RHS into an
|
|
7649
|
+
* owned copy before storing (a field is never a borrow once stored), so a
|
|
7650
|
+
* definitely-executed store leaves the field heap-owned. That is the fact a
|
|
7651
|
+
* CALL SITE transfers onto the caller's `heap_string_fields` records (the
|
|
7652
|
+
* add-direction mirror of `drop_self_written_string_field_records`): the
|
|
7653
|
+
* callee's own records are scope-local and die at return, so without the
|
|
7654
|
+
* transfer the stored copy leaks.
|
|
7655
|
+
*
|
|
7656
|
+
* Only DIRECT statements qualify: a store nested in a branch/loop may not
|
|
7657
|
+
* execute, and a record over the pre-call value would free it at the caller's
|
|
7658
|
+
* scope exit (possibly rodata — an invalid free). Memoized on the function
|
|
7659
|
+
* node; forwarding cycles are cut by a visited set (the cycle member's own
|
|
7660
|
+
* contribution is skipped, bounded-leak).
|
|
7661
|
+
*/
|
|
7662
|
+
function scan_ref_param_string_field_writes(func, structs) {
|
|
7663
|
+
const stamp = func;
|
|
7664
|
+
if (stamp._ref_param_string_field_writes) return stamp._ref_param_string_field_writes;
|
|
7665
|
+
const result = /* @__PURE__ */ new Map();
|
|
7666
|
+
stamp._ref_param_string_field_writes = result;
|
|
7667
|
+
const tracked = /* @__PURE__ */ new Map();
|
|
7668
|
+
for (const param of func.params ?? []) {
|
|
7669
|
+
if (param.is_self_param) continue;
|
|
7670
|
+
if (!(param.is_ref || param.type.is_ref)) continue;
|
|
7671
|
+
if (param.type.is_array || param.type.is_view) continue;
|
|
7672
|
+
const fields = ref_param_writable_fields(param.type.name, structs);
|
|
7673
|
+
if (fields.size) tracked.set(param.name, fields);
|
|
7674
|
+
}
|
|
7675
|
+
if (tracked.size) scan_function(func, tracked, structs, result, /* @__PURE__ */ new Set());
|
|
7676
|
+
return result;
|
|
7677
|
+
}
|
|
7678
|
+
/** The writable plain string fields of the value struct `type_name` resolves
|
|
7679
|
+
* to, or the empty set (non-struct, class, or no string fields). */
|
|
7680
|
+
function ref_param_writable_fields(type_name, structs) {
|
|
7681
|
+
if (!type_name) return /* @__PURE__ */ new Set();
|
|
7682
|
+
const struct = structs.find((s) => s.name === type_name && !s.is_simple_type && !s.is_class);
|
|
7683
|
+
if (!struct) return /* @__PURE__ */ new Set();
|
|
7684
|
+
return new Set(direct_string_fields(struct).map((f) => f.name));
|
|
7685
|
+
}
|
|
7686
|
+
function scan_function(func, tracked, structs, result, visiting) {
|
|
7687
|
+
visiting.add(func);
|
|
7688
|
+
for (const stmt of func.statements ?? []) {
|
|
7689
|
+
if (stmt.node_type === "assign") {
|
|
7690
|
+
const lhs = stmt.left_value;
|
|
7691
|
+
if (lhs?.node_type === "access") {
|
|
7692
|
+
const access = lhs;
|
|
7693
|
+
if (access.access.node_type === "access_field" && access.target.node_type === "value") {
|
|
7694
|
+
const name = access.target.value ?? "";
|
|
7695
|
+
const fields = tracked.get(name);
|
|
7696
|
+
const field = access.access.name ?? "";
|
|
7697
|
+
if (fields?.has(field)) {
|
|
7698
|
+
if (!result.has(name)) result.set(name, /* @__PURE__ */ new Set());
|
|
7699
|
+
result.get(name).add(field);
|
|
7700
|
+
}
|
|
7701
|
+
}
|
|
7702
|
+
}
|
|
7703
|
+
continue;
|
|
7704
|
+
}
|
|
7705
|
+
const call = ref_forward_call(stmt);
|
|
7706
|
+
if (!call) continue;
|
|
7707
|
+
const [callee, args] = call;
|
|
7708
|
+
const non_self_params = (callee.params ?? []).filter((p) => !p.is_self_param);
|
|
7709
|
+
for (let i = 0; i < args.length && i < non_self_params.length; i++) {
|
|
7710
|
+
const arg = args[i];
|
|
7711
|
+
if (arg?.node_type !== "value") continue;
|
|
7712
|
+
const arg_name = arg.value ?? "";
|
|
7713
|
+
const my_fields = tracked.get(arg_name);
|
|
7714
|
+
if (!my_fields) continue;
|
|
7715
|
+
const callee_param = non_self_params[i];
|
|
7716
|
+
if (!(callee_param.is_ref || callee_param.type.is_ref)) continue;
|
|
7717
|
+
if (visiting.has(callee)) continue;
|
|
7718
|
+
const callee_writes = scan_ref_param_string_field_writes(callee, structs).get(callee_param.name);
|
|
7719
|
+
if (!callee_writes?.size) continue;
|
|
7720
|
+
if (!result.has(arg_name)) result.set(arg_name, /* @__PURE__ */ new Set());
|
|
7721
|
+
const mine = result.get(arg_name);
|
|
7722
|
+
for (const field of callee_writes) if (my_fields.has(field)) mine.add(field);
|
|
7723
|
+
}
|
|
7724
|
+
}
|
|
7725
|
+
visiting.delete(func);
|
|
7726
|
+
}
|
|
7727
|
+
/**
|
|
7728
|
+
* The forwarded-call shape of a top-level statement: a resolved free-function
|
|
7729
|
+
* call or method call, with its argument list (the receiver is NOT included).
|
|
7730
|
+
*/
|
|
7731
|
+
function ref_forward_call(stmt) {
|
|
7732
|
+
if (stmt.node_type === "func_call") {
|
|
7733
|
+
const call = stmt;
|
|
7734
|
+
if (!call.resolved_function) return void 0;
|
|
7735
|
+
return [call.resolved_function, call.params ?? []];
|
|
7736
|
+
}
|
|
7737
|
+
if (stmt.node_type === "access") {
|
|
7738
|
+
const access = stmt;
|
|
7739
|
+
if (access.access.node_type !== "access_func") return void 0;
|
|
7740
|
+
const func_node = access.access.resolved_function;
|
|
7741
|
+
if (!func_node) return void 0;
|
|
7742
|
+
return [func_node, access.access.params ?? []];
|
|
7743
|
+
}
|
|
7744
|
+
}
|
|
7745
|
+
//#endregion
|
|
7746
|
+
//#region ../src/build_common/transfer_ref_param_records.ts
|
|
7747
|
+
/**
|
|
7748
|
+
* The add-direction mirror of drop_self_written_string_field_records: at a
|
|
7749
|
+
* call site whose callee takes a `ref` value-struct parameter, the callee's
|
|
7750
|
+
* string-field stores write through to the CALLER's variable — but the
|
|
7751
|
+
* callee's heap_string_fields records are scope-local and die at return, so
|
|
7752
|
+
* the stored copy would leak. Transfer the callee's definitely-executed
|
|
7753
|
+
* stores (scan_ref_param_string_field_writes) onto the caller's records for
|
|
7754
|
+
* the argument variable, making the caller's scope exit free them.
|
|
7755
|
+
*
|
|
7756
|
+
* `arg_is_trackable_local` must report true only for variables whose storage
|
|
7757
|
+
* THIS function's scope exit owns — a plain local. A `ref` param of the
|
|
7758
|
+
* enclosing function forwards caller storage, so a record here would free the
|
|
7759
|
+
* ORIGINAL caller's field at the wrong scope's exit.
|
|
7760
|
+
*/
|
|
7761
|
+
function transfer_ref_param_field_records(callee, args, status, arg_is_trackable_local) {
|
|
7762
|
+
const writes = scan_ref_param_string_field_writes(callee, status.structs);
|
|
7763
|
+
if (!writes.size) return;
|
|
7764
|
+
const non_self_params = (callee.params ?? []).filter((p) => !p.is_self_param);
|
|
7765
|
+
for (let i = 0; i < args.length && i < non_self_params.length; i++) {
|
|
7766
|
+
const param = non_self_params[i];
|
|
7767
|
+
if (!(param.is_ref || param.type.is_ref)) continue;
|
|
7768
|
+
const fields = writes.get(param.name);
|
|
7769
|
+
if (!fields?.size) continue;
|
|
7770
|
+
const arg = args[i];
|
|
7771
|
+
if (arg?.node_type !== "value") continue;
|
|
7772
|
+
const name = arg.value;
|
|
7773
|
+
if (!name || !arg_is_trackable_local(name)) continue;
|
|
7774
|
+
if (!status.heap_string_fields) status.heap_string_fields = /* @__PURE__ */ new Set();
|
|
7775
|
+
for (const field of fields) status.heap_string_fields.add(`${name}.${field}`);
|
|
7606
7776
|
}
|
|
7607
7777
|
}
|
|
7608
7778
|
//#endregion
|
|
@@ -7943,13 +8113,14 @@ function emit_field_overrides(var_name, value, build, status, declaration_termin
|
|
|
7943
8113
|
const overrides = overrides_of(value);
|
|
7944
8114
|
if (!overrides.length) return;
|
|
7945
8115
|
status.code += declaration_terminator;
|
|
7946
|
-
const carrier_type = var_name.startsWith("_return_val") ? void 0 : value.type;
|
|
8116
|
+
const carrier_type = var_name.startsWith("_return_val") || var_name.startsWith("_temp_") ? void 0 : value.type;
|
|
7947
8117
|
for (const override of overrides) {
|
|
7948
8118
|
const target = new ValueNode(override.value.start, var_name);
|
|
7949
8119
|
if (carrier_type) target.type = carrier_type;
|
|
7950
8120
|
const access_field = new AccessFieldNode(override.value.start, override.name, override.type);
|
|
7951
8121
|
const access = new AccessNode(override.value.start, target, access_field);
|
|
7952
8122
|
const assign = new AssignmentNode(override.value.start, access, override.value);
|
|
8123
|
+
if (!carrier_type) assign.raw_field_store = true;
|
|
7953
8124
|
status.code += "\n";
|
|
7954
8125
|
status.c_field_override_depth = (status.c_field_override_depth ?? 0) + 1;
|
|
7955
8126
|
build(assign, status);
|
|
@@ -8073,38 +8244,6 @@ function hoist_field_overrides(value, build, status, declaration_terminator = ""
|
|
|
8073
8244
|
carrier._fov_hoisted = true;
|
|
8074
8245
|
}
|
|
8075
8246
|
//#endregion
|
|
8076
|
-
//#region ../src/build_common/has_string_fields.ts
|
|
8077
|
-
/**
|
|
8078
|
-
* Whether any field (recursively, through nested owning value structs)
|
|
8079
|
-
* is an owned `string`. Shared by BOTH backends' owning-Buffer element
|
|
8080
|
-
* specialization (the slot deep-copies strdup'd strings, so a
|
|
8081
|
-
* string-field element is owning even when `struct_needs_destroy`
|
|
8082
|
-
* ignores string-only locals) — the C comment in
|
|
8083
|
-
* owning_buffer_specialize records the divergence this once caused.
|
|
8084
|
-
* `ref` and `view T` fields are skipped: a ref aliases the caller's
|
|
8085
|
-
* storage and a view's byte copy owns nothing.
|
|
8086
|
-
*/
|
|
8087
|
-
function has_string_fields(node, status) {
|
|
8088
|
-
for (const field of node.fields) {
|
|
8089
|
-
if (field.type.is_ref) continue;
|
|
8090
|
-
if (field.type.is_view) continue;
|
|
8091
|
-
if (field.type.name === "string" && !field.type.is_array) return true;
|
|
8092
|
-
const field_struct = status.structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_generic);
|
|
8093
|
-
if (field_struct && !field_struct.is_class && has_string_fields(field_struct, status)) return true;
|
|
8094
|
-
}
|
|
8095
|
-
return false;
|
|
8096
|
-
}
|
|
8097
|
-
/**
|
|
8098
|
-
* The DIRECT `string` fields of a value struct (nested struct fields are not
|
|
8099
|
-
* descended into — nested string fields are outside the per-field ownership
|
|
8100
|
-
* tracking everywhere else too). Used by the return-boundary normalization:
|
|
8101
|
-
* the returned struct's unrecorded fields are strdup'd so the value the
|
|
8102
|
-
* caller receives is uniformly heap-owned.
|
|
8103
|
-
*/
|
|
8104
|
-
function direct_string_fields(node) {
|
|
8105
|
-
return node.fields.filter((f) => !f.type.is_ref && !f.type.is_view && !f.type.is_array && f.type.name === "string");
|
|
8106
|
-
}
|
|
8107
|
-
//#endregion
|
|
8108
8247
|
//#region ../src/build_common/string_return_analysis.ts
|
|
8109
8248
|
const EMPTY_SET = /* @__PURE__ */ new Set();
|
|
8110
8249
|
/**
|
|
@@ -8468,6 +8607,11 @@ function is_owned_string_branch_value(node, table) {
|
|
|
8468
8607
|
*/
|
|
8469
8608
|
function is_normalized_struct_call(value, status) {
|
|
8470
8609
|
if (!value) return false;
|
|
8610
|
+
if (value.node_type === "anon_struct") {
|
|
8611
|
+
const base = value.base;
|
|
8612
|
+
if (!base) return false;
|
|
8613
|
+
value = base;
|
|
8614
|
+
}
|
|
8471
8615
|
if (value.node_type === "func_call") {
|
|
8472
8616
|
const call = value;
|
|
8473
8617
|
return !!status.normalized_struct_returners?.has(call.name);
|
|
@@ -8511,6 +8655,175 @@ function record_call_init_string_fields(node, status) {
|
|
|
8511
8655
|
for (const field of fields) status.heap_string_fields.add(`${node.name}.${field.name}`);
|
|
8512
8656
|
}
|
|
8513
8657
|
//#endregion
|
|
8658
|
+
//#region ../src/build_common/init_computed_seeds.ts
|
|
8659
|
+
function is_plain_string_field(field) {
|
|
8660
|
+
return field.type.name === "string" && !field.type.is_ref && !field.type.is_array && !field.type.is_view;
|
|
8661
|
+
}
|
|
8662
|
+
/** The `(field, rhs)` of a `self.<field> = <rhs>` write, or undefined. */
|
|
8663
|
+
function self_string_field_write(node, string_fields) {
|
|
8664
|
+
if (node.node_type !== "assign") return void 0;
|
|
8665
|
+
const lhs = node.left_value;
|
|
8666
|
+
if (lhs?.node_type !== "access") return void 0;
|
|
8667
|
+
const access = lhs;
|
|
8668
|
+
if (access.access.node_type !== "access_field") return void 0;
|
|
8669
|
+
const target = access.target;
|
|
8670
|
+
if (target?.node_type !== "value" || target.value !== "self") return void 0;
|
|
8671
|
+
const field = access.access.name ?? "";
|
|
8672
|
+
if (!string_fields.has(field)) return void 0;
|
|
8673
|
+
return {
|
|
8674
|
+
field,
|
|
8675
|
+
rhs: node.right_value
|
|
8676
|
+
};
|
|
8677
|
+
}
|
|
8678
|
+
/** Visit every AST node reachable from `value` — through arrays AND
|
|
8679
|
+
* single-node properties — skipping `parent`/`scope` back-references and NOT
|
|
8680
|
+
* descending INTO nested `func`/`struct`/`trait` declarations. */
|
|
8681
|
+
function walk$3(value, cb) {
|
|
8682
|
+
if (!value || typeof value !== "object") return;
|
|
8683
|
+
if (Array.isArray(value)) {
|
|
8684
|
+
for (const item of value) walk$3(item, cb);
|
|
8685
|
+
return;
|
|
8686
|
+
}
|
|
8687
|
+
const n = value;
|
|
8688
|
+
const is_boundary = n.node_type === "func" || n.node_type === "struct" || n.node_type === "trait";
|
|
8689
|
+
if (typeof n.node_type === "string") cb(n);
|
|
8690
|
+
if (is_boundary) return;
|
|
8691
|
+
for (const key of Object.keys(value)) {
|
|
8692
|
+
if (key === "parent" || key === "scope" || key === "node_type") continue;
|
|
8693
|
+
walk$3(value[key], cb);
|
|
8694
|
+
}
|
|
8695
|
+
}
|
|
8696
|
+
function collect_init_string_writes(struct_node, init) {
|
|
8697
|
+
const string_fields = new Set(struct_node.fields.filter(is_plain_string_field).map((f) => f.name));
|
|
8698
|
+
const top_level = /* @__PURE__ */ new Map();
|
|
8699
|
+
const nested = /* @__PURE__ */ new Map();
|
|
8700
|
+
const add = (m, field, rhs) => {
|
|
8701
|
+
const list = m.get(field);
|
|
8702
|
+
if (list) list.push(rhs);
|
|
8703
|
+
else m.set(field, [rhs]);
|
|
8704
|
+
};
|
|
8705
|
+
for (const stmt of init.statements ?? []) {
|
|
8706
|
+
const write = self_string_field_write(stmt, string_fields);
|
|
8707
|
+
if (write) {
|
|
8708
|
+
add(top_level, write.field, write.rhs);
|
|
8709
|
+
continue;
|
|
8710
|
+
}
|
|
8711
|
+
walk$3(stmt, (n) => {
|
|
8712
|
+
const nested_write = self_string_field_write(n, string_fields);
|
|
8713
|
+
if (nested_write) add(nested, nested_write.field, nested_write.rhs);
|
|
8714
|
+
});
|
|
8715
|
+
}
|
|
8716
|
+
return {
|
|
8717
|
+
top_level,
|
|
8718
|
+
nested
|
|
8719
|
+
};
|
|
8720
|
+
}
|
|
8721
|
+
/**
|
|
8722
|
+
* The plain string fields of `struct_node` that its custom `#init` COMPUTES as
|
|
8723
|
+
* a definitely-heap-owning value: for EVERY `#init` overload, the field is
|
|
8724
|
+
* either written at least once as a DIRECT body statement with every write
|
|
8725
|
+
* (direct or nested) passing `is_owned_heap` — or never written and its
|
|
8726
|
+
* DEFAULT also passes `is_owned_heap` (the overload leaves the default in
|
|
8727
|
+
* place). A param-alias seed (`self.a = s`) never qualifies: the seed borrows
|
|
8728
|
+
* the caller's argument temp, so it must never be recorded or freed.
|
|
8729
|
+
*
|
|
8730
|
+
* Binding sites record qualifying fields (scope exit frees the computed seed;
|
|
8731
|
+
* a displaced store reclaims it); override sites treat them as displaced (the
|
|
8732
|
+
* raw override store orphans the computed buffer). A param-alias seed or a
|
|
8733
|
+
* conditional computed write over a rodata default leaves a path holding a
|
|
8734
|
+
* non-heap value — such fields stay unqualified (bounded leak on the write
|
|
8735
|
+
* path, never an invalid free).
|
|
8736
|
+
*/
|
|
8737
|
+
function init_computed_heap_string_fields(struct_node, is_owned_heap) {
|
|
8738
|
+
const result = /* @__PURE__ */ new Set();
|
|
8739
|
+
const inits = struct_node.functions.filter((f) => f.name === "#init" && f.has_body);
|
|
8740
|
+
if (!inits.length) return result;
|
|
8741
|
+
for (const field of struct_node.fields) {
|
|
8742
|
+
if (!is_plain_string_field(field)) continue;
|
|
8743
|
+
const default_is_heap = !!field.value && is_owned_heap(field.value);
|
|
8744
|
+
let written_somewhere = false;
|
|
8745
|
+
let qualifies = true;
|
|
8746
|
+
for (const init of inits) {
|
|
8747
|
+
const writes = collect_init_string_writes(struct_node, init);
|
|
8748
|
+
const direct = writes.top_level.get(field.name) ?? [];
|
|
8749
|
+
const all = [...direct, ...writes.nested.get(field.name) ?? []];
|
|
8750
|
+
if (all.length === 0) {
|
|
8751
|
+
if (!default_is_heap) {
|
|
8752
|
+
qualifies = false;
|
|
8753
|
+
break;
|
|
8754
|
+
}
|
|
8755
|
+
continue;
|
|
8756
|
+
}
|
|
8757
|
+
written_somewhere = true;
|
|
8758
|
+
if (!all.every((expr) => is_owned_heap(expr))) {
|
|
8759
|
+
qualifies = false;
|
|
8760
|
+
break;
|
|
8761
|
+
}
|
|
8762
|
+
if (direct.length === 0 && !default_is_heap) {
|
|
8763
|
+
qualifies = false;
|
|
8764
|
+
break;
|
|
8765
|
+
}
|
|
8766
|
+
}
|
|
8767
|
+
if (qualifies && written_somewhere) result.add(field.name);
|
|
8768
|
+
}
|
|
8769
|
+
return result;
|
|
8770
|
+
}
|
|
8771
|
+
/**
|
|
8772
|
+
* The write-site half: does THIS custom-`#init` write to `self.<field>`
|
|
8773
|
+
* displace a heap-owning DEFAULT seed that must be reclaimed ahead of the
|
|
8774
|
+
* store? True only for the field's first write in the body (the displaced
|
|
8775
|
+
* value is then statically the default — later writes displace values whose
|
|
8776
|
+
* ownership the init cannot know, and keep the status quo). Marks the field
|
|
8777
|
+
* reclaimed on true so a second write never frees what the first stored.
|
|
8778
|
+
*
|
|
8779
|
+
* Sound even for conditional first writes: the free rides in the same branch
|
|
8780
|
+
* as the store, and the displaced value is still the default at that point.
|
|
8781
|
+
*/
|
|
8782
|
+
function claim_init_default_seed_reclaim(status, field_name, is_owned_heap) {
|
|
8783
|
+
if (status.current_function?.name !== "#init" || !status.current_struct) return false;
|
|
8784
|
+
if (status.init_default_reclaimed_fields?.has(field_name)) return false;
|
|
8785
|
+
const field = status.current_struct.fields.find((f) => f.name === field_name);
|
|
8786
|
+
if (!field || !field.value || !is_plain_string_field(field)) return false;
|
|
8787
|
+
if (!is_owned_heap(field.value)) return false;
|
|
8788
|
+
if (!status.init_default_reclaimed_fields) status.init_default_reclaimed_fields = /* @__PURE__ */ new Set();
|
|
8789
|
+
status.init_default_reclaimed_fields.add(field_name);
|
|
8790
|
+
return true;
|
|
8791
|
+
}
|
|
8792
|
+
//#endregion
|
|
8793
|
+
//#region ../src/build_common/ctor_heap_default_fields.ts
|
|
8794
|
+
/**
|
|
8795
|
+
* Record the string fields of `struct_node` whose value after construction
|
|
8796
|
+
* owns heap (per the backend's `is_owned_heap_temp`): either the DEFAULT is a
|
|
8797
|
+
* non-literal heap expression the constructor evaluates fresh at every
|
|
8798
|
+
* construction, or a custom `#init` COMPUTES the field's value
|
|
8799
|
+
* (`init_computed_heap_string_fields` — every direct `self.<field> = <expr>`
|
|
8800
|
+
* write stores a heap-owning expression, which also displaces the default).
|
|
8801
|
+
* Recording at the ctor-binding site makes scope exit free the seeded value,
|
|
8802
|
+
* and a displaced store (an override, a reassignment) reclaim it via the
|
|
8803
|
+
* record's old_was_heap check.
|
|
8804
|
+
*
|
|
8805
|
+
* `is_owned_heap` classifies the seed EXPRESSION per backend: both treat a
|
|
8806
|
+
* string-typed `op` as fresh heap; for calls the C backend's blanket strdup
|
|
8807
|
+
* makes every non-borrow string return owned, while the aarch64 backend
|
|
8808
|
+
* requires the heap-returning classification (a literal-returning user
|
|
8809
|
+
* function leaves rodata in the field — never recorded, never freed).
|
|
8810
|
+
*
|
|
8811
|
+
* `skip` names fields whose seed is reclaimed elsewhere (the C backend's
|
|
8812
|
+
* explicit displaced free ahead of an override store). Value structs only —
|
|
8813
|
+
* a class's string fields are always heap and freed by its destroy.
|
|
8814
|
+
*/
|
|
8815
|
+
function record_ctor_heap_default_fields(var_name, struct_node, status, is_owned_heap, skip) {
|
|
8816
|
+
const computed = init_computed_heap_string_fields(struct_node, is_owned_heap);
|
|
8817
|
+
for (const field of struct_node.fields) {
|
|
8818
|
+
if (field.type.name !== "string" || field.type.is_array) continue;
|
|
8819
|
+
if (field.type.is_ref || field.type.is_view) continue;
|
|
8820
|
+
if (skip?.has(field.name)) continue;
|
|
8821
|
+
if (!(!!field.value && is_owned_heap(field.value)) && !computed.has(field.name)) continue;
|
|
8822
|
+
if (!status.heap_string_fields) status.heap_string_fields = /* @__PURE__ */ new Set();
|
|
8823
|
+
status.heap_string_fields.add(`${var_name}.${field.name}`);
|
|
8824
|
+
}
|
|
8825
|
+
}
|
|
8826
|
+
//#endregion
|
|
8514
8827
|
//#region ../src/nodes/child_nodes.ts
|
|
8515
8828
|
/** Whether `value` is an AST node (carries a string `node_type`). */
|
|
8516
8829
|
function is_ast_node(value) {
|
|
@@ -8853,6 +9166,7 @@ function scan_function_literal_element_moves(func) {
|
|
|
8853
9166
|
return count;
|
|
8854
9167
|
}
|
|
8855
9168
|
function walk_stmt(walk, node) {
|
|
9169
|
+
if (node.allocations?.length) for (const alloc of node.allocations) walk_stmt(walk, alloc);
|
|
8856
9170
|
switch (node.node_type) {
|
|
8857
9171
|
case "declare": {
|
|
8858
9172
|
const decl = node;
|
|
@@ -14891,10 +15205,21 @@ function emit_string_field_strdups_at(status, struct_name, base_reg, base_offset
|
|
|
14891
15205
|
const off = base_offset + get_field_offset(struct_name, field.name, status);
|
|
14892
15206
|
emit_asm(status, `str ${base_reg}, [sp, #-16]!\n`);
|
|
14893
15207
|
emit_string_pair_load_at(status, base_reg, off);
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
15208
|
+
if (field.type.is_nullable) {
|
|
15209
|
+
const skip_label = `.Lskip_nullable_strdup_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
15210
|
+
emit_asm(status, `mov x13, x0\n`);
|
|
15211
|
+
emit_asm(status, `cbz x0, ${skip_label}\n`);
|
|
15212
|
+
emit_strdup_string(status);
|
|
15213
|
+
emit_asm(status, `mov x13, x0\n`);
|
|
15214
|
+
emit_asm(status, `${skip_label}:\n`);
|
|
15215
|
+
emit_asm(status, `ldr ${base_reg}, [sp], #16\n`);
|
|
15216
|
+
emit_pair_store_to(status, base_reg, off, "x13", "x1");
|
|
15217
|
+
} else {
|
|
15218
|
+
emit_strdup_string(status);
|
|
15219
|
+
emit_asm(status, `mov x13, x0\n`);
|
|
15220
|
+
emit_asm(status, `ldr ${base_reg}, [sp], #16\n`);
|
|
15221
|
+
emit_pair_store_to(status, base_reg, off, "x13", "x1");
|
|
15222
|
+
}
|
|
14898
15223
|
}
|
|
14899
15224
|
}
|
|
14900
15225
|
//#endregion
|
|
@@ -19441,6 +19766,12 @@ function plan_nir_registers(func, nir, options) {
|
|
|
19441
19766
|
});
|
|
19442
19767
|
}
|
|
19443
19768
|
}
|
|
19769
|
+
const loop_carry_regs = /* @__PURE__ */ new Set();
|
|
19770
|
+
for (const [key, f] of facts) {
|
|
19771
|
+
if (!f.loop_blocked) continue;
|
|
19772
|
+
const reg = allocs.get(key);
|
|
19773
|
+
if (reg) loop_carry_regs.add(reg);
|
|
19774
|
+
}
|
|
19444
19775
|
return {
|
|
19445
19776
|
allocs,
|
|
19446
19777
|
callee_saved,
|
|
@@ -19449,7 +19780,8 @@ function plan_nir_registers(func, nir, options) {
|
|
|
19449
19780
|
source_keys,
|
|
19450
19781
|
sites: site_allocs,
|
|
19451
19782
|
pairs: slp_pairs,
|
|
19452
|
-
region_free
|
|
19783
|
+
region_free,
|
|
19784
|
+
loop_carry_regs
|
|
19453
19785
|
};
|
|
19454
19786
|
}
|
|
19455
19787
|
/**
|
|
@@ -19510,6 +19842,7 @@ function seed_function_allocations(func, status, options) {
|
|
|
19510
19842
|
}]));
|
|
19511
19843
|
if (status.nir_region_free.size === 0) status.nir_region_free = void 0;
|
|
19512
19844
|
status.region_preseed = void 0;
|
|
19845
|
+
status.nir_loop_carry_regs = plan.loop_carry_regs.size > 0 ? plan.loop_carry_regs : void 0;
|
|
19513
19846
|
for (const param of func.params) {
|
|
19514
19847
|
const reg = plan.allocs.get(param.name);
|
|
19515
19848
|
if (!reg || !reg.startsWith("x")) continue;
|
|
@@ -19633,6 +19966,8 @@ function build_function_node$1(node, status) {
|
|
|
19633
19966
|
status.heap_class_arrays = void 0;
|
|
19634
19967
|
const old_heap_array_vars = status.heap_array_vars;
|
|
19635
19968
|
status.heap_array_vars = void 0;
|
|
19969
|
+
const old_heap_string_fields = status.heap_string_fields;
|
|
19970
|
+
status.heap_string_fields = void 0;
|
|
19636
19971
|
const old_heap_cleanup_stack = status.heap_cleanup_stack;
|
|
19637
19972
|
status.heap_cleanup_stack = void 0;
|
|
19638
19973
|
const old_trait_class_frames = status.trait_class_frames;
|
|
@@ -19860,11 +20195,13 @@ function build_function_node$1(node, status) {
|
|
|
19860
20195
|
}]));
|
|
19861
20196
|
if (status.nir_region_free.size === 0) status.nir_region_free = void 0;
|
|
19862
20197
|
status.region_preseed = void 0;
|
|
20198
|
+
status.nir_loop_carry_regs = plan.loop_carry_regs.size > 0 ? plan.loop_carry_regs : void 0;
|
|
19863
20199
|
} else {
|
|
19864
20200
|
fn_allocs = plan_function_promotions(node, plan_nir);
|
|
19865
20201
|
status.nir_site_allocs = void 0;
|
|
19866
20202
|
status.nir_region_free = void 0;
|
|
19867
20203
|
status.region_preseed = void 0;
|
|
20204
|
+
status.nir_loop_carry_regs = void 0;
|
|
19868
20205
|
}
|
|
19869
20206
|
} else status.nir_site_allocs = void 0;
|
|
19870
20207
|
status.register_allocations = fn_allocs && fn_allocs.size > 0 ? fn_allocs : void 0;
|
|
@@ -20245,6 +20582,7 @@ function build_function_node$1(node, status) {
|
|
|
20245
20582
|
}
|
|
20246
20583
|
status.scoped_declarations = old_scoped_declarations;
|
|
20247
20584
|
status.moved = old_moved;
|
|
20585
|
+
status.heap_string_fields = old_heap_string_fields;
|
|
20248
20586
|
status.heap_strings = old_heap_strings;
|
|
20249
20587
|
status.heap_string_arrays = old_heap_string_arrays;
|
|
20250
20588
|
status.heap_owned_string_arrays = old_heap_owned_string_arrays;
|
|
@@ -20358,6 +20696,41 @@ function init_nullable_field_default(node, field, base_reg, status) {
|
|
|
20358
20696
|
emit_asm(status, `str x9, [${base_reg}, #${has_offset}]\n`);
|
|
20359
20697
|
return true;
|
|
20360
20698
|
}
|
|
20699
|
+
/**
|
|
20700
|
+
* Initialize a field whose default is a non-literal EXPRESSION (a string op
|
|
20701
|
+
* like `"de" + "fault"`, a call returning a non-struct, a grouped expr, …):
|
|
20702
|
+
* evaluate it and store the result into the field slot. x0 carries the
|
|
20703
|
+
* result — a fat string as the (x0, x1) pair, a by-value struct as the sret
|
|
20704
|
+
* temp's address. A class's plain string field is strdup'd when the result
|
|
20705
|
+
* is not itself fresh heap (the field is freed unconditionally at destroy);
|
|
20706
|
+
* a value struct's heap-owning default is stored raw — the ctor-binding
|
|
20707
|
+
* sites record it (record_ctor_heap_default_fields) so scope exit frees it.
|
|
20708
|
+
*/
|
|
20709
|
+
function init_expr_field_default(node, field, offset, base_reg, status) {
|
|
20710
|
+
build_node(field.value, status);
|
|
20711
|
+
ensure_newline(status);
|
|
20712
|
+
const t = field.type;
|
|
20713
|
+
if (t.name === "string" && !t.is_ref && !t.is_view && !t.is_array) {
|
|
20714
|
+
if (node.is_class && !status.last_result_is_heap) emit_strdup_string(status);
|
|
20715
|
+
emit_pair_store_to(status, base_reg, offset, "x0", "x1");
|
|
20716
|
+
return;
|
|
20717
|
+
}
|
|
20718
|
+
const field_struct = status.structs.find((s) => s.name === t.name && !s.is_simple_type);
|
|
20719
|
+
if (field_struct?.is_class) {
|
|
20720
|
+
emit_typed_store(status, "x0", base_reg, offset, 8);
|
|
20721
|
+
return;
|
|
20722
|
+
}
|
|
20723
|
+
if (field_struct) {
|
|
20724
|
+
const field_size = get_struct_size(t.name, status);
|
|
20725
|
+
const words = Math.ceil(field_size / 8);
|
|
20726
|
+
for (let w = 0; w < words; w++) {
|
|
20727
|
+
emit_asm(status, `ldr x9, [x0, #${w * 8}]\n`);
|
|
20728
|
+
emit_asm(status, `str x9, [${base_reg}, #${offset + w * 8}]\n`);
|
|
20729
|
+
}
|
|
20730
|
+
return;
|
|
20731
|
+
}
|
|
20732
|
+
emit_typed_store(status, "x0", base_reg, offset, get_type_size(t, status));
|
|
20733
|
+
}
|
|
20361
20734
|
/** Store the scalar in x0 into `[base_reg + offset]` at its natural width. */
|
|
20362
20735
|
function emit_scalar_store_from_x0(offset, type, base_reg, status) {
|
|
20363
20736
|
const name = type.name;
|
|
@@ -20783,19 +21156,16 @@ function build_init_function(node, status) {
|
|
|
20783
21156
|
}
|
|
20784
21157
|
}
|
|
20785
21158
|
emit_typed_store(status, "x1", "x19", offset, get_type_size(field.type, status));
|
|
20786
|
-
} else if (field.value.node_type === "func_call") {
|
|
20787
|
-
|
|
20788
|
-
|
|
20789
|
-
|
|
20790
|
-
|
|
20791
|
-
|
|
20792
|
-
|
|
20793
|
-
|
|
20794
|
-
|
|
20795
|
-
|
|
20796
|
-
emit_asm(status, `ldr x9, [x0, #${w * 8}]\n`);
|
|
20797
|
-
emit_asm(status, `str x9, [x19, #${offset + w * 8}]\n`);
|
|
20798
|
-
}
|
|
21159
|
+
} else if (field.value.node_type === "func_call" && status.structs.find((s) => s.name === field.type.name && !s.is_simple_type)) {
|
|
21160
|
+
build_node(field.value, status);
|
|
21161
|
+
ensure_newline(status);
|
|
21162
|
+
if (status.structs.find((s) => s.name === field.type.name && !s.is_simple_type).is_class) emit_typed_store(status, "x0", "x19", offset, 8);
|
|
21163
|
+
else {
|
|
21164
|
+
const field_size = get_struct_size(field.type.name, status);
|
|
21165
|
+
const words = Math.ceil(field_size / 8);
|
|
21166
|
+
for (let w = 0; w < words; w++) {
|
|
21167
|
+
emit_asm(status, `ldr x9, [x0, #${w * 8}]\n`);
|
|
21168
|
+
emit_asm(status, `str x9, [x19, #${offset + w * 8}]\n`);
|
|
20799
21169
|
}
|
|
20800
21170
|
}
|
|
20801
21171
|
} else if (field.value.node_type === "func" && field.type.name === "func") {
|
|
@@ -20806,7 +21176,7 @@ function build_init_function(node, status) {
|
|
|
20806
21176
|
ensure_newline(status);
|
|
20807
21177
|
emit_descriptor_address(status, "x1", materialize_lambda_descriptor_a64(field.value, status));
|
|
20808
21178
|
emit_typed_store(status, "x1", "x19", offset, 8);
|
|
20809
|
-
}
|
|
21179
|
+
} else init_expr_field_default(node, field, offset, "x19", status);
|
|
20810
21180
|
}
|
|
20811
21181
|
for (const trait_name of node.traits) {
|
|
20812
21182
|
const trait = status.traits.find((t) => t.name === trait_name);
|
|
@@ -21026,6 +21396,10 @@ function build_custom_init_function(node, func, status) {
|
|
|
21026
21396
|
status.current_function = func;
|
|
21027
21397
|
const old_init_assigned_fields = status.init_assigned_fields;
|
|
21028
21398
|
status.init_assigned_fields = /* @__PURE__ */ new Set();
|
|
21399
|
+
const old_init_default_reclaimed = status.init_default_reclaimed_fields;
|
|
21400
|
+
status.init_default_reclaimed_fields = /* @__PURE__ */ new Set();
|
|
21401
|
+
const old_init_heap_string_fields = status.heap_string_fields;
|
|
21402
|
+
status.heap_string_fields = void 0;
|
|
21029
21403
|
emit_asm(status, `str xzr, [x19]\n`);
|
|
21030
21404
|
if (node.traits.length > 0) {
|
|
21031
21405
|
emit_asm(status, `adrp x9, _${node.name}_traits@PAGE\n`);
|
|
@@ -21083,22 +21457,19 @@ function build_custom_init_function(node, func, status) {
|
|
|
21083
21457
|
}
|
|
21084
21458
|
}
|
|
21085
21459
|
emit_typed_store(status, "x1", "x19", offset, get_type_size(field.type, status));
|
|
21086
|
-
} else if (field.value.node_type === "func_call") {
|
|
21087
|
-
|
|
21088
|
-
|
|
21089
|
-
|
|
21090
|
-
|
|
21091
|
-
|
|
21092
|
-
|
|
21093
|
-
|
|
21094
|
-
|
|
21095
|
-
|
|
21096
|
-
emit_asm(status, `ldr x9, [x0, #${w * 8}]\n`);
|
|
21097
|
-
emit_asm(status, `str x9, [x19, #${offset + w * 8}]\n`);
|
|
21098
|
-
}
|
|
21460
|
+
} else if (field.value.node_type === "func_call" && status.structs.find((s) => s.name === field.type.name && !s.is_simple_type)) {
|
|
21461
|
+
build_node(field.value, status);
|
|
21462
|
+
ensure_newline(status);
|
|
21463
|
+
if (status.structs.find((s) => s.name === field.type.name && !s.is_simple_type).is_class) emit_typed_store(status, "x0", "x19", offset, 8);
|
|
21464
|
+
else {
|
|
21465
|
+
const field_size = get_struct_size(field.type.name, status);
|
|
21466
|
+
const words = Math.ceil(field_size / 8);
|
|
21467
|
+
for (let w = 0; w < words; w++) {
|
|
21468
|
+
emit_asm(status, `ldr x9, [x0, #${w * 8}]\n`);
|
|
21469
|
+
emit_asm(status, `str x9, [x19, #${offset + w * 8}]\n`);
|
|
21099
21470
|
}
|
|
21100
21471
|
}
|
|
21101
|
-
}
|
|
21472
|
+
} else init_expr_field_default(node, field, offset, "x19", status);
|
|
21102
21473
|
}
|
|
21103
21474
|
status.buffer_data_cache = void 0;
|
|
21104
21475
|
status.array_ptr_cache = void 0;
|
|
@@ -21108,6 +21479,8 @@ function build_custom_init_function(node, func, status) {
|
|
|
21108
21479
|
status.raw_param_reloads = old_raw_param_reloads;
|
|
21109
21480
|
status.current_function = old_current_function;
|
|
21110
21481
|
status.init_assigned_fields = old_init_assigned_fields;
|
|
21482
|
+
status.init_default_reclaimed_fields = old_init_default_reclaimed;
|
|
21483
|
+
status.heap_string_fields = old_init_heap_string_fields;
|
|
21111
21484
|
}
|
|
21112
21485
|
const init_claims = status.callee_saved_regs_used;
|
|
21113
21486
|
const init_loop_regs = init_claims ? [...init_claims].sort() : [];
|
|
@@ -21814,6 +22187,8 @@ function collect_allocations(node, options) {
|
|
|
21814
22187
|
case "func_call": {
|
|
21815
22188
|
const call = node;
|
|
21816
22189
|
for (const param of call.params) result.push(...collect_allocations(param, options));
|
|
22190
|
+
const base = node.base;
|
|
22191
|
+
if (base) result.push(...collect_allocations(base, options));
|
|
21817
22192
|
break;
|
|
21818
22193
|
}
|
|
21819
22194
|
case "access": {
|
|
@@ -21861,6 +22236,12 @@ function collect_allocations(node, options) {
|
|
|
21861
22236
|
result.push(...collect_allocations(range.right_value, options));
|
|
21862
22237
|
break;
|
|
21863
22238
|
}
|
|
22239
|
+
case "anon_struct": {
|
|
22240
|
+
const anon = node;
|
|
22241
|
+
if (anon.base) result.push(...collect_allocations(anon.base, options));
|
|
22242
|
+
for (const field of anon.fields) result.push(...collect_allocations(field.value, options));
|
|
22243
|
+
break;
|
|
22244
|
+
}
|
|
21864
22245
|
case "array": {
|
|
21865
22246
|
const arr = node;
|
|
21866
22247
|
for (const val of arr.values) result.push(...collect_allocations(val, options));
|
|
@@ -23893,6 +24274,8 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23893
24274
|
if (outgoing > 0) emit_asm(status, `add sp, sp, #${outgoing}\n`);
|
|
23894
24275
|
if (func_call.move_param_indices?.length) for (const idx of func_call.move_param_indices) mark_moved_if_struct(func_call.params[idx], status);
|
|
23895
24276
|
build_swap_params(func_call, status);
|
|
24277
|
+
const ctor_struct = status.structs.find((s) => s.name === func_call.name && !s.is_simple_type && !s.is_class);
|
|
24278
|
+
if (ctor_struct) record_ctor_heap_default_fields(node.name, ctor_struct, status, (seed) => is_owned_heap_temp(seed, status));
|
|
23896
24279
|
} else if (status.structs.find((s) => s.name === (func_call.type?.name ?? func_call.name) && !s.is_simple_type && !s.is_class) && status.function_return_label) {
|
|
23897
24280
|
const old_buffer = status.struct_return_buffer;
|
|
23898
24281
|
const old_buffer_var = status.struct_return_buffer_var;
|
|
@@ -24770,14 +25153,18 @@ const INT_CALLER_SAVED_EXT = [
|
|
|
24770
25153
|
function can_share_claimed_register(status, name, reg) {
|
|
24771
25154
|
const shared = status.nir_alloc_shared;
|
|
24772
25155
|
if (!shared || !status.register_allocations) return false;
|
|
25156
|
+
if (!shared.source_keys.has(name)) return false;
|
|
24773
25157
|
if (status.region_pinned?.get(reg)) return false;
|
|
24774
|
-
|
|
25158
|
+
if (status.nir_loop_carry_regs?.has(reg)) return false;
|
|
25159
|
+
const cand_keys = shared.source_keys.get(name);
|
|
24775
25160
|
let occupants = 0;
|
|
24776
25161
|
for (const [occupant, occupant_reg] of status.register_allocations) {
|
|
24777
25162
|
if (occupant_reg !== reg || occupant === name) continue;
|
|
24778
25163
|
occupants++;
|
|
24779
25164
|
if (shared.pinned.has(occupant)) return false;
|
|
24780
|
-
|
|
25165
|
+
const occ_keys = shared.source_keys.get(occupant);
|
|
25166
|
+
if (!occ_keys) return false;
|
|
25167
|
+
for (const occ_key of occ_keys) for (const cand_key of cand_keys) if (shared.adj.get(cand_key)?.has(occ_key)) return false;
|
|
24781
25168
|
}
|
|
24782
25169
|
return occupants > 0;
|
|
24783
25170
|
}
|
|
@@ -24954,6 +25341,7 @@ function promote_loop_locals(status, scoped_declarations, sources, options) {
|
|
|
24954
25341
|
else used_x.add(r);
|
|
24955
25342
|
if (status.nir_caller_saved_claimed) for (const r of status.nir_caller_saved_claimed) used_x.add(r);
|
|
24956
25343
|
if (status.region_pinned) for (const r of status.region_pinned.keys()) used_x.add(r);
|
|
25344
|
+
const loop_carry_regs = status.nir_loop_carry_regs;
|
|
24957
25345
|
const site_regs = /* @__PURE__ */ new Set();
|
|
24958
25346
|
if (status.nir_site_allocs) for (const s of status.nir_site_allocs.values()) site_regs.add(s.reg);
|
|
24959
25347
|
const float_pool = options?.call_free === true ? [...FLOAT_CALLEE_SAVED, ...FLOAT_CALLER_SAVED_EXT] : FLOAT_CALLEE_SAVED;
|
|
@@ -24977,6 +25365,10 @@ function promote_loop_locals(status, scoped_declarations, sources, options) {
|
|
|
24977
25365
|
d_idx++;
|
|
24978
25366
|
continue;
|
|
24979
25367
|
}
|
|
25368
|
+
if (loop_carry_regs?.has(reg0) || loop_carry_regs?.has(reg1)) {
|
|
25369
|
+
d_idx++;
|
|
25370
|
+
continue;
|
|
25371
|
+
}
|
|
24980
25372
|
if (used_d.has(reg0)) {
|
|
24981
25373
|
if (!can_share_claimed_register(status, v.name, reg0)) {
|
|
24982
25374
|
d_idx++;
|
|
@@ -25024,6 +25416,10 @@ function promote_loop_locals(status, scoped_declarations, sources, options) {
|
|
|
25024
25416
|
d_idx++;
|
|
25025
25417
|
continue;
|
|
25026
25418
|
}
|
|
25419
|
+
if (loop_carry_regs?.has(float_pool[d_idx])) {
|
|
25420
|
+
d_idx++;
|
|
25421
|
+
continue;
|
|
25422
|
+
}
|
|
25027
25423
|
if (!used_d.has(float_pool[d_idx])) break;
|
|
25028
25424
|
if (can_share_claimed_register(status, v.name, float_pool[d_idx])) break;
|
|
25029
25425
|
d_idx++;
|
|
@@ -25046,6 +25442,10 @@ function promote_loop_locals(status, scoped_declarations, sources, options) {
|
|
|
25046
25442
|
x_idx++;
|
|
25047
25443
|
continue;
|
|
25048
25444
|
}
|
|
25445
|
+
if (loop_carry_regs?.has(x_pool[x_idx])) {
|
|
25446
|
+
x_idx++;
|
|
25447
|
+
continue;
|
|
25448
|
+
}
|
|
25049
25449
|
if (!used_x.has(x_pool[x_idx])) break;
|
|
25050
25450
|
if (can_share_claimed_register(status, v.name, x_pool[x_idx])) break;
|
|
25051
25451
|
x_idx++;
|
|
@@ -25357,23 +25757,33 @@ function build_if_else_node$1(node, status, nir) {
|
|
|
25357
25757
|
ensure_newline(status);
|
|
25358
25758
|
const pre_cache = status.buffer_data_cache;
|
|
25359
25759
|
const pre_array_cache = status.array_ptr_cache;
|
|
25760
|
+
const pre_heap_string_fields = status.heap_string_fields;
|
|
25360
25761
|
if (node.else_branch) {
|
|
25361
25762
|
const then_frame = enter_scope_frame(status);
|
|
25362
25763
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25363
25764
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
25765
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25364
25766
|
build_block_with_cursor$1(node.if_branch, nir?.then_branch, status);
|
|
25767
|
+
const then_fields = status.heap_string_fields;
|
|
25365
25768
|
exit_scope_frame(status, then_frame);
|
|
25366
25769
|
emit_asm(status, `b end_${label}\n`);
|
|
25367
25770
|
emit_asm(status, `else_${label}:\n`);
|
|
25368
25771
|
const else_frame = enter_scope_frame(status);
|
|
25369
25772
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25370
25773
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
25774
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25371
25775
|
build_block_with_cursor$1(node.else_branch, nir?.else_branch, status);
|
|
25776
|
+
const else_fields = status.heap_string_fields;
|
|
25372
25777
|
exit_scope_frame(status, else_frame);
|
|
25778
|
+
const merged = new Set(pre_heap_string_fields ?? []);
|
|
25779
|
+
for (const key of then_fields ?? []) merged.add(key);
|
|
25780
|
+
for (const key of else_fields ?? []) merged.add(key);
|
|
25781
|
+
status.heap_string_fields = merged;
|
|
25373
25782
|
} else if (node.if_branch) {
|
|
25374
25783
|
const then_frame = enter_scope_frame(status);
|
|
25375
25784
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25376
25785
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
25786
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25377
25787
|
build_block_with_cursor$1(node.if_branch, nir?.then_branch, status);
|
|
25378
25788
|
exit_scope_frame(status, then_frame);
|
|
25379
25789
|
}
|
|
@@ -25541,6 +25951,95 @@ function ctor_return_owned_string_fields(return_struct, call, heap_strings) {
|
|
|
25541
25951
|
}
|
|
25542
25952
|
return skip;
|
|
25543
25953
|
}
|
|
25954
|
+
/** The OVERRIDE value of a string field, when the field is overridden. */
|
|
25955
|
+
function override_value_for_field(overrides, field_name) {
|
|
25956
|
+
return overrides?.find((o) => o.name === field_name)?.value;
|
|
25957
|
+
}
|
|
25958
|
+
/**
|
|
25959
|
+
* The skip set for an OVERRIDE return (`return [ .. <base>, f = v, ... ]` in
|
|
25960
|
+
* its checked forms: a ctor call with `field_overrides`, or a base-bearing
|
|
25961
|
+
* anonymous struct literal). The overrides were applied to the return temp
|
|
25962
|
+
* BEFORE this analysis runs, so an overridden field holds the OVERRIDE
|
|
25963
|
+
* value's pair: it stays raw only when that value owns heap (a transferred
|
|
25964
|
+
* heap local, a fresh non-borrow expression). A non-overridden field keeps
|
|
25965
|
+
* the base's value: with `base_uniformly_owned` (a forwarded registered
|
|
25966
|
+
* normalizing base) it transfers raw; otherwise it follows `base_skip` (the
|
|
25967
|
+
* plain constructor-argument ownership analysis).
|
|
25968
|
+
*/
|
|
25969
|
+
function override_return_string_fields(return_struct, overrides, heap_strings, opts) {
|
|
25970
|
+
const skip = /* @__PURE__ */ new Set();
|
|
25971
|
+
for (const field of direct_string_fields(return_struct)) {
|
|
25972
|
+
const override = override_value_for_field(overrides, field.name);
|
|
25973
|
+
if (override) {
|
|
25974
|
+
if (tuple_return_owned_element(override, heap_strings)) skip.add(field.name);
|
|
25975
|
+
} else if (opts.base_uniformly_owned || opts.base_skip?.has(field.name)) skip.add(field.name);
|
|
25976
|
+
}
|
|
25977
|
+
return skip;
|
|
25978
|
+
}
|
|
25979
|
+
/**
|
|
25980
|
+
* The overridden string fields whose CONSTRUCTOR-SEEDED value owns heap and
|
|
25981
|
+
* is therefore displaced un-freed by a raw override store: a non-literal
|
|
25982
|
+
* default expression (the auto-constructor evaluates it fresh per
|
|
25983
|
+
* construction — `var string a = "de" + "fault"` allocates) is owned by the
|
|
25984
|
+
* constructed struct alone, so an override replacing the field orphans the
|
|
25985
|
+
* buffer unless it is reclaimed ahead of the store. Literal defaults are
|
|
25986
|
+
* rodata (never freed) and value-node defaults are consts/borrows (not
|
|
25987
|
+
* owned); a custom `#init`'s `self.<field> = <param>` seeds alias the
|
|
25988
|
+
* caller's argument temps in the common case, but a COMPUTED seed
|
|
25989
|
+
* (`self.a = s + "!"`, classified by `is_init_seed_heap`) owns heap — those
|
|
25990
|
+
* fields are displaced too.
|
|
25991
|
+
*/
|
|
25992
|
+
function override_displaced_string_fields(struct_node, overrides, is_init_seed_heap) {
|
|
25993
|
+
if (!overrides?.length) return [];
|
|
25994
|
+
const computed = init_computed_heap_string_fields(struct_node, is_init_seed_heap);
|
|
25995
|
+
const displaced = [];
|
|
25996
|
+
for (const override of overrides) {
|
|
25997
|
+
const field = struct_node.fields.find((f) => f.name === override.name);
|
|
25998
|
+
if (!field) continue;
|
|
25999
|
+
if (field.type.name !== "string" || field.type.is_array || field.type.is_view) continue;
|
|
26000
|
+
if (computed.has(override.name)) {
|
|
26001
|
+
displaced.push(override.name);
|
|
26002
|
+
continue;
|
|
26003
|
+
}
|
|
26004
|
+
const seed = field.value;
|
|
26005
|
+
if (!seed || seed.node_type === "value") continue;
|
|
26006
|
+
if (is_string_borrow(seed)) continue;
|
|
26007
|
+
displaced.push(override.name);
|
|
26008
|
+
}
|
|
26009
|
+
return displaced;
|
|
26010
|
+
}
|
|
26011
|
+
/**
|
|
26012
|
+
* The aarch64 counterpart of override_displaced_string_fields: the
|
|
26013
|
+
* overridden string fields whose ctor-seeded value is a non-literal
|
|
26014
|
+
* expression that produced a HEAP value — a non-literal DEFAULT, or a
|
|
26015
|
+
* custom `#init`'s computed seed (`init_computed_heap_string_fields` under
|
|
26016
|
+
* the same `is_owned_heap` classifier). The extra `is_owned_heap` check
|
|
26017
|
+
* (the aarch64 is_owned_heap_temp) matters because the aarch64 backend does
|
|
26018
|
+
* NOT strdup every string return: a literal-returning user function leaves
|
|
26019
|
+
* RODATA in the seeded field, which must never be freed. Used at the
|
|
26020
|
+
* expression-temp override boundaries (no heap_string_fields records exist
|
|
26021
|
+
* there, so the stores stay raw and the seed needs an explicit reclaim).
|
|
26022
|
+
*/
|
|
26023
|
+
function ctor_heap_displaced_string_fields(struct_node, overrides, is_owned_heap) {
|
|
26024
|
+
if (!overrides?.length) return [];
|
|
26025
|
+
const computed = init_computed_heap_string_fields(struct_node, is_owned_heap);
|
|
26026
|
+
const displaced = [];
|
|
26027
|
+
for (const override of overrides) {
|
|
26028
|
+
const field = struct_node.fields.find((f) => f.name === override.name);
|
|
26029
|
+
if (!field) continue;
|
|
26030
|
+
if (field.type.name !== "string" || field.type.is_array || field.type.is_view) continue;
|
|
26031
|
+
if (computed.has(override.name)) {
|
|
26032
|
+
displaced.push(override.name);
|
|
26033
|
+
continue;
|
|
26034
|
+
}
|
|
26035
|
+
const seed = field.value;
|
|
26036
|
+
if (!seed || seed.node_type === "value") continue;
|
|
26037
|
+
if (is_string_borrow(seed)) continue;
|
|
26038
|
+
if (!is_owned_heap(seed)) continue;
|
|
26039
|
+
displaced.push(override.name);
|
|
26040
|
+
}
|
|
26041
|
+
return displaced;
|
|
26042
|
+
}
|
|
25544
26043
|
//#endregion
|
|
25545
26044
|
//#region ../src/build_aarch64/build_return_node.ts
|
|
25546
26045
|
let temp_counter$1 = 0;
|
|
@@ -25723,7 +26222,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25723
26222
|
if (node.value?.node_type === "anon_struct") {
|
|
25724
26223
|
const anon = node.value;
|
|
25725
26224
|
const struct_size = get_struct_size(anon.type.name, status);
|
|
25726
|
-
const temp_name = `
|
|
26225
|
+
const temp_name = `_return_val_anon_${temp_counter$1++}`;
|
|
25727
26226
|
const offset = allocate_stack_space(status, struct_size);
|
|
25728
26227
|
status.stack_offsets.set(temp_name, offset);
|
|
25729
26228
|
if (!status.variable_types) status.variable_types = /* @__PURE__ */ new Map();
|
|
@@ -25733,6 +26232,14 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25733
26232
|
emit_var_address(status, "x1", temp_name);
|
|
25734
26233
|
emit_struct_copy("x0", "x1", 0, struct_size, status);
|
|
25735
26234
|
if (anon.base.is_moved) mark_moved_if_struct(anon.base, status);
|
|
26235
|
+
if (anon.base.node_type === "func_call" && is_normalized_struct_call(anon.base, status)) {
|
|
26236
|
+
const base_struct = status.structs.find((s) => s.name === anon.type.name && !s.is_simple_type && !s.is_class);
|
|
26237
|
+
if (base_struct) for (const field of direct_string_fields(base_struct)) {
|
|
26238
|
+
if (!anon.fields.some((o) => o.name === field.name)) continue;
|
|
26239
|
+
emit_asm(status, `ldp x0, x1, [x29, #${offset + get_field_offset(base_struct.name, field.name, status)}]\n`);
|
|
26240
|
+
emit_asm(status, `bl _nomen_free_wrap\n`);
|
|
26241
|
+
}
|
|
26242
|
+
}
|
|
25736
26243
|
emit_field_overrides(temp_name, anon, build_node, status);
|
|
25737
26244
|
emit_var_address(status, "x0", temp_name);
|
|
25738
26245
|
} else {
|
|
@@ -25768,7 +26275,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25768
26275
|
if (!is_str_lit && !is_numeric && raw_value !== "null" && !status.heap_strings?.has(raw_value)) needs_borrow_strdup = true;
|
|
25769
26276
|
} else if (is_container_borrow_access(node.value)) needs_borrow_strdup = true;
|
|
25770
26277
|
else if (node.value.node_type === "access" && node.value.access.node_type === "access_field") needs_borrow_strdup = true;
|
|
25771
|
-
else if (
|
|
26278
|
+
else if ((node.value.node_type === "func_call" || node.value.node_type === "access" && node.value.access.node_type === "access_func") && !status.last_result_is_heap && !is_container_borrow_access(node.value)) needs_borrow_strdup = true;
|
|
25772
26279
|
else if ((node.value.node_type === "match" || node.value.node_type === "switch") && !return_join_owned_string) needs_borrow_strdup = true;
|
|
25773
26280
|
}
|
|
25774
26281
|
if (needs_borrow_strdup) {
|
|
@@ -25777,6 +26284,15 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25777
26284
|
ensure_newline(status);
|
|
25778
26285
|
status.last_result_is_heap = true;
|
|
25779
26286
|
}
|
|
26287
|
+
if (!needs_borrow_strdup && current_return_is_string(status) && !is_container_borrow_access(node.value) && (node.value.node_type === "func_call" || node.value.node_type === "access" && node.value.access.node_type === "access_func") && !status.last_result_is_heap && status.heap_string_fields?.size) {
|
|
26288
|
+
emit_strdup_string(status);
|
|
26289
|
+
ensure_newline(status);
|
|
26290
|
+
status.last_result_is_heap = true;
|
|
26291
|
+
if (borrow_fn_name) {
|
|
26292
|
+
status.heap_returning_functions?.add(borrow_fn_name);
|
|
26293
|
+
if (borrow_mangled !== void 0) status.heap_returning_functions?.add(borrow_mangled);
|
|
26294
|
+
}
|
|
26295
|
+
}
|
|
25780
26296
|
if (node.value.node_type === "access" && node.value.access.node_type === "access_func" && node.value.access.name === "move" && current_return_is_string(status)) status.last_result_is_heap = true;
|
|
25781
26297
|
if (status.last_result_is_heap && current_return_is_string(status)) {
|
|
25782
26298
|
if (!status.heap_returning_functions) status.heap_returning_functions = /* @__PURE__ */ new Set();
|
|
@@ -25837,10 +26353,19 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25837
26353
|
}
|
|
25838
26354
|
emit_string_field_strdups_at(status, ret_struct.name, "x8", 0, skip_fields);
|
|
25839
26355
|
}
|
|
25840
|
-
if (node.value?.node_type === "func_call" && node.value.name === ret_struct.name && !ret_struct.name.startsWith("_")
|
|
25841
|
-
const
|
|
26356
|
+
if (node.value?.node_type === "func_call" && node.value.name === ret_struct.name && !ret_struct.name.startsWith("_")) {
|
|
26357
|
+
const call = node.value;
|
|
26358
|
+
const skip_fields = call.field_overrides?.length ? override_return_string_fields(ret_struct, call.field_overrides, status.heap_strings, { base_skip: ctor_return_owned_string_fields(ret_struct, call, status.heap_strings) }) : ctor_return_owned_string_fields(ret_struct, call, status.heap_strings);
|
|
25842
26359
|
emit_string_field_strdups_at(status, ret_struct.name, "x8", 0, skip_fields);
|
|
25843
26360
|
}
|
|
26361
|
+
{
|
|
26362
|
+
const anon = node.value?.node_type === "anon_struct" ? node.value : void 0;
|
|
26363
|
+
const forwarded_base = node.value?.node_type === "func_call" && node.value.field_overrides?.length ? node.value : anon?.base && anon.base.node_type === "func_call" ? anon.base : void 0;
|
|
26364
|
+
if (forwarded_base && node.value.name !== ret_struct.name && is_normalized_struct_call(forwarded_base, status)) {
|
|
26365
|
+
const skip_fields = override_return_string_fields(ret_struct, anon ? anon.fields : node.value.field_overrides, status.heap_strings, { base_uniformly_owned: true });
|
|
26366
|
+
emit_string_field_strdups_at(status, ret_struct.name, "x8", 0, skip_fields);
|
|
26367
|
+
}
|
|
26368
|
+
}
|
|
25844
26369
|
if (node.value?.node_type === "func_call" && String(node.value.name ?? "").startsWith("_Tuple_") && node.value.type?.name?.startsWith("_Tuple_")) {
|
|
25845
26370
|
const tuple_call = node.value;
|
|
25846
26371
|
const skip_fields = /* @__PURE__ */ new Set();
|
|
@@ -25943,6 +26468,8 @@ function build_switch_node$1(node, status, nir) {
|
|
|
25943
26468
|
const old_scoped_declarations = enter_scope_frame(status);
|
|
25944
26469
|
const pre_cache = status.buffer_data_cache;
|
|
25945
26470
|
const pre_array_cache = status.array_ptr_cache;
|
|
26471
|
+
const pre_heap_string_fields = status.heap_string_fields;
|
|
26472
|
+
const case_field_sets = [];
|
|
25946
26473
|
for (let i = 0; i < node.cases.length; i++) {
|
|
25947
26474
|
status.scoped_declarations = [];
|
|
25948
26475
|
build_node(node.cases[i].condition, status);
|
|
@@ -25951,7 +26478,9 @@ function build_switch_node$1(node, status, nir) {
|
|
|
25951
26478
|
else emit_asm(status, `beq end_switch_${label}\n`);
|
|
25952
26479
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25953
26480
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
26481
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25954
26482
|
build_block_with_cursor$1(node.cases[i].branch, nir?.arms[i]?.branch, status);
|
|
26483
|
+
case_field_sets.push(status.heap_string_fields);
|
|
25955
26484
|
emit_asm(status, `b end_switch_${label}\n`);
|
|
25956
26485
|
emit_asm(status, `sw_next_${label}_${i}:\n`);
|
|
25957
26486
|
}
|
|
@@ -25959,8 +26488,15 @@ function build_switch_node$1(node, status, nir) {
|
|
|
25959
26488
|
status.scoped_declarations = [];
|
|
25960
26489
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25961
26490
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
26491
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25962
26492
|
build_block_with_cursor$1(node.else_branch, nir?.otherwise ?? void 0, status);
|
|
26493
|
+
case_field_sets.push(status.heap_string_fields);
|
|
25963
26494
|
}
|
|
26495
|
+
if (case_field_sets.length > 1) {
|
|
26496
|
+
const merged = new Set(pre_heap_string_fields ?? []);
|
|
26497
|
+
for (const set of case_field_sets) for (const key of set) merged.add(key);
|
|
26498
|
+
status.heap_string_fields = merged;
|
|
26499
|
+
} else if (case_field_sets.length === 1) status.heap_string_fields = case_field_sets[0];
|
|
25964
26500
|
status.buffer_data_cache = pre_cache;
|
|
25965
26501
|
status.array_ptr_cache = pre_array_cache;
|
|
25966
26502
|
emit_asm(status, `end_switch_${label}:\n`);
|
|
@@ -29628,6 +30164,12 @@ function build_function_call_node$1(node, status) {
|
|
|
29628
30164
|
if (build_inline_function(inline_candidate, status)) return;
|
|
29629
30165
|
}
|
|
29630
30166
|
emit_asm(status, `bl ${func_name}\n`);
|
|
30167
|
+
{
|
|
30168
|
+
const callee = node.resolved_function;
|
|
30169
|
+
if (callee && !callee.params?.some((p) => p.is_self_param)) transfer_ref_param_field_records(callee, node.params, status, (name) => {
|
|
30170
|
+
return !!status.scoped_declarations?.some((d) => d.name === name) && !is_local_ref_var(name, status);
|
|
30171
|
+
});
|
|
30172
|
+
}
|
|
29631
30173
|
if (is_float_type$1(node.type.name)) emit_asm(status, `fmov x0, d0\n`);
|
|
29632
30174
|
if (outgoing_size > 0) emit_asm(status, `add sp, sp, #${outgoing_size}\n`);
|
|
29633
30175
|
if (status.buffer_data_cache) {
|
|
@@ -29678,6 +30220,11 @@ function build_function_call_node$1(node, status) {
|
|
|
29678
30220
|
const offset = status.stack_offsets.get(temp_addr);
|
|
29679
30221
|
emit_asm(status, `add x0, x29, #${offset}\n`);
|
|
29680
30222
|
if (node.field_overrides?.length) {
|
|
30223
|
+
const ctor_struct = status.structs.find((s) => s.name === node.name && !s.is_simple_type && !s.is_class);
|
|
30224
|
+
if (ctor_struct) for (const fname of ctor_heap_displaced_string_fields(ctor_struct, node.field_overrides, (seed) => is_owned_heap_temp(seed, status))) {
|
|
30225
|
+
emit_asm(status, `ldr x0, [x29, #${offset + get_field_offset(ctor_struct.name, fname, status)}]\n`);
|
|
30226
|
+
emit_free(status);
|
|
30227
|
+
}
|
|
29681
30228
|
emit_field_overrides(temp_addr, node, build_node, status);
|
|
29682
30229
|
emit_asm(status, `add x0, x29, #${offset}\n`);
|
|
29683
30230
|
}
|
|
@@ -30659,9 +31206,9 @@ function build_operation_node$1(node, status) {
|
|
|
30659
31206
|
if (left_type?.is_nullable && left_type.name === "string") {
|
|
30660
31207
|
status.code += `({ nomen_string _nss_${ns_tmp_counter} = `;
|
|
30661
31208
|
build_node$1(node.left_value, status);
|
|
30662
|
-
status.code += `; _nss_${ns_tmp_counter}.ptr ? _nss_${ns_tmp_counter} : `;
|
|
31209
|
+
status.code += `; _nss_${ns_tmp_counter}.ptr ? nomen_str_dup(_nss_${ns_tmp_counter}) : nomen_str_dup(`;
|
|
30663
31210
|
build_node$1(node.right_value, status);
|
|
30664
|
-
status.code +=
|
|
31211
|
+
status.code += `); })`;
|
|
30665
31212
|
ns_tmp_counter++;
|
|
30666
31213
|
} else if (is_nullable_scalar_type(left_type)) {
|
|
30667
31214
|
if (node.left_value.node_type === "func_call" && is_nullable_scalar_type(node.left_value.type)) {
|
|
@@ -32665,7 +33212,7 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
32665
33212
|
status.code += `;\n`;
|
|
32666
33213
|
return;
|
|
32667
33214
|
}
|
|
32668
|
-
if (!node.operator && node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field" && node.left_value.access.type?.name === "string" && !node.left_value.access.type?.is_ref && !node.left_value.access.type?.is_array) {
|
|
33215
|
+
if (!node.operator && node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field" && node.left_value.access.type?.name === "string" && !node.left_value.access.type?.is_ref && !node.left_value.access.type?.is_array && !node.raw_field_store) {
|
|
32669
33216
|
const access_lhs = node.left_value;
|
|
32670
33217
|
const field_access_node = access_lhs.access;
|
|
32671
33218
|
let target_type = type_from_value_node$1(access_lhs.target);
|
|
@@ -32953,7 +33500,10 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
32953
33500
|
const needs_destroy = struct_type ? struct_needs_destroy_by_name(struct_type.name, status) : false;
|
|
32954
33501
|
const rhs_is_free_factory = node.right_value.node_type === "func_call" && !rhs_references_var(node, lhs_name);
|
|
32955
33502
|
if (is_fresh_constructor(node, status) || rhs_is_free_factory) {
|
|
32956
|
-
if (struct_type)
|
|
33503
|
+
if (struct_type) {
|
|
33504
|
+
release_recorded_string_fields(status, struct_type, lhs_name);
|
|
33505
|
+
if (node.right_value.node_type === "func_call" && node.right_value.name === struct_type.name) record_ctor_heap_default_fields(lhs_name, struct_type, status, (seed) => is_owned_heap_temp$1(seed, status), new Set(node.right_value.field_overrides?.map((o) => o.name) ?? []));
|
|
33506
|
+
}
|
|
32957
33507
|
if (needs_destroy) emit_struct_destroys(status, struct_type, lhs_name);
|
|
32958
33508
|
} else if (is_self_method_call(node, lhs_name)) {} else if (!rhs_references_var(node, lhs_name)) {
|
|
32959
33509
|
if (needs_destroy) emit_struct_destroys(status, struct_type, lhs_name);
|
|
@@ -32998,6 +33548,12 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
32998
33548
|
}
|
|
32999
33549
|
}
|
|
33000
33550
|
}
|
|
33551
|
+
if (!node.operator && node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field" && node.left_value.target.node_type === "value" && node.left_value.target.value === "self" && status.current_function?.name === "#init" && claim_init_default_seed_reclaim(status, node.left_value.access.name, (expr) => is_owned_heap_temp$1(expr, status))) {
|
|
33552
|
+
const saved_seed_len = begin_code_scratch(status);
|
|
33553
|
+
build_node$1(node.left_value, status);
|
|
33554
|
+
const seed_access = end_code_scratch(status, saved_seed_len);
|
|
33555
|
+
status.code += `free(${seed_access}.ptr);\n`;
|
|
33556
|
+
}
|
|
33001
33557
|
build_node$1(node.left_value, status);
|
|
33002
33558
|
if (node.operator) status.code += ` ${node.operator.slice(0, -1)}= `;
|
|
33003
33559
|
else status.code += " = ";
|
|
@@ -33006,7 +33562,15 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
33006
33562
|
else emit_rhs_value$1(node.right_value, nir_rhs, status);
|
|
33007
33563
|
if (node.left_value.node_type === "value" && has_field_overrides(node.right_value)) {
|
|
33008
33564
|
const lname = node.left_value.value;
|
|
33009
|
-
|
|
33565
|
+
const rhs_call = node.right_value.node_type === "func_call" ? node.right_value : void 0;
|
|
33566
|
+
if (rhs_call?.field_overrides?.length && rhs_call.name === rhs_call.type?.name) {
|
|
33567
|
+
const override_struct = status.structs.find((s) => s.name === rhs_call.name && !s.is_simple_type && !s.is_class);
|
|
33568
|
+
if (override_struct) {
|
|
33569
|
+
status.code += `;\n`;
|
|
33570
|
+
for (const name of override_displaced_string_fields(override_struct, rhs_call.field_overrides, (expr) => is_owned_heap_temp$1(expr, status))) status.code += `free(${lname}.${name}.ptr);\n`;
|
|
33571
|
+
}
|
|
33572
|
+
}
|
|
33573
|
+
emit_field_overrides(lname, node.right_value, build_node$1, status, rhs_call?.field_overrides?.length && rhs_call.name === rhs_call.type?.name ? "" : ";\n", ";\n");
|
|
33010
33574
|
}
|
|
33011
33575
|
if (node.swap) {
|
|
33012
33576
|
status.code += `;\n`;
|
|
@@ -33913,8 +34477,20 @@ function build_declaration_node(node, status, nir_init, nir_swap) {
|
|
|
33913
34477
|
status.code += `${field_access} = `;
|
|
33914
34478
|
emit_swap_value$1(node.swap, nir_swap, status);
|
|
33915
34479
|
}
|
|
33916
|
-
if (has_field_overrides(node.value)) emit_field_overrides(safe_name, node.value, build_node$1, status, ";\n", ";\n");
|
|
33917
34480
|
record_call_init_string_fields(node, status);
|
|
34481
|
+
const override_ctor_call = node.value?.node_type === "func_call" && node.value.field_overrides?.length && node.type?.name && node.value.name === node.type.name ? node.value : void 0;
|
|
34482
|
+
if (override_ctor_call) {
|
|
34483
|
+
const override_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
|
|
34484
|
+
if (override_struct) {
|
|
34485
|
+
status.code += `;\n`;
|
|
34486
|
+
for (const name of override_displaced_string_fields(override_struct, override_ctor_call.field_overrides, (expr) => is_owned_heap_temp$1(expr, status))) status.code += `free(${safe_name}.${name}.ptr);\n`;
|
|
34487
|
+
}
|
|
34488
|
+
}
|
|
34489
|
+
if (node.value?.node_type === "func_call" && node.value.name === node.type?.name && !is_normalized_struct_call(node.value, status)) {
|
|
34490
|
+
const ctor_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
|
|
34491
|
+
if (ctor_struct) record_ctor_heap_default_fields(safe_name, ctor_struct, status, (seed) => is_owned_heap_temp$1(seed, status), new Set(node.value.field_overrides?.map((o) => o.name) ?? []));
|
|
34492
|
+
}
|
|
34493
|
+
if (has_field_overrides(node.value)) emit_field_overrides(safe_name, node.value, build_node$1, status, override_ctor_call ? "" : ";\n", ";\n");
|
|
33918
34494
|
}
|
|
33919
34495
|
}
|
|
33920
34496
|
}
|
|
@@ -34207,21 +34783,32 @@ function build_if_else_node(node, status, nir) {
|
|
|
34207
34783
|
status.scoped_declarations = enter_c_scope(status);
|
|
34208
34784
|
const old_deferred_frees = status.deferred_frees;
|
|
34209
34785
|
status.deferred_frees = [];
|
|
34786
|
+
const pre_heap_string_fields = status.heap_string_fields;
|
|
34210
34787
|
emit_allocations(node.condition, status);
|
|
34211
34788
|
status.code += `if (`;
|
|
34212
34789
|
build_condition(node.condition, status);
|
|
34213
34790
|
status.code += `) {\n`;
|
|
34791
|
+
let then_fields;
|
|
34214
34792
|
if (node.if_branch) {
|
|
34215
34793
|
build_block_with_cursor(node.if_branch, nir?.then_branch, status);
|
|
34216
34794
|
build_auto_free(status);
|
|
34217
34795
|
}
|
|
34796
|
+
then_fields = status.heap_string_fields;
|
|
34218
34797
|
leave_c_scope(status);
|
|
34219
34798
|
if (node.else_branch) {
|
|
34220
34799
|
status.scoped_declarations = enter_c_scope(status);
|
|
34221
34800
|
status.deferred_frees = [];
|
|
34801
|
+
status.heap_string_fields = pre_heap_string_fields;
|
|
34222
34802
|
status.code += `} else {\n`;
|
|
34223
34803
|
build_block_with_cursor(node.else_branch, nir?.else_branch, status);
|
|
34224
34804
|
build_auto_free(status);
|
|
34805
|
+
const else_fields = status.heap_string_fields;
|
|
34806
|
+
if (else_fields !== then_fields) {
|
|
34807
|
+
const merged = new Set(pre_heap_string_fields ?? []);
|
|
34808
|
+
for (const key of then_fields ?? []) merged.add(key);
|
|
34809
|
+
for (const key of else_fields ?? []) merged.add(key);
|
|
34810
|
+
status.heap_string_fields = merged;
|
|
34811
|
+
}
|
|
34225
34812
|
leave_c_scope(status);
|
|
34226
34813
|
}
|
|
34227
34814
|
status.code += `}\n`;
|
|
@@ -34558,14 +35145,31 @@ function build_return_node(node, status, nir_value) {
|
|
|
34558
35145
|
if (!returns_string_zero && !returns_struct_zero && !returns_view_value && (returns_borrowed_string || returns_string_literal || returns_borrow_var)) status.code += `)`;
|
|
34559
35146
|
status.code += `;\n`;
|
|
34560
35147
|
if (node.value.node_type === "access" && node.value.access?.node_type === "access_field" && !returns_string_zero && !returns_struct_zero && !returns_view_value && ret_type.name !== "string" && !ret_type.is_view && !ret_type.is_array && status.enums.find((e) => e.name === ret_type.name && e.has_associated_data)) status.code += `_return_val = ${ret_type.name}_copy(_return_val);\n`;
|
|
35148
|
+
const forwarded_override_base = node.value?.node_type === "func_call" && node.value.field_overrides?.length ? node.value : node.value?.node_type === "anon_struct" && node.value.base ? node.value.base : void 0;
|
|
35149
|
+
if (is_struct && !return_is_class && return_struct && forwarded_override_base?.node_type === "func_call" && node.value.name !== return_struct.name && is_normalized_struct_call(forwarded_override_base, status)) {
|
|
35150
|
+
const overrides = node.value.node_type === "func_call" ? node.value.field_overrides : node.value.fields;
|
|
35151
|
+
for (const field of direct_string_fields(return_struct)) {
|
|
35152
|
+
if (!overrides?.some((o) => o.name === field.name)) continue;
|
|
35153
|
+
status.code += `free(_return_val.${field.name}.ptr);\n`;
|
|
35154
|
+
}
|
|
35155
|
+
}
|
|
35156
|
+
if (is_struct && !return_is_class && return_struct && node.value?.node_type === "func_call" && node.value.name === return_struct.name) for (const name of override_displaced_string_fields(return_struct, node.value.field_overrides, (expr) => is_owned_heap_temp$1(expr, status))) status.code += `free(_return_val.${name}.ptr);\n`;
|
|
34561
35157
|
if (has_field_overrides(node.value)) emit_field_overrides("_return_val", node.value, build_node$1, status, "", ";\n");
|
|
34562
35158
|
const bare_local_return = node.value.node_type === "value" && node.value.value !== "0" && node.value.value !== "null" && !!returned_value_decl;
|
|
34563
35159
|
if (is_struct && !return_is_class && !ret_type.is_view && !ret_type.is_array && return_struct && !returns_struct_zero && bare_local_return) for (const field of direct_string_fields(return_struct)) {
|
|
34564
35160
|
if (returned_recorded_string_fields?.has(field.name)) continue;
|
|
34565
35161
|
status.code += `_return_val.${field.name} = nomen_str_dup(_return_val.${field.name});\n`;
|
|
34566
35162
|
}
|
|
34567
|
-
if (is_struct && !return_is_class && !ret_type.is_view && !ret_type.is_array && return_struct && !returns_struct_zero && node.value?.node_type === "func_call" && node.value.name === return_struct.name && !return_struct.name.startsWith("_")
|
|
34568
|
-
const
|
|
35163
|
+
if (is_struct && !return_is_class && !ret_type.is_view && !ret_type.is_array && return_struct && !returns_struct_zero && node.value?.node_type === "func_call" && node.value.name === return_struct.name && !return_struct.name.startsWith("_")) {
|
|
35164
|
+
const call = node.value;
|
|
35165
|
+
const skip = call.field_overrides?.length ? override_return_string_fields(return_struct, call.field_overrides, status.heap_strings, { base_skip: ctor_return_owned_string_fields(return_struct, call, status.heap_strings) }) : ctor_return_owned_string_fields(return_struct, call, status.heap_strings);
|
|
35166
|
+
for (const field of direct_string_fields(return_struct)) {
|
|
35167
|
+
if (skip.has(field.name)) continue;
|
|
35168
|
+
status.code += `_return_val.${field.name} = nomen_str_dup(_return_val.${field.name});\n`;
|
|
35169
|
+
}
|
|
35170
|
+
}
|
|
35171
|
+
if (is_struct && !return_is_class && !ret_type.is_view && !ret_type.is_array && return_struct && !returns_struct_zero && forwarded_override_base?.node_type === "func_call" && node.value.name !== return_struct.name && is_normalized_struct_call(forwarded_override_base, status)) {
|
|
35172
|
+
const skip = override_return_string_fields(return_struct, node.value.node_type === "func_call" ? node.value.field_overrides : node.value.fields, status.heap_strings, { base_uniformly_owned: true });
|
|
34569
35173
|
for (const field of direct_string_fields(return_struct)) {
|
|
34570
35174
|
if (skip.has(field.name)) continue;
|
|
34571
35175
|
status.code += `_return_val.${field.name} = nomen_str_dup(_return_val.${field.name});\n`;
|
|
@@ -34623,9 +35227,12 @@ function nullable_flag_expr_of_value(value, status) {
|
|
|
34623
35227
|
//#region ../src/build_c/build_switch_node.ts
|
|
34624
35228
|
function build_switch_node(node, status, nir) {
|
|
34625
35229
|
const old_scoped_declarations = status.scoped_declarations;
|
|
35230
|
+
const pre_heap_string_fields = status.heap_string_fields;
|
|
35231
|
+
const case_field_sets = [];
|
|
34626
35232
|
for (let i = 0; i < node.cases.length; i++) {
|
|
34627
35233
|
const c = node.cases[i];
|
|
34628
35234
|
status.scoped_declarations = enter_c_scope(status);
|
|
35235
|
+
status.heap_string_fields = pre_heap_string_fields;
|
|
34629
35236
|
const saved_code = begin_code_scratch(status);
|
|
34630
35237
|
build_node$1(c.condition, status);
|
|
34631
35238
|
let cond_code = end_code_scratch(status, saved_code);
|
|
@@ -34665,17 +35272,28 @@ function build_switch_node(node, status, nir) {
|
|
|
34665
35272
|
status.code += `if (${cond_code}) {\n`;
|
|
34666
35273
|
build_block_with_cursor(c.branch, nir?.arms[i]?.branch, status);
|
|
34667
35274
|
build_auto_free(status);
|
|
35275
|
+
case_field_sets.push(status.heap_string_fields);
|
|
34668
35276
|
leave_c_scope(status);
|
|
34669
35277
|
}
|
|
34670
35278
|
if (node.else_branch) {
|
|
34671
35279
|
status.code += "} else ";
|
|
34672
35280
|
status.scoped_declarations = enter_c_scope(status);
|
|
35281
|
+
status.heap_string_fields = pre_heap_string_fields;
|
|
34673
35282
|
status.code += `{\n`;
|
|
34674
35283
|
build_block_with_cursor(node.else_branch, nir?.otherwise ?? void 0, status);
|
|
34675
35284
|
build_auto_free(status);
|
|
35285
|
+
case_field_sets.push(status.heap_string_fields);
|
|
34676
35286
|
status.code += `}\n`;
|
|
34677
35287
|
leave_c_scope(status);
|
|
34678
35288
|
} else if (node.cases.length > 0) status.code += "}\n";
|
|
35289
|
+
if (case_field_sets.length > 1) {
|
|
35290
|
+
const first = case_field_sets[0];
|
|
35291
|
+
if (!case_field_sets.every((s) => s === first)) {
|
|
35292
|
+
const merged = new Set(pre_heap_string_fields ?? []);
|
|
35293
|
+
for (const set of case_field_sets) for (const key of set ?? []) merged.add(key);
|
|
35294
|
+
status.heap_string_fields = merged;
|
|
35295
|
+
}
|
|
35296
|
+
}
|
|
34679
35297
|
status.scoped_declarations = old_scoped_declarations;
|
|
34680
35298
|
}
|
|
34681
35299
|
//#endregion
|
|
@@ -35458,6 +36076,10 @@ function build_struct_node(node, status) {
|
|
|
35458
36076
|
if (node.traits.length) status.code += `self${accessor}_vt = &_${node.name}_traits;\n`;
|
|
35459
36077
|
const old_init_assigned_fields = status.init_assigned_fields;
|
|
35460
36078
|
status.init_assigned_fields = /* @__PURE__ */ new Set();
|
|
36079
|
+
const old_init_default_reclaimed = status.init_default_reclaimed_fields;
|
|
36080
|
+
status.init_default_reclaimed_fields = /* @__PURE__ */ new Set();
|
|
36081
|
+
const old_init_heap_string_fields = status.heap_string_fields;
|
|
36082
|
+
status.heap_string_fields = void 0;
|
|
35461
36083
|
for (const field of node.fields) if (field.value) {
|
|
35462
36084
|
status.init_assigned_fields?.add(`self.${field.name}`);
|
|
35463
36085
|
if (field.type.is_view && !field.type.is_array && !is_nullable_struct_type(field.type, status)) {
|
|
@@ -35525,6 +36147,8 @@ function build_struct_node(node, status) {
|
|
|
35525
36147
|
status.current_struct = old_current_struct;
|
|
35526
36148
|
status.current_function = old_current_function;
|
|
35527
36149
|
status.init_assigned_fields = old_init_assigned_fields;
|
|
36150
|
+
status.init_default_reclaimed_fields = old_init_default_reclaimed;
|
|
36151
|
+
status.heap_string_fields = old_init_heap_string_fields;
|
|
35528
36152
|
status.function_return_type = old_return_type;
|
|
35529
36153
|
}
|
|
35530
36154
|
build_struct_functions(node, status, true);
|
|
@@ -36441,6 +37065,8 @@ function build_function_node(node, status) {
|
|
|
36441
37065
|
status.heap_strings = /* @__PURE__ */ new Set();
|
|
36442
37066
|
const old_owned_string_vars = status.owned_string_vars;
|
|
36443
37067
|
status.owned_string_vars = /* @__PURE__ */ new Set();
|
|
37068
|
+
const old_heap_string_fields = status.heap_string_fields;
|
|
37069
|
+
status.heap_string_fields = void 0;
|
|
36444
37070
|
const old_heap_array_vars = status.heap_array_vars;
|
|
36445
37071
|
status.heap_array_vars = void 0;
|
|
36446
37072
|
const old_stack_array_lengths = status.stack_array_lengths;
|
|
@@ -36658,6 +37284,7 @@ function build_function_node(node, status) {
|
|
|
36658
37284
|
status.aliased_class_sources = old_aliased_class_sources;
|
|
36659
37285
|
status.string_borrow_vars = old_string_borrow_vars;
|
|
36660
37286
|
status.moved_string_vars = old_moved_string_vars;
|
|
37287
|
+
status.heap_string_fields = old_heap_string_fields;
|
|
36661
37288
|
status.heap_strings = old_heap_strings;
|
|
36662
37289
|
status.owned_string_vars = old_owned_string_vars;
|
|
36663
37290
|
status.heap_array_vars = old_heap_array_vars;
|
|
@@ -37090,6 +37717,12 @@ function build_function_call_node(node, status) {
|
|
|
37090
37717
|
const call_text = end_code_scratch(status, saved_call);
|
|
37091
37718
|
status.code += `({ unsigned char _nsd_${wrap_tmp} = 0; ${call_text}; })`;
|
|
37092
37719
|
}
|
|
37720
|
+
{
|
|
37721
|
+
const callee = node.resolved_function;
|
|
37722
|
+
if (callee && !callee.params?.some((p) => p.is_self_param)) transfer_ref_param_field_records(callee, node.params, status, (name) => {
|
|
37723
|
+
return !!find_decl_in_c_scopes(status, name) && !status.function_ref_params?.has(name) && !status.ref_class_params?.has(name) && !status.class_vars?.has(name);
|
|
37724
|
+
});
|
|
37725
|
+
}
|
|
37093
37726
|
if (node.name.startsWith("_string_interpolate_")) status.interpolate_string_counts.add(node.params.length - 1);
|
|
37094
37727
|
if (node.swap_params?.size) for (const [idx, swap_expr] of node.swap_params) {
|
|
37095
37728
|
const source = node.params[idx];
|
|
@@ -37497,6 +38130,10 @@ function build_access_node$1(node, status) {
|
|
|
37497
38130
|
return;
|
|
37498
38131
|
}
|
|
37499
38132
|
if (target_type.is_array && access_field.name === "length") {
|
|
38133
|
+
if (node.target.node_type === "value" && !!status.current_function?.params.some((p) => !p.is_self_param && p.name === node.target.value) && target_type.length) {
|
|
38134
|
+
build_node$1(target_type.length, status);
|
|
38135
|
+
return;
|
|
38136
|
+
}
|
|
37500
38137
|
const type = c_type(target_type.name);
|
|
37501
38138
|
status.code += "(sizeof(";
|
|
37502
38139
|
build_node$1(node.target, status);
|
|
@@ -37865,6 +38502,10 @@ function build_access_node$1(node, status) {
|
|
|
37865
38502
|
}
|
|
37866
38503
|
}
|
|
37867
38504
|
}
|
|
38505
|
+
{
|
|
38506
|
+
const ref_method = target_method ?? trait_default_func;
|
|
38507
|
+
if (ref_method) transfer_ref_param_field_records(ref_method, access_func.params ?? [], status, (name) => !!find_decl_in_c_scopes(status, name) && !status.function_ref_params?.has(name) && !status.ref_class_params?.has(name) && !status.class_vars?.has(name));
|
|
38508
|
+
}
|
|
37868
38509
|
const method_callee_params = (target_method ?? trait_default_func)?.params?.filter((p) => !p.is_self_param);
|
|
37869
38510
|
const lambda_arg_temps = /* @__PURE__ */ new Map();
|
|
37870
38511
|
for (let i = 0; i < access_func.params.length; i++) {
|
|
@@ -40283,6 +40924,11 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40283
40924
|
emit_var_address(status, "x1", name);
|
|
40284
40925
|
emit_struct_copy("x0", "x1", 0, struct_size, status);
|
|
40285
40926
|
}
|
|
40927
|
+
if (node.right_value.node_type === "func_call" && !paramReg && !is_local_ref_var(name, status)) {
|
|
40928
|
+
const rhs_call = node.right_value;
|
|
40929
|
+
const rhs_ctor_struct = status.structs.find((s) => s.name === rhs_call.name && !s.is_simple_type && !s.is_class);
|
|
40930
|
+
if (rhs_ctor_struct) record_ctor_heap_default_fields(name, rhs_ctor_struct, status, (seed) => is_owned_heap_temp(seed, status), new Set(rhs_call.field_overrides?.map((o) => o.name) ?? []));
|
|
40931
|
+
}
|
|
40286
40932
|
if (has_field_overrides(node.right_value)) emit_field_overrides(name, node.right_value, build_node, status);
|
|
40287
40933
|
build_swap(node, status, nir_swap);
|
|
40288
40934
|
return;
|
|
@@ -40503,12 +41149,13 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40503
41149
|
const move_source = rhs_is_string_var && node.last_use_move && move_on_last_use_enabled() ? rhs_value.value : void 0;
|
|
40504
41150
|
const explicit_move_source = !node.swap && !!rhs_value && rhs_value.is_moved && size === 16 && rhs_value.type?.name === "string" && !rhs_value.type?.is_view ? rhs_value.value : void 0;
|
|
40505
41151
|
const borrow_needs_dup = !node.swap && size === 16 && is_string_borrow(node.right_value) && !!status.force_heap_strings?.has(name);
|
|
41152
|
+
const move_source_owns_heap = move_source !== void 0 && !!status.heap_strings?.has(move_source);
|
|
40506
41153
|
if (rhs_is_view_into_owned) {
|
|
40507
41154
|
if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
|
|
40508
41155
|
status.heap_strings.add(name);
|
|
40509
|
-
} else if (rhs_is_string_var && move_source === void 0 || borrow_needs_dup) emit_strdup_string(status);
|
|
41156
|
+
} else if (rhs_is_string_var && (move_source === void 0 || !move_source_owns_heap) || borrow_needs_dup) emit_strdup_string(status);
|
|
40510
41157
|
if (rhs_is_string_var || borrow_needs_dup) {
|
|
40511
|
-
if (move_source !== void 0) status.heap_strings?.delete(move_source);
|
|
41158
|
+
if (move_source !== void 0 && move_source_owns_heap) status.heap_strings?.delete(move_source);
|
|
40512
41159
|
if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
|
|
40513
41160
|
status.heap_strings.add(name);
|
|
40514
41161
|
} else if (status.last_result_is_heap) {
|
|
@@ -40586,7 +41233,7 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40586
41233
|
emit_asm(status, `str xzr, [x0, #${offset + 8}]\n`);
|
|
40587
41234
|
}
|
|
40588
41235
|
}
|
|
40589
|
-
} else if (field_is_struct_string(target_type, field_type, status) && !node.operator && (() => {
|
|
41236
|
+
} else if (field_is_struct_string(target_type, field_type, status) && !node.operator && !node.raw_field_store && (() => {
|
|
40590
41237
|
const st = field_is_struct_string(target_type, field_type, status);
|
|
40591
41238
|
const tv = access.target.node_type === "value" ? access.target.value : "";
|
|
40592
41239
|
return st.target_is_class || tv !== "" && tv !== "self";
|
|
@@ -40614,12 +41261,16 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40614
41261
|
ensure_newline(status);
|
|
40615
41262
|
}
|
|
40616
41263
|
let rhs_is_heap = status.last_result_is_heap;
|
|
40617
|
-
|
|
40618
|
-
|
|
40619
|
-
|
|
41264
|
+
if (rhs_is_null_literal) {} else if (!rhs_is_heap) {
|
|
41265
|
+
const rhs_type_nullable = !!node.right_value.type?.is_nullable;
|
|
41266
|
+
if (field_type?.is_nullable || rhs_type_nullable) {
|
|
41267
|
+
const skip_dup = `.Lskip_field_dup_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
41268
|
+
emit_asm(status, `cbz x0, ${skip_dup}\n`);
|
|
41269
|
+
emit_strdup(status);
|
|
41270
|
+
emit_asm(status, `${skip_dup}:\n`);
|
|
41271
|
+
} else emit_strdup(status);
|
|
40620
41272
|
rhs_is_heap = true;
|
|
40621
41273
|
}
|
|
40622
|
-
if (rhs_is_null_literal) {} else if (is_class_target && !rhs_is_heap) emit_strdup(status);
|
|
40623
41274
|
mark_moved_if_struct(node.right_value, status);
|
|
40624
41275
|
if (field_type?.name === "string") {
|
|
40625
41276
|
emit_asm(status, `stp x0, x1, [sp, #-16]!\n`);
|
|
@@ -40805,6 +41456,14 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40805
41456
|
get_base_address(access, status, "x0");
|
|
40806
41457
|
emit_asm(status, `str x0, [sp, #-16]!\n`);
|
|
40807
41458
|
}
|
|
41459
|
+
if (!node.operator && access.target.node_type === "value" && access.target.value === "self" && status.current_function?.name === "#init" && claim_init_default_seed_reclaim(status, field_name, (expr) => is_owned_heap_temp(expr, status))) {
|
|
41460
|
+
if (base_reg !== void 0) emit_asm(status, `ldr x0, [${base_reg}, #${offset}]\n`);
|
|
41461
|
+
else {
|
|
41462
|
+
emit_asm(status, `ldr x0, [sp]\n`);
|
|
41463
|
+
emit_asm(status, `ldr x0, [x0, #${offset}]\n`);
|
|
41464
|
+
}
|
|
41465
|
+
emit_free(status);
|
|
41466
|
+
}
|
|
40808
41467
|
emit_rhs_value(node.right_value, nir_rhs, status);
|
|
40809
41468
|
ensure_newline(status);
|
|
40810
41469
|
mark_moved_if_struct(node.right_value, status);
|
|
@@ -44856,7 +45515,10 @@ function build_access_method(node, access_func, status) {
|
|
|
44856
45515
|
}
|
|
44857
45516
|
if (node.target.node_type === "value" && target_struct && !target_struct.is_class && !trait_target) {
|
|
44858
45517
|
const target_method = target_struct.functions.find((f) => f.name === access_func.name);
|
|
44859
|
-
if (target_method)
|
|
45518
|
+
if (target_method) {
|
|
45519
|
+
drop_self_written_string_field_records(status, node.target.value, scan_self_string_field_writes(target_struct, target_method));
|
|
45520
|
+
transfer_ref_param_field_records(target_method, access_func.params ?? [], status, (name) => !!status.scoped_declarations?.some((d) => d.name === name) && !is_local_ref_var(name, status));
|
|
45521
|
+
}
|
|
44860
45522
|
}
|
|
44861
45523
|
if (method_name.endsWith("_to_string") && method_name !== "string_to_string") status.last_result_is_heap = true;
|
|
44862
45524
|
if (trait_target && trait_func?.return_type?.name === "string" && !trait_func.return_type?.is_view && trait_method_all_conformers_owned(trait_target, access_func.name, status)) status.last_result_is_heap = true;
|
|
@@ -45895,10 +46557,14 @@ function gather_normalized_struct_returners(root, status) {
|
|
|
45895
46557
|
if (!return_normalizing.has(fn.name)) continue;
|
|
45896
46558
|
const ret_struct = structs.find((st) => st.name === fn.return_type?.name && !st.is_simple_type && !st.is_class);
|
|
45897
46559
|
for (const ret of direct_returns(fn)) {
|
|
45898
|
-
|
|
46560
|
+
let value = ret.value ?? void 0;
|
|
46561
|
+
if (value?.node_type === "anon_struct") {
|
|
46562
|
+
const base = value.base;
|
|
46563
|
+
if (base?.node_type === "func_call") value = base;
|
|
46564
|
+
}
|
|
45899
46565
|
if (value && value.node_type === "value") continue;
|
|
45900
46566
|
if (value && value.node_type === "func_call" && return_normalizing.has(value.name ?? "")) continue;
|
|
45901
|
-
if (value && value.node_type === "func_call" && !!ret_struct &&
|
|
46567
|
+
if (value && value.node_type === "func_call" && !!ret_struct && !ret_struct.name.startsWith("_") && (value.name === ret_struct.name || !!value.field_overrides?.length && return_normalizing.has(value.name ?? ""))) continue;
|
|
45902
46568
|
return_normalizing.delete(fn.name);
|
|
45903
46569
|
changed = true;
|
|
45904
46570
|
break;
|
|
@@ -46130,7 +46796,7 @@ function build(root, options = {}) {
|
|
|
46130
46796
|
status.code = status.code + "\n" + status.c_runtime_defs;
|
|
46131
46797
|
status.c_runtime_defs = void 0;
|
|
46132
46798
|
}
|
|
46133
|
-
status.code = "typedef struct { void* ptr; long len; } nomen_view;\ntypedef struct { char* ptr; long len; } nomen_string;\n#define nomen_str_lit(s, n) ((nomen_string){ (s), (long)(n) })\n#include <string.h>\n" + (options.audit ? `void *nomen_strdup_wrap(const char *);\n` : "") + "static nomen_string nomen_str_dup(nomen_string s) {\n nomen_string r = { strdup(s.ptr), s.len };\n return r;\n}\n" + status.code;
|
|
46799
|
+
status.code = "typedef struct { void* ptr; long len; } nomen_view;\ntypedef struct { char* ptr; long len; } nomen_string;\n#define nomen_str_lit(s, n) ((nomen_string){ (s), (long)(n) })\n#include <string.h>\n" + (options.audit ? `void *nomen_strdup_wrap(const char *);\n` : "") + "static nomen_string nomen_str_dup(nomen_string s) {\n if (!s.ptr) return s;\n nomen_string r = { strdup(s.ptr), s.len };\n return r;\n}\n" + status.code;
|
|
46134
46800
|
if (options.audit) {
|
|
46135
46801
|
status.code = wrap_c_allocators(status.code);
|
|
46136
46802
|
status.headers = "void *nomen_malloc_wrap(unsigned long);\nvoid *nomen_calloc_wrap(unsigned long, unsigned long);\nvoid *nomen_realloc_wrap(void *, unsigned long);\nvoid nomen_free_wrap(void *);\nvoid *nomen_strdup_wrap(const char *);\nvoid nomen_audit_check(void);\n" + wrap_c_allocators(status.headers);
|
|
@@ -51469,6 +52135,21 @@ function check_struct_node(struct, status) {
|
|
|
51469
52135
|
status.values.length = values_length_before_fields;
|
|
51470
52136
|
rewrite_generic_buffer_fields(struct, status);
|
|
51471
52137
|
mark_owning_auto_init_params(struct, status);
|
|
52138
|
+
if (!struct.is_library) for (const field of struct.fields) {
|
|
52139
|
+
if (field.declaration !== "move") continue;
|
|
52140
|
+
if (!field.type.name || field.type.is_ref || field.type.is_view) continue;
|
|
52141
|
+
if (field.type.is_nullable) continue;
|
|
52142
|
+
if (is_class_type$1(field.type.name, status)) continue;
|
|
52143
|
+
if (status.traits.find((t) => t.name === field.type.name)) continue;
|
|
52144
|
+
if (!is_owning_struct_type_requiring_move(new Type(mono_type_name(field.type)), status)) continue;
|
|
52145
|
+
if (!status.warnings) status.warnings = [];
|
|
52146
|
+
status.warnings.push({
|
|
52147
|
+
message: `'move' on field '${field.name}' is redundant: '${type_display(field.type)}' is a value struct — its ownership is derived from the type, so plain 'var' behaves identically`,
|
|
52148
|
+
start: field.start,
|
|
52149
|
+
line: 0,
|
|
52150
|
+
column: 0
|
|
52151
|
+
});
|
|
52152
|
+
}
|
|
51472
52153
|
status.types.push(struct.name);
|
|
51473
52154
|
status.structs.push(struct);
|
|
51474
52155
|
for (let func of struct.functions) {
|
|
@@ -51649,6 +52330,11 @@ function signature_matches(struct_func, trait_func, substitution) {
|
|
|
51649
52330
|
function types_match(a, b) {
|
|
51650
52331
|
return a.name === b.name && !!a.is_ref === !!b.is_ref && !!a.is_view === !!b.is_view && !!a.is_array === !!b.is_array;
|
|
51651
52332
|
}
|
|
52333
|
+
/** The user-visible spelling of a type (`List<int>`, `Map<string, int>`). */
|
|
52334
|
+
function type_display(t) {
|
|
52335
|
+
if (!t.type_args?.length) return t.name ?? "?";
|
|
52336
|
+
return `${t.name}<${t.type_args.map(type_display).join(", ")}>`;
|
|
52337
|
+
}
|
|
51652
52338
|
//#endregion
|
|
51653
52339
|
//#region ../src/check/utils/auto_derive.ts
|
|
51654
52340
|
function struct_has_function(struct, name) {
|
|
@@ -55252,7 +55938,14 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
55252
55938
|
const named = status.functions.findLast((f) => f.name === param_value);
|
|
55253
55939
|
if (named) param.resolved_function = named;
|
|
55254
55940
|
}
|
|
55255
|
-
if (param_type.is_array && param_type.length && !func_param.
|
|
55941
|
+
if (param_type.is_array && param_type.length && !func_param.is_variadic) {
|
|
55942
|
+
if (!func_param.type.length) func_param.type.length = param_type.length;
|
|
55943
|
+
else {
|
|
55944
|
+
const stamped = String(func_param.type.length.value);
|
|
55945
|
+
const incoming = String(param_type.length.value);
|
|
55946
|
+
if (stamped !== incoming) add_error(status, `Call passes ${incoming} elements to array parameter '${func_param.name}' whose compile-time length is ${stamped} — the raw array ABI carries no runtime length, so one parameter serves one compile-time length; use Array<${func_param.type.name}> for varying lengths`, param.start);
|
|
55947
|
+
}
|
|
55948
|
+
}
|
|
55256
55949
|
let arg_value;
|
|
55257
55950
|
if (param.node_type === "value") {
|
|
55258
55951
|
const vn = param;
|
|
@@ -56603,7 +57296,7 @@ function check_as_array_or_inferred_tuple(array, status, forced_tuple_types) {
|
|
|
56603
57296
|
array.type.is_array = true;
|
|
56604
57297
|
array.type.is_nullable = old_expected.is_nullable;
|
|
56605
57298
|
array.type.type_args = old_expected.type_args;
|
|
56606
|
-
array.type.length = old_expected.length;
|
|
57299
|
+
if (!old_expected.length || String(old_expected.length.value) === String(array.values.length)) array.type.length = old_expected.length;
|
|
56607
57300
|
} else if (!array.type.name) {
|
|
56608
57301
|
const first_type = value_types[0];
|
|
56609
57302
|
if (first_type) {
|
|
@@ -57954,6 +58647,11 @@ function check_switch_node(switch_node, status) {
|
|
|
57954
58647
|
let branch_statuses = [];
|
|
57955
58648
|
for (let switch_case of switch_node.cases) {
|
|
57956
58649
|
check_node(switch_case.condition, status);
|
|
58650
|
+
if (status.allocations.length) {
|
|
58651
|
+
switch_case.condition.allocations ??= [];
|
|
58652
|
+
switch_case.condition.allocations.push(...status.allocations);
|
|
58653
|
+
status.allocations.length = 0;
|
|
58654
|
+
}
|
|
57957
58655
|
const condition_type = type_from_value_node(switch_case.condition, status);
|
|
57958
58656
|
if (!is_bool_condition(condition_type)) add_error(status, `Switch case condition must be a bool, not ${type_name(condition_type)}`, switch_case.condition.start);
|
|
57959
58657
|
let case_status = clone_status(status);
|