diff --git a/cli/tests/snapshot/inputs/errors/let_rec_block_with_pat.ncl b/cli/tests/snapshot/inputs/errors/let_rec_block_with_pat.ncl deleted file mode 100644 index eda9309a50..0000000000 --- a/cli/tests/snapshot/inputs/errors/let_rec_block_with_pat.ncl +++ /dev/null @@ -1,7 +0,0 @@ -# capture = 'stderr' -# command = ['eval'] -let rec - a = 2, - { b } = { b = 1 } -in -b diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_destructuring_closed_fail.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_destructuring_closed_fail.ncl.snap index eb3c6adbf7..0fd4c24564 100644 --- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_destructuring_closed_fail.ncl.snap +++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_destructuring_closed_fail.ncl.snap @@ -2,11 +2,10 @@ source: cli/tests/snapshot/main.rs expression: err --- -error: unmatched pattern +error: destructuring failed ┌─ [INPUTS_PATH]/errors/destructuring_closed_fail.ncl:3:5 │ 3 │ let {a} = {a=1, b=2} - │ ^^^^^^^^^^^^^^^^ - │ │ │ - │ │ this value doesn't match any branch - │ in this match expression + │ ^^^ ---------- this value failed to match + │ │ + │ this pattern diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_let_rec_block_with_pat.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_let_rec_block_with_pat.ncl.snap deleted file mode 100644 index 6f7be5b0be..0000000000 --- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_let_rec_block_with_pat.ncl.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: cli/tests/snapshot/main.rs -expression: err ---- -error: recursive destructuring is not supported - ┌─ [INPUTS_PATH]/errors/let_rec_block_with_pat.ncl:3:5 - │ -3 │ let rec - │ ^^^ - │ - = A destructuring let-binding can't be recursive. Try removing the `rec` from `let rec`. - = You can reference other fields of a record recursively from within a field, so you might not need the recursive let. diff --git a/core/src/error/mod.rs b/core/src/error/mod.rs index dcb2d12252..27a26740dc 100644 --- a/core/src/error/mod.rs +++ b/core/src/error/mod.rs @@ -27,7 +27,7 @@ use crate::{ position::{RawSpan, TermPos}, repl, serialize::{ExportFormat, NickelPointer}, - term::{record::FieldMetadata, Number, RichTerm, Term}, + term::{pattern::Pattern, record::FieldMetadata, Number, RichTerm, Term}, typ::{EnumRow, RecordRow, Type, TypeF, VarKindDiscriminant}, }; @@ -168,6 +168,12 @@ pub enum EvalError { /// The position of the `match` expression pos: TermPos, }, + FailedDestructuring { + /// The original term matched. + value: RichTerm, + /// The pattern that failed to match. + pattern: Pattern, + }, /// Tried to query a field of something that wasn't a record. QueryNonRecord { /// Position of the original unevaluated expression. @@ -1468,6 +1474,20 @@ impl IntoDiagnostics for EvalError { .with_message("unmatched pattern") .with_labels(labels)] } + EvalError::FailedDestructuring { value, pattern } => { + let mut labels = Vec::new(); + + if let Some(span) = pattern.pos.into_opt() { + labels.push(primary(&span).with_message("this pattern")); + } + + labels + .push(secondary_term(&value, files).with_message("this value failed to match")); + + vec![Diagnostic::error() + .with_message("destructuring failed") + .with_labels(labels)] + } EvalError::IllegalPolymorphicTailAccess { action, label: contract_label, diff --git a/core/src/parser/grammar.lalrpop b/core/src/parser/grammar.lalrpop index 366fe7bd53..991673ec80 100644 --- a/core/src/parser/grammar.lalrpop +++ b/core/src/parser/grammar.lalrpop @@ -232,11 +232,11 @@ UniTerm: UniTerm = { InfixExpr, AnnotatedInfixExpr, AsUniTerm, - "let" + "let" ",")*> ","? "in" =>? { bindings.push(last); - Ok(UniTerm::from(mk_let(recursive.is_some(), bindings, body, mk_span(src_id, l, r))?)) + Ok(UniTerm::from(mk_let(recursive.is_some(), bindings, body)?)) }, "fun" "=>" => { let pos = mk_pos(src_id, l, r); diff --git a/core/src/parser/utils.rs b/core/src/parser/utils.rs index f5040dec1a..4ac5b0ca17 100644 --- a/core/src/parser/utils.rs +++ b/core/src/parser/utils.rs @@ -630,14 +630,11 @@ pub fn mk_merge_label(src_id: FileId, l: usize, r: usize) -> MergeLabel { } /// Generate a `Let` or a `LetPattern` (depending on whether there's a binding -/// with a record pattern) from the parsing of a let definition. This function -/// fails if the definition has both a pattern and is recursive because -/// recursive let-patterns are currently not supported. +/// with a record pattern) from the parsing of a let definition. pub fn mk_let( rec: bool, bindings: Vec, body: RichTerm, - span: RawSpan, ) -> Result { let all_simple = bindings .iter() @@ -678,10 +675,9 @@ pub fn mk_let( }), body, )) - } else if rec { - Err(ParseError::RecursiveLetPattern(span)) } else { Ok(mk_term::let_pat_in( + rec, bindings.into_iter().map(|mut b| { if let Some(ann) = b.annot { b.value = ann.annotation.attach_term(b.value); diff --git a/core/src/pretty.rs b/core/src/pretty.rs index 86495491da..97f2101bd6 100644 --- a/core/src/pretty.rs +++ b/core/src/pretty.rs @@ -864,9 +864,10 @@ where Lbl(_lbl) => allocator.text("%