#![allow(private_bounds, private_interfaces)]
use core::cell::Cell;
use std::sync::Mutex;
use std::time::Instant;
use crate::host::cancellation::{LeanCancellationToken, check_cancellation};
use crate::host::capabilities::LeanCapabilities;
use crate::host::declaration_search::{
DeclarationInspectionRequest, DeclarationInspectionResult, DeclarationSearchRequest, DeclarationSearchResult,
};
use crate::host::elaboration::{LeanElabFailure, LeanElabOptions};
use crate::host::evidence::{EvidenceStatus, LeanEvidence, LeanKernelOutcome, ProofSummary};
use crate::host::meta::{LeanMetaOptions, LeanMetaResponse, LeanMetaService};
use crate::host::process::{
DeclarationVerificationBatchOutcome, DeclarationVerificationBatchRequest, DeclarationVerificationOutcome,
DeclarationVerificationRequest, ModuleQuery, ModuleQueryBatchCachedOutcome, ModuleQueryBatchOutcome,
ModuleQueryCachePolicy, ModuleQueryOutcome, ModuleQueryOutputBudgets, ModuleQuerySelector,
ModuleSnapshotCacheClearResult, ProofAttemptOutcome, ProofAttemptRequest,
};
use crate::host::progress::{LeanProgressSink, ProgressBridge, report_progress};
use crate::host::shim_bindings::{HostShimBindings, binding_error_to_lean_error};
use lean_rs::Obj;
use lean_rs::abi::structure::{alloc_ctor_with_objects, take_ctor_objects, view};
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::{LeanDeclaration, LeanExpr, LeanName};
#[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 LeanImportStats {
pub direct_import_names: Vec<String>,
pub effective_module_count: u64,
pub compacted_region_count: u64,
pub memory_mapped_region_count: u64,
pub compacted_region_bytes: u64,
pub memory_mapped_region_bytes: u64,
pub non_memory_mapped_region_bytes: u64,
pub imported_bytes: u64,
pub imported_constant_count: u64,
pub extension_count: u64,
pub total_imported_extension_entries: u64,
pub import_level: String,
pub import_all: bool,
pub load_exts: bool,
}
impl<'lean> TryFromLean<'lean> for LeanImportStats {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
let (
effective_module_count,
compacted_region_count,
memory_mapped_region_count,
compacted_region_bytes,
memory_mapped_region_bytes,
non_memory_mapped_region_bytes,
imported_bytes,
imported_constant_count,
extension_count,
total_imported_extension_entries,
import_all,
load_exts,
) = {
let ctor = view(&obj).ctor_shape(0, 2, "ImportStats")?;
(
ctor.uint64(0, "ImportStats.effectiveModuleCount")?,
ctor.uint64(8, "ImportStats.compactedRegionCount")?,
ctor.uint64(16, "ImportStats.memoryMappedRegionCount")?,
ctor.uint64(24, "ImportStats.compactedRegionBytes")?,
ctor.uint64(32, "ImportStats.memoryMappedRegionBytes")?,
ctor.uint64(40, "ImportStats.nonMemoryMappedRegionBytes")?,
ctor.uint64(48, "ImportStats.importedBytes")?,
ctor.uint64(56, "ImportStats.importedConstantCount")?,
ctor.uint64(64, "ImportStats.extensionCount")?,
ctor.uint64(72, "ImportStats.totalImportedExtensionEntries")?,
ctor.bool(80, "ImportStats.importAll")?,
ctor.bool(81, "ImportStats.loadExts")?,
)
};
let [direct_import_names, import_level] = take_ctor_objects::<2>(obj, 0, "ImportStats")?;
Ok(Self {
direct_import_names: Vec::<String>::try_from_lean(direct_import_names)?,
effective_module_count,
compacted_region_count,
memory_mapped_region_count,
compacted_region_bytes,
memory_mapped_region_bytes,
non_memory_mapped_region_bytes,
imported_bytes,
imported_constant_count,
extension_count,
total_imported_extension_entries,
import_level: String::try_from_lean(import_level)?,
import_all,
load_exts,
})
}
}
impl LeanImportStats {
#[must_use]
pub fn memory_diagnostic(&self) -> String {
format!(
"import_profile=level:{} import_all:{} load_exts:{} direct_import_count={} direct_imports={} effective_modules={} compacted_regions={} memory_mapped_regions={} compacted_region_bytes={} memory_mapped_region_bytes={} non_memory_mapped_region_bytes={} imported_constants={} extension_entries={}",
self.import_level,
self.import_all,
self.load_exts,
self.direct_import_names.len(),
self.direct_import_names.join(","),
self.effective_module_count,
self.compacted_region_count,
self.memory_mapped_region_count,
self.compacted_region_bytes,
self.memory_mapped_region_bytes,
self.non_memory_mapped_region_bytes,
self.imported_constant_count,
self.total_imported_extension_entries
)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LeanImportLevel {
Exported,
Server,
Private,
}
impl LeanImportLevel {
pub const fn as_str(self) -> &'static str {
match self {
Self::Exported => "exported",
Self::Server => "server",
Self::Private => "private",
}
}
const fn code(self) -> u8 {
match self {
Self::Exported => 0,
Self::Server => 1,
Self::Private => 2,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum LeanSessionImportProfile {
ExportedPublic,
Server,
#[default]
Private,
FullPrivateCompat,
}
impl LeanSessionImportProfile {
pub const fn label(self) -> &'static str {
match self {
Self::ExportedPublic => "exported-public",
Self::Server => "server",
Self::Private => "private",
Self::FullPrivateCompat => "full-private-compat",
}
}
pub const fn import_all(self) -> bool {
matches!(self, Self::FullPrivateCompat)
}
pub const fn import_level(self) -> LeanImportLevel {
match self {
Self::ExportedPublic => LeanImportLevel::Exported,
Self::Server => LeanImportLevel::Server,
Self::Private | Self::FullPrivateCompat => LeanImportLevel::Private,
}
}
pub const fn load_exts(self) -> bool {
true
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LeanImportProfileMode {
FullSession(LeanSessionImportProfile),
ExportedNoExts,
}
impl LeanImportProfileMode {
pub const fn label(self) -> &'static str {
match self {
Self::FullSession(profile) => profile.label(),
Self::ExportedNoExts => "exported-no-exts",
}
}
pub const fn import_all(self) -> bool {
match self {
Self::FullSession(profile) => profile.import_all(),
Self::ExportedNoExts => false,
}
}
pub const fn import_level(self) -> LeanImportLevel {
match self {
Self::FullSession(profile) => profile.import_level(),
Self::ExportedNoExts => LeanImportLevel::Exported,
}
}
pub const fn load_exts(self) -> bool {
match self {
Self::FullSession(profile) => profile.load_exts(),
Self::ExportedNoExts => false,
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct LeanImportProfilerOptions {
pub profiler: bool,
pub trace_profiler: bool,
pub trace_profiler_output: Option<String>,
}
impl LeanImportProfilerOptions {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn profiler(mut self, enabled: bool) -> Self {
self.profiler = enabled;
self
}
#[must_use]
pub fn trace_profiler(mut self, enabled: bool) -> Self {
self.trace_profiler = enabled;
self
}
#[must_use]
pub fn trace_profiler_output(mut self, path: impl Into<String>) -> Self {
self.trace_profiler_output = Some(path.into());
self
}
}
#[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 = <Obj<'lean> as LeanAbi<'lean>>::CRepr;
fn into_c(self, runtime: &'lean lean_rs::LeanRuntime) -> Self::CRepr {
self.into_lean(runtime).into_raw()
}
fn from_c(_c: Self::CRepr, _runtime: &'lean lean_rs::LeanRuntime) -> LeanResult<Self> {
Err(conversion_error(
"LeanDeclarationFilter cannot decode a Lean call result; it is an argument-only type",
))
}
}
pub struct LeanSession<'lean, 'c> {
capabilities: &'c LeanCapabilities<'lean, 'c>,
shims: HostShimBindings<'lean, 'c>,
environment: Obj<'lean>,
import_stats: LeanImportStats,
stats: Cell<SessionStats>,
}
static SESSION_IMPORT_LOCK: Mutex<()> = Mutex::new(());
pub(crate) fn with_session_import_lock<T>(f: impl FnOnce() -> LeanResult<T>) -> LeanResult<T> {
let _import_guard = SESSION_IMPORT_LOCK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
f()
}
impl<'lean, 'c> LeanSession<'lean, 'c> {
pub(crate) fn import_search_paths(capabilities: &LeanCapabilities<'lean, '_>) -> LeanResult<Vec<String>> {
let project = capabilities.host().project();
let mut search_paths: Vec<String> = project
.olean_search_paths()
.into_iter()
.map(|path| path.to_string_lossy().into_owned())
.collect();
search_paths.push(
crate::host::lake::LakeProject::interop_olean_search_path()?
.to_string_lossy()
.into_owned(),
);
search_paths.push(
crate::host::lake::LakeProject::shim_olean_search_path()?
.to_string_lossy()
.into_owned(),
);
Ok(search_paths)
}
pub(crate) fn import(
capabilities: &'c LeanCapabilities<'lean, 'c>,
imports: &[&str],
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Self> {
Self::import_with_profile(
capabilities,
imports,
LeanSessionImportProfile::default(),
cancellation,
progress,
)
}
pub(crate) fn import_with_profile(
capabilities: &'c LeanCapabilities<'lean, 'c>,
imports: &[&str],
profile: LeanSessionImportProfile,
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Self> {
let _span = tracing::info_span!(
target: "lean_rs",
"lean_rs.host.session.import",
profile = profile.label(),
imports_len = imports.len(),
)
.entered();
check_cancellation(cancellation)?;
let search_paths = Self::import_search_paths(capabilities)?;
let imports_owned: Vec<String> = imports.iter().map(|&s| s.to_owned()).collect();
with_session_import_lock(|| {
let shims = HostShimBindings::resolve(capabilities.shim_capability())
.map_err(|err| binding_error_to_lean_error(&err))?;
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 raw = if profile == LeanSessionImportProfile::default() {
shims
.session_import_progress
.call(search_paths, imports_owned, handle, trampoline)?
} else {
shims.session_import_profile_progress.call(
search_paths,
imports_owned,
profile.import_all(),
profile.import_level().code(),
handle,
trampoline,
)?
};
bridge.decode(raw)?
} else if profile == LeanSessionImportProfile::default() {
shims.session_import.call(search_paths, imports_owned)?
} else {
shims.session_import_profile.call(
search_paths,
imports_owned,
profile.import_all(),
profile.import_level().code(),
profile.load_exts(),
false,
false,
String::new(),
)?
};
let import_stats = shims.env_import_stats.call(
environment.clone(),
profile.import_level().as_str().to_owned(),
profile.load_exts(),
)?;
Ok(Self {
capabilities,
shims,
environment,
import_stats,
stats: Cell::new(SessionStats::default()),
})
})
}
pub(crate) fn import_profiled(
capabilities: &'c LeanCapabilities<'lean, 'c>,
imports: &[&str],
mode: LeanImportProfileMode,
profiler_options: &LeanImportProfilerOptions,
) -> LeanResult<Self> {
let _span = tracing::info_span!(
target: "lean_rs",
"lean_rs.host.session.import_profiled",
mode = mode.label(),
imports_len = imports.len(),
)
.entered();
let search_paths = Self::import_search_paths(capabilities)?;
let imports_owned: Vec<String> = imports.iter().map(|&s| s.to_owned()).collect();
with_session_import_lock(|| {
let shims = HostShimBindings::resolve(capabilities.shim_capability())
.map_err(|err| binding_error_to_lean_error(&err))?;
let environment = shims.session_import_profile.call(
search_paths,
imports_owned,
mode.import_all(),
mode.import_level().code(),
mode.load_exts(),
profiler_options.profiler,
profiler_options.trace_profiler,
profiler_options.trace_profiler_output.clone().unwrap_or_default(),
)?;
let import_stats = shims.env_import_stats.call(
environment.clone(),
mode.import_level().as_str().to_owned(),
mode.load_exts(),
)?;
Ok(Self {
capabilities,
shims,
environment,
import_stats,
stats: Cell::new(SessionStats::default()),
})
})
}
pub(crate) fn from_environment_with_import_stats(
capabilities: &'c LeanCapabilities<'lean, 'c>,
environment: Obj<'lean>,
import_stats: LeanImportStats,
) -> LeanResult<Self> {
let shims = HostShimBindings::resolve(capabilities.shim_capability())
.map_err(|err| binding_error_to_lean_error(&err))?;
Ok(Self {
capabilities,
shims,
environment,
import_stats,
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()
}
#[must_use]
pub fn import_stats(&self) -> &LeanImportStats {
&self.import_stats
}
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 t = Instant::now();
let result = self
.shims
.env_query_declaration
.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 t = Instant::now();
let raw = self.shims.env_list_declarations.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 t = Instant::now();
let result = self.shims.env_list_declarations_filtered_progress.call(
self.environment.clone(),
*filter,
handle,
trampoline,
);
self.record_call(0, t.elapsed());
bridge.decode::<Vec<Obj<'lean>>>(result?)?
} else {
let t = Instant::now();
let result = self
.shims
.env_list_declarations_filtered
.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 t = Instant::now();
let result = self
.shims
.env_declaration_source_range
.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 t = Instant::now();
let result = self
.shims
.env_declaration_type
.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 t = Instant::now();
let mut result = self
.shims
.env_declaration_type_bulk
.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 t = Instant::now();
let result = self.shims.env_declaration_type_bulk_progress.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 t = Instant::now();
let result = self
.shims
.env_declaration_type_bulk
.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 t = Instant::now();
let result = self
.shims
.env_declaration_kind
.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 t = Instant::now();
let mut result = Self::decode_strings_cached(
self.shims
.env_declaration_kind_bulk
.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 t = Instant::now();
let result = self.shims.env_declaration_kind_bulk_progress.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 t = Instant::now();
let result = self
.shims
.env_declaration_kind_bulk
.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 t = Instant::now();
let result = self
.shims
.env_declaration_name
.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 t = Instant::now();
let mut result = Self::decode_strings_cached(
self.shims
.env_declaration_name_bulk
.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 t = Instant::now();
let result = self.shims.env_declaration_name_bulk_progress.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 t = Instant::now();
let result = self
.shims
.env_declaration_name_bulk
.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 name_to_string(
&mut self,
name: &LeanName<'lean>,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<String> {
let _span = tracing::debug_span!(target: "lean_rs", "lean_rs.host.session.name_to_string").entered();
check_cancellation(cancellation)?;
let t = Instant::now();
let result = self.shims.name_to_string.call(name.clone());
self.record_call(0, t.elapsed());
result
}
pub fn name_to_string_bulk(
&mut self,
names: &[LeanName<'lean>],
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Vec<String>> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.name_to_string_bulk",
batch_size = names.len(),
)
.entered();
if names.is_empty() {
return Ok(Vec::new());
}
check_cancellation(cancellation)?;
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.name_to_string(name, cancellation)?);
report_progress(
progress,
"name_to_string_bulk",
u64::try_from(idx.saturating_add(1)).unwrap_or(u64::MAX),
total,
started,
)?;
}
Ok(out)
}
pub fn list_declarations_strings(
&mut self,
filter: &LeanDeclarationFilter,
cancellation: Option<&LeanCancellationToken>,
progress: Option<&dyn LeanProgressSink>,
) -> LeanResult<Vec<String>> {
let _span = tracing::debug_span!(target: "lean_rs", "lean_rs.host.session.list_declarations_strings").entered();
let names = self.list_declarations_filtered(filter, cancellation, None)?;
self.name_to_string_bulk(&names, cancellation, progress)
}
pub fn search_declarations(
&mut self,
search: &DeclarationSearchRequest,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<DeclarationSearchResult> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.search_declarations",
limit = search.limit,
include_source = search.include_source,
)
.entered();
check_cancellation(cancellation)?;
let source_roots = if search.include_source {
self.capabilities
.host()
.project()
.source_roots()?
.into_iter()
.map(|path| path.to_string_lossy().into_owned())
.collect::<Vec<_>>()
} else {
Vec::new()
};
check_cancellation(cancellation)?;
let t = Instant::now();
let result = self
.shims
.env_search_declarations
.call(self.environment.clone(), search.clone(), source_roots);
self.record_call(0, t.elapsed());
result
}
pub fn inspect_declaration(
&mut self,
request: &DeclarationInspectionRequest,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<DeclarationInspectionResult> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.inspect_declaration",
source = request.fields.source,
statement = request.fields.statement,
docstring = request.fields.docstring,
attributes = request.fields.attributes,
flags = request.fields.flags,
)
.entered();
check_cancellation(cancellation)?;
let Some(inspect) = &self.shims.env_inspect_declaration else {
return Ok(DeclarationInspectionResult::Unsupported);
};
let source_roots = if request.fields.source {
self.capabilities
.host()
.project()
.source_roots()?
.into_iter()
.map(|path| path.to_string_lossy().into_owned())
.collect::<Vec<_>>()
} else {
Vec::new()
};
check_cancellation(cancellation)?;
let t = Instant::now();
let result = inspect.call(
self.environment.clone(),
request.clone(),
source_roots,
lean_toolchain::LEAN_HEARTBEAT_LIMIT_DEFAULT,
);
self.record_call(0, t.elapsed());
result
}
pub fn expr_to_string_raw(
&mut self,
expr: &LeanExpr<'lean>,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<String> {
let _span = tracing::debug_span!(target: "lean_rs", "lean_rs.host.session.expr_to_string_raw").entered();
check_cancellation(cancellation)?;
let t = Instant::now();
let result = self.shims.env_expr_to_string_raw.call(expr.clone());
self.record_call(0, t.elapsed());
result
}
pub fn process_module_query(
&mut self,
source: &str,
query: &ModuleQuery,
options: &LeanElabOptions,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<ModuleQueryOutcome> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.process_module_query",
source_len = source.len(),
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
let Some(call) = self.shims.process_module_query.as_ref() else {
return Ok(ModuleQueryOutcome::Unsupported);
};
let t = Instant::now();
let result = call.call(
self.environment.clone(),
source.to_owned(),
query.clone(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
);
self.record_call(0, t.elapsed());
result
}
pub fn process_module_query_batch(
&mut self,
source: &str,
selectors: &[ModuleQuerySelector],
budgets: &ModuleQueryOutputBudgets,
options: &LeanElabOptions,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<ModuleQueryBatchOutcome> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.process_module_query_batch",
source_len = source.len(),
selectors = selectors.len(),
per_field_bytes = budgets.per_field_bytes,
total_bytes = budgets.total_bytes,
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
let Some(call) = self.shims.process_module_query_batch.as_ref() else {
return Ok(ModuleQueryBatchOutcome::Unsupported);
};
let selectors_owned = selectors.to_vec();
let t = Instant::now();
let result = call.call(
self.environment.clone(),
source.to_owned(),
selectors_owned,
budgets.clone(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
);
self.record_call(u64::try_from(selectors.len()).unwrap_or(u64::MAX), t.elapsed());
result
}
pub fn process_module_query_batch_cached(
&mut self,
source: &str,
selectors: &[ModuleQuerySelector],
budgets: &ModuleQueryOutputBudgets,
options: &LeanElabOptions,
policy: &ModuleQueryCachePolicy,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<ModuleQueryBatchCachedOutcome> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.process_module_query_batch_cached",
source_len = source.len(),
selectors = selectors.len(),
per_field_bytes = budgets.per_field_bytes,
total_bytes = budgets.total_bytes,
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
let Some(call) = self.shims.process_module_query_batch_cached.as_ref() else {
return Ok(ModuleQueryBatchCachedOutcome::Unsupported);
};
let selectors_owned = selectors.to_vec();
let policy_text = format!(
"{}\n{}\n{}\n{}\n{}",
policy.file_identity, policy.key, policy.max_entries, policy.ttl_millis, policy.max_bytes
);
let t = Instant::now();
let result = call.call(
self.environment.clone(),
source.to_owned(),
selectors_owned,
budgets.clone(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
policy_text,
);
self.record_call(u64::try_from(selectors.len()).unwrap_or(u64::MAX), t.elapsed());
result
}
pub fn attempt_proof(
&mut self,
request: &ProofAttemptRequest,
options: &LeanElabOptions,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<ProofAttemptOutcome> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.attempt_proof",
source_len = request.source.len(),
candidates = request.candidates.len(),
per_field_bytes = request.budgets.per_field_bytes,
total_bytes = request.budgets.total_bytes,
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
let Some(call) = self.shims.attempt_proof.as_ref() else {
return Ok(ProofAttemptOutcome::Unsupported);
};
let t = Instant::now();
let result = call.call(
self.environment.clone(),
request.clone(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
);
self.record_call(u64::try_from(request.candidates.len()).unwrap_or(u64::MAX), t.elapsed());
result
}
pub fn verify_declaration(
&mut self,
request: &DeclarationVerificationRequest,
options: &LeanElabOptions,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<DeclarationVerificationOutcome> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.verify_declaration",
source_len = request.source.len(),
report_axioms = request.report_axioms,
per_field_bytes = request.budgets.per_field_bytes,
total_bytes = request.budgets.total_bytes,
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
let Some(call) = self.shims.verify_declaration.as_ref() else {
return Ok(DeclarationVerificationOutcome::Unsupported);
};
let t = Instant::now();
let result = call.call(
self.environment.clone(),
request.clone(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
);
self.record_call(1, t.elapsed());
result
}
pub fn verify_declaration_batch(
&mut self,
request: &DeclarationVerificationBatchRequest,
options: &LeanElabOptions,
cancellation: Option<&LeanCancellationToken>,
) -> LeanResult<DeclarationVerificationBatchOutcome> {
let _span = tracing::debug_span!(
target: "lean_rs",
"lean_rs.host.session.verify_declaration_batch",
source_len = request.source.len(),
targets = request.targets.len(),
report_axioms = request.report_axioms,
per_field_bytes = request.budgets.per_field_bytes,
total_bytes = request.budgets.total_bytes,
heartbeats = options.heartbeats(),
diagnostic_byte_limit = options.diagnostic_byte_limit_usize(),
)
.entered();
check_cancellation(cancellation)?;
let Some(call) = self.shims.verify_declaration_batch.as_ref() else {
return Ok(DeclarationVerificationBatchOutcome::Unsupported);
};
let t = Instant::now();
let result = call.call(
self.environment.clone(),
request.clone(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
);
self.record_call(u64::try_from(request.targets.len()).unwrap_or(u64::MAX), t.elapsed());
result
}
pub fn clear_module_snapshot_cache(&mut self) -> LeanResult<ModuleSnapshotCacheClearResult> {
let Some(call) = self.shims.clear_module_snapshot_cache.as_ref() else {
return Ok(ModuleSnapshotCacheClearResult {
entries_cleared: 0,
approx_bytes_cleared: 0,
});
};
let t = Instant::now();
let result = call.call();
self.record_call(0, t.elapsed());
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 t = Instant::now();
let result = self.shims.elaborate.call(
self.environment.clone(),
source.to_owned(),
expected_type.cloned(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
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 t = Instant::now();
let result = self.shims.kernel_check_progress.call(
self.environment.clone(),
source.to_owned(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
handle,
trampoline,
);
self.record_call(0, t.elapsed());
bridge.decode(result?)
} else {
let t = Instant::now();
let result = self.shims.kernel_check.call(
self.environment.clone(),
source.to_owned(),
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
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 t = Instant::now();
let result = self.shims.check_evidence.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 t = Instant::now();
let result = self
.shims
.evidence_summary
.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
LeanMetaService<Req, Resp>: HostMetaDispatch<'lean, Req, Resp>,
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)?;
service.dispatch(self, request, options)
}
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 t = Instant::now();
let result = self.shims.env_query_declarations_bulk_progress.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 t = Instant::now();
let result = self
.shims
.env_query_declarations_bulk
.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 t = Instant::now();
let result = self.shims.elaborate_bulk_progress.call(
self.environment.clone(),
sources_owned,
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
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 t = Instant::now();
let result = self.shims.elaborate_bulk.call(
self.environment.clone(),
sources_owned,
options.namespace_context_str().to_owned(),
options.file_label_str().to_owned(),
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
}
}
fn make_name(&self, name: &str, cancellation: Option<&LeanCancellationToken>) -> LeanResult<LeanName<'lean>> {
check_cancellation(cancellation)?;
let lean_name = lean_rs::__host_internals::string_from_str(self.capabilities.host().runtime(), name);
let t = Instant::now();
let result = self.shims.name_from_string.call(lean_name);
self.record_call(0, t.elapsed());
result
}
}
trait HostMetaDispatch<'lean, Req, Resp> {
fn dispatch(
&self,
session: &mut LeanSession<'lean, '_>,
request: Req,
options: &LeanMetaOptions,
) -> LeanResult<LeanMetaResponse<Resp>>;
}
impl<'lean> HostMetaDispatch<'lean, LeanExpr<'lean>, LeanExpr<'lean>>
for LeanMetaService<LeanExpr<'lean>, LeanExpr<'lean>>
{
fn dispatch(
&self,
session: &mut LeanSession<'lean, '_>,
request: LeanExpr<'lean>,
options: &LeanMetaOptions,
) -> LeanResult<LeanMetaResponse<LeanExpr<'lean>>> {
let Some(call) = (match self.name() {
"lean_rs_host_meta_infer_type" => session.shims.meta_infer_type.as_ref(),
"lean_rs_host_meta_whnf" => session.shims.meta_whnf.as_ref(),
"lean_rs_host_meta_heartbeat_burn" => session.shims.meta_heartbeat_burn.as_ref(),
_ => None,
}) else {
return Ok(unsupported_meta_response(self.name()));
};
let t = Instant::now();
let result = call.call(
session.environment.clone(),
request,
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
options.transparency_byte(),
);
session.record_call(0, t.elapsed());
result
}
}
impl<'lean>
HostMetaDispatch<
'lean,
(
LeanExpr<'lean>,
LeanExpr<'lean>,
crate::host::meta::LeanMetaTransparency,
),
bool,
>
for LeanMetaService<
(
LeanExpr<'lean>,
LeanExpr<'lean>,
crate::host::meta::LeanMetaTransparency,
),
bool,
>
{
fn dispatch(
&self,
session: &mut LeanSession<'lean, '_>,
request: (
LeanExpr<'lean>,
LeanExpr<'lean>,
crate::host::meta::LeanMetaTransparency,
),
options: &LeanMetaOptions,
) -> LeanResult<LeanMetaResponse<bool>> {
let Some(call) = session.shims.meta_is_def_eq.as_ref() else {
return Ok(unsupported_meta_response(self.name()));
};
let t = Instant::now();
let result = call.call(
session.environment.clone(),
request,
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
options.transparency_byte(),
);
session.record_call(0, t.elapsed());
result
}
}
impl<'lean> HostMetaDispatch<'lean, LeanExpr<'lean>, String> for LeanMetaService<LeanExpr<'lean>, String> {
fn dispatch(
&self,
session: &mut LeanSession<'lean, '_>,
request: LeanExpr<'lean>,
options: &LeanMetaOptions,
) -> LeanResult<LeanMetaResponse<String>> {
let Some(call) = session.shims.meta_pp_expr.as_ref() else {
return Ok(unsupported_meta_response(self.name()));
};
let t = Instant::now();
let result = call.call(
session.environment.clone(),
request,
options.heartbeats(),
options.diagnostic_byte_limit_usize(),
options.transparency_byte(),
);
session.record_call(0, t.elapsed());
result
}
}
fn unsupported_meta_response<Resp>(symbol: &str) -> LeanMetaResponse<Resp> {
LeanMetaResponse::Unsupported(LeanElabFailure::synthetic(
format!("bundled host shim does not export meta service '{symbol}'"),
"<lean-rs-host meta>".to_owned(),
))
}