Merge pull request #284 from rtfeldman/pattern-errors

Pattern errors
This commit is contained in:
Richard Feldman 2020-03-30 23:49:14 -04:00 committed by GitHub
commit 881e7c2b05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 12 deletions

View File

@ -11,12 +11,12 @@ roc_module = { path = "../module" }
roc_types = { path = "../types" } roc_types = { path = "../types" }
roc_can = { path = "../can" } roc_can = { path = "../can" }
roc_unify = { path = "../unify" } roc_unify = { path = "../unify" }
roc_problem = { path = "../problem" }
bumpalo = { version = "3.2", features = ["collections"] } bumpalo = { version = "3.2", features = ["collections"] }
[dev-dependencies] [dev-dependencies]
roc_constrain = { path = "../constrain" } roc_constrain = { path = "../constrain" }
roc_builtins = { path = "../builtins" } roc_builtins = { path = "../builtins" }
roc_problem = { path = "../problem" }
roc_parse = { path = "../parse" } roc_parse = { path = "../parse" }
roc_solve = { path = "../solve" } roc_solve = { path = "../solve" }
pretty_assertions = "0.5.1 " pretty_assertions = "0.5.1 "

View File

@ -340,31 +340,43 @@ fn pattern_to_when<'a>(
// for underscore we generate a dummy Symbol // for underscore we generate a dummy Symbol
(env.fresh_symbol(), body) (env.fresh_symbol(), body)
} }
Shadowed(region, loc_ident) => {
Shadowed(_, _) | UnsupportedPattern(_) => { let error = roc_problem::can::RuntimeError::Shadowing {
// create the runtime error here, instead of delegating to When. original_region: *region,
// UnsupportedPattern should then never occcur in When shadow: loc_ident.clone(),
panic!("TODO generate runtime error here"); };
(env.fresh_symbol(), Located::at_zero(RuntimeError(error)))
} }
AppliedTag {..} | RecordDestructure {..} => { UnsupportedPattern(region) => {
// create the runtime error here, instead of delegating to When.
// UnsupportedPattern should then never occcur in When
let error = roc_problem::can::RuntimeError::UnsupportedPattern(*region);
(env.fresh_symbol(), Located::at_zero(RuntimeError(error)))
}
AppliedTag { .. } | RecordDestructure { .. } => {
let symbol = env.fresh_symbol(); let symbol = env.fresh_symbol();
let wrapped_body = When { let wrapped_body = When {
cond_var: pattern_var, cond_var: pattern_var,
expr_var: body_var, expr_var: body_var,
loc_cond: Box::new(Located::at_zero(Var(symbol))), loc_cond: Box::new(Located::at_zero(Var(symbol))),
branches: vec![WhenBranch{ patterns: vec![pattern], value: body, guard: None }], branches: vec![WhenBranch {
patterns: vec![pattern],
value: body,
guard: None,
}],
}; };
(symbol, Located::at_zero(wrapped_body)) (symbol, Located::at_zero(wrapped_body))
} }
// These patters are refutable, and thus should never occur outside a `when` expression IntLiteral(_) | NumLiteral(_, _) | FloatLiteral(_) | StrLiteral(_) => {
IntLiteral(_) | NumLiteral(_,_) | FloatLiteral(_) | StrLiteral(_) => { // These patters are refutable, and thus should never occur outside a `when` expression
// They should have been replaced with `UnsupportedPattern` during canonicalization
unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value) unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value)
} }
} }
} }
@ -1019,7 +1031,7 @@ fn from_can_when<'a>(
if branches.is_empty() { if branches.is_empty() {
// A when-expression with no branches is a runtime error. // A when-expression with no branches is a runtime error.
// We can't know what to return! // We can't know what to return!
panic!("TODO compile a 0-branch when-expression to a RuntimeError"); Expr::RuntimeError("Hit a 0-branch when expression")
} else if branches.len() == 1 && branches[0].patterns.len() == 1 && branches[0].guard.is_none() } else if branches.len() == 1 && branches[0].patterns.len() == 1 && branches[0].guard.is_none()
{ {
let first = branches.remove(0); let first = branches.remove(0);

View File

@ -32,6 +32,8 @@ pub enum RuntimeError {
original_region: Region, original_region: Region,
shadow: Located<Ident>, shadow: Located<Ident>,
}, },
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
UnsupportedPattern(Region),
UnrecognizedFunctionName(Located<InlinableString>), UnrecognizedFunctionName(Located<InlinableString>),
LookupNotInScope(Located<InlinableString>), LookupNotInScope(Located<InlinableString>),
ValueNotExposed { ValueNotExposed {

View File

@ -2526,4 +2526,27 @@ mod test_solve {
"List a -> List a", "List a -> List a",
); );
} }
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn rigid_record_quantification() {
// the ext here is qualified on the outside (because we have rank 1 types, not rank 2).
// That means e.g. `f : { bar : String, foo : Int } -> Bool }` is a valid argument. but
// that function could not be applied to the `{ foo : Int }` list. Therefore, this function
// is not allowed.
//
// should hit a debug_assert! in debug mode, and produce a type error in release mode
infer_eq_without_problem(
indoc!(
r#"
test : ({ foo : Int }ext -> Bool), { foo : Int } -> Bool
test = \fn, a -> fn a
test
"#
),
"should fail",
);
}
} }