Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into control
Browse files Browse the repository at this point in the history
  • Loading branch information
croyzor committed Jan 17, 2025
2 parents 0f34053 + 30bdc9f commit 8918482
Show file tree
Hide file tree
Showing 58 changed files with 1,116 additions and 529 deletions.
29 changes: 25 additions & 4 deletions brat/Brat/Checker.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Prelude hiding (filter)
import Brat.Checker.Helpers
import Brat.Checker.Monad
import Brat.Checker.Quantity
import Brat.Checker.SolveHoles (typeEq)
import Brat.Checker.SolvePatterns (argProblems, argProblemsWithLeftovers, solve)
import Brat.Checker.Types
import Brat.Constructors
Expand Down Expand Up @@ -667,7 +668,13 @@ check' (Of n e) ((), unders) = case ?my of
(elems, unders, rightUnders) <- getVecs len unders
pure ((tgt, el):elems, (tgt, ty):unders, rightUnders)
getVecs _ unders = pure ([], [], unders)

check' Hope ((), (NamedPort hope _, ty):unders) = case (?my, ty) of
(Braty, Left _k) -> do
fc <- req AskFC
req (ANewHope hope fc)
pure (((), ()), ((), unders))
(Braty, Right _ty) -> typeErr "Can only infer kinded things with !"
(Kerny, _) -> typeErr "Won't infer kernel typed !"
check' tm _ = error $ "check' " ++ show tm


Expand Down Expand Up @@ -1133,13 +1140,27 @@ run :: VEnv
-> Namespace
-> Checking a
-> Either Error (a, ([TypedHole], Store, Graph))
run ve initStore ns m =
run ve initStore ns m = do
let ctx = Ctx { globalVEnv = ve
, store = initStore
-- TODO: fill with default constructors
, constructors = defaultConstructors
, kconstructors = kernelConstructors
, typeConstructors = defaultTypeConstructors
, aliasTable = M.empty
} in
(\(a,ctx,(holes, graph)) -> (a, (holes, store ctx, graph))) <$> handler (localNS ns m) ctx mempty
, hopes = M.empty
}
(a,ctx,(holes, graph)) <- handler (localNS ns m) ctx mempty
let tyMap = typeMap $ store ctx
-- If the `hopes` set has any remaining holes with kind Nat, we need to abort.
-- Even though we didn't need them for typechecking problems, our runtime
-- behaviour depends on the values of the holes, which we can't account for.
case M.toList $ M.filterWithKey (\e _ -> isNatKinded tyMap (InEnd e)) (hopes ctx) of
[] -> pure (a, (holes, store ctx, graph))
-- Just use the FC of the first hole while we don't have the capacity to
-- show multiple error locations
hs@((_,fc):_) -> Left $ Err (Just fc) (RemainingNatHopes (show . fst <$> hs))
where
isNatKinded tyMap e = case tyMap M.! e of
EndType Braty (Left Nat) -> True
_ -> False
43 changes: 22 additions & 21 deletions brat/Brat/Checker/Helpers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ import Hasochism
import Util (log2)

import Control.Monad (when)
import Control.Monad.State.Lazy (StateT(..), runStateT)
import Control.Monad.Freer (req)
import Data.Bifunctor
import Data.Foldable (foldrM)
import Data.List (partition)
import Data.Type.Equality (TestEquality(..), (:~:)(..))
import qualified Data.Map as M
import Prelude hiding (last)
Expand Down Expand Up @@ -100,35 +102,29 @@ pullPortsRow :: Show ty
=> [PortName]
-> [(NamedPort e, ty)]
-> Checking [(NamedPort e, ty)]
pullPortsRow = pullPorts portName showRow
pullPortsRow = pullPorts (portName . fst) showRow

pullPortsSig :: Show ty
=> [PortName]
-> [(PortName, ty)]
-> Checking [(PortName, ty)]
pullPortsSig = pullPorts id showSig
pullPortsSig = pullPorts fst showSig

