Skip to content

Numeric conversion spec - #25

Open
ESRogs wants to merge 10 commits into
mainfrom
conversion-spec
Open

Numeric conversion spec#25
ESRogs wants to merge 10 commits into
mainfrom
conversion-spec

Conversation

@ESRogs

@ESRogs ESRogs commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Rebuild of roc-lang#10736 against conversion-spec-design.md, split into small commits. Iteration happens here; roc-lang#10736 gets reset to this once it settles.

The spec is the triple (src, dst, mode) parsed from each op name once, at comptime. There is no shared grouping enum: consumers route on the source and destination classes, then switch on the mode, splitting further where a handler assumes more than the spec says.

Five comptime rules run over the whole enum, so any build of the compiler checks them:

  1. an op named like a conversion either classifies or is listed as not one.
  2. rebuilding a name from its triple reproduces the original.
  3. every conversion occupies one of the sixteen class-and-mode combinations.
  4. every combination is occupied by some conversion.
  5. a string conversion's number type and direction rebuild its name.

Each rule was verified by breaking it and confirming the build fails naming the op.

All four consumers derive from the spec. The LLVM backend routes on it; dev, wasm, and the interpreter keep the op prongs ci/zig_lints.zig requires and drop the facts they restated. Three of those commits exist because a table did not merely repeat an op name but misnamed a field.

The last three commits extend the spec to a number's text and are removable as a unit.

Verification of the LLVM dispatch swap. No CLI flag emits LLVM IR, so test/fx/runtime_dec_to_int_wrap_widths.roc was compiled at --opt=speed on the commit before and after: byte-identical binaries, same SHA-256. That fixture drives all ten Dec-to-integer widths plus truncation and past-I64 wrapping.

Suites at f07d9db39b, compared against roc-lang/roc main 7f9b644e33. run-check-* passes. Eval on the interpreter, dev, and wasm executors is 1930/1930. Under --llvm 23 tests fail, and under run-test-cli 9 fail; both sets carry the same names on the baseline, so neither belongs to this branch. The --llvm baseline additionally hangs 10 tests that pass here, at a count that varies between runs on this machine, so that difference is not attributable either way.

@ESRogs
ESRogs force-pushed the conversion-spec branch 7 times, most recently from 3c49461 to f07d9db Compare August 13, 2026 21:00
Comment thread src/base/mod.zig Outdated
Comment thread src/base/mod.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
@ESRogs
ESRogs force-pushed the conversion-spec branch 3 times, most recently from 43eb2e4 to 835f938 Compare August 14, 2026 16:42
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig Outdated
Comment thread src/base/numeric_conversion.zig
Comment thread src/base/numeric_conversion.zig Outdated
@ESRogs
ESRogs force-pushed the conversion-spec branch 4 times, most recently from baedc47 to 5638336 Compare August 14, 2026 21:07
Comment thread src/base/numeric_conversion.zig Outdated

/// Ops whose names contain `_to_` but do not convert between two number types.
/// Listing one here is how it is excused from
/// `assertNothingConversionShapedEscapes`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we put it next to that assert then?

@ESRogs
ESRogs force-pushed the conversion-spec branch 12 times, most recently from 814bd0a to e490014 Compare August 18, 2026 21:40
ESRogs and others added 10 commits August 19, 2026 15:11
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The integer-to-Dec branch reads the source's signedness, which is what the
replaced table's target-signedness field held for those two ops.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The range bounds are computed where they are emitted, which is only for
destinations narrower than 64 bits; the replaced table stored unread
placeholders for the two 64-bit entries.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 214 arms named a helper and its type arguments per op. A router picks both
from the spec instead, so the switch keeps only the op names the lint requires.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Thirteen `*_to_str` ops leave the not-a-conversion list, so rule 0 now accounts
for them by what they are rather than by exclusion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`numericParseSpec` moves beside the parse it now reads, since `LowLevel` imports
nothing and importing the spec there would close a cycle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
}

/// Split a name such as `dec_to_u64_trunc` into its three parts. Returns null
/// unless both ends are a `NumType`, which is what separates a conversion from

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two options

/// Split a name into `source`, `"_to_"`, and `destination`. If either `source` or `destination` is not a `NumType`, it returns null.

or:

/// Match a name against `"${source}_to_${destination}`. If either `source` or `destination` is not a `NumType`, it returns null.

Though not sure if that's the right way to describe the template in zig. Not sure if fixable, or we should just go with the first option.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, maybe we should use src and dst rather than source and destination.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is "name" too generic? Like what kind of name is it? I think that's why I was suggesting the example. Also "parse" is pretty generic too.

/// Split a name such as `dec_to_str` into a number type and a direction.
/// Returns null unless the half beside `str` is a `NumType`, which is what
/// separates `dec_to_str` from `num_to_str`.
fn parseString(name: []const u8) ?StringConversion {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseString does not at all sound like it means "parse the name of a function that converts a numtype to a string."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant