From 54d41f73eac99394bad69b7953f8875634f7754b Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sun, 16 Mar 2025 13:42:44 +0100 Subject: [PATCH] Fix adjacent sequence collapse in parser Without this fix, the parser wrongly turns the following: ``` { let a = Void a } let _ = True True ``` into the following: ``` let a = Void a let _ = True True ``` Which in this particular example looks benign. But now takes something more _real-world_: ``` { let scope, output <- for_each_2(scopes, other_outputs) Void } let _ = True True ``` This would lead to the the entire sequence that follows the backpassed continuation to be added to the continuation; here ultimately causing a type unification error (since for_each_2 is expected to yield Void, not Bool). This is utterly confusing.. and dangerous. Signed-off-by: KtorZ --- CHANGELOG.md | 1 + crates/aiken-lang/src/expr.rs | 21 +---- crates/aiken-lang/src/parser/expr/block.rs | 33 +++++++- .../expr/snapshots/sequence_then_expr.snap | 55 +++++++++++++ .../snapshots/sequence_then_sequence.snap | 80 +++++++++++++++++++ 5 files changed, 172 insertions(+), 18 deletions(-) create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/sequence_then_expr.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/sequence_then_sequence.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 2660b872..c0352036 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ### Fixed - **aiken-lang**: Formatter was removing comments from function type annotation args @rvcas +- **aiken-lang**: Parser wrongly merged two adjacent sequences together, effectively fusioning scopes. @KtorZ ## v1.1.13 - 2025-02-26 diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index 055f39e6..26a62236 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -1406,23 +1406,10 @@ impl UntypedExpr { }; match (self.clone(), next.clone()) { - ( - Self::Sequence { - expressions: mut current_expressions, - .. - }, - Self::Sequence { - expressions: mut next_expressions, - .. - }, - ) => { - current_expressions.append(&mut next_expressions); - - Self::Sequence { - location, - expressions: current_expressions, - } - } + (left @ Self::Sequence { .. }, right @ Self::Sequence { .. }) => Self::Sequence { + location, + expressions: vec![left, right], + }, ( _, Self::Sequence { diff --git a/crates/aiken-lang/src/parser/expr/block.rs b/crates/aiken-lang/src/parser/expr/block.rs index ac1db7c8..d0def583 100644 --- a/crates/aiken-lang/src/parser/expr/block.rs +++ b/crates/aiken-lang/src/parser/expr/block.rs @@ -31,7 +31,7 @@ pub fn parser( #[cfg(test)] mod tests { - use crate::assert_expr; + use crate::{assert_definition, assert_expr}; #[test] fn block_let() { @@ -54,4 +54,35 @@ mod tests { "# ); } + + #[test] + fn sequence_then_expr() { + assert_definition!( + r#" + test foo() { + { + let a = Void + a + } + True + } + "# + ); + } + + #[test] + fn sequence_then_sequence() { + assert_definition!( + r#" + test foo() { + { + let a = Void + a + } + let _ = True + True + } + "# + ); + } } diff --git a/crates/aiken-lang/src/parser/expr/snapshots/sequence_then_expr.snap b/crates/aiken-lang/src/parser/expr/snapshots/sequence_then_expr.snap new file mode 100644 index 00000000..c14750a0 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/sequence_then_expr.snap @@ -0,0 +1,55 @@ +--- +source: crates/aiken-lang/src/parser/expr/block.rs +description: "Code:\n\ntest foo() {\n {\n let a = Void\n a\n }\n True\n}\n" +--- +Test( + Function { + arguments: [], + body: Sequence { + location: 38..50, + expressions: [ + Sequence { + location: 21..39, + expressions: [ + Assignment { + location: 21..33, + value: Var { + location: 29..33, + name: "Void", + }, + patterns: [ + AssignmentPattern { + pattern: Var { + location: 25..26, + name: "a", + }, + annotation: None, + location: 25..26, + }, + ], + kind: Let { + backpassing: false, + }, + }, + Var { + location: 38..39, + name: "a", + }, + ], + }, + Var { + location: 46..50, + name: "True", + }, + ], + }, + doc: None, + location: 0..10, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 51, + on_test_failure: FailImmediately, + }, +) diff --git a/crates/aiken-lang/src/parser/expr/snapshots/sequence_then_sequence.snap b/crates/aiken-lang/src/parser/expr/snapshots/sequence_then_sequence.snap new file mode 100644 index 00000000..1f0d6dd0 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/sequence_then_sequence.snap @@ -0,0 +1,80 @@ +--- +source: crates/aiken-lang/src/parser/expr/block.rs +description: "Code:\n\ntest foo() {\n {\n let a = Void\n a\n }\n let _ = True\n True\n}\n" +--- +Test( + Function { + arguments: [], + body: Sequence { + location: 38..65, + expressions: [ + Sequence { + location: 21..39, + expressions: [ + Assignment { + location: 21..33, + value: Var { + location: 29..33, + name: "Void", + }, + patterns: [ + AssignmentPattern { + pattern: Var { + location: 25..26, + name: "a", + }, + annotation: None, + location: 25..26, + }, + ], + kind: Let { + backpassing: false, + }, + }, + Var { + location: 38..39, + name: "a", + }, + ], + }, + Sequence { + location: 46..65, + expressions: [ + Assignment { + location: 46..58, + value: Var { + location: 54..58, + name: "True", + }, + patterns: [ + AssignmentPattern { + pattern: Discard { + name: "_", + location: 50..51, + }, + annotation: None, + location: 50..51, + }, + ], + kind: Let { + backpassing: false, + }, + }, + Var { + location: 61..65, + name: "True", + }, + ], + }, + ], + }, + doc: None, + location: 0..10, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 66, + on_test_failure: FailImmediately, + }, +)