diff options
Diffstat (limited to 'crates/hir_ty/src/tls.rs')
-rw-r--r-- | crates/hir_ty/src/tls.rs | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/crates/hir_ty/src/tls.rs b/crates/hir_ty/src/tls.rs new file mode 100644 index 000000000..87c671a42 --- /dev/null +++ b/crates/hir_ty/src/tls.rs | |||
@@ -0,0 +1,277 @@ | |||
1 | //! Implementation of Chalk debug helper functions using TLS. | ||
2 | use std::fmt; | ||
3 | |||
4 | use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication}; | ||
5 | use itertools::Itertools; | ||
6 | |||
7 | use crate::{ | ||
8 | chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk, | ||
9 | CallableDefId, Interner, | ||
10 | }; | ||
11 | use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId}; | ||
12 | |||
13 | pub(crate) use unsafe_tls::{set_current_program, with_current_program}; | ||
14 | |||
15 | pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase); | ||
16 | |||
17 | impl DebugContext<'_> { | ||
18 | pub(crate) fn debug_struct_id( | ||
19 | &self, | ||
20 | id: chalk_db::AdtId, | ||
21 | f: &mut fmt::Formatter<'_>, | ||
22 | ) -> Result<(), fmt::Error> { | ||
23 | let name = match id.0 { | ||
24 | AdtId::StructId(it) => self.0.struct_data(it).name.clone(), | ||
25 | AdtId::UnionId(it) => self.0.union_data(it).name.clone(), | ||
26 | AdtId::EnumId(it) => self.0.enum_data(it).name.clone(), | ||
27 | }; | ||
28 | write!(f, "{}", name) | ||
29 | } | ||
30 | |||
31 | pub(crate) fn debug_trait_id( | ||
32 | &self, | ||
33 | id: chalk_db::TraitId, | ||
34 | fmt: &mut fmt::Formatter<'_>, | ||
35 | ) -> Result<(), fmt::Error> { | ||
36 | let trait_: hir_def::TraitId = from_chalk_trait_id(id); | ||
37 | let trait_data = self.0.trait_data(trait_); | ||
38 | write!(fmt, "{}", trait_data.name) | ||
39 | } | ||
40 | |||
41 | pub(crate) fn debug_assoc_type_id( | ||
42 | &self, | ||
43 | id: chalk_db::AssocTypeId, | ||
44 | fmt: &mut fmt::Formatter<'_>, | ||
45 | ) -> Result<(), fmt::Error> { | ||
46 | let type_alias: TypeAliasId = from_assoc_type_id(id); | ||
47 | let type_alias_data = self.0.type_alias_data(type_alias); | ||
48 | let trait_ = match type_alias.lookup(self.0.upcast()).container { | ||
49 | AssocContainerId::TraitId(t) => t, | ||
50 | _ => panic!("associated type not in trait"), | ||
51 | }; | ||
52 | let trait_data = self.0.trait_data(trait_); | ||
53 | write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) | ||
54 | } | ||
55 | |||
56 | pub(crate) fn debug_opaque_ty_id( | ||
57 | &self, | ||
58 | opaque_ty_id: chalk_ir::OpaqueTyId<Interner>, | ||
59 | fmt: &mut fmt::Formatter<'_>, | ||
60 | ) -> Result<(), fmt::Error> { | ||
61 | fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish() | ||
62 | } | ||
63 | |||
64 | pub(crate) fn debug_alias( | ||
65 | &self, | ||
66 | alias_ty: &AliasTy<Interner>, | ||
67 | fmt: &mut fmt::Formatter<'_>, | ||
68 | ) -> Result<(), fmt::Error> { | ||
69 | match alias_ty { | ||
70 | AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt), | ||
71 | AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt), | ||
72 | } | ||
73 | } | ||
74 | |||
75 | pub(crate) fn debug_projection_ty( | ||
76 | &self, | ||
77 | projection_ty: &chalk_ir::ProjectionTy<Interner>, | ||
78 | fmt: &mut fmt::Formatter<'_>, | ||
79 | ) -> Result<(), fmt::Error> { | ||
80 | let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); | ||
81 | let type_alias_data = self.0.type_alias_data(type_alias); | ||
82 | let trait_ = match type_alias.lookup(self.0.upcast()).container { | ||
83 | AssocContainerId::TraitId(t) => t, | ||
84 | _ => panic!("associated type not in trait"), | ||
85 | }; | ||
86 | let trait_data = self.0.trait_data(trait_); | ||
87 | let params = projection_ty.substitution.as_slice(&Interner); | ||
88 | write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?; | ||
89 | if params.len() > 1 { | ||
90 | write!( | ||
91 | fmt, | ||
92 | "<{}>", | ||
93 | ¶ms[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), | ||
94 | )?; | ||
95 | } | ||
96 | write!(fmt, ">::{}", type_alias_data.name) | ||
97 | } | ||
98 | |||
99 | pub(crate) fn debug_opaque_ty( | ||
100 | &self, | ||
101 | opaque_ty: &chalk_ir::OpaqueTy<Interner>, | ||
102 | fmt: &mut fmt::Formatter<'_>, | ||
103 | ) -> Result<(), fmt::Error> { | ||
104 | write!(fmt, "{:?}", opaque_ty.opaque_ty_id) | ||
105 | } | ||
106 | |||
107 | pub(crate) fn debug_ty( | ||
108 | &self, | ||
109 | ty: &chalk_ir::Ty<Interner>, | ||
110 | fmt: &mut fmt::Formatter<'_>, | ||
111 | ) -> Result<(), fmt::Error> { | ||
112 | write!(fmt, "{:?}", ty.data(&Interner)) | ||
113 | } | ||
114 | |||
115 | pub(crate) fn debug_lifetime( | ||
116 | &self, | ||
117 | lifetime: &Lifetime<Interner>, | ||
118 | fmt: &mut fmt::Formatter<'_>, | ||
119 | ) -> Result<(), fmt::Error> { | ||
120 | write!(fmt, "{:?}", lifetime.data(&Interner)) | ||
121 | } | ||
122 | |||
123 | pub(crate) fn debug_generic_arg( | ||
124 | &self, | ||
125 | parameter: &GenericArg<Interner>, | ||
126 | fmt: &mut fmt::Formatter<'_>, | ||
127 | ) -> Result<(), fmt::Error> { | ||
128 | write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()) | ||
129 | } | ||
130 | |||
131 | pub(crate) fn debug_goal( | ||
132 | &self, | ||
133 | goal: &Goal<Interner>, | ||
134 | fmt: &mut fmt::Formatter<'_>, | ||
135 | ) -> Result<(), fmt::Error> { | ||
136 | let goal_data = goal.data(&Interner); | ||
137 | write!(fmt, "{:?}", goal_data) | ||
138 | } | ||
139 | |||
140 | pub(crate) fn debug_goals( | ||
141 | &self, | ||
142 | goals: &Goals<Interner>, | ||
143 | fmt: &mut fmt::Formatter<'_>, | ||
144 | ) -> Result<(), fmt::Error> { | ||
145 | write!(fmt, "{:?}", goals.debug(&Interner)) | ||
146 | } | ||
147 | |||
148 | pub(crate) fn debug_program_clause_implication( | ||
149 | &self, | ||
150 | pci: &ProgramClauseImplication<Interner>, | ||
151 | fmt: &mut fmt::Formatter<'_>, | ||
152 | ) -> Result<(), fmt::Error> { | ||
153 | write!(fmt, "{:?}", pci.debug(&Interner)) | ||
154 | } | ||
155 | |||
156 | pub(crate) fn debug_substitution( | ||
157 | &self, | ||
158 | substitution: &chalk_ir::Substitution<Interner>, | ||
159 | fmt: &mut fmt::Formatter<'_>, | ||
160 | ) -> Result<(), fmt::Error> { | ||
161 | write!(fmt, "{:?}", substitution.debug(&Interner)) | ||
162 | } | ||
163 | |||
164 | pub(crate) fn debug_separator_trait_ref( | ||
165 | &self, | ||
166 | separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>, | ||
167 | fmt: &mut fmt::Formatter<'_>, | ||
168 | ) -> Result<(), fmt::Error> { | ||
169 | write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)) | ||
170 | } | ||
171 | |||
172 | pub(crate) fn debug_fn_def_id( | ||
173 | &self, | ||
174 | fn_def_id: chalk_ir::FnDefId<Interner>, | ||
175 | fmt: &mut fmt::Formatter<'_>, | ||
176 | ) -> Result<(), fmt::Error> { | ||
177 | let def: CallableDefId = from_chalk(self.0, fn_def_id); | ||
178 | let name = match def { | ||
179 | CallableDefId::FunctionId(ff) => self.0.function_data(ff).name.clone(), | ||
180 | CallableDefId::StructId(s) => self.0.struct_data(s).name.clone(), | ||
181 | CallableDefId::EnumVariantId(e) => { | ||
182 | let enum_data = self.0.enum_data(e.parent); | ||
183 | enum_data.variants[e.local_id].name.clone() | ||
184 | } | ||
185 | }; | ||
186 | match def { | ||
187 | CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name), | ||
188 | CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { | ||
189 | write!(fmt, "{{ctor {}}}", name) | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | pub(crate) fn debug_const( | ||
195 | &self, | ||
196 | _constant: &chalk_ir::Const<Interner>, | ||
197 | fmt: &mut fmt::Formatter<'_>, | ||
198 | ) -> fmt::Result { | ||
199 | write!(fmt, "const") | ||
200 | } | ||
201 | |||
202 | pub(crate) fn debug_variable_kinds( | ||
203 | &self, | ||
204 | variable_kinds: &chalk_ir::VariableKinds<Interner>, | ||
205 | fmt: &mut fmt::Formatter<'_>, | ||
206 | ) -> fmt::Result { | ||
207 | write!(fmt, "{:?}", variable_kinds.as_slice(&Interner)) | ||
208 | } | ||
209 | pub(crate) fn debug_variable_kinds_with_angles( | ||
210 | &self, | ||
211 | variable_kinds: &chalk_ir::VariableKinds<Interner>, | ||
212 | fmt: &mut fmt::Formatter<'_>, | ||
213 | ) -> fmt::Result { | ||
214 | write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner)) | ||
215 | } | ||
216 | pub(crate) fn debug_canonical_var_kinds( | ||
217 | &self, | ||
218 | canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Interner>, | ||
219 | fmt: &mut fmt::Formatter<'_>, | ||
220 | ) -> fmt::Result { | ||
221 | write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner)) | ||
222 | } | ||
223 | pub(crate) fn debug_program_clause( | ||
224 | &self, | ||
225 | clause: &chalk_ir::ProgramClause<Interner>, | ||
226 | fmt: &mut fmt::Formatter<'_>, | ||
227 | ) -> fmt::Result { | ||
228 | write!(fmt, "{:?}", clause.data(&Interner)) | ||
229 | } | ||
230 | pub(crate) fn debug_program_clauses( | ||
231 | &self, | ||
232 | clauses: &chalk_ir::ProgramClauses<Interner>, | ||
233 | fmt: &mut fmt::Formatter<'_>, | ||
234 | ) -> fmt::Result { | ||
235 | write!(fmt, "{:?}", clauses.as_slice(&Interner)) | ||
236 | } | ||
237 | pub(crate) fn debug_quantified_where_clauses( | ||
238 | &self, | ||
239 | clauses: &chalk_ir::QuantifiedWhereClauses<Interner>, | ||
240 | fmt: &mut fmt::Formatter<'_>, | ||
241 | ) -> fmt::Result { | ||
242 | write!(fmt, "{:?}", clauses.as_slice(&Interner)) | ||
243 | } | ||
244 | } | ||
245 | |||
246 | mod unsafe_tls { | ||
247 | use super::DebugContext; | ||
248 | use crate::db::HirDatabase; | ||
249 | use scoped_tls::scoped_thread_local; | ||
250 | |||
251 | scoped_thread_local!(static PROGRAM: DebugContext); | ||
252 | |||
253 | pub(crate) fn with_current_program<R>( | ||
254 | op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R, | ||
255 | ) -> R { | ||
256 | if PROGRAM.is_set() { | ||
257 | PROGRAM.with(|prog| op(Some(prog))) | ||
258 | } else { | ||
259 | op(None) | ||
260 | } | ||
261 | } | ||
262 | |||
263 | pub(crate) fn set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R | ||
264 | where | ||
265 | OP: FnOnce() -> R, | ||
266 | { | ||
267 | let ctx = DebugContext(p); | ||
268 | // we're transmuting the lifetime in the DebugContext to static. This is | ||
269 | // fine because we only keep the reference for the lifetime of this | ||
270 | // function, *and* the only way to access the context is through | ||
271 | // `with_current_program`, which hides the lifetime through the `for` | ||
272 | // type. | ||
273 | let static_p: &DebugContext<'static> = | ||
274 | unsafe { std::mem::transmute::<&DebugContext, &DebugContext<'static>>(&ctx) }; | ||
275 | PROGRAM.set(static_p, || op()) | ||
276 | } | ||
277 | } | ||