diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index ca999f2f96..af00476c12 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -420,13 +420,13 @@ fn test_at_path<'a>(selected_path: &Path, branch: Branch<'a>, all_tests: &mut Ve arguments: arguments.to_vec(), }); } - BitLiteral(v) => { - all_tests.push(IsBit(*v)); + BitLiteral { value, .. } => { + all_tests.push(IsBit(*value)); } - EnumLiteral { tag_id, enum_size } => { + EnumLiteral { tag_id, union, .. } => { all_tests.push(IsByte { tag_id: *tag_id, - num_alts: *enum_size as usize, + num_alts: union.alternatives.len(), }); } IntLiteral(v) => { @@ -629,7 +629,7 @@ fn to_relevant_branch_help<'a>( _ => None, }, - BitLiteral(bit) => match test { + BitLiteral { value: bit, .. } => match test { IsBit(test_bit) if bit == *test_bit => { start.extend(end); Some(Branch { @@ -714,7 +714,7 @@ fn needs_tests<'a>(pattern: &Pattern<'a>) -> bool { RecordDestructure(_, _) | AppliedTag { .. } - | BitLiteral(_) + | BitLiteral { .. } | EnumLiteral { .. } | IntLiteral(_) | FloatLiteral(_) diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index 17dff1980c..45b33c5193 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -854,7 +854,7 @@ fn store_pattern<'a>( // Since _ is never read, it's safe to reassign it. // stored.push((Symbol::UNDERSCORE, layout, Expr::Load(outer_symbol))) } - IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {} + IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {} AppliedTag { union, arguments, .. } => { @@ -886,7 +886,7 @@ fn store_pattern<'a>( Underscore => { // ignore } - IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {} + IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {} _ => { // store the field in a symbol, and continue matching on it let symbol = env.fresh_symbol(); @@ -963,7 +963,7 @@ fn store_record_destruct<'a>( // // internally. But `y` is never used, so we must make sure it't not stored/loaded. } - IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {} + IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } => {} _ => { let symbol = env.fresh_symbol(); stored.push((symbol, destruct.layout.clone(), load)); @@ -1416,10 +1416,15 @@ pub enum Pattern<'a> { IntLiteral(i64), FloatLiteral(u64), - BitLiteral(bool), + BitLiteral { + value: bool, + tag_name: TagName, + union: crate::pattern::Union, + }, EnumLiteral { tag_id: u8, - enum_size: u8, + tag_name: TagName, + union: crate::pattern::Union, }, StrLiteral(Box), @@ -1479,6 +1484,7 @@ fn from_can_pattern<'a>( .. } => { use crate::layout::UnionVariant::*; + use crate::pattern::Union; let variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.pointer_size); @@ -1487,18 +1493,52 @@ fn from_can_pattern<'a>( Never => unreachable!("there is no pattern of type `[]`"), Unit => Pattern::EnumLiteral { tag_id: 0, - enum_size: 1, + tag_name: tag_name.clone(), + union: Union { + alternatives: vec![Ctor { + name: tag_name.clone(), + arity: 0, + }], + }, + }, + BoolUnion { ttrue, ffalse } => Pattern::BitLiteral { + value: tag_name == &ttrue, + tag_name: tag_name.clone(), + union: Union { + alternatives: vec![ + Ctor { + name: ttrue, + arity: 0, + }, + Ctor { + name: ffalse, + arity: 0, + }, + ], + }, }, - BoolUnion { ttrue, .. } => Pattern::BitLiteral(tag_name == &ttrue), ByteUnion(tag_names) => { let tag_id = tag_names .iter() .position(|key| key == tag_name) .expect("tag must be in its own type"); + let mut ctors = std::vec::Vec::with_capacity(tag_names.len()); + for tag_name in &tag_names { + ctors.push(Ctor { + name: tag_name.clone(), + arity: 0, + }) + } + + let union = crate::pattern::Union { + alternatives: ctors, + }; + Pattern::EnumLiteral { tag_id: tag_id as u8, - enum_size: tag_names.len() as u8, + tag_name: tag_name.clone(), + union, } } Unwrapped(field_layouts) => { diff --git a/compiler/mono/src/pattern.rs b/compiler/mono/src/pattern.rs index 59d7707262..96a3e26b0a 100644 --- a/compiler/mono/src/pattern.rs +++ b/compiler/mono/src/pattern.rs @@ -42,33 +42,12 @@ fn simplify<'a>(pattern: &crate::expr::Pattern<'a>) -> Pattern { // To make sure these are exhaustive, we have to "fake" a union here // TODO: use the hash or some other integer to discriminate between constructors - BitLiteral(b) => { - let union = Union { - alternatives: vec![ - Ctor { - name: TagName::Global("False".into()), - arity: 0, - }, - Ctor { - name: TagName::Global("True".into()), - arity: 0, - }, - ], - }; - - Ctor(union, TagName::Global(format!("{}", b).into()), vec![]) - } - EnumLiteral { tag_id, enum_size } => { - let alternatives = (0..*enum_size) - .map(|id| Ctor { - name: TagName::Global(format!("{}", id).into()), - arity: 0, - }) - .collect(); - - let union = Union { alternatives }; - Ctor(union, TagName::Global(format!("{}", tag_id).into()), vec![]) - } + BitLiteral { + tag_name, union, .. + } => Ctor(union.clone(), tag_name.clone(), vec![]), + EnumLiteral { + tag_name, union, .. + } => Ctor(union.clone(), tag_name.clone(), vec![]), Underscore => Anything, Identifier(_) => Anything, diff --git a/compiler/reporting/src/report.rs b/compiler/reporting/src/report.rs index a43037621e..875bd6dd11 100644 --- a/compiler/reporting/src/report.rs +++ b/compiler/reporting/src/report.rs @@ -155,7 +155,7 @@ pub fn mono_problem<'b>( PatternProblem(Incomplete(region, context, missing)) => match context { BadArg => { let doc = alloc.stack(vec![ - alloc.reflow("This pattern does not cover all the possibilities"), + alloc.reflow("This pattern does not cover all the possibilities:"), alloc.region(region), alloc.reflow("Other possibilities include:"), unhandled_patterns_to_doc_block(alloc, missing), @@ -177,7 +177,7 @@ pub fn mono_problem<'b>( } BadDestruct => { let doc = alloc.stack(vec![ - alloc.reflow("This pattern does not cover all the possibilities"), + alloc.reflow("This pattern does not cover all the possibilities:"), alloc.region(region), alloc.reflow("Other possibilities include:"), unhandled_patterns_to_doc_block(alloc, missing), @@ -203,7 +203,7 @@ pub fn mono_problem<'b>( alloc.concat(vec![ alloc.reflow("This "), alloc.keyword("when"), - alloc.reflow(" does not cover all the possibilities"), + alloc.reflow(" does not cover all the possibilities:"), ]), alloc.region(region), alloc.reflow("Other possibilities include:"), diff --git a/compiler/reporting/src/type_error.rs b/compiler/reporting/src/type_error.rs index 2956a84a25..36db385824 100644 --- a/compiler/reporting/src/type_error.rs +++ b/compiler/reporting/src/type_error.rs @@ -160,7 +160,7 @@ fn to_expr_report<'b>( None => (alloc.text("this"), alloc.nil()), }; - let mut region = None; + let mut sub_region = None; let thing = match annotation_source { TypedIfBranch { @@ -189,7 +189,7 @@ fn to_expr_report<'b>( alloc.text(" expression:"), ]), TypedBody { region: ann_region } => { - region = Some(ann_region); + sub_region = Some(ann_region); alloc.concat(vec![ alloc.text("body of "), the_name_text, @@ -222,9 +222,10 @@ fn to_expr_report<'b>( filename, doc: alloc.stack(vec![ alloc.text("Something is off with the ").append(thing), - match region { + match sub_region { None => alloc.region(expr_region), Some(ann_region) => { + // for typed bodies, include the line(s) have the signature let joined = roc_region::all::Region::span_across(&ann_region, &expr_region); alloc.region_with_subregion(joined, expr_region) diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index ff1ce08f7e..9667497d92 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -2045,7 +2045,7 @@ mod test_reporting { r#" -- UNSAFE PATTERN -------------------------------------------------------------- - This pattern does not cover all the possibilities + This pattern does not cover all the possibilities: 7 ┆ f = \Left v -> v ┆ ^^^^ @@ -2080,7 +2080,7 @@ mod test_reporting { r#" -- UNSAFE PATTERN -------------------------------------------------------------- - This pattern does not cover all the possibilities + This pattern does not cover all the possibilities: 5 ┆ (Left y) = x ┆ ^^^^ @@ -2110,7 +2110,7 @@ mod test_reporting { r#" -- UNSAFE PATTERN -------------------------------------------------------------- - This `when` does not cover all the possibilities + This `when` does not cover all the possibilities: 1 ┆> when 0x1 is 2 ┆> 2 -> 0x3 @@ -2125,6 +2125,72 @@ mod test_reporting { ) } + #[test] + fn patterns_bool_not_exhaustive() { + report_problem_as( + indoc!( + r#" + x : Bool + x = True + + when x is + False -> 3 + "# + ), + // should not give errors + indoc!( + r#" + -- UNSAFE PATTERN -------------------------------------------------------------- + + This `when` does not cover all the possibilities: + + 4 ┆> when x is + 5 ┆> False -> 3 + + Other possibilities include: + + True + + I would have to crash if I saw one of those! Add branches for them! + "# + ), + ) + } + + #[test] + fn patterns_enum_not_exhaustive() { + report_problem_as( + indoc!( + r#" + x : [ Red, Green, Blue ] + x = Red + + when x is + Red -> 0 + Green -> 1 + "# + ), + // should not give errors + indoc!( + r#" + -- UNSAFE PATTERN -------------------------------------------------------------- + + This `when` does not cover all the possibilities: + + 4 ┆> when x is + 5 ┆> Red -> 0 + 6 ┆> Green -> 1 + + Other possibilities include: + + Blue + + I would have to crash if I saw one of those! Add branches for them! + "# + ), + ) + } + #[test] fn patterns_int_redundant() { report_problem_as(