nomen-lang 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +85 -9
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -21164,7 +21164,8 @@ function build_return_node$1(node, status, nir_value) {
21164
21164
  else if ((node.value.node_type === "match" || node.value.node_type === "switch") && !return_join_owned_string) needs_borrow_strdup = true;
21165
21165
  }
21166
21166
  if (needs_borrow_strdup) {
21167
- emit_strdup_string(status);
21167
+ if (is_view_value(node.value, status) && (node.value.node_type === "value" || node.value.node_type === "access")) emit_view_materialize_owned(status);
21168
+ else emit_strdup_string(status);
21168
21169
  if (!status.code.endsWith("\n")) status.code += "\n";
21169
21170
  status.last_result_is_heap = true;
21170
21171
  }
@@ -25996,6 +25997,12 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
25996
25997
  status.last_result_is_heap = false;
25997
25998
  emit_rhs_value$1(node.right_value, nir_rhs, status);
25998
25999
  if (!status.code.endsWith("\n")) status.code += "\n";
26000
+ const assign_lhs_type = status.scoped_declarations.find((d) => d.name === name)?.type ?? status.variable_types?.get(name);
26001
+ const rhs_is_view_into_owned = !node.swap && !node.operator && size === 16 && assign_lhs_type?.name === "string" && !assign_lhs_type.is_view && is_view_value(node.right_value, status);
26002
+ if (rhs_is_view_into_owned) {
26003
+ emit_view_materialize_owned(status);
26004
+ if (!status.code.endsWith("\n")) status.code += "\n";
26005
+ }
25999
26006
  const saves_fat_pair = size === 16;
26000
26007
  if (saves_fat_pair) status.code += `stp x0, x1, [sp, #-16]!\n`;
26001
26008
  else status.code += `str x0, [sp, #-16]!\n`;
@@ -26011,7 +26018,10 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
26011
26018
  const move_source = rhs_is_string_var && node.last_use_move && move_on_last_use_enabled() ? rhs_value.value : void 0;
26012
26019
  const explicit_move_source = !node.swap && !!rhs_value && rhs_value.is_moved && size === 16 && rhs_value.type?.name === "string" && !rhs_value.type?.is_view ? rhs_value.value : void 0;
26013
26020
  const borrow_needs_dup = !node.swap && size === 16 && is_string_borrow(node.right_value) && !!status.force_heap_strings?.has(name);
