Skip to content

Commit e912c9b

Browse files
kondylidouclaude
authored andcommitted
feat(Core): add Bv{n}.ToUInt, Bv{n}.ToInt, Int.ToBv{n} cast operators
Add three cross-sort conversion operators to Core: - Bv{n}.ToUInt : Bv{n} → Int (SMT-LIB ubv_to_int) - Bv{n}.ToInt : Bv{n} → Int (SMT-LIB sbv_to_int) - Int.ToBv{n} : Int → Bv{n} (SMT-LIB int_to_bv n) Supported widths: 1, 8, 16, 32, 64, 128. All three are total. Changes: - Core DDMTransform/Grammar: as_uint, as_sint, as_bv{1..128} built-in funcs - Core DDMTransform/Translate: dispatch for the new grammar functions - DL/SMT/Translate: handle bv2nat / sbv2int / int_to_bv - editors/vscode: regenerated syntax highlighting - BvIntCastVerifyTests.lean: rewritten using new function syntax Split from #1218. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7b40c42 commit e912c9b

5 files changed

Lines changed: 97 additions & 58 deletions

File tree

Strata/DL/SMT/Translate.lean

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -534,11 +534,13 @@ def translateTerm (t : SMT.Term) : TranslateM (Expr × Expr) := do
534534
let w ← getBitVecWidth α
535535
return (mkBitVec (w + i), mkApp3 (.const ``BitVec.zeroExtend []) (toExpr w) (toExpr (w + i)) x)
536536
| .app .ubv_to_int [x] _ =>
537-
let (_, x) ← translateTerm x
538-
return (mkInt, mkApp (.const ``Int.ofNat []) (mkApp (.const ``BitVec.toNat []) x))
537+
let (α, x) ← translateTerm x
538+
let w ← getBitVecWidth α
539+
return (mkInt, mkApp (.const ``Int.ofNat []) (mkApp2 (.const ``BitVec.toNat []) (toExpr w) x))
539540
| .app .sbv_to_int [x] _ =>
540-
let (_, x) ← translateTerm x
541-
return (mkInt, mkApp (.const ``BitVec.toInt []) x)
541+
let (α, x) ← translateTerm x
542+
let w ← getBitVecWidth α
543+
return (mkInt, mkApp2 (.const ``BitVec.toInt []) (toExpr w) x)
542544
| .app (.int_to_bv n) [x] _ =>
543545
let (_, x) ← translateTerm x
544546
return (mkBitVec n, mkApp2 (.const ``BitVec.ofInt []) (toExpr n) x)

Strata/Languages/Core/DDMTransform/Grammar.lean

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ fn bv16Lit (n : Num) : bv16 => "bv{16}" "(" n ")";
9393
fn bv32Lit (n : Num) : bv32 => "bv{32}" "(" n ")";
9494
fn bv64Lit (n : Num) : bv64 => "bv{64}" "(" n ")";
9595
fn bv128Lit (n : Num) : bv128 => "bv{128}" "(" n ")";
96+
97+
fn as_uint (T : Type, e : T) : int => "as_uint" "(" e ")";
98+
fn as_sint (T : Type, e : T) : int => "as_sint" "(" e ")";
99+
fn as_bv1 (e : int) : bv1 => "as_bv1" "(" e ")";
100+
fn as_bv8 (e : int) : bv8 => "as_bv8" "(" e ")";
101+
fn as_bv16 (e : int) : bv16 => "as_bv16" "(" e ")";
102+
fn as_bv32 (e : int) : bv32 => "as_bv32" "(" e ")";
103+
fn as_bv64 (e : int) : bv64 => "as_bv64" "(" e ")";
104+
fn as_bv128 (e : int) : bv128 => "as_bv128" "(" e ")";
96105
fn strLit (s : Str) : string => s;
97106
fn realLit (d : Decimal) : real => d;
98107

Strata/Languages/Core/DDMTransform/Translate.lean

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,9 @@ def translateFn (ty? : Option LMonoTy) (q : QualifiedIdent) : TransM Core.Expres
715715
| .some .bv64, q`Core.bv_usub_overflow => return Core.bv64USubOverflowOp
716716
| .some .bv64, q`Core.bv_umul_overflow => return Core.bv64UMulOverflowOp
717717

