Skip to content

Commit 0d25b29

Browse files
nikomatsakisNiko Matsakis
authored andcommitted
support ? in let/if-let judgment fn clauses
1 parent bdb95f1 commit 0d25b29

File tree

3 files changed

+126
-16
lines changed

3 files changed

+126
-16
lines changed

crates/formality-core/src/judgment.rs

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::cell::RefCell;
22

3-
use crate::{fixed_point::FixedPointStack, Set};
3+
use crate::{fixed_point::FixedPointStack, Fallible, Set};
44

55
mod proven_set;
66
pub use proven_set::{FailedJudgment, FailedRule, ProvenSet, RuleFailureCause, TryIntoIter};
77

8+
mod test_fallible;
89
mod test_filtered;
910
mod test_reachable;
1011

@@ -290,14 +291,21 @@ macro_rules! push_rules {
290291
// output of this rule, once all the conditions are evaluated.
291292

292293
(@body $args:tt; $inputs:tt; $step_index:expr; (if let $p:pat = $e:expr) $($m:tt)*) => {
293-
let value = &$e;
294-
if let $p = Clone::clone(value) {
295-
$crate::push_rules!(@body $args; $inputs; $step_index + 1; $($m)*);
296-
} else {
297-
$crate::push_rules!(@record_failure $inputs; $step_index, $e; $crate::judgment::RuleFailureCause::IfLetDidNotMatch {
298-
pattern: stringify!($p).to_string(),
299-
value: format!("{:?}", value),
300-
});
294+
match $crate::judgment::try_catch(|| Ok($e)) {
295+
Ok(value) => {
296+
if let $p = Clone::clone(&value) {
297+
$crate::push_rules!(@body $args; $inputs; $step_index + 1; $($m)*);
298+
} else {
299+
$crate::push_rules!(@record_failure $inputs; $step_index, $e; $crate::judgment::RuleFailureCause::IfLetDidNotMatch {
300+
pattern: stringify!($p).to_string(),
301+
value: format!("{:?}", value),
302+
});
303+
}
304+
}
305+
306+
Err(e) => {
307+
$crate::push_rules!(@record_failure $inputs; $step_index, $e; e);
308+
}
301309
}
302310
};
303311

@@ -405,16 +413,28 @@ macro_rules! push_rules {
405413
(@body $args:tt; $inputs:tt; $step_index:expr; (let $p:ident /*[1]*/: $t:ty = $i:expr) $($m:tt)*) => {
406414
// [1] I'd prefer to have `$p:pat` but the follow-set rules don't allow for it.
407415
// That's dumb.
408-
{
409-
let $p : $t = $i;
410-
$crate::push_rules!(@body $args; $inputs; $step_index + 1; $($m)*);
416+
match $crate::judgment::try_catch::<$t>(|| Ok($i)) {
417+
Ok(p) => {
418+
let $p = p;
419+
$crate::push_rules!(@body $args; $inputs; $step_index + 1; $($m)*);
420+
}
421+
422+
Err(e) => {
423+
$crate::push_rules!(@record_failure $inputs; $step_index, $i; e);
424+
}
411425
}
412426
};
413427

414428
(@body $args:tt; $inputs:tt; $step_index:expr; (let $p:pat = $i:expr) $($m:tt)*) => {
415-
{
416-
let $p = $i;
417-
$crate::push_rules!(@body $args; $inputs; $step_index + 1; $($m)*);
429+
match $crate::judgment::try_catch(|| Ok($i)) {
430+
Ok(p) => {
431+
let $p = p; // this enforces that `$p` is infalliblr
432+
$crate::push_rules!(@body $args; $inputs; $step_index + 1; $($m)*);
433+
}
434+
435+
Err(e) => {
436+
$crate::push_rules!(@record_failure $inputs; $step_index, $i; e);
437+
}
418438
}
419439
};
420440

@@ -464,3 +484,17 @@ macro_rules! push_rules {
464484
}
465485
}
466486
}
487+
488+
/// Helper function that just calls `f` and returns the value.
489+
/// Used for implementing `judgement_fn` macro to allow expressions to include `?`.
490+
pub fn try_catch<R>(f: impl FnOnce() -> Fallible<R>) -> Result<R, RuleFailureCause> {
491+
match f() {
492+
Ok(v) => Ok(v),
493+
494+
// Kind of dumb that `Inapplicable` only includes a `String` and not an `anyhow::Error`
495+
// but it's super annoying to package one of those up in the way we want.
496+
Err(e) => Err(RuleFailureCause::Inapplicable {
497+
reason: e.to_string(),
498+
}),
499+
}
500+
}

crates/formality-core/src/judgment/proven_set.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ pub enum RuleFailureCause {
404404
/// (for the given reason).
405405
FailedJudgment(Box<FailedJudgment>),
406406

407-
/// The rule did not succeed for some custom reason; this is not generated by the macro.
407+
/// The rule did not succeed for some custom reason. This occurs when a `?` fails in the judgment function macro.
408408
Inapplicable { reason: String },
409409

410410
/// The rule attempted to prove something that was already in the process of being proven
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#![cfg(test)]
2+
3+
//! Test that we permit the use of `?` in `let` and `if let`
4+
5+
use anyhow::bail;
6+
7+
use crate::cast_impl;
8+
use crate::judgment_fn;
9+
use crate::Fallible;
10+
11+
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug, Hash)]
12+
struct Check {
13+
x: u32,
14+
}
15+
16+
cast_impl!(Check);
17+
18+
impl Check {
19+
fn is(&self, x: u32) -> Fallible<u32> {
20+
if self.x == x {
21+
Ok(x)
22+
} else {
23+
bail!("expected {} got {}", self.x, x)
24+
}
25+
}
26+
}
27+
28+
judgment_fn!(
29+
fn jfn(c: Check, x: u32) => u32 {
30+
debug(c, x)
31+
32+
(
33+
(let y = c.is(x)?)
34+
--------------------------------------- ("rule")
35+
(jfn(c, x) => y)
36+
)
37+
38+
39+
(
40+
(if let 44 = c.is(x)?)
41+
--------------------------------------- ("other-rule")
42+
(jfn(c, x) => 45)
43+
)
44+
}
45+
);
46+
47+
#[test]
48+
fn is_equal_22() {
49+
jfn(Check { x: 22 }, 22).assert_ok(expect_test::expect![
50+
[r#"
51+
{
52+
22,
53+
}
54+
"#]
55+
]);
56+
}
57+
58+
#[test]
59+
fn is_equal_44() {
60+
jfn(Check { x: 44 }, 44).assert_ok(expect_test::expect![[r#"
61+
{
62+
44,
63+
45,
64+
}
65+
"#]]);
66+
}
67+
68+
#[test]
69+
fn is_not_equal() {
70+
jfn(Check { x: 22 }, 23).assert_err(expect_test::expect![[r#"
71+
judgment `jfn { c: Check { x: 22 }, x: 23 }` failed at the following rule(s):
72+
the rule "other-rule" failed at step #0 (src/file.rs:LL:CC) because
73+
expected 22 got 23
74+
the rule "rule" failed at step #0 (src/file.rs:LL:CC) because
75+
expected 22 got 23"#]]);
76+
}

0 commit comments

Comments
 (0)