diff options
author | Florian Diebold <[email protected]> | 2020-03-27 17:56:18 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-04-10 14:04:06 +0100 |
commit | a0a80a41034b1240dc3e8fd794ae1d4f77714d99 (patch) | |
tree | 1daf136828992006de9e21784a89b2d14be61b74 /crates/ra_hir_ty/src/traits/chalk | |
parent | 176f7f61175bc433c56083a758bd7a28a8ae31f8 (diff) |
Implement Chalk's debug methods using TLS
Chalk now panics if we don't implement these methods and run with CHALK_DEBUG,
so I thought I'd try to implement them 'properly'. Sadly, it seems impossible to
do without transmuting lifetimes somewhere. The problem is that we need a `&dyn
HirDatabase` to get names etc., which we can't just put into TLS. I thought I
could just use `scoped-tls`, but that doesn't support references to unsized
types. So I put the `&dyn` into another struct and put the reference to *that*
into the TLS, but I have to transmute the lifetime to 'static for that to work.
Diffstat (limited to 'crates/ra_hir_ty/src/traits/chalk')
-rw-r--r-- | crates/ra_hir_ty/src/traits/chalk/tls.rs | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs new file mode 100644 index 000000000..d9bbb54a5 --- /dev/null +++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs | |||
@@ -0,0 +1,231 @@ | |||
1 | //! Implementation of Chalk debug helper functions using TLS. | ||
2 | use std::fmt; | ||
3 | |||
4 | use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName}; | ||
5 | |||
6 | use super::{from_chalk, Interner}; | ||
7 | use crate::{db::HirDatabase, CallableDef, TypeCtor}; | ||
8 | use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId}; | ||
9 | |||
10 | pub use unsafe_tls::{set_current_program, with_current_program}; | ||
11 | |||
12 | pub struct DebugContext<'a>(&'a (dyn HirDatabase + 'a)); | ||
13 | |||
14 | impl DebugContext<'_> { | ||
15 | pub fn debug_struct_id( | ||
16 | &self, | ||
17 | id: super::StructId, | ||
18 | f: &mut fmt::Formatter<'_>, | ||
19 | ) -> Result<(), fmt::Error> { | ||
20 | let type_ctor: TypeCtor = from_chalk(self.0, TypeName::Struct(id)); | ||
21 | match type_ctor { | ||
22 | TypeCtor::Bool => write!(f, "bool")?, | ||
23 | TypeCtor::Char => write!(f, "char")?, | ||
24 | TypeCtor::Int(t) => write!(f, "{}", t)?, | ||
25 | TypeCtor::Float(t) => write!(f, "{}", t)?, | ||
26 | TypeCtor::Str => write!(f, "str")?, | ||
27 | TypeCtor::Slice => write!(f, "slice")?, | ||
28 | TypeCtor::Array => write!(f, "array")?, | ||
29 | TypeCtor::RawPtr(m) => write!(f, "*{}", m.as_keyword_for_ptr())?, | ||
30 | TypeCtor::Ref(m) => write!(f, "&{}", m.as_keyword_for_ref())?, | ||
31 | TypeCtor::Never => write!(f, "!")?, | ||
32 | TypeCtor::Tuple { .. } => { | ||
33 | write!(f, "()")?; | ||
34 | } | ||
35 | TypeCtor::FnPtr { .. } => { | ||
36 | write!(f, "fn")?; | ||
37 | } | ||
38 | TypeCtor::FnDef(def) => { | ||
39 | let name = match def { | ||
40 | CallableDef::FunctionId(ff) => self.0.function_data(ff).name.clone(), | ||
41 | CallableDef::StructId(s) => self.0.struct_data(s).name.clone(), | ||
42 | CallableDef::EnumVariantId(e) => { | ||
43 | let enum_data = self.0.enum_data(e.parent); | ||
44 | enum_data.variants[e.local_id].name.clone() | ||
45 | } | ||
46 | }; | ||
47 | match def { | ||
48 | CallableDef::FunctionId(_) => write!(f, "{{fn {}}}", name)?, | ||
49 | CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { | ||
50 | write!(f, "{{ctor {}}}", name)? | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | TypeCtor::Adt(def_id) => { | ||
55 | let name = match def_id { | ||
56 | AdtId::StructId(it) => self.0.struct_data(it).name.clone(), | ||
57 | AdtId::UnionId(it) => self.0.union_data(it).name.clone(), | ||
58 | AdtId::EnumId(it) => self.0.enum_data(it).name.clone(), | ||
59 | }; | ||
60 | write!(f, "{}", name)?; | ||
61 | } | ||
62 | TypeCtor::AssociatedType(type_alias) => { | ||
63 | let trait_ = match type_alias.lookup(self.0.upcast()).container { | ||
64 | AssocContainerId::TraitId(it) => it, | ||
65 | _ => panic!("not an associated type"), | ||
66 | }; | ||
67 | let trait_name = self.0.trait_data(trait_).name.clone(); | ||
68 | let name = self.0.type_alias_data(type_alias).name.clone(); | ||
69 | write!(f, "{}::{}", trait_name, name)?; | ||
70 | } | ||
71 | TypeCtor::Closure { def, expr } => { | ||
72 | write!(f, "{{closure {:?} in {:?}}}", expr.into_raw(), def)?; | ||
73 | } | ||
74 | } | ||
75 | Ok(()) | ||
76 | } | ||
77 | |||
78 | pub fn debug_trait_id( | ||
79 | &self, | ||
80 | id: super::TraitId, | ||
81 | fmt: &mut fmt::Formatter<'_>, | ||
82 | ) -> Result<(), fmt::Error> { | ||
83 | let trait_: hir_def::TraitId = from_chalk(self.0, id); | ||
84 | let trait_data = self.0.trait_data(trait_); | ||
85 | write!(fmt, "{}", trait_data.name) | ||
86 | } | ||
87 | |||
88 | pub fn debug_assoc_type_id( | ||
89 | &self, | ||
90 | id: super::AssocTypeId, | ||
91 | fmt: &mut fmt::Formatter<'_>, | ||
92 | ) -> Result<(), fmt::Error> { | ||
93 | let type_alias: TypeAliasId = from_chalk(self.0, id); | ||
94 | let type_alias_data = self.0.type_alias_data(type_alias); | ||
95 | let trait_ = match type_alias.lookup(self.0.upcast()).container { | ||
96 | AssocContainerId::TraitId(t) => t, | ||
97 | _ => panic!("associated type not in trait"), | ||
98 | }; | ||
99 | let trait_data = self.0.trait_data(trait_); | ||
100 | write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) | ||
101 | } | ||
102 | |||
103 | pub fn debug_alias( | ||
104 | &self, | ||
105 | alias: &AliasTy<Interner>, | ||
106 | fmt: &mut fmt::Formatter<'_>, | ||
107 | ) -> Result<(), fmt::Error> { | ||
108 | let type_alias: TypeAliasId = from_chalk(self.0, alias.associated_ty_id); | ||
109 | let type_alias_data = self.0.type_alias_data(type_alias); | ||
110 | let trait_ = match type_alias.lookup(self.0.upcast()).container { | ||
111 | AssocContainerId::TraitId(t) => t, | ||
112 | _ => panic!("associated type not in trait"), | ||
113 | }; | ||
114 | let trait_data = self.0.trait_data(trait_); | ||
115 | let params = alias.substitution.parameters(&Interner); | ||
116 | write!( | ||
117 | fmt, | ||
118 | "<{:?} as {}<{:?}>>::{}", | ||
119 | ¶ms[0], | ||
120 | trait_data.name, | ||
121 | ¶ms[1..], | ||
122 | type_alias_data.name | ||
123 | ) | ||
124 | } | ||
125 | |||
126 | pub fn debug_ty( | ||
127 | &self, | ||
128 | ty: &chalk_ir::Ty<Interner>, | ||
129 | fmt: &mut fmt::Formatter<'_>, | ||
130 | ) -> Result<(), fmt::Error> { | ||
131 | write!(fmt, "{:?}", ty.data(&Interner)) | ||
132 | } | ||
133 | |||
134 | pub fn debug_lifetime( | ||
135 | &self, | ||
136 | lifetime: &Lifetime<Interner>, | ||
137 | fmt: &mut fmt::Formatter<'_>, | ||
138 | ) -> Result<(), fmt::Error> { | ||
139 | write!(fmt, "{:?}", lifetime.data(&Interner)) | ||
140 | } | ||
141 | |||
142 | pub fn debug_parameter( | ||
143 | &self, | ||
144 | parameter: &Parameter<Interner>, | ||
145 | fmt: &mut fmt::Formatter<'_>, | ||
146 | ) -> Result<(), fmt::Error> { | ||
147 | write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()) | ||
148 | } | ||
149 | |||
150 | pub fn debug_goal( | ||
151 | &self, | ||
152 | goal: &Goal<Interner>, | ||
153 | fmt: &mut fmt::Formatter<'_>, | ||
154 | ) -> Result<(), fmt::Error> { | ||
155 | let goal_data = goal.data(&Interner); | ||
156 | write!(fmt, "{:?}", goal_data) | ||
157 | } | ||
158 | |||
159 | pub fn debug_goals( | ||
160 | &self, | ||
161 | goals: &Goals<Interner>, | ||
162 | fmt: &mut fmt::Formatter<'_>, | ||
163 | ) -> Result<(), fmt::Error> { | ||
164 | write!(fmt, "{:?}", goals.debug(&Interner)) | ||
165 | } | ||
166 | |||
167 | pub fn debug_program_clause_implication( | ||
168 | &self, | ||
169 | pci: &ProgramClauseImplication<Interner>, | ||
170 | fmt: &mut fmt::Formatter<'_>, | ||
171 | ) -> Result<(), fmt::Error> { | ||
172 | write!(fmt, "{:?}", pci.debug(&Interner)) | ||
173 | } | ||
174 | |||
175 | pub fn debug_application_ty( | ||
176 | &self, | ||
177 | application_ty: &chalk_ir::ApplicationTy<Interner>, | ||
178 | fmt: &mut fmt::Formatter<'_>, | ||
179 | ) -> Result<(), fmt::Error> { | ||
180 | write!(fmt, "{:?}", application_ty.debug(&Interner)) | ||
181 | } | ||
182 | |||
183 | pub fn debug_substitution( | ||
184 | &self, | ||
185 | substitution: &chalk_ir::Substitution<Interner>, | ||
186 | fmt: &mut fmt::Formatter<'_>, | ||
187 | ) -> Result<(), fmt::Error> { | ||
188 | write!(fmt, "{:?}", substitution.debug(&Interner)) | ||
189 | } | ||
190 | |||
191 | pub fn debug_separator_trait_ref( | ||
192 | &self, | ||
193 | separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>, | ||
194 | fmt: &mut fmt::Formatter<'_>, | ||
195 | ) -> Result<(), fmt::Error> { | ||
196 | write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)) | ||
197 | } | ||
198 | } | ||
199 | |||
200 | mod unsafe_tls { | ||
201 | use super::DebugContext; | ||
202 | use crate::db::HirDatabase; | ||
203 | use scoped_tls::scoped_thread_local; | ||
204 | |||
205 | scoped_thread_local!(static PROGRAM: DebugContext); | ||
206 | |||
207 | pub fn with_current_program<R>( | ||
208 | op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R, | ||
209 | ) -> R { | ||
210 | if PROGRAM.is_set() { | ||
211 | PROGRAM.with(|prog| op(Some(prog))) | ||
212 | } else { | ||
213 | op(None) | ||
214 | } | ||
215 | } | ||
216 | |||
217 | pub fn set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R | ||
218 | where | ||
219 | OP: FnOnce() -> R, | ||
220 | { | ||
221 | let ctx = DebugContext(p); | ||
222 | // we're transmuting the lifetime in the DebugContext to static. This is | ||
223 | // fine because we only keep the reference for the lifetime of this | ||
224 | // function, *and* the only way to access the context is through | ||
225 | // `with_current_program`, which hides the lifetime through the `for` | ||
226 | // type. | ||
227 | let static_p: &DebugContext<'static> = | ||
228 | unsafe { std::mem::transmute::<&DebugContext, &DebugContext<'static>>(&ctx) }; | ||
229 | PROGRAM.set(static_p, || op()) | ||
230 | } | ||
231 | } | ||