Skip to content

Commit dd6d0a4

Browse files
authored
Improve Rename in clone, and fix uniqueness laws (#296)
* update test * litmuss test * wip on trying to improving the renamer in types * fix clone law example * unitR law no longer possible * extra debug info * kind based tests * nub out repeated vars * nit * fix assoc typing * cleanup old code
1 parent 0e07c3a commit dd6d0a4

File tree

10 files changed

+221
-113
lines changed

10 files changed

+221
-113
lines changed

examples/uniqueness.gr

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,13 @@ fst (a, b) = let !u = share b in a
3232
goodDesire : *Cake -> (Happy, Cake)
3333
goodDesire uniqueCake = let !cake = share uniqueCake in (eat cake, have cake)
3434

35-
-- Copying a non-linear value as unique and then borrowing it gives you the
36-
-- original value
37-
unitR : forall {a : Type} . !a -> !a
38-
unitR t = clone t as x in share x
39-
4035
-- Borrowing a unique value and copying it to apply a function is the same as
41-
-- just applying the function
42-
unitL : forall {a b : Type} . *a -> (!a -> !b) -> !b
43-
unitL t f = clone (share t) as x in f (share x)
36+
-- -- just applying the function
37+
unitL : forall {a b : Type, id : Name} .
38+
*(FloatArray id) -> (exists {id1 : Name} . *(FloatArray id1) -> b) -> b
39+
unitL t f = clone (share t) as x in f x
4440

4541
-- Nesting copies works as you would expect (and obeys associativity)
46-
assoc : forall {a b c : Type} . !a -> (*a -> !b) -> (*b -> !c) -> !c
42+
assoc : forall {a b c : Type} . {Cloneable a, Cloneable b} => !a -> (*a -> !b) -> (*b -> !c) -> !c
4743
assoc t f g = clone t as x in
4844
clone f x as y in g y

frontend/src/Language/Granule/Checker/Checker.hs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,8 @@ checkExpr defs gam pol topLevel tau
674674
debugM "checkExpr[Clone]" (pretty s <> " : " <> pretty tau)
675675
(tau', gam, subst, elab) <- synthExpr defs gam pol expr
676676
-- Check the return types match
677+
debugM "checkExpr[Clone]-types" $
678+
"Expected: " <> pretty tau <> ", Synthesised: " <> pretty tau'
677679
(eqT, _, substTy) <- equalTypes s tau tau'
678680
unless eqT $ throw TypeError{ errLoc = s, tyExpected = tau, tyActual = tau' }
679681
substF <- combineSubstitutions s subst substTy
@@ -940,26 +942,47 @@ synthExpr _ _ _ (Val s _ rf (StringLiteral c)) = do
940942

941943
-- Clone
942944
-- Pattern match on an applicable of `uniqueBind fun e`
945+
--------------------------------------
946+
-- G1, id |- e : [] r A
947+
-- G2, x : exists id' . *(A[id'/id]) |- body : B
948+
-- cloneable(A)
949+
-- 1 <= r
950+
--- ------------------------------------------------
951+
---- (G1 + G2), id |- clone e as x in body : B
952+
---------------------------------------
943953
synthExpr defs gam pol
944954
expr@(App s a rf
945955
(App _ _ _
946956
(Val _ _ _ (Var _ (internalName -> "uniqueBind")))
947957
(Val _ _ _ (Abs _ (PVar _ _ _ var) _ body)))
948958
e) = do
949-
debugM "synthExpr[uniqueBind]" (pretty s <> pretty expr)
959+
debugM "synthExpr[Clone]" (pretty s <> pretty expr)
950960
-- Infer the type of e (the boxed argument)
951961
(ty, ghostVarCtxt, subst0, elabE) <- synthExpr defs gam pol e
952-
-- Check that ty is actually a boxed type
962+
-- Check that ty is actually a boxed type `[] r A`
953963
case ty of
954964
Box r tyA -> do
955-
-- existential type for the cloned var ((exists {id : Name} . *(Rename id a))
956-
idVar <- mkId <$> freshIdentifierBase "id"
965+
-- extract every type var of kind `Name` from `tyA`
966+
idVars <- typeVarsOfKind tyA (TyCon $ mkId "Name")
967+
debugM "synthExpr[Clone]typeVars" (pretty idVars)
968+
-- existential type for the cloned var ((exists {id' : Name} . *(Rename id' a))
969+
970+
-- make a fresh var for each of idVars
971+
newIdVars <- mapM (\x -> freshIdentifierBase (internalName x) >>= (return . mkId)) idVars
972+
973+
-- apply the renamer for each of the idVars
974+
let renamer (old, new) t = TyApp (TyApp (TyCon $ mkId "Rename") (TyVar old)) (TyVar new) `TyApp` t
975+
let newAndOldVars = zip idVars newIdVars
976+
let renamedTyA = foldr renamer tyA newAndOldVars
977+
-- Construct the cloned input type
957978
let clonedInputTy =
958-
TyExists idVar (TyCon $ mkId "Name")
959-
(Borrow (TyCon $ mkId "Star") (TyApp (TyApp (TyCon $ mkId "Rename") (TyVar idVar)) tyA))
979+
foldr (\ idVar ty -> TyExists idVar (TyCon $ mkId "Name") ty)
980+
(Borrow (TyCon $ mkId "Star") renamedTyA) newIdVars
981+
982+
-- cloned assumption
960983
let clonedAssumption = (var, Linear clonedInputTy)
961984

962-
debugM "synthExpr[uniqueBind]body" (pretty clonedAssumption)
985+
debugM "synthExpr[Clone]body" (pretty clonedAssumption)
963986
-- synthesise the type of the body for the clone
964987
(tyB, ghostVarCtxt', subst1, elabBody) <- synthExpr defs (clonedAssumption : gam) pol body
965988

frontend/src/Language/Granule/Checker/Kinding.hs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Control.Monad.State.Strict
1717
import Control.Monad.Trans.Maybe
1818
import Data.Functor.Identity (runIdentity)
1919
import Data.Maybe (fromMaybe)
20-
import Data.List (isPrefixOf, sortBy)
20+
import Data.List (isPrefixOf, sortBy, (\\), nub)
2121

2222
import Language.Granule.Context
2323

@@ -1377,3 +1377,42 @@ instance Unifiable t => Unifiable (Maybe t) where
13771377
unify' Nothing _ = return []
13781378
unify' _ Nothing = return []
13791379
unify' (Just x) (Just y) = unify' x y
1380+
1381+
-- Given a type, extract all the type variables which have
1382+
-- the given kind
1383+
typeVarsOfKind :: (?globals :: Globals) => Type -> Kind -> Checker [Id]
1384+
typeVarsOfKind t k = do
1385+
vars <- typeFoldM algebra t
1386+
return (nub vars)
1387+
where
1388+
algebra = TypeFold
1389+
{ tfTy = \_ -> return []
1390+
, tfFunTy = \_ _ x y -> return (x ++ y)
1391+
, tfTyCon = \_ -> return []
1392+
, tfBox = \_ x -> return x
1393+
, tfDiamond = \_ x -> return x
1394+
, tfStar = \_ x -> return x
1395+
, tfBorrow = \_ x -> return x
1396+
, tfTyVar = \v -> do
1397+
-- Synth the kind of this type variable
1398+
synthKind nullSpan (TyVar v) >>= \case
1399+
-- check for equality with the argument
1400+
(k', subst , _) | k' == k -> do
1401+
when (not (null subst)) (debugM "[WARNING]" ("type variable " <> pretty v <> " has kind " <> pretty k <> " but its kind required a substitution which is being ignored during kind variable extraction"))
1402+
return [v]
1403+
-- kinds don't match
1404+
_ -> return []
1405+
, tfTyApp = \ x y -> return (x ++ y)
1406+
, tfTyInt = \_ -> return []
1407+
, tfTyRational = \_ -> return []
1408+
, tfTyFraction = \_ -> return []
1409+
, tfTyGrade = \_ _ -> return []
1410+
, tfTyInfix = \_ x y -> return (x ++ y)
1411+
, tfSet = \ _ x -> return (concat x)
1412+
, tfTyCase = \ x branches ->
1413+
return (x ++ concatMap snd branches)
1414+
, tfTySig = \ x y -> (\_-> return x)
1415+
, tfTyExists = \ v k x -> return (x \\ [v])
1416+
, tfTyForall = \ v k x -> return (x \\ [v])
1417+
, tfTyName = \ _ -> return []
1418+
}

frontend/src/Language/Granule/Checker/Primitives.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ typeConstructors =
8686
, (mkId "ReceivePrefix", (funTy (tyCon "Protocol") (tyCon "Predicate"), [], [0]))
8787
, (mkId "Sends", (funTy (tyCon "Nat") (funTy (tyCon "Protocol") (tyCon "Predicate")), [], [0]))
8888
, (mkId "Graded", (funTy (tyCon "Nat") (funTy (tyCon "Protocol") (tyCon "Protocol")), [], [0]))
89-
, (mkId "Rename", (funTy (tyCon "Name") (funTy (Type 0) (Type 0)), [], [0]))
89+
, (mkId "Rename", (funTy (tyCon "Name") (funTy (tyCon "Name") (funTy (Type 0) (Type 0))), [], [0]))
9090
-- # Coeffect types
9191
, (mkId "Nat", (kcoeffect, [], []))
9292
, (mkId "Q", (kcoeffect, [], [])) -- Rationals

frontend/src/Language/Granule/Checker/Types.hs

Lines changed: 24 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -245,16 +245,16 @@ equalTypesRelatedCoeffectsInner s rel (Borrow p1 t1) (Borrow p2 t2) _ sp Types =
245245
u <- combineSubstitutions s unif unif'
246246
return (eq && eq', u)
247247

248+
-- Handle equality involving renaming
249+
equalTypesRelatedCoeffectsInner s rel
250+
t0@(TyApp (TyApp (TyApp (TyCon (internalName -> "Rename")) (TyVar oldName)) (TyVar newName)) t) t' ind sp mode = do
251+
debugM "RenameL" (pretty t0 <> " = " <> pretty t')
252+
eqRenameFunction s rel oldName newName t t' sp
248253

249-
equalTypesRelatedCoeffectsInner s rel t0@(TyApp (TyApp (TyCon d) name) t) t' ind sp mode
250-
| internalName d == "Rename" = do
251-
debugM "RenameL" (pretty t <> " = " <> pretty t')
252-
eqRenameFunction s rel name t t' sp
253-
254-
equalTypesRelatedCoeffectsInner s rel t' t0@(TyApp (TyApp (TyCon d) name) t) ind sp mode
255-
| internalName d == "Rename" = do
256-
debugM "RenameR" (pretty t <> " = " <> pretty t')
257-
eqRenameFunction s rel name t t' sp
254+
equalTypesRelatedCoeffectsInner s rel t'
255+
t0@(TyApp (TyApp (TyApp (TyCon (internalName -> "Rename")) (TyVar oldName)) (TyVar newName)) t) ind sp mode = do
256+
debugM "RenameR" (pretty t' <> " = " <> pretty t0)
257+
eqRenameFunction s rel oldName newName t t' sp
258258

259259
-- ## GENERAL EQUALITY
260260

@@ -557,102 +557,33 @@ eqGradedProtocolFunction sp rel grad (TyVar v) t ind = do
557557
eqGradedProtocolFunction sp _ grad t1 t2 _ = throw
558558
TypeError{ errLoc = sp, tyExpected = (TyApp (TyApp (TyCon $ mkId "Graded") grad) t1), tyActual = t2 }
559559

560-
-- Compute the behaviour of `Rename id a` on a type `A`
561-
renameBeta :: (?globals :: Globals)
562-
=> Type -- name
560+
-- Compute the behaviour of `Rename oldId newId a`
561+
-- where the arguments here are `oldId`, `newId` and `a`
562+
renameBeta ::
563+
Id -- oldName
564+
-> Id -- newName
563565
-> Type -- type
564566
-> Checker Type
565-
renameBeta name (TyApp (TyApp (TyCon c) t) s)
566-
| internalName c == "Ref" = do
567-
s' <- renameBeta name s
568-
return $ (TyApp (TyApp (TyCon c) name) s')
569-
570-
renameBeta name (TyApp (TyCon c) t)
571-
| internalName c == "FloatArray" = do
572-
return $ (TyApp (TyCon c) name)
573-
574-
renameBeta name (TyApp (TyApp (TyCon c) t1) t2)
575-
| internalName c == "," = do
576-
t1' <- renameBeta name t1
577-
t2' <- renameBeta name t2
578-
return $ (TyApp (TyApp (TyCon c) t1') t2')
579-
580-
renameBeta name (Star g t) = do
581-
t' <- renameBeta name t
582-
return $ (Star g t')
583-
584-
renameBeta name (Borrow p t) = do
585-
t' <- renameBeta name t
586-
return $ (Borrow p t')
587-
588-
renameBeta name t = return t
589-
590-
renameBetaInvert :: (?globals :: Globals)
591-
=> Span
592-
-- Explain how coeffects should be related by a solver constraint
593-
-> (Span -> Coeffect -> Coeffect -> Type -> Constraint)
594-
-> Type -- name
595-
-> Type -- type
596-
-- Indicates whether the first type or second type is a specification
597-
-> SpecIndicator
598-
-- Flag to say whether this type is actually an effect or not
599-
-> Mode
600-
-> Checker (Type, Substitution)
601-
602-
-- Ref case
603-
-- i.e., Rename id a = Ref id' a'
604-
-- therefore check `id ~ id'` and then recurse
605-
renameBetaInvert sp rel name (TyApp (TyApp (TyCon c) name') s) spec mode
606-
| internalName c == "Ref" = do
607-
-- Compute equality on names
608-
(_, subst) <- equalTypesRelatedCoeffects sp rel name name' spec mode
609-
(s, subst') <- renameBetaInvert sp rel name s spec mode
610-
substFinal <- combineSubstitutions sp subst subst'
611-
return (TyApp (TyApp (TyCon c) name') s, substFinal)
612-
613-
renameBetaInvert sp rel name (TyApp (TyCon c) name') spec mode
614-
| internalName c == "FloatArray" = do
615-
-- Compute equality on names
616-
(_, subst) <- equalTypesRelatedCoeffects sp rel name name' spec mode
617-
return (TyApp (TyCon c) name', subst)
618-
619-
renameBetaInvert sp rel name (TyApp (TyApp (TyCon c) t1) t2) spec mode
620-
| internalName c == "," = do
621-
(t1', subst1) <- renameBetaInvert sp rel name t1 spec mode
622-
(t2', subst2) <- renameBetaInvert sp rel name t2 spec mode
623-
substFinal <- combineSubstitutions sp subst1 subst2
624-
return (TyApp (TyApp (TyCon c) t1') t2', substFinal)
625-
626-
renameBetaInvert _ _ name t _ _ = return (t, [])
567+
renameBeta oldName newName t =
568+
typeFoldM (baseTypeFold { tfTyVar = return . TyVar . renamer }) t
569+
where
570+
renamer :: Id -> Id
571+
renamer id = if id == oldName then newName else id
627572

628-
-- Check if `Rename id a ~ a'` which may involve some normalisation in the
629573
-- case where `a'` is a variable
630574
eqRenameFunction :: (?globals :: Globals)
631575
=> Span
632576
-> (Span -> Coeffect -> Coeffect -> Type -> Constraint)
633577
-- These two arguments are the arguments to `Rename id a`
634-
-> Type -- name
578+
-> Id -- oldName
579+
-> Id -- newName
635580
-> Type -- type
636581
-- This is the argument of the type which we are trying to see if it equal to `Rename id a`
637582
-> Type -- compared against
638583
-> SpecIndicator
639584
-> Checker (Bool, Substitution)
640-
641-
eqRenameFunction sp rel name t (TyApp (TyApp (TyCon d) name') t') ind
642-
| internalName d == "Rename" = do
643-
(_, subst) <- equalTypesRelatedCoeffects sp rel name name' ind Types
644-
(eq, subst') <- eqRenameFunction sp rel name t t' ind
645-
substFinal <- combineSubstitutions sp subst subst'
646-
return (eq, substFinal)
647-
648-
eqRenameFunction sp rel name (TyVar v) t ind = do
649-
(t', subst) <- renameBetaInvert sp rel name t ind Types
650-
(eq, subst') <- equalTypesRelatedCoeffects sp rel t' (TyVar v) ind Types
651-
substFinal <- combineSubstitutions sp subst subst'
652-
return (eq, substFinal)
653-
654-
eqRenameFunction sp rel name t t' ind = do
655-
t'' <- renameBeta name t
585+
eqRenameFunction sp rel oldName newName t t' ind = do
586+
t'' <- renameBeta oldName newName t
656587
(eq, u) <- equalTypesRelatedCoeffects sp rel t'' t' ind Types
657588
return (eq, u)
658589

@@ -825,3 +756,4 @@ isPermission s ty = do
825756
putChecker
826757
return $ Right pTy
827758
Left err -> return $ Left pTy
759+
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
example : Int [1]
22
example =
33
clone [42] as x
4-
in (unpack <id' , a'> = x in (share a'))
4+
in (unpack <id' , a'> = x in (share a'))
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
Type checking failed:
2-
Type constraint error: frontend/tests/cases/negative/unique/cloneNonCloneable.gr:1:1:
3-
Constraint `Cloneable Int` does not hold or is not provided by the type constraint assumptions here.
2+
Type error: frontend/tests/cases/negative/unique/cloneNonCloneable.gr:4:9:
3+
Expcted an existential type on the left-hand side of unpack, but got `*Int`
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
unitL : forall {a b : Type, id : Name} .
2+
*(FloatArray id) -> (exists {id1 : Name} . *(FloatArray id1) -> b) -> b
3+
unitL t f = clone (share t) as x in f x

0 commit comments

Comments
 (0)