Skip to content

Commit 34dc2de

Browse files
proof(xml-7-44): discharge Xml744.Attribute proof debts (#80)
## Summary Discharges the two `PROOF_TODO`-marked sites in `Xml744.Attribute` that blocked the toolkit's build on Idris2 0.8.0. 1. **`attrName`** used `decEq (isValidName s) True` — `decEq` for `Bool` is not in scope with the current imports. Replaced with `with (isValidName s) proof prf`, which binds `prf : isValidName s = True` directly in the matched branch and discharges `MkAttrName`'s auto-implicit. 2. **`unsafeAttrName`** was `cast (MkAttrName s)` — never compiled (no matching `Cast` instance; `MkAttrName` requires the validity proof to construct). Now takes the proof as an auto-implicit: `(s : String) -> {auto prf : isValidName s = True} -> AttrName`, so it can only be called on strings whose validity Idris2 can verify at elaboration time. `Refl` discharges for any literal — matching the existing doc "use only for known-good literals". ## Knock-on changes (still in `Xml744.Attribute`) - `wAttr` routed through `unsafeAttrName ("w:" ++ n)`. Signature bumped to `(n : String) -> {auto prf : isValidName ("w:" ++ n) = True} -> String -> XmlAttr` so callers' literal `n` lets the elaborator reduce both the concatenation and `isValidName` to `True`. All four existing call sites (`wAttr "id"`, `"author"`, `"date"`, `"initials"`) discharge automatically. - `isNameStartChar`, `isNameChar`, `isValidName` promoted from `export` to `public export` so cross-module callers see the definitions for elaboration-time reduction. ## Verification ``` $ idris2 -p contrib Xml744/Attribute.idr 1/3: Building Xml744.Escape (Xml744/Escape.idr) 2/3: Building Xml744.Text (Xml744/Text.idr) 3/3: Building Xml744.Attribute (Xml744/Attribute.idr) Xml744.Attribute> :total Xml744.Attribute.attrName --> is total Xml744.Attribute> :total Xml744.Attribute.unsafeAttrName --> is total Xml744.Attribute> :total Xml744.Attribute.wAttr --> is total [+ attr / renderAttr / getAttrValue / id / name / value — all total] ``` `grep -E 'assert_total|assert_smaller|believe_me|%unsafe|PROOF_TODO|idris_crash|partial ' src/Xml744/Attribute.idr` → no matches. Toolkit build now advances `3/6 → 4/6`. Element still red on its own parallel debts (see below). ## Out of scope — known follow-ups in `Xml744.Element` - `tagName` uses the same `decEq` form (fix: `with proof`). - `unsafeTagName` uses the same `cast (MkTagName s)` placeholder (fix: auto-implicit proof). - `render` / `renderPretty.go` fail totality due to mutual recursion through `[XmlNode]` — needs explicit termination, e.g. Nat fuel. Stacks on top of #79 (`proof-debt/xml744-escape-totality`); base is `main` per [[feedback_stacked_pr_orphan_trap_github]], so the diff is cumulative until #79 lands, then this rebases to a single-file change. ## Test plan - [ ] CI green - [ ] `idris2 --build idris2-ecosystem/xml-toolkit/xml-7-44.ipkg` builds modules 1–3 cleanly (verified locally) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3305c92 commit 34dc2de

1 file changed

Lines changed: 33 additions & 15 deletions

File tree

idris2-ecosystem/xml-toolkit/src/Xml744/Attribute.idr

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,22 @@ import Data.List
99

1010
%default total
1111

12-
||| Valid XML name characters (simplified - ASCII subset)
12+
||| Valid XML name characters (simplified - ASCII subset).
13+
|||
14+
||| `public export` so cross-module callers of `unsafeAttrName` / `wAttr` on
15+
||| string literals can have the auto-implicit `isValidName s = True` proof
16+
||| discharged by elaboration-time reduction. With plain `export` only the
17+
||| name is visible and reduction is blocked.
18+
public export
1319
isNameStartChar : Char -> Bool
1420
isNameStartChar c = isAlpha c || c == '_' || c == ':'
1521

22+
public export
1623
isNameChar : Char -> Bool
1724
isNameChar c = isNameStartChar c || isDigit c || c == '-' || c == '.'
1825

19-
||| Check if a string is a valid XML name
20-
export
26+
||| Check if a string is a valid XML name.
27+
public export
2128
isValidName : String -> Bool
2229
isValidName s = case unpack s of
2330
[] => False
@@ -28,19 +35,26 @@ public export
2835
data AttrName : Type where
2936
MkAttrName : (n : String) -> {auto prf : isValidName n = True} -> AttrName
3037

31-
||| Try to create an attribute name (returns Nothing if invalid)
38+
||| Try to create an attribute name (returns Nothing if invalid).
39+
|||
40+
||| The `with proof` form binds `prf : isValidName s = True` in the matched
41+
||| branch, which discharges `MkAttrName`'s auto-implicit. Replaces the
42+
||| previous `decEq (isValidName s) True` form — `decEq` for `Bool` is not in
43+
||| scope on Idris2 0.8.0 with the current imports.
3244
export
3345
attrName : (s : String) -> Maybe AttrName
34-
attrName s = case decEq (isValidName s) True of
35-
Yes prf => Just (MkAttrName s)
36-
No _ => Nothing
37-
38-
||| Unsafe attribute name creation (use only for known-good literals)
46+
attrName s with (isValidName s) proof prf
47+
attrName s | True = Just (MkAttrName s)
48+
attrName s | False = Nothing
49+
50+
||| Attribute-name constructor for string literals whose validity Idris2
51+
||| can verify at elaboration time. The auto-implicit `isValidName s = True`
52+
||| is discharged by `Refl` for any literal (or compile-time-known) string.
53+
||| Replaces the previous `cast (MkAttrName s)` placeholder, which never
54+
||| compiled (no matching `Cast` instance, and `MkAttrName` needs the proof).
3955
export
40-
unsafeAttrName : String -> AttrName
41-
-- PROOF_TODO: Replace cast with actual proof
42-
-- PROOF_TODO: Replace cast with actual proof
43-
unsafeAttrName s = cast (MkAttrName s)
56+
unsafeAttrName : (s : String) -> {auto prf : isValidName s = True} -> AttrName
57+
unsafeAttrName s = MkAttrName s
4458

4559
||| Common attribute names as constants
4660
export
@@ -77,9 +91,13 @@ export
7791
getAttrValue : XmlAttr -> String
7892
getAttrValue (MkAttr _ v) = exfiltrate v
7993

80-
||| Convenience for common w: namespace attributes (OOXML)
94+
||| Convenience for common w: namespace attributes (OOXML).
95+
|||
96+
||| The auto-implicit proof obligation `isValidName ("w:" ++ n) = True` is
97+
||| discharged at the call site by `Refl` for any literal `n` (the elaborator
98+
||| reduces both the string concatenation and `isValidName` on closed terms).
8199
export
82-
wAttr : String -> String -> XmlAttr
100+
wAttr : (n : String) -> {auto prf : isValidName ("w:" ++ n) = True} -> String -> XmlAttr
83101
wAttr n v = attr (unsafeAttrName ("w:" ++ n)) v
84102

85103
||| Example: attribute that would break with raw quotes

0 commit comments

Comments
 (0)