Allow backpassing with expect.

This commit is contained in:
KtorZ 2024-03-11 00:15:53 +01:00
parent 435dd0d213
commit a57dcf3307
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
3 changed files with 81 additions and 9 deletions

View File

@ -1479,13 +1479,17 @@ impl AssignmentKind<bool> {
} }
} }
impl AssignmentKind<()> { impl<T: Default> AssignmentKind<T> {
pub fn let_() -> Self { pub fn let_() -> Self {
AssignmentKind::Let { backpassing: () } AssignmentKind::Let {
backpassing: Default::default(),
}
} }
pub fn expect() -> Self { pub fn expect() -> Self {
AssignmentKind::Expect { backpassing: () } AssignmentKind::Expect {
backpassing: Default::default(),
}
} }
} }

View File

@ -1205,6 +1205,46 @@ fn backpassing_basic() {
assert!(check(parse(source_code)).is_ok()) assert!(check(parse(source_code)).is_ok())
} }
#[test]
fn backpassing_expect_simple() {
let source_code = r#"
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
when opt is {
None -> None
Some(a) -> then(a)
}
}
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
expect 42 <- and_then(opt_i)
let j <- and_then(opt_j)
Some(j + 42)
}
"#;
assert!(check(parse(source_code)).is_ok())
}
#[test]
fn backpassing_expect_nested() {
let source_code = r#"
fn and_then(opt: Option<a>, then: fn(Option<a>) -> Option<b>) -> Option<b> {
when opt is {
None -> None
Some(a) -> then(Some(a))
}
}
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
expect Some(i) <- and_then(opt_i)
expect Some(j) <- and_then(opt_j)
Some(i + j)
}
"#;
assert!(check(parse(source_code)).is_ok())
}
#[test] #[test]
fn backpassing_interleaved_capture() { fn backpassing_interleaved_capture() {
let source_code = r#" let source_code = r#"
@ -1320,6 +1360,29 @@ fn backpassing_unsaturated_fn() {
)) ))
} }
#[test]
fn backpassing_expect_type_mismatch() {
let source_code = r#"
fn and_then(opt: Option<a>, then: fn(a) -> Option<b>) -> Option<b> {
when opt is {
None -> None
Some(a) -> then(a)
}
}
fn backpassing(opt_i: Option<Int>, opt_j: Option<Int>) -> Option<Int> {
expect Some(i) <- and_then(opt_i)
let j <- and_then(opt_j)
Some(i + j)
}
"#;
assert!(matches!(
check(parse(source_code)),
Err((_, Error::CouldNotUnify { .. }))
))
}
#[test] #[test]
fn trace_if_false_ko() { fn trace_if_false_ko() {
let source_code = r#" let source_code = r#"

View File

@ -1708,14 +1708,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
} }
fn backpass(&mut self, breakpoint: UntypedExpr, continuation: Vec<UntypedExpr>) -> UntypedExpr { fn backpass(&mut self, breakpoint: UntypedExpr, continuation: Vec<UntypedExpr>) -> UntypedExpr {
let (assign_location, value, pattern, annotation) = match breakpoint { let (value, pattern, annotation, kind, assign_location) = match breakpoint {
UntypedExpr::Assignment { UntypedExpr::Assignment {
location, location,
value, value,
pattern, pattern,
annotation, annotation,
kind,
.. ..
} => (location, value, pattern, annotation), } => (value, pattern, annotation, kind, location),
_ => unreachable!("backpass misuse: breakpoint isn't an Assignment ?!"), _ => unreachable!("backpass misuse: breakpoint isn't an Assignment ?!"),
}; };
@ -1723,19 +1724,23 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
// in front of the continuation sequence. This is because we do not support patterns in function argument // in front of the continuation sequence. This is because we do not support patterns in function argument
// (which is perhaps something we should support?). // (which is perhaps something we should support?).
let (name, continuation) = match pattern { let (name, continuation) = match pattern {
Pattern::Var { name, .. } | Pattern::Discard { name, .. } => { Pattern::Var { name, .. } | Pattern::Discard { name, .. } if kind.is_let() => {
(name.clone(), continuation) (name.clone(), continuation)
} }
_ => { _ => {
let mut with_assignment = vec![UntypedExpr::Assignment { let mut with_assignment = vec![UntypedExpr::Assignment {
location: assign_location, location: assign_location,
value: UntypedExpr::Var { value: UntypedExpr::Var {
location: assign_location, location: value_location,
name: ast::BACKPASS_VARIABLE.to_string(), name: ast::BACKPASS_VARIABLE.to_string(),
} }
.into(), .into(),
pattern, pattern,
kind: AssignmentKind::Let { backpassing: false }, // Erase backpassing while preserving assignment kind.
kind: match kind {
AssignmentKind::Let { .. } => AssignmentKind::let_(),
AssignmentKind::Expect { .. } => AssignmentKind::expect(),
},
annotation, annotation,
}]; }];
with_assignment.extend(continuation); with_assignment.extend(continuation);
@ -2126,7 +2131,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
location: Span::empty(), location: Span::empty(),
value: Box::new(subject.clone()), value: Box::new(subject.clone()),
pattern: clauses[0].patterns[0].clone(), pattern: clauses[0].patterns[0].clone(),
kind: AssignmentKind::Let { backpassing: false }, kind: AssignmentKind::let_(),
annotation: None, annotation: None,
}, },
}); });