spade_typeinference/
trace_stack.rs

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