@@ -14,6 +14,8 @@ module Anvomidaviser.ABI.Layout
1414import Anvomidaviser.ABI.Types
1515import Data.Vect
1616import Data.So
17+ import Data.Nat
18+ import Decidable.Equality
1719
1820%default total
1921
@@ -27,7 +29,7 @@ paddingFor : (offset : Nat) -> (alignment : Nat) -> Nat
2729paddingFor offset alignment =
2830 if offset `mod` alignment == 0
2931 then 0
30- else alignment - (offset `mod` alignment)
32+ else minus alignment (offset `mod` alignment)
3133
3234||| Proof that alignment divides aligned size
3335public export
@@ -40,11 +42,25 @@ alignUp : (size : Nat) -> (alignment : Nat) -> Nat
4042alignUp size alignment =
4143 size + paddingFor size alignment
4244
43- ||| Proof that alignUp produces aligned result
45+ ||| Sound decision procedure: does n divide m?
46+ ||| For n = S k, compute the candidate quotient q = m `div` (S k) and check
47+ ||| that q * (S k) recovers m exactly. The equality proof is real (decEq),
48+ ||| never assumed.
4449public export
45- alignUpCorrect : (size : Nat ) -> (align : Nat ) -> (align > 0) -> Divides align (alignUp size align)
46- alignUpCorrect size align prf =
47- DivideBy ((size + paddingFor size align) `div` align) Refl
50+ decDivides : (n : Nat ) -> (m : Nat ) -> Maybe (Divides n m)
51+ decDivides Z m = Nothing
52+ decDivides (S k) m =
53+ let q = m `div` (S k) in
54+ case decEq m (q * (S k)) of
55+ Yes prf => Just (DivideBy q prf)
56+ No _ => Nothing
57+
58+ ||| Decide whether alignUp produced an aligned result. Returning a universal
59+ ||| proof would be unsound (paddingFor uses runtime division, which does not
60+ ||| reduce), so we hand back a genuine, checked witness via decDivides.
61+ public export
62+ alignUpDivides : (size : Nat ) -> (align : Nat ) -> Maybe (Divides align (alignUp size align))
63+ alignUpDivides size align = decDivides align (alignUp size align)
4864
4965-- ------------------------------------------------------------------------------
5066-- Struct Field Layout
@@ -76,7 +92,7 @@ record StructLayout where
7692
7793||| Calculate total struct size with padding
7894public export
79- calcStructSize : Vect n Field -> Nat -> Nat
95+ calcStructSize : Vect k Field -> Nat -> Nat
8096calcStructSize [] align = 0
8197calcStructSize (f :: fs) align =
8298 let lastOffset = foldl (\ acc, field => nextFieldOffset field) f. offset fs
@@ -85,23 +101,40 @@ calcStructSize (f :: fs) align =
85101
86102||| Proof that field offsets are correctly aligned
87103public export
88- data FieldsAligned : Vect n Field -> Type where
104+ data FieldsAligned : Vect k Field -> Type where
89105 NoFields : FieldsAligned []
90106 ConsField :
91107 (f : Field) ->
92- (rest : Vect n Field) ->
108+ (rest : Vect k Field) ->
93109 Divides f.alignment f.offset ->
94110 FieldsAligned rest ->
95111 FieldsAligned (f :: rest)
96112
113+ ||| Decide whether every field in a vector is aligned (offset divisible by
114+ ||| alignment), building a real FieldsAligned witness if so.
115+ public export
116+ decFieldsAligned : (fields : Vect k Field) -> Maybe (FieldsAligned fields)
117+ decFieldsAligned [] = Just NoFields
118+ decFieldsAligned (f :: fs) =
119+ case decDivides f. alignment f. offset of
120+ Nothing => Nothing
121+ Just dv =>
122+ case decFieldsAligned fs of
123+ Nothing => Nothing
124+ Just rest => Just (ConsField f fs dv rest)
125+
97126||| Verify a struct layout is valid
98127public export
99- verifyLayout : (fields : Vect n Field) -> (align : Nat ) -> Either String StructLayout
128+ verifyLayout : (fields : Vect k Field) -> (align : Nat ) -> Either String StructLayout
100129verifyLayout fields align =
101130 let size = calcStructSize fields align
102- in case decSo (size >= sum (map (\ f => f. size) fields)) of
103- Yes prf => Right (MkStructLayout fields size align)
104- No _ => Left " Invalid struct size"
131+ in case choose (size >= sum (map (\ f => f. size) fields)) of
132+ Right _ => Left " Invalid struct size"
133+ Left szPrf =>
134+ case decDivides align size of
135+ Nothing => Left " Total size is not a multiple of the alignment"
136+ Just dv =>
137+ Right (MkStructLayout fields size align {sizeCorrect = szPrf} {aligned = dv})
105138
106139-- ------------------------------------------------------------------------------
107140-- Platform-Specific Layouts
@@ -136,7 +169,9 @@ data CABICompliant : StructLayout -> Type where
136169public export
137170checkCABI : (layout : StructLayout) -> Either String (CABICompliant layout)
138171checkCABI layout =
139- Right (CABIOk layout ? fieldsAlignedProof)
172+ case decFieldsAligned layout. fields of
173+ Just prf => Right (CABIOk layout prf)
174+ Nothing => Left " Struct fields are not C-ABI aligned"
140175
141176-- ------------------------------------------------------------------------------
142177-- ISU Element Layouts
@@ -160,11 +195,23 @@ technicalElementLayout =
160195 ]
161196 16 -- Total size: 16 bytes
162197 4 -- Alignment: 4 bytes
198+ {sizeCorrect = Oh }
199+ {aligned = DivideBy 4 Refl } -- 16 = 4 * 4
163200
164201||| Proof that TechnicalElement layout is valid
165202export
166- technicalElementLayoutValid : CABICompliant technicalElementLayout
167- technicalElementLayoutValid = CABIOk technicalElementLayout ? techElemFieldsAligned
203+ technicalElementLayoutValid : CABICompliant Layout.technicalElementLayout
204+ technicalElementLayoutValid =
205+ CABIOk Layout . technicalElementLayout
206+ (ConsField _ _ (DivideBy 0 Refl )
207+ (ConsField _ _ (DivideBy 1 Refl )
208+ (ConsField _ _ (DivideBy 2 Refl )
209+ (ConsField _ _ (DivideBy 3 Refl )
210+ (ConsField _ _ (DivideBy 1 Refl )
211+ (ConsField _ _ (DivideBy 8 Refl )
212+ (ConsField _ _ (DivideBy 9 Refl )
213+ (ConsField _ _ (DivideBy 3 Refl )
214+ NoFields ))))))))
168215
169216||| Layout for ProgramScore struct in the C ABI
170217||| Fields: total_base (u32), total_goe (i32), deductions (u32),
@@ -188,11 +235,26 @@ programScoreLayout =
188235 ]
189236 28 -- Total size: 28 bytes
190237 4 -- Alignment: 4 bytes
238+ {sizeCorrect = Oh }
239+ {aligned = DivideBy 7 Refl } -- 28 = 7 * 4
191240
192241||| Proof that ProgramScore layout is valid
193242export
194- programScoreLayoutValid : CABICompliant programScoreLayout
195- programScoreLayoutValid = CABIOk programScoreLayout ? progScoreFieldsAligned
243+ programScoreLayoutValid : CABICompliant Layout.programScoreLayout
244+ programScoreLayoutValid =
245+ CABIOk Layout . programScoreLayout
246+ (ConsField _ _ (DivideBy 0 Refl )
247+ (ConsField _ _ (DivideBy 1 Refl )
248+ (ConsField _ _ (DivideBy 2 Refl )
249+ (ConsField _ _ (DivideBy 12 Refl )
250+ (ConsField _ _ (DivideBy 13 Refl )
251+ (ConsField _ _ (DivideBy 14 Refl )
252+ (ConsField _ _ (DivideBy 15 Refl )
253+ (ConsField _ _ (DivideBy 16 Refl )
254+ (ConsField _ _ (DivideBy 17 Refl )
255+ (ConsField _ _ (DivideBy 5 Refl )
256+ (ConsField _ _ (DivideBy 6 Refl )
257+ NoFields )))))))))))
196258
197259-- ------------------------------------------------------------------------------
198260-- Offset Calculation
@@ -206,7 +268,12 @@ fieldOffset layout name =
206268 Just idx => Just (finToNat idx ** index idx layout. fields)
207269 Nothing => Nothing
208270
209- ||| Proof that field offset is within struct bounds
271+ ||| Decide whether a field lies within the struct bounds.
272+ ||| Universally claiming the bound would be unsound (it is false for
273+ ||| arbitrary fields), so we return a decision via choose.
210274public export
211- offsetInBounds : (layout : StructLayout) -> (f : Field) -> So (f.offset + f.size <= layout.totalSize)
212- offsetInBounds layout f = ? offsetInBoundsProof
275+ offsetInBounds : (layout : StructLayout) -> (f : Field) -> Maybe (So (f.offset + f.size <= layout.totalSize))
276+ offsetInBounds layout f =
277+ case choose (f. offset + f. size <= layout. totalSize) of
278+ Left ok => Just ok
279+ Right _ => Nothing
0 commit comments