Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -92,6 +92,10 @@ partial def translateHighType (arg : Arg) : TransM HighTypeMd := do
| q`Laurel.float64Type, _ => return mkHighTypeMd .TFloat64 md
| q`Laurel.realType, _ => return mkHighTypeMd .TReal md
| q`Laurel.stringType, _ => return mkHighTypeMd .TString md
| q`Laurel.bv8Type, _ => return mkHighTypeMd (.TBv 8) md
Copy link
Copy Markdown
Contributor

@keyboardDrummer keyboardDrummer Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we abstract over the amount of bits? Also, I think the right default SMT encoding is to use an int with constraints, not an SMT bitvector. Until Core supports that, can we really define this with the correct verification behavior in Laurel? Right now I think the front-ends can't use these types because of the default SMT bitvector behavior, so it seems confusing to add them.

Also, should we think about this end-to-end? How will users indicate what SMT encoding they want to use for particular variables? Can we infer the probably best encoding based on which operators the variables are applied to? My guess would be that a good heuristic is that if no bit specific operators are used, use an int and otherwise a bv.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we abstract over the amount of bits?

In principle we could, but then Core has hard-coded bit widths only (I don't know why). So it would require a change in Core first.

Also, I think the right default SMT encoding is to use an int with constraints, not an SMT bitvector.

Why do you believe this to be the case?

Until Core supports that, can we really define this with the correct verification behavior in Laurel? Right now I think the front-ends can't use these types because of the default SMT bitvector behavior, so it seems confusing to add them.

I have a changed queued up to add transforms between constrained types and bitvectors, but then it only makes sense to add that if we have tests (#656) and language support (this PR) in the first place.

Also, should we think about this end-to-end? How will users indicate what SMT encoding they want to use for particular variables? Can we infer the probably best encoding based on which operators the variables are applied to? My guess would be that a good heuristic is that if no bit specific operators are used, use an int and otherwise a bv.

I'd love to conduct all these experiments, but it seems impossible to do them when we don't have the necessary infrastructure in place first.

| q`Laurel.bv16Type, _ => return mkHighTypeMd (.TBv 16) md
| q`Laurel.bv32Type, _ => return mkHighTypeMd (.TBv 32) md
| q`Laurel.bv64Type, _ => return mkHighTypeMd (.TBv 64) md
| q`Laurel.coreType, #[.ident _ name] => return mkHighTypeMd (.TCore name) md
| q`Laurel.mapType, #[keyArg, valArg] =>
let keyType ← translateHighType keyArg
Expand All @@ -100,7 +104,7 @@ partial def translateHighType (arg : Arg) : TransM HighTypeMd := do
| q`Laurel.compositeType, #[nameArg] =>
let name ← translateIdent nameArg
return mkHighTypeMd (.UserDefined name) md
| _, _ => TransM.error s!"translateHighType expects intType, boolType, arrayType or compositeType, got {repr op.name}"
| _, _ => TransM.error s!"translateHighType: unsupported type operator {repr op.name}"
| _ => TransM.error s!"translateHighType expects operation"

def translateNat (arg : Arg) : TransM Nat := do
Expand Down
2 changes: 1 addition & 1 deletion Strata/Languages/Laurel/Grammar/LaurelGrammar.lean
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module
-- Laurel dialect definition, loaded from LaurelGrammar.st
-- NOTE: Changes to LaurelGrammar.st are not automatically tracked by the build system.
-- Update this file (e.g. this comment) to trigger a recompile after modifying LaurelGrammar.st.
-- Last grammar change: replaced regexType with general coreType
-- Last grammar change: added bv8, bv16, bv32, bv64 types
public import Strata.DDM.Integration.Lean
public meta import Strata.DDM.Integration.Lean

Expand Down
4 changes: 4 additions & 0 deletions Strata/Languages/Laurel/Grammar/LaurelGrammar.st
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ op boolType : LaurelType => "bool";
op realType : LaurelType => "real";
op float64Type : LaurelType => "float64";
op stringType : LaurelType => "string";
op bv8Type : LaurelType => "bv8";
op bv16Type : LaurelType => "bv16";
op bv32Type : LaurelType => "bv32";
op bv64Type : LaurelType => "bv64";
// Core type passthrough: parsed as Ident, translated to HighType.TCore
op coreType (name: Ident): LaurelType => "Core " name;
op mapType (keyType: LaurelType, valueType: LaurelType): LaurelType => "Map " keyType " " valueType;
Expand Down
3 changes: 3 additions & 0 deletions Strata/Languages/Laurel/Laurel.lean
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ inductive HighType : Type where
| Pure (base : WithMetadata HighType)
/-- An intersection of types. Used for implicit intersection types, e.g. `Scientist & Scandinavian`. -/
| Intersection (types : List (WithMetadata HighType))
/-- Bitvector type of a given width. -/
| TBv (size : Nat)
/-- Temporary construct meant to aid the migration of Python->Core to Python->Laurel.
Type "passed through" from Core. Intended to allow translations to Laurel to refer directly to Core. -/
| TCore (s: String)
Expand Down Expand Up @@ -346,6 +348,7 @@ def highEq (a : HighTypeMd) (b : HighTypeMd) : Bool := match _a: a.val, _b: b.va
| HighType.TReal, HighType.TReal => true
| HighType.TString, HighType.TString => true
| HighType.THeap, HighType.THeap => true
| HighType.TBv n1, HighType.TBv n2 => n1 == n2
| HighType.TTypedField t1, HighType.TTypedField t2 => highEq t1 t2
| HighType.TSet t1, HighType.TSet t2 => highEq t1 t2
| HighType.TMap k1 v1, HighType.TMap k2 v2 => highEq k1 k2 && highEq v1 v2
Expand Down
1 change: 1 addition & 0 deletions Strata/Languages/Laurel/LaurelFormat.lean
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def formatHighTypeVal : HighType → Format
| .TReal => "real"
| .TString => "string"
| .THeap => "Heap"
| .TBv n => s!"bv{n}"
| .TTypedField valueType => "Field[" ++ formatHighType valueType ++ "]"
| .TSet elementType => "Set[" ++ formatHighType elementType ++ "]"
| .TMap keyType valueType => "Map[" ++ formatHighType keyType ++ ", " ++ formatHighType valueType ++ "]"
Expand Down
1 change: 1 addition & 0 deletions Strata/Languages/Laurel/LaurelToCoreTranslator.lean
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def translateType (model : SemanticModel) (ty : HighTypeMd) : LMonoTy :=
| .TInt => LMonoTy.int
| .TBool => LMonoTy.bool
| .TString => LMonoTy.string
| .TBv n => LMonoTy.bitvec n
| .TVoid => LMonoTy.bool -- Using bool as placeholder for void
| .THeap => .tcons "Heap" []
| .TTypedField _ => .tcons "Field" []
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/-
Copyright Strata Contributors

SPDX-License-Identifier: Apache-2.0 OR MIT
-/

import StrataTest.Util.TestDiagnostics
import StrataTest.Languages.Laurel.TestExamples

open StrataTest.Util

namespace Strata
namespace Laurel

def bvProgram := r"
// Bitvector types in procedure signatures and variable declarations.

// Parameters and return types
procedure identity32(x: bv32) returns (r: bv32) {
r := x
};

procedure identity8(x: bv8) returns (r: bv8) {
r := x
};

// Local variable with bv type
procedure localBv() returns (r: bv16) {
var x: bv16 := r;
r := x
};

// Opaque procedure returning bv64 — caller gets typed result
procedure opaqueBv64() returns (r: bv64);
procedure callOpaque() returns (r: bv64) {
r := opaqueBv64()
};

// Mixed bv and int parameters
procedure mixedTypes(a: bv32, b: int) returns (r: int) {
r := b
};
"

#guard_msgs(drop info, error) in
#eval testInputWithOffset "BitvectorTypes" bvProgram 14 processLaurelFile

end Laurel
end Strata
14 changes: 14 additions & 0 deletions StrataTest/Languages/Laurel/tests/test_bitvector_types.lr.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Bitvector types through the GOTO/CBMC pipeline.

procedure identity32(x: bv32) returns (r: bv32) {
r := x
};

procedure identity8(x: bv8) returns (r: bv8) {
r := x
};

procedure localBv() returns (r: bv16) {
var x: bv16 := r;
r := x
};
3 changes: 2 additions & 1 deletion StrataTest/Languages/Python/ToLaurelTest.lean
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ private def fmtHighType : HighType → String
| .Applied _ _ => "Applied"
| .Pure _ => "Pure"
| .Intersection _ => "Intersection"
| .TBv n => s!"TBv({n})"
| .TCore s => s!"TCore({s})"
| HighType.Unknown => "Unknown"
| .Unknown => "Unknown"

private def fmtParam (p : Parameter) : String :=
s!"{p.name}:{fmtHighType p.type.val}"
Expand Down
Loading