Skip to main content

oxilean_elab/instance/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use oxilean_kernel::{Expr, Name};
7
8use std::collections::{HashMap, HashSet};
9
10/// Outcome of a single instance resolution attempt.
11#[derive(Debug, Clone, PartialEq)]
12pub enum ResolutionResult {
13    /// A unique instance was found.
14    Found(Name),
15    /// Multiple candidate instances with equal priority.
16    Ambiguous(Vec<Name>),
17    /// No instance found.
18    NotFound,
19    /// Search exceeded the depth limit.
20    DepthExceeded,
21}
22/// A trace of instance resolution attempts.
23#[derive(Debug, Default)]
24pub struct InstanceResolutionTrace {
25    entries: Vec<ResolutionTraceEntry>,
26    pub(crate) enabled: bool,
27}
28impl InstanceResolutionTrace {
29    /// Create an empty, disabled trace.
30    pub fn new() -> Self {
31        Self::default()
32    }
33    /// Create an enabled trace.
34    pub fn enabled() -> Self {
35        Self {
36            entries: Vec::new(),
37            enabled: true,
38        }
39    }
40    /// Enable tracing.
41    pub fn enable(&mut self) {
42        self.enabled = true;
43    }
44    /// Disable tracing.
45    pub fn disable(&mut self) {
46        self.enabled = false;
47    }
48    /// Log a resolution attempt.
49    pub fn log(&mut self, class: Name, instance: Name, outcome: impl Into<String>, depth: usize) {
50        if !self.enabled {
51            return;
52        }
53        self.entries.push(ResolutionTraceEntry {
54            class,
55            instance,
56            outcome: outcome.into(),
57            depth,
58        });
59    }
60    /// Return the number of trace entries.
61    pub fn len(&self) -> usize {
62        self.entries.len()
63    }
64    /// Return true if the trace is empty.
65    pub fn is_empty(&self) -> bool {
66        self.entries.is_empty()
67    }
68    /// Clear all trace entries.
69    pub fn clear(&mut self) {
70        self.entries.clear();
71    }
72    /// Return entries for a specific class.
73    pub fn entries_for_class(&self, class: &Name) -> Vec<&ResolutionTraceEntry> {
74        self.entries.iter().filter(|e| &e.class == class).collect()
75    }
76}
77/// A stack of instance scopes.
78#[derive(Debug, Default)]
79pub struct InstanceScopeStack {
80    scopes: Vec<InstanceScope>,
81}
82impl InstanceScopeStack {
83    /// Create an empty scope stack.
84    pub fn new() -> Self {
85        Self::default()
86    }
87    /// Push a new scope.
88    pub fn push_scope(&mut self) {
89        self.scopes.push(InstanceScope::new());
90    }
91    /// Pop the top scope.
92    pub fn pop_scope(&mut self) -> Option<InstanceScope> {
93        self.scopes.pop()
94    }
95    /// Add an instance to the top scope.
96    pub fn add_to_top(&mut self, inst: TypeclassInstance) {
97        if let Some(top) = self.scopes.last_mut() {
98            top.add(inst);
99        }
100    }
101    /// Return all local instances for a class, from innermost to outermost scope.
102    pub fn local_instances_for_class(&self, class: &Name) -> Vec<&TypeclassInstance> {
103        self.scopes
104            .iter()
105            .rev()
106            .flat_map(|s| s.instances_for_class(class))
107            .collect()
108    }
109    /// Return the current stack depth.
110    pub fn depth(&self) -> usize {
111        self.scopes.len()
112    }
113    /// Return true if the stack is empty.
114    pub fn is_empty(&self) -> bool {
115        self.scopes.is_empty()
116    }
117}
118/// A snapshot of instance resolution state for rollback.
119#[derive(Clone, Debug)]
120pub struct InstanceSnapshot {
121    /// The class name being resolved.
122    pub class: Name,
123    /// Candidates available at snapshot time.
124    pub candidates: Vec<InstanceDecl>,
125    /// Cache entries at snapshot time.
126    pub cache_size: usize,
127}
128impl InstanceSnapshot {
129    /// Create a new snapshot.
130    pub fn new(class: Name, candidates: Vec<InstanceDecl>, cache_size: usize) -> Self {
131        Self {
132            class,
133            candidates,
134            cache_size,
135        }
136    }
137    /// Number of candidates at snapshot time.
138    pub fn candidate_count(&self) -> usize {
139        self.candidates.len()
140    }
141}
142/// Instance resolution engine.
143///
144/// Maintains a registry of type-class instances organised by class name.
145/// Supports priority-based selection, caching, and backtracking search.
146pub struct InstanceResolver {
147    /// Registered instances, keyed by class name.
148    instances: HashMap<Name, Vec<InstanceDecl>>,
149    /// Maximum search depth before giving up.
150    max_depth: usize,
151    /// Result cache for repeated queries.
152    cache: InstanceCache,
153    /// Whether to use the cache.
154    cache_enabled: bool,
155    /// Number of instances registered in total.
156    total_registered: usize,
157}
158impl InstanceResolver {
159    /// Create a new instance resolver with default settings.
160    pub fn new() -> Self {
161        Self {
162            instances: HashMap::new(),
163            max_depth: 10,
164            cache: InstanceCache::new(),
165            cache_enabled: true,
166            total_registered: 0,
167        }
168    }
169    /// Create a resolver with a custom search depth.
170    pub fn with_max_depth(max_depth: usize) -> Self {
171        let mut r = Self::new();
172        r.max_depth = max_depth;
173        r
174    }
175    /// Register an instance.
176    pub fn register(&mut self, instance: InstanceDecl) {
177        self.total_registered += 1;
178        let bucket = self.instances.entry(instance.class.clone()).or_default();
179        bucket.push(instance);
180        bucket.sort_by(compare_priority);
181    }
182    /// Register multiple instances at once.
183    pub fn register_many(&mut self, instances: impl IntoIterator<Item = InstanceDecl>) {
184        for inst in instances {
185            self.register(inst);
186        }
187    }
188    /// Remove all instances for a given class (useful in testing).
189    pub fn clear_class(&mut self, class: &Name) {
190        if let Some(bucket) = self.instances.get_mut(class) {
191            self.total_registered -= bucket.len();
192            bucket.clear();
193        }
194    }
195    /// Find an instance for a type class and type.
196    ///
197    /// Returns the highest-priority matching instance, or `None`.
198    /// If the cache is enabled, previously found instances are returned immediately.
199    pub fn find_instance(&mut self, class: &Name, ty: &Expr) -> Option<&InstanceDecl> {
200        if self.cache_enabled {
201            let key = CacheKey::new(class, ty);
202            if self.cache.get(&key).is_some() {}
203            let _ = key;
204        }
205        if let Some(instances) = self.instances.get(class) {
206            instances.first()
207        } else {
208            None
209        }
210    }
211    /// Resolve an instance and return the full `ResolutionResult`.
212    ///
213    /// Unlike `find_instance`, this method detects ambiguity.
214    pub fn resolve(&mut self, class: &Name, ty: &Expr) -> ResolutionResult {
215        let instances = match self.instances.get(class) {
216            Some(v) if !v.is_empty() => v.clone(),
217            _ => return ResolutionResult::NotFound,
218        };
219        let candidates: Vec<InstanceDecl> = instances
220            .into_iter()
221            .filter(|i| structural_match(&i.ty, ty))
222            .collect();
223        if candidates.is_empty() {
224            return ResolutionResult::NotFound;
225        }
226        let best_priority = candidates
227            .iter()
228            .map(|i| i.priority)
229            .min()
230            .expect("candidates is non-empty (checked above)");
231        let best: Vec<&InstanceDecl> = candidates
232            .iter()
233            .filter(|i| i.priority == best_priority)
234            .collect();
235        if best.len() == 1 {
236            let name = best[0].name.clone();
237            if self.cache_enabled {
238                let key = CacheKey::new(class, ty);
239                self.cache.insert(key, name.clone());
240            }
241            ResolutionResult::Found(name)
242        } else {
243            ResolutionResult::Ambiguous(best.iter().map(|i| i.name.clone()).collect())
244        }
245    }
246    /// Try to resolve, returning `Err` with a detailed error on failure.
247    pub fn resolve_or_error(&mut self, class: &Name, ty: &Expr) -> Result<Name, InstanceError> {
248        match self.resolve(class, ty) {
249            ResolutionResult::Found(name) => Ok(name),
250            ResolutionResult::NotFound => Err(InstanceError::NotFound {
251                class: class.clone(),
252            }),
253            ResolutionResult::Ambiguous(candidates) => Err(InstanceError::Ambiguous {
254                class: class.clone(),
255                candidates,
256            }),
257            ResolutionResult::DepthExceeded => Err(InstanceError::MaxDepthExceeded {
258                depth: self.max_depth,
259            }),
260        }
261    }
262    /// Get all instances for a class.
263    pub fn get_instances(&self, class: &Name) -> Vec<&InstanceDecl> {
264        self.instances
265            .get(class)
266            .map(|v| v.iter().collect())
267            .unwrap_or_default()
268    }
269    /// Return the number of classes registered.
270    pub fn class_count(&self) -> usize {
271        self.instances.len()
272    }
273    /// Return the total number of instances registered.
274    pub fn total_registered(&self) -> usize {
275        self.total_registered
276    }
277    /// Set the maximum search depth.
278    pub fn set_max_depth(&mut self, depth: usize) {
279        self.max_depth = depth;
280    }
281    /// Get the maximum search depth.
282    pub fn max_depth(&self) -> usize {
283        self.max_depth
284    }
285    /// Enable or disable the resolution cache.
286    pub fn set_cache_enabled(&mut self, enabled: bool) {
287        self.cache_enabled = enabled;
288    }
289    /// Return whether caching is enabled.
290    pub fn cache_enabled(&self) -> bool {
291        self.cache_enabled
292    }
293    /// Obtain a reference to the resolution cache for diagnostics.
294    pub fn cache(&self) -> &InstanceCache {
295        &self.cache
296    }
297    /// Clear the resolution cache.
298    pub fn clear_cache(&mut self) {
299        self.cache.clear();
300    }
301    /// Check whether any instance is registered for a given class.
302    pub fn has_instances_for(&self, class: &Name) -> bool {
303        self.instances
304            .get(class)
305            .map(|v| !v.is_empty())
306            .unwrap_or(false)
307    }
308    /// Return all class names that have at least one instance.
309    pub fn registered_classes(&self) -> Vec<&Name> {
310        self.instances
311            .iter()
312            .filter(|(_, v)| !v.is_empty())
313            .map(|(k, _)| k)
314            .collect()
315    }
316}
317/// A lightweight matcher for instance head types.
318#[derive(Debug, Default)]
319pub struct InstanceMatcher;
320impl InstanceMatcher {
321    /// Create a new instance matcher.
322    pub fn new() -> Self {
323        Self
324    }
325    /// Try to match `query_class` against `inst_class`.
326    /// In this stub, we do a simple name equality check.
327    pub fn match_class(&self, query_class: &Name, inst_class: &Name) -> MatchOutcome {
328        if query_class == inst_class {
329            MatchOutcome::Match
330        } else {
331            MatchOutcome::NoMatch
332        }
333    }
334    /// Filter instances in a registry to those matching a query class.
335    pub fn filter_candidates<'a>(
336        &self,
337        class: &Name,
338        instances: &'a [TypeclassInstance],
339    ) -> Vec<&'a TypeclassInstance> {
340        instances
341            .iter()
342            .filter(|inst| self.match_class(class, inst.class()) == MatchOutcome::Match)
343            .collect()
344    }
345}
346/// A log entry in the instance resolution trace.
347#[derive(Debug, Clone)]
348pub struct ResolutionTraceEntry {
349    /// The class being resolved.
350    pub class: Name,
351    /// The instance tried.
352    pub instance: Name,
353    /// The outcome of the attempt.
354    pub outcome: String,
355    /// Depth at which this attempt was made.
356    pub depth: usize,
357}
358/// Error type for instance resolution failures.
359#[derive(Debug, Clone, PartialEq)]
360pub enum InstanceError {
361    /// No matching instance.
362    NotFound {
363        /// The class that had no matching instance.
364        class: Name,
365    },
366    /// Multiple equally-prioritised instances.
367    Ambiguous {
368        /// The class with multiple instances.
369        class: Name,
370        /// Candidate instance names.
371        candidates: Vec<Name>,
372    },
373    /// Recursive search went too deep.
374    MaxDepthExceeded {
375        /// The depth at which the search was aborted.
376        depth: usize,
377    },
378    /// Circular dependency between instances.
379    CircularDependency {
380        /// The dependency chain forming the cycle.
381        chain: Vec<Name>,
382    },
383    /// Instance has unresolvable sub-goals.
384    UnresolvableSubgoal {
385        /// The instance with the unresolvable sub-goal.
386        instance: Name,
387        /// The sub-goal that could not be resolved.
388        subgoal: Name,
389    },
390}
391/// An extended representation of a typeclass instance, including
392/// diamond-inheritance metadata and priority queue support.
393#[derive(Debug, Clone)]
394pub struct TypeclassInstance {
395    /// Core instance declaration.
396    pub decl: InstanceDecl,
397    /// Sub-instances required to build this instance (dependencies).
398    pub dependencies: Vec<Name>,
399    /// Functional dependency chains from superclasses.
400    pub super_instances: Vec<Name>,
401    /// Whether this is a "default" instance (lower priority).
402    pub is_default: bool,
403    /// Whether this is a "local" instance (file-scoped).
404    pub is_local: bool,
405}
406impl TypeclassInstance {
407    /// Create a new typeclass instance.
408    pub fn new(decl: InstanceDecl) -> Self {
409        Self {
410            decl,
411            dependencies: Vec::new(),
412            super_instances: Vec::new(),
413            is_default: false,
414            is_local: false,
415        }
416    }
417    /// Mark as a default instance.
418    pub fn default_instance(mut self) -> Self {
419        self.is_default = true;
420        self
421    }
422    /// Mark as a local instance.
423    pub fn local(mut self) -> Self {
424        self.is_local = true;
425        self
426    }
427    /// Add a dependency.
428    pub fn add_dependency(&mut self, dep: Name) {
429        self.dependencies.push(dep);
430    }
431    /// Add a super-instance.
432    pub fn add_super(&mut self, sup: Name) {
433        self.super_instances.push(sup);
434    }
435    /// Return the class name.
436    pub fn class(&self) -> &Name {
437        &self.decl.class
438    }
439    /// Return the instance name.
440    pub fn name(&self) -> &Name {
441        &self.decl.name
442    }
443    /// Return the instance priority.
444    pub fn priority(&self) -> u32 {
445        self.decl.priority
446    }
447}
448/// A cache entry for a resolved instance.
449#[derive(Debug, Clone)]
450pub struct InstanceCacheEntry {
451    /// The class and type key.
452    pub key: String,
453    /// The resolved chain.
454    pub chain: InstanceChain,
455    /// How many times this entry was hit.
456    pub hit_count: usize,
457}
458/// A cache for resolved instance chains.
459#[derive(Debug, Default)]
460pub struct InstanceChainCache {
461    entries: HashMap<String, InstanceCacheEntry>,
462}
463impl InstanceChainCache {
464    /// Create an empty instance cache.
465    pub fn new() -> Self {
466        Self::default()
467    }
468    /// Store a resolved chain.
469    pub fn store(&mut self, key: impl Into<String>, chain: InstanceChain) {
470        let key = key.into();
471        self.entries.insert(
472            key.clone(),
473            InstanceCacheEntry {
474                key,
475                chain,
476                hit_count: 0,
477            },
478        );
479    }
480    /// Look up a cached chain.
481    pub fn lookup(&mut self, key: &str) -> Option<&InstanceChain> {
482        if let Some(entry) = self.entries.get_mut(key) {
483            entry.hit_count += 1;
484            Some(&entry.chain)
485        } else {
486            None
487        }
488    }
489    /// Return the number of entries.
490    pub fn len(&self) -> usize {
491        self.entries.len()
492    }
493    /// Return true if the cache is empty.
494    pub fn is_empty(&self) -> bool {
495        self.entries.is_empty()
496    }
497    /// Clear all entries.
498    pub fn clear(&mut self) {
499        self.entries.clear();
500    }
501    /// Return total hit count across all entries.
502    pub fn total_hits(&self) -> usize {
503        self.entries.values().map(|e| e.hit_count).sum()
504    }
505}
506/// Configuration for instance synthesis.
507#[derive(Debug, Clone)]
508pub struct SynthConfig {
509    /// Maximum search depth.
510    pub max_depth: usize,
511    /// Maximum total instances to explore.
512    pub max_instances: usize,
513    /// Whether to allow default instances.
514    pub allow_defaults: bool,
515    /// Diamond resolution strategy.
516    pub diamond_strategy: DiamondResolutionStrategy,
517}
518/// Represents a match outcome between a type and an instance head.
519#[derive(Debug, Clone, PartialEq, Eq)]
520pub enum MatchOutcome {
521    /// The types match (possibly with substitutions).
522    Match,
523    /// The types do not match.
524    NoMatch,
525    /// Matching is deferred (requires further unification).
526    Deferred,
527}
528/// Strategy for resolving diamond inheritance in typeclass hierarchies.
529#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
530pub enum DiamondResolutionStrategy {
531    /// Reject all ambiguous instances (strict mode).
532    Strict,
533    /// Prefer the instance with the lowest priority number.
534    #[default]
535    PreferLowestPriority,
536    /// Prefer the most recently declared instance.
537    PreferMostRecent,
538    /// Use depth-first search order.
539    DepthFirst,
540}
541/// Statistics about instance resolution activity.
542#[derive(Debug, Clone, Default)]
543pub struct InstanceRegistryStats {
544    /// Number of successful resolutions.
545    pub successes: usize,
546    /// Number of failed resolutions.
547    pub failures: usize,
548    /// Number of ambiguous resolutions.
549    pub ambiguous: usize,
550    /// Total depth explored.
551    pub total_depth: usize,
552}
553impl InstanceRegistryStats {
554    /// Create new empty stats.
555    pub fn new() -> Self {
556        Self::default()
557    }
558    /// Record a successful resolution.
559    pub fn record_success(&mut self, depth: usize) {
560        self.successes += 1;
561        self.total_depth += depth;
562    }
563    /// Record a failed resolution.
564    pub fn record_failure(&mut self) {
565        self.failures += 1;
566    }
567    /// Record an ambiguous resolution.
568    pub fn record_ambiguous(&mut self) {
569        self.ambiguous += 1;
570    }
571    /// Return the total number of resolutions attempted.
572    pub fn total(&self) -> usize {
573        self.successes + self.failures + self.ambiguous
574    }
575    /// Return the success rate.
576    pub fn success_rate(&self) -> f64 {
577        if self.total() == 0 {
578            0.0
579        } else {
580            self.successes as f64 / self.total() as f64
581        }
582    }
583    /// Return the average depth of successful resolutions.
584    pub fn average_depth(&self) -> f64 {
585        if self.successes == 0 {
586            0.0
587        } else {
588            self.total_depth as f64 / self.successes as f64
589        }
590    }
591}
592/// A priority queue of instance candidates, sorted by priority (ascending = higher priority).
593#[derive(Debug, Clone, Default)]
594pub struct InstancePriorityQueue {
595    /// Candidates, sorted by priority.
596    candidates: Vec<TypeclassInstance>,
597}
598impl InstancePriorityQueue {
599    /// Create an empty priority queue.
600    pub fn new() -> Self {
601        Self::default()
602    }
603    /// Insert an instance, maintaining sorted order.
604    pub fn push(&mut self, inst: TypeclassInstance) {
605        let pos = self
606            .candidates
607            .partition_point(|c| c.priority() <= inst.priority());
608        self.candidates.insert(pos, inst);
609    }
610    /// Remove and return the highest-priority candidate (lowest priority number).
611    pub fn pop_best(&mut self) -> Option<TypeclassInstance> {
612        if self.candidates.is_empty() {
613            None
614        } else {
615            Some(self.candidates.remove(0))
616        }
617    }
618    /// Peek at the highest-priority candidate.
619    pub fn peek_best(&self) -> Option<&TypeclassInstance> {
620        self.candidates.first()
621    }
622    /// Return all candidates with the same best (lowest) priority.
623    pub fn best_candidates(&self) -> Vec<&TypeclassInstance> {
624        if self.candidates.is_empty() {
625            return Vec::new();
626        }
627        let best_priority = self.candidates[0].priority();
628        self.candidates
629            .iter()
630            .take_while(|c| c.priority() == best_priority)
631            .collect()
632    }
633    /// Return the number of candidates.
634    pub fn len(&self) -> usize {
635        self.candidates.len()
636    }
637    /// Return true if the queue is empty.
638    pub fn is_empty(&self) -> bool {
639        self.candidates.is_empty()
640    }
641    /// Clear all candidates.
642    pub fn clear(&mut self) {
643        self.candidates.clear();
644    }
645    /// Return all candidates (in priority order).
646    pub fn all_candidates(&self) -> &[TypeclassInstance] {
647        &self.candidates
648    }
649}
650/// A scope that can hold local instances (which shadow global ones).
651#[derive(Debug, Default)]
652pub struct InstanceScope {
653    /// Local instances in this scope.
654    local_instances: Vec<TypeclassInstance>,
655    /// Whether this scope is active.
656    active: bool,
657}
658impl InstanceScope {
659    /// Create a new scope.
660    pub fn new() -> Self {
661        Self {
662            local_instances: Vec::new(),
663            active: true,
664        }
665    }
666    /// Add a local instance.
667    pub fn add(&mut self, inst: TypeclassInstance) {
668        self.local_instances.push(inst);
669    }
670    /// Return local instances for a given class.
671    pub fn instances_for_class(&self, class: &Name) -> Vec<&TypeclassInstance> {
672        if !self.active {
673            return Vec::new();
674        }
675        self.local_instances
676            .iter()
677            .filter(|i| i.class() == class)
678            .collect()
679    }
680    /// Return the number of local instances.
681    pub fn len(&self) -> usize {
682        self.local_instances.len()
683    }
684    /// Return true if the scope is empty.
685    pub fn is_empty(&self) -> bool {
686        self.local_instances.is_empty()
687    }
688    /// Deactivate this scope.
689    pub fn deactivate(&mut self) {
690        self.active = false;
691    }
692    /// Return true if this scope is active.
693    pub fn is_active(&self) -> bool {
694        self.active
695    }
696}
697/// An instance of a type class.
698#[derive(Debug, Clone)]
699pub struct InstanceDecl {
700    /// Instance name
701    pub name: Name,
702    /// Type class
703    pub class: Name,
704    /// Type being instantiated
705    pub ty: Expr,
706    /// Instance priority (lower = higher priority)
707    pub priority: u32,
708}
709/// The result of instance synthesis.
710#[derive(Debug, Clone)]
711pub enum SynthResult {
712    /// Successfully synthesized an instance.
713    Success(InstanceChain),
714    /// Synthesis failed with an error.
715    Failure(InstanceError),
716    /// Synthesis is ambiguous (multiple chains found).
717    Ambiguous(Vec<InstanceChain>),
718}
719impl SynthResult {
720    /// Return true if synthesis succeeded.
721    pub fn is_success(&self) -> bool {
722        matches!(self, SynthResult::Success(_))
723    }
724    /// Return true if synthesis failed.
725    pub fn is_failure(&self) -> bool {
726        matches!(self, SynthResult::Failure(_))
727    }
728    /// Return true if synthesis is ambiguous.
729    pub fn is_ambiguous(&self) -> bool {
730        matches!(self, SynthResult::Ambiguous(_))
731    }
732    /// Extract the chain from a successful result.
733    pub fn chain(&self) -> Option<&InstanceChain> {
734        match self {
735            SynthResult::Success(chain) => Some(chain),
736            _ => None,
737        }
738    }
739}
740/// A directed graph of instance relationships.
741///
742/// Nodes are instances; edges represent "X depends on Y" (X needs Y as a sub-instance).
743#[derive(Debug, Default)]
744pub struct InstanceGraph {
745    edges: HashMap<Name, Vec<Name>>,
746}
747impl InstanceGraph {
748    /// Create an empty instance graph.
749    pub fn new() -> Self {
750        Self::default()
751    }
752    /// Add an edge: `from` depends on `to`.
753    pub fn add_edge(&mut self, from: Name, to: Name) {
754        self.edges.entry(from).or_default().push(to);
755    }
756    /// Return the dependencies of the given instance.
757    pub fn dependencies(&self, name: &Name) -> &[Name] {
758        self.edges.get(name).map(|v| v.as_slice()).unwrap_or(&[])
759    }
760    /// Return true if there is an edge from `from` to `to`.
761    pub fn has_edge(&self, from: &Name, to: &Name) -> bool {
762        self.edges
763            .get(from)
764            .map(|deps| deps.contains(to))
765            .unwrap_or(false)
766    }
767    /// Return all nodes in the graph.
768    pub fn nodes(&self) -> Vec<&Name> {
769        self.edges.keys().collect()
770    }
771    /// Detect cycles using DFS. Returns true if a cycle is found.
772    pub fn has_cycle(&self) -> bool {
773        let mut visited = std::collections::HashSet::new();
774        let mut rec_stack = std::collections::HashSet::new();
775        for node in self.edges.keys() {
776            if self.dfs_cycle_check(node, &mut visited, &mut rec_stack) {
777                return true;
778            }
779        }
780        false
781    }
782    fn dfs_cycle_check(
783        &self,
784        node: &Name,
785        visited: &mut std::collections::HashSet<Name>,
786        rec_stack: &mut std::collections::HashSet<Name>,
787    ) -> bool {
788        if rec_stack.contains(node) {
789            return true;
790        }
791        if visited.contains(node) {
792            return false;
793        }
794        visited.insert(node.clone());
795        rec_stack.insert(node.clone());
796        for dep in self.dependencies(node) {
797            if self.dfs_cycle_check(dep, visited, rec_stack) {
798                return true;
799            }
800        }
801        rec_stack.remove(node);
802        false
803    }
804}
805/// A summary report of an instance resolution attempt.
806#[derive(Debug, Clone)]
807pub struct InstanceReport {
808    /// The class that was resolved.
809    pub class: Name,
810    /// The chain found (or None if resolution failed).
811    pub chain: Option<InstanceChain>,
812    /// The error (if resolution failed).
813    pub error: Option<InstanceError>,
814    /// The number of instances explored.
815    pub instances_explored: usize,
816}
817impl InstanceReport {
818    /// Create a successful report.
819    pub fn success(class: Name, chain: InstanceChain, explored: usize) -> Self {
820        Self {
821            class,
822            chain: Some(chain),
823            error: None,
824            instances_explored: explored,
825        }
826    }
827    /// Create a failure report.
828    pub fn failure(class: Name, error: InstanceError, explored: usize) -> Self {
829        Self {
830            class,
831            chain: None,
832            error: Some(error),
833            instances_explored: explored,
834        }
835    }
836    /// Return true if the resolution succeeded.
837    pub fn is_success(&self) -> bool {
838        self.chain.is_some()
839    }
840}
841/// Cache of previously resolved instances.
842#[derive(Debug, Default)]
843pub struct InstanceCache {
844    entries: HashMap<CacheKey, Name>,
845    hits: u64,
846    misses: u64,
847}
848impl InstanceCache {
849    /// Create an empty cache.
850    pub fn new() -> Self {
851        Self::default()
852    }
853    /// Insert a successful resolution into the cache.
854    pub fn insert(&mut self, key: CacheKey, instance_name: Name) {
855        self.entries.insert(key, instance_name);
856    }
857    /// Look up a cached resolution.
858    pub fn get(&mut self, key: &CacheKey) -> Option<&Name> {
859        if let Some(v) = self.entries.get(key) {
860            self.hits += 1;
861            Some(v)
862        } else {
863            self.misses += 1;
864            None
865        }
866    }
867    /// Return the number of cache hits.
868    pub fn hits(&self) -> u64 {
869        self.hits
870    }
871    /// Return the number of cache misses.
872    pub fn misses(&self) -> u64 {
873        self.misses
874    }
875    /// Return total queries served.
876    pub fn total_queries(&self) -> u64 {
877        self.hits + self.misses
878    }
879    /// Clear all cached entries.
880    pub fn clear(&mut self) {
881        self.entries.clear();
882    }
883    /// Return the number of cached entries.
884    pub fn len(&self) -> usize {
885        self.entries.len()
886    }
887    /// Return whether the cache is empty.
888    pub fn is_empty(&self) -> bool {
889        self.entries.is_empty()
890    }
891}
892/// A chain of instance applications that resolves a typeclass constraint.
893#[derive(Debug, Clone)]
894pub struct InstanceChain {
895    /// The steps in the chain (instance names, from root to leaf).
896    pub steps: Vec<Name>,
897    /// The total cost of this chain (sum of priorities).
898    pub total_cost: u32,
899    /// Whether this chain involves any default instances.
900    pub has_default: bool,
901}
902impl InstanceChain {
903    /// Create an empty chain.
904    pub fn empty() -> Self {
905        Self {
906            steps: Vec::new(),
907            total_cost: 0,
908            has_default: false,
909        }
910    }
911    /// Create a single-step chain.
912    pub fn single(name: Name, cost: u32) -> Self {
913        Self {
914            steps: vec![name],
915            total_cost: cost,
916            has_default: false,
917        }
918    }
919    /// Extend the chain with a new step.
920    pub fn extend(&self, name: Name, cost: u32, is_default: bool) -> Self {
921        let mut new_chain = self.clone();
922        new_chain.steps.push(name);
923        new_chain.total_cost += cost;
924        if is_default {
925            new_chain.has_default = true;
926        }
927        new_chain
928    }
929    /// Return the number of steps.
930    pub fn len(&self) -> usize {
931        self.steps.len()
932    }
933    /// Return true if this chain has no steps.
934    pub fn is_empty(&self) -> bool {
935        self.steps.is_empty()
936    }
937    /// Return the last step in the chain (the leaf instance), if any.
938    pub fn leaf(&self) -> Option<&Name> {
939        self.steps.last()
940    }
941}
942/// An `InstanceResolver` that also records search statistics.
943pub struct TracedResolver {
944    /// Underlying resolver.
945    pub resolver: InstanceResolver,
946    /// Collected statistics.
947    pub stats: SearchStats,
948}
949impl TracedResolver {
950    /// Create a new traced resolver.
951    pub fn new() -> Self {
952        Self {
953            resolver: InstanceResolver::new(),
954            stats: SearchStats::new(),
955        }
956    }
957    /// Register an instance in the underlying resolver.
958    pub fn register(&mut self, inst: InstanceDecl) {
959        self.resolver.register(inst);
960    }
961    /// Resolve and record statistics.
962    pub fn resolve(&mut self, class: &Name, ty: &Expr) -> ResolutionResult {
963        let n_before = self.resolver.get_instances(class).len() as u64;
964        let result = self.resolver.resolve(class, ty);
965        match &result {
966            ResolutionResult::Found(_) => self.stats.record_success(n_before),
967            ResolutionResult::NotFound => self.stats.record_failure(),
968            ResolutionResult::Ambiguous(_) => self.stats.record_ambiguous(),
969            ResolutionResult::DepthExceeded => self.stats.record_depth_exceeded(),
970        }
971        result
972    }
973}
974/// Mutable state accumulated during instance search.
975#[derive(Debug, Default)]
976pub struct InstanceSearchState {
977    /// Instances that have already been tried (to avoid revisiting).
978    pub tried: std::collections::HashSet<Name>,
979    /// The current depth of the search.
980    pub depth: usize,
981    /// The maximum depth reached so far.
982    pub max_depth_reached: usize,
983    /// Total number of nodes explored.
984    pub nodes_explored: usize,
985}
986impl InstanceSearchState {
987    /// Create a new search state.
988    pub fn new() -> Self {
989        Self::default()
990    }
991    /// Mark an instance as tried.
992    pub fn mark_tried(&mut self, name: Name) {
993        self.tried.insert(name);
994    }
995    /// Return true if the instance has been tried.
996    pub fn already_tried(&self, name: &Name) -> bool {
997        self.tried.contains(name)
998    }
999    /// Enter a new search depth.
1000    pub fn enter_depth(&mut self) {
1001        self.depth += 1;
1002        if self.depth > self.max_depth_reached {
1003            self.max_depth_reached = self.depth;
1004        }
1005        self.nodes_explored += 1;
1006    }
1007    /// Exit a search depth.
1008    pub fn exit_depth(&mut self) {
1009        if self.depth > 0 {
1010            self.depth -= 1;
1011        }
1012    }
1013    /// Return true if the current depth exceeds the given limit.
1014    pub fn depth_exceeded(&self, limit: usize) -> bool {
1015        self.depth > limit
1016    }
1017}
1018/// Diagnostic statistics collected during instance resolution.
1019#[derive(Debug, Default, Clone)]
1020pub struct SearchStats {
1021    /// Number of successful resolutions.
1022    pub successes: u64,
1023    /// Number of failed resolutions (not-found).
1024    pub failures: u64,
1025    /// Number of ambiguous resolutions.
1026    pub ambiguous: u64,
1027    /// Number of times the depth limit was reached.
1028    pub depth_exceeded: u64,
1029    /// Total instances inspected across all queries.
1030    pub instances_inspected: u64,
1031}
1032impl SearchStats {
1033    /// Create zeroed statistics.
1034    pub fn new() -> Self {
1035        Self::default()
1036    }
1037    /// Record a successful resolution that inspected `n` candidates.
1038    pub fn record_success(&mut self, candidates_inspected: u64) {
1039        self.successes += 1;
1040        self.instances_inspected += candidates_inspected;
1041    }
1042    /// Record a failed resolution.
1043    pub fn record_failure(&mut self) {
1044        self.failures += 1;
1045    }
1046    /// Record an ambiguous resolution.
1047    pub fn record_ambiguous(&mut self) {
1048        self.ambiguous += 1;
1049    }
1050    /// Record that the depth limit was exceeded.
1051    pub fn record_depth_exceeded(&mut self) {
1052        self.depth_exceeded += 1;
1053    }
1054    /// Total number of queries processed.
1055    pub fn total_queries(&self) -> u64 {
1056        self.successes + self.failures + self.ambiguous + self.depth_exceeded
1057    }
1058    /// Success rate as a value in `[0.0, 1.0]`.
1059    pub fn success_rate(&self) -> f64 {
1060        let total = self.total_queries();
1061        if total == 0 {
1062            1.0
1063        } else {
1064            self.successes as f64 / total as f64
1065        }
1066    }
1067}
1068/// A scoped stack of locally visible instances.
1069///
1070/// Used inside `do`-notation, tactic blocks, and `haveI`/`letI` binders
1071/// where an instance is in scope only for the duration of a sub-expression.
1072#[derive(Debug, Default)]
1073pub struct LocalInstanceScope {
1074    stack: Vec<Vec<InstanceDecl>>,
1075}
1076impl LocalInstanceScope {
1077    /// Create a new empty scope.
1078    pub fn new() -> Self {
1079        Self::default()
1080    }
1081    /// Push a new layer onto the scope stack.
1082    pub fn push_layer(&mut self) {
1083        self.stack.push(Vec::new());
1084    }
1085    /// Pop the top layer from the scope stack.
1086    ///
1087    /// Returns the instances that were in the top layer.
1088    pub fn pop_layer(&mut self) -> Vec<InstanceDecl> {
1089        self.stack.pop().unwrap_or_default()
1090    }
1091    /// Add an instance to the current (top) layer.
1092    ///
1093    /// Returns `false` if no layer has been pushed yet.
1094    pub fn add_to_current(&mut self, inst: InstanceDecl) -> bool {
1095        if let Some(top) = self.stack.last_mut() {
1096            top.push(inst);
1097            true
1098        } else {
1099            false
1100        }
1101    }
1102    /// Collect all instances visible in the current scope (all layers).
1103    pub fn visible_instances(&self) -> Vec<&InstanceDecl> {
1104        self.stack.iter().flatten().collect()
1105    }
1106    /// Return the current depth of the scope stack.
1107    pub fn depth(&self) -> usize {
1108        self.stack.len()
1109    }
1110    /// Check whether the scope is empty (no layers).
1111    pub fn is_empty(&self) -> bool {
1112        self.stack.is_empty()
1113    }
1114}
1115/// An orchestrator for instance synthesis.
1116#[derive(Default)]
1117pub struct InstanceSynthesizer {
1118    config: SynthConfig,
1119    graph: InstanceGraph,
1120}
1121impl InstanceSynthesizer {
1122    /// Create a new synthesizer with default config.
1123    pub fn new() -> Self {
1124        Self::default()
1125    }
1126    /// Create a synthesizer with custom config.
1127    pub fn with_config(config: SynthConfig) -> Self {
1128        Self {
1129            config,
1130            graph: InstanceGraph::new(),
1131        }
1132    }
1133    /// Return the current configuration.
1134    pub fn config(&self) -> &SynthConfig {
1135        &self.config
1136    }
1137    /// Return the instance dependency graph.
1138    pub fn graph(&self) -> &InstanceGraph {
1139        &self.graph
1140    }
1141    /// Add an instance edge to the dependency graph.
1142    pub fn add_dependency(&mut self, from: Name, to: Name) {
1143        self.graph.add_edge(from, to);
1144    }
1145    /// Check if there are any circular dependencies.
1146    pub fn has_circular_deps(&self) -> bool {
1147        self.graph.has_cycle()
1148    }
1149}
1150/// A key used for instance resolution caching.
1151///
1152/// Caches successful resolutions to avoid redundant search.
1153#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1154pub struct CacheKey {
1155    /// The type class name.
1156    pub class: Name,
1157    /// A stable string representation of the type being resolved.
1158    pub ty_repr: String,
1159}
1160impl CacheKey {
1161    /// Build a cache key from a class name and expression.
1162    pub fn new(class: &Name, ty: &Expr) -> Self {
1163        Self {
1164            class: class.clone(),
1165            ty_repr: format!("{:?}", ty),
1166        }
1167    }
1168}
1169/// Priority group: a collection of instances sharing the same numeric priority.
1170#[derive(Clone, Debug)]
1171pub struct PriorityGroup {
1172    /// Shared priority value.
1173    pub priority: u32,
1174    /// Members.
1175    pub instances: Vec<InstanceDecl>,
1176}
1177impl PriorityGroup {
1178    /// Create a new group.
1179    pub fn new(priority: u32) -> Self {
1180        Self {
1181            priority,
1182            instances: Vec::new(),
1183        }
1184    }
1185    /// Add an instance to the group.
1186    pub fn add(&mut self, inst: InstanceDecl) {
1187        self.instances.push(inst);
1188    }
1189    /// Number of instances in this group.
1190    pub fn len(&self) -> usize {
1191        self.instances.len()
1192    }
1193    /// Check if the group is empty.
1194    pub fn is_empty(&self) -> bool {
1195        self.instances.is_empty()
1196    }
1197    /// Check if the group is unambiguous (exactly one instance).
1198    pub fn is_unambiguous(&self) -> bool {
1199        self.instances.len() == 1
1200    }
1201}
1202/// A typed instance search path — the sequence of class/type pairs explored
1203/// during a single resolution attempt.
1204#[derive(Clone, Debug, Default)]
1205pub struct SearchPath {
1206    /// Steps in the search, in order.
1207    steps: Vec<(Name, Name)>,
1208}
1209impl SearchPath {
1210    /// Create an empty path.
1211    pub fn new() -> Self {
1212        Self::default()
1213    }
1214    /// Push a step (class, instance_name).
1215    pub fn push(&mut self, class: Name, instance: Name) {
1216        self.steps.push((class, instance));
1217    }
1218    /// Number of steps.
1219    pub fn len(&self) -> usize {
1220        self.steps.len()
1221    }
1222    /// Check if empty.
1223    pub fn is_empty(&self) -> bool {
1224        self.steps.is_empty()
1225    }
1226    /// Get step at index `i`.
1227    pub fn get(&self, i: usize) -> Option<&(Name, Name)> {
1228        self.steps.get(i)
1229    }
1230    /// Check if the path contains a given class (cycle detection).
1231    pub fn has_class(&self, class: &Name) -> bool {
1232        self.steps.iter().any(|(c, _)| c == class)
1233    }
1234    /// Clear all steps.
1235    pub fn clear(&mut self) {
1236        self.steps.clear();
1237    }
1238}