nomen-lang 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1320 -268
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7147,14 +7147,37 @@ function call_result_type(node) {
|
|
|
7147
7147
|
* so `null == 0` conflates them. Nullable `string` (in-band fat zero) and
|
|
7148
7148
|
* nullable `func`/class values (null pointer) keep their in-band null and
|
|
7149
7149
|
* are NOT nullable scalars.
|
|
7150
|
+
*
|
|
7151
|
+
* The same holds for a nullable SIMPLE enum (`Color?`) or bitset
|
|
7152
|
+
* (`Permissions?`): the in-band zero (the first case's tag 0 / an empty
|
|
7153
|
+
* bitset) is a real value, so `null` needs the companion flag. Enum-with-data
|
|
7154
|
+
* and generic template enums are NOT nullable scalars — the checker rejects
|
|
7155
|
+
* `?` on enums with associated data, so every nullable enum that reaches the
|
|
7156
|
+
* backends is simple.
|
|
7150
7157
|
*/
|
|
7151
|
-
/** True if
|
|
7152
|
-
|
|
7158
|
+
/** True if the TYPE NAME is a built-in scalar, simple enum, or bitset — the
|
|
7159
|
+
* shapes that get the `<slot>_has` flag when nullable. */
|
|
7160
|
+
function is_nullable_scalar_name(name, tables) {
|
|
7161
|
+
if (!name) return false;
|
|
7162
|
+
const info = get_built_in_type(name);
|
|
7163
|
+
if (info) return info.kind !== "string" && info.kind !== "func";
|
|
7164
|
+
const enum_node = tables.enums.findLast((e) => (e.name === name || e.source_name === name) && !e.is_generic);
|
|
7165
|
+
if (enum_node) return !enum_node.has_associated_data;
|
|
7166
|
+
return tables.bitsets.some((b) => b.name === name || b.source_name === name);
|
|
7167
|
+
}
|
|
7168
|
+
/** True if `type` is a nullable built-in scalar, simple enum, or bitset. */
|
|
7169
|
+
function is_nullable_scalar_type(type, tables) {
|
|
7153
7170
|
if (!type?.is_nullable) return false;
|
|
7154
7171
|
if (type.is_array || type.is_ref || type.storage_kind === "view") return false;
|
|
7155
7172
|
if (type.is_pointer) return false;
|
|
7156
|
-
|
|
7157
|
-
|
|
7173
|
+
return is_nullable_scalar_name(type.name, tables);
|
|
7174
|
+
}
|
|
7175
|
+
/** True if `type` is a nullable struct (see nullable_struct.ts) or nullable scalar. */
|
|
7176
|
+
function is_nullable_flagged_type(type, status) {
|
|
7177
|
+
if (!type?.is_nullable) return false;
|
|
7178
|
+
if (type.name === "string") return false;
|
|
7179
|
+
if (type.name === "func") return false;
|
|
7180
|
+
return is_nullable_scalar_type(type, status) || !!status.structs.find((s) => s.name === type.name && !s.is_class && !s.is_simple_type);
|
|
7158
7181
|
}
|
|
7159
7182
|
//#endregion
|
|
7160
7183
|
//#region ../src/build_common/nullable_struct.ts
|
|
@@ -7546,7 +7569,7 @@ function scan_self_string_field_writes(struct, method) {
|
|
|
7546
7569
|
const scan_method = (func) => {
|
|
7547
7570
|
if (visited.has(func.name)) return;
|
|
7548
7571
|
visited.add(func.name);
|
|
7549
|
-
walk$
|
|
7572
|
+
walk$4(func.statements ?? [], (n) => {
|
|
7550
7573
|
if (n.node_type === "assign") {
|
|
7551
7574
|
const lhs = n.left_value;
|
|
7552
7575
|
if (lhs?.node_type !== "access") return;
|
|
@@ -7590,10 +7613,10 @@ function drop_self_written_string_field_records(status, receiver_name, fields) {
|
|
|
7590
7613
|
* statement arrays) — skipping `parent`/`scope` back-references and NOT
|
|
7591
7614
|
* descending INTO nested `func`/`struct`/`trait` declarations (a nested
|
|
7592
7615
|
* function's body is a separate function, not part of this one's writes). */
|
|
7593
|
-
function walk$
|
|
7616
|
+
function walk$4(value, cb) {
|
|
7594
7617
|
if (!value || typeof value !== "object") return;
|
|
7595
7618
|
if (Array.isArray(value)) {
|
|
7596
|
-
for (const item of value) walk$
|
|
7619
|
+
for (const item of value) walk$4(item, cb);
|
|
7597
7620
|
return;
|
|
7598
7621
|
}
|
|
7599
7622
|
const n = value;
|
|
@@ -7602,7 +7625,177 @@ function walk$3(value, cb) {
|
|
|
7602
7625
|
if (is_boundary) return;
|
|
7603
7626
|
for (const key of Object.keys(value)) {
|
|
7604
7627
|
if (key === "parent" || key === "scope" || key === "node_type") continue;
|
|
7605
|
-
walk$
|
|
7628
|
+
walk$4(value[key], cb);
|
|
7629
|
+
}
|
|
7630
|
+
}
|
|
7631
|
+
//#endregion
|
|
7632
|
+
//#region ../src/build_common/has_string_fields.ts
|
|
7633
|
+
/**
|
|
7634
|
+
* Whether any field (recursively, through nested owning value structs)
|
|
7635
|
+
* is an owned `string`. Shared by BOTH backends' owning-Buffer element
|
|
7636
|
+
* specialization (the slot deep-copies strdup'd strings, so a
|
|
7637
|
+
* string-field element is owning even when `struct_needs_destroy`
|
|
7638
|
+
* ignores string-only locals) — the C comment in
|
|
7639
|
+
* owning_buffer_specialize records the divergence this once caused.
|
|
7640
|
+
* `ref` and `view T` fields are skipped: a ref aliases the caller's
|
|
7641
|
+
* storage and a view's byte copy owns nothing.
|
|
7642
|
+
*/
|
|
7643
|
+
function has_string_fields(node, status) {
|
|
7644
|
+
for (const field of node.fields) {
|
|
7645
|
+
if (field.type.is_ref) continue;
|
|
7646
|
+
if (field.type.is_view) continue;
|
|
7647
|
+
if (field.type.name === "string" && !field.type.is_array) return true;
|
|
7648
|
+
const field_struct = status.structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_generic);
|
|
7649
|
+
if (field_struct && !field_struct.is_class && has_string_fields(field_struct, status)) return true;
|
|
7650
|
+
}
|
|
7651
|
+
return false;
|
|
7652
|
+
}
|
|
7653
|
+
/**
|
|
7654
|
+
* The DIRECT `string` fields of a value struct (nested struct fields are not
|
|
7655
|
+
* descended into — nested string fields are outside the per-field ownership
|
|
7656
|
+
* tracking everywhere else too). Used by the return-boundary normalization:
|
|
7657
|
+
* the returned struct's unrecorded fields are strdup'd so the value the
|
|
7658
|
+
* caller receives is uniformly heap-owned.
|
|
7659
|
+
*/
|
|
7660
|
+
function direct_string_fields(node) {
|
|
7661
|
+
return node.fields.filter((f) => !f.type.is_ref && !f.type.is_view && !f.type.is_array && f.type.name === "string");
|
|
7662
|
+
}
|
|
7663
|
+
//#endregion
|
|
7664
|
+
//#region ../src/build_common/scan_ref_string_writes.ts
|
|
7665
|
+
/**
|
|
7666
|
+
* The plain `string` fields of `func`'s `ref` value-struct parameters that the
|
|
7667
|
+
* function stores through with a DIRECT top-level statement — transitively
|
|
7668
|
+
* through top-level calls that forward the same param as their own `ref`
|
|
7669
|
+
* argument (callees resolved via the checker's `resolved_function` stamps).
|
|
7670
|
+
*
|
|
7671
|
+
* Both backends' string-field store paths strdup every non-heap RHS into an
|
|
7672
|
+
* owned copy before storing (a field is never a borrow once stored), so a
|
|
7673
|
+
* definitely-executed store leaves the field heap-owned. That is the fact a
|
|
7674
|
+
* CALL SITE transfers onto the caller's `heap_string_fields` records (the
|
|
7675
|
+
* add-direction mirror of `drop_self_written_string_field_records`): the
|
|
7676
|
+
* callee's own records are scope-local and die at return, so without the
|
|
7677
|
+
* transfer the stored copy leaks.
|
|
7678
|
+
*
|
|
7679
|
+
* Only DIRECT statements qualify: a store nested in a branch/loop may not
|
|
7680
|
+
* execute, and a record over the pre-call value would free it at the caller's
|
|
7681
|
+
* scope exit (possibly rodata — an invalid free). Memoized on the function
|
|
7682
|
+
* node; forwarding cycles are cut by a visited set (the cycle member's own
|
|
7683
|
+
* contribution is skipped, bounded-leak).
|
|
7684
|
+
*/
|
|
7685
|
+
function scan_ref_param_string_field_writes(func, structs) {
|
|
7686
|
+
const stamp = func;
|
|
7687
|
+
if (stamp._ref_param_string_field_writes) return stamp._ref_param_string_field_writes;
|
|
7688
|
+
const result = /* @__PURE__ */ new Map();
|
|
7689
|
+
stamp._ref_param_string_field_writes = result;
|
|
7690
|
+
const tracked = /* @__PURE__ */ new Map();
|
|
7691
|
+
for (const param of func.params ?? []) {
|
|
7692
|
+
if (param.is_self_param) continue;
|
|
7693
|
+
if (!(param.is_ref || param.type.is_ref)) continue;
|
|
7694
|
+
if (param.type.is_array || param.type.is_view) continue;
|
|
7695
|
+
const fields = ref_param_writable_fields(param.type.name, structs);
|
|
7696
|
+
if (fields.size) tracked.set(param.name, fields);
|
|
7697
|
+
}
|
|
7698
|
+
if (tracked.size) scan_function(func, tracked, structs, result, /* @__PURE__ */ new Set());
|
|
7699
|
+
return result;
|
|
7700
|
+
}
|
|
7701
|
+
/** The writable plain string fields of the value struct `type_name` resolves
|
|
7702
|
+
* to, or the empty set (non-struct, class, or no string fields). */
|
|
7703
|
+
function ref_param_writable_fields(type_name, structs) {
|
|
7704
|
+
if (!type_name) return /* @__PURE__ */ new Set();
|
|
7705
|
+
const struct = structs.find((s) => s.name === type_name && !s.is_simple_type && !s.is_class);
|
|
7706
|
+
if (!struct) return /* @__PURE__ */ new Set();
|
|
7707
|
+
return new Set(direct_string_fields(struct).map((f) => f.name));
|
|
7708
|
+
}
|
|
7709
|
+
function scan_function(func, tracked, structs, result, visiting) {
|
|
7710
|
+
visiting.add(func);
|
|
7711
|
+
for (const stmt of func.statements ?? []) {
|
|
7712
|
+
if (stmt.node_type === "assign") {
|
|
7713
|
+
const lhs = stmt.left_value;
|
|
7714
|
+
if (lhs?.node_type === "access") {
|
|
7715
|
+
const access = lhs;
|
|
7716
|
+
if (access.access.node_type === "access_field" && access.target.node_type === "value") {
|
|
7717
|
+
const name = access.target.value ?? "";
|
|
7718
|
+
const fields = tracked.get(name);
|
|
7719
|
+
const field = access.access.name ?? "";
|
|
7720
|
+
if (fields?.has(field)) {
|
|
7721
|
+
if (!result.has(name)) result.set(name, /* @__PURE__ */ new Set());
|
|
7722
|
+
result.get(name).add(field);
|
|
7723
|
+
}
|
|
7724
|
+
}
|
|
7725
|
+
}
|
|
7726
|
+
continue;
|
|
7727
|
+
}
|
|
7728
|
+
const call = ref_forward_call(stmt);
|
|
7729
|
+
if (!call) continue;
|
|
7730
|
+
const [callee, args] = call;
|
|
7731
|
+
const non_self_params = (callee.params ?? []).filter((p) => !p.is_self_param);
|
|
7732
|
+
for (let i = 0; i < args.length && i < non_self_params.length; i++) {
|
|
7733
|
+
const arg = args[i];
|
|
7734
|
+
if (arg?.node_type !== "value") continue;
|
|
7735
|
+
const arg_name = arg.value ?? "";
|
|
7736
|
+
const my_fields = tracked.get(arg_name);
|
|
7737
|
+
if (!my_fields) continue;
|
|
7738
|
+
const callee_param = non_self_params[i];
|
|
7739
|
+
if (!(callee_param.is_ref || callee_param.type.is_ref)) continue;
|
|
7740
|
+
if (visiting.has(callee)) continue;
|
|
7741
|
+
const callee_writes = scan_ref_param_string_field_writes(callee, structs).get(callee_param.name);
|
|
7742
|
+
if (!callee_writes?.size) continue;
|
|
7743
|
+
if (!result.has(arg_name)) result.set(arg_name, /* @__PURE__ */ new Set());
|
|
7744
|
+
const mine = result.get(arg_name);
|
|
7745
|
+
for (const field of callee_writes) if (my_fields.has(field)) mine.add(field);
|
|
7746
|
+
}
|
|
7747
|
+
}
|
|
7748
|
+
visiting.delete(func);
|
|
7749
|
+
}
|
|
7750
|
+
/**
|
|
7751
|
+
* The forwarded-call shape of a top-level statement: a resolved free-function
|
|
7752
|
+
* call or method call, with its argument list (the receiver is NOT included).
|
|
7753
|
+
*/
|
|
7754
|
+
function ref_forward_call(stmt) {
|
|
7755
|
+
if (stmt.node_type === "func_call") {
|
|
7756
|
+
const call = stmt;
|
|
7757
|
+
if (!call.resolved_function) return void 0;
|
|
7758
|
+
return [call.resolved_function, call.params ?? []];
|
|
7759
|
+
}
|
|
7760
|
+
if (stmt.node_type === "access") {
|
|
7761
|
+
const access = stmt;
|
|
7762
|
+
if (access.access.node_type !== "access_func") return void 0;
|
|
7763
|
+
const func_node = access.access.resolved_function;
|
|
7764
|
+
if (!func_node) return void 0;
|
|
7765
|
+
return [func_node, access.access.params ?? []];
|
|
7766
|
+
}
|
|
7767
|
+
}
|
|
7768
|
+
//#endregion
|
|
7769
|
+
//#region ../src/build_common/transfer_ref_param_records.ts
|
|
7770
|
+
/**
|
|
7771
|
+
* The add-direction mirror of drop_self_written_string_field_records: at a
|
|
7772
|
+
* call site whose callee takes a `ref` value-struct parameter, the callee's
|
|
7773
|
+
* string-field stores write through to the CALLER's variable — but the
|
|
7774
|
+
* callee's heap_string_fields records are scope-local and die at return, so
|
|
7775
|
+
* the stored copy would leak. Transfer the callee's definitely-executed
|
|
7776
|
+
* stores (scan_ref_param_string_field_writes) onto the caller's records for
|
|
7777
|
+
* the argument variable, making the caller's scope exit free them.
|
|
7778
|
+
*
|
|
7779
|
+
* `arg_is_trackable_local` must report true only for variables whose storage
|
|
7780
|
+
* THIS function's scope exit owns — a plain local. A `ref` param of the
|
|
7781
|
+
* enclosing function forwards caller storage, so a record here would free the
|
|
7782
|
+
* ORIGINAL caller's field at the wrong scope's exit.
|
|
7783
|
+
*/
|
|
7784
|
+
function transfer_ref_param_field_records(callee, args, status, arg_is_trackable_local) {
|
|
7785
|
+
const writes = scan_ref_param_string_field_writes(callee, status.structs);
|
|
7786
|
+
if (!writes.size) return;
|
|
7787
|
+
const non_self_params = (callee.params ?? []).filter((p) => !p.is_self_param);
|
|
7788
|
+
for (let i = 0; i < args.length && i < non_self_params.length; i++) {
|
|
7789
|
+
const param = non_self_params[i];
|
|
7790
|
+
if (!(param.is_ref || param.type.is_ref)) continue;
|
|
7791
|
+
const fields = writes.get(param.name);
|
|
7792
|
+
if (!fields?.size) continue;
|
|
7793
|
+
const arg = args[i];
|
|
7794
|
+
if (arg?.node_type !== "value") continue;
|
|
7795
|
+
const name = arg.value;
|
|
7796
|
+
if (!name || !arg_is_trackable_local(name)) continue;
|
|
7797
|
+
if (!status.heap_string_fields) status.heap_string_fields = /* @__PURE__ */ new Set();
|
|
7798
|
+
for (const field of fields) status.heap_string_fields.add(`${name}.${field}`);
|
|
7606
7799
|
}
|
|
7607
7800
|
}
|
|
7608
7801
|
//#endregion
|
|
@@ -7914,7 +8107,7 @@ var AssignmentNode = class extends BaseNode {
|
|
|
7914
8107
|
super("assign", start);
|
|
7915
8108
|
this.left_value = left_value;
|
|
7916
8109
|
this.right_value = right_value;
|
|
7917
|
-
this.operator = operator;
|
|
8110
|
+
this.operator = operator === "=" ? void 0 : operator;
|
|
7918
8111
|
}
|
|
7919
8112
|
};
|
|
7920
8113
|
//#endregion
|
|
@@ -7943,13 +8136,14 @@ function emit_field_overrides(var_name, value, build, status, declaration_termin
|
|
|
7943
8136
|
const overrides = overrides_of(value);
|
|
7944
8137
|
if (!overrides.length) return;
|
|
7945
8138
|
status.code += declaration_terminator;
|
|
7946
|
-
const carrier_type = var_name.startsWith("_return_val") ? void 0 : value.type;
|
|
8139
|
+
const carrier_type = var_name.startsWith("_return_val") || var_name.startsWith("_temp_") ? void 0 : value.type;
|
|
7947
8140
|
for (const override of overrides) {
|
|
7948
8141
|
const target = new ValueNode(override.value.start, var_name);
|
|
7949
8142
|
if (carrier_type) target.type = carrier_type;
|
|
7950
8143
|
const access_field = new AccessFieldNode(override.value.start, override.name, override.type);
|
|
7951
8144
|
const access = new AccessNode(override.value.start, target, access_field);
|
|
7952
8145
|
const assign = new AssignmentNode(override.value.start, access, override.value);
|
|
8146
|
+
if (!carrier_type) assign.raw_field_store = true;
|
|
7953
8147
|
status.code += "\n";
|
|
7954
8148
|
status.c_field_override_depth = (status.c_field_override_depth ?? 0) + 1;
|
|
7955
8149
|
build(assign, status);
|
|
@@ -8073,38 +8267,6 @@ function hoist_field_overrides(value, build, status, declaration_terminator = ""
|
|
|
8073
8267
|
carrier._fov_hoisted = true;
|
|
8074
8268
|
}
|
|
8075
8269
|
//#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
8270
|
//#region ../src/build_common/string_return_analysis.ts
|
|
8109
8271
|
const EMPTY_SET = /* @__PURE__ */ new Set();
|
|
8110
8272
|
/**
|
|
@@ -8468,6 +8630,11 @@ function is_owned_string_branch_value(node, table) {
|
|
|
8468
8630
|
*/
|
|
8469
8631
|
function is_normalized_struct_call(value, status) {
|
|
8470
8632
|
if (!value) return false;
|
|
8633
|
+
if (value.node_type === "anon_struct") {
|
|
8634
|
+
const base = value.base;
|
|
8635
|
+
if (!base) return false;
|
|
8636
|
+
value = base;
|
|
8637
|
+
}
|
|
8471
8638
|
if (value.node_type === "func_call") {
|
|
8472
8639
|
const call = value;
|
|
8473
8640
|
return !!status.normalized_struct_returners?.has(call.name);
|
|
@@ -8511,6 +8678,175 @@ function record_call_init_string_fields(node, status) {
|
|
|
8511
8678
|
for (const field of fields) status.heap_string_fields.add(`${node.name}.${field.name}`);
|
|
8512
8679
|
}
|
|
8513
8680
|
//#endregion
|
|
8681
|
+
//#region ../src/build_common/init_computed_seeds.ts
|
|
8682
|
+
function is_plain_string_field(field) {
|
|
8683
|
+
return field.type.name === "string" && !field.type.is_ref && !field.type.is_array && !field.type.is_view;
|
|
8684
|
+
}
|
|
8685
|
+
/** The `(field, rhs)` of a `self.<field> = <rhs>` write, or undefined. */
|
|
8686
|
+
function self_string_field_write(node, string_fields) {
|
|
8687
|
+
if (node.node_type !== "assign") return void 0;
|
|
8688
|
+
const lhs = node.left_value;
|
|
8689
|
+
if (lhs?.node_type !== "access") return void 0;
|
|
8690
|
+
const access = lhs;
|
|
8691
|
+
if (access.access.node_type !== "access_field") return void 0;
|
|
8692
|
+
const target = access.target;
|
|
8693
|
+
if (target?.node_type !== "value" || target.value !== "self") return void 0;
|
|
8694
|
+
const field = access.access.name ?? "";
|
|
8695
|
+
if (!string_fields.has(field)) return void 0;
|
|
8696
|
+
return {
|
|
8697
|
+
field,
|
|
8698
|
+
rhs: node.right_value
|
|
8699
|
+
};
|
|
8700
|
+
}
|
|
8701
|
+
/** Visit every AST node reachable from `value` — through arrays AND
|
|
8702
|
+
* single-node properties — skipping `parent`/`scope` back-references and NOT
|
|
8703
|
+
* descending INTO nested `func`/`struct`/`trait` declarations. */
|
|
8704
|
+
function walk$3(value, cb) {
|
|
8705
|
+
if (!value || typeof value !== "object") return;
|
|
8706
|
+
if (Array.isArray(value)) {
|
|
8707
|
+
for (const item of value) walk$3(item, cb);
|
|
8708
|
+
return;
|
|
8709
|
+
}
|
|
8710
|
+
const n = value;
|
|
8711
|
+
const is_boundary = n.node_type === "func" || n.node_type === "struct" || n.node_type === "trait";
|
|
8712
|
+
if (typeof n.node_type === "string") cb(n);
|
|
8713
|
+
if (is_boundary) return;
|
|
8714
|
+
for (const key of Object.keys(value)) {
|
|
8715
|
+
if (key === "parent" || key === "scope" || key === "node_type") continue;
|
|
8716
|
+
walk$3(value[key], cb);
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8719
|
+
function collect_init_string_writes(struct_node, init) {
|
|
8720
|
+
const string_fields = new Set(struct_node.fields.filter(is_plain_string_field).map((f) => f.name));
|
|
8721
|
+
const top_level = /* @__PURE__ */ new Map();
|
|
8722
|
+
const nested = /* @__PURE__ */ new Map();
|
|
8723
|
+
const add = (m, field, rhs) => {
|
|
8724
|
+
const list = m.get(field);
|
|
8725
|
+
if (list) list.push(rhs);
|
|
8726
|
+
else m.set(field, [rhs]);
|
|
8727
|
+
};
|
|
8728
|
+
for (const stmt of init.statements ?? []) {
|
|
8729
|
+
const write = self_string_field_write(stmt, string_fields);
|
|
8730
|
+
if (write) {
|
|
8731
|
+
add(top_level, write.field, write.rhs);
|
|
8732
|
+
continue;
|
|
8733
|
+
}
|
|
8734
|
+
walk$3(stmt, (n) => {
|
|
8735
|
+
const nested_write = self_string_field_write(n, string_fields);
|
|
8736
|
+
if (nested_write) add(nested, nested_write.field, nested_write.rhs);
|
|
8737
|
+
});
|
|
8738
|
+
}
|
|
8739
|
+
return {
|
|
8740
|
+
top_level,
|
|
8741
|
+
nested
|
|
8742
|
+
};
|
|
8743
|
+
}
|
|
8744
|
+
/**
|
|
8745
|
+
* The plain string fields of `struct_node` that its custom `#init` COMPUTES as
|
|
8746
|
+
* a definitely-heap-owning value: for EVERY `#init` overload, the field is
|
|
8747
|
+
* either written at least once as a DIRECT body statement with every write
|
|
8748
|
+
* (direct or nested) passing `is_owned_heap` — or never written and its
|
|
8749
|
+
* DEFAULT also passes `is_owned_heap` (the overload leaves the default in
|
|
8750
|
+
* place). A param-alias seed (`self.a = s`) never qualifies: the seed borrows
|
|
8751
|
+
* the caller's argument temp, so it must never be recorded or freed.
|
|
8752
|
+
*
|
|
8753
|
+
* Binding sites record qualifying fields (scope exit frees the computed seed;
|
|
8754
|
+
* a displaced store reclaims it); override sites treat them as displaced (the
|
|
8755
|
+
* raw override store orphans the computed buffer). A param-alias seed or a
|
|
8756
|
+
* conditional computed write over a rodata default leaves a path holding a
|
|
8757
|
+
* non-heap value — such fields stay unqualified (bounded leak on the write
|
|
8758
|
+
* path, never an invalid free).
|
|
8759
|
+
*/
|
|
8760
|
+
function init_computed_heap_string_fields(struct_node, is_owned_heap) {
|
|
8761
|
+
const result = /* @__PURE__ */ new Set();
|
|
8762
|
+
const inits = struct_node.functions.filter((f) => f.name === "#init" && f.has_body);
|
|
8763
|
+
if (!inits.length) return result;
|
|
8764
|
+
for (const field of struct_node.fields) {
|
|
8765
|
+
if (!is_plain_string_field(field)) continue;
|
|
8766
|
+
const default_is_heap = !!field.value && is_owned_heap(field.value);
|
|
8767
|
+
let written_somewhere = false;
|
|
8768
|
+
let qualifies = true;
|
|
8769
|
+
for (const init of inits) {
|
|
8770
|
+
const writes = collect_init_string_writes(struct_node, init);
|
|
8771
|
+
const direct = writes.top_level.get(field.name) ?? [];
|
|
8772
|
+
const all = [...direct, ...writes.nested.get(field.name) ?? []];
|
|
8773
|
+
if (all.length === 0) {
|
|
8774
|
+
if (!default_is_heap) {
|
|
8775
|
+
qualifies = false;
|
|
8776
|
+
break;
|
|
8777
|
+
}
|
|
8778
|
+
continue;
|
|
8779
|
+
}
|
|
8780
|
+
written_somewhere = true;
|
|
8781
|
+
if (!all.every((expr) => is_owned_heap(expr))) {
|
|
8782
|
+
qualifies = false;
|
|
8783
|
+
break;
|
|
8784
|
+
}
|
|
8785
|
+
if (direct.length === 0 && !default_is_heap) {
|
|
8786
|
+
qualifies = false;
|
|
8787
|
+
break;
|
|
8788
|
+
}
|
|
8789
|
+
}
|
|
8790
|
+
if (qualifies && written_somewhere) result.add(field.name);
|
|
8791
|
+
}
|
|
8792
|
+
return result;
|
|
8793
|
+
}
|
|
8794
|
+
/**
|
|
8795
|
+
* The write-site half: does THIS custom-`#init` write to `self.<field>`
|
|
8796
|
+
* displace a heap-owning DEFAULT seed that must be reclaimed ahead of the
|
|
8797
|
+
* store? True only for the field's first write in the body (the displaced
|
|
8798
|
+
* value is then statically the default — later writes displace values whose
|
|
8799
|
+
* ownership the init cannot know, and keep the status quo). Marks the field
|
|
8800
|
+
* reclaimed on true so a second write never frees what the first stored.
|
|
8801
|
+
*
|
|
8802
|
+
* Sound even for conditional first writes: the free rides in the same branch
|
|
8803
|
+
* as the store, and the displaced value is still the default at that point.
|
|
8804
|
+
*/
|
|
8805
|
+
function claim_init_default_seed_reclaim(status, field_name, is_owned_heap) {
|
|
8806
|
+
if (status.current_function?.name !== "#init" || !status.current_struct) return false;
|
|
8807
|
+
if (status.init_default_reclaimed_fields?.has(field_name)) return false;
|
|
8808
|
+
const field = status.current_struct.fields.find((f) => f.name === field_name);
|
|
8809
|
+
if (!field || !field.value || !is_plain_string_field(field)) return false;
|
|
8810
|
+
if (!is_owned_heap(field.value)) return false;
|
|
8811
|
+
if (!status.init_default_reclaimed_fields) status.init_default_reclaimed_fields = /* @__PURE__ */ new Set();
|
|
8812
|
+
status.init_default_reclaimed_fields.add(field_name);
|
|
8813
|
+
return true;
|
|
8814
|
+
}
|
|
8815
|
+
//#endregion
|
|
8816
|
+
//#region ../src/build_common/ctor_heap_default_fields.ts
|
|
8817
|
+
/**
|
|
8818
|
+
* Record the string fields of `struct_node` whose value after construction
|
|
8819
|
+
* owns heap (per the backend's `is_owned_heap_temp`): either the DEFAULT is a
|
|
8820
|
+
* non-literal heap expression the constructor evaluates fresh at every
|
|
8821
|
+
* construction, or a custom `#init` COMPUTES the field's value
|
|
8822
|
+
* (`init_computed_heap_string_fields` — every direct `self.<field> = <expr>`
|
|
8823
|
+
* write stores a heap-owning expression, which also displaces the default).
|
|
8824
|
+
* Recording at the ctor-binding site makes scope exit free the seeded value,
|
|
8825
|
+
* and a displaced store (an override, a reassignment) reclaim it via the
|
|
8826
|
+
* record's old_was_heap check.
|
|
8827
|
+
*
|
|
8828
|
+
* `is_owned_heap` classifies the seed EXPRESSION per backend: both treat a
|
|
8829
|
+
* string-typed `op` as fresh heap; for calls the C backend's blanket strdup
|
|
8830
|
+
* makes every non-borrow string return owned, while the aarch64 backend
|
|
8831
|
+
* requires the heap-returning classification (a literal-returning user
|
|
8832
|
+
* function leaves rodata in the field — never recorded, never freed).
|
|
8833
|
+
*
|
|
8834
|
+
* `skip` names fields whose seed is reclaimed elsewhere (the C backend's
|
|
8835
|
+
* explicit displaced free ahead of an override store). Value structs only —
|
|
8836
|
+
* a class's string fields are always heap and freed by its destroy.
|
|
8837
|
+
*/
|
|
8838
|
+
function record_ctor_heap_default_fields(var_name, struct_node, status, is_owned_heap, skip) {
|
|
8839
|
+
const computed = init_computed_heap_string_fields(struct_node, is_owned_heap);
|
|
8840
|
+
for (const field of struct_node.fields) {
|
|
8841
|
+
if (field.type.name !== "string" || field.type.is_array) continue;
|
|
8842
|
+
if (field.type.is_ref || field.type.is_view) continue;
|
|
8843
|
+
if (skip?.has(field.name)) continue;
|
|
8844
|
+
if (!(!!field.value && is_owned_heap(field.value)) && !computed.has(field.name)) continue;
|
|
8845
|
+
if (!status.heap_string_fields) status.heap_string_fields = /* @__PURE__ */ new Set();
|
|
8846
|
+
status.heap_string_fields.add(`${var_name}.${field.name}`);
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
//#endregion
|
|
8514
8850
|
//#region ../src/nodes/child_nodes.ts
|
|
8515
8851
|
/** Whether `value` is an AST node (carries a string `node_type`). */
|
|
8516
8852
|
function is_ast_node(value) {
|
|
@@ -8853,6 +9189,7 @@ function scan_function_literal_element_moves(func) {
|
|
|
8853
9189
|
return count;
|
|
8854
9190
|
}
|
|
8855
9191
|
function walk_stmt(walk, node) {
|
|
9192
|
+
if (node.allocations?.length) for (const alloc of node.allocations) walk_stmt(walk, alloc);
|
|
8856
9193
|
switch (node.node_type) {
|
|
8857
9194
|
case "declare": {
|
|
8858
9195
|
const decl = node;
|
|
@@ -11697,7 +12034,7 @@ function get_struct_size(name, status) {
|
|
|
11697
12034
|
size = align_to(size, get_type_alignment(field.type, status));
|
|
11698
12035
|
size += get_type_size(field.type, status);
|
|
11699
12036
|
if (is_nullable_struct_type(field.type, status)) size += 8;
|
|
11700
|
-
else if (is_nullable_scalar_type(field.type)) size += 1;
|
|
12037
|
+
else if (is_nullable_scalar_type(field.type, status)) size += 1;
|
|
11701
12038
|
}
|
|
11702
12039
|
size = align_to(size, 8);
|
|
11703
12040
|
return size;
|
|
@@ -11749,7 +12086,7 @@ function get_field_offset_of_fields(fields, field_name, status) {
|
|
|
11749
12086
|
if (field.name === field_name) return offset;
|
|
11750
12087
|
offset += get_type_size(field.type, status);
|
|
11751
12088
|
if (is_nullable_struct_type(field.type, status)) offset += 8;
|
|
11752
|
-
else if (is_nullable_scalar_type(field.type)) offset += 1;
|
|
12089
|
+
else if (is_nullable_scalar_type(field.type, status)) offset += 1;
|
|
11753
12090
|
}
|
|
11754
12091
|
return offset;
|
|
11755
12092
|
}
|
|
@@ -14891,10 +15228,21 @@ function emit_string_field_strdups_at(status, struct_name, base_reg, base_offset
|
|
|
14891
15228
|
const off = base_offset + get_field_offset(struct_name, field.name, status);
|
|
14892
15229
|
emit_asm(status, `str ${base_reg}, [sp, #-16]!\n`);
|
|
14893
15230
|
emit_string_pair_load_at(status, base_reg, off);
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
15231
|
+
if (field.type.is_nullable) {
|
|
15232
|
+
const skip_label = `.Lskip_nullable_strdup_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
15233
|
+
emit_asm(status, `mov x13, x0\n`);
|
|
15234
|
+
emit_asm(status, `cbz x0, ${skip_label}\n`);
|
|
15235
|
+
emit_strdup_string(status);
|
|
15236
|
+
emit_asm(status, `mov x13, x0\n`);
|
|
15237
|
+
emit_asm(status, `${skip_label}:\n`);
|
|
15238
|
+
emit_asm(status, `ldr ${base_reg}, [sp], #16\n`);
|
|
15239
|
+
emit_pair_store_to(status, base_reg, off, "x13", "x1");
|
|
15240
|
+
} else {
|
|
15241
|
+
emit_strdup_string(status);
|
|
15242
|
+
emit_asm(status, `mov x13, x0\n`);
|
|
15243
|
+
emit_asm(status, `ldr ${base_reg}, [sp], #16\n`);
|
|
15244
|
+
emit_pair_store_to(status, base_reg, off, "x13", "x1");
|
|
15245
|
+
}
|
|
14898
15246
|
}
|
|
14899
15247
|
}
|
|
14900
15248
|
//#endregion
|
|
@@ -17404,7 +17752,7 @@ function owning_buffer_element_aarch64(node, status) {
|
|
|
17404
17752
|
const elem_name = node.name.substring(7);
|
|
17405
17753
|
const elem = status.structs.find((s) => s.name === elem_name && !s.is_simple_type && !s.is_generic);
|
|
17406
17754
|
if (!elem || elem.is_class) return void 0;
|
|
17407
|
-
if (!has_string_fields(elem, status)) return void 0;
|
|
17755
|
+
if (!has_string_fields(elem, status) && !struct_needs_destroy(elem, status)) return void 0;
|
|
17408
17756
|
return elem;
|
|
17409
17757
|
}
|
|
17410
17758
|
/**
|
|
@@ -17550,6 +17898,75 @@ function collect_string_fields(elem, status, base_offset = 0) {
|
|
|
17550
17898
|
return result;
|
|
17551
17899
|
}
|
|
17552
17900
|
/**
|
|
17901
|
+
* Collect the (offset, size) pairs of every owned CONTAINER field (`List<T>`/
|
|
17902
|
+
* `Buffer<T>`/… mono with a `#destroy`) the element's teardown would reclaim.
|
|
17903
|
+
* The owning store_T/replace_T memcpy the source's bytes into the slot, so
|
|
17904
|
+
* zeroing these fields of the SOURCE completes the ownership transfer: the
|
|
17905
|
+
* slot is the sole owner and the source's own teardown (when it still runs)
|
|
17906
|
+
* finds zeroed fields. Nested value struct fields recurse so their container
|
|
17907
|
+
* leaves are zeroed at their own flat offsets; their string fields are
|
|
17908
|
+
* untouched (the slot owns the strdup'd copies via collect_string_fields).
|
|
17909
|
+
* Mirrors the C backend's `emit_transfer_owning_fields`. `view T`/nullable
|
|
17910
|
+
* fields are skipped; class/enum payload fields are left alone (recorded in
|
|
17911
|
+
* FOLLOWUP.md).
|
|
17912
|
+
*/
|
|
17913
|
+
function collect_transfer_fields(elem, status, base_offset = 0) {
|
|
17914
|
+
const result = [];
|
|
17915
|
+
let offset = 8;
|
|
17916
|
+
for (const field of elem.fields) {
|
|
17917
|
+
const size = get_type_size(field.type, status);
|
|
17918
|
+
if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable) {
|
|
17919
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
17920
|
+
if (field_struct && !field_struct.is_class) {
|
|
17921
|
+
if (has_destroy(field_struct)) result.push({
|
|
17922
|
+
offset: base_offset + offset,
|
|
17923
|
+
size
|
|
17924
|
+
});
|
|
17925
|
+
else if (struct_needs_destroy(field_struct, status)) result.push(...collect_transfer_fields(field_struct, status, base_offset + offset));
|
|
17926
|
+
}
|
|
17927
|
+
}
|
|
17928
|
+
offset += size;
|
|
17929
|
+
}
|
|
17930
|
+
return result;
|
|
17931
|
+
}
|
|
17932
|
+
/**
|
|
17933
|
+
* Emit `memset(&src.field, 0, size)` for every owned field collected by
|
|
17934
|
+
* `collect_transfer_fields`. `src` is the register holding the SOURCE struct
|
|
17935
|
+
* address (x2 inline / x21 standalone for store_T). Clobbers x0-x2.
|
|
17936
|
+
*/
|
|
17937
|
+
function emit_transfer_owning_fields$1(status, src, fields) {
|
|
17938
|
+
for (const { offset, size } of fields) {
|
|
17939
|
+
emit_asm(status, `add x0, ${src}, #${offset}\n`);
|
|
17940
|
+
emit_asm(status, `mov x1, #0\n`);
|
|
17941
|
+
emit_asm(status, `mov x2, #${size}\n`);
|
|
17942
|
+
emit_asm(status, `bl _memset\n`);
|
|
17943
|
+
}
|
|
17944
|
+
}
|
|
17945
|
+
/**
|
|
17946
|
+
* Emit the deep-copy half of owning load_T into the sret buffer (x8): for
|
|
17947
|
+
* each `List<T>` field whose mono has a `copy` method, replace the
|
|
17948
|
+
* shallow-copied field with `<List_T>_copy(&slot.field)` (sret → the sret
|
|
17949
|
+
* buffer's field; self == x8 is safe — the copy reads its input before the
|
|
17950
|
+
* final result write). Mirrors the C backend's `emit_load_deep_copy_fields`.
|
|
17951
|
+
* `sret` is the register holding the returned struct's address; `slot` holds
|
|
17952
|
+
* the source slot's address (the copy reads from the slot, writes to sret).
|
|
17953
|
+
*/
|
|
17954
|
+
function emit_load_deep_copy_fields$1(elem, status, sret, slot) {
|
|
17955
|
+
let offset = 8;
|
|
17956
|
+
for (const field of elem.fields) {
|
|
17957
|
+
const size = get_type_size(field.type, status);
|
|
17958
|
+
if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable && field.type.name !== "string") {
|
|
17959
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
17960
|
+
if (field_struct && !field_struct.is_class && field_struct.name.startsWith("List_") && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
17961
|
+
emit_asm(status, `add x8, ${sret}, #${offset}\n`);
|
|
17962
|
+
emit_asm(status, `add x0, ${slot}, #${offset}\n`);
|
|
17963
|
+
emit_asm(status, `bl ${field_struct.name}_copy\n`);
|
|
17964
|
+
}
|
|
17965
|
+
}
|
|
17966
|
+
offset += size;
|
|
17967
|
+
}
|
|
17968
|
+
}
|
|
17969
|
+
/**
|
|
17553
17970
|
* Emit `slot.field = strdup(src.field)` guarded against a NULL source field
|
|
17554
17971
|
* (e.g. JsonTree's "no text" sentinel): a NULL field is copied as NULL —
|
|
17555
17972
|
* strdup(NULL) would crash. `src`/`dst` are the register names holding the
|
|
@@ -17587,6 +18004,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
17587
18004
|
emit_owning_shift_T(elem, status, "x0");
|
|
17588
18005
|
return true;
|
|
17589
18006
|
}
|
|
18007
|
+
if (func_name === "load") {
|
|
18008
|
+
emit_owning_load_T(elem, status, "x0");
|
|
18009
|
+
return true;
|
|
18010
|
+
}
|
|
17590
18011
|
return false;
|
|
17591
18012
|
}
|
|
17592
18013
|
const enum_elem = owning_buffer_enum_element_aarch64(struct_node, status);
|
|
@@ -17648,7 +18069,7 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
17648
18069
|
function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
|
|
17649
18070
|
const elem = owning_buffer_element_aarch64(node, status);
|
|
17650
18071
|
if (elem) {
|
|
17651
|
-
if (func_name !== "store" && func_name !== "replace" && func_name !== "shift" && func_name !== "modify") return false;
|
|
18072
|
+
if (func_name !== "store" && func_name !== "replace" && func_name !== "shift" && func_name !== "modify" && func_name !== "load") return false;
|
|
17652
18073
|
emit_owning_standalone_struct(elem, func_name, status);
|
|
17653
18074
|
return true;
|
|
17654
18075
|
}
|
|
@@ -17707,6 +18128,7 @@ function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
|
|
|
17707
18128
|
function emit_owning_standalone_struct(elem, func_name, status) {
|
|
17708
18129
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
17709
18130
|
const string_fields = collect_string_fields(elem, status);
|
|
18131
|
+
const transfer_fields = collect_transfer_fields(elem, status);
|
|
17710
18132
|
if (func_name === "shift") {
|
|
17711
18133
|
emit_owning_shift_T(elem, status, "x19");
|
|
17712
18134
|
return true;
|
|
@@ -17715,6 +18137,23 @@ function emit_owning_standalone_struct(elem, func_name, status) {
|
|
|
17715
18137
|
emit_owning_modify_T(elem, status, T_SIZE, string_fields);
|
|
17716
18138
|
return true;
|
|
17717
18139
|
}
|
|
18140
|
+
if (func_name === "load") {
|
|
18141
|
+
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
18142
|
+
emit_asm(status, `stp x22, x23, [sp, #-16]!\n`);
|
|
18143
|
+
emit_asm(status, `ldr x20, [x19, #8]\n`);
|
|
18144
|
+
emit_asm(status, `mov x9, #${T_SIZE}\n`);
|
|
18145
|
+
emit_asm(status, `madd x21, x1, x9, x20\n`);
|
|
18146
|
+
emit_asm(status, `mov x22, x8\n`);
|
|
18147
|
+
emit_asm(status, `mov x0, x22\n`);
|
|
18148
|
+
emit_asm(status, `mov x1, x21\n`);
|
|
18149
|
+
emit_asm(status, `mov x2, x9\n`);
|
|
18150
|
+
emit_asm(status, `bl _memcpy\n`);
|
|
18151
|
+
emit_load_deep_copy_fields$1(elem, status, "x22", "x21");
|
|
18152
|
+
emit_asm(status, `mov x0, x22\n`);
|
|
18153
|
+
emit_asm(status, `ldp x22, x23, [sp], #16\n`);
|
|
18154
|
+
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18155
|
+
return true;
|
|
18156
|
+
}
|
|
17718
18157
|
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
17719
18158
|
emit_asm(status, `str x22, [sp, #-16]!\n`);
|
|
17720
18159
|
emit_asm(status, `ldr x20, [x19, #8]\n`);
|
|
@@ -17752,6 +18191,7 @@ function emit_owning_standalone_struct(elem, func_name, status) {
|
|
|
17752
18191
|
}
|
|
17753
18192
|
if (string_fields.length) emit_asm(status, `add sp, sp, #${Math.ceil(string_fields.length * 8 / 16) * 16}\n`);
|
|
17754
18193
|
} else for (const { offset: foff } of string_fields) emit_strdup_field(status, "x21", "x20", foff, status.label_counter = (status.label_counter ?? 0) + 1);
|
|
18194
|
+
emit_transfer_owning_fields$1(status, "x21", transfer_fields);
|
|
17755
18195
|
emit_asm(status, `ldr x22, [sp], #16\n`);
|
|
17756
18196
|
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
17757
18197
|
return true;
|
|
@@ -17953,9 +18393,35 @@ function emit_owning_shift_T(elem, status, self_reg) {
|
|
|
17953
18393
|
emit_asm(status, `ldr x22, [sp], #16\n`);
|
|
17954
18394
|
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
17955
18395
|
}
|
|
18396
|
+
/**
|
|
18397
|
+
* Specialized owning-value-struct load_T (inline context: x0 = self, x1 = i,
|
|
18398
|
+
* x8 = sret). The caller's scope-exit destroy ALWAYS reclaims a struct
|
|
18399
|
+
* local's container fields, so the returned copy must own independent
|
|
18400
|
+
* containers — a shallow return would double-free the slot's buffer at the
|
|
18401
|
+
* caller's teardown. String fields stay borrowed (struct-local teardown only
|
|
18402
|
+
* frees RECORDED heap strings, so a strdup here would leak).
|
|
18403
|
+
*/
|
|
18404
|
+
function emit_owning_load_T(elem, status, self_reg) {
|
|
18405
|
+
const T_SIZE = get_struct_size(elem.name, status);
|
|
18406
|
+
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
18407
|
+
emit_asm(status, `stp x22, x23, [sp, #-16]!\n`);
|
|
18408
|
+
emit_asm(status, `ldr x20, [${self_reg}, #8]\n`);
|
|
18409
|
+
emit_asm(status, `mov x9, #${T_SIZE}\n`);
|
|
18410
|
+
emit_asm(status, `madd x21, x1, x9, x20\n`);
|
|
18411
|
+
emit_asm(status, `mov x22, x8\n`);
|
|
18412
|
+
emit_asm(status, `mov x0, x22\n`);
|
|
18413
|
+
emit_asm(status, `mov x1, x21\n`);
|
|
18414
|
+
emit_asm(status, `mov x2, x9\n`);
|
|
18415
|
+
emit_asm(status, `bl _memcpy\n`);
|
|
18416
|
+
emit_load_deep_copy_fields$1(elem, status, "x22", "x21");
|
|
18417
|
+
emit_asm(status, `mov x0, x22\n`);
|
|
18418
|
+
emit_asm(status, `ldp x22, x23, [sp], #16\n`);
|
|
18419
|
+
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18420
|
+
}
|
|
17956
18421
|
function emit_owning_store_T(elem, status) {
|
|
17957
18422
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
17958
18423
|
const string_fields = collect_string_fields(elem, status);
|
|
18424
|
+
const transfer_fields = collect_transfer_fields(elem, status);
|
|
17959
18425
|
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
17960
18426
|
emit_asm(status, `str x22, [sp, #-16]!\n`);
|
|
17961
18427
|
emit_asm(status, `ldr x20, [x0, #8]\n`);
|
|
@@ -17987,12 +18453,14 @@ function emit_owning_store_T(elem, status) {
|
|
|
17987
18453
|
emit_asm(status, `${lbl}:\n`);
|
|
17988
18454
|
}
|
|
17989
18455
|
if (string_fields.length) emit_asm(status, `add sp, sp, #${Math.ceil(string_fields.length * 8 / 16) * 16}\n`);
|
|
18456
|
+
emit_transfer_owning_fields$1(status, "x21", transfer_fields);
|
|
17990
18457
|
emit_asm(status, `ldr x22, [sp], #16\n`);
|
|
17991
18458
|
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
17992
18459
|
}
|
|
17993
18460
|
function emit_owning_replace_T(elem, status) {
|
|
17994
18461
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
17995
18462
|
const string_fields = collect_string_fields(elem, status);
|
|
18463
|
+
const transfer_fields = collect_transfer_fields(elem, status);
|
|
17996
18464
|
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
17997
18465
|
emit_asm(status, `str x22, [sp, #-16]!\n`);
|
|
17998
18466
|
emit_asm(status, `ldr x20, [x0, #8]\n`);
|
|
@@ -18007,6 +18475,7 @@ function emit_owning_replace_T(elem, status) {
|
|
|
18007
18475
|
emit_asm(status, `mov x2, x22\n`);
|
|
18008
18476
|
emit_asm(status, `bl _memcpy\n`);
|
|
18009
18477
|
for (const { offset: foff } of string_fields) emit_strdup_field(status, "x21", "x20", foff, status.label_counter = (status.label_counter ?? 0) + 1);
|
|
18478
|
+
emit_transfer_owning_fields$1(status, "x21", transfer_fields);
|
|
18010
18479
|
emit_asm(status, `ldr x22, [sp], #16\n`);
|
|
18011
18480
|
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18012
18481
|
}
|
|
@@ -19441,6 +19910,12 @@ function plan_nir_registers(func, nir, options) {
|
|
|
19441
19910
|
});
|
|
19442
19911
|
}
|
|
19443
19912
|
}
|
|
19913
|
+
const loop_carry_regs = /* @__PURE__ */ new Set();
|
|
19914
|
+
for (const [key, f] of facts) {
|
|
19915
|
+
if (!f.loop_blocked) continue;
|
|
19916
|
+
const reg = allocs.get(key);
|
|
19917
|
+
if (reg) loop_carry_regs.add(reg);
|
|
19918
|
+
}
|
|
19444
19919
|
return {
|
|
19445
19920
|
allocs,
|
|
19446
19921
|
callee_saved,
|
|
@@ -19449,7 +19924,8 @@ function plan_nir_registers(func, nir, options) {
|
|
|
19449
19924
|
source_keys,
|
|
19450
19925
|
sites: site_allocs,
|
|
19451
19926
|
pairs: slp_pairs,
|
|
19452
|
-
region_free
|
|
19927
|
+
region_free,
|
|
19928
|
+
loop_carry_regs
|
|
19453
19929
|
};
|
|
19454
19930
|
}
|
|
19455
19931
|
/**
|
|
@@ -19510,6 +19986,7 @@ function seed_function_allocations(func, status, options) {
|
|
|
19510
19986
|
}]));
|
|
19511
19987
|
if (status.nir_region_free.size === 0) status.nir_region_free = void 0;
|
|
19512
19988
|
status.region_preseed = void 0;
|
|
19989
|
+
status.nir_loop_carry_regs = plan.loop_carry_regs.size > 0 ? plan.loop_carry_regs : void 0;
|
|
19513
19990
|
for (const param of func.params) {
|
|
19514
19991
|
const reg = plan.allocs.get(param.name);
|
|
19515
19992
|
if (!reg || !reg.startsWith("x")) continue;
|
|
@@ -19633,6 +20110,8 @@ function build_function_node$1(node, status) {
|
|
|
19633
20110
|
status.heap_class_arrays = void 0;
|
|
19634
20111
|
const old_heap_array_vars = status.heap_array_vars;
|
|
19635
20112
|
status.heap_array_vars = void 0;
|
|
20113
|
+
const old_heap_string_fields = status.heap_string_fields;
|
|
20114
|
+
status.heap_string_fields = void 0;
|
|
19636
20115
|
const old_heap_cleanup_stack = status.heap_cleanup_stack;
|
|
19637
20116
|
status.heap_cleanup_stack = void 0;
|
|
19638
20117
|
const old_trait_class_frames = status.trait_class_frames;
|
|
@@ -19661,7 +20140,7 @@ function build_function_node$1(node, status) {
|
|
|
19661
20140
|
status.code = "";
|
|
19662
20141
|
node.return_type = resolve_mono_type(node.return_type, status);
|
|
19663
20142
|
const return_struct = !node.return_type.is_view && !node.return_type.is_array && (!!status.structs.find((s) => s.name === node.return_type.name && !s.is_simple_type && !s.is_class) || get_enum_sret_size(node.return_type.name, status) !== void 0);
|
|
19664
|
-
const nullable_scalar_ret = is_nullable_scalar_type(node.return_type) && !node.return_type.is_ref;
|
|
20143
|
+
const nullable_scalar_ret = is_nullable_scalar_type(node.return_type, status) && !node.return_type.is_ref;
|
|
19665
20144
|
const uses_sret = return_struct || nullable_scalar_ret;
|
|
19666
20145
|
if (uses_sret) status.struct_return_buffer = "x8";
|
|
19667
20146
|
status.function_return_type = node.return_type;
|
|
@@ -19748,7 +20227,7 @@ function build_function_node$1(node, status) {
|
|
|
19748
20227
|
first_pass_slot += 2;
|
|
19749
20228
|
continue;
|
|
19750
20229
|
}
|
|
19751
|
-
if (is_nullable_scalar_type(param.type)) {
|
|
20230
|
+
if (is_nullable_scalar_type(param.type, status)) {
|
|
19752
20231
|
first_pass_slot += 2;
|
|
19753
20232
|
continue;
|
|
19754
20233
|
}
|
|
@@ -19860,11 +20339,13 @@ function build_function_node$1(node, status) {
|
|
|
19860
20339
|
}]));
|
|
19861
20340
|
if (status.nir_region_free.size === 0) status.nir_region_free = void 0;
|
|
19862
20341
|
status.region_preseed = void 0;
|
|
20342
|
+
status.nir_loop_carry_regs = plan.loop_carry_regs.size > 0 ? plan.loop_carry_regs : void 0;
|
|
19863
20343
|
} else {
|
|
19864
20344
|
fn_allocs = plan_function_promotions(node, plan_nir);
|
|
19865
20345
|
status.nir_site_allocs = void 0;
|
|
19866
20346
|
status.nir_region_free = void 0;
|
|
19867
20347
|
status.region_preseed = void 0;
|
|
20348
|
+
status.nir_loop_carry_regs = void 0;
|
|
19868
20349
|
}
|
|
19869
20350
|
} else status.nir_site_allocs = void 0;
|
|
19870
20351
|
status.register_allocations = fn_allocs && fn_allocs.size > 0 ? fn_allocs : void 0;
|
|
@@ -19913,7 +20394,7 @@ function build_function_node$1(node, status) {
|
|
|
19913
20394
|
param_idx += 2;
|
|
19914
20395
|
continue;
|
|
19915
20396
|
}
|
|
19916
|
-
if (is_nullable_scalar_type(param.type) && !param.type.is_ref && !param.type.is_array) {
|
|
20397
|
+
if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref && !param.type.is_array) {
|
|
19917
20398
|
const size = aarch64_size(param.type.name);
|
|
19918
20399
|
const offset = allocate_stack_space(status, size, size);
|
|
19919
20400
|
status.stack_offsets.set(param.name, offset);
|
|
@@ -20245,6 +20726,7 @@ function build_function_node$1(node, status) {
|
|
|
20245
20726
|
}
|
|
20246
20727
|
status.scoped_declarations = old_scoped_declarations;
|
|
20247
20728
|
status.moved = old_moved;
|
|
20729
|
+
status.heap_string_fields = old_heap_string_fields;
|
|
20248
20730
|
status.heap_strings = old_heap_strings;
|
|
20249
20731
|
status.heap_string_arrays = old_heap_string_arrays;
|
|
20250
20732
|
status.heap_owned_string_arrays = old_heap_owned_string_arrays;
|
|
@@ -20328,7 +20810,7 @@ function init_enum_shorthand_field_default(node, field, base_reg, status) {
|
|
|
20328
20810
|
* struct base register (`x0` for auto-init, `x19` for custom-init).
|
|
20329
20811
|
*/
|
|
20330
20812
|
function init_nullable_field_default(node, field, base_reg, status) {
|
|
20331
|
-
const scalar_nullable = is_nullable_scalar_type(field.type);
|
|
20813
|
+
const scalar_nullable = is_nullable_scalar_type(field.type, status);
|
|
20332
20814
|
if (!is_nullable_struct_type(field.type, status) && !scalar_nullable) return false;
|
|
20333
20815
|
if (!field.value) return false;
|
|
20334
20816
|
const offset = get_field_offset(node.name, field.name, status);
|
|
@@ -20358,6 +20840,41 @@ function init_nullable_field_default(node, field, base_reg, status) {
|
|
|
20358
20840
|
emit_asm(status, `str x9, [${base_reg}, #${has_offset}]\n`);
|
|
20359
20841
|
return true;
|
|
20360
20842
|
}
|
|
20843
|
+
/**
|
|
20844
|
+
* Initialize a field whose default is a non-literal EXPRESSION (a string op
|
|
20845
|
+
* like `"de" + "fault"`, a call returning a non-struct, a grouped expr, …):
|
|
20846
|
+
* evaluate it and store the result into the field slot. x0 carries the
|
|
20847
|
+
* result — a fat string as the (x0, x1) pair, a by-value struct as the sret
|
|
20848
|
+
* temp's address. A class's plain string field is strdup'd when the result
|
|
20849
|
+
* is not itself fresh heap (the field is freed unconditionally at destroy);
|
|
20850
|
+
* a value struct's heap-owning default is stored raw — the ctor-binding
|
|
20851
|
+
* sites record it (record_ctor_heap_default_fields) so scope exit frees it.
|
|
20852
|
+
*/
|
|
20853
|
+
function init_expr_field_default(node, field, offset, base_reg, status) {
|
|
20854
|
+
build_node(field.value, status);
|
|
20855
|
+
ensure_newline(status);
|
|
20856
|
+
const t = field.type;
|
|
20857
|
+
if (t.name === "string" && !t.is_ref && !t.is_view && !t.is_array) {
|
|
20858
|
+
if (node.is_class && !status.last_result_is_heap) emit_strdup_string(status);
|
|
20859
|
+
emit_pair_store_to(status, base_reg, offset, "x0", "x1");
|
|
20860
|
+
return;
|
|
20861
|
+
}
|
|
20862
|
+
const field_struct = status.structs.find((s) => s.name === t.name && !s.is_simple_type);
|
|
20863
|
+
if (field_struct?.is_class) {
|
|
20864
|
+
emit_typed_store(status, "x0", base_reg, offset, 8);
|
|
20865
|
+
return;
|
|
20866
|
+
}
|
|
20867
|
+
if (field_struct) {
|
|
20868
|
+
const field_size = get_struct_size(t.name, status);
|
|
20869
|
+
const words = Math.ceil(field_size / 8);
|
|
20870
|
+
for (let w = 0; w < words; w++) {
|
|
20871
|
+
emit_asm(status, `ldr x9, [x0, #${w * 8}]\n`);
|
|
20872
|
+
emit_asm(status, `str x9, [${base_reg}, #${offset + w * 8}]\n`);
|
|
20873
|
+
}
|
|
20874
|
+
return;
|
|
20875
|
+
}
|
|
20876
|
+
emit_typed_store(status, "x0", base_reg, offset, get_type_size(t, status));
|
|
20877
|
+
}
|
|
20361
20878
|
/** Store the scalar in x0 into `[base_reg + offset]` at its natural width. */
|
|
20362
20879
|
function emit_scalar_store_from_x0(offset, type, base_reg, status) {
|
|
20363
20880
|
const name = type.name;
|
|
@@ -20635,7 +21152,7 @@ function build_init_function(node, status) {
|
|
|
20635
21152
|
const slot = ctor_slot;
|
|
20636
21153
|
const field_is_view = !!field.type.is_view && !field.type.is_array;
|
|
20637
21154
|
const field_is_fat_string = field.type.name === "string" && !field.type.is_view && !field.type.is_ref && !field.type.is_array;
|
|
20638
|
-
const field_is_nullable_scalar = is_nullable_scalar_type(field.type);
|
|
21155
|
+
const field_is_nullable_scalar = is_nullable_scalar_type(field.type, status);
|
|
20639
21156
|
if (field_is_view || field_is_fat_string || field_is_nullable_scalar) ctor_slot += 2;
|
|
20640
21157
|
else ctor_slot += 1;
|
|
20641
21158
|
let src_reg;
|
|
@@ -20783,19 +21300,16 @@ function build_init_function(node, status) {
|
|
|
20783
21300
|
}
|
|
20784
21301
|
}
|
|
20785
21302
|
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
|
-
}
|
|
21303
|
+
} else if (field.value.node_type === "func_call" && status.structs.find((s) => s.name === field.type.name && !s.is_simple_type)) {
|
|
21304
|
+
build_node(field.value, status);
|
|
21305
|
+
ensure_newline(status);
|
|
21306
|
+
if (status.structs.find((s) => s.name === field.type.name && !s.is_simple_type).is_class) emit_typed_store(status, "x0", "x19", offset, 8);
|
|
21307
|
+
else {
|
|
21308
|
+
const field_size = get_struct_size(field.type.name, status);
|
|
21309
|
+
const words = Math.ceil(field_size / 8);
|
|
21310
|
+
for (let w = 0; w < words; w++) {
|
|
21311
|
+
emit_asm(status, `ldr x9, [x0, #${w * 8}]\n`);
|
|
21312
|
+
emit_asm(status, `str x9, [x19, #${offset + w * 8}]\n`);
|
|
20799
21313
|
}
|
|
20800
21314
|
}
|
|
20801
21315
|
} else if (field.value.node_type === "func" && field.type.name === "func") {
|
|
@@ -20806,7 +21320,7 @@ function build_init_function(node, status) {
|
|
|
20806
21320
|
ensure_newline(status);
|
|
20807
21321
|
emit_descriptor_address(status, "x1", materialize_lambda_descriptor_a64(field.value, status));
|
|
20808
21322
|
emit_typed_store(status, "x1", "x19", offset, 8);
|
|
20809
|
-
}
|
|
21323
|
+
} else init_expr_field_default(node, field, offset, "x19", status);
|
|
20810
21324
|
}
|
|
20811
21325
|
for (const trait_name of node.traits) {
|
|
20812
21326
|
const trait = status.traits.find((t) => t.name === trait_name);
|
|
@@ -20816,9 +21330,9 @@ function build_init_function(node, status) {
|
|
|
20816
21330
|
if (node.fields.find((nf) => nf.name === field.name)) continue;
|
|
20817
21331
|
const offset = get_field_offset(node.name, field.name, status);
|
|
20818
21332
|
const val = field.value.node_type === "value" ? field.value.value : void 0;
|
|
20819
|
-
if (val === "null" && (is_nullable_scalar_type(field.type) || is_nullable_struct_type(field.type, status))) {
|
|
21333
|
+
if (val === "null" && (is_nullable_scalar_type(field.type, status) || is_nullable_struct_type(field.type, status))) {
|
|
20820
21334
|
const has_off = get_field_has_offset(node.name, field.name, status);
|
|
20821
|
-
if (is_nullable_scalar_type(field.type)) emit_asm(status, `strb wzr, [x19, #${has_off}]\n`);
|
|
21335
|
+
if (is_nullable_scalar_type(field.type, status)) emit_asm(status, `strb wzr, [x19, #${has_off}]\n`);
|
|
20822
21336
|
else emit_asm(status, `str xzr, [x19, #${has_off}]\n`);
|
|
20823
21337
|
continue;
|
|
20824
21338
|
}
|
|
@@ -20959,7 +21473,7 @@ function build_custom_init_function(node, func, status) {
|
|
|
20959
21473
|
param_idx += 2;
|
|
20960
21474
|
continue;
|
|
20961
21475
|
}
|
|
20962
|
-
if (is_nullable_scalar_type(param.type) && !param.type.is_ref && !param.type.is_array) {
|
|
21476
|
+
if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref && !param.type.is_array) {
|
|
20963
21477
|
const size = aarch64_size(param.type.name);
|
|
20964
21478
|
const offset = allocate_stack_space(status, size, size);
|
|
20965
21479
|
status.stack_offsets.set(param.name, offset);
|
|
@@ -21026,6 +21540,10 @@ function build_custom_init_function(node, func, status) {
|
|
|
21026
21540
|
status.current_function = func;
|
|
21027
21541
|
const old_init_assigned_fields = status.init_assigned_fields;
|
|
21028
21542
|
status.init_assigned_fields = /* @__PURE__ */ new Set();
|
|
21543
|
+
const old_init_default_reclaimed = status.init_default_reclaimed_fields;
|
|
21544
|
+
status.init_default_reclaimed_fields = /* @__PURE__ */ new Set();
|
|
21545
|
+
const old_init_heap_string_fields = status.heap_string_fields;
|
|
21546
|
+
status.heap_string_fields = void 0;
|
|
21029
21547
|
emit_asm(status, `str xzr, [x19]\n`);
|
|
21030
21548
|
if (node.traits.length > 0) {
|
|
21031
21549
|
emit_asm(status, `adrp x9, _${node.name}_traits@PAGE\n`);
|
|
@@ -21083,22 +21601,19 @@ function build_custom_init_function(node, func, status) {
|
|
|
21083
21601
|
}
|
|
21084
21602
|
}
|
|
21085
21603
|
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
|
-
}
|
|
21604
|
+
} else if (field.value.node_type === "func_call" && status.structs.find((s) => s.name === field.type.name && !s.is_simple_type)) {
|
|
21605
|
+
build_node(field.value, status);
|
|
21606
|
+
ensure_newline(status);
|
|
21607
|
+
if (status.structs.find((s) => s.name === field.type.name && !s.is_simple_type).is_class) emit_typed_store(status, "x0", "x19", offset, 8);
|
|
21608
|
+
else {
|
|
21609
|
+
const field_size = get_struct_size(field.type.name, status);
|
|
21610
|
+
const words = Math.ceil(field_size / 8);
|
|
21611
|
+
for (let w = 0; w < words; w++) {
|
|
21612
|
+
emit_asm(status, `ldr x9, [x0, #${w * 8}]\n`);
|
|
21613
|
+
emit_asm(status, `str x9, [x19, #${offset + w * 8}]\n`);
|
|
21099
21614
|
}
|
|
21100
21615
|
}
|
|
21101
|
-
}
|
|
21616
|
+
} else init_expr_field_default(node, field, offset, "x19", status);
|
|
21102
21617
|
}
|
|
21103
21618
|
status.buffer_data_cache = void 0;
|
|
21104
21619
|
status.array_ptr_cache = void 0;
|
|
@@ -21108,6 +21623,8 @@ function build_custom_init_function(node, func, status) {
|
|
|
21108
21623
|
status.raw_param_reloads = old_raw_param_reloads;
|
|
21109
21624
|
status.current_function = old_current_function;
|
|
21110
21625
|
status.init_assigned_fields = old_init_assigned_fields;
|
|
21626
|
+
status.init_default_reclaimed_fields = old_init_default_reclaimed;
|
|
21627
|
+
status.heap_string_fields = old_init_heap_string_fields;
|
|
21111
21628
|
}
|
|
21112
21629
|
const init_claims = status.callee_saved_regs_used;
|
|
21113
21630
|
const init_loop_regs = init_claims ? [...init_claims].sort() : [];
|
|
@@ -21247,7 +21764,7 @@ function build_struct_functions$1(node, status) {
|
|
|
21247
21764
|
status.function_view_params = /* @__PURE__ */ new Set();
|
|
21248
21765
|
status.struct_return_buffer = void 0;
|
|
21249
21766
|
const return_struct = !func.return_type?.is_array && (!!status.structs.find((s) => s.name === func.return_type?.name && !s.is_simple_type && !s.is_class) || get_enum_sret_size(func.return_type?.name, status) !== void 0);
|
|
21250
|
-
const nullable_scalar_ret = !!func.return_type && is_nullable_scalar_type(func.return_type) && !func.return_type.is_ref;
|
|
21767
|
+
const nullable_scalar_ret = !!func.return_type && is_nullable_scalar_type(func.return_type, status) && !func.return_type.is_ref;
|
|
21251
21768
|
const uses_sret = return_struct || nullable_scalar_ret;
|
|
21252
21769
|
let return_buffer_stack_offset;
|
|
21253
21770
|
status.current_function_name = func.name;
|
|
@@ -21274,7 +21791,7 @@ function build_struct_functions$1(node, status) {
|
|
|
21274
21791
|
slot_idx += 2;
|
|
21275
21792
|
continue;
|
|
21276
21793
|
}
|
|
21277
|
-
if (is_nullable_scalar_type(param.type) && !param.type.is_ref) {
|
|
21794
|
+
if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref) {
|
|
21278
21795
|
if (param.declaration === "var") status.function_param_vars.add(param.name);
|
|
21279
21796
|
slot_idx += 2;
|
|
21280
21797
|
continue;
|
|
@@ -21357,7 +21874,7 @@ function build_struct_functions$1(node, status) {
|
|
|
21357
21874
|
second_slot_idx += 2;
|
|
21358
21875
|
continue;
|
|
21359
21876
|
}
|
|
21360
|
-
if (is_nullable_scalar_type(param.type) && !param.type.is_ref) {
|
|
21877
|
+
if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref) {
|
|
21361
21878
|
const size = aarch64_size(param.type.name);
|
|
21362
21879
|
const offset = allocate_stack_space(status, size, size);
|
|
21363
21880
|
status.stack_offsets.set(param.name, offset);
|
|
@@ -21814,6 +22331,8 @@ function collect_allocations(node, options) {
|
|
|
21814
22331
|
case "func_call": {
|
|
21815
22332
|
const call = node;
|
|
21816
22333
|
for (const param of call.params) result.push(...collect_allocations(param, options));
|
|
22334
|
+
const base = node.base;
|
|
22335
|
+
if (base) result.push(...collect_allocations(base, options));
|
|
21817
22336
|
break;
|
|
21818
22337
|
}
|
|
21819
22338
|
case "access": {
|
|
@@ -21861,6 +22380,12 @@ function collect_allocations(node, options) {
|
|
|
21861
22380
|
result.push(...collect_allocations(range.right_value, options));
|
|
21862
22381
|
break;
|
|
21863
22382
|
}
|
|
22383
|
+
case "anon_struct": {
|
|
22384
|
+
const anon = node;
|
|
22385
|
+
if (anon.base) result.push(...collect_allocations(anon.base, options));
|
|
22386
|
+
for (const field of anon.fields) result.push(...collect_allocations(field.value, options));
|
|
22387
|
+
break;
|
|
22388
|
+
}
|
|
21864
22389
|
case "array": {
|
|
21865
22390
|
const arr = node;
|
|
21866
22391
|
for (const val of arr.values) result.push(...collect_allocations(val, options));
|
|
@@ -22808,11 +23333,11 @@ function build_constructor_params(fc, param_regs, status) {
|
|
|
22808
23333
|
let nullable_scalar = false;
|
|
22809
23334
|
if (fc.nullable_param_indices?.includes(i)) {
|
|
22810
23335
|
const cp = ctor_callee_params?.[i];
|
|
22811
|
-
if (cp && is_nullable_scalar_type(cp.type)) nullable_scalar = true;
|
|
22812
|
-
else if (is_nullable_scalar_type(p.type)) nullable_scalar = true;
|
|
23336
|
+
if (cp && is_nullable_scalar_type(cp.type, status)) nullable_scalar = true;
|
|
23337
|
+
else if (is_nullable_scalar_type(p.type, status)) nullable_scalar = true;
|
|
22813
23338
|
else if (!cp) {
|
|
22814
23339
|
const field = (status.structs.find((s) => s.name === fc.name && !s.is_simple_type)?.fields.filter((f) => f.value == null))?.[i];
|
|
22815
|
-
if (field && is_nullable_scalar_type(field.type)) nullable_scalar = true;
|
|
23340
|
+
if (field && is_nullable_scalar_type(field.type, status)) nullable_scalar = true;
|
|
22816
23341
|
}
|
|
22817
23342
|
}
|
|
22818
23343
|
const is_pair = pt === "string" || lit || is_view || nullable_scalar;
|
|
@@ -22839,7 +23364,7 @@ function build_constructor_params(fc, param_regs, status) {
|
|
|
22839
23364
|
if (arg.node_type === "value" && arg.value === "null") {
|
|
22840
23365
|
emit_asm(status, `mov x0, #0\n`);
|
|
22841
23366
|
emit_asm(status, `mov x1, #0\n`);
|
|
22842
|
-
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
|
|
23367
|
+
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
|
|
22843
23368
|
const vname = arg.value;
|
|
22844
23369
|
emit_var_load(status, "x0", vname, aarch64_size(arg.type.name));
|
|
22845
23370
|
emit_var_load(status, "x1", has_flag_name(vname), 8);
|
|
@@ -23229,7 +23754,7 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23229
23754
|
emit_var_store(status, "x0", node.name, 8);
|
|
23230
23755
|
return;
|
|
23231
23756
|
}
|
|
23232
|
-
if (is_nullable_scalar_type(node.type)) {
|
|
23757
|
+
if (is_nullable_scalar_type(node.type, status)) {
|
|
23233
23758
|
if (status.function_return_label) {
|
|
23234
23759
|
const offset = allocate_stack_space(status, 16, 16);
|
|
23235
23760
|
status.stack_offsets.set(node.name, offset);
|
|
@@ -23240,14 +23765,23 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23240
23765
|
}
|
|
23241
23766
|
const init_value = node.value;
|
|
23242
23767
|
const init_type = call_result_type(init_value) ?? type_from_value_node$1(init_value);
|
|
23243
|
-
const value_is_nullable_call = init_value.node_type === "func_call" && is_nullable_scalar_type(init_type);
|
|
23244
|
-
|
|
23768
|
+
const value_is_nullable_call = init_value.node_type === "func_call" && is_nullable_scalar_type(init_type, status);
|
|
23769
|
+
const value_is_nullable_method = init_value.node_type === "access" && init_value.access.node_type === "access_func" && is_nullable_scalar_type(init_type, status);
|
|
23770
|
+
const value_is_nullable_field = init_value.node_type === "access" && init_value.access.node_type === "access_field" && is_nullable_scalar_type(init_type, status);
|
|
23771
|
+
if (value_is_nullable_method) {
|
|
23245
23772
|
emit_init_value$1(init_value, nir_init, status);
|
|
23246
23773
|
ensure_newline(status);
|
|
23247
23774
|
emit_asm(status, `ldr x9, [x0]\n`);
|
|
23248
23775
|
emit_asm(status, `str x9, [x29, #${offset}]\n`);
|
|
23249
23776
|
emit_asm(status, `ldr x9, [x0, #8]\n`);
|
|
23250
23777
|
emit_asm(status, `str x9, [x29, #${offset + 8}]\n`);
|
|
23778
|
+
} else if (value_is_nullable_field) {
|
|
23779
|
+
emit_init_value$1(init_value, nir_init, status);
|
|
23780
|
+
ensure_newline(status);
|
|
23781
|
+
emit_var_store(status, "x0", node.name, 8);
|
|
23782
|
+
load_nullable_has(init_value, "x9", status);
|
|
23783
|
+
ensure_newline(status);
|
|
23784
|
+
emit_asm(status, `str x9, [x29, #${offset + 8}]\n`);
|
|
23251
23785
|
} else if (value_is_nullable_call) {
|
|
23252
23786
|
const old_buffer = status.struct_return_buffer;
|
|
23253
23787
|
const old_buffer_var = status.struct_return_buffer_var;
|
|
@@ -23269,8 +23803,13 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23269
23803
|
}
|
|
23270
23804
|
return;
|
|
23271
23805
|
}
|
|
23272
|
-
|
|
23273
|
-
|
|
23806
|
+
const global_is_null = !node.value || node.value.node_type === "value" && node.value.value === "null";
|
|
23807
|
+
if (global_is_null) emit_data(status, `${node.name}: .space 8\n`);
|
|
23808
|
+
else {
|
|
23809
|
+
emit_data(status, `${node.name}: ${aarch64_type(node.type.name)} ${get_raw_value$1(node.value, status)}\n`);
|
|
23810
|
+
if (aarch64_size(node.type.name) < 8) emit_data(status, `.p2align 3\n`);
|
|
23811
|
+
}
|
|
23812
|
+
emit_data(status, `${has_flag_name(node.name)}: .quad ${global_is_null ? 0 : 1}\n`);
|
|
23274
23813
|
return;
|
|
23275
23814
|
}
|
|
23276
23815
|
const directive = aarch64_type(node.type.name);
|
|
@@ -23893,6 +24432,8 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23893
24432
|
if (outgoing > 0) emit_asm(status, `add sp, sp, #${outgoing}\n`);
|
|
23894
24433
|
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
24434
|
build_swap_params(func_call, status);
|
|
24435
|
+
const ctor_struct = status.structs.find((s) => s.name === func_call.name && !s.is_simple_type && !s.is_class);
|
|
24436
|
+
if (ctor_struct) record_ctor_heap_default_fields(node.name, ctor_struct, status, (seed) => is_owned_heap_temp(seed, status));
|
|
23896
24437
|
} 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
24438
|
const old_buffer = status.struct_return_buffer;
|
|
23898
24439
|
const old_buffer_var = status.struct_return_buffer_var;
|
|
@@ -24770,14 +25311,18 @@ const INT_CALLER_SAVED_EXT = [
|
|
|
24770
25311
|
function can_share_claimed_register(status, name, reg) {
|
|
24771
25312
|
const shared = status.nir_alloc_shared;
|
|
24772
25313
|
if (!shared || !status.register_allocations) return false;
|
|
25314
|
+
if (!shared.source_keys.has(name)) return false;
|
|
24773
25315
|
if (status.region_pinned?.get(reg)) return false;
|
|
24774
|
-
|
|
25316
|
+
if (status.nir_loop_carry_regs?.has(reg)) return false;
|
|
25317
|
+
const cand_keys = shared.source_keys.get(name);
|
|
24775
25318
|
let occupants = 0;
|
|
24776
25319
|
for (const [occupant, occupant_reg] of status.register_allocations) {
|
|
24777
25320
|
if (occupant_reg !== reg || occupant === name) continue;
|
|
24778
25321
|
occupants++;
|
|
24779
25322
|
if (shared.pinned.has(occupant)) return false;
|
|
24780
|
-
|
|
25323
|
+
const occ_keys = shared.source_keys.get(occupant);
|
|
25324
|
+
if (!occ_keys) return false;
|
|
25325
|
+
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
25326
|
}
|
|
24782
25327
|
return occupants > 0;
|
|
24783
25328
|
}
|
|
@@ -24954,6 +25499,7 @@ function promote_loop_locals(status, scoped_declarations, sources, options) {
|
|
|
24954
25499
|
else used_x.add(r);
|
|
24955
25500
|
if (status.nir_caller_saved_claimed) for (const r of status.nir_caller_saved_claimed) used_x.add(r);
|
|
24956
25501
|
if (status.region_pinned) for (const r of status.region_pinned.keys()) used_x.add(r);
|
|
25502
|
+
const loop_carry_regs = status.nir_loop_carry_regs;
|
|
24957
25503
|
const site_regs = /* @__PURE__ */ new Set();
|
|
24958
25504
|
if (status.nir_site_allocs) for (const s of status.nir_site_allocs.values()) site_regs.add(s.reg);
|
|
24959
25505
|
const float_pool = options?.call_free === true ? [...FLOAT_CALLEE_SAVED, ...FLOAT_CALLER_SAVED_EXT] : FLOAT_CALLEE_SAVED;
|
|
@@ -24977,6 +25523,10 @@ function promote_loop_locals(status, scoped_declarations, sources, options) {
|
|
|
24977
25523
|
d_idx++;
|
|
24978
25524
|
continue;
|
|
24979
25525
|
}
|
|
25526
|
+
if (loop_carry_regs?.has(reg0) || loop_carry_regs?.has(reg1)) {
|
|
25527
|
+
d_idx++;
|
|
25528
|
+
continue;
|
|
25529
|
+
}
|
|
24980
25530
|
if (used_d.has(reg0)) {
|
|
24981
25531
|
if (!can_share_claimed_register(status, v.name, reg0)) {
|
|
24982
25532
|
d_idx++;
|
|
@@ -25024,6 +25574,10 @@ function promote_loop_locals(status, scoped_declarations, sources, options) {
|
|
|
25024
25574
|
d_idx++;
|
|
25025
25575
|
continue;
|
|
25026
25576
|
}
|
|
25577
|
+
if (loop_carry_regs?.has(float_pool[d_idx])) {
|
|
25578
|
+
d_idx++;
|
|
25579
|
+
continue;
|
|
25580
|
+
}
|
|
25027
25581
|
if (!used_d.has(float_pool[d_idx])) break;
|
|
25028
25582
|
if (can_share_claimed_register(status, v.name, float_pool[d_idx])) break;
|
|
25029
25583
|
d_idx++;
|
|
@@ -25046,6 +25600,10 @@ function promote_loop_locals(status, scoped_declarations, sources, options) {
|
|
|
25046
25600
|
x_idx++;
|
|
25047
25601
|
continue;
|
|
25048
25602
|
}
|
|
25603
|
+
if (loop_carry_regs?.has(x_pool[x_idx])) {
|
|
25604
|
+
x_idx++;
|
|
25605
|
+
continue;
|
|
25606
|
+
}
|
|
25049
25607
|
if (!used_x.has(x_pool[x_idx])) break;
|
|
25050
25608
|
if (can_share_claimed_register(status, v.name, x_pool[x_idx])) break;
|
|
25051
25609
|
x_idx++;
|
|
@@ -25357,23 +25915,33 @@ function build_if_else_node$1(node, status, nir) {
|
|
|
25357
25915
|
ensure_newline(status);
|
|
25358
25916
|
const pre_cache = status.buffer_data_cache;
|
|
25359
25917
|
const pre_array_cache = status.array_ptr_cache;
|
|
25918
|
+
const pre_heap_string_fields = status.heap_string_fields;
|
|
25360
25919
|
if (node.else_branch) {
|
|
25361
25920
|
const then_frame = enter_scope_frame(status);
|
|
25362
25921
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25363
25922
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
25923
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25364
25924
|
build_block_with_cursor$1(node.if_branch, nir?.then_branch, status);
|
|
25925
|
+
const then_fields = status.heap_string_fields;
|
|
25365
25926
|
exit_scope_frame(status, then_frame);
|
|
25366
25927
|
emit_asm(status, `b end_${label}\n`);
|
|
25367
25928
|
emit_asm(status, `else_${label}:\n`);
|
|
25368
25929
|
const else_frame = enter_scope_frame(status);
|
|
25369
25930
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25370
25931
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
25932
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25371
25933
|
build_block_with_cursor$1(node.else_branch, nir?.else_branch, status);
|
|
25934
|
+
const else_fields = status.heap_string_fields;
|
|
25372
25935
|
exit_scope_frame(status, else_frame);
|
|
25936
|
+
const merged = new Set(pre_heap_string_fields ?? []);
|
|
25937
|
+
for (const key of then_fields ?? []) merged.add(key);
|
|
25938
|
+
for (const key of else_fields ?? []) merged.add(key);
|
|
25939
|
+
status.heap_string_fields = merged;
|
|
25373
25940
|
} else if (node.if_branch) {
|
|
25374
25941
|
const then_frame = enter_scope_frame(status);
|
|
25375
25942
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25376
25943
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
25944
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25377
25945
|
build_block_with_cursor$1(node.if_branch, nir?.then_branch, status);
|
|
25378
25946
|
exit_scope_frame(status, then_frame);
|
|
25379
25947
|
}
|
|
@@ -25541,6 +26109,95 @@ function ctor_return_owned_string_fields(return_struct, call, heap_strings) {
|
|
|
25541
26109
|
}
|
|
25542
26110
|
return skip;
|
|
25543
26111
|
}
|
|
26112
|
+
/** The OVERRIDE value of a string field, when the field is overridden. */
|
|
26113
|
+
function override_value_for_field(overrides, field_name) {
|
|
26114
|
+
return overrides?.find((o) => o.name === field_name)?.value;
|
|
26115
|
+
}
|
|
26116
|
+
/**
|
|
26117
|
+
* The skip set for an OVERRIDE return (`return [ .. <base>, f = v, ... ]` in
|
|
26118
|
+
* its checked forms: a ctor call with `field_overrides`, or a base-bearing
|
|
26119
|
+
* anonymous struct literal). The overrides were applied to the return temp
|
|
26120
|
+
* BEFORE this analysis runs, so an overridden field holds the OVERRIDE
|
|
26121
|
+
* value's pair: it stays raw only when that value owns heap (a transferred
|
|
26122
|
+
* heap local, a fresh non-borrow expression). A non-overridden field keeps
|
|
26123
|
+
* the base's value: with `base_uniformly_owned` (a forwarded registered
|
|
26124
|
+
* normalizing base) it transfers raw; otherwise it follows `base_skip` (the
|
|
26125
|
+
* plain constructor-argument ownership analysis).
|
|
26126
|
+
*/
|
|
26127
|
+
function override_return_string_fields(return_struct, overrides, heap_strings, opts) {
|
|
26128
|
+
const skip = /* @__PURE__ */ new Set();
|
|
26129
|
+
for (const field of direct_string_fields(return_struct)) {
|
|
26130
|
+
const override = override_value_for_field(overrides, field.name);
|
|
26131
|
+
if (override) {
|
|
26132
|
+
if (tuple_return_owned_element(override, heap_strings)) skip.add(field.name);
|
|
26133
|
+
} else if (opts.base_uniformly_owned || opts.base_skip?.has(field.name)) skip.add(field.name);
|
|
26134
|
+
}
|
|
26135
|
+
return skip;
|
|
26136
|
+
}
|
|
26137
|
+
/**
|
|
26138
|
+
* The overridden string fields whose CONSTRUCTOR-SEEDED value owns heap and
|
|
26139
|
+
* is therefore displaced un-freed by a raw override store: a non-literal
|
|
26140
|
+
* default expression (the auto-constructor evaluates it fresh per
|
|
26141
|
+
* construction — `var string a = "de" + "fault"` allocates) is owned by the
|
|
26142
|
+
* constructed struct alone, so an override replacing the field orphans the
|
|
26143
|
+
* buffer unless it is reclaimed ahead of the store. Literal defaults are
|
|
26144
|
+
* rodata (never freed) and value-node defaults are consts/borrows (not
|
|
26145
|
+
* owned); a custom `#init`'s `self.<field> = <param>` seeds alias the
|
|
26146
|
+
* caller's argument temps in the common case, but a COMPUTED seed
|
|
26147
|
+
* (`self.a = s + "!"`, classified by `is_init_seed_heap`) owns heap — those
|
|
26148
|
+
* fields are displaced too.
|
|
26149
|
+
*/
|
|
26150
|
+
function override_displaced_string_fields(struct_node, overrides, is_init_seed_heap) {
|
|
26151
|
+
if (!overrides?.length) return [];
|
|
26152
|
+
const computed = init_computed_heap_string_fields(struct_node, is_init_seed_heap);
|
|
26153
|
+
const displaced = [];
|
|
26154
|
+
for (const override of overrides) {
|
|
26155
|
+
const field = struct_node.fields.find((f) => f.name === override.name);
|
|
26156
|
+
if (!field) continue;
|
|
26157
|
+
if (field.type.name !== "string" || field.type.is_array || field.type.is_view) continue;
|
|
26158
|
+
if (computed.has(override.name)) {
|
|
26159
|
+
displaced.push(override.name);
|
|
26160
|
+
continue;
|
|
26161
|
+
}
|
|
26162
|
+
const seed = field.value;
|
|
26163
|
+
if (!seed || seed.node_type === "value") continue;
|
|
26164
|
+
if (is_string_borrow(seed)) continue;
|
|
26165
|
+
displaced.push(override.name);
|
|
26166
|
+
}
|
|
26167
|
+
return displaced;
|
|
26168
|
+
}
|
|
26169
|
+
/**
|
|
26170
|
+
* The aarch64 counterpart of override_displaced_string_fields: the
|
|
26171
|
+
* overridden string fields whose ctor-seeded value is a non-literal
|
|
26172
|
+
* expression that produced a HEAP value — a non-literal DEFAULT, or a
|
|
26173
|
+
* custom `#init`'s computed seed (`init_computed_heap_string_fields` under
|
|
26174
|
+
* the same `is_owned_heap` classifier). The extra `is_owned_heap` check
|
|
26175
|
+
* (the aarch64 is_owned_heap_temp) matters because the aarch64 backend does
|
|
26176
|
+
* NOT strdup every string return: a literal-returning user function leaves
|
|
26177
|
+
* RODATA in the seeded field, which must never be freed. Used at the
|
|
26178
|
+
* expression-temp override boundaries (no heap_string_fields records exist
|
|
26179
|
+
* there, so the stores stay raw and the seed needs an explicit reclaim).
|
|
26180
|
+
*/
|
|
26181
|
+
function ctor_heap_displaced_string_fields(struct_node, overrides, is_owned_heap) {
|
|
26182
|
+
if (!overrides?.length) return [];
|
|
26183
|
+
const computed = init_computed_heap_string_fields(struct_node, is_owned_heap);
|
|
26184
|
+
const displaced = [];
|
|
26185
|
+
for (const override of overrides) {
|
|
26186
|
+
const field = struct_node.fields.find((f) => f.name === override.name);
|
|
26187
|
+
if (!field) continue;
|
|
26188
|
+
if (field.type.name !== "string" || field.type.is_array || field.type.is_view) continue;
|
|
26189
|
+
if (computed.has(override.name)) {
|
|
26190
|
+
displaced.push(override.name);
|
|
26191
|
+
continue;
|
|
26192
|
+
}
|
|
26193
|
+
const seed = field.value;
|
|
26194
|
+
if (!seed || seed.node_type === "value") continue;
|
|
26195
|
+
if (is_string_borrow(seed)) continue;
|
|
26196
|
+
if (!is_owned_heap(seed)) continue;
|
|
26197
|
+
displaced.push(override.name);
|
|
26198
|
+
}
|
|
26199
|
+
return displaced;
|
|
26200
|
+
}
|
|
25544
26201
|
//#endregion
|
|
25545
26202
|
//#region ../src/build_aarch64/build_return_node.ts
|
|
25546
26203
|
let temp_counter$1 = 0;
|
|
@@ -25616,7 +26273,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25616
26273
|
hoist_field_overrides(node.value, build_node, status);
|
|
25617
26274
|
if (node.from_inline) return;
|
|
25618
26275
|
const returns_nullable_struct = is_nullable_struct_type(status.function_return_type, status);
|
|
25619
|
-
const returns_nullable_scalar = is_nullable_scalar_type(status.function_return_type);
|
|
26276
|
+
const returns_nullable_scalar = is_nullable_scalar_type(status.function_return_type, status);
|
|
25620
26277
|
if ((returns_nullable_struct || returns_nullable_scalar) && (!node.value || node.value.node_type === "value" && node.value.value === "null")) {
|
|
25621
26278
|
if (status.return_buffer_stack_offset !== void 0) {
|
|
25622
26279
|
const struct_size = get_struct_size(status.function_return_type.name, status);
|
|
@@ -25723,7 +26380,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25723
26380
|
if (node.value?.node_type === "anon_struct") {
|
|
25724
26381
|
const anon = node.value;
|
|
25725
26382
|
const struct_size = get_struct_size(anon.type.name, status);
|
|
25726
|
-
const temp_name = `
|
|
26383
|
+
const temp_name = `_return_val_anon_${temp_counter$1++}`;
|
|
25727
26384
|
const offset = allocate_stack_space(status, struct_size);
|
|
25728
26385
|
status.stack_offsets.set(temp_name, offset);
|
|
25729
26386
|
if (!status.variable_types) status.variable_types = /* @__PURE__ */ new Map();
|
|
@@ -25733,6 +26390,14 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25733
26390
|
emit_var_address(status, "x1", temp_name);
|
|
25734
26391
|
emit_struct_copy("x0", "x1", 0, struct_size, status);
|
|
25735
26392
|
if (anon.base.is_moved) mark_moved_if_struct(anon.base, status);
|
|
26393
|
+
if (anon.base.node_type === "func_call" && is_normalized_struct_call(anon.base, status)) {
|
|
26394
|
+
const base_struct = status.structs.find((s) => s.name === anon.type.name && !s.is_simple_type && !s.is_class);
|
|
26395
|
+
if (base_struct) for (const field of direct_string_fields(base_struct)) {
|
|
26396
|
+
if (!anon.fields.some((o) => o.name === field.name)) continue;
|
|
26397
|
+
emit_asm(status, `ldp x0, x1, [x29, #${offset + get_field_offset(base_struct.name, field.name, status)}]\n`);
|
|
26398
|
+
emit_asm(status, `bl _nomen_free_wrap\n`);
|
|
26399
|
+
}
|
|
26400
|
+
}
|
|
25736
26401
|
emit_field_overrides(temp_name, anon, build_node, status);
|
|
25737
26402
|
emit_var_address(status, "x0", temp_name);
|
|
25738
26403
|
} else {
|
|
@@ -25768,7 +26433,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25768
26433
|
if (!is_str_lit && !is_numeric && raw_value !== "null" && !status.heap_strings?.has(raw_value)) needs_borrow_strdup = true;
|
|
25769
26434
|
} else if (is_container_borrow_access(node.value)) needs_borrow_strdup = true;
|
|
25770
26435
|
else if (node.value.node_type === "access" && node.value.access.node_type === "access_field") needs_borrow_strdup = true;
|
|
25771
|
-
else if (
|
|
26436
|
+
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
26437
|
else if ((node.value.node_type === "match" || node.value.node_type === "switch") && !return_join_owned_string) needs_borrow_strdup = true;
|
|
25773
26438
|
}
|
|
25774
26439
|
if (needs_borrow_strdup) {
|
|
@@ -25777,6 +26442,15 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25777
26442
|
ensure_newline(status);
|
|
25778
26443
|
status.last_result_is_heap = true;
|
|
25779
26444
|
}
|
|
26445
|
+
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) {
|
|
26446
|
+
emit_strdup_string(status);
|
|
26447
|
+
ensure_newline(status);
|
|
26448
|
+
status.last_result_is_heap = true;
|
|
26449
|
+
if (borrow_fn_name) {
|
|
26450
|
+
status.heap_returning_functions?.add(borrow_fn_name);
|
|
26451
|
+
if (borrow_mangled !== void 0) status.heap_returning_functions?.add(borrow_mangled);
|
|
26452
|
+
}
|
|
26453
|
+
}
|
|
25780
26454
|
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
26455
|
if (status.last_result_is_heap && current_return_is_string(status)) {
|
|
25782
26456
|
if (!status.heap_returning_functions) status.heap_returning_functions = /* @__PURE__ */ new Set();
|
|
@@ -25789,7 +26463,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25789
26463
|
if (status.return_buffer_stack_offset !== void 0) emit_asm(status, `ldr x8, [x29, #${status.return_buffer_stack_offset}]\n`);
|
|
25790
26464
|
emit_typed_store(status, "x0", "x8", 0, aarch64_size(status.function_return_type.name));
|
|
25791
26465
|
const value_type = node.value ? type_from_value_node$1(node.value) : void 0;
|
|
25792
|
-
if (node.value && value_type?.is_nullable && is_nullable_scalar_type(value_type)) {
|
|
26466
|
+
if (node.value && value_type?.is_nullable && is_nullable_scalar_type(value_type, status)) {
|
|
25793
26467
|
load_nullable_has(node.value, "x9", status);
|
|
25794
26468
|
ensure_newline(status);
|
|
25795
26469
|
emit_asm(status, `str x9, [x8, #8]\n`);
|
|
@@ -25837,10 +26511,19 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
25837
26511
|
}
|
|
25838
26512
|
emit_string_field_strdups_at(status, ret_struct.name, "x8", 0, skip_fields);
|
|
25839
26513
|
}
|
|
25840
|
-
if (node.value?.node_type === "func_call" && node.value.name === ret_struct.name && !ret_struct.name.startsWith("_")
|
|
25841
|
-
const
|
|
26514
|
+
if (node.value?.node_type === "func_call" && node.value.name === ret_struct.name && !ret_struct.name.startsWith("_")) {
|
|
26515
|
+
const call = node.value;
|
|
26516
|
+
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
26517
|
emit_string_field_strdups_at(status, ret_struct.name, "x8", 0, skip_fields);
|
|
25843
26518
|
}
|
|
26519
|
+
{
|
|
26520
|
+
const anon = node.value?.node_type === "anon_struct" ? node.value : void 0;
|
|
26521
|
+
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;
|
|
26522
|
+
if (forwarded_base && node.value.name !== ret_struct.name && is_normalized_struct_call(forwarded_base, status)) {
|
|
26523
|
+
const skip_fields = override_return_string_fields(ret_struct, anon ? anon.fields : node.value.field_overrides, status.heap_strings, { base_uniformly_owned: true });
|
|
26524
|
+
emit_string_field_strdups_at(status, ret_struct.name, "x8", 0, skip_fields);
|
|
26525
|
+
}
|
|
26526
|
+
}
|
|
25844
26527
|
if (node.value?.node_type === "func_call" && String(node.value.name ?? "").startsWith("_Tuple_") && node.value.type?.name?.startsWith("_Tuple_")) {
|
|
25845
26528
|
const tuple_call = node.value;
|
|
25846
26529
|
const skip_fields = /* @__PURE__ */ new Set();
|
|
@@ -25943,6 +26626,8 @@ function build_switch_node$1(node, status, nir) {
|
|
|
25943
26626
|
const old_scoped_declarations = enter_scope_frame(status);
|
|
25944
26627
|
const pre_cache = status.buffer_data_cache;
|
|
25945
26628
|
const pre_array_cache = status.array_ptr_cache;
|
|
26629
|
+
const pre_heap_string_fields = status.heap_string_fields;
|
|
26630
|
+
const case_field_sets = [];
|
|
25946
26631
|
for (let i = 0; i < node.cases.length; i++) {
|
|
25947
26632
|
status.scoped_declarations = [];
|
|
25948
26633
|
build_node(node.cases[i].condition, status);
|
|
@@ -25951,7 +26636,9 @@ function build_switch_node$1(node, status, nir) {
|
|
|
25951
26636
|
else emit_asm(status, `beq end_switch_${label}\n`);
|
|
25952
26637
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25953
26638
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
26639
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25954
26640
|
build_block_with_cursor$1(node.cases[i].branch, nir?.arms[i]?.branch, status);
|
|
26641
|
+
case_field_sets.push(status.heap_string_fields);
|
|
25955
26642
|
emit_asm(status, `b end_switch_${label}\n`);
|
|
25956
26643
|
emit_asm(status, `sw_next_${label}_${i}:\n`);
|
|
25957
26644
|
}
|
|
@@ -25959,8 +26646,15 @@ function build_switch_node$1(node, status, nir) {
|
|
|
25959
26646
|
status.scoped_declarations = [];
|
|
25960
26647
|
status.buffer_data_cache = new Map(pre_cache);
|
|
25961
26648
|
status.array_ptr_cache = new Map(pre_array_cache);
|
|
26649
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
25962
26650
|
build_block_with_cursor$1(node.else_branch, nir?.otherwise ?? void 0, status);
|
|
26651
|
+
case_field_sets.push(status.heap_string_fields);
|
|
25963
26652
|
}
|
|
26653
|
+
if (case_field_sets.length > 1) {
|
|
26654
|
+
const merged = new Set(pre_heap_string_fields ?? []);
|
|
26655
|
+
for (const set of case_field_sets) for (const key of set) merged.add(key);
|
|
26656
|
+
status.heap_string_fields = merged;
|
|
26657
|
+
} else if (case_field_sets.length === 1) status.heap_string_fields = case_field_sets[0];
|
|
25964
26658
|
status.buffer_data_cache = pre_cache;
|
|
25965
26659
|
status.array_ptr_cache = pre_array_cache;
|
|
25966
26660
|
emit_asm(status, `end_switch_${label}:\n`);
|
|
@@ -29399,7 +30093,7 @@ function build_function_call_node$1(node, status) {
|
|
|
29399
30093
|
if (!node.nullable_param_indices?.includes(i)) continue;
|
|
29400
30094
|
const arg_type = node.params[i].type;
|
|
29401
30095
|
const callee_param = node.resolved_function?.params?.[i];
|
|
29402
|
-
if (is_nullable_scalar_type(arg_type) || callee_param && is_nullable_scalar_type(callee_param.type)) nullable_scalar_arg_set.add(i);
|
|
30096
|
+
if (is_nullable_scalar_type(arg_type, status) || callee_param && is_nullable_scalar_type(callee_param.type, status)) nullable_scalar_arg_set.add(i);
|
|
29403
30097
|
}
|
|
29404
30098
|
const arg_slot = [];
|
|
29405
30099
|
let total_slots = 0;
|
|
@@ -29497,7 +30191,7 @@ function build_function_call_node$1(node, status) {
|
|
|
29497
30191
|
if (arg.node_type === "value" && arg.value === "null") {
|
|
29498
30192
|
emit_asm(status, `mov x0, #0\n`);
|
|
29499
30193
|
emit_asm(status, `mov x1, #0\n`);
|
|
29500
|
-
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
|
|
30194
|
+
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
|
|
29501
30195
|
const vname = arg.value;
|
|
29502
30196
|
emit_var_load(status, "x0", vname, aarch64_size(param_type.name));
|
|
29503
30197
|
emit_var_load(status, "x1", has_flag_name(vname), 8);
|
|
@@ -29611,7 +30305,7 @@ function build_function_call_node$1(node, status) {
|
|
|
29611
30305
|
} else if (!is_struct && node.type?.name && !status.call_x8_preset) {
|
|
29612
30306
|
const return_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
|
|
29613
30307
|
const return_enum_size = get_enum_sret_size(node.type?.name, status);
|
|
29614
|
-
const nullable_scalar_ret = is_nullable_scalar_type(node.type);
|
|
30308
|
+
const nullable_scalar_ret = is_nullable_scalar_type(node.type, status);
|
|
29615
30309
|
if (return_struct || return_enum_size !== void 0 || nullable_scalar_ret) {
|
|
29616
30310
|
const nullable_ret = is_nullable_struct_type(node.type, status);
|
|
29617
30311
|
const struct_size = return_struct ? get_struct_size(node.type.name, status) : nullable_scalar_ret ? 8 : return_enum_size;
|
|
@@ -29628,6 +30322,12 @@ function build_function_call_node$1(node, status) {
|
|
|
29628
30322
|
if (build_inline_function(inline_candidate, status)) return;
|
|
29629
30323
|
}
|
|
29630
30324
|
emit_asm(status, `bl ${func_name}\n`);
|
|
30325
|
+
{
|
|
30326
|
+
const callee = node.resolved_function;
|
|
30327
|
+
if (callee && !callee.params?.some((p) => p.is_self_param)) transfer_ref_param_field_records(callee, node.params, status, (name) => {
|
|
30328
|
+
return !!status.scoped_declarations?.some((d) => d.name === name) && !is_local_ref_var(name, status);
|
|
30329
|
+
});
|
|
30330
|
+
}
|
|
29631
30331
|
if (is_float_type$1(node.type.name)) emit_asm(status, `fmov x0, d0\n`);
|
|
29632
30332
|
if (outgoing_size > 0) emit_asm(status, `add sp, sp, #${outgoing_size}\n`);
|
|
29633
30333
|
if (status.buffer_data_cache) {
|
|
@@ -29661,7 +30361,7 @@ function build_function_call_node$1(node, status) {
|
|
|
29661
30361
|
if (!is_struct && node.type?.name && !status.call_x8_preset) {
|
|
29662
30362
|
const return_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
|
|
29663
30363
|
const return_enum_size = get_enum_sret_size(node.type?.name, status);
|
|
29664
|
-
const nullable_scalar_ret = is_nullable_scalar_type(node.type);
|
|
30364
|
+
const nullable_scalar_ret = is_nullable_scalar_type(node.type, status);
|
|
29665
30365
|
if (return_struct || return_enum_size !== void 0 || nullable_scalar_ret) {
|
|
29666
30366
|
const temp_name = `_call_ret_${temp_counter - 1}`;
|
|
29667
30367
|
emit_asm(status, `add x0, x29, #${status.stack_offsets.get(temp_name)}\n`);
|
|
@@ -29678,6 +30378,11 @@ function build_function_call_node$1(node, status) {
|
|
|
29678
30378
|
const offset = status.stack_offsets.get(temp_addr);
|
|
29679
30379
|
emit_asm(status, `add x0, x29, #${offset}\n`);
|
|
29680
30380
|
if (node.field_overrides?.length) {
|
|
30381
|
+
const ctor_struct = status.structs.find((s) => s.name === node.name && !s.is_simple_type && !s.is_class);
|
|
30382
|
+
if (ctor_struct) for (const fname of ctor_heap_displaced_string_fields(ctor_struct, node.field_overrides, (seed) => is_owned_heap_temp(seed, status))) {
|
|
30383
|
+
emit_asm(status, `ldr x0, [x29, #${offset + get_field_offset(ctor_struct.name, fname, status)}]\n`);
|
|
30384
|
+
emit_free(status);
|
|
30385
|
+
}
|
|
29681
30386
|
emit_field_overrides(temp_addr, node, build_node, status);
|
|
29682
30387
|
emit_asm(status, `add x0, x29, #${offset}\n`);
|
|
29683
30388
|
}
|
|
@@ -30531,7 +31236,7 @@ function build_struct_body(node, status) {
|
|
|
30531
31236
|
status.code += `];\n`;
|
|
30532
31237
|
} else {
|
|
30533
31238
|
status.code += `${field_c_type(field.type, status)} ${field.name};\n`;
|
|
30534
|
-
if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) status.code += `unsigned char ${has_flag_name(field.name)};\n`;
|
|
31239
|
+
if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) status.code += `unsigned char ${has_flag_name(field.name)};\n`;
|
|
30535
31240
|
}
|
|
30536
31241
|
for (let traitName of node.traits) {
|
|
30537
31242
|
const trait = status.traits.find((n) => n.name === traitName);
|
|
@@ -30659,12 +31364,12 @@ function build_operation_node$1(node, status) {
|
|
|
30659
31364
|
if (left_type?.is_nullable && left_type.name === "string") {
|
|
30660
31365
|
status.code += `({ nomen_string _nss_${ns_tmp_counter} = `;
|
|
30661
31366
|
build_node$1(node.left_value, status);
|
|
30662
|
-
status.code += `; _nss_${ns_tmp_counter}.ptr ? _nss_${ns_tmp_counter} : `;
|
|
31367
|
+
status.code += `; _nss_${ns_tmp_counter}.ptr ? nomen_str_dup(_nss_${ns_tmp_counter}) : nomen_str_dup(`;
|
|
30663
31368
|
build_node$1(node.right_value, status);
|
|
30664
|
-
status.code +=
|
|
31369
|
+
status.code += `); })`;
|
|
30665
31370
|
ns_tmp_counter++;
|
|
30666
|
-
} else if (is_nullable_scalar_type(left_type)) {
|
|
30667
|
-
if (node.left_value.node_type === "func_call" && is_nullable_scalar_type(node.left_value.type)) {
|
|
31371
|
+
} else if (is_nullable_scalar_type(left_type, status)) {
|
|
31372
|
+
if (node.left_value.node_type === "func_call" && is_nullable_scalar_type(node.left_value.type, status)) {
|
|
30668
31373
|
const id = ns_tmp_counter++;
|
|
30669
31374
|
const flag = `_nsh_${id}`;
|
|
30670
31375
|
const val = `_nsv_${id}`;
|
|
@@ -30726,8 +31431,8 @@ function build_operation_node$1(node, status) {
|
|
|
30726
31431
|
else status.code += `((`;
|
|
30727
31432
|
build_node$1(nullable_side, status);
|
|
30728
31433
|
status.code += `.ptr != 0))`;
|
|
30729
|
-
} else if (is_nullable_scalar_type(type_from_value_node$1(nullable_side)) && type?.is_nullable && type.name !== "string") {
|
|
30730
|
-
if (nullable_side.node_type === "func_call" && is_nullable_scalar_type(nullable_side.type)) {
|
|
31434
|
+
} else if (is_nullable_scalar_type(type_from_value_node$1(nullable_side), status) && type?.is_nullable && type.name !== "string") {
|
|
31435
|
+
if (nullable_side.node_type === "func_call" && is_nullable_scalar_type(nullable_side.type, status)) {
|
|
30731
31436
|
const id = ns_tmp_counter++;
|
|
30732
31437
|
const flag = `_nsh_${id}`;
|
|
30733
31438
|
const val = `_nsv_${id}`;
|
|
@@ -30891,7 +31596,7 @@ function is_null_comparison(node) {
|
|
|
30891
31596
|
}
|
|
30892
31597
|
function is_nullable_struct_side(node, status) {
|
|
30893
31598
|
const t = type_from_value_node$1(node);
|
|
30894
|
-
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
|
|
31599
|
+
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
|
|
30895
31600
|
}
|
|
30896
31601
|
/**
|
|
30897
31602
|
* Build the companion `_has` flag expression for a nullable-struct or
|
|
@@ -30909,8 +31614,8 @@ function is_nullable_struct_side(node, status) {
|
|
|
30909
31614
|
function build_nullable_has(node, status) {
|
|
30910
31615
|
if (node.node_type === "value") {
|
|
30911
31616
|
const name = node.value;
|
|
30912
|
-
if (status.function_ref_params?.has(name) && (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type))) return has_flag_name(name);
|
|
30913
|
-
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) return has_flag_name(name);
|
|
31617
|
+
if (status.function_ref_params?.has(name) && (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status))) return has_flag_name(name);
|
|
31618
|
+
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) return has_flag_name(name);
|
|
30914
31619
|
}
|
|
30915
31620
|
const saved = begin_code_scratch(status);
|
|
30916
31621
|
build_node$1(node, status);
|
|
@@ -32575,7 +33280,7 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
32575
33280
|
if (rhs_is_null) status.code += `${flag} = 0`;
|
|
32576
33281
|
else {
|
|
32577
33282
|
const call_ret = call_result_type(node.right_value);
|
|
32578
|
-
if (!!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret))) {
|
|
33283
|
+
if (!!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret, status))) {
|
|
32579
33284
|
const old = status.current_nullable_call_flag;
|
|
32580
33285
|
status.current_nullable_call_flag = flag;
|
|
32581
33286
|
status.code += `${lhs_expr} = `;
|
|
@@ -32665,7 +33370,7 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
32665
33370
|
status.code += `;\n`;
|
|
32666
33371
|
return;
|
|
32667
33372
|
}
|
|
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) {
|
|
33373
|
+
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
33374
|
const access_lhs = node.left_value;
|
|
32670
33375
|
const field_access_node = access_lhs.access;
|
|
32671
33376
|
let target_type = type_from_value_node$1(access_lhs.target);
|
|
@@ -32953,7 +33658,10 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
32953
33658
|
const needs_destroy = struct_type ? struct_needs_destroy_by_name(struct_type.name, status) : false;
|
|
32954
33659
|
const rhs_is_free_factory = node.right_value.node_type === "func_call" && !rhs_references_var(node, lhs_name);
|
|
32955
33660
|
if (is_fresh_constructor(node, status) || rhs_is_free_factory) {
|
|
32956
|
-
if (struct_type)
|
|
33661
|
+
if (struct_type) {
|
|
33662
|
+
release_recorded_string_fields(status, struct_type, lhs_name);
|
|
33663
|
+
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) ?? []));
|
|
33664
|
+
}
|
|
32957
33665
|
if (needs_destroy) emit_struct_destroys(status, struct_type, lhs_name);
|
|
32958
33666
|
} else if (is_self_method_call(node, lhs_name)) {} else if (!rhs_references_var(node, lhs_name)) {
|
|
32959
33667
|
if (needs_destroy) emit_struct_destroys(status, struct_type, lhs_name);
|
|
@@ -32998,6 +33706,12 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
32998
33706
|
}
|
|
32999
33707
|
}
|
|
33000
33708
|
}
|
|
33709
|
+
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))) {
|
|
33710
|
+
const saved_seed_len = begin_code_scratch(status);
|
|
33711
|
+
build_node$1(node.left_value, status);
|
|
33712
|
+
const seed_access = end_code_scratch(status, saved_seed_len);
|
|
33713
|
+
status.code += `free(${seed_access}.ptr);\n`;
|
|
33714
|
+
}
|
|
33001
33715
|
build_node$1(node.left_value, status);
|
|
33002
33716
|
if (node.operator) status.code += ` ${node.operator.slice(0, -1)}= `;
|
|
33003
33717
|
else status.code += " = ";
|
|
@@ -33006,7 +33720,15 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
33006
33720
|
else emit_rhs_value$1(node.right_value, nir_rhs, status);
|
|
33007
33721
|
if (node.left_value.node_type === "value" && has_field_overrides(node.right_value)) {
|
|
33008
33722
|
const lname = node.left_value.value;
|
|
33009
|
-
|
|
33723
|
+
const rhs_call = node.right_value.node_type === "func_call" ? node.right_value : void 0;
|
|
33724
|
+
if (rhs_call?.field_overrides?.length && rhs_call.name === rhs_call.type?.name) {
|
|
33725
|
+
const override_struct = status.structs.find((s) => s.name === rhs_call.name && !s.is_simple_type && !s.is_class);
|
|
33726
|
+
if (override_struct) {
|
|
33727
|
+
status.code += `;\n`;
|
|
33728
|
+
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`;
|
|
33729
|
+
}
|
|
33730
|
+
}
|
|
33731
|
+
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
33732
|
}
|
|
33011
33733
|
if (node.swap) {
|
|
33012
33734
|
status.code += `;\n`;
|
|
@@ -33028,11 +33750,11 @@ function lhs_nullable_struct_type(node, status) {
|
|
|
33028
33750
|
if (node.left_value.node_type === "value") {
|
|
33029
33751
|
const name = node.left_value.value;
|
|
33030
33752
|
const t = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
|
|
33031
|
-
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
|
|
33753
|
+
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
|
|
33032
33754
|
}
|
|
33033
33755
|
if (node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field") {
|
|
33034
33756
|
const field_type = node.left_value.access.type;
|
|
33035
|
-
return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type);
|
|
33757
|
+
return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type, status);
|
|
33036
33758
|
}
|
|
33037
33759
|
return false;
|
|
33038
33760
|
}
|
|
@@ -33045,7 +33767,7 @@ function lhs_nullable_struct_type(node, status) {
|
|
|
33045
33767
|
function nullable_value_flag_expr(value, status) {
|
|
33046
33768
|
const t = type_from_value_node$1(value);
|
|
33047
33769
|
if (!t?.is_nullable) return void 0;
|
|
33048
|
-
if (!is_nullable_struct_type(t, status) && !is_nullable_scalar_type(t)) return void 0;
|
|
33770
|
+
if (!is_nullable_struct_type(t, status) && !is_nullable_scalar_type(t, status)) return void 0;
|
|
33049
33771
|
if (value.node_type === "value") return has_flag_name(value.value);
|
|
33050
33772
|
if (value.node_type === "access" && value.access.node_type === "access_field") {
|
|
33051
33773
|
const saved = begin_code_scratch(status);
|
|
@@ -33776,32 +34498,37 @@ function build_declaration_node(node, status, nir_init, nir_swap) {
|
|
|
33776
34498
|
status.stack_array_lengths.set(safe_name, len_text);
|
|
33777
34499
|
}
|
|
33778
34500
|
}
|
|
33779
|
-
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) {
|
|
34501
|
+
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) {
|
|
33780
34502
|
const flag = has_flag_name(safe_name);
|
|
33781
34503
|
const is_null = !node.value || node.value.node_type === "value" && node.value.value === "null";
|
|
33782
|
-
if (!status.current_function && is_nullable_scalar_type(node.type)) {
|
|
33783
|
-
|
|
33784
|
-
|
|
33785
|
-
|
|
33786
|
-
status.code += `;\nunsigned char ${flag} = 0`;
|
|
33787
|
-
if (node.value && !is_null) {
|
|
33788
|
-
const call_ret = call_result_type(node.value);
|
|
33789
|
-
const value_is_nullable_call = !!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret));
|
|
33790
|
-
status.code += `;\n${safe_name} = `;
|
|
33791
|
-
if (value_is_nullable_call) {
|
|
33792
|
-
const old = status.current_nullable_call_flag;
|
|
33793
|
-
status.current_nullable_call_flag = flag;
|
|
33794
|
-
emit_init_value(node.value, nir_init, status);
|
|
33795
|
-
status.current_nullable_call_flag = old;
|
|
33796
|
-
record_call_init_string_fields(node, status);
|
|
33797
|
-
} else {
|
|
33798
|
-
const rhs_flag = nullable_value_flag_expr(node.value, status);
|
|
33799
|
-
emit_init_value(node.value, nir_init, status);
|
|
33800
|
-
status.code += `;\n${flag} = ${rhs_flag ?? "1"}`;
|
|
33801
|
-
}
|
|
34504
|
+
if (!status.current_function && is_nullable_scalar_type(node.type, status)) {
|
|
34505
|
+
if (is_null) {
|
|
34506
|
+
status.code += `;\nunsigned char ${flag} = 0`;
|
|
34507
|
+
return;
|
|
33802
34508
|
}
|
|
34509
|
+
status.code += ` = `;
|
|
34510
|
+
emit_init_value(node.value, nir_init, status);
|
|
34511
|
+
status.code += `;\nunsigned char ${flag} = 1`;
|
|
33803
34512
|
return;
|
|
33804
34513
|
}
|
|
34514
|
+
status.code += `;\nunsigned char ${flag} = 0`;
|
|
34515
|
+
if (node.value && !is_null) {
|
|
34516
|
+
const call_ret = call_result_type(node.value);
|
|
34517
|
+
const value_is_nullable_call = !!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret, status));
|
|
34518
|
+
status.code += `;\n${safe_name} = `;
|
|
34519
|
+
if (value_is_nullable_call) {
|
|
34520
|
+
const old = status.current_nullable_call_flag;
|
|
34521
|
+
status.current_nullable_call_flag = flag;
|
|
34522
|
+
emit_init_value(node.value, nir_init, status);
|
|
34523
|
+
status.current_nullable_call_flag = old;
|
|
34524
|
+
record_call_init_string_fields(node, status);
|
|
34525
|
+
} else {
|
|
34526
|
+
const rhs_flag = nullable_value_flag_expr(node.value, status);
|
|
34527
|
+
emit_init_value(node.value, nir_init, status);
|
|
34528
|
+
status.code += `;\n${flag} = ${rhs_flag ?? "1"}`;
|
|
34529
|
+
}
|
|
34530
|
+
}
|
|
34531
|
+
return;
|
|
33805
34532
|
}
|
|
33806
34533
|
if (node.value) {
|
|
33807
34534
|
const is_heap_literal_init = node.is_heap_array_literal || node.type.storage_kind === "heap_array" && (node.value.node_type === "array" || node.value.node_type === "range");
|
|
@@ -33913,8 +34640,20 @@ function build_declaration_node(node, status, nir_init, nir_swap) {
|
|
|
33913
34640
|
status.code += `${field_access} = `;
|
|
33914
34641
|
emit_swap_value$1(node.swap, nir_swap, status);
|
|
33915
34642
|
}
|
|
33916
|
-
if (has_field_overrides(node.value)) emit_field_overrides(safe_name, node.value, build_node$1, status, ";\n", ";\n");
|
|
33917
34643
|
record_call_init_string_fields(node, status);
|
|
34644
|
+
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;
|
|
34645
|
+
if (override_ctor_call) {
|
|
34646
|
+
const override_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
|
|
34647
|
+
if (override_struct) {
|
|
34648
|
+
status.code += `;\n`;
|
|
34649
|
+
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`;
|
|
34650
|
+
}
|
|
34651
|
+
}
|
|
34652
|
+
if (node.value?.node_type === "func_call" && node.value.name === node.type?.name && !is_normalized_struct_call(node.value, status)) {
|
|
34653
|
+
const ctor_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
|
|
34654
|
+
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) ?? []));
|
|
34655
|
+
}
|
|
34656
|
+
if (has_field_overrides(node.value)) emit_field_overrides(safe_name, node.value, build_node$1, status, override_ctor_call ? "" : ";\n", ";\n");
|
|
33918
34657
|
}
|
|
33919
34658
|
}
|
|
33920
34659
|
}
|
|
@@ -34207,21 +34946,32 @@ function build_if_else_node(node, status, nir) {
|
|
|
34207
34946
|
status.scoped_declarations = enter_c_scope(status);
|
|
34208
34947
|
const old_deferred_frees = status.deferred_frees;
|
|
34209
34948
|
status.deferred_frees = [];
|
|
34949
|
+
const pre_heap_string_fields = status.heap_string_fields;
|
|
34210
34950
|
emit_allocations(node.condition, status);
|
|
34211
34951
|
status.code += `if (`;
|
|
34212
34952
|
build_condition(node.condition, status);
|
|
34213
34953
|
status.code += `) {\n`;
|
|
34954
|
+
let then_fields;
|
|
34214
34955
|
if (node.if_branch) {
|
|
34215
34956
|
build_block_with_cursor(node.if_branch, nir?.then_branch, status);
|
|
34216
34957
|
build_auto_free(status);
|
|
34217
34958
|
}
|
|
34959
|
+
then_fields = status.heap_string_fields;
|
|
34218
34960
|
leave_c_scope(status);
|
|
34219
34961
|
if (node.else_branch) {
|
|
34220
34962
|
status.scoped_declarations = enter_c_scope(status);
|
|
34221
34963
|
status.deferred_frees = [];
|
|
34964
|
+
status.heap_string_fields = pre_heap_string_fields;
|
|
34222
34965
|
status.code += `} else {\n`;
|
|
34223
34966
|
build_block_with_cursor(node.else_branch, nir?.else_branch, status);
|
|
34224
34967
|
build_auto_free(status);
|
|
34968
|
+
const else_fields = status.heap_string_fields;
|
|
34969
|
+
if (else_fields !== then_fields) {
|
|
34970
|
+
const merged = new Set(pre_heap_string_fields ?? []);
|
|
34971
|
+
for (const key of then_fields ?? []) merged.add(key);
|
|
34972
|
+
for (const key of else_fields ?? []) merged.add(key);
|
|
34973
|
+
status.heap_string_fields = merged;
|
|
34974
|
+
}
|
|
34225
34975
|
leave_c_scope(status);
|
|
34226
34976
|
}
|
|
34227
34977
|
status.code += `}\n`;
|
|
@@ -34395,7 +35145,7 @@ function build_return_node(node, status, nir_value) {
|
|
|
34395
35145
|
emit_nursery_joins_on_return_c(status);
|
|
34396
35146
|
reclaim_all_c_scopes(status);
|
|
34397
35147
|
status.code += `*${ret_has} = 0;\n`;
|
|
34398
|
-
if (status.function_return_type && is_nullable_scalar_type(status.function_return_type)) status.code += `return (${c_type(status.function_return_type.name)})0;\n`;
|
|
35148
|
+
if (status.function_return_type && is_nullable_scalar_type(status.function_return_type, status)) status.code += `return (${c_type(status.function_return_type.name)})0;\n`;
|
|
34399
35149
|
else status.code += `return (struct ${status.function_return_type.name}){0};\n`;
|
|
34400
35150
|
return;
|
|
34401
35151
|
}
|
|
@@ -34558,14 +35308,31 @@ function build_return_node(node, status, nir_value) {
|
|
|
34558
35308
|
if (!returns_string_zero && !returns_struct_zero && !returns_view_value && (returns_borrowed_string || returns_string_literal || returns_borrow_var)) status.code += `)`;
|
|
34559
35309
|
status.code += `;\n`;
|
|
34560
35310
|
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`;
|
|
35311
|
+
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;
|
|
35312
|
+
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)) {
|
|
35313
|
+
const overrides = node.value.node_type === "func_call" ? node.value.field_overrides : node.value.fields;
|
|
35314
|
+
for (const field of direct_string_fields(return_struct)) {
|
|
35315
|
+
if (!overrides?.some((o) => o.name === field.name)) continue;
|
|
35316
|
+
status.code += `free(_return_val.${field.name}.ptr);\n`;
|
|
35317
|
+
}
|
|
35318
|
+
}
|
|
35319
|
+
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
35320
|
if (has_field_overrides(node.value)) emit_field_overrides("_return_val", node.value, build_node$1, status, "", ";\n");
|
|
34562
35321
|
const bare_local_return = node.value.node_type === "value" && node.value.value !== "0" && node.value.value !== "null" && !!returned_value_decl;
|
|
34563
35322
|
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
35323
|
if (returned_recorded_string_fields?.has(field.name)) continue;
|
|
34565
35324
|
status.code += `_return_val.${field.name} = nomen_str_dup(_return_val.${field.name});\n`;
|
|
34566
35325
|
}
|
|
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
|
|
35326
|
+
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("_")) {
|
|
35327
|
+
const call = node.value;
|
|
35328
|
+
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);
|
|
35329
|
+
for (const field of direct_string_fields(return_struct)) {
|
|
35330
|
+
if (skip.has(field.name)) continue;
|
|
35331
|
+
status.code += `_return_val.${field.name} = nomen_str_dup(_return_val.${field.name});\n`;
|
|
35332
|
+
}
|
|
35333
|
+
}
|
|
35334
|
+
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)) {
|
|
35335
|
+
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
35336
|
for (const field of direct_string_fields(return_struct)) {
|
|
34570
35337
|
if (skip.has(field.name)) continue;
|
|
34571
35338
|
status.code += `_return_val.${field.name} = nomen_str_dup(_return_val.${field.name});\n`;
|
|
@@ -34611,7 +35378,7 @@ function nullable_flag_expr_of_value(value, status) {
|
|
|
34611
35378
|
if (!value) return void 0;
|
|
34612
35379
|
const t = type_from_value_node$1(value);
|
|
34613
35380
|
if (!t?.is_nullable) return void 0;
|
|
34614
|
-
if (!(is_nullable_struct_type(t, status) || is_nullable_scalar_type(t))) return void 0;
|
|
35381
|
+
if (!(is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status))) return void 0;
|
|
34615
35382
|
if (value.node_type === "value") return has_flag_name(value.value);
|
|
34616
35383
|
if (value.node_type === "access") {
|
|
34617
35384
|
const saved = begin_code_scratch(status);
|
|
@@ -34623,9 +35390,12 @@ function nullable_flag_expr_of_value(value, status) {
|
|
|
34623
35390
|
//#region ../src/build_c/build_switch_node.ts
|
|
34624
35391
|
function build_switch_node(node, status, nir) {
|
|
34625
35392
|
const old_scoped_declarations = status.scoped_declarations;
|
|
35393
|
+
const pre_heap_string_fields = status.heap_string_fields;
|
|
35394
|
+
const case_field_sets = [];
|
|
34626
35395
|
for (let i = 0; i < node.cases.length; i++) {
|
|
34627
35396
|
const c = node.cases[i];
|
|
34628
35397
|
status.scoped_declarations = enter_c_scope(status);
|
|
35398
|
+
status.heap_string_fields = pre_heap_string_fields;
|
|
34629
35399
|
const saved_code = begin_code_scratch(status);
|
|
34630
35400
|
build_node$1(c.condition, status);
|
|
34631
35401
|
let cond_code = end_code_scratch(status, saved_code);
|
|
@@ -34665,17 +35435,28 @@ function build_switch_node(node, status, nir) {
|
|
|
34665
35435
|
status.code += `if (${cond_code}) {\n`;
|
|
34666
35436
|
build_block_with_cursor(c.branch, nir?.arms[i]?.branch, status);
|
|
34667
35437
|
build_auto_free(status);
|
|
35438
|
+
case_field_sets.push(status.heap_string_fields);
|
|
34668
35439
|
leave_c_scope(status);
|
|
34669
35440
|
}
|
|
34670
35441
|
if (node.else_branch) {
|
|
34671
35442
|
status.code += "} else ";
|
|
34672
35443
|
status.scoped_declarations = enter_c_scope(status);
|
|
35444
|
+
status.heap_string_fields = pre_heap_string_fields;
|
|
34673
35445
|
status.code += `{\n`;
|
|
34674
35446
|
build_block_with_cursor(node.else_branch, nir?.otherwise ?? void 0, status);
|
|
34675
35447
|
build_auto_free(status);
|
|
35448
|
+
case_field_sets.push(status.heap_string_fields);
|
|
34676
35449
|
status.code += `}\n`;
|
|
34677
35450
|
leave_c_scope(status);
|
|
34678
35451
|
} else if (node.cases.length > 0) status.code += "}\n";
|
|
35452
|
+
if (case_field_sets.length > 1) {
|
|
35453
|
+
const first = case_field_sets[0];
|
|
35454
|
+
if (!case_field_sets.every((s) => s === first)) {
|
|
35455
|
+
const merged = new Set(pre_heap_string_fields ?? []);
|
|
35456
|
+
for (const set of case_field_sets) for (const key of set ?? []) merged.add(key);
|
|
35457
|
+
status.heap_string_fields = merged;
|
|
35458
|
+
}
|
|
35459
|
+
}
|
|
34679
35460
|
status.scoped_declarations = old_scoped_declarations;
|
|
34680
35461
|
}
|
|
34681
35462
|
//#endregion
|
|
@@ -35039,20 +35820,78 @@ function has_owning_fields(node, status) {
|
|
|
35039
35820
|
}
|
|
35040
35821
|
/**
|
|
35041
35822
|
* The names of Buffer methods that get specialized for owning value structs.
|
|
35042
|
-
* load_T
|
|
35043
|
-
*
|
|
35044
|
-
*
|
|
35045
|
-
*
|
|
35823
|
+
* load_T IS specialized when the element has owning container fields (a
|
|
35824
|
+
* `List<T>` member): the returned copy must own independent containers, or
|
|
35825
|
+
* the caller's scope-exit destroy would free the slot's buffer (double-free
|
|
35826
|
+
* at container teardown). String fields stay BORROWED on load (struct-local
|
|
35827
|
+
* teardown only frees RECORDED heap strings, so strdup'ing them would leak);
|
|
35828
|
+
* container fields have no such borrow convention — a struct local's
|
|
35829
|
+
* container fields are ALWAYS destroyed — so they must be deep-copied.
|
|
35830
|
+
* move_T is NOT specialized: it transfers ownership (the slot is zeroed, the
|
|
35831
|
+
* caller takes the pointers).
|
|
35046
35832
|
*/
|
|
35047
35833
|
const OWNING_BUFFER_METHODS = /* @__PURE__ */ new Set([
|
|
35048
35834
|
"store",
|
|
35049
35835
|
"replace",
|
|
35836
|
+
"load",
|
|
35050
35837
|
"modify",
|
|
35051
35838
|
"shift",
|
|
35052
35839
|
"destroy",
|
|
35053
35840
|
"#destroy"
|
|
35054
35841
|
]);
|
|
35055
35842
|
/**
|
|
35843
|
+
* Emit the transfer half of owning store_T/replace_T: the slot bytes were
|
|
35844
|
+
* already memcpy'd from the source, so zeroing each owned CONTAINER field
|
|
35845
|
+
* of the SOURCE completes the move — the slot is the sole owner, and the
|
|
35846
|
+
* source's own teardown (when it still runs: an unstamped generic-body
|
|
35847
|
+
* local) finds zeroed fields and reclaims nothing. Nested value struct
|
|
35848
|
+
* fields recurse so their container leaves are zeroed at their own paths;
|
|
35849
|
+
* their string fields are untouched (the slot aliases them — the pre-existing
|
|
35850
|
+
* nested-string convention). Class/enum payload fields are left alone
|
|
35851
|
+
* (recorded in FOLLOWUP.md).
|
|
35852
|
+
*/
|
|
35853
|
+
function emit_transfer_owning_fields(elem, src, status, prefix = "") {
|
|
35854
|
+
for (const field of elem.fields) {
|
|
35855
|
+
if (field.type.is_ref || field.type.is_view) continue;
|
|
35856
|
+
if (field.type.is_array || field.type.is_nullable) continue;
|
|
35857
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
35858
|
+
if (!field_struct || field_struct.is_class) continue;
|
|
35859
|
+
const src_field = `${prefix}${src}${arrow(src)}${field.name}`;
|
|
35860
|
+
if (has_destroy(field_struct)) status.code += `memset((void*)&${src_field}, 0, sizeof(${src_field}));\n`;
|
|
35861
|
+
else if (struct_needs_destroy(field_struct, status)) emit_transfer_owning_fields(field_struct, src_field, status, "");
|
|
35862
|
+
}
|
|
35863
|
+
}
|
|
35864
|
+
/**
|
|
35865
|
+
* Emit the deep-copy half of owning load_T: for each field whose type has a
|
|
35866
|
+
* monomorphized `copy` helper that yields an independent value (a `List<T>`
|
|
35867
|
+
* field — `<List_T>_copy` deep-copies slots per the owning store contract —
|
|
35868
|
+
* or an enum-with-data field whose string payloads `<E>_copy` duplicates),
|
|
35869
|
+
* overwrite the shallow-copied bytes with an owned copy. Fields without a
|
|
35870
|
+
* copyable shape (nested owning user structs, class fields) stay shallow —
|
|
35871
|
+
* recorded in FOLLOWUP.md ("remaining shallow container-field shapes").
|
|
35872
|
+
*/
|
|
35873
|
+
function emit_load_deep_copy_fields(elem, dst, status) {
|
|
35874
|
+
for (const field of elem.fields) {
|
|
35875
|
+
if (field.type.is_ref || field.type.is_view || field.type.is_array) continue;
|
|
35876
|
+
if (field.type.is_nullable) continue;
|
|
35877
|
+
if (field.type.name === "string") continue;
|
|
35878
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
35879
|
+
if (field_struct && !field_struct.is_class && field_struct.name.startsWith("List_") && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
35880
|
+
status.code += `${dst}.${field.name} = ${field_struct.name}_copy(&${dst}.${field.name});\n`;
|
|
35881
|
+
continue;
|
|
35882
|
+
}
|
|
35883
|
+
const field_enum = status.enums.find((e) => e.name === field.type.name && e.has_associated_data);
|
|
35884
|
+
if (field_enum && enum_copy_is_deep(field_enum, status)) status.code += `${dst}.${field.name} = ${field_enum.name}_copy(${dst}.${field.name});\n`;
|
|
35885
|
+
}
|
|
35886
|
+
}
|
|
35887
|
+
/** Whether `<E>_copy` yields a fully independent value: no case payload is a
|
|
35888
|
+
* class/trait reference (those are shared pointers the copy cannot
|
|
35889
|
+
* duplicate — mirroring owning_buffer_enum_element's gate). */
|
|
35890
|
+
function enum_copy_is_deep(elem, status) {
|
|
35891
|
+
for (const c of elem.cases) for (const p of c.params) if (status.structs.find((s) => s.name === p.type.name)?.is_class || status.traits.find((t) => t.name === p.type.name)) return false;
|
|
35892
|
+
return true;
|
|
35893
|
+
}
|
|
35894
|
+
/**
|
|
35056
35895
|
* A `Buffer<string>` owns an independent heap copy of each slot (strdup on
|
|
35057
35896
|
* store_T, free+strdup on replace_T, per-slot free on #destroy). The element
|
|
35058
35897
|
* is a primitive, so this is a separate path from the value-struct
|
|
@@ -35215,6 +36054,13 @@ function emit_owning_buffer_body(func_name, elem, status) {
|
|
|
35215
36054
|
status.code += `struct ${elem.name} _old = _slots[i];\n`;
|
|
35216
36055
|
status.code += `_slots[i] = (*val);\n`;
|
|
35217
36056
|
emit_deep_copy_fields(elem, "_slots[i]", "val", status, "_old");
|
|
36057
|
+
emit_transfer_owning_fields(elem, "val", status);
|
|
36058
|
+
return true;
|
|
36059
|
+
}
|
|
36060
|
+
if (func_name === "load") {
|
|
36061
|
+
status.code += `struct ${elem.name} _loaded = ((struct ${elem.name} *)(unsigned long long)self->data)[i];\n`;
|
|
36062
|
+
emit_load_deep_copy_fields(elem, "_loaded", status);
|
|
36063
|
+
status.code += `return _loaded;\n`;
|
|
35218
36064
|
return true;
|
|
35219
36065
|
}
|
|
35220
36066
|
if (func_name === "modify") {
|
|
@@ -35229,6 +36075,7 @@ function emit_owning_buffer_body(func_name, elem, status) {
|
|
|
35229
36075
|
status.code += `${elem.name}_destroy(&_slots[i]);\n`;
|
|
35230
36076
|
status.code += `_slots[i] = (*val);\n`;
|
|
35231
36077
|
emit_deep_copy_fields(elem, "_slots[i]", "val", status);
|
|
36078
|
+
emit_transfer_owning_fields(elem, "val", status);
|
|
35232
36079
|
return true;
|
|
35233
36080
|
}
|
|
35234
36081
|
if (func_name === "shift") {
|
|
@@ -35255,12 +36102,13 @@ function emit_owning_buffer_body(func_name, elem, status) {
|
|
|
35255
36102
|
return false;
|
|
35256
36103
|
}
|
|
35257
36104
|
/**
|
|
35258
|
-
* For each string field on the element struct, overwrite the
|
|
35259
|
-
* pointer in `dst` with a fresh strdup of `src`'s field. This
|
|
35260
|
-
* pointer aliasing between the source and the slot so each can be
|
|
35261
|
-
* independently.
|
|
35262
|
-
*
|
|
35263
|
-
*
|
|
36105
|
+
* For each DIRECT string field on the element struct, overwrite the
|
|
36106
|
+
* shallow-copied pointer in `dst` with a fresh strdup of `src`'s field. This
|
|
36107
|
+
* breaks the pointer aliasing between the source and the slot so each can be
|
|
36108
|
+
* destroyed independently. Nested value struct fields are NOT walked: their
|
|
36109
|
+
* string fields stay aliased (the pre-existing convention — a spliced
|
|
36110
|
+
* `move` donor has no teardown, so the slot is the sole owner), and their
|
|
36111
|
+
* container leaves are transferred by `emit_transfer_owning_fields`.
|
|
35264
36112
|
*
|
|
35265
36113
|
* `old_expr` (optional) is the slot's PRE-copy value expression. When the
|
|
35266
36114
|
* source field pointer equals the old slot field pointer, the slot already
|
|
@@ -35278,12 +36126,6 @@ function emit_deep_copy_fields(elem, dst, src, status, old_expr) {
|
|
|
35278
36126
|
const src_field = `${src}${arrow(src)}${field.name}`;
|
|
35279
36127
|
if (old_expr) status.code += `${dst}.${field.name} = (${src_field}.ptr && ${src_field}.ptr != ${old_expr}.${field.name}.ptr) ? nomen_str_dup(${src_field}) : ${src_field};\n`;
|
|
35280
36128
|
else status.code += `${dst}.${field.name} = ${src_field}.ptr ? nomen_str_dup(${src_field}) : ${src_field};\n`;
|
|
35281
|
-
} else if (field.type.name && !field.type.is_array) {
|
|
35282
|
-
const field_struct = status.structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_generic);
|
|
35283
|
-
if (field_struct && !field_struct.is_class && struct_needs_destroy(field_struct, status)) {
|
|
35284
|
-
status.code += `${field_struct.name}_destroy(&${dst}.${field.name});\n`;
|
|
35285
|
-
emit_deep_copy_fields(field_struct, `${dst}.${field.name}`, `${src}${arrow(src)}${field.name}`, status);
|
|
35286
|
-
}
|
|
35287
36129
|
}
|
|
35288
36130
|
}
|
|
35289
36131
|
}
|
|
@@ -35431,7 +36273,7 @@ function build_struct_node(node, status) {
|
|
|
35431
36273
|
const ctor_return = is_class ? `struct ${node.name}*` : `struct ${node.name}`;
|
|
35432
36274
|
const field_params = node.fields.filter((f) => f.value == null).map((f) => {
|
|
35433
36275
|
let decl = c_param_decl(f.type, f.name, status);
|
|
35434
|
-
if (is_nullable_struct_type(f.type, status) || is_nullable_scalar_type(f.type)) decl += `, unsigned char ${has_flag_name(f.name)}`;
|
|
36276
|
+
if (is_nullable_struct_type(f.type, status) || is_nullable_scalar_type(f.type, status)) decl += `, unsigned char ${has_flag_name(f.name)}`;
|
|
35435
36277
|
return decl;
|
|
35436
36278
|
}).join(", ");
|
|
35437
36279
|
const ctor_sig = (init) => {
|
|
@@ -35442,7 +36284,7 @@ function build_struct_node(node, status) {
|
|
|
35442
36284
|
is_ref: p.is_ref || p.type.is_ref,
|
|
35443
36285
|
declaration: p.declaration
|
|
35444
36286
|
});
|
|
35445
|
-
if (!p.is_variadic && (is_nullable_struct_type(p.type, status) || is_nullable_scalar_type(p.type))) decl += `, unsigned char ${has_flag_name(p.name)}`;
|
|
36287
|
+
if (!p.is_variadic && (is_nullable_struct_type(p.type, status) || is_nullable_scalar_type(p.type, status))) decl += `, unsigned char ${has_flag_name(p.name)}`;
|
|
35446
36288
|
return decl;
|
|
35447
36289
|
}).join(", ");
|
|
35448
36290
|
const label = is_overloaded(node, "#init") ? mangled_label(init, node.name) : `${node.name}_init`;
|
|
@@ -35458,6 +36300,10 @@ function build_struct_node(node, status) {
|
|
|
35458
36300
|
if (node.traits.length) status.code += `self${accessor}_vt = &_${node.name}_traits;\n`;
|
|
35459
36301
|
const old_init_assigned_fields = status.init_assigned_fields;
|
|
35460
36302
|
status.init_assigned_fields = /* @__PURE__ */ new Set();
|
|
36303
|
+
const old_init_default_reclaimed = status.init_default_reclaimed_fields;
|
|
36304
|
+
status.init_default_reclaimed_fields = /* @__PURE__ */ new Set();
|
|
36305
|
+
const old_init_heap_string_fields = status.heap_string_fields;
|
|
36306
|
+
status.heap_string_fields = void 0;
|
|
35461
36307
|
for (const field of node.fields) if (field.value) {
|
|
35462
36308
|
status.init_assigned_fields?.add(`self.${field.name}`);
|
|
35463
36309
|
if (field.type.is_view && !field.type.is_array && !is_nullable_struct_type(field.type, status)) {
|
|
@@ -35466,7 +36312,7 @@ function build_struct_node(node, status) {
|
|
|
35466
36312
|
build_node$1(field.value, status);
|
|
35467
36313
|
status.code += `; self${accessor}${field.name} = (nomen_view){ _p.ptr, _p.len }; }\n`;
|
|
35468
36314
|
} else status.code += `self${accessor}${field.name} = (nomen_view){0};\n`;
|
|
35469
|
-
} else if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) {
|
|
36315
|
+
} else if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) {
|
|
35470
36316
|
if (field.value.node_type === "value" && field.value.value === "null") status.code += `self${accessor}${has_flag_name(field.name)} = 0;\n`;
|
|
35471
36317
|
else {
|
|
35472
36318
|
status.code += `self${accessor}${field.name} = `;
|
|
@@ -35525,6 +36371,8 @@ function build_struct_node(node, status) {
|
|
|
35525
36371
|
status.current_struct = old_current_struct;
|
|
35526
36372
|
status.current_function = old_current_function;
|
|
35527
36373
|
status.init_assigned_fields = old_init_assigned_fields;
|
|
36374
|
+
status.init_default_reclaimed_fields = old_init_default_reclaimed;
|
|
36375
|
+
status.heap_string_fields = old_init_heap_string_fields;
|
|
35528
36376
|
status.function_return_type = old_return_type;
|
|
35529
36377
|
}
|
|
35530
36378
|
build_struct_functions(node, status, true);
|
|
@@ -35542,7 +36390,7 @@ function build_struct_node(node, status) {
|
|
|
35542
36390
|
status.code += `; ${object_name}${accessor}${field.name} = (nomen_view){ _p.ptr, _p.len }; }\n`;
|
|
35543
36391
|
} else status.code += `${object_name}${accessor}${field.name} = (nomen_view){0};\n`;
|
|
35544
36392
|
} else if (field.type.storage_kind === "stack_array" && field.type.length) status.code += `memcpy(${object_name}${accessor}${field.name}, ${field.name}, sizeof(${object_name}${accessor}${field.name}));\n`;
|
|
35545
|
-
else if ((is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) && field.value) {
|
|
36393
|
+
else if ((is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) && field.value) {
|
|
35546
36394
|
if (field.value.node_type === "value" && field.value.value === "null") status.code += `${object_name}${accessor}${has_flag_name(field.name)} = 0;\n`;
|
|
35547
36395
|
else {
|
|
35548
36396
|
status.code += `${object_name}${accessor}${field.name} = `;
|
|
@@ -35552,7 +36400,7 @@ function build_struct_node(node, status) {
|
|
|
35552
36400
|
} else if (is_nullable_struct_type(field.type, status)) {
|
|
35553
36401
|
status.code += `${object_name}${accessor}${field.name} = *${field.name};\n`;
|
|
35554
36402
|
status.code += `${object_name}${accessor}${has_flag_name(field.name)} = ${has_flag_name(field.name)};\n`;
|
|
35555
|
-
} else if (is_nullable_scalar_type(field.type)) {
|
|
36403
|
+
} else if (is_nullable_scalar_type(field.type, status)) {
|
|
35556
36404
|
status.code += `${object_name}${accessor}${field.name} = ${field.name};\n`;
|
|
35557
36405
|
status.code += `${object_name}${accessor}${has_flag_name(field.name)} = ${has_flag_name(field.name)};\n`;
|
|
35558
36406
|
} else if (!field.type.is_ref && !field.type.is_view && status.enums.find((e) => e.name === field.type.name && e.has_associated_data)) {
|
|
@@ -35690,7 +36538,7 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
35690
36538
|
status.force_heap_strings = scan_force_heap_strings(func.statements ?? [], status.structs);
|
|
35691
36539
|
status.function_return_type = func.return_type;
|
|
35692
36540
|
const old_nullable_ret_has = status.nullable_ret_has_param;
|
|
35693
|
-
if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type)) status.nullable_ret_has_param = "_ret_has";
|
|
36541
|
+
if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type, status)) status.nullable_ret_has_param = "_ret_has";
|
|
35694
36542
|
else status.nullable_ret_has_param = void 0;
|
|
35695
36543
|
const self_param = func.params[0]?.is_self_param ? func.params[0] : null;
|
|
35696
36544
|
status.self_is_ref = !!self_param?.is_ref || self_param?.declaration === "var";
|
|
@@ -35747,9 +36595,9 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
35747
36595
|
for (let i = 0; i < func.params.length; i++) {
|
|
35748
36596
|
if (i > 0) status.code += ", ";
|
|
35749
36597
|
build_parameter_node(func.params[i], status);
|
|
35750
|
-
if (!func.params[i].is_self_param && (is_nullable_struct_type(func.params[i].type, status) || is_nullable_scalar_type(func.params[i].type))) status.code += `, unsigned char ${has_flag_name(c_function_name(func.params[i].name))}`;
|
|
36598
|
+
if (!func.params[i].is_self_param && (is_nullable_struct_type(func.params[i].type, status) || is_nullable_scalar_type(func.params[i].type, status))) status.code += `, unsigned char ${has_flag_name(c_function_name(func.params[i].name))}`;
|
|
35751
36599
|
}
|
|
35752
|
-
if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type)) {
|
|
36600
|
+
if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type, status)) {
|
|
35753
36601
|
if (func.params.length > 0) status.code += `, `;
|
|
35754
36602
|
status.code += `unsigned char *_ret_has`;
|
|
35755
36603
|
}
|
|
@@ -36441,6 +37289,8 @@ function build_function_node(node, status) {
|
|
|
36441
37289
|
status.heap_strings = /* @__PURE__ */ new Set();
|
|
36442
37290
|
const old_owned_string_vars = status.owned_string_vars;
|
|
36443
37291
|
status.owned_string_vars = /* @__PURE__ */ new Set();
|
|
37292
|
+
const old_heap_string_fields = status.heap_string_fields;
|
|
37293
|
+
status.heap_string_fields = void 0;
|
|
36444
37294
|
const old_heap_array_vars = status.heap_array_vars;
|
|
36445
37295
|
status.heap_array_vars = void 0;
|
|
36446
37296
|
const old_stack_array_lengths = status.stack_array_lengths;
|
|
@@ -36508,9 +37358,9 @@ function build_function_node(node, status) {
|
|
|
36508
37358
|
first_param = false;
|
|
36509
37359
|
if (node.params[i].is_variadic) status.code += `long _${node.params[i].name}_len, `;
|
|
36510
37360
|
build_parameter_node(node.params[i], status);
|
|
36511
|
-
if ((is_nullable_struct_type(node.params[i].type, status) || is_nullable_scalar_type(node.params[i].type)) && !node.params[i].is_self_param) status.code += `, unsigned char ${has_flag_name(c_function_name(node.params[i].name))}`;
|
|
37361
|
+
if ((is_nullable_struct_type(node.params[i].type, status) || is_nullable_scalar_type(node.params[i].type, status)) && !node.params[i].is_self_param) status.code += `, unsigned char ${has_flag_name(c_function_name(node.params[i].name))}`;
|
|
36512
37362
|
}
|
|
36513
|
-
if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type)) {
|
|
37363
|
+
if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type, status)) {
|
|
36514
37364
|
if (!first_param) status.code += ", ";
|
|
36515
37365
|
status.code += `unsigned char *_ret_has`;
|
|
36516
37366
|
}
|
|
@@ -36563,7 +37413,7 @@ function build_function_node(node, status) {
|
|
|
36563
37413
|
const old_variable_types = status.variable_types;
|
|
36564
37414
|
status.variable_types = /* @__PURE__ */ new Map();
|
|
36565
37415
|
const old_nullable_ret_has = status.nullable_ret_has_param;
|
|
36566
|
-
if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type)) status.nullable_ret_has_param = "_ret_has";
|
|
37416
|
+
if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type, status)) status.nullable_ret_has_param = "_ret_has";
|
|
36567
37417
|
else status.nullable_ret_has_param = void 0;
|
|
36568
37418
|
const old_function_name = status.current_function_name;
|
|
36569
37419
|
status.current_function_name = emission_label(node);
|
|
@@ -36658,6 +37508,7 @@ function build_function_node(node, status) {
|
|
|
36658
37508
|
status.aliased_class_sources = old_aliased_class_sources;
|
|
36659
37509
|
status.string_borrow_vars = old_string_borrow_vars;
|
|
36660
37510
|
status.moved_string_vars = old_moved_string_vars;
|
|
37511
|
+
status.heap_string_fields = old_heap_string_fields;
|
|
36661
37512
|
status.heap_strings = old_heap_strings;
|
|
36662
37513
|
status.owned_string_vars = old_owned_string_vars;
|
|
36663
37514
|
status.heap_array_vars = old_heap_array_vars;
|
|
@@ -36949,7 +37800,7 @@ function build_function_call_node(node, status) {
|
|
|
36949
37800
|
const lambda_ret_c = lambda_arg_temps.size > 0 ? c_return_type(node.type, status) : void 0;
|
|
36950
37801
|
const lambda_ret_tmp = lambda_ret_c && lambda_ret_c !== "void" ? next_lambda_ret_temp() : void 0;
|
|
36951
37802
|
if (lambda_ret_tmp) status.code += `${lambda_ret_c} ${lambda_ret_tmp} = `;
|
|
36952
|
-
const saved_call = (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) && !status.current_nullable_call_flag ? begin_code_scratch(status) : void 0;
|
|
37803
|
+
const saved_call = (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) && !status.current_nullable_call_flag ? begin_code_scratch(status) : void 0;
|
|
36953
37804
|
let wrap_tmp;
|
|
36954
37805
|
status.code += `${func_name}(${is_closure_callee ? "NULL" : ""}`;
|
|
36955
37806
|
if (is_closure_callee && node.params.length > 0) status.code += ", ";
|
|
@@ -36990,7 +37841,7 @@ function build_function_call_node(node, status) {
|
|
|
36990
37841
|
}
|
|
36991
37842
|
if (node.nullable_param_indices?.includes(i) && node.params[i].node_type === "value" && node.params[i].value === "null") {
|
|
36992
37843
|
const callee_null_param_type = callee_params?.[i]?.type ?? param_type;
|
|
36993
|
-
if (is_nullable_scalar_type(callee_null_param_type)) status.code += `0, 0`;
|
|
37844
|
+
if (is_nullable_scalar_type(callee_null_param_type, status)) status.code += `0, 0`;
|
|
36994
37845
|
else status.code += `(void *)&(struct ${mono_type_name(callee_null_param_type)}){0}, 0`;
|
|
36995
37846
|
continue;
|
|
36996
37847
|
}
|
|
@@ -37076,7 +37927,7 @@ function build_function_call_node(node, status) {
|
|
|
37076
37927
|
emit_nullable_arg_flag(node.params[i], status);
|
|
37077
37928
|
}
|
|
37078
37929
|
}
|
|
37079
|
-
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) {
|
|
37930
|
+
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) {
|
|
37080
37931
|
const flag_name = status.current_nullable_call_flag;
|
|
37081
37932
|
const arg_list_empty = node.params.length === 0 && !is_closure_callee;
|
|
37082
37933
|
if (flag_name) status.code += `${arg_list_empty ? "" : ", "}&${flag_name}`;
|
|
@@ -37090,6 +37941,12 @@ function build_function_call_node(node, status) {
|
|
|
37090
37941
|
const call_text = end_code_scratch(status, saved_call);
|
|
37091
37942
|
status.code += `({ unsigned char _nsd_${wrap_tmp} = 0; ${call_text}; })`;
|
|
37092
37943
|
}
|
|
37944
|
+
{
|
|
37945
|
+
const callee = node.resolved_function;
|
|
37946
|
+
if (callee && !callee.params?.some((p) => p.is_self_param)) transfer_ref_param_field_records(callee, node.params, status, (name) => {
|
|
37947
|
+
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);
|
|
37948
|
+
});
|
|
37949
|
+
}
|
|
37093
37950
|
if (node.name.startsWith("_string_interpolate_")) status.interpolate_string_counts.add(node.params.length - 1);
|
|
37094
37951
|
if (node.swap_params?.size) for (const [idx, swap_expr] of node.swap_params) {
|
|
37095
37952
|
const source = node.params[idx];
|
|
@@ -37179,13 +38036,13 @@ function emit_nullable_arg_flag(arg, status) {
|
|
|
37179
38036
|
}
|
|
37180
38037
|
if (arg.node_type === "value") {
|
|
37181
38038
|
const vn = arg;
|
|
37182
|
-
if (is_nullable_struct_type(vn.type, status) || is_nullable_scalar_type(vn.type)) status.code += `${has_flag_name(vn.value)}`;
|
|
38039
|
+
if (is_nullable_struct_type(vn.type, status) || is_nullable_scalar_type(vn.type, status)) status.code += `${has_flag_name(vn.value)}`;
|
|
37183
38040
|
else status.code += `1`;
|
|
37184
38041
|
return;
|
|
37185
38042
|
}
|
|
37186
38043
|
if (arg.node_type === "access" && arg.access.node_type === "access_field") {
|
|
37187
38044
|
const t = type_from_value_node$1(arg);
|
|
37188
|
-
if (is_nullable_struct_type(t, status) || is_nullable_scalar_type(t)) {
|
|
38045
|
+
if (is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status)) {
|
|
37189
38046
|
const saved = begin_code_scratch(status);
|
|
37190
38047
|
build_node$1(arg, status);
|
|
37191
38048
|
const expr = end_code_scratch(status, saved);
|
|
@@ -37497,6 +38354,10 @@ function build_access_node$1(node, status) {
|
|
|
37497
38354
|
return;
|
|
37498
38355
|
}
|
|
37499
38356
|
if (target_type.is_array && access_field.name === "length") {
|
|
38357
|
+
if (node.target.node_type === "value" && !!status.current_function?.params.some((p) => !p.is_self_param && p.name === node.target.value) && target_type.length) {
|
|
38358
|
+
build_node$1(target_type.length, status);
|
|
38359
|
+
return;
|
|
38360
|
+
}
|
|
37500
38361
|
const type = c_type(target_type.name);
|
|
37501
38362
|
status.code += "(sizeof(";
|
|
37502
38363
|
build_node$1(node.target, status);
|
|
@@ -37865,6 +38726,10 @@ function build_access_node$1(node, status) {
|
|
|
37865
38726
|
}
|
|
37866
38727
|
}
|
|
37867
38728
|
}
|
|
38729
|
+
{
|
|
38730
|
+
const ref_method = target_method ?? trait_default_func;
|
|
38731
|
+
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));
|
|
38732
|
+
}
|
|
37868
38733
|
const method_callee_params = (target_method ?? trait_default_func)?.params?.filter((p) => !p.is_self_param);
|
|
37869
38734
|
const lambda_arg_temps = /* @__PURE__ */ new Map();
|
|
37870
38735
|
for (let i = 0; i < access_func.params.length; i++) {
|
|
@@ -37979,13 +38844,13 @@ function build_access_node$1(node, status) {
|
|
|
37979
38844
|
build_node$1(access_func.params[i], status);
|
|
37980
38845
|
status.suppress_dereference = false;
|
|
37981
38846
|
}
|
|
37982
|
-
if (target_param && !target_param.is_self_param && (is_nullable_struct_type(target_param.type, status) || is_nullable_scalar_type(target_param.type))) {
|
|
38847
|
+
if (target_param && !target_param.is_self_param && (is_nullable_struct_type(target_param.type, status) || is_nullable_scalar_type(target_param.type, status))) {
|
|
37983
38848
|
status.code += `, `;
|
|
37984
38849
|
emit_nullable_arg_flag(access_func.params[i], status);
|
|
37985
38850
|
}
|
|
37986
38851
|
}
|
|
37987
38852
|
const method_ret_type = (target_method ?? trait_default_func)?.return_type ?? access_func.type;
|
|
37988
|
-
if (is_nullable_struct_type(method_ret_type, status) || is_nullable_scalar_type(method_ret_type)) {
|
|
38853
|
+
if (is_nullable_struct_type(method_ret_type, status) || is_nullable_scalar_type(method_ret_type, status)) {
|
|
37989
38854
|
const flag_name = status.current_nullable_call_flag;
|
|
37990
38855
|
status.code += `, `;
|
|
37991
38856
|
status.code += flag_name ? `&${flag_name}` : `(unsigned char *)&(unsigned char){0}`;
|
|
@@ -39730,11 +40595,11 @@ function is_nullable_struct_assignment(node, status) {
|
|
|
39730
40595
|
if (node.left_value.node_type === "value") {
|
|
39731
40596
|
const name = node.left_value.value;
|
|
39732
40597
|
const t = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
|
|
39733
|
-
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
|
|
40598
|
+
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
|
|
39734
40599
|
}
|
|
39735
40600
|
if (node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field") {
|
|
39736
40601
|
const field_type = node.left_value.access.type;
|
|
39737
|
-
return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type);
|
|
40602
|
+
return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type, status);
|
|
39738
40603
|
}
|
|
39739
40604
|
return false;
|
|
39740
40605
|
}
|
|
@@ -39789,7 +40654,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
39789
40654
|
const flag_name = has_flag_name(name);
|
|
39790
40655
|
const decl_type = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
|
|
39791
40656
|
const type_name = decl_type?.name || "";
|
|
39792
|
-
const is_scalar = is_nullable_scalar_type(decl_type);
|
|
40657
|
+
const is_scalar = is_nullable_scalar_type(decl_type, status);
|
|
39793
40658
|
const scalar_size = is_scalar ? aarch64_size(type_name) : 0;
|
|
39794
40659
|
const reclaimable = !(node.right_value.node_type === "value" && node.right_value.value === name) && !!status.heap_string_fields?.size && Array.from(status.heap_string_fields).some((k) => k.startsWith(`${name}.`));
|
|
39795
40660
|
if (rhs_is_null) {
|
|
@@ -39808,9 +40673,11 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
39808
40673
|
}
|
|
39809
40674
|
}
|
|
39810
40675
|
};
|
|
39811
|
-
const value_is_nullable_call = node.right_value.node_type === "func_call" && (is_nullable_struct_type(node.right_value.type, status) || is_nullable_scalar_type(node.right_value.type));
|
|
40676
|
+
const value_is_nullable_call = node.right_value.node_type === "func_call" && (is_nullable_struct_type(node.right_value.type, status) || is_nullable_scalar_type(node.right_value.type, status));
|
|
39812
40677
|
const rhs_value_type = call_result_type(node.right_value) ?? type_from_value_node$1(node.right_value);
|
|
39813
|
-
|
|
40678
|
+
const value_is_nullable_access = node.right_value.node_type === "access" && rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type, status));
|
|
40679
|
+
const rhs_is_field_read = value_is_nullable_access && is_nullable_scalar_type(rhs_value_type, status) && node.right_value.access.node_type === "access_field";
|
|
40680
|
+
if (value_is_nullable_access && !rhs_is_field_read) {
|
|
39814
40681
|
if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
|
|
39815
40682
|
retarget_records();
|
|
39816
40683
|
emit_rhs_value(node.right_value, nir_rhs, status);
|
|
@@ -39827,6 +40694,17 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
39827
40694
|
emit_var_store(status, "x9", flag_name, 8);
|
|
39828
40695
|
return;
|
|
39829
40696
|
}
|
|
40697
|
+
if (rhs_is_field_read) {
|
|
40698
|
+
if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
|
|
40699
|
+
retarget_records();
|
|
40700
|
+
emit_rhs_value(node.right_value, nir_rhs, status);
|
|
40701
|
+
ensure_newline(status);
|
|
40702
|
+
emit_var_store(status, "x0", name, scalar_size);
|
|
40703
|
+
load_nullable_has(node.right_value, "x9", status);
|
|
40704
|
+
ensure_newline(status);
|
|
40705
|
+
emit_var_store(status, "x9", flag_name, 8);
|
|
40706
|
+
return;
|
|
40707
|
+
}
|
|
39830
40708
|
if (value_is_nullable_call) {
|
|
39831
40709
|
if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
|
|
39832
40710
|
retarget_records();
|
|
@@ -39843,7 +40721,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
39843
40721
|
status.call_x8_preset = old_preset;
|
|
39844
40722
|
return;
|
|
39845
40723
|
}
|
|
39846
|
-
const rhs_is_nullable_value = !!rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type));
|
|
40724
|
+
const rhs_is_nullable_value = !!rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type, status));
|
|
39847
40725
|
let rhs_flag_slot;
|
|
39848
40726
|
if (rhs_is_nullable_value) {
|
|
39849
40727
|
load_nullable_has(node.right_value, "x9", status);
|
|
@@ -39880,7 +40758,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
39880
40758
|
const field_offset = get_field_offset(target_type.name, field_name, status);
|
|
39881
40759
|
const has_offset = get_field_has_offset(target_type.name, field_name, status);
|
|
39882
40760
|
const struct_size = get_struct_size(type_name, status);
|
|
39883
|
-
const field_is_scalar = is_nullable_scalar_type(field_access.type);
|
|
40761
|
+
const field_is_scalar = is_nullable_scalar_type(field_access.type, status);
|
|
39884
40762
|
get_source_address(access.target, status);
|
|
39885
40763
|
ensure_newline(status);
|
|
39886
40764
|
emit_asm(status, `str x0, [sp, #-16]!\n`);
|
|
@@ -39891,7 +40769,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
39891
40769
|
return;
|
|
39892
40770
|
}
|
|
39893
40771
|
const rhs_t = call_result_type(node.right_value) ?? type_from_value_node$1(node.right_value);
|
|
39894
|
-
const rhs_is_nullable_value = !!rhs_t?.is_nullable && (is_nullable_struct_type(rhs_t, status) || is_nullable_scalar_type(rhs_t));
|
|
40772
|
+
const rhs_is_nullable_value = !!rhs_t?.is_nullable && (is_nullable_struct_type(rhs_t, status) || is_nullable_scalar_type(rhs_t, status));
|
|
39895
40773
|
let rhs_flag_slot;
|
|
39896
40774
|
if (rhs_is_nullable_value) {
|
|
39897
40775
|
load_nullable_has(node.right_value, "x10", status);
|
|
@@ -40283,6 +41161,11 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40283
41161
|
emit_var_address(status, "x1", name);
|
|
40284
41162
|
emit_struct_copy("x0", "x1", 0, struct_size, status);
|
|
40285
41163
|
}
|
|
41164
|
+
if (node.right_value.node_type === "func_call" && !paramReg && !is_local_ref_var(name, status)) {
|
|
41165
|
+
const rhs_call = node.right_value;
|
|
41166
|
+
const rhs_ctor_struct = status.structs.find((s) => s.name === rhs_call.name && !s.is_simple_type && !s.is_class);
|
|
41167
|
+
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) ?? []));
|
|
41168
|
+
}
|
|
40286
41169
|
if (has_field_overrides(node.right_value)) emit_field_overrides(name, node.right_value, build_node, status);
|
|
40287
41170
|
build_swap(node, status, nir_swap);
|
|
40288
41171
|
return;
|
|
@@ -40503,12 +41386,13 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40503
41386
|
const move_source = rhs_is_string_var && node.last_use_move && move_on_last_use_enabled() ? rhs_value.value : void 0;
|
|
40504
41387
|
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
41388
|
const borrow_needs_dup = !node.swap && size === 16 && is_string_borrow(node.right_value) && !!status.force_heap_strings?.has(name);
|
|
41389
|
+
const move_source_owns_heap = move_source !== void 0 && !!status.heap_strings?.has(move_source);
|
|
40506
41390
|
if (rhs_is_view_into_owned) {
|
|
40507
41391
|
if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
|
|
40508
41392
|
status.heap_strings.add(name);
|
|
40509
|
-
} else if (rhs_is_string_var && move_source === void 0 || borrow_needs_dup) emit_strdup_string(status);
|
|
41393
|
+
} else if (rhs_is_string_var && (move_source === void 0 || !move_source_owns_heap) || borrow_needs_dup) emit_strdup_string(status);
|
|
40510
41394
|
if (rhs_is_string_var || borrow_needs_dup) {
|
|
40511
|
-
if (move_source !== void 0) status.heap_strings?.delete(move_source);
|
|
41395
|
+
if (move_source !== void 0 && move_source_owns_heap) status.heap_strings?.delete(move_source);
|
|
40512
41396
|
if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
|
|
40513
41397
|
status.heap_strings.add(name);
|
|
40514
41398
|
} else if (status.last_result_is_heap) {
|
|
@@ -40586,7 +41470,7 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40586
41470
|
emit_asm(status, `str xzr, [x0, #${offset + 8}]\n`);
|
|
40587
41471
|
}
|
|
40588
41472
|
}
|
|
40589
|
-
} else if (field_is_struct_string(target_type, field_type, status) && !node.operator && (() => {
|
|
41473
|
+
} else if (field_is_struct_string(target_type, field_type, status) && !node.operator && !node.raw_field_store && (() => {
|
|
40590
41474
|
const st = field_is_struct_string(target_type, field_type, status);
|
|
40591
41475
|
const tv = access.target.node_type === "value" ? access.target.value : "";
|
|
40592
41476
|
return st.target_is_class || tv !== "" && tv !== "self";
|
|
@@ -40614,12 +41498,16 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40614
41498
|
ensure_newline(status);
|
|
40615
41499
|
}
|
|
40616
41500
|
let rhs_is_heap = status.last_result_is_heap;
|
|
40617
|
-
|
|
40618
|
-
|
|
40619
|
-
|
|
41501
|
+
if (rhs_is_null_literal) {} else if (!rhs_is_heap) {
|
|
41502
|
+
const rhs_type_nullable = !!node.right_value.type?.is_nullable;
|
|
41503
|
+
if (field_type?.is_nullable || rhs_type_nullable) {
|
|
41504
|
+
const skip_dup = `.Lskip_field_dup_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
41505
|
+
emit_asm(status, `cbz x0, ${skip_dup}\n`);
|
|
41506
|
+
emit_strdup(status);
|
|
41507
|
+
emit_asm(status, `${skip_dup}:\n`);
|
|
41508
|
+
} else emit_strdup(status);
|
|
40620
41509
|
rhs_is_heap = true;
|
|
40621
41510
|
}
|
|
40622
|
-
if (rhs_is_null_literal) {} else if (is_class_target && !rhs_is_heap) emit_strdup(status);
|
|
40623
41511
|
mark_moved_if_struct(node.right_value, status);
|
|
40624
41512
|
if (field_type?.name === "string") {
|
|
40625
41513
|
emit_asm(status, `stp x0, x1, [sp, #-16]!\n`);
|
|
@@ -40805,6 +41693,14 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
40805
41693
|
get_base_address(access, status, "x0");
|
|
40806
41694
|
emit_asm(status, `str x0, [sp, #-16]!\n`);
|
|
40807
41695
|
}
|
|
41696
|
+
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))) {
|
|
41697
|
+
if (base_reg !== void 0) emit_asm(status, `ldr x0, [${base_reg}, #${offset}]\n`);
|
|
41698
|
+
else {
|
|
41699
|
+
emit_asm(status, `ldr x0, [sp]\n`);
|
|
41700
|
+
emit_asm(status, `ldr x0, [x0, #${offset}]\n`);
|
|
41701
|
+
}
|
|
41702
|
+
emit_free(status);
|
|
41703
|
+
}
|
|
40808
41704
|
emit_rhs_value(node.right_value, nir_rhs, status);
|
|
40809
41705
|
ensure_newline(status);
|
|
40810
41706
|
mark_moved_if_struct(node.right_value, status);
|
|
@@ -41698,7 +42594,7 @@ function build_operation_node(node, status) {
|
|
|
41698
42594
|
if (node.op === "??") {
|
|
41699
42595
|
const left_t = type_from_value_node$1(node.left_value);
|
|
41700
42596
|
const left_is_nullable_struct = is_nullable_struct_type(left_t, status);
|
|
41701
|
-
const left_is_nullable_scalar = is_nullable_scalar_type(left_t);
|
|
42597
|
+
const left_is_nullable_scalar = is_nullable_scalar_type(left_t, status);
|
|
41702
42598
|
if (left_is_nullable_scalar && node.left_value.node_type === "func_call") {
|
|
41703
42599
|
build_operand(node.left_value, "x0", status);
|
|
41704
42600
|
ensure_newline(status);
|
|
@@ -41750,11 +42646,11 @@ function build_operation_node(node, status) {
|
|
|
41750
42646
|
if ((node.op === "==" || node.op === "!=") && is_null_literal$1(node.left_value) !== is_null_literal$1(node.right_value)) {
|
|
41751
42647
|
const nullable_flagged_side = (n) => {
|
|
41752
42648
|
const t = type_from_value_node$1(n);
|
|
41753
|
-
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
|
|
42649
|
+
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
|
|
41754
42650
|
};
|
|
41755
42651
|
const nullable_side = nullable_flagged_side(node.left_value) ? node.left_value : node.right_value;
|
|
41756
42652
|
const side_type = type_from_value_node$1(nullable_side);
|
|
41757
|
-
if (is_nullable_struct_type(side_type, status) || is_nullable_scalar_type(side_type)) {
|
|
42653
|
+
if (is_nullable_struct_type(side_type, status) || is_nullable_scalar_type(side_type, status)) {
|
|
41758
42654
|
load_nullable_has(nullable_side, "x1", status);
|
|
41759
42655
|
ensure_newline(status);
|
|
41760
42656
|
emit_asm(status, `cmp x1, #0\n`);
|
|
@@ -42272,7 +43168,7 @@ function load_nullable_has(node, target_reg, status) {
|
|
|
42272
43168
|
get_source_address(access.target, status);
|
|
42273
43169
|
ensure_newline(status);
|
|
42274
43170
|
if (target_reg !== "x0") emit_asm(status, `mov ${target_reg}, x0\n`);
|
|
42275
|
-
const scalar_field = is_nullable_scalar_type(access.access.type);
|
|
43171
|
+
const scalar_field = is_nullable_scalar_type(access.access.type, status);
|
|
42276
43172
|
const wreg = target_reg.replace("x", "w");
|
|
42277
43173
|
const load_op = scalar_field ? `ldrb ${wreg}` : `ldr ${target_reg}`;
|
|
42278
43174
|
if (has_off === 0) emit_asm(status, `${load_op}, [${target_reg}]\n`);
|
|
@@ -44474,7 +45370,7 @@ function build_access_method(node, access_func, status) {
|
|
|
44474
45370
|
const access_callee_params = access_callee_method?.params?.filter((p) => !p.is_self_param);
|
|
44475
45371
|
const return_struct = !access_func.type.is_view && !access_func.type.is_array && (!!status.structs.find((s) => s.name === access_func.type.name && !s.is_simple_type && !s.is_class) || get_enum_sret_size(access_func.type.name, status) !== void 0);
|
|
44476
45372
|
const method_ret_type = access_callee_method?.return_type ?? access_func.type;
|
|
44477
|
-
const nullable_scalar_ret = is_nullable_scalar_type(method_ret_type) && !method_ret_type.is_ref;
|
|
45373
|
+
const nullable_scalar_ret = is_nullable_scalar_type(method_ret_type, status) && !method_ret_type.is_ref;
|
|
44478
45374
|
let temp_addr = "";
|
|
44479
45375
|
let temp_offset = 0;
|
|
44480
45376
|
if (return_struct || nullable_scalar_ret) {
|
|
@@ -44594,7 +45490,7 @@ function build_access_method(node, access_func, status) {
|
|
|
44594
45490
|
for (let i = 0; i < access_func.params.length; i++) {
|
|
44595
45491
|
if ((access_func.ref_param_indices ?? []).includes(i)) continue;
|
|
44596
45492
|
const cp = access_callee_params?.[i];
|
|
44597
|
-
if (cp && is_nullable_scalar_type(cp.type) || is_nullable_scalar_type(access_func.params[i].type)) nullable_scalar_arg_set.add(i);
|
|
45493
|
+
if (cp && is_nullable_scalar_type(cp.type, status) || is_nullable_scalar_type(access_func.params[i].type, status)) nullable_scalar_arg_set.add(i);
|
|
44598
45494
|
}
|
|
44599
45495
|
const arg_slot = [];
|
|
44600
45496
|
let total_arg_slots = 0;
|
|
@@ -44649,7 +45545,7 @@ function build_access_method(node, access_func, status) {
|
|
|
44649
45545
|
if (arg.node_type === "value" && arg.value === "null") {
|
|
44650
45546
|
emit_asm(status, `mov x0, #0\n`);
|
|
44651
45547
|
emit_asm(status, `mov x1, #0\n`);
|
|
44652
|
-
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
|
|
45548
|
+
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
|
|
44653
45549
|
const vname = arg.value;
|
|
44654
45550
|
emit_var_load(status, "x0", vname, aarch64_size(arg.type.name));
|
|
44655
45551
|
emit_var_load(status, "x1", has_flag_name(vname), 8);
|
|
@@ -44856,7 +45752,10 @@ function build_access_method(node, access_func, status) {
|
|
|
44856
45752
|
}
|
|
44857
45753
|
if (node.target.node_type === "value" && target_struct && !target_struct.is_class && !trait_target) {
|
|
44858
45754
|
const target_method = target_struct.functions.find((f) => f.name === access_func.name);
|
|
44859
|
-
if (target_method)
|
|
45755
|
+
if (target_method) {
|
|
45756
|
+
drop_self_written_string_field_records(status, node.target.value, scan_self_string_field_writes(target_struct, target_method));
|
|
45757
|
+
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));
|
|
45758
|
+
}
|
|
44860
45759
|
}
|
|
44861
45760
|
if (method_name.endsWith("_to_string") && method_name !== "string_to_string") status.last_result_is_heap = true;
|
|
44862
45761
|
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 +46794,14 @@ function gather_normalized_struct_returners(root, status) {
|
|
|
45895
46794
|
if (!return_normalizing.has(fn.name)) continue;
|
|
45896
46795
|
const ret_struct = structs.find((st) => st.name === fn.return_type?.name && !st.is_simple_type && !st.is_class);
|
|
45897
46796
|
for (const ret of direct_returns(fn)) {
|
|
45898
|
-
|
|
46797
|
+
let value = ret.value ?? void 0;
|
|
46798
|
+
if (value?.node_type === "anon_struct") {
|
|
46799
|
+
const base = value.base;
|
|
46800
|
+
if (base?.node_type === "func_call") value = base;
|
|
46801
|
+
}
|
|
45899
46802
|
if (value && value.node_type === "value") continue;
|
|
45900
46803
|
if (value && value.node_type === "func_call" && return_normalizing.has(value.name ?? "")) continue;
|
|
45901
|
-
if (value && value.node_type === "func_call" && !!ret_struct &&
|
|
46804
|
+
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
46805
|
return_normalizing.delete(fn.name);
|
|
45903
46806
|
changed = true;
|
|
45904
46807
|
break;
|
|
@@ -46130,7 +47033,7 @@ function build(root, options = {}) {
|
|
|
46130
47033
|
status.code = status.code + "\n" + status.c_runtime_defs;
|
|
46131
47034
|
status.c_runtime_defs = void 0;
|
|
46132
47035
|
}
|
|
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;
|
|
47036
|
+
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
47037
|
if (options.audit) {
|
|
46135
47038
|
status.code = wrap_c_allocators(status.code);
|
|
46136
47039
|
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);
|
|
@@ -48906,13 +49809,33 @@ function can_class_int_coerce(target, value, status) {
|
|
|
48906
49809
|
}
|
|
48907
49810
|
//#endregion
|
|
48908
49811
|
//#region ../src/check/utils/check_type_exists.ts
|
|
49812
|
+
/**
|
|
49813
|
+
* Containers whose elements live in raw T-sized slots (the `#arch` generic
|
|
49814
|
+
* bodies size each slot from the type name alone). A flagged-nullable
|
|
49815
|
+
* element type (`List<int?>`) has no per-slot null flag there, so `0`/`false`
|
|
49816
|
+
* would read back as null — rejected instead of silently misbehaving.
|
|
49817
|
+
* Element types with an in-band null (`string?`, class/trait `?`) stay legal.
|
|
49818
|
+
*/
|
|
49819
|
+
const RAW_ELEMENT_CONTAINERS = /* @__PURE__ */ new Set([
|
|
49820
|
+
"Array",
|
|
49821
|
+
"Buffer",
|
|
49822
|
+
"List",
|
|
49823
|
+
"Set",
|
|
49824
|
+
"Map",
|
|
49825
|
+
"Graph",
|
|
49826
|
+
"LinkedList",
|
|
49827
|
+
"Tree",
|
|
49828
|
+
"Arena"
|
|
49829
|
+
]);
|
|
48909
49830
|
function check_type_exists(type, status, start) {
|
|
49831
|
+
if (type.is_nullable && type.is_array && !type.is_ref && !type.is_pointer && is_nullable_scalar_name(type.name, status)) add_error(status, `Nullable element type '${type.name}?' is not supported in an array: element storage has no per-element null flag`, start);
|
|
48910
49832
|
if (type.name === "tuple" && type.tuple_types?.length) {
|
|
48911
49833
|
let ok = true;
|
|
48912
49834
|
for (const elem of type.tuple_types) if (!check_type_exists(elem, status, start)) ok = false;
|
|
48913
49835
|
return ok;
|
|
48914
49836
|
}
|
|
48915
49837
|
if (type.name === "anon_enum" && type.enum_cases?.length) {
|
|
49838
|
+
if (type.is_nullable && type.enum_cases.some((c) => c.types.length > 0)) add_error(status, `Nullable type '${type_name(type)}' is not supported: enums with associated data have no separate null flag`, start);
|
|
48916
49839
|
let ok = true;
|
|
48917
49840
|
for (const c of type.enum_cases) for (const t of c.types) if (!check_type_exists(t, status, start)) ok = false;
|
|
48918
49841
|
return ok;
|
|
@@ -48929,19 +49852,26 @@ function check_type_exists(type, status, start) {
|
|
|
48929
49852
|
}
|
|
48930
49853
|
}
|
|
48931
49854
|
type.name = declared.name;
|
|
49855
|
+
if (type.is_nullable) {
|
|
49856
|
+
if (status.enums.findLast((e) => (e.name === declared.name || e.source_name === declared.name) && !e.is_generic)?.has_associated_data) add_error(status, `Nullable type '${type_name(type)}' is not supported: enums with associated data have no separate null flag`, start);
|
|
49857
|
+
}
|
|
48932
49858
|
} else if (!status.types.includes(type.name)) {
|
|
48933
49859
|
add_error(status, `Unknown type: ${type_name(type)}`, start);
|
|
48934
49860
|
return false;
|
|
48935
49861
|
}
|
|
48936
49862
|
if (type.tuple_types) for (const elem of type.tuple_types) check_type_exists(elem, status, start);
|
|
48937
|
-
if (type.type_args)
|
|
48938
|
-
if (
|
|
48939
|
-
add_error(status, `
|
|
48940
|
-
continue;
|
|
49863
|
+
if (type.type_args) {
|
|
49864
|
+
if (RAW_ELEMENT_CONTAINERS.has(type.name)) {
|
|
49865
|
+
for (const arg of type.type_args) if (is_nullable_flagged_type(arg, status)) add_error(status, `Nullable element type '${type_name(arg)}' is not supported in '${type.name}<...>': container element storage has no per-element null flag`, start);
|
|
48941
49866
|
}
|
|
48942
|
-
|
|
48943
|
-
|
|
48944
|
-
|
|
49867
|
+
for (const arg of type.type_args) {
|
|
49868
|
+
if (arg.name === "func") {
|
|
49869
|
+
add_error(status, `func types cannot be used as type arguments ('${type.name}<...>')`, start);
|
|
49870
|
+
continue;
|
|
49871
|
+
}
|
|
49872
|
+
check_type_exists(arg, status, start);
|
|
49873
|
+
}
|
|
49874
|
+
} else {
|
|
48945
49875
|
const struct = status.structs.findLast((s) => s.name === type.name);
|
|
48946
49876
|
if (struct?.is_generic) {
|
|
48947
49877
|
if (!struct.type_params.every((tp) => status.type_params.includes(tp))) add_error(status, `Generic type '${type.name}' requires type arguments (expected <${struct.type_params.join(", ")}>)`, start);
|
|
@@ -51469,6 +52399,21 @@ function check_struct_node(struct, status) {
|
|
|
51469
52399
|
status.values.length = values_length_before_fields;
|
|
51470
52400
|
rewrite_generic_buffer_fields(struct, status);
|
|
51471
52401
|
mark_owning_auto_init_params(struct, status);
|
|
52402
|
+
if (!struct.is_library) for (const field of struct.fields) {
|
|
52403
|
+
if (field.declaration !== "move") continue;
|
|
52404
|
+
if (!field.type.name || field.type.is_ref || field.type.is_view) continue;
|
|
52405
|
+
if (field.type.is_nullable) continue;
|
|
52406
|
+
if (is_class_type$1(field.type.name, status)) continue;
|
|
52407
|
+
if (status.traits.find((t) => t.name === field.type.name)) continue;
|
|
52408
|
+
if (!is_owning_struct_type_requiring_move(new Type(mono_type_name(field.type)), status)) continue;
|
|
52409
|
+
if (!status.warnings) status.warnings = [];
|
|
52410
|
+
status.warnings.push({
|
|
52411
|
+
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`,
|
|
52412
|
+
start: field.start,
|
|
52413
|
+
line: 0,
|
|
52414
|
+
column: 0
|
|
52415
|
+
});
|
|
52416
|
+
}
|
|
51472
52417
|
status.types.push(struct.name);
|
|
51473
52418
|
status.structs.push(struct);
|
|
51474
52419
|
for (let func of struct.functions) {
|
|
@@ -51649,6 +52594,11 @@ function signature_matches(struct_func, trait_func, substitution) {
|
|
|
51649
52594
|
function types_match(a, b) {
|
|
51650
52595
|
return a.name === b.name && !!a.is_ref === !!b.is_ref && !!a.is_view === !!b.is_view && !!a.is_array === !!b.is_array;
|
|
51651
52596
|
}
|
|
52597
|
+
/** The user-visible spelling of a type (`List<int>`, `Map<string, int>`). */
|
|
52598
|
+
function type_display(t) {
|
|
52599
|
+
if (!t.type_args?.length) return t.name ?? "?";
|
|
52600
|
+
return `${t.name}<${t.type_args.map(type_display).join(", ")}>`;
|
|
52601
|
+
}
|
|
51652
52602
|
//#endregion
|
|
51653
52603
|
//#region ../src/check/utils/auto_derive.ts
|
|
51654
52604
|
function struct_has_function(struct, name) {
|
|
@@ -51800,20 +52750,57 @@ function build_hash(struct, fields) {
|
|
|
51800
52750
|
}
|
|
51801
52751
|
/**
|
|
51802
52752
|
* Whether `struct` is eligible for a synthesized `copy` method: a value
|
|
51803
|
-
* struct whose
|
|
51804
|
-
*
|
|
51805
|
-
*
|
|
51806
|
-
*
|
|
51807
|
-
*
|
|
51808
|
-
*
|
|
51809
|
-
*
|
|
52753
|
+
* struct whose every field is independently deep-copyable — direct string
|
|
52754
|
+
* fields (strdup'd), scalars/arrays (byte copies), `List<T>` fields whose
|
|
52755
|
+
* element is itself deep-copyable (the library `List.copy` deep-copies slots
|
|
52756
|
+
* via the owning store contract), and nested copyable structs (recursing
|
|
52757
|
+
* through their own `copy`). Class fields, enum-with-data fields (their
|
|
52758
|
+
* stores transfer payload ownership), class/trait-carrying `List` elements,
|
|
52759
|
+
* and resource-releasing `#destroy`s cannot be deep-copied by synthesis —
|
|
52760
|
+
* the copy stays unwritten (move/swap remains the escape hatch) — and a
|
|
52761
|
+
* struct with nothing to deep-copy (no string, `List`, or nested-copyable
|
|
52762
|
+
* field) is already soundly byte-copyable, so it needs no method.
|
|
51810
52763
|
*/
|
|
51811
52764
|
function struct_is_copyable(struct, status) {
|
|
51812
52765
|
if (struct.is_class || struct.is_generic || struct.is_simple_type) return false;
|
|
51813
|
-
if (
|
|
51814
|
-
if (
|
|
52766
|
+
if (struct_owns_resource(struct, status, /* @__PURE__ */ new Set())) return false;
|
|
52767
|
+
if (!struct_needs_copy(struct, status)) return false;
|
|
51815
52768
|
return struct_copy_ctor_args(struct) !== null;
|
|
51816
52769
|
}
|
|
52770
|
+
/** Whether any field of `struct` actually needs the synthesized deep copy:
|
|
52771
|
+
* a direct string field, a deep-copyable `List<T>` field, or a nested
|
|
52772
|
+
* copyable struct field. A struct of flat values byte-copies soundly
|
|
52773
|
+
* without a method. */
|
|
52774
|
+
function struct_needs_copy(struct, status) {
|
|
52775
|
+
if (direct_string_fields(struct).length > 0) return true;
|
|
52776
|
+
for (const field of struct.fields) {
|
|
52777
|
+
if (field.type.is_ref || field.type.is_view || field.type.is_array) continue;
|
|
52778
|
+
if (field_copy_call(field.type, status)) return true;
|
|
52779
|
+
}
|
|
52780
|
+
return false;
|
|
52781
|
+
}
|
|
52782
|
+
/**
|
|
52783
|
+
* Whether `struct` owns a resource a synthesized copy CANNOT duplicate: a
|
|
52784
|
+
* `#destroy` that releases something (a raw block / extern free), or a field
|
|
52785
|
+
* whose type has no deep-copy story (a class instance, a `List` of
|
|
52786
|
+
* class/trait elements, a nested struct with such a field). Fields that DO
|
|
52787
|
+
* have a deep-copy story (strings, nested copyables, copyable `List`s,
|
|
52788
|
+
* payload-copyable enums) don't block synthesis. Mirrors the old
|
|
52789
|
+
* `struct_owns_non_string_heap` gate but understands container deep copies.
|
|
52790
|
+
*/
|
|
52791
|
+
function struct_owns_resource(struct, status, visited) {
|
|
52792
|
+
if (visited.has(struct.name)) return false;
|
|
52793
|
+
visited.add(struct.name);
|
|
52794
|
+
const destroy = struct.functions.find((f) => f.name === "#destroy");
|
|
52795
|
+
if (destroy && destroy_releases(destroy)) return true;
|
|
52796
|
+
for (const field of struct.fields) {
|
|
52797
|
+
if (field.type.is_ref || field.type.is_view) continue;
|
|
52798
|
+
if (field.type.name === "string" && !field.type.is_array) continue;
|
|
52799
|
+
if (field.type.is_nullable) return true;
|
|
52800
|
+
if (!field_copyable(field.type, status)) return true;
|
|
52801
|
+
}
|
|
52802
|
+
return false;
|
|
52803
|
+
}
|
|
51817
52804
|
/**
|
|
51818
52805
|
* How a synthesized `copy` constructs the fresh value: the zero-argument
|
|
51819
52806
|
* `#init` overload when one exists (auto or user), else the bodyless AUTO
|
|
@@ -51829,14 +52816,49 @@ function struct_copy_ctor_args(struct) {
|
|
|
51829
52816
|
if (!auto) return null;
|
|
51830
52817
|
return auto.params.filter((p) => !p.is_self_param).map((p) => p.name);
|
|
51831
52818
|
}
|
|
51832
|
-
/** Whether `type`
|
|
51833
|
-
*
|
|
51834
|
-
*
|
|
51835
|
-
*
|
|
51836
|
-
|
|
52819
|
+
/** Whether `type` has (or will get) a deep-copy story: a struct with a
|
|
52820
|
+
* synthesized `copy`, a `List<T>` whose element is deep-copyable (the
|
|
52821
|
+
* library `List.copy` deep-copies slots via the owning store contract), or
|
|
52822
|
+
* a primitive / plain (payload-free) enum. Order-independent: it
|
|
52823
|
+
* re-evaluates the eligibility predicate instead of looking for an
|
|
52824
|
+
* already-synthesized method, so an outer struct nested in the same
|
|
52825
|
+
* pre-pass resolves a later-declared field struct. */
|
|
52826
|
+
function field_copyable(type, status) {
|
|
52827
|
+
if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
|
|
52828
|
+
if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
|
|
52829
|
+
const nested_struct = status.structs.find((s) => s.name === type.name);
|
|
52830
|
+
if (nested_struct) {
|
|
52831
|
+
if (nested_struct.is_simple_type) return true;
|
|
52832
|
+
return struct_is_copyable(nested_struct, status);
|
|
52833
|
+
}
|
|
52834
|
+
const en = status.enums.find((e) => e.name === type.name);
|
|
52835
|
+
if (en) return !en.has_associated_data;
|
|
52836
|
+
return !status.traits.some((t) => t.name === type.name);
|
|
52837
|
+
}
|
|
52838
|
+
/** Whether a `List<T>` element type is deep-copyable (see
|
|
52839
|
+
* `field_copyable`). */
|
|
52840
|
+
function element_copyable(type, status) {
|
|
51837
52841
|
if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
|
|
52842
|
+
if (type.name === "string") return true;
|
|
52843
|
+
if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
|
|
51838
52844
|
const nested = status.structs.find((s) => s.name === type.name);
|
|
51839
|
-
|
|
52845
|
+
if (nested) {
|
|
52846
|
+
if (nested.is_simple_type) return true;
|
|
52847
|
+
return !nested.is_class && struct_is_copyable(nested, status);
|
|
52848
|
+
}
|
|
52849
|
+
const en = status.enums.find((e) => e.name === type.name);
|
|
52850
|
+
if (en) return !en.has_associated_data;
|
|
52851
|
+
return !status.traits.some((t) => t.name === type.name);
|
|
52852
|
+
}
|
|
52853
|
+
/** Whether `build_copy` must route the field through a `.copy()` call (a
|
|
52854
|
+
* nested copyable struct, or a `List<T>` field): everything else byte
|
|
52855
|
+
* copies through plain field assignment (strings strdup via the field-write
|
|
52856
|
+
* path, primitives/plain enums are flat values). */
|
|
52857
|
+
function field_copy_call(type, status) {
|
|
52858
|
+
if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
|
|
52859
|
+
if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
|
|
52860
|
+
const nested_struct = status.structs.find((s) => s.name === type.name);
|
|
52861
|
+
return !!nested_struct && !nested_struct.is_simple_type && struct_is_copyable(nested_struct, status);
|
|
51840
52862
|
}
|
|
51841
52863
|
/**
|
|
51842
52864
|
* Synthesize `pub func copy(self) -> Struct` for owning value structs: a
|
|
@@ -51852,8 +52874,8 @@ function build_copy(struct, status) {
|
|
|
51852
52874
|
const ctor = new FunctionCallNode(-1, struct.name, new Type(struct.name), ctor_args.map((name) => field_access("self", name)));
|
|
51853
52875
|
const statements = [new DeclarationNode(-1, "private", "var", "c", new Type(struct.name), ctor)];
|
|
51854
52876
|
for (const field of struct.fields) {
|
|
51855
|
-
const right =
|
|
51856
|
-
statements.push(new AssignmentNode(-1, field_access("c", field.name), right
|
|
52877
|
+
const right = field_copy_call(field.type, status) ? method_call(field_access("self", field.name), "copy") : field_access("self", field.name);
|
|
52878
|
+
statements.push(new AssignmentNode(-1, field_access("c", field.name), right));
|
|
51857
52879
|
}
|
|
51858
52880
|
statements.push(new ReturnNode(-1, new ValueNode(-1, "c")));
|
|
51859
52881
|
return new FunctionNode(-1, "pub", "copy", new Type(struct.name), [self_param(struct)], statements);
|
|
@@ -52932,7 +53954,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
52932
53954
|
mono_field.type = constructed;
|
|
52933
53955
|
}
|
|
52934
53956
|
}
|
|
52935
|
-
substitute_raw_in_node(mono_field.value, substitution, status.structs, void 0, status.traits,
|
|
53957
|
+
substitute_raw_in_node(mono_field.value, substitution, status.structs, void 0, status.traits, {
|
|
53958
|
+
enums: status.enums,
|
|
53959
|
+
bitsets: status.bitsets
|
|
53960
|
+
});
|
|
52936
53961
|
}
|
|
52937
53962
|
mono_field.is_library = field.is_library;
|
|
52938
53963
|
mono_field.is_readonly = field.is_readonly;
|
|
@@ -52967,7 +53992,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
52967
53992
|
for (const func of generic_struct.functions) {
|
|
52968
53993
|
if (func.name === "#init") continue;
|
|
52969
53994
|
const cloned = clone_node(func);
|
|
52970
|
-
substitute_raw_types(cloned, substitution, status.structs, status.traits,
|
|
53995
|
+
substitute_raw_types(cloned, substitution, status.structs, status.traits, {
|
|
53996
|
+
enums: status.enums,
|
|
53997
|
+
bitsets: status.bitsets
|
|
53998
|
+
});
|
|
52971
53999
|
fold_substituted_constant_ifs(cloned.statements);
|
|
52972
54000
|
rename_local_labels(cloned, mono_name);
|
|
52973
54001
|
const retype_roots = [...cloned.statements, ...cloned.allocations ?? []];
|
|
@@ -52980,7 +54008,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
52980
54008
|
param.type = substitute_type(param.type, substitution);
|
|
52981
54009
|
substitute_param_signature(param, substitution);
|
|
52982
54010
|
if (param.type.type_args?.length) param.type = materialize_generic_enum_type(param.type, status);
|
|
52983
|
-
if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits,
|
|
54011
|
+
if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits, {
|
|
54012
|
+
enums: status.enums,
|
|
54013
|
+
bitsets: status.bitsets
|
|
54014
|
+
});
|
|
52984
54015
|
}
|
|
52985
54016
|
retype_param_references(retype_roots, cloned.params);
|
|
52986
54017
|
retype_local_references(retype_roots);
|
|
@@ -53008,7 +54039,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
53008
54039
|
if (variadic_init_unsupported(generic_init)) continue;
|
|
53009
54040
|
if (!generic_init.statements.some((s) => s.node_type !== "raw")) continue;
|
|
53010
54041
|
const cloned = clone_node(generic_init);
|
|
53011
|
-
substitute_raw_types(cloned, substitution, status.structs, status.traits,
|
|
54042
|
+
substitute_raw_types(cloned, substitution, status.structs, status.traits, {
|
|
54043
|
+
enums: status.enums,
|
|
54044
|
+
bitsets: status.bitsets
|
|
54045
|
+
});
|
|
53012
54046
|
fold_substituted_constant_ifs(cloned.statements);
|
|
53013
54047
|
rename_local_labels(cloned, mono_name);
|
|
53014
54048
|
cloned.return_type = new Type(mono_name);
|
|
@@ -53016,7 +54050,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
53016
54050
|
for (const param of cloned.params) {
|
|
53017
54051
|
param.type = substitute_type(param.type, substitution);
|
|
53018
54052
|
substitute_param_signature(param, substitution);
|
|
53019
|
-
if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits,
|
|
54053
|
+
if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits, {
|
|
54054
|
+
enums: status.enums,
|
|
54055
|
+
bitsets: status.bitsets
|
|
54056
|
+
});
|
|
53020
54057
|
}
|
|
53021
54058
|
const cloned_self = cloned.params.find((p) => p.is_self_param);
|
|
53022
54059
|
if (cloned_self) cloned_self.type = new Type(mono_name);
|
|
@@ -53521,7 +54558,7 @@ function derive_annotations_for_access_func(access_node, fc, status) {
|
|
|
53521
54558
|
const p = func.params[i];
|
|
53522
54559
|
if (p.is_self_param) continue;
|
|
53523
54560
|
if (!p.type?.is_nullable) continue;
|
|
53524
|
-
if (status.structs.find((s) => s.name === p.type.name && !s.is_class) || is_nullable_scalar_type(p.type)) nullable_indices.push(i - (self_offset >= 0 ? self_offset + 1 : 0));
|
|
54561
|
+
if (status.structs.find((s) => s.name === p.type.name && !s.is_class) || is_nullable_scalar_type(p.type, status)) nullable_indices.push(i - (self_offset >= 0 ? self_offset + 1 : 0));
|
|
53525
54562
|
}
|
|
53526
54563
|
if (nullable_indices.length > 0) fc.nullable_param_indices = nullable_indices;
|
|
53527
54564
|
if (func.return_constraint && !fc.return_bounds) {
|
|
@@ -53900,7 +54937,7 @@ function fold_substituted_constant_ifs(statements) {
|
|
|
53900
54937
|
statements.splice(i, 1, ...kept);
|
|
53901
54938
|
}
|
|
53902
54939
|
}
|
|
53903
|
-
function substitute_raw_types(func, substitution, structs, traits,
|
|
54940
|
+
function substitute_raw_types(func, substitution, structs, traits, tables) {
|
|
53904
54941
|
const deref_params = /* @__PURE__ */ new Set();
|
|
53905
54942
|
for (const param of func.params) {
|
|
53906
54943
|
const resolved = substitution.get(param.type.name);
|
|
@@ -53909,7 +54946,7 @@ function substitute_raw_types(func, substitution, structs, traits, enums) {
|
|
|
53909
54946
|
const s = structs.find((x) => x.name === resolved);
|
|
53910
54947
|
if (s && !s.is_simple_type && !s.is_class) deref_params.add(param.name);
|
|
53911
54948
|
}
|
|
53912
|
-
for (const stmt of func.statements) substitute_raw_in_node(stmt, substitution, structs, deref_params, traits,
|
|
54949
|
+
for (const stmt of func.statements) substitute_raw_in_node(stmt, substitution, structs, deref_params, traits, tables);
|
|
53913
54950
|
}
|
|
53914
54951
|
function rename_local_labels(node, prefix) {
|
|
53915
54952
|
if (node.node_type === "raw") {
|
|
@@ -53942,7 +54979,7 @@ function raw_type_alignment(name) {
|
|
|
53942
54979
|
function raw_align_to(offset, alignment) {
|
|
53943
54980
|
return Math.ceil(offset / alignment) * alignment;
|
|
53944
54981
|
}
|
|
53945
|
-
function raw_type_size(name, structs,
|
|
54982
|
+
function raw_type_size(name, structs, tables) {
|
|
53946
54983
|
switch (name) {
|
|
53947
54984
|
case "bool":
|
|
53948
54985
|
case "int8":
|
|
@@ -53953,13 +54990,13 @@ function raw_type_size(name, structs, enums) {
|
|
|
53953
54990
|
case "int32":
|
|
53954
54991
|
case "uint32": return 4;
|
|
53955
54992
|
}
|
|
53956
|
-
const enum_node = enums
|
|
54993
|
+
const enum_node = tables.enums.find((e) => e.name === name && !e.is_generic);
|
|
53957
54994
|
if (enum_node) {
|
|
53958
54995
|
if (!enum_node.has_associated_data) return 8;
|
|
53959
54996
|
let max_payload = 0;
|
|
53960
54997
|
for (const c of enum_node.cases) {
|
|
53961
54998
|
let case_size = 0;
|
|
53962
|
-
for (const p of c.params) case_size += raw_type_size(p.type.name, structs,
|
|
54999
|
+
for (const p of c.params) case_size += raw_type_size(p.type.name, structs, tables);
|
|
53963
55000
|
max_payload = Math.max(max_payload, case_size);
|
|
53964
55001
|
}
|
|
53965
55002
|
return 8 + Math.ceil(max_payload / 8) * 8;
|
|
@@ -53974,9 +55011,9 @@ function raw_type_size(name, structs, enums) {
|
|
|
53974
55011
|
size += 16;
|
|
53975
55012
|
continue;
|
|
53976
55013
|
}
|
|
53977
|
-
size += raw_type_size(field.type.name, structs,
|
|
55014
|
+
size += raw_type_size(field.type.name, structs, tables);
|
|
53978
55015
|
if (field.type.is_nullable && structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_class)) size += 8;
|
|
53979
|
-
else if (is_nullable_scalar_type(field.type)) size += 1;
|
|
55016
|
+
else if (is_nullable_scalar_type(field.type, tables)) size += 1;
|
|
53980
55017
|
}
|
|
53981
55018
|
return raw_align_to(size, 8);
|
|
53982
55019
|
}
|
|
@@ -54021,12 +55058,15 @@ function raw_block_is_pure_asm(value) {
|
|
|
54021
55058
|
function is_reference_type_arg(t, structs, traits) {
|
|
54022
55059
|
return !!traits.some((tr) => tr.name === t.name) || !!structs.find((s) => s.name === t.name && s.is_class);
|
|
54023
55060
|
}
|
|
54024
|
-
function substitute_raw_in_node(node, substitution, structs, deref_params = /* @__PURE__ */ new Set(), traits = [],
|
|
55061
|
+
function substitute_raw_in_node(node, substitution, structs, deref_params = /* @__PURE__ */ new Set(), traits = [], tables = {
|
|
55062
|
+
enums: [],
|
|
55063
|
+
bitsets: []
|
|
55064
|
+
}) {
|
|
54025
55065
|
if (node.node_type === "value") {
|
|
54026
55066
|
const vn = node;
|
|
54027
55067
|
for (const [param, type] of substitution) {
|
|
54028
55068
|
if (vn.value === `${param}_SIZE`) {
|
|
54029
|
-
vn.value = String(raw_type_size(type, structs,
|
|
55069
|
+
vn.value = String(raw_type_size(type, structs, tables));
|
|
54030
55070
|
vn.type = new Type("int", true);
|
|
54031
55071
|
return;
|
|
54032
55072
|
}
|
|
@@ -54051,7 +55091,7 @@ function substitute_raw_in_node(node, substitution, structs, deref_params = /* @
|
|
|
54051
55091
|
else if (struct_node) c_type_name = `struct ${type}`;
|
|
54052
55092
|
else c_type_name = raw_c_type_name(type);
|
|
54053
55093
|
value = value.replace(new RegExp(`\\b${param}\\b`, "g"), c_type_name);
|
|
54054
|
-
const size = raw_type_size(type, structs,
|
|
55094
|
+
const size = raw_type_size(type, structs, tables);
|
|
54055
55095
|
const size_expr = raw_block_is_pure_asm(value) ? String(size) : `sizeof(${c_type_name})`;
|
|
54056
55096
|
value = value.replace(new RegExp(`\\b${param}_SIZE\\b`, "g"), size_expr);
|
|
54057
55097
|
value = value.replace(new RegExp(`\\b${param}_destroy\\b`, "g"), `${type}_destroy`);
|
|
@@ -54087,22 +55127,22 @@ function substitute_raw_in_node(node, substitution, structs, deref_params = /* @
|
|
|
54087
55127
|
if (any_node.func_return_type?.name) any_node.func_return_type = substitute_type(any_node.func_return_type, substitution);
|
|
54088
55128
|
}
|
|
54089
55129
|
if (any_node.allocations && Array.isArray(any_node.allocations)) {
|
|
54090
|
-
for (const child of any_node.allocations) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits,
|
|
55130
|
+
for (const child of any_node.allocations) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
|
|
54091
55131
|
}
|
|
54092
55132
|
if (any_node.statements && Array.isArray(any_node.statements)) {
|
|
54093
|
-
for (const child of any_node.statements) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits,
|
|
55133
|
+
for (const child of any_node.statements) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
|
|
54094
55134
|
}
|
|
54095
55135
|
if (any_node.params && Array.isArray(any_node.params)) {
|
|
54096
|
-
for (const child of any_node.params) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits,
|
|
55136
|
+
for (const child of any_node.params) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
|
|
54097
55137
|
}
|
|
54098
|
-
if (any_node.value && any_node.value.node_type) substitute_raw_in_node(any_node.value, substitution, structs, deref_params, traits,
|
|
54099
|
-
if (any_node.left_value?.node_type) substitute_raw_in_node(any_node.left_value, substitution, structs, deref_params, traits,
|
|
54100
|
-
if (any_node.right_value?.node_type) substitute_raw_in_node(any_node.right_value, substitution, structs, deref_params, traits,
|
|
54101
|
-
if (any_node.target?.node_type) substitute_raw_in_node(any_node.target, substitution, structs, deref_params, traits,
|
|
54102
|
-
if (any_node.access?.node_type) substitute_raw_in_node(any_node.access, substitution, structs, deref_params, traits,
|
|
54103
|
-
if (any_node.swap?.node_type) substitute_raw_in_node(any_node.swap, substitution, structs, deref_params, traits,
|
|
54104
|
-
if (any_node.condition?.node_type) substitute_raw_in_node(any_node.condition, substitution, structs, deref_params, traits,
|
|
54105
|
-
if (any_node.index?.node_type) substitute_raw_in_node(any_node.index, substitution, structs, deref_params, traits,
|
|
55138
|
+
if (any_node.value && any_node.value.node_type) substitute_raw_in_node(any_node.value, substitution, structs, deref_params, traits, tables);
|
|
55139
|
+
if (any_node.left_value?.node_type) substitute_raw_in_node(any_node.left_value, substitution, structs, deref_params, traits, tables);
|
|
55140
|
+
if (any_node.right_value?.node_type) substitute_raw_in_node(any_node.right_value, substitution, structs, deref_params, traits, tables);
|
|
55141
|
+
if (any_node.target?.node_type) substitute_raw_in_node(any_node.target, substitution, structs, deref_params, traits, tables);
|
|
55142
|
+
if (any_node.access?.node_type) substitute_raw_in_node(any_node.access, substitution, structs, deref_params, traits, tables);
|
|
55143
|
+
if (any_node.swap?.node_type) substitute_raw_in_node(any_node.swap, substitution, structs, deref_params, traits, tables);
|
|
55144
|
+
if (any_node.condition?.node_type) substitute_raw_in_node(any_node.condition, substitution, structs, deref_params, traits, tables);
|
|
55145
|
+
if (any_node.index?.node_type) substitute_raw_in_node(any_node.index, substitution, structs, deref_params, traits, tables);
|
|
54106
55146
|
}
|
|
54107
55147
|
function specialize_function(generic_func, call_node, status) {
|
|
54108
55148
|
const substitution = /* @__PURE__ */ new Map();
|
|
@@ -55121,7 +56161,7 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
55121
56161
|
for (let [i, param] of node.params.entries()) {
|
|
55122
56162
|
const func_param = func.params[i + self_offset];
|
|
55123
56163
|
if (func_param && func_param.type.is_nullable && !func_param.is_self_param && !func_param.type.is_ref && !func_param.type.is_array) {
|
|
55124
|
-
if (status.structs.find((st) => st.name === func_param.type.name && !st.is_class && !st.is_simple_type) || is_nullable_scalar_type(func_param.type)) {
|
|
56164
|
+
if (status.structs.find((st) => st.name === func_param.type.name && !st.is_class && !st.is_simple_type) || is_nullable_scalar_type(func_param.type, status)) {
|
|
55125
56165
|
if (!node.nullable_param_indices) node.nullable_param_indices = [];
|
|
55126
56166
|
if (!node.nullable_param_indices.includes(i)) node.nullable_param_indices.push(i);
|
|
55127
56167
|
}
|
|
@@ -55252,7 +56292,14 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
55252
56292
|
const named = status.functions.findLast((f) => f.name === param_value);
|
|
55253
56293
|
if (named) param.resolved_function = named;
|
|
55254
56294
|
}
|
|
55255
|
-
if (param_type.is_array && param_type.length && !func_param.
|
|
56295
|
+
if (param_type.is_array && param_type.length && !func_param.is_variadic) {
|
|
56296
|
+
if (!func_param.type.length) func_param.type.length = param_type.length;
|
|
56297
|
+
else {
|
|
56298
|
+
const stamped = String(func_param.type.length.value);
|
|
56299
|
+
const incoming = String(param_type.length.value);
|
|
56300
|
+
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);
|
|
56301
|
+
}
|
|
56302
|
+
}
|
|
55256
56303
|
let arg_value;
|
|
55257
56304
|
if (param.node_type === "value") {
|
|
55258
56305
|
const vn = param;
|
|
@@ -56603,7 +57650,7 @@ function check_as_array_or_inferred_tuple(array, status, forced_tuple_types) {
|
|
|
56603
57650
|
array.type.is_array = true;
|
|
56604
57651
|
array.type.is_nullable = old_expected.is_nullable;
|
|
56605
57652
|
array.type.type_args = old_expected.type_args;
|
|
56606
|
-
array.type.length = old_expected.length;
|
|
57653
|
+
if (!old_expected.length || String(old_expected.length.value) === String(array.values.length)) array.type.length = old_expected.length;
|
|
56607
57654
|
} else if (!array.type.name) {
|
|
56608
57655
|
const first_type = value_types[0];
|
|
56609
57656
|
if (first_type) {
|
|
@@ -57954,6 +59001,11 @@ function check_switch_node(switch_node, status) {
|
|
|
57954
59001
|
let branch_statuses = [];
|
|
57955
59002
|
for (let switch_case of switch_node.cases) {
|
|
57956
59003
|
check_node(switch_case.condition, status);
|
|
59004
|
+
if (status.allocations.length) {
|
|
59005
|
+
switch_case.condition.allocations ??= [];
|
|
59006
|
+
switch_case.condition.allocations.push(...status.allocations);
|
|
59007
|
+
status.allocations.length = 0;
|
|
59008
|
+
}
|
|
57957
59009
|
const condition_type = type_from_value_node(switch_case.condition, status);
|
|
57958
59010
|
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
59011
|
let case_status = clone_status(status);
|