26014
- if (rhs_is_string_var && move_source === void 0 || borrow_needs_dup) emit_strdup_string(status);
26021
+ if (rhs_is_view_into_owned) {
26022
+ if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
26023
+ status.heap_strings.add(name);
26024
+ } else if (rhs_is_string_var && move_source === void 0 || borrow_needs_dup) emit_strdup_string(status);
26015
26025
  if (rhs_is_string_var || borrow_needs_dup) {
26016
26026
  if (move_source !== void 0) status.heap_strings?.delete(move_source);
26017
26027
  if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
@@ -34108,6 +34118,7 @@ function build_return_node(node, status, nir_value) {
34108
34118
  if (!returned_value_decl) returns_borrow_var = true;
34109
34119
  else if (is_string_borrow(returned_value_decl.value) || !!status.string_borrow_vars?.has(var_name)) returns_borrow_var = true;
34110
34120
  }
34121
+ const returns_view_value = ret_type.name === "string" && !ret_type.is_view && !ret_type.is_array && is_view_value(node.value, status) && (node.value.node_type === "value" || node.value.node_type === "access");
34111
34122
  if (returns_borrowed_string) {
34112
34123
  const access = node.value.access;
34113
34124
  if (access.node_type === "access_func") {
@@ -34134,16 +34145,17 @@ function build_return_node(node, status, nir_value) {
34134
34145
  }
34135
34146
  }
34136
34147
  const returns_string_zero = ret_type.name === "string" && !ret_type.is_view && !ret_type.is_array && node.value.node_type === "value" && (node.value.value === "0" || node.value.value === "null");
34137
- if (!returns_string_zero && (returns_borrowed_string || returns_string_literal || returns_borrow_var)) status.code += `nomen_str_dup(`;
34148
+ if (!returns_string_zero && !returns_view_value && (returns_borrowed_string || returns_string_literal || returns_borrow_var)) status.code += `nomen_str_dup(`;
34138
34149
  const wrap_view_string_return = !!ret_type.is_view && ret_type.name === "string" && !is_view_value(node.value, status);
34139
34150
  const expr_type_name = node.type?.name || "";
34140
34151
  const expr_is_simple = !status.structs.find((s) => s.name === expr_type_name && !s.is_simple_type);
34141
34152
  if (is_struct && expr_is_simple && !returns_borrowed_string && !ret_type.is_view) status.code += return_is_class ? `(struct ${mono_ret_name}*)` : `(struct ${mono_ret_name})`;
34142
34153
  if (wrap_view_string_return) status.code += `({ nomen_string _p = `;
34143
34154
  if (returns_string_zero) status.code += `(nomen_string){0,0}`;
34155
+ else if (returns_view_value) c_materialize_view_string(node.value, status);
34144
34156
  else emit_return_value(node.value, nir_value, status);
34145
34157
  if (wrap_view_string_return) status.code += `; nomen_view _v = { (void*)_p.ptr, _p.len }; _v; })`;
34146
- if (!returns_string_zero && (returns_borrowed_string || returns_string_literal || returns_borrow_var)) status.code += `)`;
34158
+ if (!returns_string_zero && !returns_view_value && (returns_borrowed_string || returns_string_literal || returns_borrow_var)) status.code += `)`;
34147
34159
  status.code += `;\n`;
34148
34160
  if (node.value.node_type === "func_call" && node.value.field_overrides?.length) emit_field_overrides("_return_val", node.value, build_node, status, "", ";\n");
34149
34161
  reclaim_all_c_scopes(status);
@@ -35884,9 +35896,15 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
35884
35896
  } else {
35885
35897
  if (lhs_is_string && !rhs_is_bare_value) {
35886
35898
  const temp = `_reassign_${status.label_counter = (status.label_counter ?? 0) + 1}`;
35899
+ const rhs_is_view = is_view_value(node.right_value, status);
35887
35900
  status.code += `nomen_string ${temp} = `;
35888
- emit_rhs_value(node.right_value, nir_rhs, status);
35901
+ if (rhs_is_view) c_materialize_view_string(node.right_value, status);
35902
+ else emit_rhs_value(node.right_value, nir_rhs, status);
35889
35903
  status.code += `;\nfree(${lhs_name}.ptr);\n${lhs_name} = ${temp};\n`;
35904
+ if (rhs_is_view) {
35905
+ if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
35906
+ status.heap_strings.add(lhs_name);
35907
+ }
35890
35908
  return;
35891
35909
  }
35892
35910
  status.code += `free(${lhs_name}.ptr);\n`;
@@ -35900,6 +35918,13 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
35900
35918
  if (rhs_is_bare_value) {
35901
35919
  if (lhs_is_class) {
35902
35920
  if (!node.swap) splice_decl_from_c_scopes(status, rhs.value);
35921
+ } else if (lhs_is_string && !node.swap && rhs.node_type === "value" && is_view_value(rhs, status)) {
35922
+ status.code += `${lhs_name} = `;
35923
+ c_materialize_view_string(rhs, status);
35924
+ status.code += `;\n`;
35925
+ if (!status.heap_strings) status.heap_strings = /* @__PURE__ */ new Set();
35926
+ status.heap_strings.add(lhs_name);
35927
+ return;
35903
35928
  } else if (!node.swap && rhs.is_moved) {
35904
35929
  const src_name = rhs.value;
35905
35930
  if (string_var_owns_heap(status, src_name)) {
@@ -40222,6 +40247,15 @@ function evaluate_operation(op, status) {
40222
40247
  }
40223
40248
  if (op.op === "<=" && left_decl?.upper_bound_inclusive_exprs?.length && target_str) {
40224
40249
  if (left_decl.upper_bound_inclusive_exprs.includes(target_str)) return true;
40250
+ if (left_decl.upper_bound_inclusive_exprs.some((u) => upper_implies(u, target_str))) return true;
40251
+ for (const u of left_decl.upper_bound_inclusive_exprs) {
40252
+ const v = parse_offset_expr(u);
40253
+ if (v && v.offset === 0 && u !== target_str) {
40254
+ const vdecl = status.values.findLast((vv) => vv.name === v.base);
40255
+ if (vdecl?.upper_bound_exprs?.includes(target_str)) return true;
40256
+ if (vdecl?.upper_bound_inclusive_exprs?.includes(target_str)) return true;
40257
+ }
40258
+ }
40225
40259
  }
40226
40260
  if (op.op === "<" && left_decl?.upper_bound_inclusive_exprs?.length && target_str && left_decl.upper_bound_inclusive_exprs.includes(target_str)) return "unsafe";
40227
40261
  if (left_decl?.upper_bound_expr && target_str) {
@@ -40531,6 +40565,7 @@ function is_nonnegative_access(node, status) {
40531
40565
  if (m) {
40532
40566
  const field_path = m[1];
40533
40567
  const offset_str = m[2];
40568
+ if (!field_path.includes(".")) return false;
40534
40569
  const last = field_path.split(".").at(-1);
40535
40570
  if (last && NON_NEGATIVE_FIELDS.has(last)) {
40536
40571
  if (!offset_str) return true;
@@ -40738,6 +40773,30 @@ function materialize_type(type, status) {
40738
40773
  //#endregion
40739
40774
  //#region ../src/check/utils/view_fields.ts
40740
40775
  /**
40776
+ * Whether the named struct (or any value struct embedded in its fields,
40777
+ * recursively) declares a `view T` field. A struct with view fields carries
40778
+ * non-owning borrows inside its bytes, which drives the borrow rules:
40779
+ * copies are sound (a pair copy aliases nothing owned), but escapes
40780
+ * (returning, outer-scope assignment) are checked against where those
40781
+ * borrows were taken.
40782
+ */
40783
+ function struct_has_view_fields(type_name, status) {
40784
+ if (!type_name) return false;
40785
+ const struct = status.structs.find((s) => s.name === type_name && !s.is_simple_type);
40786
+ return !!struct && has_view_fields(struct, status, /* @__PURE__ */ new Set());
40787
+ }
40788
+ function has_view_fields(struct, status, visited) {
40789
+ if (visited.has(struct.name)) return false;
40790
+ visited.add(struct.name);
40791
+ for (const field of struct.fields) {
40792
+ if (field.type.is_view) return true;
40793
+ if (field.type.is_ref || field.type.is_array) continue;
40794
+ const field_struct = status.structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_class);
40795
+ if (field_struct && has_view_fields(field_struct, status, visited)) return true;
40796
+ }
40797
+ return false;
40798
+ }
40799
+ /**
40741
40800
  * The root variable an access chain bottoms out at (`line.text` → "line",
40742
40801
  * `a.b.c` → "a", bare `x` → "x"). Returns undefined when the chain does not
40743
40802
  * bottom out in a plain name (e.g. a call receiver).
@@ -40859,6 +40918,23 @@ function view_borrows_root_at_self(sv) {
40859
40918
  return !!owners?.size && [...owners].every((o) => o === "self");
40860
40919
  }
40861
40920
  /**
40921
+ * Whether a value of this type can carry a non-owning view borrow: the type
40922
+ * itself is a `view`, a `ref` (a borrow by definition — keep the existing
40923
+ * taint), a struct declaring (transitively) `view T` fields, or a generic
40924
+ * instantiation with such an argument. Anything else (primitives, owned
40925
+ * strings, containers of owned values) owns its storage outright, so a call
40926
+ * producing it does not propagate its view arguments' borrows — tainting it
40927
+ * is a false positive (e.g. returning `slice_string(view)` from a function
40928
+ * returning owned `string`). Callers pass an unset/unknown type through as
40929
+ * carrying, preserving today's behavior where nothing is known.
40930
+ */
40931
+ function type_can_carry_view_borrow(type, status) {
40932
+ if (!type || !type.name) return true;
40933
+ if (type.is_view || type.is_ref) return true;
40934
+ if (struct_has_view_fields(type.name, status)) return true;
40935
+ return (type.type_args ?? []).some((t) => type_can_carry_view_borrow(t, status));
40936
+ }
40937
+ /**
40862
40938
  * Whether reading view fields of `root` is currently rejected because their
40863
40939
  * source was mutated (reassigned / ref-mutated).
40864
40940
  */
@@ -40983,7 +41059,7 @@ function check_declaration_node(decl, status) {
40983
41059
  if (type_from_value_node(decl.value, status)?.is_const_ref) decl.type.is_const_ref = true;
40984
41060
  }
40985
41061
  let view_borrows;
40986
- if (decl.value?.node_type === "func_call") {
41062
+ if (decl.value?.node_type === "func_call" && type_can_carry_view_borrow(decl.type, status)) {
40987
41063
  view_borrows = ctor_call_view_borrow(decl.value, status);
40988
41064
  if (view_borrows?.size) for (const [, info] of view_borrows) {
40989
41065
  const depth = info.depth ?? status.scope_depth;
@@ -43904,7 +43980,7 @@ function check_function_call(node, status, func, target_type, self_value, self_p
43904
43980
  lower_bound_inclusive_exprs = decl.lower_bound_inclusive_exprs;
43905
43981
  upper_bound_expr = decl.upper_bound_expr;
43906
43982
  lower_bound_expr = decl.lower_bound_expr;
43907
- alias_of = decl.alias_of;
43983
+ alias_of = decl.alias_of ?? expr_to_string(param, status);
43908
43984
  }
43909
43985
  } else if (param.node_type === "op") {
43910
43986
  const pop = param;
@@ -45189,7 +45265,7 @@ function check_assignment_node(assign, status) {
45189
45265
  const src_name = value_from_value_node(assign.right_value);
45190
45266
  const src_sv = assign.right_value.node_type === "value" ? status.values.findLast((v) => v.name === src_name) : void 0;
45191
45267
  if (src_sv?.has_view_borrows) propagate_view_borrows(left_value, src_sv);
45192
- } else if (assign.right_value.node_type === "func_call" && type_from_value_node(assign.right_value, status)?.name) {
45268
+ } else if (assign.right_value.node_type === "func_call" && type_from_value_node(assign.right_value, status)?.name && type_can_carry_view_borrow(type_from_value_node(assign.right_value, status), status)) {
45193
45269
  const infos = ctor_call_view_borrow(assign.right_value, status);
45194
45270
  if (infos?.size) {
45195
45271
  for (const [, info] of infos) {
@@ -46031,7 +46107,7 @@ function check_return_node(ret, status) {
46031
46107
  if (!safe_view_from_self && !explicit_mov) add_error(status, `cannot return a borrowed reference — use 'move' (with swap) to transfer ownership`, ret.value.start);
46032
46108
  }
46033
46109
  } else if (func && ret.value.node_type === "func_call") {
46034
- const infos = ctor_call_view_borrow(ret.value, status);
46110
+ const infos = type_can_carry_view_borrow(ret.type, status) ? ctor_call_view_borrow(ret.value, status) : void 0;
46035
46111
  if (infos?.size && ![...infos.keys()].every((o) => o === "self")) add_error(status, `cannot return '${ret.type.name}' — its 'view' field(s) borrow from this scope`, ret.value.start);
46036
46112
  }
46037
46113
  if (func) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nomen-lang",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "The CLI for the Nomen programming language.",
5
5
  "keywords": [],
6
6
  "license": "ISC",