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