From 6ce30bd949e9751cbf501018d26e8d9d934e03bb Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 27 Nov 2023 23:11:17 -0500 Subject: [PATCH] fix: allow spread operator on positional constructors closes #677 --- crates/aiken-lang/src/tests/check.rs | 40 +++++++++++++++++++++++++++ crates/aiken-lang/src/tipo/pattern.rs | 31 +++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index 21ec7311..6583ebdd 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -981,6 +981,46 @@ fn list_pattern_6() { assert!(check(parse(source_code)).is_ok()) } +#[test] +fn spread_with_positional_constr_args() { + let source_code = r#" + type Redeemer { + First(Int) + Second + } + + fn foo(redeemer: Redeemer) { + when redeemer is { + First(..) -> True + Second -> True + } + } + "#; + assert!(check(parse(source_code)).is_ok()) +} + +#[test] +fn unnecessary_spread_with_positional_constr_args() { + let source_code = r#" + type Redeemer { + First(Int) + Second + } + + fn foo(redeemer: Redeemer) { + when redeemer is { + First(x, ..) -> True + Second -> True + } + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err((_, Error::UnnecessarySpreadOperator { .. })) + )) +} + #[test] fn trace_strings() { let source_code = r#" diff --git a/crates/aiken-lang/src/tipo/pattern.rs b/crates/aiken-lang/src/tipo/pattern.rs index e8da2cff..5c87bce3 100644 --- a/crates/aiken-lang/src/tipo/pattern.rs +++ b/crates/aiken-lang/src/tipo/pattern.rs @@ -314,6 +314,8 @@ impl<'a, 'b> PatternTyper<'a, 'b> { self.environment .get_value_constructor(module.as_ref(), &name, location)?; + let has_no_fields = cons.field_map().is_none(); + match cons.field_map() { // The fun has a field map so labelled arguments may be present and need to be reordered. Some(field_map) => { @@ -399,8 +401,37 @@ impl<'a, 'b> PatternTyper<'a, 'b> { &mut HashMap::new(), self.hydrator, ); + match instantiated_constructor_type.deref() { Type::Fn { args, ret } => { + if with_spread && has_no_fields { + if pattern_args.len() == args.len() { + return Err(Error::UnnecessarySpreadOperator { + location: Span { + start: location.end - 3, + end: location.end - 1, + }, + arity: args.len(), + }); + } + + while pattern_args.len() < args.len() { + let location = Span { + start: location.end - 3, + end: location.end - 1, + }; + + pattern_args.push(CallArg { + value: Pattern::Discard { + name: "_".to_string(), + location, + }, + location, + label: None, + }); + } + } + if args.len() == pattern_args.len() { let pattern_args = pattern_args .into_iter()