Skip to content

fix: preserve resolved numeric values across declarations - #23

Closed
waywardmonkeys wants to merge 9 commits into
fix/mf2-conformancefrom
fix/mf2-numeric-values
Closed

waywardmonkeys wants to merge 9 commits into
fix/mf2-conformancefrom
fix/mf2-numeric-values

Conversation

@waywardmonkeys

@waywardmonkeys waywardmonkeys commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Numeric declarations previously lost their payload and options through formatting or repeated evaluation. Retain exact integers and decimals, inherit and override parsed options across annotations, and separate exact/plural selection from display. Use checked offset arithmetic.

Evaluate declarations eagerly, once per formatting execution, including bare :string inputs. This makes errors in unused declarations observable: .local $x = {$missing :number} {{hello}} outputs hello and reports MissingArg. Retain raw string text and direction metadata until output, so a later numeric annotation receives 1 rather than text containing bidi isolates.

Reuse the locals buffer and drop its values before returning on success or error. The catalog verifier rejects uninitialized local access on any reachable path and stores that skip slots, checking each message entry independently. Selector dispatch reads stored values by private slot index without cloning them.

When another selector rejects an exact numeric variant, retry plural/ordinal candidates before wildcard candidates. Validate each source selector once before dispatch, avoiding duplicate BadSelector diagnostics. Emit repeated fallback subtrees once and jump to their continuation while closing intervening select scopes.

Carry failed expressions as Value::Fallback through nested calls and aliases. Skip functions with failed operands without repeating their errors, and preserve each alias's fallback name. Correct declaration diagnostic spans to use document coordinates.

Host::call and Host::call_select gain a recoverable-error callback. New Value variants retain numeric, string, selector, and fallback state; render through Host::format_default to apply direction metadata. Catalogs must be recompiled for the new local/selector instructions. The runtime migration notes cover custom hosts, CatalogError::InvalidLocalSlot, and exhaustive runtime/compiler enum matches.

Current WG result: 415/462 passing, versus the corrected baseline of 404/462. The initial numeric implementation scored 416; the fallback correction exposes one additional mismatch in pattern-selection.json case 12, which expects a cascading bad-operand. The current formatting algorithm short-circuits function resolution for a fallback operand, so that additional error is no longer emitted. The fixture has not been modified.

Recoverable function option resolution is handled in #25. Arena storage and decimal/currency feature expansion remain separate work.

Stacked on #22.

Keep numeric payloads and effective options until output formatting so
`:number`, `:integer`, and `:offset` can inherit and override options
without parsing formatted text. Preserve exact integers and separate
numeric selection from display; use checked arithmetic for offsets.

Evaluate declarations once into per-format local slots. Retain failed
resolution metadata so selection reports `BadSelector` without repeating
the original diagnostic. Let `Host` callbacks report recoverable errors
while returning a usable value, and document the API migration.

Cover option inheritance, precision, exact and plural selection, dynamic
`select` errors, declaration evaluation counts, and local-slot validation.
Update conformance assertions to require exact diagnostic multisets.

The WG corpus passes 416 of 462 cases, up from 404 with the corrected
harness.
Declaration AST spans already contain document offsets. Avoid adding the
pattern body offset again when lowering declarations, so manifest errors
point at the actual function annotation.
Short-circuit function resolution when a stored operand is a fallback,
without repeating the original error or producing `BadOperand`.

Give each failed local its own fallback name when storing declarations,
so aliases render `{$a}` and `{$b}` independently. Preserve failure state
through `ExprFallback` and `StoreLocal`, then clear it before unrelated
expressions.
When a later selector rejects an exact numeric variant, try matching
plural or ordinal variants before falling back to wildcard candidates.
Reuse stored numeric values for both dispatch passes so declarations
still resolve once.

Apply the same dispatch to stored `:offset` results, and default raw
offset operands to plural selection while preserving inherited modes.
Cover variant ordering, exact precedence, and multi-selector offsets.
Keep inherited number formatting settings in their parsed form, with explicit
selection provenance, instead of cloning and reparsing an owned string map.
Compute plural operands directly from retained integers and decimals,
preserving visible precision and half-expand rounding.

Resolve integral floats within the exact integer range without allocating
text. Preserve negative zero and the existing representation of larger
floating-point inputs.
Validate each source selector before comparing variants. Keep generated
exact and plural dispatches free of repeated diagnostics, and compare
locals by private slot index instead of cloning their resolved values.
Verify both new local-selector instructions before execution.

Emit repeated default subtrees once and jump to their shared continuation,
closing intervening select scopes. Preserve exact-to-plural retry when a
later selector rejects an exact variant. Keep function metadata available
for manifest validation and document the catalog migration.
Resolve string declarations once, retaining raw text and direction metadata
until output. Later annotations consume the raw string instead of bidi
isolation characters. Compare resolved strings without numeric coercion
and let custom hosts observe string input resolution.

Carry failures as `Value::Fallback` through nested calls and local aliases.
Remove instruction-dependent failure flags so nested calls skip failed
operands even when option loads intervene. Preserve each alias fallback
name and keep independent expression diagnostics separate.

Document eager declaration evaluation and the resolved-string migration.
The WG corpus remains at 415/462 passing.
Measure repeated numeric locals, string-to-number reannotation, and a
multi-selector match that rejects an exact variant before using a plural
candidate. Reuse formatter state and resolved handles so setup is outside
the measured operation.
Enable `libm` for runtime-only ICU builds while retaining it for compiler-only builds. Keep compiler-only helpers out of runtime builds and satisfy Clippy across the no-std feature matrix.
@waywardmonkeys
waywardmonkeys deleted the fix/mf2-numeric-values branch September 16, 2026 02:59
@waywardmonkeys

Copy link
Copy Markdown
Contributor Author

Superseded by #26, which contains this change and the reviewed follow-up fixes in the consolidated stack.

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