Skip to content

Commit 53cb928

Browse files
committed
fix(lol/abi): make the lol-abi Idris package typecheck
The lol-abi.ipkg package did not build. All failures were in estate-authored (MPL-2.0) modules; fixed without any believe_me or holes. Layout.idr: - Every MkStructLayout left its erased `aligned : Divides alignment totalSize` auto-implicit to proof search, which cannot guess the quotient k. Supplied it explicitly per layout via {aligned = DivideBy k Refl} (32=4*8, 24=3*8, 56=7*8, 40=5*8, 16=4*4). - paddingFor used `-` on Nat, resolving to a nonexistent `Neg Nat`; switched to truncating `minus`. - Removed the unused `alignUpCorrect` lemma: its `Refl` body asserted `n = (n `div` a) * a`, which only holds definitionally for concrete args, so it never typechecked. A correct general proof needs the division theorem (absent from Idris2 base); the divisibility the concrete layouts need is discharged per-layout by the explicit DivideBy witnesses. Documented as future work. I18nStore.idr: - extractLanguage used Data.List1.split (wants a List) on a String; switched to Data.String.split. - LookupError used `data X = .. | ..` with per-constructor `|||` doc comments (only valid in GADT form); converted to `data LookupError : Type where`. - The CorrectStore interface ended with a dangling `|||` doc comment (no method after it), breaking the block's scope so the following top-level `data LookupError` failed to register; demoted to a plain comment. `idris2 --build lol-abi.ipkg` now exits 0 (pre-existing shadowing warnings only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019awZjBD1qx61tvmEuEKNpn
1 parent c43aafa commit 53cb928

2 files changed

Lines changed: 37 additions & 20 deletions

File tree

lol/src/Lol/ABI/I18nStore.idr

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import Lol.ABI.PluralForm
2626
import Data.So
2727
import Data.List
2828
import Data.List1
29+
import Data.String
2930

3031
%default total
3132

@@ -150,7 +151,7 @@ lookupWithFallback lookupFn strategy reqLocale defLocale key =
150151
||| "en-US" -> "en", "zh-Hans-CN" -> "zh"
151152
public export
152153
extractLanguage : String -> String
153-
extractLanguage s = let (lang ::: _) = split (== '-') s in lang
154+
extractLanguage s = let (lang ::: _) = Data.String.split (== '-') s in lang
154155

155156
||| Convert a plural category to its CLDR string suffix.
156157
public export
@@ -213,9 +214,11 @@ interface I18nStore store => CorrectStore (store : Type) where
213214
(prf : KeyExists locale key) ->
214215
lookup st locale key = Just (guaranteedLookup st prf)
215216

216-
||| The store is monotonic: adding translations never removes existing ones
217-
||| (expressed as: if a key exists, it continues to exist)
218-
||| This is an invariant the Zig store must maintain.
217+
-- The store is monotonic: adding translations never removes existing ones
218+
-- (expressed as: if a key exists, it continues to exist). Stated as a plain
219+
-- comment, not a `|||` doc comment: a trailing doc comment with no method
220+
-- following it is dangling and breaks the interface block's scope. This
221+
-- invariant is enforced on the Zig store side, not provable generically here.
219222

220223
--------------------------------------------------------------------------------
221224
-- Error Classification
@@ -224,15 +227,15 @@ interface I18nStore store => CorrectStore (store : Type) where
224227
||| Classification of lookup failures for error reporting.
225228
||| The Zig FFI maps these to the Result enum in Types.idr.
226229
public export
227-
data LookupError
228-
= ||| The requested locale is not in the corpus at all
229-
NoSuchLocale String
230-
| ||| The locale exists but the key is not defined
231-
NoSuchKey String String
232-
| ||| The plural form variant is missing (e.g. "items.few" missing)
233-
MissingPluralForm String String PluralCategory
234-
| ||| The entire fallback chain was exhausted without finding a translation
235-
FallbackExhausted String String (List String)
230+
data LookupError : Type where
231+
||| The requested locale is not in the corpus at all
232+
NoSuchLocale : String -> LookupError
233+
||| The locale exists but the key is not defined
234+
NoSuchKey : String -> String -> LookupError
235+
||| The plural form variant is missing (e.g. "items.few" missing)
236+
MissingPluralForm : String -> String -> PluralCategory -> LookupError
237+
||| The entire fallback chain was exhausted without finding a translation
238+
FallbackExhausted : String -> String -> List String -> LookupError
236239

