From 5a4a8df7272ea14aeef10d7698f02063b9f2915a Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 9 Feb 2023 00:25:54 -0500 Subject: [PATCH 01/10] feat(token): add Token::Expect --- crates/aiken-lang/src/parser/token.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/aiken-lang/src/parser/token.rs b/crates/aiken-lang/src/parser/token.rs index 24c53c2d..3106326d 100644 --- a/crates/aiken-lang/src/parser/token.rs +++ b/crates/aiken-lang/src/parser/token.rs @@ -65,6 +65,8 @@ pub enum Token { Fn, If, Else, + ErrorTerm, + Expect, Is, Let, Opaque, @@ -75,7 +77,6 @@ pub enum Token { Type, When, Trace, - ErrorTerm, } impl fmt::Display for Token { @@ -141,6 +142,7 @@ impl fmt::Display for Token { Token::NewLine => "NEWLINE", Token::As => "as", Token::Assert => "assert", + Token::Expect => "expect", Token::When => "when", Token::Is => "is", Token::Const => "const", From dbd162e985f45eb34bfec02f3a2d7713b3757da9 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 9 Feb 2023 00:43:29 -0500 Subject: [PATCH 02/10] feat: handle expect in parser * map both assert/expect to Token::Expect * use the new token in the parser * new unit test to expect --- crates/aiken-lang/src/parser.rs | 2 +- crates/aiken-lang/src/parser/lexer.rs | 3 +- crates/aiken-lang/src/parser/token.rs | 2 - crates/aiken-lang/src/tests/parser.rs | 71 +++++++++++++++++++++++++++ crates/aiken-lang/src/tipo/infer.rs | 5 +- 5 files changed, 78 insertions(+), 5 deletions(-) diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 99d4c633..e9163e21 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -1028,7 +1028,7 @@ pub fn expr_parser( }, ); - let assert_parser = just(Token::Assert) + let assert_parser = just(Token::Expect) .ignore_then(pattern_parser()) .then(just(Token::Colon).ignore_then(type_parser()).or_not()) .then_ignore(just(Token::Equal)) diff --git a/crates/aiken-lang/src/parser/lexer.rs b/crates/aiken-lang/src/parser/lexer.rs index 84aaa4d2..9b390364 100644 --- a/crates/aiken-lang/src/parser/lexer.rs +++ b/crates/aiken-lang/src/parser/lexer.rs @@ -87,7 +87,8 @@ pub fn lexer() -> impl Parser, Error = ParseError> { "trace" => Token::Trace, "error" => Token::ErrorTerm, "as" => Token::As, - "assert" => Token::Assert, + "assert" => Token::Expect, + "expect" => Token::Expect, "const" => Token::Const, "fn" => Token::Fn, "test" => Token::Test, diff --git a/crates/aiken-lang/src/parser/token.rs b/crates/aiken-lang/src/parser/token.rs index 3106326d..e94de71c 100644 --- a/crates/aiken-lang/src/parser/token.rs +++ b/crates/aiken-lang/src/parser/token.rs @@ -60,7 +60,6 @@ pub enum Token { NewLine, // Keywords (alphabetically): As, - Assert, Const, Fn, If, @@ -141,7 +140,6 @@ impl fmt::Display for Token { Token::EmptyLine => "EMPTYLINE", Token::NewLine => "NEWLINE", Token::As => "as", - Token::Assert => "assert", Token::Expect => "expect", Token::When => "when", Token::Is => "is", diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 845c9393..e71f882e 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -324,6 +324,77 @@ fn empty_function() { ) } +#[test] +fn expect() { + let code = indoc! {r#" + pub fn run() { + expect Some(x) = something.field + x.other_field + } + "#}; + + assert_definitions( + code, + vec![ast::UntypedDefinition::Fn(Function { + arguments: vec![], + body: expr::UntypedExpr::Sequence { + location: Span::new((), 19..69), + expressions: vec![ + expr::UntypedExpr::Assignment { + location: Span::new((), 19..51), + value: expr::UntypedExpr::FieldAccess { + location: Span::new((), 36..51), + label: "field".to_string(), + container: expr::UntypedExpr::Var { + location: Span::new((), 36..45), + name: "something".to_string(), + } + .into(), + } + .into(), + pattern: ast::Pattern::Constructor { + is_record: false, + location: Span::new((), 26..33), + name: "Some".to_string(), + arguments: vec![ast::CallArg { + label: None, + location: Span::new((), 31..32), + value: ast::Pattern::Var { + location: Span::new((), 31..32), + name: "x".to_string(), + }, + }], + module: None, + constructor: (), + with_spread: false, + tipo: (), + }, + kind: ast::AssignmentKind::Assert, + annotation: None, + }, + expr::UntypedExpr::FieldAccess { + location: Span::new((), 56..69), + label: "other_field".to_string(), + container: expr::UntypedExpr::Var { + location: Span::new((), 56..57), + name: "x".to_string(), + } + .into(), + }, + ], + }, + doc: None, + + location: Span::new((), 0..12), + name: "run".to_string(), + public: true, + return_annotation: None, + return_type: (), + end_position: 70, + })], + ) +} + #[test] fn plus_binop() { let code = indoc! {r#" diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index 54037cfd..771c5d0c 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -511,8 +511,11 @@ fn validate_module_name(name: &str) -> Result<(), Error> { fn str_to_keyword(word: &str) -> Option { // Alphabetical keywords: match word { + "assert" => Some(Token::Expect), + "expect" => Some(Token::Expect), + "else" => Some(Token::Else), + "is" => Some(Token::Is), "as" => Some(Token::As), - "assert" => Some(Token::Assert), "when" => Some(Token::When), "const" => Some(Token::Const), "fn" => Some(Token::Fn), From 3f540c7c99b01e014f7fc9fe3f783ae4842083b9 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 9 Feb 2023 00:47:38 -0500 Subject: [PATCH 03/10] chore: rename assignment kind --- crates/aiken-lang/src/ast.rs | 4 ++-- crates/aiken-lang/src/format.rs | 2 +- crates/aiken-lang/src/parser.rs | 2 +- crates/aiken-lang/src/tests/parser.rs | 2 +- crates/aiken-lang/src/tipo/error.rs | 2 +- crates/aiken-lang/src/tipo/expr.rs | 2 +- crates/aiken-lang/src/uplc.rs | 14 +++++++------- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 4c8da18d..51ba9e2d 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -766,7 +766,7 @@ impl Pattern { #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub enum AssignmentKind { Let, - Assert, + Expect, } impl AssignmentKind { @@ -775,7 +775,7 @@ impl AssignmentKind { } pub fn is_assert(&self) -> bool { - matches!(self, AssignmentKind::Assert) + matches!(self, AssignmentKind::Expect) } } diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 6bb0a76c..8134340d 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -606,7 +606,7 @@ impl<'comments> Formatter<'comments> { let keyword = match kind { Some(AssignmentKind::Let) => "let ", - Some(AssignmentKind::Assert) => "assert ", + Some(AssignmentKind::Expect) => "assert ", None => "try ", }; diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index e9163e21..4760e0c0 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -1038,7 +1038,7 @@ pub fn expr_parser( location: span, value: Box::new(value), pattern, - kind: ast::AssignmentKind::Assert, + kind: ast::AssignmentKind::Expect, annotation, }, ); diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index e71f882e..3837ef73 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -369,7 +369,7 @@ fn expect() { with_spread: false, tipo: (), }, - kind: ast::AssignmentKind::Assert, + kind: ast::AssignmentKind::Expect, annotation: None, }, expr::UntypedExpr::FieldAccess { diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 2c0fed74..9be75612 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -333,7 +333,7 @@ Perhaps, try the following: #[diagnostic(code("illegal::module_name"))] #[diagnostic(help(r#"You cannot use keywords as part of a module path name. As a quick reminder, here's a list of all the keywords (and thus, of invalid module path names): - as, assert, check, const, else, fn, if, is, let, opaque, pub, test, todo, trace, type, use, when"#))] + as, expect, check, const, else, fn, if, is, let, opaque, pub, test, todo, trace, type, use, when"#))] KeywordInModuleName { name: String, keyword: String }, #[error("I discovered a function which is ending with an assignment.\n")] diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 3787b991..fd0915fb 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -877,7 +877,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { // We currently only do limited exhaustiveness checking of custom types // at the top level of patterns. // Do not perform exhaustiveness checking if user explicitly used `assert`. - if kind != AssignmentKind::Assert { + if kind != AssignmentKind::Expect { if let Err(unmatched) = self.environment.check_exhaustiveness( vec![pattern.clone()], collapse_links(value_typ.clone()), diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 15384e91..a2679763 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -1578,7 +1578,7 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(value_vec); - if matches!(assignment_properties.kind, AssignmentKind::Assert) + if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { @@ -1604,7 +1604,7 @@ impl<'a> CodeGenerator<'a> { pattern_vec.append(value_vec); } list @ Pattern::List { .. } => { - if matches!(assignment_properties.kind, AssignmentKind::Assert) + if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { @@ -1629,7 +1629,7 @@ impl<'a> CodeGenerator<'a> { } // TODO: Check constr for assert on all cases constr @ Pattern::Constructor { .. } => { - if matches!(assignment_properties.kind, AssignmentKind::Assert) + if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { @@ -1653,7 +1653,7 @@ impl<'a> CodeGenerator<'a> { } } tuple @ Pattern::Tuple { .. } => { - if matches!(assignment_properties.kind, AssignmentKind::Assert) + if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { @@ -2503,7 +2503,7 @@ impl<'a> CodeGenerator<'a> { let id = self.id_gen.next(); let list_name = format!("__list_{id}"); - if matches!(assignment_properties.kind, AssignmentKind::Assert) + if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { @@ -2556,7 +2556,7 @@ impl<'a> CodeGenerator<'a> { let id = self.id_gen.next(); let constr_name = format!("{constr_name}_{id}"); - if matches!(assignment_properties.kind, AssignmentKind::Assert) + if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { @@ -2605,7 +2605,7 @@ impl<'a> CodeGenerator<'a> { let id = self.id_gen.next(); let tuple_name = format!("__tuple_name_{id}"); - if matches!(assignment_properties.kind, AssignmentKind::Assert) + if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { From 6f591c633dd7934e392de8ef10b100fe073ad7b5 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 9 Feb 2023 00:57:14 -0500 Subject: [PATCH 04/10] chore: fmt most of the acceptance tests --- examples/acceptance_tests/008/lib/tests.ak | 8 ++--- examples/acceptance_tests/009/lib/tests.ak | 2 +- examples/acceptance_tests/016/lib/tests.ak | 4 +-- examples/acceptance_tests/017/lib/tests.ak | 2 +- examples/acceptance_tests/023/lib/tests.ak | 6 +--- examples/acceptance_tests/029/lib/tests.ak | 6 +--- .../acceptance_tests/035/lib/aiken/dict.ak | 2 +- examples/acceptance_tests/035/lib/tests.ak | 2 +- .../acceptance_tests/036/validators/spend.ak | 2 +- examples/acceptance_tests/039/lib/tests.ak | 36 +++++++++---------- examples/acceptance_tests/047/lib/tests.ak | 1 + examples/acceptance_tests/fmt | 26 ++++++++++++++ examples/acceptance_tests/fmt-all | 3 ++ 13 files changed, 61 insertions(+), 39 deletions(-) create mode 100755 examples/acceptance_tests/fmt create mode 100755 examples/acceptance_tests/fmt-all diff --git a/examples/acceptance_tests/008/lib/tests.ak b/examples/acceptance_tests/008/lib/tests.ak index 689fb805..d9fcc3ed 100644 --- a/examples/acceptance_tests/008/lib/tests.ak +++ b/examples/acceptance_tests/008/lib/tests.ak @@ -5,17 +5,17 @@ pub fn is_empty(bytes: ByteArray) -> Bool { } test is_empty_1() { - is_empty(#[]) == True + is_empty(#"") == True } test is_empty_1_alt() { - is_empty(#[]) + is_empty(#"") } test is_empty_2() { - is_empty(#[1]) == False + is_empty(#"01") == False } test is_empty_2_alt() { - !is_empty(#[1]) + !is_empty(#"01") } diff --git a/examples/acceptance_tests/009/lib/tests.ak b/examples/acceptance_tests/009/lib/tests.ak index b4ef2b0d..c3384d04 100644 --- a/examples/acceptance_tests/009/lib/tests.ak +++ b/examples/acceptance_tests/009/lib/tests.ak @@ -5,5 +5,5 @@ pub fn is_empty(bytes: ByteArray) -> Bool { } test is_empty_1() { - is_empty(#[]) == True + is_empty(#"") == True } diff --git a/examples/acceptance_tests/016/lib/tests.ak b/examples/acceptance_tests/016/lib/tests.ak index 9c658c82..72462cea 100644 --- a/examples/acceptance_tests/016/lib/tests.ak +++ b/examples/acceptance_tests/016/lib/tests.ak @@ -13,6 +13,6 @@ pub fn drop(bytes: ByteArray, n: Int) -> ByteArray { } test drop_1() { - let x = #[1, 2, 3, 4, 5, 6, 7] - drop(x, 2) == #[3, 4, 5, 6, 7] + let x = #"01020304050607" + drop(x, 2) == #"0304050607" } diff --git a/examples/acceptance_tests/017/lib/tests.ak b/examples/acceptance_tests/017/lib/tests.ak index af71db7b..62863b0d 100644 --- a/examples/acceptance_tests/017/lib/tests.ak +++ b/examples/acceptance_tests/017/lib/tests.ak @@ -9,5 +9,5 @@ pub fn take(bytes: ByteArray, n: Int) -> ByteArray { } test take_1() { - take(#[1, 2, 3], 2) == #[1, 2] + take(#"010203", 2) == #"0102" } diff --git a/examples/acceptance_tests/023/lib/tests.ak b/examples/acceptance_tests/023/lib/tests.ak index cdafc006..761e86ef 100644 --- a/examples/acceptance_tests/023/lib/tests.ak +++ b/examples/acceptance_tests/023/lib/tests.ak @@ -18,11 +18,7 @@ pub fn insert( AssocList { inner: do_insert(m.inner, k, v) } } -fn do_insert( - elems: List<(key, value)>, - k: key, - v: value, -) -> List<(key, value)> { +fn do_insert(elems: List<(key, value)>, k: key, v: value) -> List<(key, value)> { when elems is { [] -> [(k, v)] [(k2, v2), ..rest] -> diff --git a/examples/acceptance_tests/029/lib/tests.ak b/examples/acceptance_tests/029/lib/tests.ak index 6d74cfac..bc3f1835 100644 --- a/examples/acceptance_tests/029/lib/tests.ak +++ b/examples/acceptance_tests/029/lib/tests.ak @@ -25,11 +25,7 @@ pub fn insert( AssocList { inner: do_insert(m.inner, k, v) } } -fn do_insert( - elems: List<(key, value)>, - k: key, - v: value, -) -> List<(key, value)> { +fn do_insert(elems: List<(key, value)>, k: key, v: value) -> List<(key, value)> { when elems is { [] -> [(k, v)] [(k2, v2), ..rest] -> diff --git a/examples/acceptance_tests/035/lib/aiken/dict.ak b/examples/acceptance_tests/035/lib/aiken/dict.ak index 9640ff8f..a2bbcc2f 100644 --- a/examples/acceptance_tests/035/lib/aiken/dict.ak +++ b/examples/acceptance_tests/035/lib/aiken/dict.ak @@ -20,7 +20,7 @@ pub fn insert( ) -> Dict { Dict { inner: do_insert_with(self.inner, k, v, fn(_, left, _right) { Some(left) })} - + } pub fn union_with( diff --git a/examples/acceptance_tests/035/lib/tests.ak b/examples/acceptance_tests/035/lib/tests.ak index 60279bab..2cf7eade 100644 --- a/examples/acceptance_tests/035/lib/tests.ak +++ b/examples/acceptance_tests/035/lib/tests.ak @@ -22,7 +22,7 @@ pub fn from_asset( } pub fn from_lovelace(quantity: Int) -> Value { - from_asset(#[], #[], quantity) + from_asset(#"", #"", quantity) } pub fn add(left v0: Value, right v1: Value) -> Value { diff --git a/examples/acceptance_tests/036/validators/spend.ak b/examples/acceptance_tests/036/validators/spend.ak index 23094f20..f68427db 100644 --- a/examples/acceptance_tests/036/validators/spend.ak +++ b/examples/acceptance_tests/036/validators/spend.ak @@ -3,7 +3,7 @@ use aiken/list use aiken/transaction.{Output, ScriptContext} use aiken/transaction/value.{PolicyId} -const my_policy_id: PolicyId = #[0, 0, 0, 0, 0] +const my_policy_id: PolicyId = #"0000000000" pub fn has_policy_id(self: Output, policy_id: PolicyId) -> Bool { self.value diff --git a/examples/acceptance_tests/039/lib/tests.ak b/examples/acceptance_tests/039/lib/tests.ak index 22b1bc55..66c5933b 100644 --- a/examples/acceptance_tests/039/lib/tests.ak +++ b/examples/acceptance_tests/039/lib/tests.ak @@ -37,52 +37,52 @@ pub fn update_door_locked_and_wheels( test update_owner1() { let initial_car = Car { - owner: #[], + owner: #"", wheels: 4, - vin: #[1, 1, 1, 1, 1, 1, 1], + vin: #"01010101010101", door: Door { locked: False, hinge_angle: 45 }, } let final_car = Car { - owner: #[244, 244, 244, 244], + owner: #"f4f4f4f4", wheels: 4, - vin: #[1, 1, 1, 1, 1, 1, 1], + vin: #"01010101010101", door: Door { locked: False, hinge_angle: 45 }, } - update_owner(#[244, 244, 244, 244], initial_car) == final_car + update_owner(#"f4f4f4f4", initial_car) == final_car } test update_vin1() { let initial_car = Car { - owner: #[], + owner: #"", wheels: 4, - vin: #[1, 1, 1, 1, 1, 1, 1], + vin: #"01010101010101", door: Door { locked: False, hinge_angle: 45 }, } let final_car = Car { - owner: #[], + owner: #"", wheels: 4, - vin: #[2, 2, 2, 2, 2, 2, 2, 2, 2], + vin: #"020202020202020202", door: Door { locked: False, hinge_angle: 45 }, } - update_vin(#[2, 2, 2, 2, 2, 2, 2, 2, 2], initial_car) == final_car + update_vin(#"020202020202020202", initial_car) == final_car } test update_door_angle1() { let initial_car = Car { - owner: #[], + owner: #"", wheels: 4, - vin: #[1, 1, 1, 1, 1, 1, 1], + vin: #"01010101010101", door: Door { locked: False, hinge_angle: 45 }, } let final_car = Car { - owner: #[], + owner: #"", wheels: 4, - vin: #[1, 1, 1, 1, 1, 1, 1], + vin: #"01010101010101", door: Door { locked: False, hinge_angle: 90 }, } update_door_angle(90, initial_car) == final_car @@ -91,16 +91,16 @@ test update_door_angle1() { test update_door_locked_and_wheels1() { let initial_car = Car { - owner: #[], + owner: #"", wheels: 4, - vin: #[1, 1, 1, 1, 1, 1, 1], + vin: #"01010101010101", door: Door { locked: False, hinge_angle: 45 }, } let final_car = Car { - owner: #[], + owner: #"", wheels: 5, - vin: #[1, 1, 1, 1, 1, 1, 1], + vin: #"01010101010101", door: Door { locked: True, hinge_angle: 45 }, } update_door_locked_and_wheels(True, 5, initial_car) == final_car diff --git a/examples/acceptance_tests/047/lib/tests.ak b/examples/acceptance_tests/047/lib/tests.ak index e69de29b..8b137891 100644 --- a/examples/acceptance_tests/047/lib/tests.ak +++ b/examples/acceptance_tests/047/lib/tests.ak @@ -0,0 +1 @@ + diff --git a/examples/acceptance_tests/fmt b/examples/acceptance_tests/fmt new file mode 100755 index 00000000..4e424e97 --- /dev/null +++ b/examples/acceptance_tests/fmt @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [ -z $1 ]; then + echo -e "\033[31mMissing argument: \033[1mACCEPTANCE_TEST\033[0m" + echo "" + echo -e "\033[1mUsage: \033[0m" + echo " run.sh {ACCEPTANCE_TEST}" + echo "" + echo -e "\033[1mExample: \033[0m" + echo " run.sh 034" + exit 1 +fi + +WORKDIR="$(dirname -- "${BASH_SOURCE[0]}")" +TARGET="$WORKDIR/$(basename $1)" + +TMP=$(mktemp) +RESULT=$(cargo run --quiet -- fmt $TARGET 2>$TMP) + +if [ "$?" -eq "0" ]; then + echo "✅ $(basename $TARGET)" +else + echo "❌ $(basename $TARGET)" + cat $TMP + exit 1 +fi diff --git a/examples/acceptance_tests/fmt-all b/examples/acceptance_tests/fmt-all new file mode 100755 index 00000000..fd06db44 --- /dev/null +++ b/examples/acceptance_tests/fmt-all @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +find . -regex ".*[0-9]\{3\}" -type d | xargs -P 8 -I {} -- ./fmt {} From 9348caab2f80e856fce3252a974ed706237df0a8 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 9 Feb 2023 00:57:38 -0500 Subject: [PATCH 05/10] feat(expect): update formatter * Token::Expect should format to expect * since we still capture assert this means we can help users update to the new syntax --- crates/aiken-lang/src/format.rs | 2 +- examples/acceptance_tests/040/lib/tests.ak | 2 +- examples/acceptance_tests/050/lib/tests.ak | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 8134340d..9e02c278 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -606,7 +606,7 @@ impl<'comments> Formatter<'comments> { let keyword = match kind { Some(AssignmentKind::Let) => "let ", - Some(AssignmentKind::Expect) => "assert ", + Some(AssignmentKind::Expect) => "expect ", None => "try ", }; diff --git a/examples/acceptance_tests/040/lib/tests.ak b/examples/acceptance_tests/040/lib/tests.ak index 5cdb783d..3bd58bd9 100644 --- a/examples/acceptance_tests/040/lib/tests.ak +++ b/examples/acceptance_tests/040/lib/tests.ak @@ -28,6 +28,6 @@ test update_owner1() { builtin.list_data([]), ], ) - assert Ford { owner, wheels, truck_bed_limit, .. }: Car = initial_car + expect Ford { owner, wheels, truck_bed_limit, .. }: Car = initial_car owner == #"" && wheels == 4 && truck_bed_limit == 10000 } diff --git a/examples/acceptance_tests/050/lib/tests.ak b/examples/acceptance_tests/050/lib/tests.ak index 4416c0bc..dca9b864 100644 --- a/examples/acceptance_tests/050/lib/tests.ak +++ b/examples/acceptance_tests/050/lib/tests.ak @@ -13,19 +13,19 @@ test let_1() { test let_2() { let x: Data = 1 - assert y: Int = x + expect y: Int = x y == 1 } test assert_1() { - assert thing: Thing = builtin.constr_data(0, [builtin.i_data(1)]) + expect thing: Thing = builtin.constr_data(0, [builtin.i_data(1)]) thing.wow == 1 } fn cast_to_thing(x: Data) -> Thing { - assert x: Thing = x + expect x: Thing = x x } From e9caa710c46b3c3547c1cab610ee049ed3281e08 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 9 Feb 2023 01:05:11 -0500 Subject: [PATCH 06/10] chore: rename assert_parser to expect_parser --- crates/aiken-lang/src/parser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 4760e0c0..2c3eed8e 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -1028,7 +1028,7 @@ pub fn expr_parser( }, ); - let assert_parser = just(Token::Expect) + let expect_parser = just(Token::Expect) .ignore_then(pattern_parser()) .then(just(Token::Colon).ignore_then(type_parser()).or_not()) .then_ignore(just(Token::Equal)) @@ -1093,7 +1093,7 @@ pub fn expr_parser( block_parser, when_parser, let_parser, - assert_parser, + expect_parser, if_parser, )); From 460da20e4a3ed83d12991de5a5a276f588c689b8 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 9 Feb 2023 02:10:22 -0500 Subject: [PATCH 07/10] feat: warn when using expect in a completely safe way --- crates/aiken-lang/src/ast.rs | 2 +- crates/aiken-lang/src/tipo/error.rs | 15 +++++++++ crates/aiken-lang/src/tipo/expr.rs | 48 +++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 51ba9e2d..54c944e9 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -774,7 +774,7 @@ impl AssignmentKind { matches!(self, AssignmentKind::Let) } - pub fn is_assert(&self) -> bool { + pub fn is_expect(&self) -> bool { matches!(self, AssignmentKind::Expect) } } diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 9be75612..5a24446b 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1076,6 +1076,21 @@ pub enum Warning { sample: UntypedExpr, }, + #[error("I found an {} trying to match a type with one constructor", "expect".purple())] + #[diagnostic( + code("single_constructor_expect"), + help("If your type has one constructor, unless you are casting {} {}, you can\nprefer using a {} binding like so...\n\n{}", "FROM".bold(), "Data".purple(), "let".purple(), format_suggestion(sample)) + )] + SingleConstructorExpect { + #[label("use let")] + location: Span, + #[label("only one constructor")] + pattern_location: Span, + #[label("is not {}", "Data".purple())] + value_location: Span, + sample: UntypedExpr, + }, + #[error("I found a todo left in the code.\n")] #[diagnostic(help("You probably want to replace that one with real code... eventually."))] #[diagnostic(code("todo"))] diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index fd0915fb..919be51f 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -817,15 +817,18 @@ impl<'a, 'b> ExprTyper<'a, 'b> { fn infer_assignment( &mut self, - pattern: UntypedPattern, - value: UntypedExpr, + untyped_pattern: UntypedPattern, + untyped_value: UntypedExpr, kind: AssignmentKind, annotation: &Option, location: Span, ) -> Result { - let typed_value = self.in_new_scope(|value_typer| value_typer.infer(value.clone()))?; + let typed_value = + self.in_new_scope(|value_typer| value_typer.infer(untyped_value.clone()))?; let mut value_typ = typed_value.tipo(); + let value_is_data = value_typ.is_data(); + // Check that any type annotation is accurate. let pattern = if let Some(ann) = annotation { let ann_typ = self @@ -836,25 +839,25 @@ impl<'a, 'b> ExprTyper<'a, 'b> { ann_typ.clone(), value_typ.clone(), typed_value.type_defining_location(), - (kind.is_let() && ann_typ.is_data()) || (kind.is_assert() && value_typ.is_data()), + (kind.is_let() && ann_typ.is_data()) || (kind.is_expect() && value_is_data), )?; value_typ = ann_typ.clone(); // Ensure the pattern matches the type of the value PatternTyper::new(self.environment, &self.hydrator).unify( - pattern, + untyped_pattern.clone(), value_typ.clone(), Some(ann_typ), )? } else { - if value_typ.is_data() && !pattern.is_var() && !pattern.is_discard() { + if value_is_data && !untyped_pattern.is_var() && !untyped_pattern.is_discard() { return Err(Error::CastDataNoAnn { location, value: UntypedExpr::Assignment { location, - value: value.into(), - pattern, + value: untyped_value.into(), + pattern: untyped_pattern, kind, annotation: Some(Annotation::Constructor { location: Span::empty(), @@ -868,7 +871,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { // Ensure the pattern matches the type of the value PatternTyper::new(self.environment, &self.hydrator).unify( - pattern, + untyped_pattern.clone(), value_typ.clone(), None, )? @@ -888,6 +891,33 @@ impl<'a, 'b> ExprTyper<'a, 'b> { unmatched, }); } + } else if !value_is_data + && self + .environment + .check_exhaustiveness( + vec![pattern.clone()], + collapse_links(value_typ.clone()), + location, + ) + .is_ok() + { + self.environment + .warnings + .push(Warning::SingleConstructorExpect { + location: Span { + start: location.start, + end: location.start + 6, + }, + pattern_location: dbg!(untyped_pattern.location()), + value_location: dbg!(untyped_value.location()), + sample: UntypedExpr::Assignment { + location: Span::empty(), + value: Box::new(untyped_value), + pattern: untyped_pattern, + kind: AssignmentKind::Let, + annotation: None, + }, + }) } Ok(TypedExpr::Assignment { From efbc3abb952f8af6f3eafe412e3dc3ba33b9848e Mon Sep 17 00:00:00 2001 From: Matthias Benkort <5680256+KtorZ@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:23:46 +0100 Subject: [PATCH 08/10] Delete tests.ak --- examples/acceptance_tests/047/lib/tests.ak | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/acceptance_tests/047/lib/tests.ak diff --git a/examples/acceptance_tests/047/lib/tests.ak b/examples/acceptance_tests/047/lib/tests.ak deleted file mode 100644 index 8b137891..00000000 --- a/examples/acceptance_tests/047/lib/tests.ak +++ /dev/null @@ -1 +0,0 @@ - From b803332f9918a345d902b43c35c693f2cfc97c05 Mon Sep 17 00:00:00 2001 From: Matthias Benkort <5680256+KtorZ@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:25:33 +0100 Subject: [PATCH 09/10] Update crates/aiken-lang/src/tipo/error.rs --- crates/aiken-lang/src/tipo/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 5a24446b..c481ffbf 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1086,7 +1086,7 @@ pub enum Warning { location: Span, #[label("only one constructor")] pattern_location: Span, - #[label("is not {}", "Data".purple())] + #[label("is not Data")] value_location: Span, sample: UntypedExpr, }, From 461aaf14bd5c9b6df641f5a4106529b997b3f7cb Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 9 Feb 2023 09:16:54 -0500 Subject: [PATCH 10/10] chore: no more loose numbers for location offsets --- crates/aiken-lang/src/ast.rs | 7 +++++++ crates/aiken-lang/src/tipo/expr.rs | 2 +- crates/aiken-lang/src/tipo/infer.rs | 10 ++++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 54c944e9..b1d2a7c2 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -777,6 +777,13 @@ impl AssignmentKind { pub fn is_expect(&self) -> bool { matches!(self, AssignmentKind::Expect) } + + pub fn location_offset(&self) -> usize { + match self { + AssignmentKind::Let => 3, + AssignmentKind::Expect => 6, + } + } } pub type MultiPattern = Vec>; diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 919be51f..cb745d35 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -906,7 +906,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { .push(Warning::SingleConstructorExpect { location: Span { start: location.start, - end: location.start + 6, + end: kind.location_offset(), }, pattern_location: dbg!(untyped_pattern.location()), value_location: dbg!(untyped_value.location()), diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index 771c5d0c..2aead900 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -20,6 +20,8 @@ use super::{ TypeInfo, ValueConstructor, ValueConstructorVariant, }; +const PUB_OFFSET: usize = 3; + impl UntypedModule { pub fn infer( mut self, @@ -162,7 +164,7 @@ fn infer_definition( environment.warnings.push(Warning::PubInValidatorModule { location: Span { start: location.start, - end: location.start + 3, + end: location.start + PUB_OFFSET, }, }) } @@ -270,7 +272,7 @@ fn infer_definition( environment.warnings.push(Warning::PubInValidatorModule { location: Span { start: location.start, - end: location.start + 3, + end: location.start + PUB_OFFSET, }, }) } @@ -306,7 +308,7 @@ fn infer_definition( environment.warnings.push(Warning::PubInValidatorModule { location: Span { start: location.start, - end: location.start + 3, + end: location.start + PUB_OFFSET, }, }) } @@ -449,7 +451,7 @@ fn infer_definition( environment.warnings.push(Warning::PubInValidatorModule { location: Span { start: location.start, - end: location.start + 3, + end: location.start + PUB_OFFSET, }, }) }