pullPorts :: forall a ty. Show ty
=> (a -> PortName) -- A way to get a port name for each element
-> ([(a, ty)] -> String) -- A way to print the list
pullPorts :: forall a ty
. (a -> PortName) -- A way to get a port name for each element
-> ([a] -> String) -- A way to print the list
-> [PortName] -- Things to pull to the front
-> [(a, ty)] -- The list to rearrange
-> Checking [(a, ty)]
pullPorts _ _ [] types = pure types
pullPorts toPort showFn (p:ports) types = do
(x, types) <- pull1Port p types
(x:) <$> pullPorts toPort showFn ports types
-> [a] -- The list to rearrange
-> Checking [a]
pullPorts toPort showFn to_pull types =
-- the "state" here is the things still available to be pulled
uncurry (++) <$> runStateT (mapM pull1Port to_pull) types
where
pull1Port :: PortName
-> [(a, ty)]
-> Checking ((a, ty), [(a, ty)])
pull1Port p [] = fail $ "Port not found: " ++ p ++ " in " ++ showFn types
pull1Port p (x@(a,_):xs)
| p == toPort a
= if p `elem` (toPort . fst <$> xs)
then err (AmbiguousPortPull p (showFn (x:xs)))
else pure (x, xs)
| otherwise = second (x:) <$> pull1Port p xs
pull1Port :: PortName -> StateT [a] Checking a
pull1Port p = StateT $ \available -> case partition ((== p) . toPort) available of
([], _) -> err $ BadPortPull p (showFn available)
([found], remaining) -> pure (found, remaining)
(_, _) -> err $ AmbiguousPortPull p (showFn available)

