Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5a6808e
fix: Core.formatProgram roundtrip issues (#1158)
MikaelMayer May 14, 2026
725dc86
Merge origin/main into issue-1158 branch
MikaelMayer May 14, 2026
eb37eca
chore: regenerate editor syntax files with Bv overflow functions
MikaelMayer May 14, 2026
2f3045a
fix: update expected transform output to match preserved variable names
MikaelMayer May 14, 2026
b18c0e9
fix: type-parameterize overflow ops in Translate and add SDivOverflow…
MikaelMayer May 14, 2026
e74350b
chore: regenerate emacs syntax file to match GenSyntax output
MikaelMayer May 14, 2026
4683739
feat: implement lhsArray translation, seq_empty grammar, and datatype…
MikaelMayer May 14, 2026
0af4a85
Merge remote-tracking branch 'origin/main' into issue-1158-core-forma…
MikaelMayer May 14, 2026
c3b37cd
fix: distinguish SNegOverflow/UNegOverflow in grammar, remove dead co…
MikaelMayer May 15, 2026
28aa718
ci: retry after transient infrastructure failures
MikaelMayer May 15, 2026
8c5a08c
Merge remote-tracking branch 'origin/main' into issue-1158-core-forma…
MikaelMayer May 15, 2026
61c96b1
Merge remote-tracking branch 'origin/main' into issue-1158-core-forma…
MikaelMayer May 15, 2026
a3a2011
Merge remote-tracking branch 'origin/main' into issue-1158-core-forma…
MikaelMayer May 15, 2026
743e4c9
review: use logError for unsupported BV widths, remove extra newline
MikaelMayer May 15, 2026
af99d91
refactor: extract bvTypeOfWidth helper to deduplicate width-to-type m…
MikaelMayer May 15, 2026
5c67077
refactor: use getLFuncCall in lappToExpr to decompose nested applicat…
MikaelMayer May 15, 2026
027b700
fix: update Loops test expected output for freeVars context
MikaelMayer May 15, 2026
5bfee94
refactor: use getLFuncCall in decomposeMapUpdate to decompose nested …
MikaelMayer May 16, 2026
3b08ebe
docs: update seqEmptyFunc comment — Sequence.empty is now parseable
MikaelMayer May 18, 2026
3f3a940
test: add Sequence.empty solver tests from PR #1036
MikaelMayer May 18, 2026
f212d39
revert: remove incorrectly-authored Sequence.empty tests
MikaelMayer May 18, 2026
abb0c2e
test: add Sequence.empty solver tests from PR #1036
atomb May 18, 2026
f7cce37
fix: make comment stateless — remove reference to 'new' syntax
MikaelMayer May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
program Core;

function f (x : int) : int;
axiom [f_positive]: forall __q0 : int :: f(__q0) > 0;
axiom [f_monotone]: forall __q0 : int :: forall __q1 : int :: __q0 < __q1 ==> f(__q0) < f(__q1);
axiom [f_positive]: forall x : int :: f(x) > 0;
axiom [f_monotone]: forall x : int :: forall y : int :: x < y ==> f(x) < f(y);
function g (x : int) : int;
function h (x : int) : int;
axiom [h_def]: forall __q0 : int :: h(__q0) == f(__q0) + 1;
axiom [h_def]: forall x : int :: h(x) == f(x) + 1;
procedure TestF (x : int, out result : int)
spec {
ensures [result_positive]: result > 0;
Expand Down
4 changes: 3 additions & 1 deletion Strata/Languages/Core/DDMTransform/ASTtoCST.lean
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ def funcToCST {M} [Inhabited M]
-- Convert preconditions
let preconds ← precondsToSpecElts func.preconditions
let bodyExpr ← lexprToExpr body 0
let inline? : Ann (Option (Inline M)) M := ⟨default, none⟩
let inline? : Ann (Option (Inline M)) M :=
Comment thread
MikaelMayer marked this conversation as resolved.
if func.attr.any (· == .inline) then ⟨default, some (.inline default)⟩
else ⟨default, none⟩
pure (.command_fndef default name typeArgs b r preconds bodyExpr inline?)
modify ToCSTContext.popScope
-- Register function name as free variable.
Expand Down
98 changes: 76 additions & 22 deletions Strata/Languages/Core/DDMTransform/FormatCore.lean
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ Known issues:
translation in the latter's metadata field and recover them in the future.

- Misc. formatting issues
-- Remove extra parentheses around constructors in datatypes, assignments,
etc.
-- Remove extra indentation from the last brace of a block or the `end`
keyword of a mutual block.
-/
Expand Down Expand Up @@ -295,7 +293,6 @@ def handleZeroaryOps {M} [Inhabited M] (name : String)
| .re .All => pure (.re_all default)
| .re .AllChar => pure (.re_allchar default)
| .re .None => pure (.re_none default)
-- TODO: seq_empty is not yet parseable (see Grammar.lean); handle here when added.
| _ => do
ToCSTM.logError "lopToExpr" "0-ary op not found" name
pure (.re_none default)
Expand Down Expand Up @@ -338,8 +335,19 @@ def handleUnaryOps {M} [Inhabited M] (name : String) (arg : CoreDDM.Expr M)
| .bv ⟨16, .SafeNeg⟩ | .bv ⟨16, .SafeUNeg⟩ => pure (.safeneg_expr default (.bv16 default) arg)
| .bv ⟨32, .SafeNeg⟩ | .bv ⟨32, .SafeUNeg⟩ => pure (.safeneg_expr default (.bv32 default) arg)
| .bv ⟨64, .SafeNeg⟩ | .bv ⟨64, .SafeUNeg⟩ => pure (.safeneg_expr default (.bv64 default) arg)
-- Overflow predicates: approximated as Bool.Not for CST printing
| .bv ⟨_, .SNegOverflow⟩ | .bv ⟨_, .UNegOverflow⟩ => pure (.not default arg)
-- Overflow predicates
| .bv ⟨w, .SNegOverflow⟩ =>
let bvTy := match w with
| 1 => CoreType.bv1 default | 8 => .bv8 default
| 16 => .bv16 default | 32 => .bv32 default
| _ => .bv64 default
Comment thread
MikaelMayer marked this conversation as resolved.
Outdated
pure (.bv_neg_overflow default bvTy arg)
| .bv ⟨w, .UNegOverflow⟩ =>
let bvTy := match w with
| 1 => CoreType.bv1 default | 8 => .bv8 default
| 16 => .bv16 default | 32 => .bv32 default
| _ => .bv64 default
Comment thread
shigoel marked this conversation as resolved.
Outdated
pure (.bv_uneg_overflow default bvTy arg)
-- Bitvector extract ops
| .bvExtract 8 7 7 => pure (.bvextract_7_7 default arg)
| .bvExtract 16 15 15 => pure (.bvextract_15_15 default arg)
Expand Down Expand Up @@ -386,14 +394,14 @@ def bvBinaryOpMap {M} [Inhabited M] :
(.SafeUAdd, fun ty arg1 arg2 => .safeadd_expr default ty arg1 arg2),
(.SafeUSub, fun ty arg1 arg2 => .safesub_expr default ty arg1 arg2),
(.SafeUMul, fun ty arg1 arg2 => .safemul_expr default ty arg1 arg2),
-- Overflow predicates: approximated as boolean ops for CST printing
(.SAddOverflow, fun _ty arg1 arg2 => .le default _ty arg1 arg2),
(.SSubOverflow, fun _ty arg1 arg2 => .le default _ty arg1 arg2),
(.SMulOverflow, fun _ty arg1 arg2 => .le default _ty arg1 arg2),
(.SDivOverflow, fun _ty arg1 arg2 => .le default _ty arg1 arg2),
(.UAddOverflow, fun _ty arg1 arg2 => .le default _ty arg1 arg2),
(.USubOverflow, fun _ty arg1 arg2 => .le default _ty arg1 arg2),
(.UMulOverflow, fun _ty arg1 arg2 => .le default _ty arg1 arg2)
-- Overflow predicates
(.SAddOverflow, fun ty arg1 arg2 => .bv_sadd_overflow default ty arg1 arg2),
(.SSubOverflow, fun ty arg1 arg2 => .bv_ssub_overflow default ty arg1 arg2),
(.SMulOverflow, fun ty arg1 arg2 => .bv_smul_overflow default ty arg1 arg2),
(.SDivOverflow, fun ty arg1 arg2 => .bv_sdiv_overflow default ty arg1 arg2),
(.UAddOverflow, fun ty arg1 arg2 => .bv_uadd_overflow default ty arg1 arg2),
(.USubOverflow, fun ty arg1 arg2 => .bv_usub_overflow default ty arg1 arg2),
(.UMulOverflow, fun ty arg1 arg2 => .bv_umul_overflow default ty arg1 arg2)
]

/-- Map from bitvector sizes to their corresponding type constructors -/
Expand Down Expand Up @@ -551,11 +559,19 @@ partial def lexprToExpr {M} [Inhabited M]
pure (.fvar default (ctx.allFreeVars.size))
| .ite _ c t f => liteToExpr c t f qLevel
| .eq _ e1 e2 => leqToExpr e1 e2 qLevel
| .op _ name _ => lopToExpr name.name []
| .op _ name ty => do
-- seq_empty needs the type annotation to render the explicit type parameter
if name.name == "Sequence.empty" then
let tyCST ← match ty with
| some (.tcons "Sequence" [ety]) => lmonoTyToCoreType ety
| _ => pure (CoreType.tvar default unknownTypeVar)
pure (.seq_empty default tyCST)
else
lopToExpr name.name []
| .app _ _ _ => lappToExpr e qLevel
| .abs _ prettyName ty body => labsToExpr prettyName ty body (qLevel + 1)
| .quant _ qkind _ ty trigger body =>
lquantToExpr qkind ty trigger body (qLevel + 1)
| .quant _ qkind prettyName ty trigger body =>
lquantToExpr qkind prettyName ty trigger body (qLevel + 1)

/-- Extract trigger patterns from Lambda's trigger expression representation -/
partial def extractTriggerPatterns {M} [Inhabited M]
Expand Down Expand Up @@ -609,11 +625,13 @@ partial def labsToExpr {M} [Inhabited M]
pure (.lambda default tyExpr dl bodyExpr)

partial def lquantToExpr {M} [Inhabited M]
(qkind : Lambda.QuantifierKind) (ty : Option Lambda.LMonoTy)
(qkind : Lambda.QuantifierKind) (prettyName : String)
(ty : Option Lambda.LMonoTy)
(trigger : Lambda.LExpr CoreLParams.mono) (body : Lambda.LExpr CoreLParams.mono)
(qLevel : Nat)
: ToCSTM M (CoreDDM.Expr M) := do
let name : Ann String M := ⟨default, mkQuantVarName (qLevel - 1)⟩
let varName := if prettyName.isEmpty then mkQuantVarName (qLevel - 1) else prettyName
let name : Ann String M := ⟨default, varName⟩
modify ToCSTContext.pushScope
modify (·.addScopedBoundVars #[name.val])
let tyExpr ← match ty with
Expand Down Expand Up @@ -717,7 +735,9 @@ def funcDeclToStatement {M} [Inhabited M] (decl : Imperative.PureFunc Expression
let paramNames := results.map (·.2)
let b : Bindings M := .mkBindings default ⟨default, bindings⟩
let r ← lTyToCoreType decl.output
let inline? : Ann (Option (Inline M)) M := ⟨default, none⟩
let inline? : Ann (Option (Inline M)) M :=
if decl.attr.any (· == .inline) then ⟨default, some (.inline default)⟩
else ⟨default, none⟩
-- Add formals to the context
modify (·.addScopedBoundVars (reverse? := false) paramNames)
-- Convert preconditions
Expand All @@ -735,6 +755,29 @@ def funcDeclToStatement {M} [Inhabited M] (decl : Imperative.PureFunc Expression
modify (·.pushBoundVar name.val)
pure (.funcDecl_statement default name typeArgs b r preconds bodyExpr inline?)

/-- Decompose a single-level `map_update(base, idx, val)` where `base` is (or starts
with) an fvar matching `varName`. Returns `(indices, innerVal)` with indices
in left-to-right order, or `none` if the expression is not this pattern. -/
private def decomposeMapUpdate (varName : String)
(e : Lambda.LExpr CoreLParams.mono)
: Option (List (Lambda.LExpr CoreLParams.mono) × Lambda.LExpr CoreLParams.mono) :=
-- map_update(base, idx, val) = app(app(app(op "update" _) base) idx) val
match e with
| .app _ (.app _ (.app _ (.op _ opName _) base) idx) val =>
Comment thread
joscoh marked this conversation as resolved.
Outdated
if opName.name == "update" then
match base with
| .fvar _ ident _ =>
if ident.name == varName then some ([idx], val)
else none
-- Nested: map_update(map_update(fvar, i1, ...), i2, val) is not the
-- pattern we produce (we use map_update(fvar, i1, map_update(select(...), i2, val))).
-- For nested lhsArray like m[k1][k2] := v, the translation produces:
-- map_update(fvar(m), k1, map_update(map_select(fvar(m), k1), k2, v))
-- We detect this by checking if val is itself a map_update with a select base.
| _ => none
else none
| _ => none

mutual
/-- Convert `Core.Statement` to `CoreDDM.Statement` -/
partial def stmtToCST {M} [Inhabited M] (s : Core.Statement)
Expand All @@ -758,9 +801,20 @@ partial def stmtToCST {M} [Inhabited M] (s : Core.Statement)
modify (·.pushBoundVar name.toPretty)
pure result
| .set name expr _md => do
let lhs := Lhs.lhsIdent default ⟨default, name.name⟩
let exprCST ← lexprToExpr expr 0
-- Type annotation required by CST but not semantically used.
-- Detect map_update(name, idx, val) pattern to produce lhsArray syntax
let (lhs, exprCST) ← match decomposeMapUpdate name.name expr with
| some (idxs, val) => do
let baseLhs := Lhs.lhsIdent default ⟨default, name.name⟩
let lhs ← idxs.foldlM (init := baseLhs) fun acc idx => do
let idxCST ← lexprToExpr idx 0
let tyCST := CoreType.tvar default unknownTypeVar
pure (Lhs.lhsArray default tyCST acc idxCST)
let valCST ← lexprToExpr val 0
pure (lhs, valCST)
| none => do
let lhs := Lhs.lhsIdent default ⟨default, name.name⟩
let exprCST ← lexprToExpr expr 0
pure (lhs, exprCST)
let tyCST := CoreType.tvar default unknownTypeVar
pure (.assign default tyCST lhs exprCST)
| .havoc name _md => do
Expand Down
18 changes: 14 additions & 4 deletions Strata/Languages/Core/DDMTransform/Grammar.lean
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ fn map_get (K : Type, V : Type, m : Map K V, k : K) : V => m "[" k "]";
fn map_set (K : Type, V : Type, m : Map K V, k : K, v : V) : Map K V =>
m "[" k ":=" v "]";

// TODO: seq_empty is not yet supported in the grammar because the DDM parser
// cannot currently handle 0-ary polymorphic functions (no arguments to infer
// the type parameter from). The Factory definition exists for programmatic use.
// seq_empty uses explicit type annotation syntax since there are no value
// arguments to infer the type parameter from.
fn seq_empty (A : Type) : Sequence A => "Sequence.empty" "<" A ">" "(" ")";
Comment thread
joscoh marked this conversation as resolved.
fn seq_length (A : Type, s : Sequence A) : int => "Sequence.length" "(" s ")";
fn seq_select (A : Type, s : Sequence A, i : int) : A => "Sequence.select" "(" s ", " i ")";
fn seq_append (A : Type, s1 : Sequence A, s2 : Sequence A) : Sequence A =>
Expand Down Expand Up @@ -188,6 +188,16 @@ fn bvsle (tp : Type, a : tp, b : tp) : bool => @[prec(20), leftassoc] a " <=s "
fn bvsgt (tp : Type, a : tp, b : tp) : bool => @[prec(20), leftassoc] a " >s " b;
fn bvsge (tp : Type, a : tp, b : tp) : bool => @[prec(20), leftassoc] a " >=s " b;

fn bv_neg_overflow (tp : Type, a : tp) : bool => "Bv.SNegOverflow" "(" a ")";
fn bv_uneg_overflow (tp : Type, a : tp) : bool => "Bv.UNegOverflow" "(" a ")";
fn bv_sadd_overflow (tp : Type, a : tp, b : tp) : bool => "Bv.SAddOverflow" "(" a ", " b ")";
fn bv_ssub_overflow (tp : Type, a : tp, b : tp) : bool => "Bv.SSubOverflow" "(" a ", " b ")";
fn bv_smul_overflow (tp : Type, a : tp, b : tp) : bool => "Bv.SMulOverflow" "(" a ", " b ")";
fn bv_sdiv_overflow (tp : Type, a : tp, b : tp) : bool => "Bv.SDivOverflow" "(" a ", " b ")";
fn bv_uadd_overflow (tp : Type, a : tp, b : tp) : bool => "Bv.UAddOverflow" "(" a ", " b ")";
fn bv_usub_overflow (tp : Type, a : tp, b : tp) : bool => "Bv.USubOverflow" "(" a ", " b ")";
fn bv_umul_overflow (tp : Type, a : tp, b : tp) : bool => "Bv.UMulOverflow" "(" a ", " b ")";

fn bvconcat8 (a : bv8, b : bv8) : bv16 => "bvconcat{8}{8}" "(" a ", " b ")";
fn bvconcat16 (a : bv16, b : bv16) : bv32 => "bvconcat{16}{16}" "(" a ", " b ")";
fn bvconcat32 (a : bv32, b : bv32) : bv64 => "bvconcat{32}{32}" "(" a ", " b ")";
Expand Down Expand Up @@ -355,7 +365,7 @@ op command_fndecl (name : Ident,
"function " name typeArgs b " : " r ";\n";

category Inline;
op inline () : Inline => "inline";
op inline () : Inline => "inline ";

// Note: when editing command_fndef, consider whether recfn_decl needs
// matching edits.
Expand Down
Loading
Loading