From b9a29bdb16acb82d821fc2634a3332ac839314c0 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sun, 27 Feb 2022 20:28:25 -0500 Subject: [PATCH 1/3] Make exhaustive checking its own crate Prepares for moving exhaustiveness checking to the closer typechecking phase. Doing this one step at a time so that git preserves the rename. --- Cargo.lock | 12 + Cargo.toml | 1 + compiler/exhaustive/Cargo.toml | 12 + .../exhaustive.rs => exhaustive/src/lib.rs} | 207 ++---------------- compiler/module/src/ident.rs | 2 + compiler/mono/Cargo.toml | 1 + compiler/mono/src/decision_tree.rs | 4 +- compiler/mono/src/exhaustive_wrap.rs | 199 +++++++++++++++++ compiler/mono/src/ir.rs | 48 ++-- compiler/mono/src/lib.rs | 2 +- reporting/Cargo.toml | 1 + reporting/src/error/mono.rs | 16 +- 12 files changed, 277 insertions(+), 228 deletions(-) create mode 100644 compiler/exhaustive/Cargo.toml rename compiler/{mono/src/exhaustive.rs => exhaustive/src/lib.rs} (66%) create mode 100644 compiler/mono/src/exhaustive_wrap.rs diff --git a/Cargo.lock b/Cargo.lock index 6f5be90118..6409366ecb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3502,6 +3502,16 @@ dependencies = [ name = "roc_error_macros" version = "0.1.0" +[[package]] +name = "roc_exhaustive" +version = "0.1.0" +dependencies = [ + "roc_collections", + "roc_module", + "roc_region", + "roc_std", +] + [[package]] name = "roc_fmt" version = "0.1.0" @@ -3651,6 +3661,7 @@ dependencies = [ "roc_can", "roc_collections", "roc_error_macros", + "roc_exhaustive", "roc_module", "roc_problem", "roc_region", @@ -3772,6 +3783,7 @@ dependencies = [ "roc_can", "roc_collections", "roc_constrain", + "roc_exhaustive", "roc_module", "roc_mono", "roc_parse", diff --git a/Cargo.toml b/Cargo.toml index 874eeb705d..bbca25568c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "compiler/ident", "compiler/region", "compiler/collections", + "compiler/exhaustive", "compiler/module", "compiler/parse", "compiler/can", diff --git a/compiler/exhaustive/Cargo.toml b/compiler/exhaustive/Cargo.toml new file mode 100644 index 0000000000..7fa1a63bda --- /dev/null +++ b/compiler/exhaustive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "roc_exhaustive" +version = "0.1.0" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" + +[dependencies] +roc_collections = { path = "../collections" } +roc_region = { path = "../region" } +roc_module = { path = "../module" } +roc_std = { path = "../../roc_std", default-features = false } diff --git a/compiler/mono/src/exhaustive.rs b/compiler/exhaustive/src/lib.rs similarity index 66% rename from compiler/mono/src/exhaustive.rs rename to compiler/exhaustive/src/lib.rs index c64ee79e06..2edcbc0377 100644 --- a/compiler/mono/src/exhaustive.rs +++ b/compiler/exhaustive/src/lib.rs @@ -1,7 +1,6 @@ -use crate::{ir::DestructType, layout::TagIdIntType}; use roc_collections::all::{Index, MutMap}; -use roc_module::ident::{Lowercase, TagName}; -use roc_region::all::{Loc, Region}; +use roc_module::ident::{Lowercase, TagIdIntType, TagName}; +use roc_region::all::Region; use roc_std::RocDec; use self::Pattern::*; @@ -63,96 +62,6 @@ pub enum Literal { Str(Box), } -fn simplify(pattern: &crate::ir::Pattern) -> Pattern { - use crate::ir::Pattern::*; - - match pattern { - IntLiteral(v, _) => Literal(Literal::Int(*v)), - U128Literal(v) => Literal(Literal::U128(*v)), - FloatLiteral(v, _) => Literal(Literal::Float(*v)), - DecimalLiteral(v) => Literal(Literal::Decimal(*v)), - StrLiteral(v) => Literal(Literal::Str(v.clone())), - - // To make sure these are exhaustive, we have to "fake" a union here - BitLiteral { value, union, .. } => { - Ctor(union.clone(), TagId(*value as TagIdIntType), vec![]) - } - EnumLiteral { tag_id, union, .. } => { - Ctor(union.clone(), TagId(*tag_id as TagIdIntType), vec![]) - } - - Underscore => Anything, - 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.typ { - DestructType::Required(_) => patterns.push(Anything), - DestructType::Guard(guard) => patterns.push(simplify(guard)), - } - } - - let union = Union { - render_as: RenderAs::Record(field_names), - alternatives: vec![Ctor { - name: TagName::Global("#Record".into()), - tag_id, - arity: destructures.len(), - }], - }; - - Ctor(union, tag_id, patterns) - } - - NewtypeDestructure { - arguments, - tag_name, - } => { - let tag_id = 0; - let simplified_args: std::vec::Vec<_> = - arguments.iter().map(|v| simplify(&v.0)).collect(); - Ctor( - Union::newtype_wrapper(tag_name.clone(), arguments.len()), - TagId(tag_id), - simplified_args, - ) - } - - AppliedTag { - tag_id, - arguments, - union, - .. - } => { - let simplified_args: std::vec::Vec<_> = - arguments.iter().map(|v| simplify(&v.0)).collect(); - Ctor(union.clone(), TagId(*tag_id), simplified_args) - } - - OpaqueUnwrap { opaque, argument } => { - let (argument, _) = &(**argument); - - let tag_id = TagId(0); - - let union = Union { - render_as: RenderAs::Opaque, - alternatives: vec![Ctor { - name: TagName::Private(*opaque), - tag_id, - arity: 1, - }], - }; - - Ctor(union, tag_id, vec![simplify(argument)]) - } - } -} - /// Error #[derive(Clone, Debug, PartialEq)] @@ -180,42 +89,22 @@ pub enum Guard { /// Check -pub fn check( +pub fn check<'a>( region: Region, - patterns: &[(Loc, Guard)], context: Context, + matrix: Vec>, ) -> Result<(), Vec> { let mut errors = Vec::new(); - check_patterns(region, context, patterns, &mut errors); - - if errors.is_empty() { - Ok(()) - } else { - Err(errors) - } -} - -pub fn check_patterns<'a>( - region: Region, - context: Context, - patterns: &[(Loc>, Guard)], - errors: &mut Vec, -) { - match to_nonredundant_rows(region, patterns) { - Err(err) => errors.push(err), - Ok(matrix) => { - let bad_patterns = is_exhaustive(&matrix, 1); - if !bad_patterns.is_empty() { - // TODO i suspect this is like a concat in in practice? code below can panic - // if this debug_assert! ever fails, the theory is disproven - debug_assert!( - bad_patterns.iter().map(|v| v.len()).sum::() == bad_patterns.len() - ); - let heads = bad_patterns.into_iter().map(|mut v| v.remove(0)).collect(); - errors.push(Error::Incomplete(region, context, heads)); - } - } + let bad_patterns = is_exhaustive(&matrix, 1); + if !bad_patterns.is_empty() { + // TODO i suspect this is like a concat in in practice? code below can panic + // if this debug_assert! ever fails, the theory is disproven + debug_assert!(bad_patterns.iter().map(|v| v.len()).sum::() == bad_patterns.len()); + let heads = bad_patterns.into_iter().map(|mut v| v.remove(0)).collect(); + errors.push(Error::Incomplete(region, context, heads)); + return Err(errors); } + Ok(()) } /// EXHAUSTIVE PATTERNS @@ -326,76 +215,8 @@ fn recover_ctor( rest } -/// REDUNDANT PATTERNS - -/// INVARIANT: Produces a list of rows where (forall row. length row == 1) -fn to_nonredundant_rows( - overall_region: Region, - patterns: &[(Loc, Guard)], -) -> Result>, Error> { - let mut checked_rows = Vec::with_capacity(patterns.len()); - - // If any of the branches has a guard, e.g. - // - // when x is - // y if y < 10 -> "foo" - // _ -> "bar" - // - // then we treat it as a pattern match on the pattern and a boolean, wrapped in the #Guard - // constructor. We can use this special constructor name to generate better error messages. - // This transformation of the pattern match only works because we only report exhaustiveness - // errors: the Pattern created in this file is not used for code gen. - // - // when x is - // #Guard y True -> "foo" - // #Guard _ _ -> "bar" - let any_has_guard = patterns.iter().any(|(_, guard)| guard == &Guard::HasGuard); - - for (loc_pat, guard) in patterns { - let region = loc_pat.region; - - let next_row = if any_has_guard { - let guard_pattern = match guard { - Guard::HasGuard => Pattern::Literal(Literal::Bit(true)), - Guard::NoGuard => Pattern::Anything, - }; - - let tag_id = TagId(0); - - let union = Union { - render_as: RenderAs::Guard, - alternatives: vec![Ctor { - tag_id, - name: TagName::Global("#Guard".into()), - arity: 2, - }], - }; - - vec![Pattern::Ctor( - union, - tag_id, - vec![simplify(&loc_pat.value), guard_pattern], - )] - } else { - vec![simplify(&loc_pat.value)] - }; - - if matches!(guard, Guard::HasGuard) || is_useful(checked_rows.clone(), next_row.clone()) { - checked_rows.push(next_row); - } else { - return Err(Error::Redundant { - overall_region, - branch_region: region, - index: Index::zero_based(checked_rows.len()), - }); - } - } - - Ok(checked_rows) -} - /// Check if a new row "vector" is useful given previous rows "matrix" -fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool { +pub fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool { let mut matrix = Vec::with_capacity(old_matrix.len()); // this loop ping-pongs the rows between old_matrix and matrix diff --git a/compiler/module/src/ident.rs b/compiler/module/src/ident.rs index 73a5fc9737..3f3c8d51f1 100644 --- a/compiler/module/src/ident.rs +++ b/compiler/module/src/ident.rs @@ -40,6 +40,8 @@ pub struct Uppercase(IdentStr); #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] pub struct ForeignSymbol(IdentStr); +pub type TagIdIntType = u16; + #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum TagName { /// Global tags have no module, but tend to be short strings (since they're diff --git a/compiler/mono/Cargo.toml b/compiler/mono/Cargo.toml index dd07c5f107..d47714ebdd 100644 --- a/compiler/mono/Cargo.toml +++ b/compiler/mono/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] roc_collections = { path = "../collections" } +roc_exhaustive = { path = "../exhaustive" } roc_region = { path = "../region" } roc_module = { path = "../module" } roc_types = { path = "../types" } diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index cd68b13503..d0b1a1dff7 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -1,10 +1,10 @@ -use crate::exhaustive::{Ctor, RenderAs, TagId, Union}; use crate::ir::{ BranchInfo, DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt, }; use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout}; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::{MutMap, MutSet}; +use roc_exhaustive::{Ctor, RenderAs, TagId, Union}; use roc_module::ident::TagName; use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; @@ -83,7 +83,7 @@ enum Test<'a> { IsCtor { tag_id: TagIdIntType, tag_name: TagName, - union: crate::exhaustive::Union, + union: roc_exhaustive::Union, arguments: Vec<(Pattern<'a>, Layout<'a>)>, }, IsInt(i128, IntWidth), diff --git a/compiler/mono/src/exhaustive_wrap.rs b/compiler/mono/src/exhaustive_wrap.rs new file mode 100644 index 0000000000..6890e6a381 --- /dev/null +++ b/compiler/mono/src/exhaustive_wrap.rs @@ -0,0 +1,199 @@ +use crate::ir::DestructType; +use roc_collections::all::Index; +use roc_exhaustive::{ + is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union, +}; +use roc_module::ident::{TagIdIntType, TagName}; +use roc_region::all::{Loc, Region}; + +use Pattern::*; + +fn simplify(pattern: &crate::ir::Pattern) -> Pattern { + use crate::ir::Pattern::*; + + match pattern { + IntLiteral(v, _) => Literal(Literal::Int(*v)), + U128Literal(v) => Literal(Literal::U128(*v)), + FloatLiteral(v, _) => Literal(Literal::Float(*v)), + DecimalLiteral(v) => Literal(Literal::Decimal(*v)), + StrLiteral(v) => Literal(Literal::Str(v.clone())), + + // To make sure these are exhaustive, we have to "fake" a union here + BitLiteral { value, union, .. } => { + Ctor(union.clone(), TagId(*value as TagIdIntType), vec![]) + } + EnumLiteral { tag_id, union, .. } => { + Ctor(union.clone(), TagId(*tag_id as TagIdIntType), vec![]) + } + + Underscore => Anything, + 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.typ { + DestructType::Required(_) => patterns.push(Anything), + DestructType::Guard(guard) => patterns.push(simplify(guard)), + } + } + + let union = Union { + render_as: RenderAs::Record(field_names), + alternatives: vec![Ctor { + name: TagName::Global("#Record".into()), + tag_id, + arity: destructures.len(), + }], + }; + + Ctor(union, tag_id, patterns) + } + + NewtypeDestructure { + arguments, + tag_name, + } => { + let tag_id = 0; + let simplified_args: std::vec::Vec<_> = + arguments.iter().map(|v| simplify(&v.0)).collect(); + Ctor( + Union::newtype_wrapper(tag_name.clone(), arguments.len()), + TagId(tag_id), + simplified_args, + ) + } + + AppliedTag { + tag_id, + arguments, + union, + .. + } => { + let simplified_args: std::vec::Vec<_> = + arguments.iter().map(|v| simplify(&v.0)).collect(); + Ctor(union.clone(), TagId(*tag_id), simplified_args) + } + + OpaqueUnwrap { opaque, argument } => { + let (argument, _) = &(**argument); + + let tag_id = TagId(0); + + let union = Union { + render_as: RenderAs::Opaque, + alternatives: vec![Ctor { + name: TagName::Private(*opaque), + tag_id, + arity: 1, + }], + }; + + Ctor(union, tag_id, vec![simplify(argument)]) + } + } +} + +pub fn check( + region: Region, + patterns: &[(Loc, Guard)], + context: Context, +) -> Result<(), Vec> { + let mut errors = Vec::new(); + check_patterns(region, context, patterns, &mut errors); + + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } +} + +pub fn check_patterns<'a>( + region: Region, + context: Context, + patterns: &[(Loc>, Guard)], + errors: &mut Vec, +) { + match to_nonredundant_rows(region, patterns) { + Err(err) => errors.push(err), + Ok(matrix) => match roc_exhaustive::check(region, context, matrix) { + Err(err) => { + *errors = err; + } + Ok(_) => {} + }, + } +} + +/// REDUNDANT PATTERNS + +/// INVARIANT: Produces a list of rows where (forall row. length row == 1) +fn to_nonredundant_rows( + overall_region: Region, + patterns: &[(Loc, Guard)], +) -> Result>, Error> { + let mut checked_rows = Vec::with_capacity(patterns.len()); + + // If any of the branches has a guard, e.g. + // + // when x is + // y if y < 10 -> "foo" + // _ -> "bar" + // + // then we treat it as a pattern match on the pattern and a boolean, wrapped in the #Guard + // constructor. We can use this special constructor name to generate better error messages. + // This transformation of the pattern match only works because we only report exhaustiveness + // errors: the Pattern created in this file is not used for code gen. + // + // when x is + // #Guard y True -> "foo" + // #Guard _ _ -> "bar" + let any_has_guard = patterns.iter().any(|(_, guard)| guard == &Guard::HasGuard); + + for (loc_pat, guard) in patterns { + let region = loc_pat.region; + + let next_row = if any_has_guard { + let guard_pattern = match guard { + Guard::HasGuard => Pattern::Literal(Literal::Bit(true)), + Guard::NoGuard => Pattern::Anything, + }; + + let tag_id = TagId(0); + + let union = Union { + render_as: RenderAs::Guard, + alternatives: vec![Ctor { + tag_id, + name: TagName::Global("#Guard".into()), + arity: 2, + }], + }; + + vec![Pattern::Ctor( + union, + tag_id, + vec![simplify(&loc_pat.value), guard_pattern], + )] + } else { + vec![simplify(&loc_pat.value)] + }; + + if matches!(guard, Guard::HasGuard) || is_useful(checked_rows.clone(), next_row.clone()) { + checked_rows.push(next_row); + } else { + return Err(Error::Redundant { + overall_region, + branch_region: region, + index: Index::zero_based(checked_rows.len()), + }); + } + } + + Ok(checked_rows) +} diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 8375046869..8212dc7d6b 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1,6 +1,5 @@ #![allow(clippy::manual_map)] -use crate::exhaustive::{Ctor, Guard, RenderAs, TagId}; use crate::layout::{ Builtin, ClosureRepresentation, LambdaSet, Layout, LayoutCache, LayoutProblem, RawFunctionLayout, TagIdIntType, UnionLayout, WrappedVariant, @@ -10,6 +9,7 @@ use bumpalo::Bump; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_can::expr::{ClosureData, IntValue}; use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap}; +use roc_exhaustive::{Ctor, Guard, RenderAs, TagId}; use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::low_level::LowLevel; use roc_module::symbol::{IdentIds, ModuleId, Symbol}; @@ -95,7 +95,7 @@ pub enum OptLevel { #[derive(Clone, Debug, PartialEq)] pub enum MonoProblem { - PatternProblem(crate::exhaustive::Error), + PatternProblem(roc_exhaustive::Error), } #[derive(Debug, Clone, Copy)] @@ -1895,7 +1895,7 @@ fn patterns_to_when<'a>( // see https://github.com/rtfeldman/roc/issues/786 // this must be fixed when moving exhaustiveness checking to the new canonical AST for (pattern_var, pattern) in patterns.into_iter() { - let context = crate::exhaustive::Context::BadArg; + let context = roc_exhaustive::Context::BadArg; let mono_pattern = match from_can_pattern(env, layout_cache, &pattern.value) { Ok((pat, _assignments)) => { // Don't apply any assignments (e.g. to initialize optional variables) yet. @@ -1917,11 +1917,11 @@ fn patterns_to_when<'a>( } }; - match crate::exhaustive::check( + match crate::exhaustive_wrap::check( pattern.region, &[( Loc::at(pattern.region, mono_pattern), - crate::exhaustive::Guard::NoGuard, + roc_exhaustive::Guard::NoGuard, )], context, ) { @@ -3279,12 +3279,12 @@ pub fn with_hole<'a>( } }; - let context = crate::exhaustive::Context::BadDestruct; - match crate::exhaustive::check( + let context = roc_exhaustive::Context::BadDestruct; + match crate::exhaustive_wrap::check( def.loc_pattern.region, &[( Loc::at(def.loc_pattern.region, mono_pattern.clone()), - crate::exhaustive::Guard::NoGuard, + roc_exhaustive::Guard::NoGuard, )], context, ) { @@ -5619,12 +5619,12 @@ pub fn from_can<'a>( hole, ) } else { - let context = crate::exhaustive::Context::BadDestruct; - match crate::exhaustive::check( + let context = roc_exhaustive::Context::BadDestruct; + match crate::exhaustive_wrap::check( def.loc_pattern.region, &[( Loc::at(def.loc_pattern.region, mono_pattern.clone()), - crate::exhaustive::Guard::NoGuard, + roc_exhaustive::Guard::NoGuard, )], context, ) { @@ -5750,11 +5750,11 @@ fn to_opt_branches<'a>( // NOTE exhaustiveness is checked after the construction of all the branches // In contrast to elm (currently), we still do codegen even if a pattern is non-exhaustive. // So we not only report exhaustiveness errors, but also correct them - let context = crate::exhaustive::Context::BadCase; - match crate::exhaustive::check(region, &loc_branches, context) { + let context = roc_exhaustive::Context::BadCase; + match crate::exhaustive_wrap::check(region, &loc_branches, context) { Ok(_) => {} Err(errors) => { - use crate::exhaustive::Error::*; + use roc_exhaustive::Error::*; let mut is_not_exhaustive = false; let mut overlapping_branches = std::vec::Vec::new(); @@ -7657,12 +7657,12 @@ pub enum Pattern<'a> { BitLiteral { value: bool, tag_name: TagName, - union: crate::exhaustive::Union, + union: roc_exhaustive::Union, }, EnumLiteral { tag_id: u8, tag_name: TagName, - union: crate::exhaustive::Union, + union: roc_exhaustive::Union, }, StrLiteral(Box), @@ -7676,7 +7676,7 @@ pub enum Pattern<'a> { tag_id: TagIdIntType, arguments: Vec<'a, (Pattern<'a>, Layout<'a>)>, layout: UnionLayout<'a>, - union: crate::exhaustive::Union, + union: roc_exhaustive::Union, }, OpaqueUnwrap { opaque: Symbol, @@ -7817,8 +7817,8 @@ fn from_can_pattern_help<'a>( arguments, .. } => { - use crate::exhaustive::Union; use crate::layout::UnionVariant::*; + use roc_exhaustive::Union; let res_variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.target_info) @@ -7883,7 +7883,7 @@ fn from_can_pattern_help<'a>( }) } - let union = crate::exhaustive::Union { + let union = roc_exhaustive::Union { render_as: RenderAs::Tag, alternatives: ctors, }; @@ -7974,7 +7974,7 @@ fn from_can_pattern_help<'a>( }) } - let union = crate::exhaustive::Union { + let union = roc_exhaustive::Union { render_as: RenderAs::Tag, alternatives: ctors, }; @@ -8026,7 +8026,7 @@ fn from_can_pattern_help<'a>( }) } - let union = crate::exhaustive::Union { + let union = roc_exhaustive::Union { render_as: RenderAs::Tag, alternatives: ctors, }; @@ -8069,7 +8069,7 @@ fn from_can_pattern_help<'a>( arity: fields.len(), }); - let union = crate::exhaustive::Union { + let union = roc_exhaustive::Union { render_as: RenderAs::Tag, alternatives: ctors, }; @@ -8139,7 +8139,7 @@ fn from_can_pattern_help<'a>( }); } - let union = crate::exhaustive::Union { + let union = roc_exhaustive::Union { render_as: RenderAs::Tag, alternatives: ctors, }; @@ -8194,7 +8194,7 @@ fn from_can_pattern_help<'a>( arity: other_fields.len() - 1, }); - let union = crate::exhaustive::Union { + let union = roc_exhaustive::Union { render_as: RenderAs::Tag, alternatives: ctors, }; diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index b48c0e6fd6..bcbf92107e 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -19,4 +19,4 @@ pub mod tail_recursion; //pub mod decision_tree; pub mod decision_tree; #[allow(clippy::ptr_arg)] -pub mod exhaustive; +pub mod exhaustive_wrap; diff --git a/reporting/Cargo.toml b/reporting/Cargo.toml index 2add55b827..f2f9bf1c57 100644 --- a/reporting/Cargo.toml +++ b/reporting/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] roc_collections = { path = "../compiler/collections" } +roc_exhaustive = { path = "../compiler/exhaustive" } roc_region = { path = "../compiler/region" } roc_module = { path = "../compiler/module" } roc_parse = { path = "../compiler/parse" } diff --git a/reporting/src/error/mono.rs b/reporting/src/error/mono.rs index beac8a2d20..74e241281b 100644 --- a/reporting/src/error/mono.rs +++ b/reporting/src/error/mono.rs @@ -10,8 +10,8 @@ pub fn mono_problem<'b>( filename: PathBuf, problem: roc_mono::ir::MonoProblem, ) -> Report<'b> { - use roc_mono::exhaustive::Context::*; - use roc_mono::exhaustive::Error::*; + use roc_exhaustive::Context::*; + use roc_exhaustive::Error::*; use roc_mono::ir::MonoProblem::*; match problem { @@ -121,7 +121,7 @@ pub fn mono_problem<'b>( pub fn unhandled_patterns_to_doc_block<'b>( alloc: &'b RocDocAllocator<'b>, - patterns: Vec, + patterns: Vec, ) -> RocDocBuilder<'b> { alloc .vcat(patterns.into_iter().map(|v| pattern_to_doc(alloc, v))) @@ -131,19 +131,19 @@ pub fn unhandled_patterns_to_doc_block<'b>( fn pattern_to_doc<'b>( alloc: &'b RocDocAllocator<'b>, - pattern: roc_mono::exhaustive::Pattern, + pattern: roc_exhaustive::Pattern, ) -> RocDocBuilder<'b> { pattern_to_doc_help(alloc, pattern, false) } fn pattern_to_doc_help<'b>( alloc: &'b RocDocAllocator<'b>, - pattern: roc_mono::exhaustive::Pattern, + pattern: roc_exhaustive::Pattern, in_type_param: bool, ) -> RocDocBuilder<'b> { - use roc_mono::exhaustive::Literal::*; - use roc_mono::exhaustive::Pattern::*; - use roc_mono::exhaustive::RenderAs; + use roc_exhaustive::Literal::*; + use roc_exhaustive::Pattern::*; + use roc_exhaustive::RenderAs; match pattern { Anything => alloc.text("_"), From 27a50a3e6b29e1c299598e8e05c6d9e8d1f0b7a7 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sun, 27 Feb 2022 20:30:13 -0500 Subject: [PATCH 2/3] exhaustive_wrap -> exhaustive --- compiler/mono/src/{exhaustive_wrap.rs => exhaustive.rs} | 0 compiler/mono/src/ir.rs | 8 ++++---- compiler/mono/src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename compiler/mono/src/{exhaustive_wrap.rs => exhaustive.rs} (100%) diff --git a/compiler/mono/src/exhaustive_wrap.rs b/compiler/mono/src/exhaustive.rs similarity index 100% rename from compiler/mono/src/exhaustive_wrap.rs rename to compiler/mono/src/exhaustive.rs diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 8212dc7d6b..559b9dae1f 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1917,7 +1917,7 @@ fn patterns_to_when<'a>( } }; - match crate::exhaustive_wrap::check( + match crate::exhaustive::check( pattern.region, &[( Loc::at(pattern.region, mono_pattern), @@ -3280,7 +3280,7 @@ pub fn with_hole<'a>( }; let context = roc_exhaustive::Context::BadDestruct; - match crate::exhaustive_wrap::check( + match crate::exhaustive::check( def.loc_pattern.region, &[( Loc::at(def.loc_pattern.region, mono_pattern.clone()), @@ -5620,7 +5620,7 @@ pub fn from_can<'a>( ) } else { let context = roc_exhaustive::Context::BadDestruct; - match crate::exhaustive_wrap::check( + match crate::exhaustive::check( def.loc_pattern.region, &[( Loc::at(def.loc_pattern.region, mono_pattern.clone()), @@ -5751,7 +5751,7 @@ fn to_opt_branches<'a>( // In contrast to elm (currently), we still do codegen even if a pattern is non-exhaustive. // So we not only report exhaustiveness errors, but also correct them let context = roc_exhaustive::Context::BadCase; - match crate::exhaustive_wrap::check(region, &loc_branches, context) { + match crate::exhaustive::check(region, &loc_branches, context) { Ok(_) => {} Err(errors) => { use roc_exhaustive::Error::*; diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index bcbf92107e..b48c0e6fd6 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -19,4 +19,4 @@ pub mod tail_recursion; //pub mod decision_tree; pub mod decision_tree; #[allow(clippy::ptr_arg)] -pub mod exhaustive_wrap; +pub mod exhaustive; From a62fd31ab64267480e85ea26edb8d269f367ebeb Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sun, 27 Feb 2022 20:41:07 -0500 Subject: [PATCH 3/3] Fix new clippy warnings --- compiler/exhaustive/src/lib.rs | 25 +++++++++++++------------ compiler/mono/src/exhaustive.rs | 7 +++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/exhaustive/src/lib.rs b/compiler/exhaustive/src/lib.rs index 2edcbc0377..c80b9b01d0 100644 --- a/compiler/exhaustive/src/lib.rs +++ b/compiler/exhaustive/src/lib.rs @@ -89,7 +89,7 @@ pub enum Guard { /// Check -pub fn check<'a>( +pub fn check( region: Region, context: Context, matrix: Vec>, @@ -114,7 +114,7 @@ pub fn check<'a>( /// The initial rows "matrix" are all of length 1 /// The initial count of items per row "n" is also 1 /// The resulting rows are examples of missing patterns -fn is_exhaustive(matrix: &PatternMatrix, n: usize) -> PatternMatrix { +fn is_exhaustive(matrix: &RefPatternMatrix, n: usize) -> PatternMatrix { if matrix.is_empty() { vec![std::iter::repeat(Anything).take(n).collect()] } else if n == 0 { @@ -124,9 +124,9 @@ fn is_exhaustive(matrix: &PatternMatrix, n: usize) -> PatternMatrix { let num_seen = ctors.len(); if num_seen == 0 { - let new_matrix = matrix + let new_matrix: Vec<_> = matrix .iter() - .filter_map(specialize_row_by_anything) + .filter_map(|row| specialize_row_by_anything(row)) .collect(); let mut rest = is_exhaustive(&new_matrix, n - 1); @@ -142,9 +142,9 @@ fn is_exhaustive(matrix: &PatternMatrix, n: usize) -> PatternMatrix { let num_alts = alt_list.len(); if num_seen < num_alts { - let new_matrix = matrix + let new_matrix: Vec<_> = matrix .iter() - .filter_map(specialize_row_by_anything) + .filter_map(|row| specialize_row_by_anything(row)) .collect(); let rest: Vec> = is_exhaustive(&new_matrix, n - 1); @@ -165,7 +165,7 @@ fn is_exhaustive(matrix: &PatternMatrix, n: usize) -> PatternMatrix { result } else { let is_alt_exhaustive = |Ctor { arity, tag_id, .. }| { - let new_matrix = matrix + let new_matrix: Vec<_> = matrix .iter() .filter_map(|r| specialize_row_by_ctor(tag_id, arity, r)) .collect(); @@ -353,8 +353,8 @@ fn specialize_row_by_ctor2( } /// INVARIANT: (length row == N) ==> (length result == arity + N - 1) -fn specialize_row_by_ctor(tag_id: TagId, arity: usize, row: &Row) -> Option { - let mut row = row.clone(); +fn specialize_row_by_ctor(tag_id: TagId, arity: usize, row: &RefRow) -> Option { + let mut row = row.to_vec(); let head = row.pop(); let patterns = row; @@ -387,8 +387,8 @@ fn specialize_row_by_ctor(tag_id: TagId, arity: usize, row: &Row) -> Option } /// INVARIANT: (length row == N) ==> (length result == N-1) -fn specialize_row_by_anything(row: &Row) -> Option { - let mut row = row.clone(); +fn specialize_row_by_anything(row: &RefRow) -> Option { + let mut row = row.to_vec(); match row.pop() { Some(Anything) => Some(row), @@ -403,7 +403,7 @@ pub enum Complete { No, } -fn is_complete(matrix: &PatternMatrix) -> Complete { +fn is_complete(matrix: &RefPatternMatrix) -> Complete { let ctors = collect_ctors(matrix); let length = ctors.len(); let mut it = ctors.into_iter(); @@ -424,6 +424,7 @@ fn is_complete(matrix: &PatternMatrix) -> Complete { type RefPatternMatrix = [Vec]; type PatternMatrix = Vec>; +type RefRow = [Pattern]; type Row = Vec; fn collect_ctors(matrix: &RefPatternMatrix) -> MutMap { diff --git a/compiler/mono/src/exhaustive.rs b/compiler/mono/src/exhaustive.rs index 6890e6a381..cafbf8bbf9 100644 --- a/compiler/mono/src/exhaustive.rs +++ b/compiler/mono/src/exhaustive.rs @@ -121,12 +121,11 @@ pub fn check_patterns<'a>( ) { match to_nonredundant_rows(region, patterns) { Err(err) => errors.push(err), - Ok(matrix) => match roc_exhaustive::check(region, context, matrix) { - Err(err) => { + Ok(matrix) => { + if let Err(err) = roc_exhaustive::check(region, context, matrix) { *errors = err; } - Ok(_) => {} - }, + } } }