ensureEmpty :: Show ty => String -> [(NamedPort e, ty)] -> Checking ()
ensureEmpty _ [] = pure ()
Expand Down Expand Up @@ -506,3 +502,8 @@ runArith (NumValue upl grol) Pow (NumValue upr gror)
-- 2^(2^k * upr) + 2^(2^k * upr) * (full(2^(k + k') * mono))
= pure $ NumValue (upl ^ upr) (StrictMonoFun (StrictMono (l * upr) (Full (StrictMono (k + k') mono))))
runArith _ _ _ = Nothing

buildConst :: SimpleTerm -> Val Z -> Checking Src
buildConst tm ty = do
(_, _, [(out,_)], _) <- next "buildConst" (Const tm) (S0, Some (Zy :* S0)) R0 (RPr ("value", ty) R0)
pure out
15 changes: 15 additions & 0 deletions brat/Brat/Checker/Monad.hs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,15 @@ data CtxEnv = CtxEnv
, locals :: VEnv
}

type Hopes = M.Map InPort FC

data Context = Ctx { globalVEnv :: VEnv
, store :: Store
, constructors :: ConstructorMap Brat
, kconstructors :: ConstructorMap Kernel
, typeConstructors :: M.Map (Mode, QualName) [(PortName, TypeKind)]
, aliasTable :: M.Map QualName Alias
, hopes :: Hopes
}

data CheckingSig ty where
Expand Down Expand Up @@ -89,6 +92,9 @@ data CheckingSig ty where
AskVEnv :: CheckingSig CtxEnv
Declare :: End -> Modey m -> BinderType m -> CheckingSig ()
Define :: End -> Val Z -> CheckingSig ()
ANewHope :: InPort -> FC -> CheckingSig ()
AskHopes :: CheckingSig Hopes
RemoveHope :: InPort -> CheckingSig ()

localAlias :: (QualName, Alias) -> Checking v -> Checking v
localAlias _ (Ret v) = Ret v
Expand Down Expand Up @@ -267,6 +273,15 @@ handler (Req s k) ctx g
M.lookup tycon tbl
handler (k args) ctx g

ANewHope e fc -> handler (k ()) (ctx { hopes = M.insert e fc (hopes ctx) }) g

AskHopes -> handler (k (hopes ctx)) ctx g

RemoveHope e -> let hset = hopes ctx in
if M.member e hset
then handler (k ()) (ctx { hopes = M.delete e hset }) g
else Left (dumbErr (InternalError ("Trying to remove unknown Hope: " ++ show e)))

type Checking = Free CheckingSig

instance Semigroup a => Semigroup (Checking a) where
Expand Down
169 changes: 169 additions & 0 deletions brat/Brat/Checker/SolveHoles.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
module Brat.Checker.SolveHoles (typeEq) where

import Brat.Checker.Helpers (buildConst)
import Brat.Checker.Monad
import Brat.Checker.Types (kindForMode)
import Brat.Error (ErrorMsg(..))
import Brat.Eval
import Brat.Syntax.CircuitProperties (eqProps)
import Brat.Syntax.Common
import Brat.Syntax.Simple (SimpleTerm(..))
import Brat.Syntax.Value
import Control.Monad.Freer
import Bwd
import Hasochism

import Control.Monad (when)
import Data.Bifunctor (second)
import Data.Foldable (sequenceA_)
import Data.Functor
import Data.Maybe (mapMaybe)
import qualified Data.Map as M
import Data.Type.Equality (TestEquality(..), (:~:)(..))

-- Demand that two closed values are equal, we're allowed to solve variables in the
-- hope set to make this true.
-- Raises a user error if the vals cannot be made equal.
typeEq :: String -- String representation of the term for error reporting
-> TypeKind -- The kind we're comparing at
-> Val Z -- Expected
-> Val Z -- Actual
-> Checking ()
typeEq str = typeEq' str (Zy :* S0 :* S0)


-- Internal version of typeEq with environment for non-closed values
typeEq' :: String -- String representation of the term for error reporting
-> (Ny :* Stack Z TypeKind :* Stack Z Sem) n
-> TypeKind -- The kind we're comparing at
-> Val n -- Expected
-> Val n -- Actual
-> Checking ()
typeEq' str stuff@(_ny :* _ks :* sems) k exp act = do
hopes <- req AskHopes
exp <- sem sems exp
act <- sem sems act
typeEqEta str stuff hopes k exp act

isNumVar :: Sem -> Maybe SVar
isNumVar (SNum (NumValue 0 (StrictMonoFun (StrictMono 0 (Linear v))))) = Just v
isNumVar _ = Nothing

-- Presumes that the hope set and the two `Sem`s are up to date.
typeEqEta :: String -- String representation of the term for error reporting
-> (Ny :* Stack Z TypeKind :* Stack Z Sem) n
-> Hopes -- A map from the hope set to corresponding FCs
-> TypeKind -- The kind we're comparing at
-> Sem -- Expected
-> Sem -- Actual
-> Checking ()
typeEqEta tm (lvy :* kz :* sems) hopes (TypeFor m ((_, k):ks)) exp act = do
-- Higher kinded things
let nextSem = semLvl lvy
let xz = B0 :< nextSem
exp <- applySem exp xz
act <- applySem act xz
typeEqEta tm (Sy lvy :* (kz :<< k) :* (sems :<< nextSem)) hopes (TypeFor m ks) exp act
-- Not higher kinded - check for flex terms
-- (We don't solve under binders for now, so we only consider Zy here)
-- 1. "easy" flex cases
typeEqEta _tm (Zy :* _ks :* _sems) hopes k (SApp (SPar (InEnd e)) B0) act
| M.member e hopes = solveHope k e act
typeEqEta _tm (Zy :* _ks :* _sems) hopes k exp (SApp (SPar (InEnd e)) B0)
| M.member e hopes = solveHope k e exp
typeEqEta _ (Zy :* _ :* _) hopes Nat exp act
| Just (SPar (InEnd e)) <- isNumVar exp, M.member e hopes = solveHope Nat e act
| Just (SPar (InEnd e)) <- isNumVar act, M.member e hopes = solveHope Nat e exp
-- 2. harder cases, neither is in the hope set, so we can't define it ourselves
typeEqEta tm stuff@(ny :* _ks :* _sems) hopes k exp act = do
exp <- quote ny exp
act <- quote ny act
let ends = mapMaybe getEnd [exp,act]
-- sanity check: we've already dealt with either end being in the hopeset
when (or [M.member ie hopes | InEnd ie <- ends]) $ typeErr "ends were in hopeset"
case ends of
[] -> typeEqRigid tm stuff k exp act -- easyish, both rigid i.e. already defined
-- variables are trivially the same, even if undefined, but the values may
-- be different! E.g. X =? 1 + X
[_, _] | exp == act -> pure ()
-- TODO: Once we have scheduling, we must wait for one or the other to become more defined, rather than failing
_ -> err (TypeMismatch tm (show exp) (show act))
where
getEnd (VApp (VPar e) _) = Just e
getEnd (VNum n) = getNumVar n
getEnd _ = Nothing

-- This will update the `hopes`, potentially invalidating things that have
-- been eval'd.
-- The Sem is closed, for now.
solveHope :: TypeKind -> InPort -> Sem -> Checking ()
solveHope k hope v = quote Zy v >>= \v -> case doesntOccur (InEnd hope) v of
Right () -> do
defineEnd (InEnd hope) v
dangling <- case (k, v) of
(Nat, VNum _v) -> err $ Unimplemented "Nat hope solving" []
(Nat, _) -> err $ InternalError "Head of Nat wasn't a VNum"
_ -> buildConst Unit TUnit
req (Wire (end dangling, kindType k, hope))
req (RemoveHope hope)
Left msg -> case v of
VApp (VPar (InEnd end)) B0 | hope == end -> pure ()
-- TODO: Not all occurrences are toxic. The end could be in an argument
-- to a hoping variable which isn't used.
-- E.g. h1 = h2 h1 - this is valid if h2 is the identity, or ignores h1.
_ -> err msg

typeEqs :: String -> (Ny :* Stack Z TypeKind :* Stack Z Sem) n -> [TypeKind] -> [Val n] -> [Val n] -> Checking ()
typeEqs _ _ [] [] [] = pure ()
typeEqs tm stuff (k:ks) (exp:exps) (act:acts) = typeEqs tm stuff ks exps acts <* typeEq' tm stuff k exp act
typeEqs _ _ _ _ _ = typeErr "arity mismatch"

typeEqRow :: Modey m
-> String -- The term we complain about in errors
-> (Ny :* Stack Z TypeKind :* Stack Z Sem) lv -- Next available level, the kinds of existing levels
-> Ro m lv top0
-> Ro m lv top1
-> Either ErrorMsg (Some ((Ny :* Stack Z TypeKind :* Stack Z Sem) -- The new stack of kinds and fresh level
:* ((:~:) top0 :* (:~:) top1)) -- Proofs both input rows have same length (quantified over by Some)
,[Checking ()] -- subproblems to run in parallel
)
typeEqRow _ _ stuff R0 R0 = pure (Some (stuff :* (Refl :* Refl)), [])
typeEqRow m tm stuff (RPr (_,ty1) ro1) (RPr (_,ty2) ro2) = typeEqRow m tm stuff ro1 ro2 <&> second
((:) (typeEq' tm stuff (kindForMode m) ty1 ty2))
typeEqRow m tm (ny :* kz :* semz) (REx (_,k1) ro1) (REx (_,k2) ro2) | k1 == k2 = typeEqRow m tm (Sy ny :* (kz :<< k1) :* (semz :<< semLvl ny)) ro1 ro2
typeEqRow _ _ _ _ _ = Left $ TypeErr "Mismatched rows"

-- Calls to typeEqRigid *must* start with rigid types to ensure termination
typeEqRigid :: String -- String representation of the term for error reporting
-> (Ny :* Stack Z TypeKind :* Stack Z Sem) n
-> TypeKind -- The kind we're comparing at
-> Val n -- Expected
-> Val n -- Actual
-> Checking ()
typeEqRigid tm (_ :* _ :* semz) Nat exp act = do
-- TODO: What if there's hope in the numbers?
exp <- sem semz exp
act <- sem semz act
if getNum exp == getNum act
then pure ()
else err $ TypeMismatch tm (show exp) (show act)
typeEqRigid tm stuff@(_ :* kz :* _) (TypeFor m []) (VApp f args) (VApp f' args') | f == f' =
svKind f >>= \case
TypeFor m' ks | m == m' -> typeEqs tm stuff (snd <$> ks) (args <>> []) (args' <>> [])
-- pattern should always match
_ -> err $ InternalError "quote gave a surprising result"
where
svKind (VPar e) = kindOf (VPar e)
svKind (VInx n) = pure $ proj kz n
typeEqRigid tm lvkz (TypeFor m []) (VCon c args) (VCon c' args') | c == c' =
req (TLup (m, c)) >>= \case
Just ks -> typeEqs tm lvkz (snd <$> ks) args args'
Nothing -> err $ TypeErr $ "Type constructor " ++ show c
++ " undefined " ++ " at kind " ++ show (TypeFor m [])
typeEqRigid tm lvkz (Star []) (VFun m0 (FunTy ps0 ins0 outs0)) (VFun m1 (FunTy ps1 ins1 outs1))
| Just Refl <- testEquality m0 m1
, eqProps m0 ps0 ps1 = do
probs :: [Checking ()] <- throwLeft $ typeEqRow m0 tm lvkz ins0 ins1 >>= \case -- this is in Either ErrorMsg
(Some (lvkz :* (Refl :* Refl)), ps1) -> typeEqRow m0 tm lvkz outs0 outs1 <&> (ps1++) . snd
sequenceA_ probs -- uses Applicative (unlike sequence_ which uses Monad), hence parallelized
typeEqRigid tm _ _ v0 v1 = err $ TypeMismatch tm (show v0) (show v1)
Loading

0 comments on commit 8918482

Please sign in to comment.