ref int gap in NK1257, migrate, then refuseOpen and answer this issue on GitHub.
ready · opened 2026-10-18 · updated 2026-11-03 · 7 comments
The owner asked whether ref is still used without a warning. Checked against today’s compiler (727bb97):
What is decided
ref String outside extern "C" / pub extern fn is warning NK1261 (ref is redundant here, the ledger records it). It becomes a refusal once the specification, examples/ and tools/*.nika carry none.ref on a word (a number, bool, scalar, a data-less enum) is refusal NK1257 at every position.ref T on other types (structs, Vec, Expr) stays the written assertion of a view (ADR-184 D1). It is not part of this issue.What the compiler does
NK1261 is not built: fn s(t: ref String) passes nikaia check, run and build without any warning. No code mentions NK1261.NK1257 misses the int alias: fn s(t: ref int) passes silently, while fn s(t: ref i64) is refused.Count of ref String, still written:
| Where | Count |
|---|---|
tools/*.nika |
1961 |
examples/ |
289 |
| Part I, Part II, Part III | 36, 6, 19 |
benches/ |
8 |
scripts/ |
2 |
guide/ |
1 |
NK1261 as a warning on a parameter, a field, a result and a let, not inside extern "C" or a pub extern fn. Tests, including that the program’s meaning is unchanged.NK1257 on ref int, and on any alias that resolves to a word. Test.examples/, benches/, scripts/ and the specification’s code blocks: drop ref before String outside the C boundary. Regenerate tests/specification/EXPECTED.txt.tools/*.nika. package.rs must lower to the same Rust: &str stays where a view suffices (ADR-282 D2). Any difference is a defect to fix in the compiler first. Work one file at a time, with lower-std and the sysroot test.ref String are rewritten.NK1261 becomes a refusal (ADR-282 D2’s own condition), with a test, and Part III’s text for it changes.Generated by Claude Code
First round (7529675): benches/page and scripts/tokens.nika dropped ref. Both lower to byte-identical Rust.
Step 4 is blocked: ADR-282 D2 (“a String is a view wherever a view suffices”) is not built for most positions. Dropping ref changes behaviour in five places, checked by comparing the lowered Rust, the ledgers and nikaia test output before and after:
; sep: String = " ", examples/tally) becomes an owned String, and every call writes String::from(..). The option path in emit/mod.rs (~3938) never asks lends_in_own.rule FIELD -> String = … { s }) is refused with NK1104. This hits access-log, config, json, k-nucleotide, report, inventory and 1brc.Hit.path, Section.name, Entry.item, Member.key);Row’s lifetimes and allocates once per row.Vec element (HashMap[ref String, …], benches/brc/naive): TrustedMap<&str> becomes TrustedMap<String>.views = ["x: borrowed"] (escaping, json, k-nucleotide);report_fragment(dna, fragment: String) becomes owned, and the generated Rust then fails rustc (&str: Borrow<String>);read(data: String) -> Vec[Entry] is NK2302;fn path(ref self) -> String returning a view of self is NK1104 (examples/http, which already fails at HEAD).Part I §6 (about lines 2476-2660) also states rules in terms of “a parameter written ref String” (NK2302). Its text changes with these fixes.
String that only views reach is a view at every position. Done means the programs above, with ref dropped, lower to the same Rust as today.types_text_by_ref beside types_word_by_ref in types.nika, skipped for is_extern, plus Stmt::Let). This waits until tools/ is free."int" in NK1257’s word list.EXPECTED.txt.tools/*.nika.NK1261 becomes a refusal.Generated by Claude Code
State after 0.0.1022 (c202263; commits 36d4ec9, 9634840, 7d51315, 7973244):
Built
String map key no longer reads as kept by Locked::get(self) / SharedMut::get(self) (a candidate without the argument’s position does not count); rustc no longer refuses &str: Borrow<String>.-> String, an action-filled field are views; no more NK1104/NK1106), a parameter handed to a parse is a view (NK2302 gone), a std method whose every entry returns ref String is a view, entry(k) and let (a, b) = s.split_at(..) give views.String parameter is listed in views as borrowed.ref int is NK1257.ref-free twins of examples inventory, access-log, config, report, json and benches/brc/naive lower to the same Rust as with ref String. Tests: crates/nikaia/tests/text_views.rs, ref_int_is_refused_as_a_word.Still to build
; sep: String = " "): options are not in the keeps walk. Adding them puts option names into every ledger’s keeps; build so that an option appears in the ledger only when it is kept, and cover the emitter and check_arguments.String parameter handed on gets a & the ref version does not write (k-nucleotide: count(&dna, k), get(&fragment)). Same meaning, different text; bind a lent String parameter as lent where parameters are bound (check/mod.rs). Test: the k-nucleotide twin lowers byte-identical.ref String outside extern "C"), with the test outputs and tests/specification/EXPECTED.txt it moves.Needs the owner (status stays ready for 1-4; this one is asked separately)
Row): ADR-282 D13 says “a literal alone moves nothing”, so such a position stays String by design. Either literal-only positions become views (fields, results and lets gain lifetimes), or D13 stands and these fields stay owned.Generated by Claude Code
Where #654 stands after 0.0.1026 (455b697, 6f39ef0).
Built: step 0 c1. A String option the body only reads is lent the way a parameter is. Options are now part of the keeps analysis. An owned String passed to a ref String option is lent instead of refused with NK1106.
Open:
&. The same happens for options: inner(n, &sep).-> String result that returns a view of ref self is refused with NK1131 or NK1104 instead of becoming a view result.nk1261-step1.patch, covering types.nika, the lowered package.rs, two tests and EXPECTED.txt.
ref String.ref from them would change what some of the tests mean.ref_string_outside_the_c_boundary_is_a_warning also fails in its run step, and the cause is not known yet.Generated by Claude Code
Item 2 built in 5e503c1, version 0.0.1047 (6894a71).
String parameter that is not mut, not nullable and that the emitter lowers as &str (lends_in_own) is bound in the checker as a written ref String is; a lent String option gets the type lowered_options gives it. So count(dna, k), counts.get(fragment), inner(sep, n), opt(n; sep: sep) and net::listen(at) lower exactly like their ref String twins.only_read block (87caa23 ff.), which already covers it; no second rule was added.ref on its parameters lowers byte-identical; a lent option and a reading parameter handed on; examples/http with every ref String dropped except two method parameters lowers byte-identical (item 3).Still open
ref String dropped from examples/http, the one difference left is pub fn query(&self, name: String) (and header) instead of &str: lends_with in lends.nika never lends a method’s inferred parameter, which ADR-288 defers. Lowering it as a view is not mechanical - such a method keeps its argument today (a String used after r.has(o) is NK2105), and trait implementations and generic calls must agree on the type. Needs ADR-288’s question decided, or its own step; until then a method parameter that is only read still needs ref String to be a view.Generated by Claude Code
Step 1 (NK1261): built but not committed - it fires on plain String.
Why. parse_to_ast runs text_tiers::refine (parser/mod.rs ~886); where it decides a view, mark() (text_tiers.rs ~245) sets ty.is_view = true on a field, result or let written as plain String. The checker sees the AST after that, so struct User { name: String } and let nobody: String = "nobody" get NK1261 as if ref were written. Parameters are not rewritten, which is why the first tests missed it.
Direction taken (supervisor): refine already records what it decided on Parsed (parsed.text_tiers); types.nika skips the positions refine turned into views, so NK1261 fires only where the source wrote ref. Rejected: a flag on Type (changes PartialEq between a written ref String and a decided view, ~15 literals), and computing NK1261 before refine (a second walk over the same declarations).
Saved work: scratchpad nk1261-step1-v2.patch - types.nika re-applied, package.rs regenerated, the earlier test fixed (its run step failed on its own program: two text inputs and one view result is rustc’s “missing lifetime specifier”; split into join(..) -> String and first(a) -> ref String), and the findings helpers of 25 test files filtering NK1261. Still owed after the fix: tests/specification/EXPECTED.txt (cargo run -p nikaia --example specification), project:2247 and option_defaults:435 (the warning in their output), comptime_compiled:448. Part III’s code table already words NK1261. lower-std refuses only on errors, so warnings in tools/ do not block it.
Found on the way, develop red since 5e503c1: list_repeat::a_value_that_does_not_copy_is_refused ([name; 3] does not count as keeping name) and views_handed_back::following_nk1104s_help_is_a_program; a fix is being built.
Generated by Claude Code
Step 1 built in c43c96b, version 0.0.1052 (1a6c53c): NK1261 is a warning on ref String written on a parameter, option, field, result or let outside extern "C" / pub extern fn. The tier pass records the positions it made a view (Parsed::text_views, keyed field T f, result f, result T::m, param f 0, let <byte>), and NK1261 skips them, so plain String never warns. Test helpers in 25 files filter NK1261; the repository-wide checks in typecheck and sequences no longer count warnings; EXPECTED.txt gains 5 NK1261 tags.
Also: develop was red from 5e503c1 - fixed in 7cca0ee (0.0.1050): [x; n] keeps x; and fn e(t: String) -> ref String { return t } is a program now (the parameter the result points into is lent, D5), so NK1104’s help test returns a local instead.
Warns now, for step 4/5: crates/nikaia-std/src/text.nika (parse_i64 …), examples/1brc.nika, examples/access-log and others; the specification’s own examples Part I #4, #9 (let mut maybe_string: ref String? = null), #78, #80 and Part III #25.
Generated by Claude Code
Step 4 built: examples (0298543), benches (c6a95f1), the specification’s examples (99354b1, 0.0.1053). Every ref String whose removal lowers byte-identical is gone: fields, rule results, parameters, ref self results (also String?), Vec[ref String], options. escaping and benches/template Row now lower to &'static str (#659, same output).
Kept with ref, each a gap with its own issue:
query/header): ADR-288 defers it - a decision still to prepare;Pair/pair/longest): #678;serve(at): the loose corpus check keeps a parameter captured for an unknown callee: #679;Text(ref String), a variant payload: #680;pub extern fn: the C boundary, by rule.Still open: Part I §6’s prose on NK2302 (“a parameter written ref String may not be kept past its call”) and Part III’s NK1106 text are about ref String semantics and need rewording when step 6 makes ref String a refusal; steps 4 (tools/*.nika, std .nika) and 6.
Generated by Claude Code