Skip to content

Commit 72e7758

Browse files
Lef IoannidisCopilot
andcommitted
Remove --cmi flag, fix auto-paren lexer for compound bracket tokens
- Remove --cmi from all Makefiles (flag no longer recognized by F*) - Fix make_auto_paren_lexer to track DOT_LPAREN, DOT_LBRACK, DOT_LBRACK_BAR, DOT_LENS_PAREN_LEFT, LENS_PAREN_LEFT, BAR_RBRACK, and LENS_PAREN_RIGHT for depth counting. Without this, qualified-open syntax like US.(expr) in if/match conditions caused the scanner to lose track of nesting depth and consume the entire file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4f5a589 commit 72e7758

29 files changed

Lines changed: 346 additions & 66 deletions

cond-hoisting.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Stateful If/Match Conditions
2+
3+
## Problem
4+
5+
`if` and `match` conditions were `term` (pure F\* expressions). Hoisting only
6+
descends into `Tv_App` arguments, so stateful operations nested inside F\*-level
7+
`if`/`match` within a condition were invisible:
8+
9+
```
10+
if (if true then !r = 0 else false) { ... } // fails: !r buried in Tv_Match
11+
```
12+
13+
## Solution
14+
15+
Change `Tm_If.b` and `Tm_Match.sc` from `term` to `st_term`, following the
16+
existing `Tm_While.condition` pattern. A token-stream preprocessor makes
17+
parentheses optional for backward compatibility.
18+
19+
## Files Changed
20+
21+
### Parser & Sugar
22+
23+
| File | Change |
24+
|------|--------|
25+
| `pulseparser.mly``ifStmt`, `matchStmt` | `IF LPAREN pulseStmt RPAREN`, `MATCH LPAREN pulseStmt RPAREN` |
26+
| `PulseSyntaxExtension_Parser.ml``make_auto_paren_lexer` | Token preprocessor: auto-inserts `LPAREN`/`RPAREN` when omitted. Detects `LBRACE` at depth 0 as end-of-condition. Passes through F\*-level `if/then` and `match/with` unmodified. |
27+
| `PulseSyntaxExtension.Sugar.fst``If.head`, `Match.head` | `A.term``stmt` |
28+
| `PulseSyntaxExtension.Desugar.fst``If`/`Match` cases | `desugar_term``desugar_stmt` |
29+
| `PulseSyntaxExtension.SyntaxWrapper.fsti``tm_if`, `tm_match` | Parameter type `term``st_term` |
30+
| `PulseSyntaxExtension_SyntaxWrapper.ml``tm_if`, `tm_match` | Wrap with `Tm_STApp`/return as needed |
31+
32+
### AST & Naming
33+
34+
| File | Change |
35+
|------|--------|
36+
| `Pulse.Syntax.Base.fsti``Tm_If.b`, `Tm_Match.sc` | `term``st_term` |
37+
| `Pulse.Syntax.Base.fst``eq_st_term'` | `eq_tm``eq_st_term` for `b`/`sc` |
38+
| `Pulse.Syntax.Naming.fsti``freevars_st'`, `ln_st'`, `subst_st_term'` | `*_term``*_st_term` for `b`/`sc` (follows `Tm_While.condition`) |
39+
| `Pulse.Syntax.Naming.fst``close_open_inverse_st'` | Same pattern |
40+
| `Pulse.Typing.FV.fst``freevars_close_st_term'` | Same pattern |
41+
| `Pulse.Syntax.Printer.fst``print_st_head` | `term_to_string b``st_term_to_string b` |
42+
| `Pulse.ElimGoto.fst``Tm_If`/`Tm_Match` cases | `elab_term``elab_st_term` for `b`/`sc` |
43+
44+
### Checker
45+
46+
| File | Function | Change |
47+
|------|----------|--------|
48+
| `Pulse.Checker.If.fst` | `check_if_term` (new) | Pure path: extracts `term` from `Tm_Return`, uses original `check_equiv_emp` logic |
49+
| | `check` | Dispatches: `Tm_Return``check_if_term`, other → checks `b` via `check g b ...`, composes with `compose_checker_result_t` |
50+
| `Pulse.Checker.Match.fst` | `check_match_term` (new) | Pure path: `compute_tot_term_type_and_u` on extracted term |
51+
| | `check` | Dispatches: `Tm_Return``check_match_term`, other → checks `sc` via `check g sc ...`, composes |
52+
| `Pulse.Checker.fst` | `maybe_elaborate_stateful_head` | Restored `Tm_If`/`Tm_Match` cases: extracts F\* term from `Tm_Return`, runs `hoist_stateful_apps`, rebuilds as `st_term` |
53+
54+
### Extraction
55+
56+
| File | Change |
57+
|------|--------|
58+
| `Pulse.Extract.Main.fst``Tm_If`, `Tm_Match` | Uniform `extract_dv g b` / `extract_dv g sc` — no `Tm_Return` special-case |
59+
| `Pulse_Extract_CompilerLib.ml``mk_if` | Binds monadic condition to fresh variable via `mk_let`, then branches on it |
60+
61+
### Test
62+
63+
| File | Purpose |
64+
|------|---------|
65+
| `test/StatefulIfCondition.fst` | Regression: `if !r = 0 { }`, `if (if true { !r = 0 } else { false }) { }`, `match Some 1 { }` |
66+
67+
## Token Preprocessor Logic
68+
69+
`make_auto_paren_lexer` wraps the base lexer. On `IF`/`MATCH`:
70+
71+
1. If next token is `LPAREN` → pass through (already parenthesized)
72+
2. Otherwise, buffer tokens tracking `()`/`[]` nesting depth:
73+
- `LBRACE` at depth 0 → insert `LPAREN` before buffered tokens, `RPAREN` after
74+
- `RETURNS`/`ENSURES` at depth 0 → same (annotation before body)
75+
- `THEN`/`WITH` at depth 0 → F\*-level syntax, no modification

