1
- use std:: { cell:: RefCell , collections :: BTreeSet } ;
1
+ use std:: cell:: RefCell ;
2
2
3
- use crate :: fixed_point:: FixedPointStack ;
3
+ use crate :: { fixed_point:: FixedPointStack , Set } ;
4
+
5
+ mod proven_set;
6
+ pub use proven_set:: { FailedJudgment , FailedRule , ProvenSet , RuleFailureCause , TryIntoIter } ;
4
7
5
8
mod test_filtered;
6
9
mod test_reachable;
7
10
8
- pub type JudgmentStack < J , O > = RefCell < FixedPointStack < J , BTreeSet < O > > > ;
11
+ pub type JudgmentStack < J , O > = RefCell < FixedPointStack < J , Set < O > > > ;
9
12
10
13
#[ macro_export]
11
14
macro_rules! judgment_fn {
@@ -19,7 +22,7 @@ macro_rules! judgment_fn {
19
22
}
20
23
) => {
21
24
$( #[ $attr] ) *
22
- $v fn $name( $( $input_name : impl $crate:: Upcast <$input_ty>) ,* ) -> $crate:: Set <$output> {
25
+ $v fn $name( $( $input_name : impl $crate:: Upcast <$input_ty>) ,* ) -> $crate:: ProvenSet <$output> {
23
26
#[ derive( Ord , PartialOrd , Eq , PartialEq , Hash , Clone ) ]
24
27
struct __JudgmentStruct( $( $input_ty) ,* ) ;
25
28
@@ -30,7 +33,7 @@ macro_rules! judgment_fn {
30
33
let mut f = fmt. debug_struct( stringify!( $name) ) ;
31
34
let __JudgmentStruct( $( $input_name) ,* ) = self ;
32
35
$(
33
- f. field( stringify!( $debug_input_name) , $input_name ) ;
36
+ f. field( stringify!( $debug_input_name) , $debug_input_name ) ;
34
37
) *
35
38
f. finish( )
36
39
}
@@ -47,11 +50,13 @@ macro_rules! judgment_fn {
47
50
// Trivial cases are an (important) optimization that lets
48
51
// you cut out all the normal rules.
49
52
if $trivial_expr {
50
- return std:: iter:: once( $trivial_result) . collect( ) ;
53
+ return $crate :: ProvenSet :: proven ( std:: iter:: once( $trivial_result) . collect( ) ) ;
51
54
}
52
55
) *
53
56
54
- $crate:: fixed_point:: fixed_point:: <
57
+ let mut failed_rules = $crate:: set![ ] ;
58
+ let input = __JudgmentStruct( $( $input_name) ,* ) ;
59
+ let output = $crate:: fixed_point:: fixed_point:: <
55
60
__JudgmentStruct,
56
61
$crate:: Set <$output>,
57
62
>(
@@ -73,7 +78,7 @@ macro_rules! judgment_fn {
73
78
} ,
74
79
75
80
// Input:
76
- __JudgmentStruct ( $ ( $input_name ) , * ) ,
81
+ input . clone ( ) ,
77
82
78
83
// Default value:
79
84
|_| Default :: default ( ) ,
@@ -82,17 +87,26 @@ macro_rules! judgment_fn {
82
87
|input: __JudgmentStruct| {
83
88
let mut output = $crate:: Set :: new( ) ;
84
89
90
+ failed_rules. clear( ) ;
91
+
85
92
$crate:: push_rules!(
86
93
$name,
87
94
& input,
88
95
output,
96
+ failed_rules,
89
97
( $( $input_name) ,* ) => $output,
90
98
$( ( $( $rule) * ) ) *
91
99
) ;
92
100
93
101
output
94
102
} ,
95
- )
103
+ ) ;
104
+
105
+ if !output. is_empty( ) {
106
+ $crate:: ProvenSet :: proven( output)
107
+ } else {
108
+ $crate:: ProvenSet :: failed_rules( & input, failed_rules)
109
+ }
96
110
}
97
111
}
98
112
}
@@ -123,15 +137,15 @@ macro_rules! judgment_fn {
123
137
/// * `(<pat> => <binding>)
124
138
#[ macro_export]
125
139
macro_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) ; ) *
140
+ ( $judgment_name: ident, $input_value: expr, $output: expr, $failed_rules : expr , $ input_names: tt => $output_ty: ty, $( $rule: tt) * ) => {
141
+ $( $crate:: push_rules!( @rule ( $judgment_name, $input_value, $output, $failed_rules , $ input_names => $output_ty) $rule) ; ) *
128
142
} ;
129
143
130
144
// `@rule (builder) rule` phase: invoked for each rule, emits `push_rule` call
131
145
132
- ( @rule ( $judgment_name: ident, $input_value: expr, $output: expr, $input_names: tt => $output_ty: ty) ( $( $m: tt) * ) ) => {
146
+ ( @rule ( $judgment_name: ident, $input_value: expr, $output: expr, $failed_rules : expr , $ input_names: tt => $output_ty: ty) ( $( $m: tt) * ) ) => {
133
147
$crate:: push_rules!( @accum
134
- args( $judgment_name, $input_value, $output, $input_names => $output_ty)
148
+ args( $judgment_name, $input_value, $output, $failed_rules , $ input_names => $output_ty)
135
149
accum( )
136
150
$( $m) *
137
151
) ;
@@ -141,7 +155,7 @@ macro_rules! push_rules {
141
155
// pushing tokens into `conditions` until the `-----` and conclusion are found.
142
156
143
157
( @accum
144
- args( $judgment_name: ident, $input_value: expr, $output: expr, ( $( $input_names: ident) ,* ) => $output_ty: ty)
158
+ args( $judgment_name: ident, $input_value: expr, $output: expr, $failed_rules : expr , ( $( $input_names: ident) ,* ) => $output_ty: ty)
145
159
accum( $( $m: tt) * )
146
160
---$( -) * ( $n: literal)
147
161
( $conclusion_name: ident( $( $patterns: tt) * ) => $v: expr)
@@ -159,7 +173,7 @@ macro_rules! push_rules {
159
173
} ;
160
174
161
175
if let Some ( __JudgmentStruct( $( $input_names) ,* ) ) = Some ( $input_value) {
162
- $crate:: push_rules!( @match inputs( $( $input_names) * ) patterns( $( $patterns) * , ) args( @body ( $judgment_name; $n; $v; $output) ; $( $m) * ) ) ;
176
+ $crate:: push_rules!( @match inputs( $( $input_names) * ) patterns( $( $patterns) * , ) args( @body ( $judgment_name; $n; $v; $output) ; ( $failed_rules , ( $ ( $input_names ) , * ) , $n ) ; $( $m) * ) ) ;
163
177
}
164
178
}
165
179
} ;
@@ -172,9 +186,10 @@ macro_rules! push_rules {
172
186
// Matching phase: peel off the patterns one by one and match them against the values
173
187
// extracted from the input. For anything that is not an identity pattern, invoke `downcast`.
174
188
175
- ( @match inputs( ) patterns( ) args( @body ( $judgment_name: ident; $n: literal; $v: expr; $output: expr) ; $( $m: tt) * ) ) => {
189
+ ( @match inputs( ) patterns( ) args( @body ( $judgment_name: ident; $n: literal; $v: expr; $output: expr) ; ( $failed_rules : expr , $inputs : tt , $rule_name : literal ) ; $( $m: tt) * ) ) => {
176
190
tracing:: trace_span!( "matched rule" , rule = $n, judgment = stringify!( $judgment_name) ) . in_scope( || {
177
- $crate:: push_rules!( @body ( $judgment_name, $n, $v, $output) $( $m) * ) ;
191
+ let mut step_index = 0 ;
192
+ $crate:: push_rules!( @body ( $judgment_name, $n, $v, $output) ; ( $failed_rules, $inputs, $rule_name) ; step_index; $( $m) * ) ;
178
193
} ) ;
179
194
} ;
180
195
@@ -222,47 +237,89 @@ macro_rules! push_rules {
222
237
// expression `v` is carried in from the conclusion and forms the final
223
238
// output of this rule, once all the conditions are evaluated.
224
239
225
- ( @body $args: tt ( if $c: expr) $( $m: tt) * ) => {
240
+ ( @body $args: tt; $inputs : tt ; $step_index : ident ; ( if $c: expr) $( $m: tt) * ) => {
226
241
if $c {
227
- $crate:: push_rules!( @body $args $( $m) * ) ;
242
+ $step_index += 1 ;
243
+ $crate:: push_rules!( @body $args; $inputs; $step_index; $( $m) * ) ;
228
244
} else {
229
- tracing:: trace!( "failed to match if condition {:?}" , stringify!( $c) )
245
+ $crate:: push_rules!( @record_failure $inputs; $step_index; $crate:: judgment:: RuleFailureCause :: IfFalse {
246
+ expr: stringify!( $c) . to_string( ) ,
247
+ } ) ;
230
248
}
231
249
} ;
232
250
233
- ( @body $args: tt ( assert $c: expr) $( $m: tt) * ) => {
251
+ ( @body $args: tt; $inputs : tt ; $step_index : ident ; ( assert $c: expr) $( $m: tt) * ) => {
234
252
assert!( $c) ;
235
- $crate:: push_rules!( @body $args $( $m) * ) ;
253
+ $step_index += 1 ;
254
+ $crate:: push_rules!( @body $args; $inputs; $step_index; $( $m) * ) ;
236
255
} ;
237
256
238
- ( @body $args: tt ( if let $p: pat = $e: expr) $( $m: tt) * ) => {
239
- if let $p = $e {
240
- $crate:: push_rules!( @body $args $( $m) * ) ;
257
+ ( @body $args: tt; $inputs: tt; $step_index: ident; ( if let $p: pat = $e: expr) $( $m: tt) * ) => {
258
+ let value = & $e;
259
+ if let $p = Clone :: clone( value) {
260
+ $step_index += 1 ;
261
+ $crate:: push_rules!( @body $args; $inputs; $step_index; $( $m) * ) ;
241
262
} else {
242
- tracing:: trace!( "failed to match pattern {:?}" , stringify!( $p) )
263
+ $crate:: push_rules!( @record_failure $inputs; $step_index; $crate:: judgment:: RuleFailureCause :: IfLetDidNotMatch {
264
+ pattern: stringify!( $p) . to_string( ) ,
265
+ value: format!( "{:?}" , value) ,
266
+ } ) ;
243
267
}
244
268
} ;
245
269
246
- ( @body $args: tt ( $i: expr => $p: pat) $( $m: tt) * ) => {
270
+ ( @body $args: tt; $inputs : tt ; $step_index : ident ; ( $i: expr => $p: pat) $( $m: tt) * ) => {
247
271
// Explicitly calling `into_iter` silences some annoying lints
248
272
// in the case where `$i` is an `Option` or a `Result`
249
- for $p in std:: iter:: IntoIterator :: into_iter( $i) {
250
- $crate:: push_rules!( @body $args $( $m) * ) ;
273
+ match $crate:: judgment:: TryIntoIter :: try_into_iter( $i, || stringify!( $i) . to_string( ) ) {
274
+ Ok ( i) => {
275
+ $step_index += 1 ;
276
+ for $p in std:: iter:: IntoIterator :: into_iter( i) {
277
+ $crate:: push_rules!( @body $args; $inputs; $step_index; $( $m) * ) ;
278
+ }
279
+ }
280
+ Err ( e) => {
281
+ $crate:: push_rules!( @record_failure $inputs; $step_index; e) ;
282
+ }
251
283
}
252
284
} ;
253
285
254
- ( @body $args: tt ( let $p: pat = $i: expr) $( $m: tt) * ) => {
286
+ ( @body $args: tt; $inputs : tt ; $step_index : ident ; ( let $p: pat = $i: expr) $( $m: tt) * ) => {
255
287
{
256
288
let $p = $i;
257
- $crate:: push_rules!( @body $args $( $m) * ) ;
289
+ $step_index += 1 ;
290
+ $crate:: push_rules!( @body $args; $inputs; $step_index; $( $m) * ) ;
258
291
}
259
292
} ;
260
293
261
- ( @body ( $judgment_name: ident, $rule_name: literal, $v: expr, $output: expr) ) => {
294
+ ( @body ( $judgment_name: ident, $rule_name: literal, $v: expr, $output: expr) ; $inputs : tt ; $step_index : ident ; ) => {
262
295
{
296
+ let _ = $step_index; // suppress warnings about value not being read
263
297
let result = $crate:: Upcast :: upcast( $v) ;
264
298
tracing:: debug!( "produced {:?} from rule {:?} in judgment {:?}" , result, $rule_name, stringify!( $judgment_name) ) ;
265
299
$output. insert( result)
266
300
}
267
301
} ;
302
+
303
+ //
304
+
305
+ ( @record_failure ( $failed_rules: expr, $inputs: tt, $rule_name: literal) ; $step_index: ident; $cause: expr) => {
306
+ tracing:: trace!(
307
+ "rule {rn} failed at step {s} because {cause} ({file}:{line}:{column})" ,
308
+ rn = $rule_name,
309
+ s = $step_index,
310
+ cause = $cause,
311
+ file = file!( ) ,
312
+ line = line!( ) ,
313
+ column = column!( ) ,
314
+ ) ;
315
+ $failed_rules. insert(
316
+ $crate:: judgment:: FailedRule {
317
+ rule_name_index: Some ( ( $rule_name. to_string( ) , $step_index) ) ,
318
+ file: file!( ) . to_string( ) ,
319
+ line: line!( ) ,
320
+ column: column!( ) ,
321
+ cause: $cause,
322
+ }
323
+ ) ;
324
+ }
268
325
}
0 commit comments