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