#![allow(unsafe_code)]
#![allow(private_bounds, private_interfaces)]
use core::cell::Cell;
use core::ffi::c_void;
use std::time::Instant;
use crate::host::cancellation::{LeanCancellationToken, check_cancellation};
use crate::host::capabilities::LeanCapabilities;
use crate::host::elaboration::{LeanElabFailure, LeanElabOptions};
use crate::host::evidence::{EvidenceStatus, LeanEvidence, LeanKernelOutcome, ProofSummary};
use crate::host::meta::{LeanMetaOptions, LeanMetaResponse, LeanMetaService};
use crate::host::progress::{LeanProgressSink, ProgressBridge, report_progress};
use lean_rs::Obj;
use lean_rs::abi::structure::{alloc_ctor_with_objects, take_ctor_objects};
use lean_rs::abi::traits::{IntoLean, LeanAbi, TryFromLean, conversion_error, sealed};
#[cfg(doc)]
use lean_rs::error::HostStage;
use lean_rs::error::LeanResult;
use lean_rs::module::{DecodeCallResult, LeanArgs, LeanExported, LeanIo, LeanLibrary};
use lean_rs::{LeanDeclaration, LeanExpr, LeanName};
use lean_rs_sys::lean_object;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct SessionStats {
pub ffi_calls: u64,
pub batch_items: u64,
pub elapsed_ns: u64,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LeanSourceRange {
pub file: String,
pub start_line: u32,
pub start_column: u32,
pub end_line: u32,
pub end_column: u32,
}
impl<'lean> TryFromLean<'lean> for LeanSourceRange {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
let [file_o, start_line_o, start_column_o, end_line_o, end_column_o] =
take_ctor_objects::<5>(obj, 0, "SourceRange")?;
Ok(Self {
file: String::try_from_lean(file_o)?,
start_line: u32::try_from_lean(start_line_o)?,
start_column: u32::try_from_lean(start_column_o)?,
end_line: u32::try_from_lean(end_line_o)?,
end_column: u32::try_from_lean(end_column_o)?,
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LeanDeclarationFilter {
pub include_private: bool,
pub include_generated: bool,
pub include_internal: bool,
}
impl Default for LeanDeclarationFilter {
fn default() -> Self {
Self {
include_private: true,
include_generated: false,
include_internal: false,
}
}
}
impl<'lean> IntoLean<'lean> for LeanDeclarationFilter {
fn into_lean(self, runtime: &'lean lean_rs::LeanRuntime) -> Obj<'lean> {
alloc_ctor_with_objects(
runtime,
0,
[
self.include_private.into_lean(runtime),
self.include_generated.into_lean(runtime),
self.include_internal.into_lean(runtime),
],
)
}
}
impl<'lean> TryFromLean<'lean> for LeanDeclarationFilter {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
let [include_private_o, include_generated_o, include_internal_o] =
take_ctor_objects::<3>(obj, 0, "DeclarationFilter")?;
Ok(Self {
include_private: bool::try_from_lean(include_private_o)?,
include_generated: bool::try_from_lean(include_generated_o)?,
include_internal: bool::try_from_lean(include_internal_o)?,
})
}
}
impl sealed::SealedAbi for LeanDeclarationFilter {}
impl<'lean> LeanAbi<'lean> for LeanDeclarationFilter {
type CRepr = *mut lean_object;
fn into_c(self, runtime: &'lean lean_rs::LeanRuntime) -> Self::CRepr {
self.into_lean(runtime).into_raw()
}
#[allow(
clippy::not_unsafe_ptr_arg_deref,
reason = "sealed trait — called only by LeanExported"
)]
fn from_c(c: Self::CRepr, runtime: &'lean lean_rs::LeanRuntime) -> LeanResult<Self> {
if c.is_null() {
return Err(conversion_error("Lean DeclarationFilter returned a null pointer"));
}
let obj = unsafe { Obj::from_owned_raw(runtime, c) };
Self::try_from_lean(obj)
}
}
pub(crate) struct SessionSymbols {
pub(crate) session_import: *mut c_void,
pub(crate) session_import_progress: *mut c_void,
pub(crate) name_from_string: *mut c_void,
pub(crate) env_query_declaration: *mut c_void,
pub(crate) env_query_declarations_bulk: *mut c_void,
pub(crate) env_query_declarations_bulk_progress: *mut c_void,
pub(crate) env_list_declarations: *mut c_void,
pub(crate) env_list_declarations_filtered: *mut c_void,
pub(crate) env_list_declarations_filtered_progress: *mut c_void,
pub(crate) env_declaration_source_range: *mut c_void,
pub(crate) env_declaration_type: *mut c_void,
pub(crate) env_declaration_type_bulk: *mut c_void,
pub(crate) env_declaration_type_bulk_progress: *mut c_void,
pub(crate) env_declaration_kind: *mut c_void,
pub(crate) env_declaration_kind_bulk: *mut c_void,
pub(crate) env_declaration_kind_bulk_progress: *mut c_void,
pub(crate) env_declaration_name: *mut c_void,
pub(crate) env_declaration_name_bulk: *mut c_void,
pub(crate) env_declaration_name_bulk_progress: *mut c_void,
pub(crate) elaborate: *mut c_void,
pub(crate) elaborate_bulk: *mut c_void,
pub(crate) elaborate_bulk_progress: *mut c_void,
pub(crate) kernel_check: *mut c_void,
pub(crate) kernel_check_progress: *mut c_void,
pub(crate) check_evidence: *mut c_void,
pub(crate) evidence_summary: *mut c_void,
pub(crate) meta_infer_type: Option<*mut c_void>,
pub(crate) meta_whnf: Option<*mut c_void>,
pub(crate) meta_heartbeat_burn: Option<*mut c_void>,
pub(crate) meta_is_def_eq: Option<*mut c_void>,
}
impl SessionSymbols {
pub(crate) fn resolve(library: &LeanLibrary<'_>) -> LeanResult<Self> {
Ok(Self {
session_import: library.resolve_function_symbol("lean_rs_host_session_import")?,
session_import_progress: library.resolve_function_symbol("lean_rs_host_session_import_progress")?,
name_from_string: library.resolve_function_symbol("lean_rs_host_name_from_string")?,
env_query_declaration: library.resolve_function_symbol("lean_rs_host_env_query_declaration")?,
env_query_declarations_bulk: library.resolve_function_symbol("lean_rs_host_env_query_declarations_bulk")?,
env_query_declarations_bulk_progress: library
.resolve_function_symbol("lean_rs_host_env_query_declarations_bulk_progress")?,
env_list_declarations: library.resolve_function_symbol("lean_rs_host_env_list_declarations")?,
env_list_declarations_filtered: library
.resolve_function_symbol("lean_rs_host_env_list_declarations_filtered")?,
env_list_declarations_filtered_progress: library
.resolve_function_symbol("lean_rs_host_env_list_declarations_filtered_progress")?,
env_declaration_source_range: library
.resolve_function_symbol("lean_rs_host_env_declaration_source_range")?,
env_declaration_type: library.resolve_function_symbol("lean_rs_host_env_declaration_type")?,
env_declaration_type_bulk: library.resolve_function_symbol("lean_rs_host_env_declaration_type_bulk")?,
env_declaration_type_bulk_progress: library
.resolve_function_symbol("lean_rs_host_env_declaration_type_bulk_progress")?,
env_declaration_kind: library.resolve_function_symbol("lean_rs_host_env_declaration_kind")?,
env_declaration_kind_bulk: library.resolve_function_symbol("lean_rs_host_env_declaration_kind_bulk")?,
env_declaration_kind_bulk_progress: library
.resolve_function_symbol("lean_rs_host_env_declaration_kind_bulk_progress")?,
env_declaration_name: library.resolve_function_symbol("lean_rs_host_env_declaration_name")?,
env_declaration_name_bulk: library.resolve_function_symbol("lean_rs_host_env_declaration_name_bulk")?,
env_declaration_name_bulk_progress: library
.resolve_function_symbol("lean_rs_host_env_declaration_name_bulk_progress")?,
elaborate: library.resolve_function_symbol("lean_rs_host_elaborate")?,
elaborate_bulk: library.resolve_function_symbol("lean_rs_host_elaborate_bulk")?,
elaborate_bulk_progress: library.resolve_function_symbol("lean_rs_host_elaborate_bulk_progress")?,
kernel_check: library.resolve_function_symbol("lean_rs_host_kernel_check")?,
kernel_check_progress: library.resolve_function_symbol("lean_rs_host_kernel_check_progress")?,
check_evidence: library.resolve_function_symbol("lean_rs_host_check_evidence")?,
evidence_summary: library.resolve_function_symbol("lean_rs_host_evidence_summary")?,
meta_infer_type: library.resolve_optional_function_symbol("lean_rs_host_meta_infer_type"),
meta_whnf: library.resolve_optional_function_symbol("lean_rs_host_meta_whnf"),
meta_heartbeat_burn: library.resolve_optional_function_symbol("lean_rs_host_meta_heartbeat_burn"),
meta_is_def_eq: library.resolve_optional_function_symbol("lean_rs_host_meta_is_def_eq"),
})
}
pub(crate) fn meta_address_by_name(&self, name: &str) -> Option<*mut c_void> {
match name {
"lean_rs_host_meta_infer_type" => self.meta_infer_type,
"lean_rs_host_meta_whnf" => self.meta_whnf,
"lean_rs_host_meta_heartbeat_burn" => self.meta_heartbeat_burn,
"lean_rs_host_meta_is_def_eq" => self.meta_is_def_eq,
_ => None,
}
}
}
pub struct LeanSession<'lean, 'c> {
capabilities: &'c LeanCapabilities<'lean, 'c>,
environment: Obj<'lean>,
stats: Cell<SessionStats>,
}
impl<'lean, 'c> LeanSession<'lean, 'c> {
pub(crate) fn import(
capabilities: &'c LeanCapabilities<'lean, 'c>,
imports: &[&str],
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Self> {
let _span = tracing::info_span!(
target: "lean_rs",
"lean_rs.host.session.import",
imports_len = imports.len(),
)
.entered();
check_cancellation(cancellation)?;
let runtime = capabilities.host().runtime();
let project = capabilities.host().project();
let search_paths: Vec<String> = vec![
project.olean_search_path().to_string_lossy().into_owned(),
crate::host::lake::LakeProject::interop_olean_search_path()?
.to_string_lossy()
.into_owned(),
crate::host::lake::LakeProject::shim_olean_search_path()?
.to_string_lossy()
.into_owned(),
];
let imports_owned: Vec<String> = imports.iter().map(|&s| s.to_owned()).collect();
let environment = if let Some(sink) = progress {
let bridge = ProgressBridge::new(sink, "import", Some(u64::try_from(imports.len()).unwrap_or(u64::MAX)))?;
let (handle, trampoline) = bridge.abi_parts();
let address = capabilities.symbols().session_import_progress;
let import_fn: LeanExported<'lean, '_, (Vec<String>, Vec<String>, usize, usize), LeanIo<Obj<'lean>>> =
unsafe { LeanExported::from_function_address(runtime, address) };
let raw = import_fn.call(search_paths, imports_owned, handle, trampoline)?;
bridge.decode(raw)?
} else {
let address = capabilities.symbols().session_import;
let import_fn: LeanExported<'lean, '_, (Vec<String>, Vec<String>), LeanIo<Obj<'lean>>> =
unsafe { LeanExported::from_function_address(runtime, address) };
import_fn.call(search_paths, imports_owned)?
};
Ok(Self {
capabilities,
environment,
stats: Cell::new(SessionStats::default()),
})
}
pub(crate) fn from_environment(capabilities: &'c LeanCapabilities<'lean, 'c>, environment: Obj<'lean>) -> Self {
Self {
capabilities,
environment,
stats: Cell::new(SessionStats::default()),
}
}
pub(crate) fn into_environment(self) -> Obj<'lean> {
self.environment
}
#[must_use]
pub fn stats(&self) -> SessionStats {
self.stats.get()
}
fn record_call(&self, batch: u64, elapsed: std::time::Duration) {
let mut s = self.stats.get();
s.ffi_calls = s.ffi_calls.saturating_add(1);
s.batch_items = s.batch_items.saturating_add(batch);
s.elapsed_ns = s
.elapsed_ns
.saturating_add(u64::try_from(elapsed.as_nanos()).unwrap_or(u64::MAX));
self.stats.set(s);
}
fn decode_strings_cached(raw: Vec<Obj<'lean>>) -> LeanResult<Vec<String>> {
if raw.is_empty() {
return Ok(Vec::new());
}
let Some(first_key) = raw.first().map(Obj::as_raw_borrowed) else {
return Ok(Vec::new());
};
if raw.iter().all(|obj| obj.as_raw_borrowed() == first_key) {
let len = raw.len();
let mut raw_iter = raw.into_iter();
let Some(first) = raw_iter.next() else {
return Ok(Vec::new());
};
let value = String::try_from_lean(first)?;
return Ok(vec![value; len]);
}
let mut out = Vec::with_capacity(raw.len());
for obj in raw {
out.push(String::try_from_lean(obj)?);
}
Ok(out)
}
fn all_equal_name<'a>(names: &'a [&str]) -> Option<&'a str> {
let first = *names.first()?;
names.iter().all(|name| *name == first).then_some(first)
}
pub fn query_declaration(
&mut self,
name: &str,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<LeanDeclaration<'lean>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.query_declaration",
name = name,
)
.entered();
check_cancellation(cancellation)?;
let name_handle = self.make_name(name, cancellation)?;
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().env_query_declaration;
let query: LeanExported<'lean, '_, (Obj<'lean>, LeanName<'lean>), LeanIo<Option<LeanDeclaration<'lean>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), name_handle);
self.record_call(0, t.elapsed());
match result? {
Some(decl) => Ok(decl),
None => Err(lean_rs::abi::traits::conversion_error(format!(
"declaration '{name}' not found in imported environment"
))),
}
}
pub fn list_declarations(
&mut self,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<Vec<LeanName<'lean>>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.list_declarations",
)
.entered();
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().env_list_declarations;
let list: LeanExported<'lean, '_, (Obj<'lean>,), LeanIo<Vec<Obj<'lean>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let raw = list.call(self.environment.clone());
self.record_call(0, t.elapsed());
raw?.into_iter().map(LeanName::try_from_lean).collect()
}
pub fn list_declarations_filtered(
&mut self,
filter: &LeanDeclarationFilter,
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Vec<LeanName<'lean>>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.list_declarations_filtered",
include_private = filter.include_private,
include_generated = filter.include_generated,
include_internal = filter.include_internal,
)
.entered();
check_cancellation(cancellation)?;
let raw = if let Some(sink) = progress {
let bridge = ProgressBridge::new(sink, "list_declarations_filtered", None)?;
let (handle, trampoline) = bridge.abi_parts();
let address = self.capabilities.symbols().env_list_declarations_filtered_progress;
let list: LeanExported<'lean, '_, (Obj<'lean>, LeanDeclarationFilter, usize, usize), LeanIo<Obj<'lean>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = list.call(self.environment.clone(), *filter, handle, trampoline);
self.record_call(0, t.elapsed());
bridge.decode::<Vec<Obj<'lean>>>(result?)?
} else {
let address = self.capabilities.symbols().env_list_declarations_filtered;
let list: LeanExported<'lean, '_, (Obj<'lean>, LeanDeclarationFilter), LeanIo<Vec<Obj<'lean>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = list.call(self.environment.clone(), *filter);
self.record_call(0, t.elapsed());
result?
};
raw.into_iter().map(LeanName::try_from_lean).collect()
}
pub fn declaration_source_range(
&mut self,
name: &str,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<Option<LeanSourceRange>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.declaration_source_range",
name = name,
)
.entered();
check_cancellation(cancellation)?;
let name_handle = self.make_name(name, cancellation)?;
check_cancellation(cancellation)?;
let source_roots = self
.capabilities
.host()
.project()
.source_roots()?
.into_iter()
.map(|path| path.to_string_lossy().into_owned())
.collect::<Vec<_>>();
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().env_declaration_source_range;
let query: LeanExported<
'lean,
'_,
(Obj<'lean>, LeanName<'lean>, Vec<String>),
LeanIo<Option<LeanSourceRange>>,
> = unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), name_handle, source_roots);
self.record_call(0, t.elapsed());
result
}
pub fn declaration_type(
&mut self,
name: &str,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<Option<LeanExpr<'lean>>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.declaration_type",
name = name,
)
.entered();
check_cancellation(cancellation)?;
let name_handle = self.make_name(name, cancellation)?;
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().env_declaration_type;
let query: LeanExported<'lean, '_, (Obj<'lean>, LeanName<'lean>), LeanIo<Option<LeanExpr<'lean>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), name_handle);
self.record_call(0, t.elapsed());
result
}
pub fn declaration_type_bulk(
&mut self,
names: &[&str],
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Vec<Option<LeanExpr<'lean>>>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.declaration_type_bulk",
batch_size = names.len(),
)
.entered();
if names.is_empty() {
return Ok(Vec::new());
}
check_cancellation(cancellation)?;
if cancellation.is_some() {
let started = Instant::now();
let total = Some(u64::try_from(names.len()).unwrap_or(u64::MAX));
let mut out = Vec::with_capacity(names.len());
for (idx, name) in names.iter().enumerate() {
check_cancellation(cancellation)?;
out.push(self.declaration_type(name, cancellation)?);
report_progress(
progress,
"declaration_type_bulk",
u64::try_from(idx.saturating_add(1)).unwrap_or(u64::MAX),
total,
started,
)?;
}
return Ok(out);
}
if progress.is_none()
&& let Some(name) = Self::all_equal_name(names)
{
let names_owned = vec![name.to_owned()];
let address = self.capabilities.symbols().env_declaration_type_bulk;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>), LeanIo<Vec<Option<LeanExpr<'lean>>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let mut result = query.call(self.environment.clone(), names_owned)?;
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
let value = result.pop().unwrap_or(None);
return Ok(vec![value; names.len()]);
}
let names_owned: Vec<String> = names.iter().map(|&name| name.to_owned()).collect();
if let Some(sink) = progress {
let total = Some(u64::try_from(names.len()).unwrap_or(u64::MAX));
let bridge = ProgressBridge::new(sink, "declaration_type_bulk", total)?;
let (handle, trampoline) = bridge.abi_parts();
let address = self.capabilities.symbols().env_declaration_type_bulk_progress;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>, usize, usize), LeanIo<Obj<'lean>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), names_owned, handle, trampoline);
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
bridge.decode(result?)
} else {
let address = self.capabilities.symbols().env_declaration_type_bulk;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>), LeanIo<Vec<Option<LeanExpr<'lean>>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), names_owned);
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
result
}
}
pub fn declaration_kind(&mut self, name: &str, cancellation: Option<&LeanCancellationToken>) -> LeanResult<String> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.declaration_kind",
name = name,
)
.entered();
check_cancellation(cancellation)?;
let name_handle = self.make_name(name, cancellation)?;
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().env_declaration_kind;
let query: LeanExported<'lean, '_, (Obj<'lean>, LeanName<'lean>), LeanIo<String>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), name_handle);
self.record_call(0, t.elapsed());
result
}
pub fn declaration_kind_bulk(
&mut self,
names: &[&str],
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Vec<String>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.declaration_kind_bulk",
batch_size = names.len(),
)
.entered();
if names.is_empty() {
return Ok(Vec::new());
}
check_cancellation(cancellation)?;
if cancellation.is_some() {
let started = Instant::now();
let total = Some(u64::try_from(names.len()).unwrap_or(u64::MAX));
let mut out = Vec::with_capacity(names.len());
for (idx, name) in names.iter().enumerate() {
check_cancellation(cancellation)?;
out.push(self.declaration_kind(name, cancellation)?);
report_progress(
progress,
"declaration_kind_bulk",
u64::try_from(idx.saturating_add(1)).unwrap_or(u64::MAX),
total,
started,
)?;
}
return Ok(out);
}
if progress.is_none()
&& let Some(name) = Self::all_equal_name(names)
{
let names_owned = vec![name.to_owned()];
let address = self.capabilities.symbols().env_declaration_kind_bulk;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>), LeanIo<Vec<Obj<'lean>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let mut result = Self::decode_strings_cached(query.call(self.environment.clone(), names_owned)?)?;
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
let value = result.pop().unwrap_or_default();
return Ok(vec![value; names.len()]);
}
let names_owned: Vec<String> = names.iter().map(|&name| name.to_owned()).collect();
if let Some(sink) = progress {
let total = Some(u64::try_from(names.len()).unwrap_or(u64::MAX));
let bridge = ProgressBridge::new(sink, "declaration_kind_bulk", total)?;
let (handle, trampoline) = bridge.abi_parts();
let address = self.capabilities.symbols().env_declaration_kind_bulk_progress;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>, usize, usize), LeanIo<Obj<'lean>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), names_owned, handle, trampoline);
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
let raw = bridge.decode::<Vec<Obj<'lean>>>(result?)?;
Self::decode_strings_cached(raw)
} else {
let address = self.capabilities.symbols().env_declaration_kind_bulk;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>), LeanIo<Vec<Obj<'lean>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), names_owned);
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
Self::decode_strings_cached(result?)
}
}
pub fn declaration_name(&mut self, name: &str, cancellation: Option<&LeanCancellationToken>) -> LeanResult<String> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.declaration_name",
name = name,
)
.entered();
check_cancellation(cancellation)?;
let name_handle = self.make_name(name, cancellation)?;
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().env_declaration_name;
let query: LeanExported<'lean, '_, (Obj<'lean>, LeanName<'lean>), LeanIo<String>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), name_handle);
self.record_call(0, t.elapsed());
result
}
pub fn declaration_name_bulk(
&mut self,
names: &[&str],
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Vec<String>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.declaration_name_bulk",
batch_size = names.len(),
)
.entered();
if names.is_empty() {
return Ok(Vec::new());
}
check_cancellation(cancellation)?;
if cancellation.is_some() {
let started = Instant::now();
let total = Some(u64::try_from(names.len()).unwrap_or(u64::MAX));
let mut out = Vec::with_capacity(names.len());
for (idx, name) in names.iter().enumerate() {
check_cancellation(cancellation)?;
out.push(self.declaration_name(name, cancellation)?);
report_progress(
progress,
"declaration_name_bulk",
u64::try_from(idx.saturating_add(1)).unwrap_or(u64::MAX),
total,
started,
)?;
}
return Ok(out);
}
if progress.is_none()
&& let Some(name) = Self::all_equal_name(names)
{
let names_owned = vec![name.to_owned()];
let address = self.capabilities.symbols().env_declaration_name_bulk;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>), LeanIo<Vec<Obj<'lean>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let mut result = Self::decode_strings_cached(query.call(self.environment.clone(), names_owned)?)?;
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
let value = result.pop().unwrap_or_default();
return Ok(vec![value; names.len()]);
}
let names_owned: Vec<String> = names.iter().map(|&name| name.to_owned()).collect();
if let Some(sink) = progress {
let total = Some(u64::try_from(names.len()).unwrap_or(u64::MAX));
let bridge = ProgressBridge::new(sink, "declaration_name_bulk", total)?;
let (handle, trampoline) = bridge.abi_parts();
let address = self.capabilities.symbols().env_declaration_name_bulk_progress;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>, usize, usize), LeanIo<Obj<'lean>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), names_owned, handle, trampoline);
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
let raw = bridge.decode::<Vec<Obj<'lean>>>(result?)?;
Self::decode_strings_cached(raw)
} else {
let address = self.capabilities.symbols().env_declaration_name_bulk;
let query: LeanExported<'lean, '_, (Obj<'lean>, Vec<String>), LeanIo<Vec<Obj<'lean>>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = query.call(self.environment.clone(), names_owned);
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
Self::decode_strings_cached(result?)
}
}
pub fn elaborate(
&mut self,
source: &str,
expected_type: Option<&LeanExpr<'lean>>,
options: &LeanElabOptions,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<Result<LeanExpr<'lean>, LeanElabFailure>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.elaborate",
source_len = source.len(),
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().elaborate;
let call: LeanExported<
'lean,
'_,
(Obj<'lean>, &str, Option<LeanExpr<'lean>>, &str, &str, u64, usize),
LeanIo<Result<LeanExpr<'lean>, LeanElabFailure>>,
> = unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(
self.environment.clone(),
source,
expected_type.cloned(),
options.namespace_context_str(),
options.file_label_str(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
);
self.record_call(0, t.elapsed());
result
}
pub fn kernel_check(
&mut self,
source: &str,
options: &LeanElabOptions,
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<LeanKernelOutcome<'lean>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.kernel_check",
source_len = source.len(),
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
if let Some(sink) = progress {
let bridge = ProgressBridge::new(sink, "kernel_check", Some(1))?;
let (handle, trampoline) = bridge.abi_parts();
let address = self.capabilities.symbols().kernel_check_progress;
let call: LeanExported<
'lean,
'_,
(Obj<'lean>, &str, &str, &str, u64, usize, usize, usize),
LeanIo<Obj<'lean>>,
> = unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(
self.environment.clone(),
source,
options.namespace_context_str(),
options.file_label_str(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
handle,
trampoline,
);
self.record_call(0, t.elapsed());
bridge.decode(result?)
} else {
let address = self.capabilities.symbols().kernel_check;
let call: LeanExported<
'lean,
'_,
(Obj<'lean>, &str, &str, &str, u64, usize),
LeanIo<LeanKernelOutcome<'lean>>,
> = unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(
self.environment.clone(),
source,
options.namespace_context_str(),
options.file_label_str(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
);
self.record_call(0, t.elapsed());
result
}
}
pub fn check_evidence(
&mut self,
handle: &LeanEvidence<'lean>,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<EvidenceStatus> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.check_evidence",
)
.entered();
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().check_evidence;
let call: LeanExported<'lean, '_, (Obj<'lean>, LeanEvidence<'lean>), LeanIo<EvidenceStatus>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(self.environment.clone(), handle.clone());
self.record_call(0, t.elapsed());
result
}
pub fn summarize_evidence(
&mut self,
handle: &LeanEvidence<'lean>,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<ProofSummary> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.summarize_evidence",
)
.entered();
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().evidence_summary;
let call: LeanExported<'lean, '_, (Obj<'lean>, LeanEvidence<'lean>), LeanIo<ProofSummary>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(self.environment.clone(), handle.clone());
self.record_call(0, t.elapsed());
result
}
pub fn run_meta<Req, Resp>(
&mut self,
service: &LeanMetaService<Req, Resp>,
request: Req,
options: &LeanMetaOptions,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<LeanMetaResponse<Resp>>
where
Req: lean_rs::abi::traits::LeanAbi<'lean>,
Resp: TryFromLean<'lean>,
{
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.run_meta",
service = service.name(),
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
let Some(address) = self.capabilities.symbols().meta_address_by_name(service.name()) else {
let message = format!(
"meta service '{}' is not exported by the loaded capability",
service.name()
);
return Ok(LeanMetaResponse::Unsupported(LeanElabFailure::synthetic(
message,
"<host>".to_owned(),
)));
};
let call: LeanExported<'lean, '_, (Obj<'lean>, Req, u64, usize, u8), LeanIo<LeanMetaResponse<Resp>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(
self.environment.clone(),
request,
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
options.transparency_byte(),
);
self.record_call(0, t.elapsed());
result
}
pub fn query_declarations_bulk(
&mut self,
names: &[&str],
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Vec<LeanDeclaration<'lean>>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.query_declarations_bulk",
batch_size = names.len(),
)
.entered();
if names.is_empty() {
return Ok(Vec::new());
}
check_cancellation(cancellation)?;
if cancellation.is_some() {
let started = Instant::now();
let mut out = Vec::with_capacity(names.len());
let total = Some(u64::try_from(names.len()).unwrap_or(u64::MAX));
for (idx, name) in names.iter().enumerate() {
check_cancellation(cancellation)?;
out.push(self.query_declaration(name, cancellation)?);
report_progress(
progress,
"query_declarations_bulk",
u64::try_from(idx.saturating_add(1)).unwrap_or(u64::MAX),
total,
started,
)?;
}
return Ok(out);
}
let prepare_started = Instant::now();
let total = Some(u64::try_from(names.len()).unwrap_or(u64::MAX));
let mut name_handles: Vec<LeanName<'lean>> = Vec::with_capacity(names.len());
for (idx, name) in names.iter().enumerate() {
name_handles.push(self.make_name(name, cancellation)?);
report_progress(
progress,
"prepare_names",
u64::try_from(idx.saturating_add(1)).unwrap_or(u64::MAX),
total,
prepare_started,
)?;
}
check_cancellation(cancellation)?;
let raw = if let Some(sink) = progress {
let bridge = ProgressBridge::new(sink, "query_declarations_bulk", total)?;
let (handle, trampoline) = bridge.abi_parts();
let address = self.capabilities.symbols().env_query_declarations_bulk_progress;
let call: LeanExported<'lean, '_, (Obj<'lean>, Vec<LeanName<'lean>>, usize, usize), LeanIo<Obj<'lean>>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(self.environment.clone(), name_handles, handle, trampoline);
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
bridge.decode::<Vec<Option<LeanDeclaration<'lean>>>>(result?)?
} else {
let address = self.capabilities.symbols().env_query_declarations_bulk;
let call: LeanExported<
'lean,
'_,
(Obj<'lean>, Vec<LeanName<'lean>>),
LeanIo<Vec<Option<LeanDeclaration<'lean>>>>,
> = unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(self.environment.clone(), name_handles);
let batch_len = u64::try_from(names.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
result?
};
let mut out: Vec<LeanDeclaration<'lean>> = Vec::with_capacity(raw.len());
for (slot, name) in raw.into_iter().zip(names.iter()) {
match slot {
Some(decl) => out.push(decl),
None => {
return Err(lean_rs::abi::traits::conversion_error(format!(
"declaration '{name}' not found in imported environment"
)));
}
}
}
Ok(out)
}
pub fn elaborate_bulk(
&mut self,
sources: &[&str],
options: &LeanElabOptions,
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Vec<Result<LeanExpr<'lean>, LeanElabFailure>>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.elaborate_bulk",
batch_size = sources.len(),
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
if sources.is_empty() {
return Ok(Vec::new());
}
check_cancellation(cancellation)?;
if cancellation.is_some() {
let started = Instant::now();
let total = Some(u64::try_from(sources.len()).unwrap_or(u64::MAX));
let mut out = Vec::with_capacity(sources.len());
for (idx, source) in sources.iter().enumerate() {
check_cancellation(cancellation)?;
out.push(self.elaborate(source, None, options, cancellation)?);
report_progress(
progress,
"elaborate_bulk",
u64::try_from(idx.saturating_add(1)).unwrap_or(u64::MAX),
total,
started,
)?;
}
return Ok(out);
}
let sources_owned: Vec<String> = sources.iter().map(|&s| s.to_owned()).collect();
if let Some(sink) = progress {
let total = Some(u64::try_from(sources.len()).unwrap_or(u64::MAX));
let bridge = ProgressBridge::new(sink, "elaborate_bulk", total)?;
let (handle, trampoline) = bridge.abi_parts();
let address = self.capabilities.symbols().elaborate_bulk_progress;
let call: LeanExported<
'lean,
'_,
(Obj<'lean>, Vec<String>, &str, &str, u64, usize, usize, usize),
LeanIo<Obj<'lean>>,
> = unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(
self.environment.clone(),
sources_owned,
options.namespace_context_str(),
options.file_label_str(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
handle,
trampoline,
);
let batch_len = u64::try_from(sources.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
bridge.decode(result?)
} else {
let address = self.capabilities.symbols().elaborate_bulk;
let call: LeanExported<
'lean,
'_,
(Obj<'lean>, Vec<String>, &str, &str, u64, usize),
LeanIo<Vec<Result<LeanExpr<'lean>, LeanElabFailure>>>,
> = unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = call.call(
self.environment.clone(),
sources_owned,
options.namespace_context_str(),
options.file_label_str(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
);
let batch_len = u64::try_from(sources.len()).unwrap_or(u64::MAX);
self.record_call(batch_len, t.elapsed());
result
}
}
pub fn call_capability<Args, R>(
&mut self,
name: &str,
args: Args,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<R::Output>
where
Args: LeanArgs<'lean>,
R: DecodeCallResult<'lean>,
{
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.call_capability",
symbol = name,
arity = Args::ARITY,
)
.entered();
check_cancellation(cancellation)?;
let address = self.capabilities.library().resolve_function_symbol(name)?;
check_cancellation(cancellation)?;
let call: LeanExported<'lean, '_, Args, R> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = Args::invoke(&call, args);
self.record_call(0, t.elapsed());
result
}
fn runtime(&self) -> &'lean lean_rs::LeanRuntime {
self.capabilities.host().runtime()
}
fn make_name(&self, name: &str, cancellation: Option<&LeanCancellationToken>) -> LeanResult<LeanName<'lean>> {
check_cancellation(cancellation)?;
let address = self.capabilities.symbols().name_from_string;
let to_name: LeanExported<'lean, '_, (&str,), LeanName<'lean>> =
unsafe { LeanExported::from_function_address(self.runtime(), address) };
let t = Instant::now();
let result = to_name.call(name);
self.record_call(0, t.elapsed());
result
}
}