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]
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 Enter(String),
49 Exit,
51 TryingUnify(TypeVarString, TypeVarString),
52 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 InferringFromConstraints(TypeVarString, KnownType),
68 AddingPipelineLabel(NameID, TypeVarString),
69 RecoveringPipelineLabel(NameID, TypeVarString),
70 PreAddingPipelineLabel(NameID, TypeVarString),
71 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}