@@ -104,9 +104,42 @@ private def freshCondVar : LiftM Identifier := do
104104 modify fun s => { s with condCounter := n + 1 }
105105 return s! "$c_{ n} "
106106
107- private def addPrepend (stmt : StmtExprMd) : LiftM Unit :=
107+ private def prepend (stmt : StmtExprMd) : LiftM Unit :=
108108 modify fun s => { s with prependedStmts := stmt :: s.prependedStmts }
109109
110+ private def onlyKeepSideEffectStmtsAndLast (stmts : List StmtExprMd) : LiftM (List StmtExprMd) := do
111+ match stmts with
112+ | [] => return []
113+ | _ =>
114+ let last := stmts.getLast!
115+ let nonLast ← stmts.dropLast.flatMapM (fun s =>
116+ match s.val with
117+ | .LocalVariable .. => do
118+ -- This addPrepend is a hack to work around Core not having let expressions
119+ -- Otherwise we could keep them in the block
120+ prepend s
121+ pure []
122+ | .Assert _ => do
123+ -- Hack to work around Core not supporting assert expressions
124+ -- Otherwise we could keep them in the block
125+ prepend s
126+ pure []
127+ | .Assume _ => do
128+ -- Hack to work around Core not supporting assume expressions
129+ -- Otherwise we could keep them in the block
130+ prepend s
131+ pure []
132+
133+ /-
134+ Any other impure StmtExpr, like .Assign, .Exit or .Return,
135+ should already have been processed by translateExpr,
136+ so we can assume this StmtExpr is pure and can be dropped.
137+ TODO: currently .Exit and .Return are not processed by translateExpr, this is a bug
138+ -/
139+ | _ => pure []
140+ )
141+ return nonLast ++ [last]
142+
110143private def takePrepends : LiftM (List StmtExprMd) := do
111144 let stmts := (← get).prependedStmts
112145 modify fun s => { s with prependedStmts := [] }
@@ -171,15 +204,15 @@ and updates substitutions. The value should already be transformed by the caller
171204private def liftAssignExpr (targets : List StmtExprMd) (seqValue : StmtExprMd)
172205 (md : Imperative.MetaData Core.Expression) : LiftM Unit := do
173206 -- Prepend the assignment itself
174- addPrepend (⟨.Assign targets seqValue, md⟩)
207+ prepend (⟨.Assign targets seqValue, md⟩)
175208 -- Create a before-snapshot for each target and update substitutions
176209 for target in targets do
177210 match target.val with
178211 | .Identifier varName =>
179212 let snapshotName ← freshTempFor varName
180213 let varType ← computeType target
181214 -- Snapshot goes before the assignment (cons pushes to front)
182- addPrepend (⟨.LocalVariable snapshotName varType (some (⟨.Identifier varName, md⟩)), md⟩)
215+ prepend (⟨.LocalVariable snapshotName varType (some (⟨.Identifier varName, md⟩)), md⟩)
183216 setSubst varName snapshotName
184217 | _ => pure ()
185218
@@ -200,7 +233,7 @@ def transformExpr (expr : StmtExprMd) : LiftM StmtExprMd := do
200233 | .Hole false (some holeType) =>
201234 -- Nondeterministic typed hole: lift to a fresh variable with no initializer (havoc)
202235 let holeVar ← freshCondVar
203- addPrepend (bare (.LocalVariable holeVar holeType none))
236+ prepend (bare (.LocalVariable holeVar holeType none))
204237 return bare (.Identifier holeVar)
205238
206239 | .Assign targets value =>
@@ -235,12 +268,13 @@ def transformExpr (expr : StmtExprMd) : LiftM StmtExprMd := do
235268 return seqCall
236269 else
237270 -- Imperative call in expression position: lift it like an assignment
238- -- Order matters: assign must be prepended first (it's newest-first),
239- -- so that when reversed the var declaration comes before the call.
240271 let callResultVar ← freshCondVar
241272 let callResultType ← computeType expr
242- addPrepend (⟨.Assign [bare (.Identifier callResultVar)] seqCall, md⟩)
243- addPrepend (bare (.LocalVariable callResultVar callResultType none))
273+ let liftedCall := [
274+ ⟨ (.LocalVariable callResultVar callResultType none), md ⟩,
275+ ⟨.Assign [bare (.Identifier callResultVar)] seqCall, md⟩
276+ ]
277+ modify fun s => { s with prependedStmts := s.prependedStmts ++ liftedCall}
244278 return bare (.Identifier callResultVar)
245279
246280 | .IfThenElse cond thenBranch elseBranch =>
@@ -277,8 +311,8 @@ def transformExpr (expr : StmtExprMd) : LiftM StmtExprMd := do
277311 let condType ← computeType thenBranch
278312 -- IfThenElse added first (cons puts it deeper), then declaration (cons puts it on top)
279313 -- Output order: declaration, then if-then-else
280- addPrepend (⟨.IfThenElse seqCond thenBlock seqElse, md⟩)
281- addPrepend (bare (.LocalVariable condVar condType none))
314+ prepend (⟨.IfThenElse seqCond thenBlock seqElse, md⟩)
315+ prepend (bare (.LocalVariable condVar condType none))
282316 return bare (.Identifier condVar)
283317 else
284318 -- No assignments in branches — recurse normally
@@ -289,21 +323,9 @@ def transformExpr (expr : StmtExprMd) : LiftM StmtExprMd := do
289323 | none => pure none
290324 return ⟨.IfThenElse seqCond seqThen seqElse, md⟩
291325
292- | .Block stmts metadata =>
293- -- Block in expression position: lift all but last to prepends
294- match h_last : stmts.getLast? with
295- | none => return bare (.Block [] metadata)
296- | some last => do
297- have := List.mem_of_getLast? h_last
298-
299- -- Process all-but-last as statements and prepend them in order
300- let mut blockStmts : List StmtExprMd := []
301- for nonLastStatement in stmts.dropLast.attach do
302- have := List.dropLast_subset stmts nonLastStatement.property
303- blockStmts := blockStmts ++ (← transformStmt nonLastStatement)
304- for s in blockStmts.reverse do addPrepend s
305- -- Last element is the expression value
306- transformExpr last
326+ | .Block stmts labelOption =>
327+ let newStmts := (← stmts.reverse.mapM transformExpr).reverse
328+ return ⟨ .Block (← onlyKeepSideEffectStmtsAndLast newStmts) labelOption, md ⟩
307329
308330 | .LocalVariable name ty initializer =>
309331 -- If the substitution map has an entry for this variable, it was
@@ -314,9 +336,9 @@ def transformExpr (expr : StmtExprMd) : LiftM StmtExprMd := do
314336 match initializer with
315337 | some initExpr =>
316338 let seqInit ← transformExpr initExpr
317- addPrepend (⟨.LocalVariable name ty (some seqInit), expr.md⟩)
339+ prepend (⟨.LocalVariable name ty (some seqInit), expr.md⟩)
318340 | none =>
319- addPrepend (⟨.LocalVariable name ty none, expr.md⟩)
341+ prepend (⟨.LocalVariable name ty none, expr.md⟩)
320342 return ⟨.Identifier (← getSubst name), expr.md⟩
321343 else
322344 return expr
0 commit comments