formality_core/
judgment.rs1use std::{cell::RefCell, collections::BTreeSet};
2
3use crate::fixed_point::FixedPointStack;
4
5mod test_filtered;
6mod test_reachable;
7
8pub type JudgmentStack<J, O> = RefCell<FixedPointStack<J, BTreeSet<O>>>;
9
10#[macro_export]
11macro_rules! judgment_fn {
12 (
13 $(#[$attr:meta])*
14 $v:vis fn $name:ident($($input_name:ident : $input_ty:ty),* $(,)?) => $output:ty {
15 debug($($debug_input_name:ident),*)
16 $(assert($assert_expr:expr))*
17 $(trivial($trivial_expr:expr => $trivial_result:expr))*
18 $(($($rule:tt)*))*
19 }
20 ) => {
21 $(#[$attr])*
22 $v fn $name($($input_name : impl $crate::Upcast<$input_ty>),*) -> $crate::Set<$output> {
23 #[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Clone)]
24 struct __JudgmentStruct($($input_ty),*);
25
26 $crate::cast_impl!(__JudgmentStruct);
27
28 impl std::fmt::Debug for __JudgmentStruct {
29 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 let mut f = fmt.debug_struct(stringify!($name));
31 let __JudgmentStruct($($input_name),*) = self;
32 $(
33 f.field(stringify!($debug_input_name), $input_name);
34 )*
35 f.finish()
36 }
37 }
38
39 $(let $input_name: $input_ty = $crate::Upcast::upcast($input_name);)*
40
41 $(
42 assert!($assert_expr);
44 )*
45
46 $(
47 if $trivial_expr {
50 return std::iter::once($trivial_result).collect();
51 }
52 )*
53
54 $crate::fixed_point::fixed_point::<
55 __JudgmentStruct,
56 $crate::Set<$output>,
57 >(
58 |input| {
60 let __JudgmentStruct($($input_name),*) = input;
61 tracing::debug_span!(
62 stringify!($name),
63 $(?$debug_input_name),*
64 )
65 },
66
67 {
69 thread_local! {
70 static R: $crate::judgment::JudgmentStack<__JudgmentStruct, $output> = Default::default()
71 }
72 &R
73 },
74
75 __JudgmentStruct($($input_name),*),
77
78 |_| Default::default(),
80
81 |input: __JudgmentStruct| {
83 let mut output = $crate::Set::new();
84
85 $crate::push_rules!(
86 $name,
87 &input,
88 output,
89 ($($input_name),*) => $output,
90 $(($($rule)*))*
91 );
92
93 output
94 },
95 )
96 }
97 }
98}
99
100#[macro_export]
125macro_rules! push_rules {
126 ($judgment_name:ident, $input_value:expr, $output:expr, $input_names:tt => $output_ty:ty, $($rule:tt)*) => {
127 $($crate::push_rules!(@rule ($judgment_name, $input_value, $output, $input_names => $output_ty) $rule);)*
128 };
129
130 (@rule ($judgment_name:ident, $input_value:expr, $output:expr, $input_names:tt => $output_ty:ty) ($($m:tt)*)) => {
133 $crate::push_rules!(@accum
134 args($judgment_name, $input_value, $output, $input_names => $output_ty)
135 accum()
136 $($m)*
137 );
138 };
139
140 (@accum
144 args($judgment_name:ident, $input_value:expr, $output:expr, ($($input_names:ident),*) => $output_ty:ty)
145 accum($($m:tt)*)
146 ---$(-)* ($n:literal)
147 ($conclusion_name:ident($($patterns:tt)*) => $v:expr)
148 ) => {
149 {
151 #[allow(dead_code)]
155 struct WrongJudgmentNameInConclusion;
156 const _: WrongJudgmentNameInConclusion = {
157 let $judgment_name = WrongJudgmentNameInConclusion;
158 $conclusion_name
159 };
160
161 if let Some(__JudgmentStruct($($input_names),*)) = Some($input_value) {
162 $crate::push_rules!(@match inputs($($input_names)*) patterns($($patterns)*) args($judgment_name; $n; $v; $output; $($m)*));
163 }
164 }
165 };
166
167 (@accum args $args:tt accum($($m:tt)*) ($($n:tt)*) $($o:tt)*) => {
168 $crate::push_rules!(@accum args $args accum($($m)* ($($n)*)) $($o)*)
170 };
171
172 (@match inputs() patterns() args($judgment_name:ident; $n:literal; $v:expr; $output:expr; $($m:tt)*)) => {
176 tracing::trace_span!("matched rule", rule = $n, judgment = stringify!($judgment_name)).in_scope(|| {
177 $crate::push_rules!(@body ($judgment_name, $n, $v, $output) $($m)*);
178 });
179 };
180
181 (@match inputs() patterns(,) args $args:tt) => {
182 $crate::push_rules!(@match inputs() patterns() args $args);
183 };
184
185 (@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:ident, $($pats:tt)*) args $args:tt) => {
186 {
187 let $pat0 = Clone::clone($in0);
188 $crate::push_rules!(@match inputs($($inputs)*) patterns($($pats)*) args $args);
189 }
190 };
191
192 (@match inputs($in0:ident) patterns($pat0:ident) args $args:tt) => {
193 {
194 let $pat0 = Clone::clone($in0);
195 $crate::push_rules!(@match inputs() patterns() args $args);
196 }
197 };
198
199 (@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:ident : $ty0:ty, $($pats:tt)*) args $args:tt) => {
200 {
201 if let Some($pat0) = $crate::Downcast::downcast::<$ty0>($in0) {
202 $crate::push_rules!(@match inputs($($inputs)*) patterns($($pats)*) args $args);
203 }
204 }
205 };
206
207 (@match inputs($in0:ident) patterns($pat0:ident : $ty0:ty) args $args:tt) => {
208 {
209 if let Some($pat0) = $crate::Downcast::downcast::<$ty0>($in0) {
210 $crate::push_rules!(@match inputs() patterns() args $args);
211 }
212 }
213 };
214
215 (@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:pat, $($pats:tt)*) args $args:tt) => {
216 if let Some($pat0) = $crate::Downcast::downcast(&$in0) {
217 $crate::push_rules!(@match inputs($($inputs)*) patterns($($pats)*) args $args);
218 }
219 };
220
221 (@match inputs($in0:ident) patterns($pat0:pat) args $args:tt) => {
222 if let Some($pat0) = $crate::Downcast::downcast(&$in0) {
223 $crate::push_rules!(@match inputs() patterns() args $args);
224 }
225 };
226
227 (@body $args:tt (if $c:expr) $($m:tt)*) => {
239 if $c {
240 $crate::push_rules!(@body $args $($m)*);
241 } else {
242 tracing::trace!("failed to match if condition {:?}", stringify!($c))
243 }
244 };
245
246 (@body $args:tt (assert $c:expr) $($m:tt)*) => {
247 assert!($c);
248 $crate::push_rules!(@body $args $($m)*);
249 };
250
251 (@body $args:tt (if let $p:pat = $e:expr) $($m:tt)*) => {
252 if let $p = $e {
253 $crate::push_rules!(@body $args $($m)*);
254 } else {
255 tracing::trace!("failed to match pattern {:?}", stringify!($p))
256 }
257 };
258
259 (@body $args:tt ($i:expr => $p:pat) $($m:tt)*) => {
260 for $p in $i {
261 $crate::push_rules!(@body $args $($m)*);
262 }
263 };
264
265 (@body $args:tt (let $p:pat = $i:expr) $($m:tt)*) => {
266 {
267 let $p = $i;
268 $crate::push_rules!(@body $args $($m)*);
269 }
270 };
271
272 (@body ($judgment_name:ident, $rule_name:literal, $v:expr, $output:expr)) => {
273 {
274 let result = $crate::Upcast::upcast($v);
275 tracing::debug!("produced {:?} from rule {:?} in judgment {:?}", result, $rule_name, stringify!($judgment_name));
276 $output.insert(result)
277 }
278 };
279}