nomen-lang 0.0.18 → 0.0.19

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.
@@ -30,7 +30,7 @@ pub struct Array<T>: Viewable {
30
30
  ```
31
31
  }
32
32
 
33
- pub func at = (self, int index: index >= 0 && index < self.length, out T) {
33
+ pub inline func at = (self, int index: index >= 0 && index < self.length, out T) {
34
34
  ```
35
35
  #arch: c
36
36
  T* _data = (T*)((char*)self + sizeof(*self));
package/dist/index.mjs CHANGED
@@ -2750,6 +2750,20 @@ function is_container_borrow_access(node) {
2750
2750
  return !!node && node.node_type === "access" && node.access?.node_type === "access_func" && !node.access.owned_return && is_container_borrow_accessor_name(node.access.name);
2751
2751
  }
2752
2752
  /**
2753
+ * Whether `target` (the receiver of a `.to_string()` call) is a `view string`
2754
+ * VALUE — a named view local/param (the checker stamps variable references
2755
+ * with the declared type, and substitute_type preserves `is_view` across
2756
+ * monomorphization) or an inline `.slice(...)` chain (a slice call always
2757
+ * produces a view, even when its cached type lost the modifier). Such a call
2758
+ * MATERIALIZES a fresh owned heap copy (malloc + memcpy), unlike
2759
+ * `to_string` on an owned string (the `string_to_string` identity).
2760
+ */
2761
+ function is_view_materialization_target(target) {
2762
+ if (!target || typeof target !== "object") return false;
2763
+ if (target.type?.is_view) return true;
2764
+ return target.node_type === "access" && target.access?.node_type === "access_func" && !target.access.owned_return && target.access.name === "slice";
2765
+ }
2766
+ /**
2753
2767
  * Whether a function has any `return <expr>` statement in its body (used by
2754
2768
  * the C backend's gather, which registers every string-returning function
2755
2769
  * that actually returns — the backend strdup's borrows at the return site,
@@ -2858,6 +2872,7 @@ function value_is_owned_string(v, table, visiting, borrow_names, enclosing_fn) {
2858
2872
  if (mangled.startsWith("_string_interpolate_")) return true;
2859
2873
  if (mangled.endsWith("_to_string") && mangled !== "string_to_string") return true;
2860
2874
  if (raw === "to_string" && v.target?.type?.name && v.target.type.name !== "string") return true;
2875
+ if (raw === "to_string" && is_view_materialization_target(v.target)) return true;
2861
2876
  if (!v.access.owned_return && is_container_borrow_accessor_name(raw)) return !is_call_site_borrow_accessor(enclosing_fn);
2862
2877
  const resolved = method_call_returns_owned(v, table, visiting);
2863
2878
  if (resolved !== void 0) return resolved;
@@ -9569,6 +9584,7 @@ function is_owned_heap_temp$1(node, status) {
9569
9584
  if (mangled.startsWith("_string_interpolate_")) return true;
9570
9585
  if (mangled.endsWith("_to_string") && mangled !== "string_to_string") return true;
9571
9586
  if (raw_name === "to_string" && target_type_name && target_type_name !== "string") return true;
9587
+ if (raw_name === "to_string" && status && node.node_type === "access" && is_view_value$1(node.target, status)) return true;
9572
9588
  const heap_set = status?.heap_returning_functions;
9573
9589
  if (heap_set?.has(mangled)) return true;
9574
9590
  if (heap_set && target_value && heap_set.has(`${target_value}_${raw_name}`)) return true;
@@ -12237,10 +12253,17 @@ function build_naked_inline(struct_node, func, status) {
12237
12253
  let asm = extract_aarch64_asm(func, status.platform);
12238
12254
  const standalone_return_label = `.return_${struct_node.name}_${func.name.replace(/#/g, "")}`;
12239
12255
  asm = asm.replaceAll(`b ${standalone_return_label}`, "");
12240
- if (count_x19_reads(asm) === 1) asm = asm.replace(/\bx19\b/g, "x0");
12256
+ const x19_reads = count_x19_reads(asm);
12257
+ let prologue = "";
12258
+ let epilogue = "";
12259
+ if (x19_reads === 1) asm = asm.replace(/\bx19\b/g, "x0");
12260
+ else if (x19_reads > 1) {
12261
+ prologue = `str x19, [sp, #-16]!\nmov x19, x0\n`;
12262
+ epilogue = `ldr x19, [sp], #16\n`;
12263
+ }
12241
12264
  const site = inline_counter++;
12242
- asm = asm.replace(/(\.L[A-Za-z0-9_]+)/g, `$1_${site}`);
12243
- status.code += asm + "\n";
12265
+ asm = asm.replace(/(\.L[A-Za-z0-9_]+)/g, `$1__ni${site}`);
12266
+ status.code += prologue + asm + "\n" + epilogue;
12244
12267
  }
12245
12268
  function build_inline_method(struct_node, func, status) {
12246
12269
  if (emit_owning_buffer_inline_aarch64(struct_node, func.name, status)) return;
@@ -12688,6 +12711,14 @@ function emit_string_length$1(target, status) {
12688
12711
  * struct element, with x0 = temp address)
12689
12712
  * v.to_string() → malloc(len+1); memcpy; null-terminate; owned copy in x0
12690
12713
  * (string views only)
12714
+ * The receiver is either a NAMED view local/param (pair in its two stack
12715
+ * slots) or an INLINE view-producing expression such as
12716
+ * `text.slice(start, end).to_string()` — the expression leaves the pair in
12717
+ * x0/x1, which is spilled to scratch slots first. Without this, the inline
12718
+ * chain falls through to struct-method dispatch and resolves to
12719
+ * `string_to_string` (an identity `mov x0, x19`), handing consumers the raw
12720
+ * slice pointer — not NUL-terminated at len — and strlen-based readers run
12721
+ * past the slice.
12691
12722
  * Returns true if handled (caller skips struct-method dispatch).
12692
12723
  */
12693
12724
  function build_view_op(node, access_func, status) {
@@ -12696,11 +12727,19 @@ function build_view_op(node, access_func, status) {
12696
12727
  const vt = status.variable_types?.get(node.target.value);
12697
12728
  if (vt?.is_view) t = vt;
12698
12729
  }
12699
- if (!t?.is_view) return false;
12700
- if (node.target.node_type !== "value") return false;
12701
- const base = status.stack_offsets?.get(node.target.value);
12702
- if (base === void 0) return false;
12703
- const elem_name = t.name === "string" ? "char" : t.name;
12730
+ if (!t?.is_view && !is_view_value$1(node.target, status)) return false;
12731
+ let base;
12732
+ if (node.target.node_type === "value") base = status.stack_offsets?.get(node.target.value);
12733
+ if (base === void 0) {
12734
+ build_node$1(node.target, status);
12735
+ if (!status.code.endsWith("\n")) status.code += "\n";
12736
+ const temp_base = allocate_stack_space(status, 16, 16);
12737
+ status.code += `str x0, [x29, #${temp_base}]\n`;
12738
+ status.code += `str x1, [x29, #${temp_base + 8}]\n`;
12739
+ base = temp_base;
12740
+ }
12741
+ const view_elem = t?.name || "string";
12742
+ const elem_name = view_elem === "string" ? "char" : view_elem;
12704
12743
  if (access_func.name === "at" && access_func.params.length === 1) {
12705
12744
  build_node$1(access_func.params[0], status);
12706
12745
  if (!status.code.endsWith("\n")) status.code += "\n";
@@ -12726,7 +12765,7 @@ function build_view_op(node, access_func, status) {
12726
12765
  else status.code += `ldr x0, [x0, x1, lsl #3]\n`;
12727
12766
  return true;
12728
12767
  }
12729
- if (access_func.name === "to_string" && t.name === "string") {
12768
+ if (access_func.name === "to_string" && view_elem === "string") {
12730
12769
  status.code += `ldr x0, [x29, #${base}]\n`;
12731
12770
  status.code += `ldr x1, [x29, #${base + 8}]\n`;
12732
12771
  emit_view_materialize_owned(status);
@@ -25034,6 +25073,29 @@ function clone_type(t) {
25034
25073
  c.type_args = t.type_args;
25035
25074
  return c;
25036
25075
  }
25076
+ /**
25077
+ * Strip parallel-length equality clauses (`a.length == b.length`) from every
25078
+ * parameter's constraint of `func` at SIGNATURE-GATHER time — before any call
25079
+ * site can observe the signature, regardless of declaration order. A
25080
+ * forward-referenced callee must not hand its caller a clause no call site
25081
+ * could ever prove; the clause becomes an assumed equality for the callee's
25082
+ * own body instead (stashed on the param, seeded into scope when its params
25083
+ * are checked). Idempotent: a constraint already stripped has no clauses
25084
+ * left to extract. `check_function_node` re-runs the same extraction at
25085
+ * registration for function clones that never pass through a gather pass
25086
+ * (generic monomorphizations).
25087
+ */
25088
+ function strip_param_length_equalities(func) {
25089
+ const param_names = new Set(func.params.map((p) => p.name));
25090
+ for (const param of func.params) {
25091
+ if (!param.constraint) continue;
25092
+ const { constraint, equalities } = extract_length_equalities_at_registration(param.constraint, param.name, param_names);
25093
+ if (equalities.length) {
25094
+ param.constraint = constraint;
25095
+ param.stripped_length_equalities = equalities;
25096
+ }
25097
+ }
25098
+ }
25037
25099
  function gather_structs(block, status) {
25038
25100
  const names_in_block = {
25039
25101
  structs: /* @__PURE__ */ new Set(),
@@ -25050,6 +25112,7 @@ function gather_structs(block, status) {
25050
25112
  names_in_block.structs.add(struct.name);
25051
25113
  struct.scope = status.stack.at(-1) || block;
25052
25114
  struct.is_generic = struct.type_params.length > 0;
25115
+ for (const func of struct.functions) strip_param_length_equalities(func);
25053
25116
  status.types.push(struct.name);
25054
25117
  status.structs.push(struct);
25055
25118
  }
@@ -25062,6 +25125,7 @@ function gather_structs(block, status) {
25062
25125
  names_in_block.traits.add(trait.name);
25063
25126
  status.types.push(trait.name);
25064
25127
  status.traits.push(trait);
25128
+ for (const func of trait.functions) strip_param_length_equalities(func);
25065
25129
  }
25066
25130
  break;
25067
25131
  }
@@ -25073,15 +25137,7 @@ function gather_structs(block, status) {
25073
25137
  func.scope = status.stack.at(-1) || block;
25074
25138
  func.return_type = materialize_type(func.return_type, status);
25075
25139
  instantiate_generic_type(func.return_type, status);
25076
- const param_names = new Set(func.params.map((p) => p.name));
25077
- for (const param of func.params) {
25078
- if (!param.constraint) continue;
25079
- const { constraint, equalities } = extract_length_equalities_at_registration(param.constraint, param.name, param_names);
25080
- if (equalities.length) {
25081
- param.constraint = constraint;
25082
- param.stripped_length_equalities = equalities;
25083
- }
25084
- }
25140
+ strip_param_length_equalities(func);
25085
25141
  status.functions.push(func);
25086
25142
  }
25087
25143
  break;
@@ -25147,7 +25203,10 @@ function apply_extensions(block, status) {
25147
25203
  continue;
25148
25204
  }
25149
25205
  ext.scope = target;
25150
- for (const func of ext.functions) target.functions.push(func);
25206
+ for (const func of ext.functions) {
25207
+ strip_param_length_equalities(func);
25208
+ target.functions.push(func);
25209
+ }
25151
25210
  for (let i = 0; i < ext.traits.length; i++) {
25152
25211
  if (target.traits.includes(ext.traits[i])) {
25153
25212
  add_error(status, `Type '${ext.name}' already conforms to trait '${ext.traits[i]}'`, ext.start);
@@ -27886,7 +27945,7 @@ function check_if_else_node(if_else, status) {
27886
27945
  const else_returns = if_else.else_branch && block_always_returns(if_else.else_branch);
27887
27946
  const if_falls_through = if_reachable && !if_returns;
27888
27947
  const else_falls_through = else_reachable && (!has_else || !else_returns);
27889
- if (!has_else && else_reachable) apply_negated_bounds(if_else.condition, status);
27948
+ if (!has_else && else_reachable && !if_returns) apply_negated_bounds(if_else.condition, status);
27890
27949
  for (let [i, value] of status.values.entries()) {
27891
27950
  const contributing_sets = [];
27892
27951
  if (if_falls_through) contributing_sets.push(!!if_status.values[i].is_set);
@@ -27940,6 +27999,7 @@ function check_if_else_node(if_else, status) {
27940
27999
  }
27941
28000
  }
27942
28001
  }
28002
+ if (!has_else && if_returns && else_reachable) apply_negated_bounds(if_else.condition, status);
27943
28003
  if (if_else.if_branch && !if_else.else_branch) {
27944
28004
  const parent = status.stack.at(-1);
27945
28005
  if (parent && (parent.node_type === "declare" || parent.node_type === "assign")) add_error(status, "If expression must have an else branch", if_else.start);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nomen-lang",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "description": "The CLI for the Nomen programming language.",
5
5
  "keywords": [],
6
6
  "license": "ISC",