Issue #906: Specialising a function per build-time value: comptime?, @unroll, branches decided while building (ADR-specialization-1)

Open and answer this issue on GitHub.

ready · opened 2026-10-28 · updated 2026-10-31 · 2 comments

What this is about

Nikaia has no macro system (Part II 10.1, ADR-304 D1); code that depends on something known while the program is built is ordinary code that runs earlier. Until now that produced code in one place: a loop over T::fields, unrolled once per type (ADR-304). A loop over a build-time value is a run-time loop over a constant table (for x in TABLE.iter(), checked on 0.0.1225). A library that wants code shaped by a value - a regular expression’s automaton (#673), a route pattern, a layout - had no way to say so short of a special case in the compiler.

History

Decision

ADR-specialization-1; specification Part I 4.6 (comptime?, computed result type), Part II 10.3 (@unroll, decided branches), Part III (NK1304, NK1305).

To build

  1. ADR-333 D3 (a comptime parameter of a function, emitted once per value): evaluating a value per copy while building is the part this record shares with it.
  2. Measurement (owed before step 5 is shaped): hand-written Rust in the form the lowering would emit - a loop over bytes with an if s == k chain per state - against the same DFA as a table loop and against regex-automata’s dense DFA; callgrind instruction counts, same output first. If the chain does not become a jump table, match arms written from a list become a question of their own (ADR-specialization-1 §4).
  3. comptime? on parameters and self: copies per known value keyed by the value, the function as written for the rest; the ledger carries the mark across packages; NK1305 for such a parameter where only a build-time value is allowed.
  4. Branches decided while building (D4): the checker records the arm taken per copy and turn, the emitter writes only it.
  5. @unroll before a for (the first attribute before a statement): the list evaluated per copy, each turn a block with the element bound as a constant, continue/break through labelled Rust blocks, nested unrolling, NK1304; @unroll(up_to: N). The checker’s and emitter’s machinery for T::fields (facts.unrolled, check_program_turns, walk.specialising, emit_stmt_for) generalised from types to values, not duplicated.
  6. D7, a result type computed from comptime parameters.
  7. The report (D8) in --comptime.
  8. Tests in Nikaia for each: an unrolled loop, a nested one, continue/break in a turn, a message naming the turn, NK1152 past the budget, NK1304, NK1305, both copies of a comptime? function, a decided match, a computed result type.

Then #673 step 7 builds on it. The commit that builds the last step says Fixes #906.

Comments

keywan-ghadami, 2026-10-28

Owner, 2026-10-28: Q1-Q4 decided as recommended - comptime for over a build-time value; an if on a build-time condition decided while building, without a word; per-turn checking and messages as ADR-304 D7-D8; each turn counts against ADR-321’s budget (NK1152). Q5 (how a build-time value reaches a method) is open: the options need to be laid out first. The measurement (unrolled if chain against a table loop) is still owed; whether code can be slower than a table is being explained to the owner.


Generated by Claude Code

keywan-ghadami, 2026-10-31

Probe for step 1 (tools lane, j3 report 2026-10-31): a comptime value parameter does not parse at all today.

fn twice(comptime n: i64) -> i64 {
	return n * 2
}

fn main() {
	println(f"{twice(3)}")
}

Today: “comptime is a reserved word, so it can’t be used as a name here” at the parameter. Part I (Decimal[comptime precision: i64 …], get(self, comptime path: String, …)) and Part II 10.3 (comptime f: meta::Field means the same as f: meta::Field) write it. The tools lane does not need it for #1019 now (a meta::Field parameter is comptime by its type); the reflected-field build-time evaluation is #1087.


Generated by Claude Code

Back to Open Issues