Fix compiler crash around dangling expect/let in traces

Fixes #1029.
This commit is contained in:
KtorZ 2024-10-01 12:24:31 +02:00
parent 9533903acc
commit 5737556efc
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
5 changed files with 142 additions and 22 deletions

View File

@ -8,6 +8,7 @@
### Changed ### Changed
- **aiken-lang**: Fix compiler crash on trace + expect as last expression of a clause. See #1029. @KtorZ
- **uplc**: Fix (again :grimacing:) cost-models for PlutusV1 & PlutusV2. @MicroProofs - **uplc**: Fix (again :grimacing:) cost-models for PlutusV1 & PlutusV2. @MicroProofs
### Removed ### Removed

View File

@ -203,6 +203,28 @@ impl<T> From<Vec1Ref<T>> for Vec1<T> {
} }
impl TypedExpr { impl TypedExpr {
pub fn and_then(self, next: Self) -> Self {
if let TypedExpr::Trace {
tipo,
location,
then,
text,
} = self
{
return TypedExpr::Trace {
tipo,
location,
then: Box::new(then.and_then(next)),
text,
};
}
TypedExpr::Sequence {
location: self.location(),
expressions: vec![self, next],
}
}
pub fn sequence(exprs: &[TypedExpr]) -> Self { pub fn sequence(exprs: &[TypedExpr]) -> Self {
TypedExpr::Sequence { TypedExpr::Sequence {
location: Span::empty(), location: Span::empty(),

View File

@ -3295,3 +3295,50 @@ fn softcasting_unused_let_binding() {
let (warnings, _) = result.unwrap(); let (warnings, _) = result.unwrap();
assert!(warnings.is_empty(), "should not contain any warnings"); assert!(warnings.is_empty(), "should not contain any warnings");
} }
#[test]
fn dangling_trace_let_standalone() {
let source_code = r#"
test foo() {
trace @"foo"
let True = True
}
"#;
assert!(matches!(
check_validator(parse(source_code)),
Err((_, Error::LastExpressionIsAssignment { .. }))
))
}
#[test]
fn dangling_trace_let_in_sequence() {
let source_code = r#"
test foo() {
let predicate = True
trace @"foo"
let result = predicate
}
"#;
assert!(matches!(
check_validator(parse(source_code)),
Err((_, Error::LastExpressionIsAssignment { .. }))
))
}
#[test]
fn dangling_trace_let_in_trace() {
let source_code = r#"
test foo() {
trace @"foo"
trace @"bar"
let result = True
}
"#;
assert!(matches!(
check_validator(parse(source_code)),
Err((_, Error::LastExpressionIsAssignment { .. }))
))
}

View File

@ -1565,10 +1565,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let then = if let Some(filler) = let then = if let Some(filler) =
recover_from_no_assignment(assert_no_assignment(&then), then.location())? recover_from_no_assignment(assert_no_assignment(&then), then.location())?
{ {
TypedExpr::Sequence { scope.infer(then)?.and_then(filler)
location,
expressions: vec![scope.infer(then)?, filler],
}
} else { } else {
scope.infer(then)? scope.infer(then)?
}; };
@ -1638,10 +1635,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let typed_final_else = if let Some(filler) = let typed_final_else = if let Some(filler) =
recover_from_no_assignment(assert_no_assignment(&final_else), final_else.location())? recover_from_no_assignment(assert_no_assignment(&final_else), final_else.location())?
{ {
TypedExpr::Sequence { self.infer(final_else)?.and_then(filler)
location: final_else.location(),
expressions: vec![self.infer(final_else)?, filler],
}
} else { } else {
self.infer(final_else)? self.infer(final_else)?
}; };
@ -1696,10 +1690,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
assert_no_assignment(&branch.body), assert_no_assignment(&branch.body),
branch.body.location(), branch.body.location(),
)? { )? {
TypedExpr::Sequence { typer.infer(branch.body.clone())?.and_then(filler)
location: branch.body.location(),
expressions: vec![typer.infer(branch.body.clone())?, filler],
}
} else { } else {
typer.infer(branch.body.clone())? typer.infer(branch.body.clone())?
}; };
@ -1720,10 +1711,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
assert_no_assignment(&branch.body), assert_no_assignment(&branch.body),
branch.body.location(), branch.body.location(),
)? { )? {
TypedExpr::Sequence { self.infer(branch.body.clone())?.and_then(filler)
location: branch.body.location(),
expressions: vec![self.infer(branch.body.clone())?, filler],
}
} else { } else {
self.infer(branch.body.clone())? self.infer(branch.body.clone())?
}; };
@ -1815,10 +1803,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
body_infer.map_err(|e| e.with_unify_error_rigid_names(&body_rigid_names)); body_infer.map_err(|e| e.with_unify_error_rigid_names(&body_rigid_names));
let body = if let Some(filler) = recover_from_no_assignment(no_assignment, location)? { let body = if let Some(filler) = recover_from_no_assignment(no_assignment, location)? {
TypedExpr::Sequence { inferred_body?.and_then(filler)
location,
expressions: vec![inferred_body?, filler],
}
} else { } else {
inferred_body? inferred_body?
}; };
@ -2206,8 +2191,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
if let Some(filler) = if let Some(filler) =
recover_from_no_assignment(no_assignment, typed_expression.location())? recover_from_no_assignment(no_assignment, typed_expression.location())?
{ {
expressions.push(typed_expression); match typed_expression.and_then(filler) {
expressions.push(filler); TypedExpr::Sequence {
expressions: seq, ..
} => expressions.extend(seq),
trace => expressions.push(trace),
}
} else { } else {
expressions.push(typed_expression); expressions.push(typed_expression);
} }
@ -2391,6 +2380,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
.collect::<Result<Vec<_>, Error>>()?; .collect::<Result<Vec<_>, Error>>()?;
let then = self.infer(then)?; let then = self.infer(then)?;
let tipo = then.tipo(); let tipo = then.tipo();
if let TraceKind::Todo = kind { if let TraceKind::Todo = kind {

View File

@ -6477,3 +6477,63 @@ fn hard_soft_cast() {
assert_uplc(src, program, false, false); assert_uplc(src, program, false, false);
} }
#[test]
fn dangling_trace_expect_standalone() {
let src = r#"
test foo() {
trace @"foo"
expect True
}
"#;
let program = Term::bool(true)
.delayed_if_then_else(
Term::unit(),
Term::Error.delayed_trace(Term::string("expect True")),
)
.delayed_trace(Term::string("foo"));
assert_uplc(src, program, false, true)
}
#[test]
fn dangling_trace_expect_in_sequence() {
let src = r#"
test foo() {
let predicate = True
trace @"foo"
expect predicate
}
"#;
let program = Term::bool(true)
.delayed_if_then_else(
Term::unit(),
Term::Error.delayed_trace(Term::string("expect predicate")),
)
.delayed_trace(Term::string("foo"));
assert_uplc(src, program, false, true)
}
#[test]
fn dangling_trace_expect_in_trace() {
let src = r#"
test foo() {
trace @"foo"
trace @"bar"
expect True
}
"#;
let program = Term::bool(true)
.delayed_if_then_else(
Term::unit(),
Term::Error.delayed_trace(Term::string("expect True")),
)
.delayed_trace(Term::string("bar"))
.delayed_trace(Term::string("foo"));
assert_uplc(src, program, false, true)
}