Skip to content

Commit 698dcd9

Browse files
committed
Abstract LeanObject usage where possible
1 parent 0aa49b3 commit 698dcd9

30 files changed

Lines changed: 956 additions & 869 deletions

lean-ffi/src/lib.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,6 @@ macro_rules! lean_domain_type {
6767
fn from(x: $name) -> Self { x.0 }
6868
}
6969

70-
impl From<$crate::object::LeanObject> for $name {
71-
#[inline]
72-
fn from(obj: $crate::object::LeanObject) -> Self { Self(obj) }
73-
}
74-
7570
impl $name {
7671
#[inline]
7772
pub fn new(obj: $crate::object::LeanObject) -> Self { Self(obj) }

lean-ffi/src/nat.rs

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Lean stores small naturals as tagged scalars and large ones as GMP
44
//! `mpz_object`s on the heap. This module handles both representations.
55
6-
use std::ffi::{c_int, c_void};
6+
use std::ffi::c_int;
77
use std::fmt;
88
use std::mem::MaybeUninit;
99

@@ -36,28 +36,19 @@ impl Nat {
3636
u64::try_from(&self.0).ok()
3737
}
3838

39-
/// Decode a `Nat` from a Lean object pointer. Handles both scalar (unboxed)
39+
/// Decode a `Nat` from a `LeanObject`. Handles both scalar (unboxed)
4040
/// and heap-allocated (GMP `mpz_object`) representations.
41-
///
42-
/// # Safety
43-
/// The pointer must be a valid Lean `Nat` object (scalar or mpz).
44-
pub unsafe fn from_ptr(ptr: *const c_void) -> Nat {
45-
let obj = unsafe { LeanObject::from_raw(ptr) };
41+
pub fn from_obj(obj: LeanObject) -> Nat {
4642
if obj.is_scalar() {
4743
let u = obj.unbox_usize();
4844
Nat(BigUint::from_bytes_le(&u.to_le_bytes()))
4945
} else {
5046
// Heap-allocated big integer (mpz_object)
51-
let mpz: &MpzObject = unsafe { &*ptr.cast() };
47+
let mpz: &MpzObject = unsafe { &*obj.as_ptr().cast() };
5248
Nat(mpz.m_value.to_biguint())
5349
}
5450
}
5551

56-
/// Decode a `Nat` from a `LeanObject`. Convenience wrapper over `from_ptr`.
57-
pub fn from_obj(obj: LeanObject) -> Nat {
58-
unsafe { Self::from_ptr(obj.as_ptr()) }
59-
}
60-
6152
#[inline]
6253
pub fn from_le_bytes(bytes: &[u8]) -> Nat {
6354
Nat(BigUint::from_bytes_le(bytes))
@@ -133,7 +124,7 @@ unsafe extern "C" {
133124

134125
/// Lean's internal mpz allocation — deep-copies the mpz value.
135126
/// Caller must still call mpz_clear on the original.
136-
fn lean_alloc_mpz(v: *mut Mpz) -> *mut c_void;
127+
fn lean_alloc_mpz(v: *mut Mpz) -> *mut std::ffi::c_void;
137128
}
138129

139130
/// Create a Lean `Nat` from a little-endian array of u64 limbs.
@@ -143,17 +134,17 @@ unsafe extern "C" {
143134
pub unsafe fn lean_nat_from_limbs(
144135
num_limbs: usize,
145136
limbs: *const u64,
146-
) -> *mut c_void {
137+
) -> LeanObject {
147138
if num_limbs == 0 {
148-
return LeanObject::box_usize(0).as_mut_ptr();
139+
return LeanObject::box_usize(0);
149140
}
150141
let first = unsafe { *limbs };
151142
if num_limbs == 1 && first <= LEAN_MAX_SMALL_NAT {
152143
#[allow(clippy::cast_possible_truncation)] // only targets 64-bit
153-
return LeanObject::box_usize(first as usize).as_mut_ptr();
144+
return LeanObject::box_usize(first as usize);
154145
}
155146
if num_limbs == 1 {
156-
return unsafe { lean_uint64_to_nat(first).cast() };
147+
return unsafe { LeanObject::from_lean_ptr(lean_uint64_to_nat(first)) };
157148
}
158149
// Multi-limb: use GMP
159150
unsafe {
@@ -165,6 +156,6 @@ pub unsafe fn lean_nat_from_limbs(
165156
// lean_alloc_mpz deep-copies; we must free the original
166157
let result = lean_alloc_mpz(value.as_mut_ptr());
167158
mpz_clear(value.as_mut_ptr());
168-
result
159+
LeanObject::from_raw(result)
169160
}
170161
}

lean-ffi/src/object.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@ impl LeanObject {
9898
}
9999
}
100100

101+
/// Create a `LeanObject` from a raw tag value for zero-field enum constructors.
102+
/// Lean passes simple enums (all constructors have zero fields) as unboxed
103+
/// tag values (0, 1, 2, ...) across FFI, not as `lean_box(tag)`.
104+
#[inline]
105+
pub fn from_enum_tag(tag: usize) -> Self {
106+
Self(tag as *const c_void)
107+
}
108+
109+
/// Extract the raw tag value from a zero-field enum constructor.
110+
/// Inverse of `from_enum_tag`.
111+
#[inline]
112+
pub fn as_enum_tag(self) -> usize {
113+
self.0 as usize
114+
}
115+
101116
/// Box a `usize` into a tagged scalar pointer.
102117
#[inline]
103118
pub fn box_usize(n: usize) -> Self {
@@ -174,6 +189,62 @@ impl LeanObject {
174189
}
175190
}
176191

192+
// =============================================================================
193+
// LeanNat — Nat (scalar or heap mpz)
194+
// =============================================================================
195+
196+
/// Typed wrapper for a Lean `Nat` (small = tagged scalar, big = heap `mpz_object`).
197+
#[derive(Clone, Copy)]
198+
#[repr(transparent)]
199+
pub struct LeanNat(LeanObject);
200+
201+
impl Deref for LeanNat {
202+
type Target = LeanObject;
203+
#[inline]
204+
fn deref(&self) -> &LeanObject {
205+
&self.0
206+
}
207+
}
208+
209+
impl From<LeanNat> for LeanObject {
210+
#[inline]
211+
fn from(x: LeanNat) -> Self {
212+
x.0
213+
}
214+
}
215+
216+
// =============================================================================
217+
// LeanBool — Bool (unboxed scalar: false = 0, true = 1)
218+
// =============================================================================
219+
220+
/// Typed wrapper for a Lean `Bool` (always an unboxed scalar: false = 0, true = 1).
221+
#[derive(Clone, Copy)]
222+
#[repr(transparent)]
223+
pub struct LeanBool(LeanObject);
224+
225+
impl Deref for LeanBool {
226+
type Target = LeanObject;
227+
#[inline]
228+
fn deref(&self) -> &LeanObject {
229+
&self.0
230+
}
231+
}
232+
233+
impl From<LeanBool> for LeanObject {
234+
#[inline]
235+
fn from(x: LeanBool) -> Self {
236+
x.0
237+
}
238+
}
239+
240+
impl LeanBool {
241+
/// Decode to a Rust `bool`.
242+
#[inline]
243+
pub fn to_bool(self) -> bool {
244+
self.0.as_enum_tag() != 0
245+
}
246+
}
247+
177248
// =============================================================================
178249
// LeanArray — Array α (tag LEAN_TAG_ARRAY)
179250
// =============================================================================

src/ffi.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
// Lean and C don't support feature flags, so the _iroh module is exposed as a fallback for when the `net` feature is disabled and/or on the `aarch64-darwin` target.
2+
// This fallback module contains dummy functions that can still be called via Lean->Rust FFI, but will return an error message that Lean then prints before exiting.
3+
#[cfg(any(
4+
not(feature = "net"),
5+
all(target_os = "macos", target_arch = "aarch64")
6+
))]
7+
pub mod _iroh;
18
pub mod aiur;
29
pub mod byte_array;
310
#[cfg(all(
Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
1-
use lean_ffi::object::{LeanExcept, LeanObject};
1+
use lean_ffi::object::{LeanArray, LeanExcept, LeanString};
22

33
const ERR_MSG: &str = "Iroh functions not supported when the Rust `net` feature is disabled \
44
or on MacOS aarch64-darwin";
55

66
/// `Iroh.Connect.putBytes' : @& String → @& Array String → @& String → @& String → Except String PutResponse`
77
#[unsafe(no_mangle)]
88
extern "C" fn rs_iroh_put(
9-
_node_id: LeanObject,
10-
_addrs: LeanObject,
11-
_relay_url: LeanObject,
12-
_input: LeanObject,
9+
_node_id: LeanString,
10+
_addrs: LeanArray,
11+
_relay_url: LeanString,
12+
_input: LeanString,
1313
) -> LeanExcept {
1414
LeanExcept::error_string(ERR_MSG)
1515
}
1616

1717
/// `Iroh.Connect.getBytes' : @& String → @& Array String → @& String → @& String → Except String GetResponse`
1818
#[unsafe(no_mangle)]
1919
extern "C" fn rs_iroh_get(
20-
_node_id: LeanObject,
21-
_addrs: LeanObject,
22-
_relay_url: LeanObject,
23-
_hash: LeanObject,
20+
_node_id: LeanString,
21+
_addrs: LeanArray,
22+
_relay_url: LeanString,
23+
_hash: LeanString,
2424
) -> LeanExcept {
2525
LeanExcept::error_string(ERR_MSG)
2626
}
27+
28+
/// `Iroh.Serve.serve' : Unit → Except String Unit`
29+
#[unsafe(no_mangle)]
30+
extern "C" fn rs_iroh_serve() -> LeanExcept {
31+
LeanExcept::error_string(
32+
"Iroh functions not supported when the Rust `net` feature is disabled \
33+
or on MacOS aarch64-darwin",
34+
)
35+
}

src/ffi/aiur/protocol.rs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::{
2020
ffi::aiur::{
2121
lean_unbox_g, lean_unbox_nat_as_usize, toplevel::decode_toplevel,
2222
},
23+
lean::{LeanAiurFriParameters, LeanAiurToplevel},
2324
};
2425

2526
// =============================================================================
@@ -63,12 +64,12 @@ extern "C" fn rs_aiur_proof_of_bytes(
6364
/// `AiurSystem.build : @&Bytecode.Toplevel → @&CommitmentParameters → AiurSystem`
6465
#[unsafe(no_mangle)]
6566
extern "C" fn rs_aiur_system_build(
66-
toplevel: LeanObject,
67+
toplevel: LeanAiurToplevel,
6768
commitment_parameters: LeanObject,
6869
) -> LeanExternal<AiurSystem> {
6970
let system = AiurSystem::build(
7071
decode_toplevel(toplevel),
71-
lean_ptr_to_commitment_parameters(commitment_parameters),
72+
decode_commitment_parameters(commitment_parameters),
7273
);
7374
LeanExternal::alloc(system_class(), system)
7475
}
@@ -77,12 +78,12 @@ extern "C" fn rs_aiur_system_build(
7778
#[unsafe(no_mangle)]
7879
extern "C" fn rs_aiur_system_verify(
7980
aiur_system_obj: LeanExternal<AiurSystem>,
80-
fri_parameters: LeanObject,
81-
claim: LeanObject,
81+
fri_parameters: LeanAiurFriParameters,
82+
claim: LeanArray,
8283
proof_obj: LeanExternal<Proof>,
8384
) -> LeanExcept {
84-
let fri_parameters = lean_ctor_to_fri_parameters(fri_parameters);
85-
let claim = claim.as_array().map(lean_unbox_g);
85+
let fri_parameters = decode_fri_parameters(fri_parameters);
86+
let claim = claim.map(lean_unbox_g);
8687
match aiur_system_obj.get().verify(fri_parameters, &claim, proof_obj.get()) {
8788
Ok(()) => LeanExcept::ok(LeanObject::box_usize(0)),
8889
Err(err) => LeanExcept::error_string(&format!("{err:?}")),
@@ -94,17 +95,17 @@ extern "C" fn rs_aiur_system_verify(
9495
#[unsafe(no_mangle)]
9596
extern "C" fn rs_aiur_system_prove(
9697
aiur_system_obj: LeanExternal<AiurSystem>,
97-
fri_parameters: LeanObject,
98+
fri_parameters: LeanAiurFriParameters,
9899
fun_idx: LeanObject,
99-
args: LeanObject,
100-
io_data_arr: LeanObject,
101-
io_map_arr: LeanObject,
100+
args: LeanArray,
101+
io_data_arr: LeanArray,
102+
io_map_arr: LeanArray,
102103
) -> LeanObject {
103-
let fri_parameters = lean_ctor_to_fri_parameters(fri_parameters);
104+
let fri_parameters = decode_fri_parameters(fri_parameters);
104105
let fun_idx = lean_unbox_nat_as_usize(fun_idx);
105-
let args = args.as_array().map(lean_unbox_g);
106-
let io_data = io_data_arr.as_array().map(lean_unbox_g);
107-
let io_map = lean_array_to_io_buffer_map(io_map_arr);
106+
let args = args.map(lean_unbox_g);
107+
let io_data = io_data_arr.map(lean_unbox_g);
108+
let io_map = decode_io_buffer_map(io_map_arr);
108109
let mut io_buffer = IOBuffer { data: io_data, map: io_map };
109110

110111
let (claim, proof) =
@@ -158,19 +159,19 @@ extern "C" fn rs_aiur_system_prove(
158159
// =============================================================================
159160

160161
/// Build a Lean `Array G` from a slice of field elements.
161-
fn build_g_array(values: &[G]) -> LeanObject {
162+
fn build_g_array(values: &[G]) -> LeanArray {
162163
let arr = LeanArray::alloc(values.len());
163164
for (i, g) in values.iter().enumerate() {
164165
arr.set(i, LeanObject::box_u64(g.as_canonical_u64()));
165166
}
166-
*arr
167+
arr
167168
}
168169

169-
fn lean_ptr_to_commitment_parameters(obj: LeanObject) -> CommitmentParameters {
170+
fn decode_commitment_parameters(obj: LeanObject) -> CommitmentParameters {
170171
CommitmentParameters { log_blowup: lean_unbox_nat_as_usize(obj) }
171172
}
172173

173-
fn lean_ctor_to_fri_parameters(obj: LeanObject) -> FriParameters {
174+
fn decode_fri_parameters(obj: LeanAiurFriParameters) -> FriParameters {
174175
let ctor = obj.as_ctor();
175176
FriParameters {
176177
log_final_poly_len: lean_unbox_nat_as_usize(ctor.get(0)),
@@ -180,10 +181,9 @@ fn lean_ctor_to_fri_parameters(obj: LeanObject) -> FriParameters {
180181
}
181182
}
182183

183-
fn lean_array_to_io_buffer_map(
184-
obj: LeanObject,
184+
fn decode_io_buffer_map(
185+
arr: LeanArray,
185186
) -> FxHashMap<Vec<G>, IOKeyInfo> {
186-
let arr = obj.as_array();
187187
let mut map = FxHashMap::with_capacity_and_hasher(arr.len(), FxBuildHasher);
188188
for elt in arr.iter() {
189189
let pair = elt.as_ctor();

0 commit comments

Comments
 (0)