@@ -297,27 +297,26 @@ If there are no fields, the enum can be decoded by using their data as the discr
297297 requires TAG =/=Int DISCRIMINANT
298298```
299299
300- #### Enums with two variants
300+ #### Enums with direct tag encoding and fields
301301
302- Having two variants with possibly zero or one field each is a very common case,
303- it includes a number of standard library types such as ` Option ` and ` Result ` .
304-
305- The following rules are specialised to the case of encoding an ` Option ` .
306- An important distinction here is whether or not the tag is niche-encoded.
307- If the option holds data that has all-zero as a possible value, a separate tag is used, usually as the first field.
308- In both cases we expect the tag to be in the single shared field, and the discriminant to be just 0 and 1.
302+ Decodes any enum with ` tagEncodingDirect ` and a ` primitiveInt ` tag,
303+ with arbitrary variant counts, discriminant values, and field counts per variant.
304+ The tag is read as an unsigned little-endian integer, matched against the discriminant
305+ list to find the variant index, and then the variant's fields are decoded using the
306+ per-variant layout offsets.
309307
310308``` k
309+ // General entry rule: direct-tag enum with at least one field somewhere.
311310 rule #decodeValue(
312311 BYTES
313312 , typeInfoEnumType(...
314313 name: _
315314 , adtDef: _
316- , discriminants: discriminant(0) discriminant(1) .Discriminants
317- , fields: (.Tys : (FIELD_TYPE .Tys) : .Tyss)
315+ , discriminants: DISCRIMINANTS
316+ , fields: FIELD_TYPESS
318317 , layout:
319318 someLayoutShape(layoutShape(...
320- fields: fieldsShapeArbitrary(mk(... offsets: machineSize(0) .MachineSizes))
319+ fields: _FIELDS
321320 , variants:
322321 variantsShapeMultiple(
323322 mk(...
@@ -329,7 +328,7 @@ In both cases we expect the tag to be in the single shared field, and the discri
329328 )
330329 , tagEncoding: tagEncodingDirect
331330 , tagField: 0
332- , variants: _VARIANTS
331+ , variants: VARIANT_LAYOUTS
333332 )
334333 )
335334 , abi: _ABI
@@ -338,22 +337,59 @@ In both cases we expect the tag to be in the single shared field, and the discri
338337 ))
339338 ) #as ENUM_TYPE
340339 )
341- => #decodeOptionTag01(BYTES, TAG_WIDTH, FIELD_TYPE, ENUM_TYPE)
342-
343- syntax Evaluation ::= #decodeOptionTag01 ( Bytes , IntegerLength , Ty , TypeInfo ) [function, total]
344- // --------------------------------------------------------------------------------------
345- rule #decodeOptionTag01(BYTES, _LEN, _TY, _ENUM_TYPE)
346- => Aggregate(variantIdx(0), .List)
347- requires 0 ==Int BYTES[0] // expect only 0 or 1 as tags, so higher bytes do not matter
348- [preserves-definedness]
349- rule #decodeOptionTag01(BYTES, LEN, TY, _ENUM_TYPE)
350- => Aggregate(variantIdx(1), ListItem(#decodeValue(substrBytes(BYTES, #byteLength(LEN), lengthBytes(BYTES)), lookupTy(TY))))
351- requires 1 ==Int BYTES[0] // expect only 0 or 1 as tags, so higher bytes do not matter
340+ => #decodeEnumDirectFields(
341+ BYTES,
342+ #findVariantIdx(#decodeEnumDirectTag(BYTES, TAG_WIDTH), DISCRIMINANTS),
343+ FIELD_TYPESS,
344+ VARIANT_LAYOUTS,
345+ ENUM_TYPE
346+ )
347+ requires notBool #noFields(FIELD_TYPESS)
348+
349+ // ---------------------------------------------------------------------------
350+ // #decodeEnumDirectFields: given the variant index, decode its fields
351+ // ---------------------------------------------------------------------------
352+ syntax Evaluation ::= #decodeEnumDirectFields ( Bytes , VariantIdx , Tyss , LayoutShapes , TypeInfo ) [function, total]
353+ // --------------------------------------------------------------------------------------------------------------------------
354+ rule #decodeEnumDirectFields(BYTES, variantIdx(IDX), FIELD_TYPESS, VARIANT_LAYOUTS, _ENUM_TYPE)
355+ => Aggregate(
356+ variantIdx(IDX),
357+ #decodeFieldsWithOffsets(BYTES, #nthTys(FIELD_TYPESS, IDX), #nthVariantOffsets(VARIANT_LAYOUTS, IDX))
358+ )
359+ requires 0 <=Int IDX
352360 [preserves-definedness]
353- rule #decodeOptionTag01(BYTES, _LEN, _TY, ENUM_TYPE)
361+
362+ // Error cases: variant not found or other failure
363+ rule #decodeEnumDirectFields(BYTES, _, _FIELD_TYPESS, _VARIANT_LAYOUTS, ENUM_TYPE)
354364 => UnableToDecode(BYTES, ENUM_TYPE)
355365 [owise]
356366
367+ // ---------------------------------------------------------------------------
368+ // #decodeEnumDirectTag: read the tag bytes as an unsigned little-endian int
369+ // ---------------------------------------------------------------------------
370+ syntax Int ::= #decodeEnumDirectTag ( Bytes , IntegerLength ) [function, total]
371+ rule #decodeEnumDirectTag(BYTES, TAG_WIDTH)
372+ => Bytes2Int(substrBytes(BYTES, 0, #byteLength(TAG_WIDTH)), LE, Unsigned)
373+ requires lengthBytes(BYTES) >=Int #byteLength(TAG_WIDTH)
374+ [preserves-definedness]
375+ rule #decodeEnumDirectTag(_, _) => -1 [owise]
376+
377+ // ---------------------------------------------------------------------------
378+ // List-indexing helpers
379+ // ---------------------------------------------------------------------------
380+
381+ // Index into a Tyss (list of per-variant field type lists)
382+ syntax Tys ::= #nthTys ( Tyss , Int ) [function, total]
383+ rule #nthTys(TYS : _REST, 0) => TYS
384+ rule #nthTys(_TYS : REST, N) => #nthTys(REST, N -Int 1) requires N >Int 0
385+ rule #nthTys(_, _) => .Tys [owise]
386+
387+ // Index into variant layouts and extract field offsets in one step (total)
388+ syntax MachineSizes ::= #nthVariantOffsets ( LayoutShapes , Int ) [function, total]
389+ rule #nthVariantOffsets(layoutShape(fieldsShapeArbitrary(mk(OFFSETS)), _, _, _, _) _REST, 0) => OFFSETS
390+ rule #nthVariantOffsets(_L REST, N) => #nthVariantOffsets(REST, N -Int 1) requires N >Int 0
391+ rule #nthVariantOffsets(_, _) => .MachineSizes [owise]
392+
357393 syntax Int ::= #byteLength ( IntegerLength ) [function, total]
358394 // -----------------------------------------------------------
359395 rule #byteLength(integerLengthI8 ) => 1
0 commit comments