improve error detection in case-tree building

This commit is contained in:
2024-12-07 20:50:22 -08:00
parent 6ba88713f1
commit 9c0b20a6ce
5 changed files with 54 additions and 15 deletions

View File

@@ -3,15 +3,17 @@
- [x] SortedMap.newt issue in `where` - [x] SortedMap.newt issue in `where`
- [x] fix "insufficient patterns", wire in M or Either String - [x] fix "insufficient patterns", wire in M or Either String
- [ ] Matching _,_ when Maybe is expected should be an error - [x] Matching _,_ when Maybe is expected should be an error
- [ ] error for non-linear pattern - [ ] error for non-linear pattern
- [ ] typeclass dependencies - [ ] typeclass dependencies
- need to flag internal functions to not search (or flag functions for search). I need to decide on syntax for this. - need to flag internal functions to not search (or flag functions for search). I need to decide on syntax for this.
- don't search functions that are currently being defined. This is subtle... We do want to recurse in bind, we don't want to do that for the isEq function. Maybe something idris like. - don't search functions that are currently being defined. This is subtle... We do want to recurse in bind, we don't want to do that for the isEq function. Maybe something idris like.
- [ ] default implementations (use them if nothing is defined, where do we store them?) e.g. Ord compare, <, etc in Idris - [ ] default implementations (use them if nothing is defined, where do we store them?) e.g. Ord compare, <, etc in Idris
- [ ] syntax for negative integers - [ ] syntax for negative integers
- [ ] White box tests in `test` directory
- [x] Put worker in iframe on safari - [x] Put worker in iframe on safari
- [ ] Warnings or errors for missing definitions - [ ] Warnings or errors for missing definitions
- [ ] Add the type name to dcon so confusion detection in case split is simpler
- [ ] Warnings or errors for unused cases - [ ] Warnings or errors for unused cases
- This is important when misspelled constructors become pattern vars - This is important when misspelled constructors become pattern vars
- [ ] if we're staying with this version of auto, we might need to list candidates and why they're rejected. e.g. I had a bifunctor fail to solve because the right answer unblocked a Foo vs IO Foo mismatch - [ ] if we're staying with this version of auto, we might need to list candidates and why they're rejected. e.g. I had a bifunctor fail to solve because the right answer unblocked a Foo vs IO Foo mismatch
@@ -27,6 +29,7 @@
- Makes inference easier, cleaner output, and allows `foo $ \ x => ...` - Makes inference easier, cleaner output, and allows `foo $ \ x => ...`
- remove hack from Elab.infer - remove hack from Elab.infer
- [ ] `$` no longer works inside ≡⟨ ⟩ sort out how to support both that and `$ \ x => ...` (or don't bother) - [ ] `$` no longer works inside ≡⟨ ⟩ sort out how to support both that and `$ \ x => ...` (or don't bother)
- We'd either need to blacklist all non-initial mixfix bits at the appropriate spots or always pass around a terminating token.
- [ ] **Translate newt to newt** - [ ] **Translate newt to newt**
- [ ] Support @ on the LHS - [ ] Support @ on the LHS
- [x] if / then / else sugar - [x] if / then / else sugar

View File

