Do not allow empty continuation for backpassed assignments
Fixes #1111. Signed-off-by: KtorZ <matthias.benkort@gmail.com>
This commit is contained in:
parent
b8bd91a589
commit
222d244bcf
|
@ -33,6 +33,7 @@ fn check_module(
|
||||||
|
|
||||||
for (package, module) in extra {
|
for (package, module) in extra {
|
||||||
let mut warnings = vec![];
|
let mut warnings = vec![];
|
||||||
|
|
||||||
let typed_module = module
|
let typed_module = module
|
||||||
.infer(
|
.infer(
|
||||||
&id_gen,
|
&id_gen,
|
||||||
|
@ -3309,6 +3310,28 @@ fn softcasting_unused_let_binding() {
|
||||||
assert!(warnings.is_empty(), "should not contain any warnings");
|
assert!(warnings.is_empty(), "should not contain any warnings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dangling_let_in_block() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn for_each(xs: List<Int>, with: fn(Int) -> a) -> a {
|
||||||
|
todo
|
||||||
|
}
|
||||||
|
|
||||||
|
test foo() {
|
||||||
|
{
|
||||||
|
let _ <- for_each([1, 2, 3])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let result = check_validator(parse(source_code));
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
matches!(result, Err((_, Error::LastExpressionIsAssignment { .. }))),
|
||||||
|
"{result:?}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dangling_trace_let_standalone() {
|
fn dangling_trace_let_standalone() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
|
|
|
@ -1970,11 +1970,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
PipeTyper::infer(self, expressions)
|
PipeTyper::infer(self, expressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::result_large_err)]
|
||||||
fn backpass(
|
fn backpass(
|
||||||
&mut self,
|
&mut self,
|
||||||
breakpoint: UntypedExpr,
|
breakpoint: UntypedExpr,
|
||||||
mut continuation: Vec<UntypedExpr>,
|
mut continuation: Vec<UntypedExpr>,
|
||||||
) -> UntypedExpr {
|
) -> Result<UntypedExpr, Error> {
|
||||||
let UntypedExpr::Assignment {
|
let UntypedExpr::Assignment {
|
||||||
location,
|
location,
|
||||||
value,
|
value,
|
||||||
|
@ -1985,6 +1986,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
unreachable!("backpass misuse: breakpoint isn't an Assignment ?!");
|
unreachable!("backpass misuse: breakpoint isn't an Assignment ?!");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if continuation.is_empty() {
|
||||||
|
return Err(Error::LastExpressionIsAssignment {
|
||||||
|
location,
|
||||||
|
expr: *value,
|
||||||
|
patterns: patterns.clone(),
|
||||||
|
kind,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let value_location = value.location();
|
let value_location = value.location();
|
||||||
|
|
||||||
let call_location = Span {
|
let call_location = Span {
|
||||||
|
@ -2101,11 +2111,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
value: UntypedExpr::lambda(names, continuation, lambda_span),
|
value: UntypedExpr::lambda(names, continuation, lambda_span),
|
||||||
});
|
});
|
||||||
|
|
||||||
UntypedExpr::Call {
|
Ok(UntypedExpr::Call {
|
||||||
location: call_location,
|
location: call_location,
|
||||||
fun,
|
fun,
|
||||||
arguments: new_arguments,
|
arguments: new_arguments,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This typically occurs on function captures. We do not try to assert anything on the
|
// This typically occurs on function captures. We do not try to assert anything on the
|
||||||
|
@ -2136,15 +2146,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if arguments.is_empty() {
|
if arguments.is_empty() {
|
||||||
call
|
Ok(call)
|
||||||
} else {
|
} else {
|
||||||
UntypedExpr::Fn {
|
Ok(UntypedExpr::Fn {
|
||||||
location: call_location,
|
location: call_location,
|
||||||
fn_style,
|
fn_style,
|
||||||
arguments,
|
arguments,
|
||||||
body: call.into(),
|
body: call.into(),
|
||||||
return_annotation,
|
return_annotation,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2152,7 +2162,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
// with our continuation. If the expression isn't callable? No problem, the
|
// with our continuation. If the expression isn't callable? No problem, the
|
||||||
// type-checker will catch that eventually in exactly the same way as if the code was
|
// type-checker will catch that eventually in exactly the same way as if the code was
|
||||||
// written like that to begin with.
|
// written like that to begin with.
|
||||||
_ => UntypedExpr::Call {
|
_ => Ok(UntypedExpr::Call {
|
||||||
location: call_location,
|
location: call_location,
|
||||||
fun: value,
|
fun: value,
|
||||||
arguments: vec![CallArg {
|
arguments: vec![CallArg {
|
||||||
|
@ -2160,7 +2170,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
label: None,
|
label: None,
|
||||||
value: UntypedExpr::lambda(names, continuation, lambda_span),
|
value: UntypedExpr::lambda(names, continuation, lambda_span),
|
||||||
}],
|
}],
|
||||||
},
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2203,7 +2213,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(breakpoint) = breakpoint {
|
if let Some(breakpoint) = breakpoint {
|
||||||
prefix.push(self.backpass(breakpoint, suffix));
|
prefix.push(self.backpass(breakpoint, suffix)?);
|
||||||
return self.infer_seq(location, prefix);
|
return self.infer_seq(location, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue