diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index 4afa6a9cae..ca0070b326 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -9,7 +9,7 @@ use roc_module::symbol::Symbol; use crate::expr::specialize_equality; use crate::layout::Builtin; use crate::layout::Layout; -use crate::pattern::{Ctor, TagId, Union}; +use crate::pattern::{Ctor, RenderAs, TagId, Union}; /// COMPILE CASES @@ -381,7 +381,9 @@ fn test_at_path<'a>(selected_path: &Path, branch: Branch<'a>, all_tests: &mut Ve } RecordDestructure(destructs, _) => { + // not rendered, so pick the easiest let union = Union { + render_as: RenderAs::Tag, alternatives: vec![Ctor { tag_id: TagId(0), name: TagName::Global("#Record".into()), diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index b0a6900980..224fe6dd42 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -1,5 +1,5 @@ use crate::layout::{Builtin, Layout}; -use crate::pattern::{Ctor, Guard, TagId}; +use crate::pattern::{Ctor, Guard, RenderAs, TagId}; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_can; @@ -1487,6 +1487,7 @@ fn from_can_pattern<'a>( tag_id: 0, tag_name: tag_name.clone(), union: Union { + render_as: RenderAs::Tag, alternatives: vec![Ctor { tag_id: TagId(0), name: tag_name.clone(), @@ -1498,6 +1499,7 @@ fn from_can_pattern<'a>( value: tag_name == &ttrue, tag_name: tag_name.clone(), union: Union { + render_as: RenderAs::Tag, alternatives: vec![ Ctor { tag_id: TagId(0), @@ -1528,6 +1530,7 @@ fn from_can_pattern<'a>( } let union = crate::pattern::Union { + render_as: RenderAs::Tag, alternatives: ctors, }; @@ -1539,6 +1542,7 @@ fn from_can_pattern<'a>( } Unwrapped(field_layouts) => { let union = crate::pattern::Union { + render_as: RenderAs::Tag, alternatives: vec![Ctor { tag_id: TagId(0), name: tag_name.clone(), @@ -1573,6 +1577,7 @@ fn from_can_pattern<'a>( } let union = crate::pattern::Union { + render_as: RenderAs::Tag, alternatives: ctors, }; diff --git a/compiler/mono/src/pattern.rs b/compiler/mono/src/pattern.rs index 5177925456..b6de39d2a6 100644 --- a/compiler/mono/src/pattern.rs +++ b/compiler/mono/src/pattern.rs @@ -1,5 +1,5 @@ use roc_collections::all::{Index, MutMap}; -use roc_module::ident::TagName; +use roc_module::ident::{Lowercase, TagName}; use roc_region::all::{Located, Region}; use self::Pattern::*; @@ -7,6 +7,14 @@ use self::Pattern::*; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Union { pub alternatives: Vec, + pub render_as: RenderAs, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum RenderAs { + Tag, + Record(Vec), + Guard, } #[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)] @@ -52,7 +60,20 @@ fn simplify<'a>(pattern: &crate::expr::Pattern<'a>) -> Pattern { Identifier(_) => Anything, RecordDestructure(destructures, _) => { let tag_id = TagId(0); + let mut patterns = std::vec::Vec::with_capacity(destructures.len()); + let mut field_names = std::vec::Vec::with_capacity(destructures.len()); + + for destruct in destructures { + field_names.push(destruct.label.clone()); + + match &destruct.guard { + None => patterns.push(Anything), + Some(guard) => patterns.push(simplify(guard)), + } + } + let union = Union { + render_as: RenderAs::Record(field_names), alternatives: vec![Ctor { name: TagName::Global("#Record".into()), tag_id, @@ -60,15 +81,6 @@ fn simplify<'a>(pattern: &crate::expr::Pattern<'a>) -> Pattern { }], }; - let mut patterns = std::vec::Vec::with_capacity(destructures.len()); - - for destruct in destructures { - match &destruct.guard { - None => patterns.push(Anything), - Some(guard) => patterns.push(simplify(guard)), - } - } - Ctor(union, tag_id, patterns) } @@ -306,6 +318,7 @@ fn to_nonredundant_rows<'a>( let tag_id = TagId(0); let union = Union { + render_as: RenderAs::Guard, alternatives: vec![Ctor { tag_id, name: TagName::Global("#Guard".into()), diff --git a/compiler/reporting/src/error/mono.rs b/compiler/reporting/src/error/mono.rs index 6e7cfac805..65dc625652 100644 --- a/compiler/reporting/src/error/mono.rs +++ b/compiler/reporting/src/error/mono.rs @@ -133,6 +133,7 @@ fn pattern_to_doc_help<'b>( ) -> RocDocBuilder<'b> { use roc_mono::pattern::Literal::*; use roc_mono::pattern::Pattern::*; + use roc_mono::pattern::RenderAs; match pattern { Anything => alloc.text("_"), @@ -145,26 +146,55 @@ fn pattern_to_doc_help<'b>( Str(s) => alloc.string(s.into()), }, Ctor(union, tag_id, args) => { - let has_args = !args.is_empty(); - let arg_docs = args - .into_iter() - .map(|v| pattern_to_doc_help(alloc, v, true)); + match union.render_as { + RenderAs::Guard => panic!("can this happen? inform Folkert"), + RenderAs::Record(field_names) => { + let mut arg_docs = Vec::with_capacity(args.len()); - let tag = &union.alternatives[tag_id.0 as usize]; - let tag_name = tag.name.clone(); + for (label, v) in field_names.into_iter().zip(args.into_iter()) { + match &v { + Anything => { + arg_docs.push(alloc.text(label.to_string())); + } + Literal(_) | Ctor(_, _, _) => { + arg_docs.push( + alloc + .text(label.to_string()) + .append(alloc.reflow(": ")) + .append(pattern_to_doc_help(alloc, v, false)), + ); + } + } + } - // We assume the alternatives are sorted. If not, this assert will trigger - debug_assert!(tag_id == tag.tag_id); + alloc + .text("{ ") + .append(alloc.intersperse(arg_docs, alloc.reflow(", "))) + .append(" }") + } + RenderAs::Tag => { + let has_args = !args.is_empty(); + let arg_docs = args + .into_iter() + .map(|v| pattern_to_doc_help(alloc, v, true)); - let docs = std::iter::once(alloc.tag_name(tag_name)).chain(arg_docs); + let tag = &union.alternatives[tag_id.0 as usize]; + let tag_name = tag.name.clone(); - if in_type_param && has_args { - alloc - .text("(") - .append(alloc.intersperse(docs, alloc.space())) - .append(")") - } else { - alloc.intersperse(docs, alloc.space()) + // We assume the alternatives are sorted. If not, this assert will trigger + debug_assert!(tag_id == tag.tag_id); + + let docs = std::iter::once(alloc.tag_name(tag_name)).chain(arg_docs); + + if in_type_param && has_args { + alloc + .text("(") + .append(alloc.intersperse(docs, alloc.space())) + .append(")") + } else { + alloc.intersperse(docs, alloc.space()) + } + } } } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 0ac724f127..23225fffef 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -2254,6 +2254,7 @@ mod test_reporting { { a: 4 } -> 4 "# ), + // Hint: Looks like a record field guard is not exhaustive. Learn more about record pattern matches at TODO. indoc!( r#" -- UNSAFE PATTERN -------------------------------------------------------------- @@ -2265,7 +2266,7 @@ mod test_reporting { Other possibilities include: - #Record _ + { a } I would have to crash if I saw one of those! Add branches for them! "# @@ -2299,7 +2300,7 @@ mod test_reporting { Other possibilities include: - #Record (Just _) _ + { a: Just _, b } I would have to crash if I saw one of those! Add branches for them! "#