You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
lean: textual values, as a draft spec, the model, and parse vectors - #785
Drafts the first section of a new Candid spec in lean/Spec/TextValues.md, covering the textual value syntax. It is a separate document rather than an edit to spec/Candid.md, and is meant as an example of how the new spec is organized and worded. It will become a Verso chapter later.
Until it replaces Candid.md under the conditions of REWRITE.md §6, Candid.md governs wherever the two disagree.
Shape
It follows RFC and Wasm-spec conventions:
Status, abstract, and the BCP 14 paragraph on requirement keywords.
Grammar in ABNF over Unicode scalar values, with case-sensitive strings.
Two steps, as in Wasm's text format: parsing text into an expression (§3–4), then elaborating the expression at a type, e ⇐ t ⇝ v (§5).
Sections on conformance and security considerations, and normative and informative references.
Appendices: the textual form of principals (A), differences from Candid.md and candid 0.10 (B), and examples (C).
What it decides
Candid.md gives the value grammar but says only that types are "known from context", so the meaning of text at a type is new here. Most rules agree with candid 0.10, such as ignoring extra record fields and filling in missing optional fields and arguments. Appendix B.2 lists where they differ. Points for review:
Annotations.(e : s) is accepted only where s is the same type as the expected one, which rejects (5 : nat) at int. The alternative, any subtype followed by coercion, is described in B.3.
Literals. A float literal is rounded once, directly to the target width. A sign must be attached to its number. Underscores follow the grammar, and hex floats are accepted.
Text literals cannot contain raw control characters.
The model
lean/Candid/Text/ implements the draft: a lexer, a parser into the document's abstract syntax, and elaboration, one function per rule. It also needed:
type equivalence (Candid/Equiv.lean), for annotations. It reuses the subtype procedure's worklist and its termination measure.
the textual form of principals (Candid/Principal.lean): CRC-32 and base32.
float literals rounded once and exactly (Candid/Float.lean), deciding an extreme exponent like 1e999999999 before multiplying anything out.
Vectors
A new vector kind, parse: text, its types, and the values or a rejection.
86 hand-written vectors follow the document section by section, with expectations written from it.
Every assertion of test/ is now checked. The 7 with text on the left are parse vectors, which completes the "all 471" condition for deleting test/. Text on the right of == is read by the model too, and the oracle checks that the model reads every text in test/ as candid_parser does. It does.
The candid-0.10 runner asks parse vectors through candid_parser. It reports 14,465 agreeing, and 17 new known divergences, each a row of Appendix B.2.
Bugs found in candid 0.10 (not fixed here)
annotate_types panics when there are more arguments than types: (1, 2) at (nat). This is the path candid_parser's own test runner takes.
0X10 panics, even in a release build.
record { 4294967295 = 1; 2 } panics in a debug build. In a release build the second field silently gets id 0.
The text lexer builds a String that may not be UTF-8 before checking it.
A first section of the new spec, written as a separate document rather
than an edit to spec/Candid.md, in RFC/Wasm-spec style: BCP 14 keywords,
ABNF over Unicode scalar values, and elaboration rules `e ⇐ t ⇝ v` that
give text its meaning at a type, which Candid.md leaves to "context".
Appendix B lists where the draft decides what Candid.md leaves open and
where it differs from candid 0.10.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The specification cannot parse function-type annotations or hexadecimal labels as intended, and its text elaboration rule omits the UTF-8 decoding needed to produce Unicode scalar values.
Adds a standalone draft specification for Candid textual values, intended to become a Verso chapter. The existing spec/Candid.md remains authoritative until the replacement conditions are met.
Changes:
Defines lexical syntax, expressions, and type-directed elaboration.
Documents conformance, security considerations, and principal text encoding.
Records differences from candid 0.10, open decisions, and examples; parser implementation and conformance vectors remain future work.
File
Description
lean/Spec/TextValues.md
Introduces the draft textual-value specification, compatibility notes, and examples.
Add `->` to the tokens, since annotations can contain function types.
Make numeric labels single number tokens. Decode a text literal's bytes
before they become a text value. Limit the ban on raw control characters
to C0 and DEL, as the grammar and Wasm already do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Implements lean/Spec/TextValues.md in the model (Candid/Text): a lexer,
a parser into the document's abstract syntax, and elaboration at a type.
It needs type equivalence (Candid/Equiv.lean) for annotations, the
textual form of principals (Candid/Principal.lean), and float literals
rounded once and exactly (Candid/Float.lean).
A new vector kind, `parse`: text, its types, and the values or a
rejection. 86 hand-written ones follow the document section by section.
Every assertion of test/ is now checked: the 7 with text on the left are
parse vectors, and the model reads every text in test/ as candid_parser
does.
The candid-0.10 runner asks parse vectors through candid_parser, and
lists the 17 where 0.10 differs as known divergences, each in the
document's Appendix B.2. Three are panics in 0.10, recorded in the
conformance README's findings, including a new one: annotate_types
panics when there are more arguments than types.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lwshang
changed the title
lean: draft spec for textual values
lean: textual values, as a draft spec, the model, and parse vectors
Nov 11, 2026
A name in a function type's parameter or result list must be UTF-8 and
must not repeat within its list. The parser dropped names before
checking either. Three vectors, which candid 0.10 agrees with.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Missing tests for floating-point rounding boundaries and recursive-type equivalence leave subnormal, tie, carry, and cycle-handling behavior unverified.
No open issues 🎉
2 previously missed issues
In code that hasn't changed since last review
Test recursive type equivalence and mismatching fields
lean/Candid/Equiv.lean:65
All current calls to decEquiv come through annotations, whose flattened type tables are acyclic trees. Consequently, the repeated-reference shortcut here is not exercised by the oracle, despite recursive-type equivalence being part of this procedure's contract. Please add direct checks for an equivalent one-node/two-node cycle and for recursive types with a mismatching nonrecursive field; these distinguish correct coinductive comparison from accepting a cycle before checking its remaining obligations.
Add rounding tests for subnormal and halfway boundaries
lean/Candid/Float.lean:51
The new rounding routine has no parse vectors for a nonzero subnormal, an exact halfway tie, or a carry from the largest subnormal to the smallest normal. The existing tiny-literal case only produces zero, and binary decoding vectors bypass this routine. Please add independent bit-pattern expectations for these boundaries in both formats—for example, 0x1p-149 and 0x1p-150 at float32, plus the corresponding float64 cases—so these rules are checked before their results become conformance expectations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Drafts the first section of a new Candid spec in
lean/Spec/TextValues.md, covering the textual value syntax. It is a separate document rather than an edit tospec/Candid.md, and is meant as an example of how the new spec is organized and worded. It will become a Verso chapter later.Until it replaces Candid.md under the conditions of REWRITE.md §6, Candid.md governs wherever the two disagree.
Shape
It follows RFC and Wasm-spec conventions:
e ⇐ t ⇝ v(§5).What it decides
Candid.md gives the value grammar but says only that types are "known from context", so the meaning of text at a type is new here. Most rules agree with candid 0.10, such as ignoring extra record fields and filling in missing optional fields and arguments. Appendix B.2 lists where they differ. Points for review:
(e : s)is accepted only wheresis the same type as the expected one, which rejects(5 : nat)atint. The alternative, any subtype followed by coercion, is described in B.3.The model
lean/Candid/Text/implements the draft: a lexer, a parser into the document's abstract syntax, and elaboration, one function per rule. It also needed:Candid/Equiv.lean), for annotations. It reuses the subtype procedure's worklist and its termination measure.Candid/Principal.lean): CRC-32 and base32.Candid/Float.lean), deciding an extreme exponent like1e999999999before multiplying anything out.Vectors
A new vector kind,
parse: text, its types, and the values or a rejection.test/is now checked. The 7 with text on the left areparsevectors, which completes the "all 471" condition for deletingtest/. Text on the right of==is read by the model too, and the oracle checks that the model reads every text intest/ascandid_parserdoes. It does.parsevectors throughcandid_parser. It reports 14,465 agreeing, and 17 new known divergences, each a row of Appendix B.2.Bugs found in candid 0.10 (not fixed here)
annotate_typespanics when there are more arguments than types:(1, 2)at(nat). This is the pathcandid_parser's own test runner takes.0X10panics, even in a release build.record { 4294967295 = 1; 2 }panics in a debug build. In a release build the second field silently gets id 0.Stringthat may not be UTF-8 before checking it.🤖 Generated with Claude Code