@@ -23,6 +23,7 @@ modules =
Lib.Prettier, Lib.Prettier,
Lib.ProcessDecl, Lib.ProcessDecl,
Lib.Syntax, Lib.Syntax,
Lib.Common,
Lib.Eval, Lib.Eval,
Lib.Token, Lib.Token,
Lib.TopContext, Lib.TopContext,

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
for i in tests/black/*.newt playground/samples/*.newt; do for i in tests/black/*.newt playground/samples/*.newt aoc2024/*.newt; do
./build/exec/newt $i ./build/exec/newt $i
if [ $? != "0" ]; then if [ $? != "0" ]; then
echo FAIL $i echo FAIL $i

View File

@@ -556,6 +556,7 @@ buildCase ctx prob scnm scty (dcName, arity, ty) = do
-- if the value is already constrained to a different constructor, return Nothing -- if the value is already constrained to a different constructor, return Nothing
debug "scrut \{scnm} constrained to \{show $ lookupDef ctx scnm}" debug "scrut \{scnm} constrained to \{show $ lookupDef ctx scnm}"
let (VRef _ sctynm _ _) = scty | _ => error (getFC scty) "case split on non-inductive \{show scty}"
case lookupDef ctx scnm of case lookupDef ctx scnm of
Just val@(VRef fc nm y sp) => Just val@(VRef fc nm y sp) =>
@@ -571,7 +572,7 @@ buildCase ctx prob scnm scty (dcName, arity, ty) = do
debug "(dcon \{show dcName} ty \{show ty'} scty \{show scty}" debug "(dcon \{show dcName} ty \{show ty'} scty \{show scty}"
debug "(dcon \{show dcName}) (vars \{show vars}) clauses were" debug "(dcon \{show dcName}) (vars \{show vars}) clauses were"
for_ prob.clauses $ (\x => debug " \{show x}") for_ prob.clauses $ (\x => debug " \{show x}")
clauses <- mapMaybe id <$> traverse (rewriteClause vars) prob.clauses clauses <- mapMaybe id <$> traverse (rewriteClause sctynm vars) prob.clauses
debug "and now:" debug "and now:"
for_ clauses $ (\x => debug " \{show x}") for_ clauses $ (\x => debug " \{show x}")
when (length clauses == 0) $ error ctx.fc "Missing case for \{dcName} splitting \{scnm}" when (length clauses == 0) $ error ctx.fc "Missing case for \{dcName} splitting \{scnm}"
@@ -609,7 +610,7 @@ buildCase ctx prob scnm scty (dcName, arity, ty) = do
debug "(dcon \{show dcName} ty \{show ty'} scty \{show scty}" debug "(dcon \{show dcName} ty \{show ty'} scty \{show scty}"
debug "(dcon \{show dcName}) (vars \{show vars}) clauses were" debug "(dcon \{show dcName}) (vars \{show vars}) clauses were"
for_ prob.clauses $ (\x => debug " \{show x}") for_ prob.clauses $ (\x => debug " \{show x}")
clauses <- mapMaybe id <$> traverse (rewriteClause vars) prob.clauses clauses <- mapMaybe id <$> traverse (rewriteClause sctynm vars) prob.clauses
debug "and now:" debug "and now:"
for_ clauses $ (\x => debug " \{show x}") for_ clauses $ (\x => debug " \{show x}")
when (length clauses == 0) $ error ctx.fc "Missing case for \{dcName} splitting \{scnm}" when (length clauses == 0) $ error ctx.fc "Missing case for \{dcName} splitting \{scnm}"
@@ -647,23 +648,32 @@ buildCase ctx prob scnm scty (dcName, arity, ty) = do
else pure $ (nm, pat) :: !(makeConstr xs pats) else pure $ (nm, pat) :: !(makeConstr xs pats)
-- replace constraint with constraints on parameters, or nothing if non-matching clause -- replace constraint with constraints on parameters, or nothing if non-matching clause
rewriteConstraint : List Bind -> List Constraint -> List Constraint -> M (Maybe (List Constraint)) rewriteConstraint : String -> List Bind -> List Constraint -> List Constraint -> M (Maybe (List Constraint))
rewriteConstraint vars [] acc = pure $ Just acc rewriteConstraint sctynm vars [] acc = pure $ Just acc
rewriteConstraint vars (c@(nm, y) :: xs) acc = rewriteConstraint sctynm vars (c@(nm, y) :: xs) acc =
if nm == scnm if nm == scnm
then case y of then case y of
PatVar _ _ s => pure $ Just $ c :: (xs ++ acc) PatVar _ _ s => pure $ Just $ c :: (xs ++ acc)
PatWild _ _ => pure $ Just $ c :: (xs ++ acc) PatWild _ _ => pure $ Just $ c :: (xs ++ acc)
-- FIXME why don't we hit this ('x' for Just x) -- FIXME why don't we hit this ('x' for Just x)
PatLit fc lit => error fc "Literal \{show lit} in constructor split" PatLit fc lit => error fc "Literal \{show lit} in constructor split"
PatCon _ _ str ys => if str == dcName -- FIXME check type
PatCon fc _ nm ys => if nm == dcName
then pure $ Just $ !(makeConstr vars ys) ++ xs ++ acc then pure $ Just $ !(makeConstr vars ys) ++ xs ++ acc
else pure Nothing -- TODO can we check this when we make the PatCon?
else rewriteConstraint vars xs (c :: acc) else do
case lookup nm !get of
(Just (MkEntry _ name type (DCon k tcname))) =>
if (tcname /= sctynm)
then error fc "Constructor is \{tcname} expected \{sctynm}"
else pure Nothing
Just _ => error fc "Internal Error: \{nm} is not a DCon"
Nothing => error fc "Internal Error: DCon \{nm} not found"
else rewriteConstraint sctynm vars xs (c :: acc)
rewriteClause : List Bind -> Clause -> M (Maybe Clause) rewriteClause : String -> List Bind -> Clause -> M (Maybe Clause)
rewriteClause vars (MkClause fc cons pats expr) = do rewriteClause sctynm vars (MkClause fc cons pats expr) = do
Just cons <- rewriteConstraint vars cons [] | _ => pure Nothing Just cons <- rewriteConstraint sctynm vars cons [] | _ => pure Nothing
pure $ Just $ MkClause fc cons pats expr pure $ Just $ MkClause fc cons pats expr
@@ -843,6 +853,10 @@ buildLitCases ctx prob fc scnm scty = do
Nothing => True Nothing => True
_ => False _ => False
litTyName : Literal -> String
litTyName (LString str) = "String"
litTyName (LInt i) = "Int"
litTyName (LChar c) = "Char"
-- This process is similar to extendPi, but we need to stop -- This process is similar to extendPi, but we need to stop
-- if one clause is short on patterns. -- if one clause is short on patterns.
@@ -873,21 +887,27 @@ buildTree ctx prob@(MkProb ((MkClause fc constraints [] expr) :: cs) ty) = do
let Just (sctm, scty) := lookupName ctx scnm let Just (sctm, scty) := lookupName ctx scnm
| _ => error fc "Internal Error: can't find \{scnm} in environment" | _ => error fc "Internal Error: can't find \{scnm} in environment"
-- REVIEW We probably need to know this is a VRef before we decide to split on this slot..
scty' <- unlet ctx.env scty >>= forceType ctx.env
case pat of case pat of
PatCon _ _ _ _ => do PatCon _ _ _ _ => do
-- expand vars that may be solved, eval top level functions -- expand vars that may be solved, eval top level functions
scty' <- unlet ctx.env scty >>= forceType ctx.env
debug "EXP \{show scty} -> \{show scty'}" debug "EXP \{show scty} -> \{show scty'}"
-- this is per the paper, but it would be nice to coalesce -- this is per the paper, but it would be nice to coalesce
-- default cases -- default cases
cons <- getConstructors ctx (getFC pat) scty' cons <- getConstructors ctx (getFC pat) scty'
debug "CONS \{show $ map fst cons}" debug "CONS \{show $ map fst cons}"
alts <- traverse (buildCase ctx prob scnm scty) cons alts <- traverse (buildCase ctx prob scnm scty') cons
debug "GOTALTS \{show alts}" debug "GOTALTS \{show alts}"
when (length (catMaybes alts) == 0) $ error (fc) "no alts for \{show scty'}" when (length (catMaybes alts) == 0) $ error (fc) "no alts for \{show scty'}"
-- TODO check for empty somewhere? -- TODO check for empty somewhere?
pure $ Case fc sctm (catMaybes alts) pure $ Case fc sctm (catMaybes alts)
PatLit fc v => do PatLit fc v => do
let tyname = litTyName v
case scty' of
(VRef fc1 nm x sp) => when (nm /= tyname) $ error fc "expected \{show scty} and got \{tyname}"
_ => error fc "expected \{show scty} and got \{tyname}"
-- need to run through all of the PatLits in this slot and then find a fallback -- need to run through all of the PatLits in this slot and then find a fallback
-- walk the list of patterns, stop if we hit a PatVar / PatWild, fail if we don't -- walk the list of patterns, stop if we hit a PatVar / PatWild, fail if we don't
alts <- buildLitCases ctx prob fc scnm scty alts <- buildLitCases ctx prob fc scnm scty

View File

@@ -7,6 +7,21 @@ import Lib.ProcessDecl
import Lib.TopContext import Lib.TopContext
import Lib.Syntax import Lib.Syntax
{-
Expect these to throw. (need failing blocks or a white box test here)
After we get pack/lsp issues sorted with this directory
foo : Maybe (Int × Int) -> Int
foo 1 = ?
foo _ = ?
foo : Maybe (Int × Int) -> Int
foo (1,1) = ?
foo _ = ?
-}
testCase : M () testCase : M ()
testCase = do testCase = do
-- need to get some defs in here -- need to get some defs in here