Fil-C on Nix

Filnix wiki

the formal campaign

rocq/theories/Transport.v

Transport between Prolog terms and the model: decoding a checked module that nanofil printed (nanofiloutcomes FILE, main.pl's outcomes/3) into the syntax of Syntax.v, and encoding the model's outcome as the term interp:run/5 would give, so the two can be compared with ==.

Decoding is partial in one deliberate way: a function that uses anything outside the core subset (a vector class, store, cas, setjmp, asm_fixed, touch ...) decodes to IOutsideName Term. It keeps its address and its term (the validator reads its signature), so calls to it and pointers to it behave as the interpreter's do, but running it is stuck.

From Stdlib Require Import String ZArith List Bool Ascii
From Nanofil Require Import Term Syntax Values Memory Ops Semantics Runtime Run Validate
Import ListNotations
Open Scope string_scope
Open Scope Z_scope

String equality, written as Prolog's ==.

21Notation a == b ≔ String.eqba b (at level 70)

Decoding

The integer the term t is.

26Definition dzt : term : optionZ ≔
match t with
⇒
Somen
_
⇒
None

The name of the atom t.

29Definition datomt : term : optionstring ≔
match t with
⇒
Somea
_
⇒
None

The elements of the list t.

32Definition dlistt : term : optionlistterm ≔
match t with
⇒
Somets
_
⇒
None

The arguments of t when it is the compound f/n.

35Definition dappf : string n : nat t : term : optionlistterm ≔
match t with
TAppg xs
⇒
g == f && lengthxs =? n%nat
→
Somexs
else
None
_
⇒
None

The class an atom names: w, l, p, a, f, s, d or x.

43Definition dclasst : term : optionclass ≔
let* a ≔ datomt in
a == "w"
→
SomeW
a == "l"
→
SomeL
a == "p"
→
SomeP
a == "a"
→
SomeA
a == "f"
→
SomeF
a == "s"
→
SomeSingle
a == "d"
→
SomeDouble
a == "x"
→
else
None

The name of the temporary tmp(N).

51Definition dtmpt : term : optionname ≔
let* xs ≔ dapp"tmp" 1 t in
match xs with
n
⇒
Somen
_
⇒
None

An IR value: a temporary, an integer constant, or a global symbol.

55Definition dvaluet : term : optionvalue ≔
match t with
TApp"tmp" n
⇒
SomeTmpn
TApp"const" TIntn
⇒
SomeConstn
TApp"global" s
⇒
SomeGlobals
_
⇒
None

The access rights read or write.

64Definition drightst : term : optionrights ≔
let* a ≔ datomt in
a == "read"
→
SomeRead
a == "write"
→
SomeWrite
else
None

The bound a half check tests: upper or lower.

69Definition dboundt : term : optionbound ≔
let* a ≔ datomt in
a == "upper"
→
SomeUpper
a == "lower"
→
SomeLower
else
None

A narrow integer width with its signedness: sw, uw, sh, uh, sb or ub (signed or unsigned word, half or byte).

75Definition dnarrowt : term : optionnarrow ≔
let* a ≔ datomt in
a == "sw"
→
SomeSw
a == "uw"
→
SomeUw
a == "sh"
→
SomeSh
a == "uh"
→
SomeUh
a == "sb"
→
SomeSb
a == "ub"
→
SomeUb
else
None

The type a raw load reads: l, s, d, x, or a narrow width.

82Definition dload_typet : term : optionload_type ≔
let* a ≔ datomt in
a == "l"
→
SomeLdL
a == "s"
→
SomeLdS
a == "d"
→
SomeLdD
a == "x"
→
SomeLdX
else
let* n ≔ dnarrowt in

A comparison's condition: equality, signed and unsigned integer order, float order, and whether two floats are ordered or not.

90Definition dcondt : term : optioncond ≔
let* a ≔ datomt in
a == "eq"
→
SomeEq
a == "ne"
→
SomeNe
a == "sle"
→
SomeSle
a == "slt"
→
SomeSlt
a == "sge"
→
SomeSge
a == "sgt"
→
SomeSgt
a == "ule"
→
SomeUle
a == "ult"
→
SomeUlt
a == "uge"
→
SomeUge
a == "ugt"
→
SomeUgt
a == "le"
→
SomeLe
a == "lt"
→
SomeLt
a == "ge"
→
SomeGe
a == "gt"
→
SomeGt
a == "o"
→
SomeOrd
a == "uo"
→
SomeUnord
else
None

One memory effect of a raw pointer load or store, such as reading the aux or resolving the lower.

103Definition deffectt : term : optioneffect ≔
let* a ≔ datomt in
a == "aux_read"
→
a == "lower_resolve"
→
a == "address_read"
→
a == "atomic_read"
→
a == "aux_ensure"
→
a == "lower_barrier"
→
a == "rooted_reload"
→
a == "lower_write"
→
a == "address_write"
→
a == "atomic_write"
→
else
None

A list of memory effects.

113Definition deffectst : term : optionlisteffect ≔
let* ts ≔ dlistt in

The width of a store or a data item: b, h, w, l, p, s, d or x.

118Definition dwidtht : term : optionwidth ≔
let* a ≔ datomt in
a == "b"
→
SomeWb
a == "h"
→
SomeWh
a == "w"
→
SomeWw
a == "l"
→
SomeWl
a == "p"
→
SomeWp
a == "s"
→
SomeWs
a == "d"
→
SomeWd
a == "x"
→
SomeWx
else
None

The four bounds arguments Lo, Hi, Rights, Align of a check, given to k to make the op.

126Definition dcheck4xs : listterm k : Z → Z → rights → Z → op : optionop ≔
match xs with
lo; hi; r; al
⇒
let* lo′ ≔ dzlo in
let* hi′ ≔ dzhi in
let* r′ ≔ drightsr in
let* al′ ≔ dzal in
Someklo′ hi′ r′ al′
_
⇒
None

An Op of ir.pl's table, in the core subset: arithmetic, conversions, comparisons, checks, raw loads and the rest.

136Definition dopt : term : optionop ≔
match t with
⇒
a == "add"
→
SomeAdd
a == "sub"
→
SomeSub
a == "mul"
→
SomeMul
a == "div"
→
SomeDiv
a == "udiv"
→
SomeUdiv
a == "rem"
→
SomeRem
a == "urem"
→
SomeUrem
a == "and"
→
SomeAnd
a == "or"
→
SomeOr
a == "xor"
→
SomeXor
a == "sar"
→
SomeSar
a == "shr"
→
SomeShr
a == "shl"
→
SomeShl
a == "neg"
→
SomeNeg
a == "copy"
→
SomeCopy
a == "ptoi"
→
SomePtoi
a == "itop"
→
SomeItop
a == "truncd"
→
SomeTruncd
a == "truncx"
→
SomeTruncx
a == "stosi"
→
a == "stoui"
→
a == "dtosi"
→
a == "dtoui"
→
a == "xtosi"
→
a == "xtoui"
→
a == "swtof"
→
a == "uwtof"
→
a == "sltof"
→
a == "ultof"
→
a == "cast"
→
SomeCast
a == "va_args"
→
SomeVaArgs
a == "va_arg"
→
SomeVaArg
a == "check_function"
→
else
None
TAppf xs
⇒
f == "cmp"
→
match xs with
c; k
⇒
let* c′ ≔ dcondc in
let* k′ ≔ dclassk in
SomeCmpc′ k′
_
⇒
None
f == "ext"
→
match xs with
⇒
⇒
n
⇒
let* n′ ≔ dnarrown in
SomeExtn′
_
⇒
None
f == "alloc"
→
match xs with
al
⇒
let* al′ ≔ dzal in
SomeAllocal′
_
⇒
None
f == "check"
→
f == "renew"
→
f == "renew_hull"
→
f == "renew_if"
→
f == "check_limit"
→
f == "check_ahead"
→
match xs with
lo; hi; r; al; ahi
⇒
let* ahi′ ≔ dzahi in
dcheck4lo; hi; r; al λlo hi r al ⇒ CheckAheadlo hi r al ahi′
_
⇒
None
f == "half_check"
→
match xs with
b :: rest
⇒
let* b′ ≔ dboundb in
_
⇒
None
f == "span_test"
→
match xs with
lo; hi
⇒
let* lo′ ≔ dzlo in
let* hi′ ≔ dzhi in
SomeSpanTestlo′ hi′
_
⇒
None
f == "limit"
→
match xs with
n
⇒
let* n′ ≔ dzn in
SomeLimitn′
_
⇒
None
f == "check_local"
→
match xs with
x; off; bytes; r; al
⇒
let* off′ ≔ dzoff in
let* bytes′ ≔ dzbytes in
let* r′ ≔ drightsr in
let* al′ ≔ dzal in
SomeCheckLocalx off′ bytes′ r′ al′
_
⇒
None
f == "raw_load"
→
match xs with
ty; off
⇒
let* ty′ ≔ dload_typety in
let* off′ ≔ dzoff in
SomeRawLoadty′ off′
_
⇒
None
f == "raw_load_pointer"
→
match xs with
off; es
⇒
let* off′ ≔ dzoff in
let* es′ ≔ deffectses in
SomeRawLoadPointeroff′ es′
_
⇒
None
else
None
_
⇒
None

An argument of a flight: address(V), a pointer's address, or a plain value.

216Definition dflight_argt : term : optionflight_arg ≔
match t with
TApp"address" v
⇒
let* v′ ≔ dvaluev in
SomeFAddressv′
_
⇒
let* v ≔ dvaluet in
SomeFValuev

A flight's capability source: none, or lower(V), the lower of the pointer V.

224Definition dflight_lowert : term : optionflight_lower ≔
match t with
TAtom"none"
⇒
TApp"lower" v
⇒
let* v′ ≔ dvaluev in
SomeLowerOfv′
_
⇒
None

One result result(tmp(D), K) of a call: the temporary it binds and its class.

233Definition dresultt : term : optionname × class ≔
let* xs ≔ dapp"result" 2 t in
match xs with
d; k
⇒
let* d′ ≔ dtmpd in
let* k′ ≔ dclassk in
Somed′, k′
_
⇒
None

How a call reads its result: void, a tuple of results, or one result.

239Definition dcall_resultt : term : optioncall_result ≔
match t with
TAtom"void"
⇒
SomeCVoid
TApp"tuple" TListrs
⇒
let* rs′ ≔ map_optiondresult rs in
SomeCTuplers′
_
⇒
let* r ≔ dresultt in
SomeCResultfstr sndr

A call argument arg(K, V): a value and its class.

247Definition dargt : term : optionarg ≔
let* xs ≔ dapp"arg" 2 t in
match xs with
k; v
⇒
let* k′ ≔ dclassk in
let* v′ ≔ dvaluev in
SomeArgk′ v′
_
⇒
None

A list of call arguments.

252Definition dargst : term : optionlistarg ≔
let* ts ≔ dlistt in

An instruction of the core subset; None for any other.

255Definition dinstt : term : optioninst ≔
match t with
TApp"let" d; k; o; TListargs
⇒
let* d′ ≔ dtmpd in
let* k′ ≔ dclassk in
let* o′ ≔ dopo in
let* args′ ≔ map_optiondvalue args in
SomeLetd′ k′ o′ args′
TApp"flight" d; o; TListargs; lw
⇒
let* d′ ≔ dtmpd in
let* o′ ≔ dopo in
let* args′ ≔ map_optiondflight_arg args in
let* lw′ ≔ dflight_lowerlw in
SomeFlightd′ o′ args′ lw′
TApp"raw_store" w; off; v; a
⇒
let* w′ ≔ dwidthw in
match w′ with
Wb | Wh | Ww | Wl | Ws | Wd | Wx
⇒
let* off′ ≔ dzoff in
let* v′ ≔ dvaluev in
let* a′ ≔ dvaluea in
SomeRawStorew′ off′ v′ a′
_
⇒
None
TApp"raw_store_pointer" off; v; a; es
⇒
let* off′ ≔ dzoff in
let* v′ ≔ dvaluev in
let* a′ ≔ dvaluea in
let* es′ ≔ deffectses in
SomeRawStorePointeroff′ v′ a′ es′
TApp"call" r; callee; args
⇒
let* r′ ≔ dcall_resultr in
let* c ≔ dvaluecallee in
let* args′ ≔ dargsargs in
SomeCallr′ c args′
TApp"raw_call" r; callee; args
⇒
let* r′ ≔ dcall_resultr in
let* c ≔ dvaluecallee in
let* args′ ≔ dargsargs in
SomeRawCallr′ c args′
TApp"local_alloc" x; TIntsize
⇒
SomeLocalAllocx size
TApp"loc" file; TIntline
⇒
SomeLocfile line
_
⇒
None

A jump target: poll(L), a poll edge to L, or a plain label.

288Definition dtargett : term : optiontarget ≔
match t with
TApp"poll" l
⇒
SomePolll
_
⇒
SomeTot

The jump that ends a block: goto, branch, return of one or several values or of none, or unreachable.

296Definition djumpt : term : optionjump ≔
match t with
TApp"goto" l
⇒
let* l′ ≔ dtargetl in
SomeJGotol′
TApp"branch" v; l1; l2
⇒
let* v′ ≔ dvaluev in
let* l1′ ≔ dtargetl1 in
let* l2′ ≔ dtargetl2 in
SomeJBranchv′ l1′ l2′
TApp"ret" v
⇒
let* v′ ≔ dvaluev in
SomeJRetv′
TApp"rets" TListvs
⇒
let* vs′ ≔ map_optiondvalue vs in
SomeJRetsvs′
TAtom"ret"
⇒
TAtom"unreachable"
⇒
_
⇒
None

One L − V pair of a phi: a predecessor's label and the value for the edge from it.

311Definition dpairt : term : optionname × value ≔
let* xs ≔ dapp"-" 2 t in
match xs with
l; v
⇒
let* v′ ≔ dvaluev in
Somel, v′
_
⇒
None

A phi phi(tmp(D), K, Pairs).

316Definition dphit : term : optionphi ≔
match t with
TApp"phi" d; k; TListpairs
⇒
let* d′ ≔ dtmpd in
let* k′ ≔ dclassk in
let* ps ≔ map_optiondpair pairs in
SomePhid′ k′ ps
_
⇒
None

A block block(L, Phis, Insts, Jump), every part decoded.

325Definition dblockt : term : optionblock ≔
match t with
TApp"block" l; TListphis; TListinsts; j
⇒
let* phis′ ≔ map_optiondphi phis in
let* insts′ ≔ map_optiondinst insts in
let* j′ ≔ djumpj in
SomemkBlockl phis′ insts′ j′
_
⇒
None

A function's return type: void, a tuple of classes l, p, s or d, or one class w, l, p, s or d.

335Definition drett : term : optionret_type ≔
match t with
TAtom"void"
⇒
SomeRVoid
TApp"tuple" TListks
⇒
let* ks′ ≔ map_optiondclass ks in
forallb
λ k  ⇒
match k with
⇒
true
_
⇒
false
ks′
→
SomeRTupleks′
else
None
_
⇒
let* k ≔ dclasst in
match k with
W | L | P | Single | Double
⇒
SomeRClassk
_
⇒
None

A parameter param(K, tmp(D)): its class and temporary.

347Definition dparamt : term : optionclass × name ≔
let* xs ≔ dapp"param" 2 t in
match xs with
k; d
⇒
let* k′ ≔ dclassk in
let* d′ ≔ dtmpd in
Somek′, d′
_
⇒
None

A function from the arguments of its func(N, Lk, R, Ps, Bs) term; None if any part is outside the core subset.

353Definition dfuncn lk r ps bs : term : optionfunc ≔
let* r′ ≔ dretr in
let* ps′ ≔
let* ts ≔ dlistps in
in
let* bs′ ≔
let* ts ≔ dlistbs in
in
SomemkFuncn lk r′ ps′ bs′

An item's linkage; one the model does not know is kept as its term.

361Definition dlinkaget : term : optionlinkage ≔
match t with
TAtom"export"
⇒
TAtom"local"
⇒
SomeLLocal
TAtom"weak"
⇒
SomeLWeak
TApp"ro" l
⇒
SomeLRol
TApp"tls" l
⇒
SomeLTlsl
TApp"init" w; p
⇒
SomeLInitw p
TApp"extern" TAtom"weak"
⇒
_
⇒
SomeLOthert

One item of a data object: a string of bytes, a pointer to a symbol plus an offset, a constant of a width, or zero bytes; any other is kept as its term.

376Definition ddata_itemt : term : optiondata_item ≔
match t with
TApp"item" TAtom"b"; TApp"str" TListcs
⇒
Some
match map_optionchar_code cs with
Somecodes
⇒
Strcodes
None
⇒
TApp"item" TAtom"p"; TApp"sym" s; TIntoff
⇒
SomeSyms off
TApp"item" w; TApp"const" TIntn
⇒
Some
match dwidthw with
Somew′
⇒
Itemw′ n
None
⇒
TApp"zero" TIntn
⇒
SomeZeron
_
⇒

An item of the module: a function, outside the core subset if dfunc fails, data or an alias; Some None for an explanatory context(_, _) item (context).

390Definition ditemt : term : optionoptionitem ≔
match t with
TApp"func" n; lk; r; ps; bs
⇒
Some
Some
match dfuncn lk r ps bs with
Somef
⇒
None
⇒
TApp"data" n; lk; TIntal; TListits
⇒
let* lk′ ≔ dlinkagelk in
let* its′ ≔ map_optionddata_item its in
SomeSomeIDatan lk′ al its′
TApp"alias" n; lk; tg
⇒
SomeSomeIAliasn lk tg
TApp"context" _; _
⇒
SomeNone
_
⇒
None

The module module(Items), its context items dropped.

406Definition decode_modulet : term : optionmodule ≔
match t with
TApp"module" TListitems
⇒
let* its ≔ map_optionditem items in
Some
flat_map
λ o  ⇒
match o with
Somei
⇒
i
None
⇒
[ ]
its
_
⇒
None

Encoding

A capability as the interpreter writes it: none, obj(B), ro(B), fn(F) or ext(N).

418Definition ecapc : cap : term ≔
match c with
⇒
TAtom"none"
⇒
TApp"obj" TIntb
⇒
TApp"ro" TIntb
⇒
TApp"fn" f
⇒
TApp"ext" n

A word as the interpreter writes it: an integer, ptr(Addr, Cap) or limit(Cap, Size).

429Definition ewordx : word : term ≔
match x with
WLimitc size
⇒
TApp"limit" ecapc; TIntsize

A trap reason as the interpreter writes it, an atom, or early(R) and ahead(R) around another.

438Fixpoint ereasonr : reason : term ≔
match r with
⇒
TAtom"null_capability"
⇒
TAtom"out_of_bounds"
⇒
TAtom"misaligned"
⇒
TAtom"readonly"
⇒
TAtom"not_a_function"
⇒
TAtom"argument_size"
⇒
TAtom"return_size"
⇒
TAtom"invalid_free"
⇒
TAtom"double_free"
⇒
TAtom"too_big"
⇒
TAtom"out_of_memory"
⇒
TAtom"alias"
⇒
TAtom"span"
⇒
TAtom"half"
⇒
TAtom"limit"
Earlyr′
⇒
TApp"early" ereasonr′
Aheadr′
⇒
TApp"ahead" ereasonr′

The outcome as interp:run/5 writes it.

460Definition eoutcomeo : outcome : term ≔
match o with
⇒
TApp"ret" ewordx
⇒
TApp"rets" TListmapeword xs
⇒
TAtom"ret"
⇒
TAtom"unreachable"
UBwhy
⇒
TApp"ub" why
⇒
TApp"trap" ereasonr
⇒
TApp"unchecked" ereasonr
⇒
TAtom"out_of_fuel"

Judging a line

The run halted because it left the core subset.

475Definition is_outsidewhy : string : bool ≔
why == "outside the core subset"

The model the gates run: the driver with the interpreter's runtime (DriverInterpRuntime).

479Module Model ≔ Driver InterpRuntime

The model's run of the module md on one outcome(Fn, Args, Fuel, Outcome) or error(Fn, Args, Fuel, E) line that outcomes/3 printed, compared with the interpreter's:

JudgmentMeans
agree(Outcome)the model and the interpreter have the same outcome
disagree(Model, Prolog)they differ: a finding, presumed the model's fault
outside(Fn, Why)the run leaves the core subset, so it is not compared
stuck_both(Fn, Why)the interpreter threw or failed, and the model is stuck too
491Definition judgemd : module line : term : term ≔
match line with
TApp"outcome" fn; TListargs; TIntfuel; expected
⇒
match map_optiondz args with
None
⇒
TApp"bad_line" line
Somens
⇒
match Model.runmd fn mapWInt ns fuel with
⇒
let got ≔ eoutcomeo in
term_eqbgot expected
→
TApp"agree" fn; TListargs; got
else
TApp"disagree" fn; TListargs; got; expected
⇒
→
TApp"outside" fn; TListargs; TAtomwhy
else
TApp"disagree" fn; TListargs; TApp"stuck" TAtomwhy; expected
TApp"error" fn; TListargs; TIntfuel; e
⇒
match map_optiondz args with
None
⇒
TApp"bad_line" line
Somens
⇒
match Model.runmd fn mapWInt ns fuel with
⇒
TApp"stuck_both" fn; TListargs; TAtomwhy
⇒
TApp"disagree" fn; TListargs; eoutcomeo; TApp"error" e
_
⇒
TApp"bad_line" line

The module with its item at index i (0-based, without contexts) replaced by the items xs: none deletes it, one replaces it, the new item and then the old one insert before it.

522Fixpoint splice_ati : nat xs : listitem md : module : module ≔
match i, md with
O, _ :: rest
⇒
appxs rest
O, [ ]
⇒
xs
Si′, it :: rest
⇒
it :: splice_ati′ xs rest
S_, [ ]
⇒
[ ]

A validity verdict as rocq/mutants.pl writes it: valid or invalid.

532Definition everdictb : bool : term ≔
b
→
"valid"
else
"invalid"

The term t mentions global(N), for the name n.

535Fixpoint mentionsn : name t : term : bool ≔
match t with
TApp"global" m
⇒
TApp_ ts | TListts
⇒
existsbmentionsn ts
_
⇒
false

Whether two function signatures are the same: parameters, return and variadicity.

544Definition sig_eqba b : sig : bool ≔
lengthsig_paramsa =? lengthsig_paramsb%nat && forallbλp ⇒ term_eqbfstp sndp combinesig_paramsa sig_paramsb && term_eqbsig_reta sig_retb && Bool.eqbsig_variadica sig_variadicb

A mutant f of the function fn that changes its signature, in a module md where a function outside the core subset refers to it: what that function may now pass or read cannot be judged by the model.

553Definition unjudged_callersmd : module fn : name f : func : bool ≔
match func_sigfn md, func_sigfn IFuncf with
Someold, Somenew
⇒
negbsig_eqbold new
&&
existsb
λ it  ⇒
match it with
_
⇒
false
md
_, _
⇒
false

The name an item defines.

562Definition defined_nameit : item : name ≔
match it with
IOutsiden _ | IDatan _ _ _ | IAliasn _ _
⇒
n

A change, removing the items olds and adding news, to a data object or an alias that a function outside the core subset refers to by the old or the new name: whether that function is still valid cannot be judged by the model.

572Definition unjudged_usersmd : module olds news : listitem : bool ≔
let names ≔ mapdefined_name appolds news in
existsb
λ it  ⇒
match it with
⇒
existsbλn ⇒ mentionsn t names
_
⇒
false
md

The model's validator on one verdict(Name, At, Id, Change, V) line that rocq/mutants.pl printed, compared with ir:valid_module/1's verdict V. It makes the Change, replace(Item), delete or insert(Item) before it, to the module's item at index At, and judges valid_items of the whole module (a mutant can change what its callers may pass it): agree(Name, Id, V), disagree(Name, Id, Model, Prolog) or outside(Name, Id). A mutant is outside when it is outside the core subset, when it changes a function's signature and a function outside the subset refers to it, or when it changes data or an alias that a function outside the subset refers to by name.

590Definition judge_validmd : module line : term : term ≔
match line with
TApp"verdict" nm; TIntat_; id; change; expected
⇒
let i ≔ Z.to_natat_ in
let old ≔ firstn1 skipni md in
let new ≔
match change with

What replaces the item, what is removed and what is added.

TApp"replace" t
⇒
let* it ≔ ditemt in
let* it ≔ it in
Someit, old, it
TAtom"delete"
⇒
Some[ ], old, [ ]
TApp"insert" t
⇒
let* it ≔ ditemt in
let* it ≔ it in
Someit :: old, [ ], it
_
⇒
None
in
match new with
None
⇒
TApp"bad_line" line
Somexs, removed, added
⇒
let outside ≔
existsb
λ it  ⇒
match it with
⇒
true
_
⇒
false
added
||
match removed, added with
IFunc_, _ | _, IFunc_
⇒
true
_, _
⇒
unjudged_usersmd removed added
in
outside
→
TApp"outside" nm; id
else
let got ≔ everdictvalid_itemssplice_ati xs md in
term_eqbgot expected
→
TApp"agree" nm; id; got
else
TApp"disagree" nm; id; got; expected
_
⇒
TApp"bad_line" line