Skip to content

LowParse: add support for non-injective parsers#258

Merged
tahina-pro merged 8 commits intoproject-everest:masterfrom
tahina-pro:_taramana_parser_kind_injective
Feb 24, 2026
Merged

LowParse: add support for non-injective parsers#258
tahina-pro merged 8 commits intoproject-everest:masterfrom
tahina-pro:_taramana_parser_kind_injective

Conversation

@tahina-pro
Copy link
Copy Markdown
Member

@tahina-pro tahina-pro commented Feb 13, 2026

Commits of this PR have been entirely AI-generated (including a rebase) by GitHub Copilot CLI 0.406.0 using Claude Opus 4.5 and 4.6, running from tahina-pro/quackyducky@b390b2c (which has AI skills duplicated and adapted from F* and Pulse.)

So far the injective condition in parser_kind_prop has been unconditionally required for all parser specifications. This PR now adds a new Boolean field to parser_kind and makes injective conditional on the value of that field. Then, it allows non-injective parsers on many existing combinators, including parse_filter, parse_synth, and (non-dependent and dependent) pairs, sums, and bitfields.

At this point, serializer specifications (and thus PulseParse), Low* accessors, and 3D still require injective parsers. (Of course, so do QuackyDucky and ASN.1*, which are designed for proving non-malleability anyway.)

…ivity

Add a Boolean field parser_kind_injective to parser_kind so that
parser_kind_prop enforces the injectivity property only when
parser_kind_injective is true. The serializer refinement type now
requires parser_kind_injective == true, ensuring serializers can
only be constructed for injective parsers.

Key changes:

- LowParse.Spec.Base: Added parser_kind_injective field to parser_kind'.
  Made injective enforcement conditional in parser_kind_prop'. Strengthened
  no_lookahead_on_postcond to include consumed length equality. Updated glb
  to AND the injective fields. Added serializer_parser_injective lemma.