mk/test.mk

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ include $(PULSE_ROOT)/mk/locate.mk
2525

2626
HINTS_ENABLED?=
2727

28-
OTHERFLAGS += --cmi
2928
# This warning is really useless.
3029
OTHERFLAGS += --warn_error -321
3130
OTHERFLAGS += --warn_error @247 # couldn't write a checked file? FAIL RIGHT NOW

pulse2rust/dpe/c.Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ INCLUDE_PATHS += $(DICE_DIR)/external $(DICE_DIR)/dpe $(DICE_DIR)/engine $(DICE_
1616
INCLUDE_PATHS += $(PULSE_ROOT)/out/lib/pulse
1717
ROOTS := $(DICE_DIR)/dpe/DPE.fst
1818
# ALREADY_CACHED_LIST = *,-HACL,-EverCrypt,-Spec.Hash.Definitions,-L0Core
19-
OTHERFLAGS += --warn_error -342 --cmi
19+
OTHERFLAGS += --warn_error -342
2020
DEPFLAGS += --extract '+EverCrypt +L0Core'
2121
all: verify extract
2222

pulse2rust/tests/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ OTHERFLAGS += --include $(PULSE_EXAMPLES_ROOT)/dice/common/hacl-c/_cache
2626
OTHERFLAGS += --include $(PULSE_ROOT)/test
2727
OTHERFLAGS += --include $(PULSE_ROOT)/test/_cache
2828

29-
OTHERFLAGS += --warn_error -342 --cmi
29+
OTHERFLAGS += --warn_error -342
3030
OTHERFLAGS += --include $(PULSE_ROOT)/out/lib/pulse
3131

3232
include $(PULSE_ROOT)/mk/boot.mk

share/pulse/examples/dice/c.Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ PULSE_ROOT ?= ../../../..
77
SRC=.
88
CACHE_DIR=_cache
99
OTHERFLAGS += --include $(PULSE_ROOT)/out/lib/pulse
10-
OTHERFLAGS += --warn_error -342 --cmi
10+
OTHERFLAGS += --warn_error -342
1111
OUTPUT_DIR=_output
1212
CODEGEN=krml
1313
TAG=dicec

share/pulse/examples/dice/cbor/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ OTHERFLAGS += --include ../_cache
1111
# Note: ^ a bit of a hack. This directory can work independently of the
1212
# DPE directory above, but in a test we first run verify on DPE which
1313
# involves verifying everything here already, so this saves some time.
14-
OTHERFLAGS += --warn_error -342 --cmi
14+
OTHERFLAGS += --warn_error -342
1515
OUTPUT_DIR=_output
1616
CODEGEN=krml
1717
TAG=cbor
1818
ROOTS=$(shell find $(SRC) -type f -name '*.fst' -o -name '*.fsti')
1919
DEPFLAGS=--extract '* -FStar -Pulse -PulseCore'
20-
OTHERFLAGS += --cmi --already_cached '*,-CBOR.Pulse.Type,-CDDLExtractionTest'
20+
OTHERFLAGS += --already_cached '*,-CBOR.Pulse.Type,-CDDLExtractionTest'
2121
include $(PULSE_ROOT)/mk/boot.mk
2222

2323
.DEFAULT_GOAL := myall

src/checker/Pulse.Checker.If.fst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,18 @@ let check
4848
(pre:term)
4949
(post_hint:post_hint_opt g)
5050
(res_ppname:ppname)
51-
(b:term)
51+
(b:st_term)
5252
(e1 e2:st_term)
5353
(check:check_t)
5454
: T.Tac (checker_result_t g pre post_hint) =
5555

5656
let g = Pulse.Typing.Env.push_context g "check_if" e1.range in
5757

58-
let b =
59-
check_tot_term g b tm_bool in
58+
let b : term =
59+
match b.term with
60+
| Tm_Return { term=bt } -> check_tot_term g bt tm_bool
61+
| _ -> fail g (Some b.range) "check_if: expected a pure condition (Tm_Return); stateful conditions should have been elaborated away"
62+
in
6063

6164
let hyp = fresh g in
6265

@@ -129,7 +132,8 @@ let check
129132

130133
let c_typing = comp_typing_from_post_hint c post_hint' in
131134

132-
let if_st = wrst c (Tm_If { b; then_=e1; else_=e2; post=None }) in
135+
let b_st = mk_term (Tm_Return { expected_type = tm_bool; insert_eq = false; term = b }) e1.range in
136+
let if_st = wrst c (Tm_If { b=b_st; then_=e1; else_=e2; post=None }) in
133137
let d : st_typing_in_ctxt g pre (PostHint post_hint') =
134138
(| if_st, c |) in
135139

src/checker/Pulse.Checker.If.fsti

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ val check
2727
(pre:term)
2828
(post_hint:post_hint_opt g)
2929
(res_ppname:ppname)
30-
(b:term)
30+
(b:st_term)
3131
(e1 e2:st_term)
3232
(check:check_t)
3333
: T.Tac (checker_result_t g pre post_hint)

src/checker/Pulse.Checker.Match.fst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,14 +481,19 @@ let check
481481
(pre:term)
482482
(post_hint:post_hint_for_env g)
483483
(res_ppname:ppname)
484-
(sc:term)
484+
(sc0:st_term)
485485
(brs:list branch)
486486
(check:check_t)
487487
: T.Tac (checker_result_t g pre (PostHint post_hint))
488488
=
489489

490490
let g = Pulse.Typing.Env.push_context_no_range g "check_match" in
491491

492+
let sc : term =
493+
match sc0.term with
494+
| Tm_Return { term=sct } -> sct
495+
| _ -> fail g (Some sc0.range) "check_match: expected a pure scrutinee (Tm_Return); stateful scrutinees should have been elaborated away"
496+
in
492497
let sc_range = Pulse.RuntimeUtils.range_of_term sc in // save range, it gets lost otherwise
493498
let orig_brs = brs in
494499
let nbr = L.length brs in
@@ -538,7 +543,8 @@ let check
538543
(* Provable *)
539544
assume (L.map (fun br -> elab_pat br.pat) brs == elab_pats');
540545
let c_typing = comp_typing_from_post_hint c post_hint in
541-
let t = wtag (Some (ctag_of_comp_st c)) (Tm_Match {sc; returns_=None; brs}) in
546+
let sc_st = mk_term (Tm_Return { expected_type = sc_ty; insert_eq = false; term = sc }) sc_range in
547+
let t = wtag (Some (ctag_of_comp_st c)) (Tm_Match {sc=sc_st; returns_=None; brs}) in
542548

543549
checker_result_for_st_typing (| t, c |) res_ppname
544550
#pop-options

src/checker/Pulse.Checker.Match.fsti

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ val check
3131
(pre:term)
3232
(post_hint:post_hint_for_env g)
3333
(res_ppname:ppname)
34-
(sc:term)
34+
(sc:st_term)
3535
(brs:list branch)
3636
(check:check_t)
3737
: T.Tac (checker_result_t g pre (PostHint post_hint))

0 commit comments

Comments
 (0)