diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c912b43a..8b71f4622 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@ Changes to the Lean backend:
not introduce extra `do` block (#1746)
- Rename `Result` monad to `RustM` to avoid confusion with Rust `Result` type (#1768)
- Add support for default methods of traits (#1777)
+ - Add support for floats (#1784)
- Add support for pattern matching on constant literals (#1789)
Miscellaneous:
diff --git a/hax-lib/proof-libs/lean/Hax/Float.lean b/hax-lib/proof-libs/lean/Hax/Float.lean
new file mode 100644
index 000000000..cb85abc11
--- /dev/null
+++ b/hax-lib/proof-libs/lean/Hax/Float.lean
@@ -0,0 +1,27 @@
+import Hax.Lib
+import Hax.Integers.Ops
+
+abbrev f32 := Float32
+abbrev f64 := Float
+
+macro "declare_float_ops" typeName:ident : command =>
+ `(
+ namespace $typeName
+
+ instance : HaxAdd $typeName where
+ add := fun x y => x + y
+
+ instance : HaxSub $typeName where
+ sub := fun x y => x - y
+
+ instance : HaxMul $typeName where
+ mul := fun x y => x * y
+
+ instance : HaxDiv $typeName where
+ div := fun x y => x / y
+
+ end $typeName
+ )
+
+declare_float_ops f32
+declare_float_ops f64
diff --git a/hax-lib/proof-libs/lean/Hax/Rust_primitives.lean b/hax-lib/proof-libs/lean/Hax/Rust_primitives.lean
index bfd84298a..c05e7d11e 100644
--- a/hax-lib/proof-libs/lean/Hax/Rust_primitives.lean
+++ b/hax-lib/proof-libs/lean/Hax/Rust_primitives.lean
@@ -1,2 +1,3 @@
import Hax.Integers.Ops
import Hax.Integers.Spec
+import Hax.Float
diff --git a/rust-engine/src/backends/lean.rs b/rust-engine/src/backends/lean.rs
index d5f6cbf11..249a0aa0c 100644
--- a/rust-engine/src/backends/lean.rs
+++ b/rust-engine/src/backends/lean.rs
@@ -430,9 +430,6 @@ set_option linter.unusedVariables false
fn expr(&self, Expr { kind, ty, meta: _ }: &Expr) -> DocBuilder {
match &**kind {
- ExprKind::Literal(int_lit @ Literal::Int { .. }) => {
- docs![int_lit, reflow!(" : "), ty].parens().group()
- }
ExprKind::If {
condition,
then,
@@ -477,6 +474,9 @@ set_option linter.unusedVariables false
}
}
}
+ ExprKind::Literal(numeric_lit @ (Literal::Float { .. } | Literal::Int { .. })) => {
+ docs![numeric_lit, reflow!(" : "), ty].parens().group()
+ }
ExprKind::Literal(literal) => docs![literal],
ExprKind::Array(exprs) => docs![
"#v[",
@@ -786,7 +786,11 @@ set_option linter.unusedVariables false
negative,
kind: _,
} => format!("{}{value}", if *negative { "-" } else { "" }),
- Literal::Float { .. } => emit_error!(issue 1715, "Unsupported Float literal"),
+ Literal::Float {
+ value,
+ negative,
+ kind: _,
+ } => format!("{}{value}", if *negative { "-" } else { "" }),
}]
}
@@ -803,7 +807,7 @@ set_option linter.unusedVariables false
match primitive_ty {
PrimitiveTy::Bool => docs!["Bool"],
PrimitiveTy::Int(int_kind) => docs![int_kind],
- PrimitiveTy::Float(_) => emit_error!(issue 1715, "Unsupported Float type"),
+ PrimitiveTy::Float(float_kind) => docs![float_kind],
PrimitiveTy::Char => docs!["Char"],
PrimitiveTy::Str => docs!["String"],
}
@@ -826,6 +830,14 @@ set_option linter.unusedVariables false
}]
}
+ fn float_kind(&self, float_kind: &FloatKind) -> DocBuilder {
+ docs![match float_kind {
+ FloatKind::F32 => "f32",
+ FloatKind::F64 => "f64",
+ _ => emit_error!(issue 1787, "The only supported float types are `f32` and `f64`."),
+ }]
+ }
+
fn generic_value(&self, generic_value: &GenericValue) -> DocBuilder {
match generic_value {
GenericValue::Ty(ty) => docs![ty],
@@ -1246,10 +1258,6 @@ set_option linter.unusedVariables false
unreachable_by_invariant!(Drop_references)
}
- fn float_kind(&self, _float_kind: &FloatKind) -> DocBuilder {
- emit_error!(issue 1715, "floats are unsupported")
- }
-
fn dyn_trait_goal(&self, _dyn_trait_goal: &DynTraitGoal) -> DocBuilder {
emit_error!(issue 1708, "`dyn` traits are unsupported")
}
diff --git a/test-harness/src/snapshots/toolchain__lean-tests into-lean.snap b/test-harness/src/snapshots/toolchain__lean-tests into-lean.snap
index 8466569a8..cf93dfa90 100644
--- a/test-harness/src/snapshots/toolchain__lean-tests into-lean.snap
+++ b/test-harness/src/snapshots/toolchain__lean-tests into-lean.snap
@@ -654,6 +654,20 @@ def Lean_tests.Loops.Errors.loop4
| (Core.Ops.Control_flow.ControlFlow.Continue e)
=> (pure (Core.Result.Result.Ok (Rust_primitives.Hax.Tuple2.mk e e)))
+def Lean_tests.Floats.N : f32 := RustM.of_isOk (do (pure (1.0 : f32))) (by rfl)
+
+def Lean_tests.Floats.test
+ (_ : Rust_primitives.Hax.Tuple0)
+ : RustM Rust_primitives.Hax.Tuple0
+ := do
+ let l0 : f64 := (1.0 : f64);
+ let l1 : f64 := (0.9 : f64);
+ let l2 : f32 := (5.0 : f32);
+ let l5 : f32 := Lean_tests.Floats.N;
+ (pure Rust_primitives.Hax.Tuple0.mk)
+
+def Lean_tests.Floats.f (x : f64) (y : f32) : RustM f32 := do (pure y)
+
inductive Lean_tests.Enums.E : Type
| V1 : Lean_tests.Enums.E
| V2 : Lean_tests.Enums.E
diff --git a/tests/lean-tests/src/floats.rs b/tests/lean-tests/src/floats.rs
new file mode 100644
index 000000000..99213ac82
--- /dev/null
+++ b/tests/lean-tests/src/floats.rs
@@ -0,0 +1,16 @@
+// Tests on floats
+#![allow(dead_code)]
+#![allow(unused_variables)]
+
+const N: f32 = 1.0;
+
+fn test() {
+ let l0 = 1.0;
+ let l1 = 0.9;
+ let l2 = 5.0f32;
+ let l5 = N;
+}
+
+fn f(x: f64, y: f32) -> f32 {
+ y
+}
diff --git a/tests/lean-tests/src/lib.rs b/tests/lean-tests/src/lib.rs
index 7bef89c22..c9f1950d7 100644
--- a/tests/lean-tests/src/lib.rs
+++ b/tests/lean-tests/src/lib.rs
@@ -4,6 +4,7 @@
pub mod comments;
pub mod constants;
pub mod enums;
+pub mod floats;
pub mod ite;
pub mod loops;
pub mod matching;