237240
||| Map a lookup error to the appropriate Result code for FFI transport
238241
public export

lol/src/Lol/ABI/Layout.idr

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ paddingFor : (offset : Nat) -> (alignment : Nat) -> Nat
3232
paddingFor offset alignment =
3333
if offset `mod` alignment == 0
3434
then 0
35-
else alignment - (offset `mod` alignment)
35+
else alignment `minus` (offset `mod` alignment)
3636

3737
||| Proof that alignment divides aligned size
3838
public export
@@ -45,12 +45,21 @@ alignUp : (size : Nat) -> (alignment : Nat) -> Nat
4545
alignUp size alignment =
4646
size + paddingFor size alignment
4747

48-
||| Proof that alignUp produces an aligned result.
49-
||| For any positive alignment, (alignUp s a) is divisible by a.
50-
public export
51-
alignUpCorrect : (size : Nat) -> (align : Nat) -> (align > 0) -> Divides align (alignUp size align)
52-
alignUpCorrect size align prf =
53-
DivideBy ((size + paddingFor size align) `div` align) Refl
48+
-- NOTE (2026-06-18): a general lemma
49+
-- alignUpCorrect : (size, align : Nat) -> So (align > 0)
50+
-- -> Divides align (alignUp size align)
51+
-- was removed here. It was unused, and its body asserted
52+
-- alignUp size align = (alignUp size align `div` align) * align
53+
-- via `Refl`, which only holds definitionally for *concrete* arguments, not
54+
-- symbolic ones, so the lemma did not actually typecheck. A correct general
55+
-- proof needs the division theorem (n = d * (n `div` d) + n `mod` d), which
56+
-- Idris2 `base` does not export (only the `divNatNZ`/`modNatNZ`/`divmod'`
57+
-- functions exist, with no accompanying equational lemma), so it would have to
58+
-- be hand-proved from `Data.Nat.divmod'`. The divisibility that the concrete
59+
-- struct layouts actually rely on is discharged per-layout below by the
60+
-- explicit `{aligned = DivideBy k Refl}` witnesses (which compute, since the
61+
-- sizes/alignments there are concrete). Re-introducing the general lemma with a
62+
-- real proof is tracked as future work, not a `believe_me`.
5463

5564
--------------------------------------------------------------------------------
5665
-- Struct Field Layout
@@ -117,6 +126,7 @@ localeLayout =
117126
]
118127
32 -- Total size: 32 bytes
119128
8 -- Alignment: 8 bytes (pointer alignment)
129+
{aligned = DivideBy 4 Refl} -- 32 = 4 * 8
120130

121131
||| Layout of lol_translation_result_t.
122132
|||
@@ -138,6 +148,7 @@ translationResultLayout =
138148
]
139149
24 -- Total size: 24 bytes
140150
8 -- Alignment: 8 bytes
151+
{aligned = DivideBy 3 Refl} -- 24 = 3 * 8
141152

142153
||| Layout of lol_language_info_t.
143154
|||
@@ -167,6 +178,7 @@ languageInfoLayout =
167178
]
168179
56 -- Total size: 56 bytes
169180
8 -- Alignment: 8 bytes
181+
{aligned = DivideBy 7 Refl} -- 56 = 7 * 8
170182

171183
||| Layout of lol_plural_rule_t.
172184
|||
@@ -188,6 +200,7 @@ pluralRuleLayout =
188200
]
189201
40 -- Total size: 40 bytes
190202
8 -- Alignment: 8 bytes
203+
{aligned = DivideBy 5 Refl} -- 40 = 5 * 8
191204

192205
--------------------------------------------------------------------------------
193206
-- C ABI Compatibility Proofs
@@ -243,3 +256,4 @@ localeLayoutWasm32 =
243256
]
244257
16 -- Total size: 16 bytes on WASM32
245258
4 -- Alignment: 4 bytes
259+
{aligned = DivideBy 4 Refl} -- 16 = 4 * 4

0 commit comments

Comments
 (0)