- LowParse.Spec.Combinators: Made and_then_cases_injective conditional on
  (and_then_kind k k').parser_kind_injective. Made synth_injective
  conditional on k.parser_kind_injective. Required parser_kind_injective
  for serialize_tagged_union and serialize_dtuple2.

- LowParse.Spec.BitSum: Added inj:bool parameter to
  weaken_parse_bitsum_cases_kind' to avoid nested quantifier SMT issues.
  Used Classical.impl_intro with named lemmas for injectivity proofs.
  Added serializer_parser_injective loops in serialize_bitsum_cases and
  serialize_bitsum.

- LowParse.Low.Base.Spec: Removed gaccessor_injective from gaccessor_prop',
  keeping only gaccessor_no_lookahead. Added k2.parser_kind_subkind ==
  Some ParserStrong precondition to gaccessor_compose. Proved
  gaccessor_compose_no_lookahead using parse_strong_prefix.

- LowParse.Low.Combinators: Updated gaccessor/accessor variants
  (fst_then, then_fst, then_snd) with ParserStrong constraints.
  Made validate_filter_and_then's and_then_cases_injective conditional.

- LowParse.Spec.List: Made parse_list_bare_injective conditional on
  k.parser_kind_injective. Changed parse_list_kind to take inj:bool.

- LowParse.Spec.Recursive: Fixed parse_recursive_payload_kind calls to
  pass inj argument.

- LowParse.Spec.VLData: Made and_then_cases_injective conditional in
  parse_vldata_gen_eq_def.

- LowParse.SLow.Combinators: Made parse32_and_then's and_then_cases_injective
  conditional.

- LowParse.Low.Writers.Instances: Added k2.parser_kind_injective == true to
  swrite_weaken precondition.

- LowParse.Pulse.Combinators: Added k2.parser_kind_injective == true to all
  s2 serializer parameters for serialize_dtuple2 compatibility.

All files in src/lowparse and src/lowparse/pulse verify successfully.
accessor_compose no longer takes a unit argument after the
parser_kind_injective refactoring. Update LowParseExample3.Aux.fst
to remove the extra () arguments from accessor_compose calls.
accessor_compose no longer takes a unit argument after the
parser_kind_injective refactoring. Update the QD code generator
(src/qd/rfc_fstar_compiler.ml) to stop emitting the extra ()
argument in generated accessor_compose calls. Also remove the
now-unnecessary compose_needs_unit parameter from write_accessor
helper functions.

Fix hand-written test files in tests/sample0/ accordingly.
- ASN1.Base.fst: Add parser_kind_injective = true to
  asn1_strong_parser_kind and asn1_weak_parser_kind.

- ASN1.Spec.Content.INTEGER.fst: Add parser_kind_injective = true
  to parse_untagged_bounded_integer_kind and
  parse_integer_payload_kind.

- ASN1.Spec.Automata.fst: Add parser_kind_injective = true to
  automata_default_parser_kind.

- ASN1.Spec.Choice.fst: Propagate parser_kind_injective field in
  sanitify_parser_kind.

- ASN1.Spec.Set.fst: Add k.parser_kind_injective == true
  precondition to repr_order_prop_intro,
  tot_parse_byte_sorted_list_aux_correct,
  tot_parse_byte_sorted_list_correct, and
  tot_parse_byte_sorted_list. Pass inj argument to
  parse_list_kind.

- ASN1.Spec.Interpreter.fst: Add k.parser_kind_injective == true
  to parse_non_empty_list. Inline kc = ASN1_TERMINAL key_k to
  avoid variable escape. Add assert_norm for is_weaker_than
  checks on and_then_kind.
Add parser_kind_injective = true to parse_content_kind and
parse_raw_data_item_kind record literals in
CBOR.Spec.Raw.EverParse.fst.
- Added parser_kind_injective = true to parser_kind_prop in EverParse3d.Kinds.fst
  (all 3d parser kinds are injective, since parser_kind is abstract)
- Added parser_kind_injective = true to kind_nlist_default, kind_nlist Some branch,
  and parse_string_kind record literals
- Fixed kind_all_zeros to pass true to parse_list_kind
- Updated parse_list_kind calls in EverParse3d.Prelude.fst and
  EverParse3d.Actions.Base.fst to pass k.LP.parser_kind_injective
- Added k.parser_kind_injective == true to parse_nlist_total_fixed_size_kind_correct
  requires clause
Replace the overly restrictive k2.parser_kind_subkind == Some ParserStrong
precondition on gaccessor_compose and related functions with the weaker
k2.parser_kind_injective == true. This restores the previous expressivity
of gaccessors in LowParse.Low.

Key changes:
- Add gaccessor_injective back to gaccessor_prop' (conjunction with
  gaccessor_no_lookahead), so gaccessor constructors must prove both
- Change gaccessor_compose, gaccessor_compose_eq, accessor_compose to
  require k2.parser_kind_injective == true instead of ParserStrong
- Add gaccessor_compose_injective proof (uses injective_precond +
  no_lookahead of intermediate parser p2)
- Update gaccessor_compose_no_lookahead proof to use injective_precond
- Change gaccessor_fst_then, gaccessor_then_fst, gaccessor_then_snd and
  their accessor variants to use parser_kind_injective == true
- Add gaccessor_snd_injective call to gaccessor_snd construction
- Add _injective lemma calls to gaccessor constructions in:
  LowParse.Low.Combinators (tagged_union_payload)
  LowParse.Low.Sum (sum_payload, dsum_payload, dsum_unknown_payload)
  LowParse.Low.IfThenElse (ifthenelse_payload)
  LowParse.Low.VLGen (bounded_vlgen_payload, vlgen_payload)
- Add parser_kind_injective == true to LowParse.Repr.fsti strong_parser_kind
@tahina-pro tahina-pro merged commit 59b5430 into project-everest:master Feb 24, 2026
23 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant