module Cryptol.TypeCheck.Default where
import qualified Data.Set as Set
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe(mapMaybe)
import Data.List((\\),nub)
import Control.Monad(guard,mzero)
import Control.Applicative((<|>))
import Cryptol.TypeCheck.Type
import Cryptol.TypeCheck.SimpType(tMax)
import Cryptol.TypeCheck.Error(Warning(..), Error(..))
import Cryptol.TypeCheck.Subst(Subst,apSubst,listSubst,substBinds,uncheckedSingleSubst)
import Cryptol.TypeCheck.InferTypes(Goal,goal,Goals(..),goalsFromList)
import Cryptol.TypeCheck.Solver.SMT(Solver,tryGetModel,shrinkModel)
import Cryptol.Utils.Panic(panic)
defaultLiterals :: [TVar] -> [Goal] -> ([TVar], Subst, [Warning])
defaultLiterals as gs = let (binds,warns) = unzip (mapMaybe tryDefVar as)
in (as \\ map fst binds, listSubst binds, warns)
where
gSet = goalsFromList gs
allProps = saturatedPropSet gSet
flitCandidates = flitDefaultCandidates gSet
tryDefVar a =
Map.lookup a flitCandidates
<|>
do _gt <- Map.lookup a (literalGoals gSet)
defT <- if Set.member (pLogic (TVar a)) allProps then
mzero
else if Set.member (pField (TVar a)) allProps then
pure tRational
else
pure tInteger
let d = tvInfo a
w = DefaultingTo d defT
guard (not (Set.member a (fvs defT)))
return ((a,defT),w)
flitDefaultCandidates :: Goals -> Map TVar ((TVar,Type),Warning)
flitDefaultCandidates gs =
Map.fromList (mapMaybe flitCandidate (Set.toList (goalSet gs)))
where
flitCandidate g =
do (_,_,_,x) <- pIsFLiteral (goal g)
a <- tIsVar x
guard (not (Set.member (pLogic (TVar a)) (saturatedPropSet gs)))
let defT = tRational
let w = DefaultingTo (tvInfo a) defT
pure (a, ((a,defT),w))
improveByDefaultingWithPure :: [TVar] -> [Goal] ->
( [TVar]
, [Goal]
, Subst
, [Error]
)
improveByDefaultingWithPure as ps =
classify (Map.fromList [ (a,([],Set.empty)) | a <- as ]) [] [] ps
where
classify leqs fins others [] =
let
(defs, newOthers) = select [] [] (fvs others) (Map.toList leqs)
su = listSubst defs
names = substBinds su
mkErr (x,t) =
case x of
TVFree _ _ _ d
| Just 0 <- tIsNum t -> AmbiguousSize d Nothing
| otherwise -> AmbiguousSize d (Just t)
TVBound {} -> panic "Crypto.TypeCheck.Infer"
[ "tryDefault attempted to default a quantified variable."
]
in ( [ a | a <- as, not (a `Set.member` names) ]
, newOthers ++ others ++ nub (apSubst su fins)
, su
, map mkErr defs
)
classify leqs fins others (prop : more) =
case tNoUser (goal prop) of
TCon (PC PFin) [ _ ] -> classify leqs (prop : fins) others more
TCon (PC PGeq) [ TVar x, t ]
| x `elem` as && x `Set.notMember` freeRHS ->
classify leqs' fins others more
where freeRHS = fvs t
add (xs1,vs1) (xs2,vs2) = (xs1 ++ xs2, Set.union vs1 vs2)
leqs' = Map.insertWith add x ([(t,prop)],freeRHS) leqs
_ -> classify leqs fins (prop : others) more
select yes no _ [] = ([ (x, t) | (x,t) <- yes ] ,no)
select yes no otherFree ((x,(rhsG,vs)) : more) =
select newYes newNo newFree newMore
where
(ts,gs) = unzip rhsG
(newYes,newNo,newFree,newMore)
| x `Set.member` otherFree = noDefaulting
| otherwise =
let deps = [ y | (y,(_,yvs)) <- more, x `Set.member` yvs ]
recs = filter (`Set.member` vs) deps
in if not (null recs) || isBoundTV x
then noDefaulting
else yesDefaulting
where
noDefaulting = ( yes, gs ++ no, vs `Set.union` otherFree, more )
yesDefaulting =
let ty = case ts of
[] -> tNum (0::Int)
_ -> foldr1 tMax ts
su1 = uncheckedSingleSubst x ty
in ( (x,ty) : [ (y,apSubst su1 t) | (y,t) <- yes ]
, no
, otherFree
, [ (y, (apSubst su1 ts1, vs1)) | (y,(ts1,vs1)) <- more ]
)
defaultReplExpr' :: Solver -> [TParam] -> [Prop] -> IO (Maybe [ (TParam,Type) ])
defaultReplExpr' sol as props =
do let params = map tpVar as
mb <- tryGetModel sol params props
case mb of
Nothing -> return Nothing
Just mdl0 ->
do mdl <- shrinkModel sol params props mdl0
let su = listSubst [ (x, tNat' n) | (x,n) <- mdl ]
return $
do guard (null (concatMap pSplitAnd (apSubst su props)))
tys <- mapM (bindParam su) params
return (zip as tys)
where
bindParam su tp =
do let ty = TVar tp
ty' = apSubst su ty
guard (ty /= ty')
return ty'