spade_typeinference/
trace_stack.rs

1use std::{cell::RefCell, collections::HashMap};
2
3use colored::*;
4use itertools::Itertools;
5use spade_common::name::NameID;
6use spade_types::KnownType;
7
8use crate::{
9    constraints::ConstraintRhs,
10    equation::{TypeVarString, TypedExpression},
11    requirements::Requirement,
12    traits::TraitList,
13    TypeState,
14};
15
16#[derive(Clone)]
17pub struct TraceStack {
18    entries: RefCell<Vec<TraceStackEntry>>,
19}
20
21impl Default for TraceStack {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl TraceStack {
28    pub fn new() -> Self {
29        Self {
30            entries: RefCell::new(vec![]),
31        }
32    }
33
34    // Inline because we don't want the compiler to construct the entries if they are
35    // not going to be used
36    #[inline]
37    pub fn push(&self, entry: TraceStackEntry) {
38        if std::env::var("SPADE_TRACE_TYPEINFERENCE").is_ok() {
39            self.entries.borrow_mut().push(entry)
40        }
41    }
42}
43
44#[derive(Clone)]
45pub enum TraceStackEntry {
46    /// Entering the specified visitor
47    Enter(String),
48    /// Exited the most recent visitor and the node had the specified type
49    Exit,
50    TryingUnify(TypeVarString, TypeVarString),
51    /// Unified .0 with .1 producing .2. .3 were replaced
52    Unified(
53        TypeVarString,
54        TypeVarString,
55        TypeVarString,
56        Vec<TypeVarString>,
57    ),
58    EnsuringImpls(TypeVarString, TraitList, bool),
59    AddingEquation(TypedExpression, TypeVarString),
60    AddingTraitBounds(TypeVarString, TraitList),
61    AddRequirement(Requirement),
62    ResolvedRequirement(Requirement),
63    NewGenericList(HashMap<NameID, TypeVarString>),
64    AddingConstraint(TypeVarString, ConstraintRhs),
65    /// Inferring more from constraints
66    InferringFromConstraints(TypeVarString, KnownType),
67    AddingPipelineLabel(NameID, TypeVarString),
68    RecoveringPipelineLabel(NameID, TypeVarString),
69    PreAddingPipelineLabel(NameID, TypeVarString),
70    /// Arbitrary message
71    Message(String),
72}
73
74pub fn format_trace_stack(type_state: &TypeState) -> String {
75    let stack = &type_state.trace_stack;
76    let mut result = String::new();
77    let mut indent_amount = 0;
78
79    for entry in stack.entries.borrow().iter() {
80        let mut next_indent_amount = indent_amount;
81        let message = match entry {
82            TraceStackEntry::Enter(function) => {
83                next_indent_amount += 1;
84                format!("{} `{}`", "call".white(), function.blue())
85            }
86            TraceStackEntry::AddingEquation(expr, t) => {
87                format!("{} {:?} <- {}", "eq".yellow(), expr, t)
88            }
89            TraceStackEntry::Unified(lhs, rhs, result, replaced) => {
90                next_indent_amount -= 1;
91                format!(
92                    "{} {}, {} -> {} (replacing {}) {}",
93                    "unified".green(),
94                    lhs,
95                    rhs,
96                    result,
97                    replaced.iter().join(","),
98                    format!(
99                        "{}, {} -> {} (replacing {})",
100                        lhs.1.inner,
101                        rhs.1.inner,
102                        result.1.inner,
103                        replaced.iter().map(|r| r.1.inner).join(",")
104                    )
105                    .bright_black()
106                )
107            }
108            TraceStackEntry::TryingUnify(lhs, rhs) => {
109                next_indent_amount += 1;
110                format!("{} of {} with {}", "trying unification".cyan(), lhs, rhs)
111            }
112            TraceStackEntry::EnsuringImpls(ty, tr, trait_is_expected) => {
113                format!(
114                    "{} {ty} as {} (trait_is_expected: {trait_is_expected})",
115                    "ensuring impls".yellow(),
116                    tr.display_with_meta(true, type_state),
117                )
118            }
119            TraceStackEntry::InferringFromConstraints(lhs, rhs) => {
120                format!("{} {lhs} as {rhs:?} from constraints", "inferring".purple(),)
121            }
122            TraceStackEntry::AddingConstraint(lhs, rhs) => {
123                format!(
124                    "adding constraint for {lhs} ({})",
125                    rhs.debug_display(type_state)
126                )
127            }
128            TraceStackEntry::NewGenericList(mapping) => {
129                format!(
130                    "{}: {}",
131                    "new generic list".bright_cyan(),
132                    mapping
133                        .iter()
134                        .map(|(k, v)| format!("{k} -> {v}"))
135                        .join(", ")
136                )
137            }
138            TraceStackEntry::AddingPipelineLabel(name, var) => {
139                format!("{} {name:?} as {var}", "declaring label".bright_black())
140            }
141            TraceStackEntry::PreAddingPipelineLabel(name, var) => {
142                format!("{} {name:?} as {var}", "pre-declaring label".bright_black())
143            }
144            TraceStackEntry::RecoveringPipelineLabel(name, var) => {
145                format!(
146                    "{} {name:?} as {var}",
147                    "using previously declared label".bright_black()
148                )
149            }
150            TraceStackEntry::Message(msg) => {
151                format!("{}: {}", "m".purple(), msg)
152            }
153            TraceStackEntry::Exit => {
154                next_indent_amount -= 1;
155                String::new()
156            }
157            TraceStackEntry::AddRequirement(req) => format!("{} {req:?}", "added".yellow()),
158            TraceStackEntry::ResolvedRequirement(req) => format!("{} {req:?}", "resolved".blue()),
159            TraceStackEntry::AddingTraitBounds(tvar, traits) => {
160                format!("{} {traits:?} to {tvar}", "adding trait bound".yellow())
161            }
162        };
163        if let TraceStackEntry::Exit = entry {
164        } else {
165            for _ in 0..indent_amount {
166                result += "| ";
167            }
168            result += &message;
169            result += "\n";
170        }
171        indent_amount = next_indent_amount;
172    }
173    result
174}