spade_typeinference/
trace_stack.rs

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