718+
| .some (.bitvec n), q`Core.as_uint => return .op () ⟨s!"Bv{n}.ToUInt", ()⟩ (.some (.arrow (.bitvec n) .int))
719+
| .some (.bitvec n), q`Core.as_sint => return .op () ⟨s!"Bv{n}.ToInt", ()⟩ (.some (.arrow (.bitvec n) .int))
720+
718721
| _, q`Core.str_len => return Core.strLengthOp
719722
| _, q`Core.str_concat => return Core.strConcatOp
720723
| _, q`Core.str_substr => return Core.strSubstrOp
@@ -878,6 +881,24 @@ partial def translateExpr (p : Program) (bindings : TransBindings) (arg : Arg) :
878881
let fn : LExpr Core.CoreLParams.mono ←
879882
translateFn (.some tp) q`Core.bvnot
880883
return (.app () fn x)
884+
-- Bv → Int casts
885+
| .fn _ q`Core.as_uint, [tpa, xa] =>
886+
let tp ← translateLMonoTy bindings (dealiasTypeArg p tpa)
887+
let x ← translateExpr p bindings xa
888+
let fn ← translateFn (.some tp) q`Core.as_uint
889+
return .app () fn x
890+
| .fn _ q`Core.as_sint, [tpa, xa] =>
891+
let tp ← translateLMonoTy bindings (dealiasTypeArg p tpa)
892+
let x ← translateExpr p bindings xa
893+
let fn ← translateFn (.some tp) q`Core.as_sint
894+
return .app () fn x
895+
-- Int → Bv casts
896+
| .fn _ q`Core.as_bv1, [xa] => return .app () (.op () ⟨"Int.ToBv1", ()⟩ (.some (.arrow .int (.bitvec 1)))) (← translateExpr p bindings xa)
897+
| .fn _ q`Core.as_bv8, [xa] => return .app () (.op () ⟨"Int.ToBv8", ()⟩ (.some (.arrow .int (.bitvec 8)))) (← translateExpr p bindings xa)
898+
| .fn _ q`Core.as_bv16, [xa] => return .app () (.op () ⟨"Int.ToBv16", ()⟩ (.some (.arrow .int (.bitvec 16)))) (← translateExpr p bindings xa)
899+
| .fn _ q`Core.as_bv32, [xa] => return .app () (.op () ⟨"Int.ToBv32", ()⟩ (.some (.arrow .int (.bitvec 32)))) (← translateExpr p bindings xa)
900+
| .fn _ q`Core.as_bv64, [xa] => return .app () (.op () ⟨"Int.ToBv64", ()⟩ (.some (.arrow .int (.bitvec 64)))) (← translateExpr p bindings xa)
901+
| .fn _ q`Core.as_bv128, [xa] => return .app () (.op () ⟨"Int.ToBv128", ()⟩ (.some (.arrow .int (.bitvec 128)))) (← translateExpr p bindings xa)
881902
-- If-then-else expression
882903
| .fn _ q`Core.if, [_tpa, ca, ta, fa] =>
883904
let c ← translateExpr p bindings ca

StrataTest/Languages/Core/Tests/BvIntCastVerifyTests.lean

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,71 @@
33
44
SPDX-License-Identifier: Apache-2.0 OR MIT
55
-/
6+
module
67

7-
import Strata.Languages.Core.Verifier
8-
import Strata.Languages.Core.Factory
9-
import Strata.SimpleAPI
8+
meta import Strata.Languages.Core
9+
import StrataDDM.Integration.Lean.HashCommands
1010

1111
/-!
12-
End-to-end verification tests for the three Bv↔Int cast operators,
13-
exercised all the way through the SMT pipeline via `Strata.Core.verifyProgram`.
12+
End-to-end verification tests for the three Bv↔Int cast built-in functions,
13+
exercised all the way through the SMT pipeline via `Core.verify`.
1414
15-
Factory ops (`Bv{n}.ToUInt`, `Bv{n}.ToInt`, `Int.ToBv{n}`) cannot be called
16-
from `#strata program Core;` text, so programs are constructed programmatically
17-
using the Lean API.
18-
19-
- `Bv{n}.ToUInt` ≙ SMT-LIB 2.7 `ubv_to_int` — unsigned bv → Int
20-
- `Bv{n}.ToInt` ≙ SMT-LIB 2.7 `sbv_to_int` — signed bv → Int
21-
- `Int.ToBv{n}` ≙ SMT-LIB 2.7 `(_ int_to_bv n)` — Int → bv
15+
- `as_uint(e)` ≙ SMT-LIB 2.7 `ubv_to_int` — unsigned bv → Int
16+
- `as_sint(e)` ≙ SMT-LIB 2.7 `sbv_to_int` — signed bv → Int
17+
- `as_bv8(e)` ≙ SMT-LIB 2.7 `(_ int_to_bv 8)` — Int → bv8
2218
-/
2319

