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 Enter(String),
39 Exit,
41 TryingUnify(TypeVar, TypeVar),
42 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 InferringFromConstraints(TypeVar, KnownType),
53 AddingPipelineLabel(NameID, TypeVar),
54 RecoveringPipelineLabel(NameID, TypeVar),
55 PreAddingPipelineLabel(NameID, TypeVar),
56 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 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}