Skip to content

Commit f12a4fb

Browse files
authored
Properly handle unreachable clauses (#30)
* add empty type matching, remove let for case default * put empty type at the beginning of the environment
1 parent d279046 commit f12a4fb

File tree

4 files changed

+46
-25
lines changed

4 files changed

+46
-25
lines changed

src/Agda/Utils/EliminateDefaults.hs

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@ Taken straight from Agda source.
44
There are two important differences:
55
- we also add branches for unreachable defaults (because we need all cases to be exhaustive)
66
- we sort the case alts according to the order of constructors for the given inductive
7-
8-
Currently, the default branch value is bound in a let binding,
9-
I don't know if this will be problematic for some targets that evaluate
10-
the binding before the case. Will they fail?
11-
127
-}
138

149
-- | Eliminates case defaults by adding an alternative for all possible
@@ -22,7 +17,7 @@ import Control.Monad.IO.Class (liftIO)
2217
import Agda.Syntax.Treeless
2318
import Agda.TypeChecking.Monad
2419
import Agda.TypeChecking.Substitute
25-
import Agda.Compiler.Treeless.Subst () --instance only
20+
import Agda.Compiler.Treeless.Subst () -- instance only
2621

2722

2823
eliminateCaseDefaults :: TTerm -> TCM TTerm
@@ -35,35 +30,30 @@ eliminateCaseDefaults = tr
3530

3631
let missingCons = dtCons List.\\ map aCon alts
3732
def <- tr def
38-
newAlts <- forM missingCons $ \con -> do
33+
34+
-- we produce a new alternative for every missing constructor
35+
-- whose body is the default body, raised by #args brought in scope
36+
newAlts <- forM missingCons \con -> do
3937
Constructor {conArity = ar} <- theDef <$> getConstInfo con
40-
return $ TACon con ar (TVar ar)
38+
return $ TACon con ar $ raise ar def
4139

42-
alts' <- (++ newAlts) <$> mapM (trAlt . raise 1) alts
40+
alts' <- (++ newAlts) <$> mapM trAlt alts
4341

44-
-- sort the alts
45-
let alts'' = flip List.sortOn alts' \alt -> List.elemIndex (aCon alt) dtCons
42+
-- then we sort the alts
43+
let alts'' = flip List.sortOn alts' \alt -> List.elemIndex (aCon alt) dtCons
4644

47-
return $ TLet def $ TCase (sc + 1) ct tUnreachable alts''
45+
return $ TCase sc ct tUnreachable alts''
4846

4947
-- case on non-data are always exhaustive
5048
TCase sc ct def alts -> TCase sc ct <$> tr def <*> mapM trAlt alts
5149

52-
t@TVar{} -> return t
53-
t@TDef{} -> return t
54-
t@TCon{} -> return t
55-
t@TPrim{} -> return t
56-
t@TLit{} -> return t
57-
t@TUnit{} -> return t
58-
t@TSort{} -> return t
59-
t@TErased{} -> return t
60-
t@TError{} -> return t
61-
6250
TCoerce a -> TCoerce <$> tr a
6351
TLam b -> TLam <$> tr b
6452
TApp a bs -> TApp <$> tr a <*> mapM tr bs
6553
TLet e b -> TLet <$> tr e <*> tr b
6654

55+
t -> return t
56+
6757
trAlt :: TAlt -> TCM TAlt
6858
trAlt = \case
6959
TAGuard g b -> TAGuard <$> tr g <*> tr b

src/Agda2Lambox/Compile.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import Agda2Lambox.Compile.Inductive ( compileInductive )
2828
import Agda2Lambox.Compile.TypeScheme ( compileTypeScheme )
2929
import Agda2Lambox.Compile.Type ( compileTopLevelType )
3030

31+
import LambdaBox ( emptyName, emptyDecl )
3132
import LambdaBox.Names
3233
import LambdaBox.Env (GlobalEnv(..), GlobalDecl(..), ConstantBody(..))
3334
import LambdaBox.Term (Term(LBox))
@@ -38,7 +39,7 @@ import LambdaBox.Term (Term(LBox))
3839
compile :: Target t -> [QName] -> CompileM (GlobalEnv t)
3940
compile t qs = do
4041
items <- compileLoop (compileDefinition t) qs
41-
pure $ GlobalEnv $ map itemToEntry items
42+
pure $ GlobalEnv $ map itemToEntry items ++ [(emptyName, emptyDecl t)]
4243
where
4344
itemToEntry :: CompiledItem (GlobalDecl t) -> (KerName, GlobalDecl t)
4445
itemToEntry CompiledItem{..} = (qnameToKName itemName, itemValue)

src/Agda2Lambox/Compile/Term.hs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Agda.TypeChecking.Monad.Base ( TCM , liftTCM, MonadTCEnv, MonadTCM )
2626
import Agda.TypeChecking.Monad.Builtin ( getBuiltinName_ )
2727

2828
import LambdaBox.Names ( Name(..) )
29-
import LambdaBox ( Term(..) )
29+
import LambdaBox ( Term(..), emptyName )
3030
import LambdaBox qualified as LBox
3131
import Agda2Lambox.Compile.Utils
3232
import Agda2Lambox.Compile.Monad
@@ -144,7 +144,11 @@ compileTermC = \case
144144
TSort -> return LBox
145145
TErased -> return LBox
146146

147-
TError terr -> return LBox -- unreachable clause
147+
TError terr -> return $ LCase (LBox.Inductive emptyName 0) 0 LBox []
148+
-- ^ Unreachable clause.
149+
-- We explicitely match on the empty type (for which we do not construct a proof),
150+
-- so that later passes recognize that this is unreachable.
151+
148152
TCoerce tt -> genericError "Coerces not supported."
149153

150154
compileLit :: Literal -> C LBox.Term

src/LambdaBox.hs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,35 @@ module LambdaBox
55
, module LambdaBox.Term
66
, module LambdaBox.Type
77
, module LambdaBox.Env
8+
, emptyName
9+
, emptyDecl
810
) where
911

12+
import Control.Monad.Identity
13+
import Agda2Lambox.Compile.Target
1014
import LambdaBox.Names
1115
import LambdaBox.Term
1216
import LambdaBox.Type
1317
import LambdaBox.Env
18+
19+
-- | Kername for the backed-in empty type.
20+
emptyName :: KerName
21+
emptyName = KerName (MPFile ["LamBox"]) "Empty"
22+
23+
-- | Backed-in definition for the empty type.
24+
-- Used to discard unreachable branches in typed targets.
25+
emptyDecl :: Target t -> GlobalDecl t
26+
emptyDecl t = InductiveDecl MutualInductive
27+
{ indFinite = Finite
28+
, indPars = 0
29+
, indBodies = [
30+
OneInductive
31+
{ indName = "Empty"
32+
, indPropositional = False
33+
, indKElim = IntoAny
34+
, indTypeVars = runIdentity $ whenTyped t $ pure []
35+
, indCtors = []
36+
, indProjs = []
37+
}
38+
]
39+
}

0 commit comments

Comments
 (0)