2018-04-24 11:14:27 +02:00
|
|
|
{-# LANGUAGE AllowAmbiguousTypes #-}
|
2018-04-07 21:02:50 +02:00
|
|
|
{-# LANGUAGE ConstraintKinds #-}
|
2018-04-24 11:14:27 +02:00
|
|
|
{-# LANGUAGE DataKinds #-}
|
2018-04-07 21:02:50 +02:00
|
|
|
{-# LANGUAGE DeriveTraversable #-}
|
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# LANGUAGE FlexibleInstances #-}
|
2018-04-24 11:14:27 +02:00
|
|
|
{-# LANGUAGE GADTs #-}
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
2018-04-07 21:02:50 +02:00
|
|
|
{-# LANGUAGE LambdaCase #-}
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
2018-04-07 21:02:50 +02:00
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
{-# LANGUAGE TupleSections #-}
|
|
|
|
{-# LANGUAGE TypeApplications #-}
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
{-# LANGUAGE UndecidableInstances #-}
|
2018-04-07 21:02:50 +02:00
|
|
|
|
|
|
|
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
|
2018-04-16 08:00:02 +02:00
|
|
|
{-# OPTIONS_GHC -Wno-missing-methods #-}
|
2018-04-07 21:02:50 +02:00
|
|
|
|
|
|
|
module Nix.Lint where
|
|
|
|
|
|
|
|
import Control.Monad
|
2018-04-11 06:01:48 +02:00
|
|
|
import Control.Monad.Catch
|
2018-04-07 21:02:50 +02:00
|
|
|
import Control.Monad.Fix
|
2019-03-17 22:47:38 +01:00
|
|
|
import Control.Monad.Reader ( MonadReader )
|
2018-11-16 20:04:14 +01:00
|
|
|
import Control.Monad.Ref
|
2018-04-11 06:01:48 +02:00
|
|
|
import Control.Monad.ST
|
|
|
|
import Control.Monad.Trans.Reader
|
2019-03-17 22:47:38 +01:00
|
|
|
import Data.HashMap.Lazy ( HashMap )
|
|
|
|
import qualified Data.HashMap.Lazy as M
|
2018-04-07 21:02:50 +02:00
|
|
|
import Data.List
|
2019-03-17 22:47:38 +01:00
|
|
|
import qualified Data.List.NonEmpty as NE
|
|
|
|
import Data.Text ( Text )
|
|
|
|
import qualified Data.Text as Text
|
2018-04-07 21:02:50 +02:00
|
|
|
import Nix.Atoms
|
2018-04-11 06:01:48 +02:00
|
|
|
import Nix.Context
|
2018-04-15 10:43:01 +02:00
|
|
|
import Nix.Convert
|
2019-03-17 22:47:38 +01:00
|
|
|
import Nix.Eval ( MonadEval(..) )
|
|
|
|
import qualified Nix.Eval as Eval
|
2018-04-07 21:02:50 +02:00
|
|
|
import Nix.Expr
|
2018-04-24 11:14:27 +02:00
|
|
|
import Nix.Frames
|
2019-03-10 16:47:10 +01:00
|
|
|
import Nix.Fresh
|
2018-09-23 22:03:44 +02:00
|
|
|
import Nix.String
|
2018-04-18 02:25:59 +02:00
|
|
|
import Nix.Options
|
2018-04-07 21:02:50 +02:00
|
|
|
import Nix.Scope
|
2019-03-19 02:55:59 +01:00
|
|
|
import Nix.Thunk
|
|
|
|
import Nix.Thunk.Basic
|
2018-04-07 21:02:50 +02:00
|
|
|
import Nix.Utils
|
2019-03-11 16:04:15 +01:00
|
|
|
import Nix.Var
|
2019-03-18 22:01:48 +01:00
|
|
|
import Nix.Value.Monad
|
2018-04-07 21:02:50 +02:00
|
|
|
|
|
|
|
data TAtom
|
|
|
|
= TInt
|
|
|
|
| TFloat
|
|
|
|
| TBool
|
|
|
|
| TNull
|
|
|
|
deriving (Show, Eq, Ord)
|
|
|
|
|
|
|
|
data NTypeF (m :: * -> *) r
|
|
|
|
= TConstant [TAtom]
|
|
|
|
| TStr
|
|
|
|
| TList r
|
|
|
|
| TSet (Maybe (HashMap Text r))
|
2018-05-02 22:46:32 +02:00
|
|
|
| TClosure (Params ())
|
2018-04-07 21:02:50 +02:00
|
|
|
| TPath
|
2019-03-18 22:01:48 +01:00
|
|
|
| TBuiltin String (Symbolic m -> m r)
|
2018-04-07 21:02:50 +02:00
|
|
|
deriving Functor
|
|
|
|
|
|
|
|
compareTypes :: NTypeF m r -> NTypeF m r -> Ordering
|
2019-03-17 22:47:38 +01:00
|
|
|
compareTypes (TConstant _) (TConstant _) = EQ
|
|
|
|
compareTypes (TConstant _) _ = LT
|
|
|
|
compareTypes _ (TConstant _) = GT
|
|
|
|
compareTypes TStr TStr = EQ
|
|
|
|
compareTypes TStr _ = LT
|
|
|
|
compareTypes _ TStr = GT
|
|
|
|
compareTypes (TList _) (TList _) = EQ
|
|
|
|
compareTypes (TList _) _ = LT
|
|
|
|
compareTypes _ (TList _) = GT
|
|
|
|
compareTypes (TSet _) (TSet _) = EQ
|
|
|
|
compareTypes (TSet _) _ = LT
|
|
|
|
compareTypes _ (TSet _) = GT
|
|
|
|
compareTypes TClosure{} TClosure{} = EQ
|
|
|
|
compareTypes TClosure{} _ = LT
|
|
|
|
compareTypes _ TClosure{} = GT
|
|
|
|
compareTypes TPath TPath = EQ
|
|
|
|
compareTypes TPath _ = LT
|
|
|
|
compareTypes _ TPath = GT
|
|
|
|
compareTypes (TBuiltin _ _) (TBuiltin _ _) = EQ
|
2018-04-07 21:02:50 +02:00
|
|
|
|
|
|
|
data NSymbolicF r
|
|
|
|
= NAny
|
|
|
|
| NMany [r]
|
|
|
|
deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
|
|
|
|
|
2019-03-19 02:55:59 +01:00
|
|
|
type SThunk (m :: * -> *) = NThunkF m (Symbolic m)
|
2018-04-07 21:02:50 +02:00
|
|
|
|
2019-03-19 02:55:59 +01:00
|
|
|
type SValue (m :: * -> *) = Var m (NSymbolicF (NTypeF m (Symbolic m)))
|
|
|
|
|
|
|
|
data Symbolic m = SV { getSV :: SValue m } | ST { getST :: SThunk m }
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
instance Show (Symbolic m) where
|
2019-03-17 22:47:38 +01:00
|
|
|
show _ = "<symbolic>"
|
2018-04-07 21:02:50 +02:00
|
|
|
|
|
|
|
everyPossible :: MonadVar m => m (Symbolic m)
|
|
|
|
everyPossible = packSymbolic NAny
|
|
|
|
|
2019-03-18 22:01:48 +01:00
|
|
|
mkSymbolic :: MonadVar m => [NTypeF m (Symbolic m)] -> m (Symbolic m)
|
2018-04-07 21:02:50 +02:00
|
|
|
mkSymbolic xs = packSymbolic (NMany xs)
|
|
|
|
|
2019-03-19 05:47:43 +01:00
|
|
|
packSymbolic
|
|
|
|
:: MonadVar m => NSymbolicF (NTypeF m (Symbolic m)) -> m (Symbolic m)
|
2019-03-19 02:55:59 +01:00
|
|
|
packSymbolic = fmap SV . newVar
|
2018-04-07 21:02:50 +02:00
|
|
|
|
2019-03-19 05:47:43 +01:00
|
|
|
unpackSymbolic
|
|
|
|
:: (MonadVar m, MonadThunkId m, MonadCatch m)
|
|
|
|
=> Symbolic m
|
|
|
|
-> m (NSymbolicF (NTypeF m (Symbolic m)))
|
2019-03-19 02:55:59 +01:00
|
|
|
unpackSymbolic = flip demand $ readVar . getSV
|
2018-04-07 21:02:50 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
type MonadLint e m
|
2019-03-19 05:47:43 +01:00
|
|
|
= ( Scoped (Symbolic m) m
|
|
|
|
, Framed e m
|
|
|
|
, MonadVar m
|
|
|
|
, MonadCatch m
|
|
|
|
, MonadThunkId m
|
|
|
|
)
|
2018-04-11 20:53:30 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
symerr :: forall e m a . MonadLint e m => String -> m a
|
2018-05-02 02:33:17 +02:00
|
|
|
symerr = evalError @(Symbolic m) . ErrorCall
|
2018-04-11 20:53:30 +02:00
|
|
|
|
2018-04-11 06:01:48 +02:00
|
|
|
renderSymbolic :: MonadLint e m => Symbolic m -> m String
|
2018-04-07 21:02:50 +02:00
|
|
|
renderSymbolic = unpackSymbolic >=> \case
|
2020-09-18 14:54:39 +02:00
|
|
|
NAny -> pure "<any>"
|
2019-03-17 22:47:38 +01:00
|
|
|
NMany xs -> fmap (intercalate ", ") $ forM xs $ \case
|
|
|
|
TConstant ys -> fmap (intercalate ", ") $ forM ys $ \case
|
2020-09-18 14:54:39 +02:00
|
|
|
TInt -> pure "int"
|
|
|
|
TFloat -> pure "float"
|
|
|
|
TBool -> pure "bool"
|
|
|
|
TNull -> pure "null"
|
|
|
|
TStr -> pure "string"
|
2019-03-17 22:47:38 +01:00
|
|
|
TList r -> do
|
2019-03-18 22:01:48 +01:00
|
|
|
x <- demand r renderSymbolic
|
2020-09-18 14:54:39 +02:00
|
|
|
pure $ "[" ++ x ++ "]"
|
|
|
|
TSet Nothing -> pure "<any set>"
|
2019-03-17 22:47:38 +01:00
|
|
|
TSet (Just s) -> do
|
2019-03-18 22:01:48 +01:00
|
|
|
x <- traverse (`demand` renderSymbolic) s
|
2020-09-18 14:54:39 +02:00
|
|
|
pure $ "{" ++ show x ++ "}"
|
2019-03-17 22:47:38 +01:00
|
|
|
f@(TClosure p) -> do
|
|
|
|
(args, sym) <- do
|
|
|
|
f' <- mkSymbolic [f]
|
|
|
|
lintApp (NAbs (void p) ()) f' everyPossible
|
|
|
|
args' <- traverse renderSymbolic args
|
|
|
|
sym' <- renderSymbolic sym
|
2020-09-18 14:54:39 +02:00
|
|
|
pure $ "(" ++ show args' ++ " -> " ++ sym' ++ ")"
|
|
|
|
TPath -> pure "path"
|
|
|
|
TBuiltin _n _f -> pure "<builtin function>"
|
2018-04-07 21:02:50 +02:00
|
|
|
|
|
|
|
-- This function is order and uniqueness preserving (of types).
|
2019-03-17 22:47:38 +01:00
|
|
|
merge
|
|
|
|
:: forall e m
|
|
|
|
. MonadLint e m
|
|
|
|
=> NExprF ()
|
2019-03-18 22:01:48 +01:00
|
|
|
-> [NTypeF m (Symbolic m)]
|
|
|
|
-> [NTypeF m (Symbolic m)]
|
|
|
|
-> m [NTypeF m (Symbolic m)]
|
2018-04-07 21:02:50 +02:00
|
|
|
merge context = go
|
2019-03-17 22:47:38 +01:00
|
|
|
where
|
|
|
|
go
|
2019-03-19 05:47:43 +01:00
|
|
|
:: [NTypeF m (Symbolic m)]
|
|
|
|
-> [NTypeF m (Symbolic m)]
|
|
|
|
-> m [NTypeF m (Symbolic m)]
|
2020-09-18 14:54:39 +02:00
|
|
|
go [] _ = pure []
|
|
|
|
go _ [] = pure []
|
2019-03-17 22:47:38 +01:00
|
|
|
go (x : xs) (y : ys) = case (x, y) of
|
|
|
|
(TStr , TStr ) -> (TStr :) <$> go xs ys
|
|
|
|
(TPath, TPath) -> (TPath :) <$> go xs ys
|
|
|
|
(TConstant ls, TConstant rs) ->
|
|
|
|
(TConstant (ls `intersect` rs) :) <$> go xs ys
|
2019-03-18 22:01:48 +01:00
|
|
|
(TList l, TList r) -> demand l $ \l' -> demand r $ \r' -> do
|
|
|
|
m <- defer $ unify context l' r'
|
2019-03-17 22:47:38 +01:00
|
|
|
(TList m :) <$> go xs ys
|
|
|
|
(TSet x , TSet Nothing ) -> (TSet x :) <$> go xs ys
|
|
|
|
(TSet Nothing , TSet x ) -> (TSet x :) <$> go xs ys
|
|
|
|
(TSet (Just l), TSet (Just r)) -> do
|
|
|
|
m <- sequenceA $ M.intersectionWith
|
|
|
|
(\i j -> i >>= \i' ->
|
|
|
|
j
|
2019-03-18 22:01:48 +01:00
|
|
|
>>= \j' -> demand i'
|
|
|
|
$ \i'' -> demand j' $ \j'' -> defer $ unify context i'' j''
|
2019-03-17 22:47:38 +01:00
|
|
|
)
|
2020-09-18 14:54:39 +02:00
|
|
|
(pure <$> l)
|
|
|
|
(pure <$> r)
|
2019-03-17 22:47:38 +01:00
|
|
|
if M.null m then go xs ys else (TSet (Just m) :) <$> go xs ys
|
|
|
|
(TClosure{}, TClosure{}) ->
|
|
|
|
throwError $ ErrorCall "Cannot unify functions"
|
|
|
|
(TBuiltin _ _, TBuiltin _ _) ->
|
|
|
|
throwError $ ErrorCall "Cannot unify builtin functions"
|
|
|
|
_ | compareTypes x y == LT -> go xs (y : ys)
|
|
|
|
| compareTypes x y == GT -> go (x : xs) ys
|
|
|
|
| otherwise -> error "impossible"
|
2018-04-07 21:02:50 +02:00
|
|
|
|
|
|
|
{-
|
|
|
|
mergeFunctions pl nl fl pr fr xs ys = do
|
|
|
|
m <- sequenceA $ M.intersectionWith
|
|
|
|
(\i j -> i >>= \i' -> j >>= \j' -> case (i', j') of
|
|
|
|
(Nothing, Nothing) -> return $ Just Nothing
|
|
|
|
(_, Nothing) -> return Nothing
|
|
|
|
(Nothing, _) -> return Nothing
|
|
|
|
(Just i'', Just j'') ->
|
|
|
|
Just . Just <$> unify context i'' j'')
|
|
|
|
(return <$> pl) (return <$> pr)
|
|
|
|
let Just m' = sequenceA $ M.filter isJust m
|
|
|
|
if M.null m'
|
|
|
|
then go xs ys
|
|
|
|
else do
|
|
|
|
g <- unify context fl fr
|
|
|
|
(TClosure (ParamSet m' False nl) g :)
|
|
|
|
<$> go xs ys
|
|
|
|
-}
|
|
|
|
|
|
|
|
-- | unify raises an error if the result is would be 'NMany []'.
|
2019-03-17 22:47:38 +01:00
|
|
|
unify
|
|
|
|
:: forall e m
|
|
|
|
. MonadLint e m
|
|
|
|
=> NExprF ()
|
|
|
|
-> Symbolic m
|
|
|
|
-> Symbolic m
|
|
|
|
-> m (Symbolic m)
|
2019-03-19 02:55:59 +01:00
|
|
|
unify context (SV x) (SV y) = do
|
2019-03-17 22:47:38 +01:00
|
|
|
x' <- readVar x
|
|
|
|
y' <- readVar y
|
|
|
|
case (x', y') of
|
|
|
|
(NAny, _) -> do
|
|
|
|
writeVar x y'
|
2020-09-18 14:54:39 +02:00
|
|
|
pure $ SV y
|
2019-03-17 22:47:38 +01:00
|
|
|
(_, NAny) -> do
|
|
|
|
writeVar y x'
|
2020-09-18 14:54:39 +02:00
|
|
|
pure $ SV x
|
2019-03-17 22:47:38 +01:00
|
|
|
(NMany xs, NMany ys) -> do
|
|
|
|
m <- merge context xs ys
|
|
|
|
if null m
|
|
|
|
then do
|
|
|
|
-- x' <- renderSymbolic (Symbolic x)
|
|
|
|
-- y' <- renderSymbolic (Symbolic y)
|
|
|
|
throwError $ ErrorCall "Cannot unify "
|
|
|
|
-- ++ show x' ++ " with " ++ show y'
|
|
|
|
-- ++ " in context: " ++ show context
|
|
|
|
else do
|
|
|
|
writeVar x (NMany m)
|
|
|
|
writeVar y (NMany m)
|
|
|
|
packSymbolic (NMany m)
|
2019-03-19 02:55:59 +01:00
|
|
|
unify _ _ _ = error "The unexpected hath transpired!"
|
2018-04-07 21:02:50 +02:00
|
|
|
|
2018-04-27 02:13:22 +02:00
|
|
|
-- These aren't worth defining yet, because once we move to Hindley-Milner,
|
|
|
|
-- we're not going to be managing Symbolic values this way anymore.
|
2018-04-16 06:08:29 +02:00
|
|
|
|
2018-04-30 21:56:24 +02:00
|
|
|
instance ToValue Bool m (Symbolic m) where
|
2018-04-16 19:56:29 +02:00
|
|
|
|
2019-03-18 22:01:48 +01:00
|
|
|
instance ToValue [Symbolic m] m (Symbolic m) where
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2018-05-03 14:07:54 +02:00
|
|
|
instance FromValue NixString m (Symbolic m) where
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2019-03-18 22:01:48 +01:00
|
|
|
instance FromValue (AttrSet (Symbolic m), AttrSet SourcePos) m (Symbolic m) where
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2019-03-18 22:01:48 +01:00
|
|
|
instance ToValue (AttrSet (Symbolic m), AttrSet SourcePos) m (Symbolic m) where
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2019-03-19 02:55:59 +01:00
|
|
|
instance (MonadThunkId m, MonadAtomicRef m, MonadCatch m)
|
|
|
|
=> MonadValue (Symbolic m) m where
|
|
|
|
defer = fmap ST . thunk
|
|
|
|
demand (ST v) f = force v (flip demand f)
|
|
|
|
demand (SV v) f = f (SV v)
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
instance MonadLint e m => MonadEval (Symbolic m) m where
|
2019-03-17 22:47:38 +01:00
|
|
|
freeVariable var = symerr $ "Undefined variable '" ++ Text.unpack var ++ "'"
|
|
|
|
|
|
|
|
attrMissing ks Nothing =
|
|
|
|
evalError @(Symbolic m)
|
|
|
|
$ ErrorCall
|
|
|
|
$ "Inheriting unknown attribute: "
|
|
|
|
++ intercalate "." (map Text.unpack (NE.toList ks))
|
|
|
|
|
|
|
|
attrMissing ks (Just s) =
|
|
|
|
evalError @(Symbolic m)
|
|
|
|
$ ErrorCall
|
|
|
|
$ "Could not look up attribute "
|
|
|
|
++ intercalate "." (map Text.unpack (NE.toList ks))
|
|
|
|
++ " in "
|
|
|
|
++ show s
|
|
|
|
|
|
|
|
evalCurPos = do
|
2019-03-18 22:01:48 +01:00
|
|
|
f <- mkSymbolic [TPath]
|
|
|
|
l <- mkSymbolic [TConstant [TInt]]
|
|
|
|
c <- mkSymbolic [TConstant [TInt]]
|
2019-03-17 22:47:38 +01:00
|
|
|
mkSymbolic [TSet (Just (M.fromList (go f l c)))]
|
|
|
|
where
|
|
|
|
go f l c =
|
|
|
|
[(Text.pack "file", f), (Text.pack "line", l), (Text.pack "col", c)]
|
|
|
|
|
2019-05-16 21:42:49 +02:00
|
|
|
evalConstant c = mkSymbolic [go c]
|
2019-03-17 22:47:38 +01:00
|
|
|
where
|
|
|
|
go = \case
|
2019-05-16 21:42:49 +02:00
|
|
|
NURI _ -> TStr
|
|
|
|
NInt _ -> TConstant [TInt]
|
|
|
|
NFloat _ -> TConstant [TFloat]
|
|
|
|
NBool _ -> TConstant [TBool]
|
|
|
|
NNull -> TConstant [TNull]
|
2019-03-17 22:47:38 +01:00
|
|
|
|
|
|
|
evalString = const $ mkSymbolic [TStr]
|
|
|
|
evalLiteralPath = const $ mkSymbolic [TPath]
|
|
|
|
evalEnvPath = const $ mkSymbolic [TPath]
|
|
|
|
|
|
|
|
evalUnary op arg =
|
|
|
|
unify (void (NUnary op arg)) arg =<< mkSymbolic [TConstant [TInt, TBool]]
|
|
|
|
|
|
|
|
evalBinary = lintBinaryOp
|
|
|
|
|
|
|
|
-- The scope is deliberately wrapped in a thunk here, since it is evaluated
|
|
|
|
-- each time a name is looked up within the weak scope, and we want to be
|
|
|
|
-- sure the action it evaluates is to force a thunk, so its value is only
|
|
|
|
-- computed once.
|
|
|
|
evalWith scope body = do
|
2019-03-18 22:01:48 +01:00
|
|
|
s <- defer scope
|
|
|
|
pushWeakScope ?? body $ demand s $ unpackSymbolic >=> \case
|
2020-09-18 14:54:39 +02:00
|
|
|
NMany [TSet (Just s')] -> pure s'
|
2019-03-17 22:47:38 +01:00
|
|
|
NMany [TSet Nothing] -> error "NYI: with unknown"
|
|
|
|
_ -> throwError $ ErrorCall "scope must be a set in with statement"
|
|
|
|
|
|
|
|
evalIf cond t f = do
|
|
|
|
t' <- t
|
|
|
|
f' <- f
|
|
|
|
let e = NIf cond t' f'
|
|
|
|
_ <- unify (void e) cond =<< mkSymbolic [TConstant [TBool]]
|
|
|
|
unify (void e) t' f'
|
|
|
|
|
|
|
|
evalAssert cond body = do
|
|
|
|
body' <- body
|
|
|
|
let e = NAssert cond body'
|
|
|
|
_ <- unify (void e) cond =<< mkSymbolic [TConstant [TBool]]
|
|
|
|
pure body'
|
|
|
|
|
|
|
|
evalApp = (fmap snd .) . lintApp (NBinary NApp () ())
|
|
|
|
evalAbs params _ = mkSymbolic [TClosure (void params)]
|
|
|
|
|
|
|
|
evalError = throwError
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
lintBinaryOp
|
2019-03-17 22:47:38 +01:00
|
|
|
:: forall e m
|
|
|
|
. (MonadLint e m, MonadEval (Symbolic m) m)
|
|
|
|
=> NBinaryOp
|
|
|
|
-> Symbolic m
|
|
|
|
-> m (Symbolic m)
|
|
|
|
-> m (Symbolic m)
|
2018-04-11 06:01:48 +02:00
|
|
|
lintBinaryOp op lsym rarg = do
|
2019-03-17 22:47:38 +01:00
|
|
|
rsym <- rarg
|
2019-03-18 22:01:48 +01:00
|
|
|
y <- defer everyPossible
|
2019-03-17 22:47:38 +01:00
|
|
|
case op of
|
|
|
|
NApp -> symerr "lintBinaryOp:NApp: should never get here"
|
|
|
|
NEq -> check lsym rsym [TConstant [TInt, TBool, TNull], TStr, TList y]
|
|
|
|
NNEq -> check lsym rsym [TConstant [TInt, TBool, TNull], TStr, TList y]
|
|
|
|
|
|
|
|
NLt -> check lsym rsym [TConstant [TInt, TBool, TNull]]
|
|
|
|
NLte -> check lsym rsym [TConstant [TInt, TBool, TNull]]
|
|
|
|
NGt -> check lsym rsym [TConstant [TInt, TBool, TNull]]
|
|
|
|
NGte -> check lsym rsym [TConstant [TInt, TBool, TNull]]
|
|
|
|
|
|
|
|
NAnd -> check lsym rsym [TConstant [TBool]]
|
|
|
|
NOr -> check lsym rsym [TConstant [TBool]]
|
|
|
|
NImpl -> check lsym rsym [TConstant [TBool]]
|
|
|
|
|
|
|
|
-- jww (2018-04-01): NYI: Allow Path + Str
|
|
|
|
NPlus -> check lsym rsym [TConstant [TInt], TStr, TPath]
|
|
|
|
NMinus -> check lsym rsym [TConstant [TInt]]
|
|
|
|
NMult -> check lsym rsym [TConstant [TInt]]
|
|
|
|
NDiv -> check lsym rsym [TConstant [TInt]]
|
|
|
|
|
|
|
|
NUpdate -> check lsym rsym [TSet Nothing]
|
|
|
|
|
|
|
|
NConcat -> check lsym rsym [TList y]
|
|
|
|
where
|
|
|
|
check lsym rsym xs = do
|
|
|
|
let e = NBinary op lsym rsym
|
|
|
|
m <- mkSymbolic xs
|
|
|
|
_ <- unify (void e) lsym m
|
|
|
|
_ <- unify (void e) rsym m
|
|
|
|
unify (void e) lsym rsym
|
2018-04-07 21:02:50 +02:00
|
|
|
|
|
|
|
infixl 1 `lintApp`
|
2019-03-17 22:47:38 +01:00
|
|
|
lintApp
|
|
|
|
:: forall e m
|
|
|
|
. MonadLint e m
|
|
|
|
=> NExprF ()
|
|
|
|
-> Symbolic m
|
|
|
|
-> m (Symbolic m)
|
|
|
|
-> m (HashMap VarName (Symbolic m), Symbolic m)
|
2018-04-11 06:01:48 +02:00
|
|
|
lintApp context fun arg = unpackSymbolic fun >>= \case
|
2019-03-17 22:47:38 +01:00
|
|
|
NAny ->
|
|
|
|
throwError $ ErrorCall "Cannot apply something not known to be a function"
|
|
|
|
NMany xs -> do
|
|
|
|
(args, ys) <- fmap unzip $ forM xs $ \case
|
|
|
|
TClosure _params -> arg >>= unpackSymbolic >>= \case
|
|
|
|
NAny -> do
|
|
|
|
error "NYI"
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
NMany [TSet (Just _)] -> do
|
|
|
|
error "NYI"
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
NMany _ -> throwError $ ErrorCall "NYI: lintApp NMany not set"
|
|
|
|
TBuiltin _ _f -> throwError $ ErrorCall "NYI: lintApp builtin"
|
|
|
|
TSet _m -> throwError $ ErrorCall "NYI: lintApp Set"
|
|
|
|
_x -> throwError $ ErrorCall "Attempt to call non-function"
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
y <- everyPossible
|
|
|
|
(head args, ) <$> foldM (unify context) y ys
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
newtype Lint s a = Lint
|
2019-03-18 22:01:48 +01:00
|
|
|
{ runLint :: ReaderT (Context (Lint s) (Symbolic (Lint s))) (FreshIdT Int (ST s)) a }
|
2019-03-09 23:10:59 +01:00
|
|
|
deriving
|
|
|
|
( Functor
|
|
|
|
, Applicative
|
|
|
|
, Monad
|
|
|
|
, MonadFix
|
2019-03-18 22:01:48 +01:00
|
|
|
, MonadReader (Context (Lint s) (Symbolic (Lint s)))
|
2019-03-19 02:55:59 +01:00
|
|
|
, MonadThunkId
|
2019-03-09 23:10:59 +01:00
|
|
|
, MonadRef
|
|
|
|
, MonadAtomicRef
|
|
|
|
)
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
instance MonadThrow (Lint s) where
|
2019-03-17 22:47:38 +01:00
|
|
|
throwM e = Lint $ ReaderT $ \_ -> throw e
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2018-09-09 17:01:09 +02:00
|
|
|
instance MonadCatch (Lint s) where
|
2019-03-17 22:47:38 +01:00
|
|
|
catch _m _h = Lint $ ReaderT $ \_ -> error "Cannot catch in 'Lint s'"
|
2018-09-09 17:01:09 +02:00
|
|
|
|
2018-04-18 02:25:59 +02:00
|
|
|
runLintM :: Options -> Lint s a -> ST s a
|
2019-03-16 20:48:07 +01:00
|
|
|
runLintM opts action = do
|
2019-03-17 22:47:38 +01:00
|
|
|
i <- newVar (1 :: Int)
|
|
|
|
runFreshIdT i $ flip runReaderT (newContext opts) $ runLint action
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2019-03-18 22:01:48 +01:00
|
|
|
symbolicBaseEnv :: Monad m => m (Scopes m (Symbolic m))
|
2020-09-18 14:54:39 +02:00
|
|
|
symbolicBaseEnv = pure emptyScopes
|
2018-04-11 06:01:48 +02:00
|
|
|
|
2018-04-18 02:25:59 +02:00
|
|
|
lint :: Options -> NExprLoc -> ST s (Symbolic (Lint s))
|
2019-03-17 22:47:38 +01:00
|
|
|
lint opts expr =
|
|
|
|
runLintM opts
|
|
|
|
$ symbolicBaseEnv
|
|
|
|
>>= (`pushScopes` adi (Eval.eval . annotated . getCompose)
|
|
|
|
Eval.addSourcePositions
|
|
|
|
expr
|
|
|
|
)
|
2018-11-17 22:21:03 +01:00
|
|
|
|
2019-03-18 22:01:48 +01:00
|
|
|
instance Scoped (Symbolic (Lint s)) (Lint s) where
|
2018-11-17 22:21:03 +01:00
|
|
|
currentScopes = currentScopesReader
|
2019-03-18 22:01:48 +01:00
|
|
|
clearScopes = clearScopesReader @(Lint s) @(Symbolic (Lint s))
|
2019-03-17 22:47:38 +01:00
|
|
|
pushScopes = pushScopesReader
|
|
|
|
lookupVar = lookupVarReader
|