24-
namespace Core.BvIntCastVerify
25-
26-
open Lambda Core
27-
28-
private def xBv8 : Expression.Expr := .fvar () ⟨"x", ()⟩ (.some (.bitvec 8))
29-
private def bv8255 : Expression.Expr := .bitvecConst () 8 (255 : BitVec 8)
30-
31-
private def zero : Expression.Expr := .intConst () 0
32-
private def i255 : Expression.Expr := .intConst () 255
33-
private def i256 : Expression.Expr := .intConst () 256
34-
private def negOne : Expression.Expr := .intConst () (-1)
35-
36-
private def applyGe (l r : Expression.Expr) : Expression.Expr :=
37-
.app () (.app () intGeOp l) r
38-
39-
private def mkProc (name : String) (postcond : Expression.Expr) : Decl :=
20+
meta section
21+
open Strata
22+
open StrataDDM (Program)
23+
24+
private def bvIntCastProgram : Program :=
25+
#strata
26+
program Core;
27+
28+
procedure test_ubv_nonneg(x : bv8)
29+
spec {
30+
ensures as_uint(x) >= 0;
31+
}
32+
{
33+
assume true;
34+
};
35+
36+
procedure test_ubv_concrete()
37+
spec {
38+
ensures as_uint(bv{8}(255)) == 255;
39+
}
40+
{
41+
assume true;
42+
};
43+
44+
procedure test_ubv_roundtrip(x : bv8)
45+
spec {
46+
ensures as_bv8(as_uint(x)) == x;
47+
}
48+
{
49+
assume true;
50+
};
51+
52+
procedure test_sbv_concrete()
53+
spec {
54+
ensures as_sint(bv{8}(255)) == -1;
55+
}
56+
{
57+
assume true;
58+
};
59+
60+
procedure test_ubv_impossible(x : bv8)
61+
spec {
62+
ensures as_uint(x) >= 256;
63+
}
64+
{
65+
assume true;
66+
};
67+
68+
#end
69+
70+
private def mkProc (name : String) (postcond : Core.Expression.Expr) : Core.Decl :=
4071
.proc {
4172
header := {
4273
name := ⟨name, ()⟩
@@ -51,25 +82,6 @@ private def mkProc (name : String) (postcond : Expression.Expr) : Decl :=
5182
body := .structured [.assume "body" (.true ()) #[]]
5283
} #[]
5384

54-
private def castVerifyProg : Core.Program :=
55-
{ decls := [
56-
-- Provable: Bv8.ToUInt is always nonneg
57-
mkProc "test_ubv_nonneg"
58-
(applyGe (.app () bv8ToUIntFunc.opExpr xBv8) zero),
59-
-- Provable: concrete value bv{8}(255) as unsigned == 255
60-
mkProc "test_ubv_concrete"
61-
(.eq () (.app () bv8ToUIntFunc.opExpr bv8255) i255),
62-
-- Provable: unsigned round-trip Int.ToBv8(Bv8.ToUInt(x)) == x
63-
mkProc "test_ubv_roundtrip"
64-
(.eq () (.app () int8ToBvFunc.opExpr (.app () bv8ToUIntFunc.opExpr xBv8)) xBv8),
65-
-- Provable: signed semantics bv{8}(255) as signed == -1
66-
mkProc "test_sbv_concrete"
67-
(.eq () (.app () bv8ToIntFunc.opExpr bv8255) negOne),
68-
-- Failing: Bv8.ToUInt(x) >= 256 is impossible for 8-bit
69-
mkProc "test_ubv_impossible"
70-
(applyGe (.app () bv8ToUIntFunc.opExpr xBv8) i256),
71-
] }
72-
7385
/--
7486
info:
7587
Obligation: test_ubv_nonneg_ensures_0
@@ -93,9 +105,4 @@ Property: assert
93105
Result: ❌ fail
94106
-/
95107
#guard_msgs in
96-
#eval show IO Unit from do
97-
let results ← EIO.toIO (fun e => IO.Error.userError e)
98-
(Strata.Core.verifyProgram castVerifyProg Core.VerifyOptions.quiet)
99-
IO.println (toString results)
100-
101-
end Core.BvIntCastVerify
108+
#eval Strata.Core.verify bvIntCastProgram (options := .quiet)

editors/vscode/syntaxes/core-st.tmLanguage.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
},
8080
{
8181
"name": "keyword.operator.symbol.core-st",
82-
"match": "(safe_neg|safe-|safe\\+|safe\\*|<==>|>=s|==>|>>s|<=s|/t|\\|\\||>=|%t|<<|>>|&&|<=|==|!=|<s|>s|>|\\^|\\+|\\*|/|%|!|-|~|&|<|\\|)"
82+
"match": "(safe_neg|as_bv128|as_bv32|as_bv64|as_sint|as_bv16|as_uint|as_bv1|as_bv8|safe-|safe\\+|safe\\*|<==>|>=s|==>|>>s|<=s|!=|/t|%t|<<|>>|&&|\\|\\||==|<s|<=|>=|>s|!|\\^|-|\\+|\\*|/|%|<|>|~|&|\\|)"
8383
}
8484
]
8585
},

0 commit comments

Comments
 (0)