Merge branch 'master' into vs-context-primops

This commit is contained in:
Ken Micklas 2019-03-10 13:48:58 -04:00
commit 718c94e0bb
30 changed files with 521 additions and 205 deletions

5
.gitignore vendored
View file

@ -1,5 +1,5 @@
**/*~
**/#* **/#*
**/*~
**/.#* **/.#*
/.history /.history
/Setup /Setup
@ -7,6 +7,7 @@
/nix-test-eval* /nix-test-eval*
/nix/ /nix/
TAGS TAGS
cabal.project*
ctags ctags
dist-newstyle dist-newstyle
result result

View file

@ -15,8 +15,8 @@ env:
- secure: "dm6I+M4+V+C7QMTpcSADdKPE633SvmToXZrTbZ7miNDGmMN+/SfHeN2ybi1+PW6oViMlbPN/7J/aEfiGjSJI8vLk72Y4uCWGmpSb8TXZLu6+whnxtZzzW8+z4tsM4048QJg7CF3N/25U8thRFgs3DqUub1Sf3nG9LrNWdz6ZcDQ=" - secure: "dm6I+M4+V+C7QMTpcSADdKPE633SvmToXZrTbZ7miNDGmMN+/SfHeN2ybi1+PW6oViMlbPN/7J/aEfiGjSJI8vLk72Y4uCWGmpSb8TXZLu6+whnxtZzzW8+z4tsM4048QJg7CF3N/25U8thRFgs3DqUub1Sf3nG9LrNWdz6ZcDQ="
matrix: matrix:
- GHCVERSION=ghc844 STRICT=false TRACING=false - GHCVERSION=ghc863 STRICT=false TRACING=false
- GHCVERSION=ghc844 STRICT=false TRACING=true - GHCVERSION=ghc863 STRICT=false TRACING=true
# - GHCVERSION=ghcjs # - GHCVERSION=ghcjs
# #
# matrix: # matrix:

View file

@ -24,7 +24,7 @@ $ cabal test
# To run all of the tests, which takes up to a minute: # To run all of the tests, which takes up to a minute:
$ env ALL_TESTS=yes cabal test $ env ALL_TESTS=yes cabal test
# To run only specific tests (see `tests/Main.hs` for a list) # To run only specific tests (see `tests/Main.hs` for a list)
$ env NIXPKGS_TESTS=yes PRETTY_TESTS=yes cabal test $ env NIXPKGS_TESTS=yes PRETTY_TESTS=1 cabal test
$ ./dist/build/hnix/hnix --help $ ./dist/build/hnix/hnix --help
``` ```

View file

@ -1,4 +1,4 @@
{ compiler ? "ghc844" { compiler ? "ghc863"
, doBenchmark ? false , doBenchmark ? false
, doTracing ? false , doTracing ? false
@ -6,8 +6,10 @@
, doProfiling ? false # enables profiling support in GHC , doProfiling ? false # enables profiling support in GHC
, doStrict ? false , doStrict ? false
, rev ? "3f3f6021593070330091a4a2bc785f6761bbb3c1" , withHoogle ? false
, sha256 ? "1a7vvxxz8phff51vwsrdlsq5i70ig5hxvvb7lkm2lgwizgvpa6gv"
, rev ? "120eab94e0981758a1c928ff81229cd802053158"
, sha256 ? "0qk6k8gxx5xlkyg05dljywj5wx5fvrc3dzp4v2h6ab83b7zwg813"
, pkgs ? , pkgs ?
if builtins.compareVersions builtins.nixVersion "2.0" < 0 if builtins.compareVersions builtins.nixVersion "2.0" < 0
@ -17,37 +19,68 @@
inherit sha256; }) { inherit sha256; }) {
config.allowUnfree = true; config.allowUnfree = true;
config.allowBroken = false; config.allowBroken = false;
config.packageOverrides = pkgs: rec {
nix = pkgs.nixUnstable.overrideDerivation (attrs: {
src = data/nix;
configureFlags = attrs.configureFlags ++ [ "--disable-doc-gen" ];
buildInputs = attrs.buildInputs ++
[ pkgs.editline.dev
];
outputs = builtins.filter (s: s != "doc" && s != "man" ) attrs.outputs;
});
};
} }
, returnShellEnv ? pkgs.lib.inNixShell
, mkDerivation ? null , mkDerivation ? null
}: }:
let haskellPackages = pkgs.haskell.packages.${compiler}; let
hnix-store-src = pkgs.fetchFromGitHub {
owner = "haskell-nix";
repo = "hnix-store";
rev = "0fe7ff5e8492ce6141d0eb400685516b4d07594b";
sha256 = "1izqp4ma6bkvdjcxhkasjcv1p11l72hdnm4dqmnnpkbmw70xrp36";
};
overlay = pkgs.lib.foldr pkgs.lib.composeExtensions (_: _: {}) [
(import "${hnix-store-src}/overlay.nix")
(self: super: with pkgs.haskell.lib; {
mono-traversable = dontCheck super.mono-traversable;
these = doJailbreak super.these;
} // pkgs.lib.optionalAttrs withHoogle {
ghc = super.ghc // { withPackages = super.ghc.withHoogle; };
ghcWithPackages = self.ghc.withPackages;
})
];
overrideHaskellPackages = orig: {
buildHaskellPackages =
orig.buildHaskellPackages.override overrideHaskellPackages;
overrides = if orig ? overrides
then pkgs.lib.composeExtensions orig.overrides overlay
else overlay;
};
haskellPackages = pkgs.haskell.packages.${compiler}.override
overrideHaskellPackages;
drv = haskellPackages.developPackage { drv = haskellPackages.developPackage {
name = "hnix"; name = "hnix";
root = ./.; root = ./.;
overrides = with pkgs.haskell.lib; self: super: {
mono-traversable = dontCheck super.mono-traversable;
megaparsec = super.megaparsec_7_0_4;
};
source-overrides = {};
modifier = drv: pkgs.haskell.lib.overrideCabal drv (attrs: { modifier = drv: pkgs.haskell.lib.overrideCabal drv (attrs: {
buildTools = (attrs.buildTools or []) ++ [ buildTools = (attrs.buildTools or []) ++ [
pkgs.haskell.packages.${compiler}.cabal-install haskellPackages.cabal-install
]; ];
enableLibraryProfiling = doProfiling; enableLibraryProfiling = doProfiling;
enableExecutableProfiling = doProfiling; enableExecutableProfiling = doProfiling;
testHaskellDepends = attrs.testHaskellDepends ++ testHaskellDepends = attrs.testHaskellDepends ++ [
[ pkgs.nix pkgs.nix
pkgs.haskell.packages.ghc844.criterion haskellPackages.criterion
]; ];
inherit doBenchmark; inherit doBenchmark;
@ -58,10 +91,11 @@ drv = haskellPackages.developPackage {
passthru = { passthru = {
nixpkgs = pkgs; nixpkgs = pkgs;
inherit haskellPackages;
}; };
}); });
inherit returnShellEnv; returnShellEnv = false;
}; };
in drv in drv

View file

@ -433,11 +433,6 @@ flag profiling
manual: True manual: True
default: False default: False
flag tracing
description: Enable full debug tracing
manual: True
default: False
library library
exposed-modules: exposed-modules:
Nix Nix
@ -454,6 +449,7 @@ library
Nix.Expr.Types Nix.Expr.Types
Nix.Expr.Types.Annotated Nix.Expr.Types.Annotated
Nix.Frames Nix.Frames
Nix.Fresh
Nix.Json Nix.Json
Nix.Lint Nix.Lint
Nix.Normal Nix.Normal
@ -496,6 +492,7 @@ library
, filepath , filepath
, free , free
, hashing , hashing
, hnix-store-core
, http-client , http-client
, http-client-tls , http-client-tls
, http-types , http-types
@ -527,8 +524,6 @@ library
, xml , xml
if flag(optimize) if flag(optimize)
ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2 ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2
if flag(tracing)
cpp-options: -DENABLE_TRACING=1
if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3) if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)
build-depends: build-depends:
compact compact
@ -548,9 +543,9 @@ library
build-depends: build-depends:
lens-family >=1.2.2 lens-family >=1.2.2
, lens-family-core >=1.2.2 , lens-family-core >=1.2.2
if impl(ghc < 8.4.0) && !flag(profiling) -- if impl(ghc < 8.4.0) && !flag(profiling)
build-depends: -- build-depends:
ghc-datasize -- ghc-datasize
if impl(ghcjs) if impl(ghcjs)
build-depends: build-depends:
hashable >=1.2.4 && <1.3 hashable >=1.2.4 && <1.3
@ -595,8 +590,6 @@ executable hnix
, unordered-containers >=0.2.9 && <0.3 , unordered-containers >=0.2.9 && <0.3
if flag(optimize) if flag(optimize)
ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2 ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2
if flag(tracing)
cpp-options: -DENABLE_TRACING=1
if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3) if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)
build-depends: build-depends:
compact compact
@ -666,8 +659,6 @@ test-suite hnix-tests
, unordered-containers >=0.2.9 && <0.3 , unordered-containers >=0.2.9 && <0.3
if flag(optimize) if flag(optimize)
ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2 ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2
if flag(tracing)
cpp-options: -DENABLE_TRACING=1
if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3) if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)
build-depends: build-depends:
compact compact
@ -684,7 +675,6 @@ test-suite hnix-tests
else else
buildable: True buildable: True
default-language: Haskell2010 default-language: Haskell2010
build-tool-depends: hspec-discover:hspec-discover == 2.*
benchmark hnix-benchmarks benchmark hnix-benchmarks
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
@ -715,8 +705,6 @@ benchmark hnix-benchmarks
, unordered-containers >=0.2.9 && <0.3 , unordered-containers >=0.2.9 && <0.3
if flag(optimize) if flag(optimize)
ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2 ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2
if flag(tracing)
cpp-options: -DENABLE_TRACING=1
if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3) if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)
build-depends: build-depends:
compact compact

1
shell.nix Normal file
View file

@ -0,0 +1 @@
{} @ attrs: (import ./. attrs).env

View file

@ -89,8 +89,10 @@ import Nix.Thunk
import Nix.Utils import Nix.Utils
import Nix.Value import Nix.Value
import Nix.XML import Nix.XML
import System.Nix.Internal.Hash (printHashBytes32)
import System.FilePath import System.FilePath
import System.Posix.Files (isRegularFile, isDirectory, isSymbolicLink) import System.Posix.Files (isRegularFile, isDirectory, isSymbolicLink)
import Text.Read
import Text.Regex.TDFA import Text.Regex.TDFA
-- | Evaluate a nix expression in the default context -- | Evaluate a nix expression in the default context
@ -459,7 +461,7 @@ splitVersion s = case Text.uncons s of
| h `elem` versionComponentSeparators -> splitVersion t | h `elem` versionComponentSeparators -> splitVersion t
| isDigit h -> | isDigit h ->
let (digits, rest) = Text.span isDigit s let (digits, rest) = Text.span isDigit s
in VersionComponent_Number (read $ Text.unpack digits) : splitVersion rest in VersionComponent_Number (fromMaybe (error $ "splitVersion: couldn't parse " <> show digits) $ readMaybe $ Text.unpack digits) : splitVersion rest
| otherwise -> | otherwise ->
let (chars, rest) = Text.span (\c -> not $ isDigit c || c `elem` versionComponentSeparators) s let (chars, rest) = Text.span (\c -> not $ isDigit c || c `elem` versionComponentSeparators) s
thisComponent = case chars of thisComponent = case chars of
@ -950,7 +952,7 @@ placeHolder :: MonadNix e m => m (NValue m) -> m (NValue m)
placeHolder = fromValue >=> fromStringNoContext >=> \t -> do placeHolder = fromValue >=> fromStringNoContext >=> \t -> do
h <- runPrim (hashString (principledMakeNixStringWithoutContext "sha256") h <- runPrim (hashString (principledMakeNixStringWithoutContext "sha256")
(principledMakeNixStringWithoutContext ("nix-output:" <> t))) (principledMakeNixStringWithoutContext ("nix-output:" <> t)))
toNix $ principledMakeNixStringWithoutContext $ Text.cons '/' $ printHash32 $ toNix $ principledMakeNixStringWithoutContext $ Text.cons '/' $ printHashBytes32 $
-- The result coming out of hashString is base16 encoded -- The result coming out of hashString is base16 encoded
fst $ Base16.decode $ encodeUtf8 $ principledStringIgnoreContext h fst $ Base16.decode $ encodeUtf8 $ principledStringIgnoreContext h
@ -1094,11 +1096,16 @@ fetchurl v = v >>= \case
where where
go :: Maybe (NThunk m) -> NValue m -> m (NValue m) go :: Maybe (NThunk m) -> NValue m -> m (NValue m)
go _msha = \case go _msha = \case
NVStr ns -> getURL (hackyStringIgnoreContext ns) >>= \case -- msha NVStr ns -> noContextAttrs ns >>= getURL >>= \case -- msha
Left e -> throwError e Left e -> throwError e
Right p -> toValue p Right p -> toValue p
v -> throwError $ ErrorCall $ v -> throwError $ ErrorCall $
"builtins.fetchurl: Expected URI or string, got " ++ show v "builtins.fetchurl: Expected URI or string, got " ++ show v
noContextAttrs ns = case principledGetStringNoContext ns of
Nothing -> throwError $ ErrorCall $
"builtins.fetchurl: unsupported arguments to url"
Just t -> pure t
partition_ :: forall e m. MonadNix e m partition_ :: forall e m. MonadNix e m
=> m (NValue m) -> m (NValue m) -> m (NValue m) => m (NValue m) -> m (NValue m) -> m (NValue m)
@ -1115,7 +1122,7 @@ currentSystem :: MonadNix e m => m (NValue m)
currentSystem = do currentSystem = do
os <- getCurrentSystemOS os <- getCurrentSystemOS
arch <- getCurrentSystemArch arch <- getCurrentSystemArch
return $ nvStr $ hackyMakeNixStringWithoutContext (arch <> "-" <> os) return $ nvStr $ principledMakeNixStringWithoutContext (arch <> "-" <> os)
currentTime_ :: MonadNix e m => m (NValue m) currentTime_ :: MonadNix e m => m (NValue m)
currentTime_ = do currentTime_ = do

View file

@ -39,8 +39,9 @@ import Nix.Strings (runAntiquoted)
import Nix.Thunk import Nix.Thunk
import Nix.Utils import Nix.Utils
class (Show v, Monad m) => MonadEval v m | v -> m where class (Show v, Monad m) => MonadEval v m where
freeVariable :: Text -> m v freeVariable :: Text -> m v
synHole :: Text -> m v
attrMissing :: NonEmpty Text -> Maybe v -> m v attrMissing :: NonEmpty Text -> Maybe v -> m v
evaledSym :: Text -> v -> m v evaledSym :: Text -> v -> m v
evalCurPos :: m v evalCurPos :: m v
@ -91,10 +92,18 @@ data EvalFrame m v
= EvaluatingExpr (Scopes m v) NExprLoc = EvaluatingExpr (Scopes m v) NExprLoc
| ForcingExpr (Scopes m v) NExprLoc | ForcingExpr (Scopes m v) NExprLoc
| Calling String SrcSpan | Calling String SrcSpan
| SynHole (SynHoleInfo m v)
deriving (Show, Typeable) deriving (Show, Typeable)
instance (Typeable m, Typeable v) => Exception (EvalFrame m v) instance (Typeable m, Typeable v) => Exception (EvalFrame m v)
data SynHoleInfo m t = SynHoleInfo
{ _synHoleInfo_expr :: NExprLoc
, _synHoleInfo_scope :: Scopes m t
} deriving (Show, Typeable)
instance (Typeable m, Typeable t) => Exception (SynHoleInfo m t)
eval :: forall v t m. MonadNixEval v t m => NExprF (m v) -> m v eval :: forall v t m. MonadNixEval v t m => NExprF (m v) -> m v
eval (NSym "__curPos") = evalCurPos eval (NSym "__curPos") = evalCurPos
@ -148,6 +157,8 @@ eval (NAbs params body) = do
args <- buildArgument params arg args <- buildArgument params arg
pushScope args (k (M.map (`force` pure) args) body) pushScope args (k (M.map (`force` pure) args) body)
eval (NSynHole name) = synHole name
-- | If you know that the 'scope' action will result in an 'AttrSet t', then -- | If you know that the 'scope' action will result in an 'AttrSet t', then
-- this implementation may be used as an implementation for 'evalWith'. -- this implementation may be used as an implementation for 'evalWith'.
evalWithAttrSet :: forall v t m. MonadNixEval v t m => m v -> m v -> m v evalWithAttrSet :: forall v t m. MonadNixEval v t m => m v -> m v -> m v
@ -341,26 +352,25 @@ buildArgument params arg = do
Nothing -> id Nothing -> id
Just n -> M.insert n $ const $ Just n -> M.insert n $ const $
thunk (withScopes scope arg) thunk (withScopes scope arg)
loebM (inject $ alignWithKey (assemble scope isVariadic) loebM (inject $ M.mapMaybe id $ alignWithKey (assemble scope isVariadic)
args (M.fromList s)) args (M.fromList s))
where where
assemble :: Scopes m t assemble :: Scopes m t
-> Bool -> Bool
-> Text -> Text
-> These t (Maybe (m v)) -> These t (Maybe (m v))
-> AttrSet t -> Maybe (AttrSet t -> m t)
-> m t
assemble scope isVariadic k = \case assemble scope isVariadic k = \case
That Nothing -> That Nothing -> Just $
const $ evalError @v $ ErrorCall $ const $ evalError @v $ ErrorCall $
"Missing value for parameter: " ++ show k "Missing value for parameter: " ++ show k
That (Just f) -> \args -> That (Just f) -> Just $ \args ->
thunk $ withScopes scope $ pushScope args f thunk $ withScopes scope $ pushScope args f
This x | isVariadic -> const (pure x) This _ | isVariadic -> Nothing
| otherwise -> | otherwise -> Just $
const $ evalError @v $ ErrorCall $ const $ evalError @v $ ErrorCall $
"Unexpected parameter: " ++ show k "Unexpected parameter: " ++ show k
These x _ -> const (pure x) These x _ -> Just (const (pure x))
addSourcePositions :: (MonadReader e m, Has e SrcSpan) addSourcePositions :: (MonadReader e m, Has e SrcSpan)
=> Transform NExprLocF (m a) => Transform NExprLocF (m a)

View file

@ -57,6 +57,7 @@ import Nix.Effects
import Nix.Eval as Eval import Nix.Eval as Eval
import Nix.Expr import Nix.Expr
import Nix.Frames import Nix.Frames
import Nix.Fresh
import Nix.String import Nix.String
import Nix.Normal import Nix.Normal
import Nix.Options import Nix.Options
@ -84,7 +85,7 @@ import GHC.DataSize
type MonadNix e m = type MonadNix e m =
(Scoped (NThunk m) m, Framed e m, Has e SrcSpan, Has e Options, (Scoped (NThunk m) m, Framed e m, Has e SrcSpan, Has e Options,
Typeable m, MonadVar m, MonadEffects m, MonadFix m, MonadCatch m, Typeable m, MonadVar m, MonadEffects m, MonadFix m, MonadCatch m,
Alternative m) Alternative m, MonadFreshId Int m)
data ExecFrame m = Assertion SrcSpan (NValue m) data ExecFrame m = Assertion SrcSpan (NValue m)
deriving (Show, Typeable) deriving (Show, Typeable)
@ -150,6 +151,14 @@ instance MonadNix e m => MonadEval (NValue m) m where
freeVariable var = freeVariable var =
nverr $ ErrorCall $ "Undefined variable '" ++ Text.unpack var ++ "'" nverr $ ErrorCall $ "Undefined variable '" ++ Text.unpack var ++ "'"
synHole name = do
span <- currentPos
scope <- currentScopes
evalError @(NValue m) $ SynHole $ SynHoleInfo
{ _synHoleInfo_expr = Fix $ NSynHole_ span name
, _synHoleInfo_scope = scope
}
attrMissing ks Nothing = attrMissing ks Nothing =
evalError @(NValue m) $ ErrorCall $ evalError @(NValue m) $ ErrorCall $
"Inheriting unknown attribute: " "Inheriting unknown attribute: "
@ -487,13 +496,13 @@ fromStringNoContext ns =
newtype Lazy m a = Lazy newtype Lazy m a = Lazy
{ runLazy :: ReaderT (Context (Lazy m) (NThunk (Lazy m))) { runLazy :: ReaderT (Context (Lazy m) (NThunk (Lazy m)))
(StateT (HashMap FilePath NExprLoc) m) a } (StateT (HashMap FilePath NExprLoc) (FreshIdT Int m)) a }
deriving (Functor, Applicative, Alternative, Monad, MonadPlus, deriving (Functor, Applicative, Alternative, Monad, MonadPlus,
MonadFix, MonadIO, MonadFix, MonadIO,
MonadReader (Context (Lazy m) (NThunk (Lazy m)))) MonadReader (Context (Lazy m) (NThunk (Lazy m))))
instance MonadTrans Lazy where instance MonadTrans Lazy where
lift = Lazy . lift . lift lift = Lazy . lift . lift . lift
instance MonadRef m => MonadRef (Lazy m) where instance MonadRef m => MonadRef (Lazy m) where
type Ref (Lazy m) = Ref m type Ref (Lazy m) = Ref m
@ -520,6 +529,9 @@ instance MonadException m => MonadException (Lazy m) where
in runLazy <$> f run' in runLazy <$> f run'
#endif #endif
instance Monad m => MonadFreshId Int (Lazy m) where
freshId = Lazy $ lift $ lift freshId
instance MonadStore m => MonadStore (Lazy m) where instance MonadStore m => MonadStore (Lazy m) where
addPath' = lift . addPath' addPath' = lift . addPath'
toFile_' n = lift . toFile_' n toFile_' n = lift . toFile_' n
@ -591,19 +603,20 @@ instance (MonadFix m, MonadCatch m, MonadFile m, MonadStore m, MonadVar m,
mapMaybeM op = foldr f (return []) mapMaybeM op = foldr f (return [])
where f x xs = op x >>= (<$> xs) . (++) . maybeToList where f x xs = op x >>= (<$> xs) . (++) . maybeToList
--handleEntry :: Bool -> (Text, NThunk (Lazy m)) -> Lazy m (Maybe (Text, NThunk (Lazy m))) handleEntry :: Bool -> (Text, NThunk (Lazy m)) -> Lazy m (Maybe (Text, NThunk (Lazy m)))
handleEntry ignoreNulls (k, v) = fmap (k,) <$> case k of handleEntry ignoreNulls (k, v) = fmap (k,) <$> case k of
-- The `args' attribute is special: it supplies the command-line -- The `args' attribute is special: it supplies the command-line
-- arguments to the builder. -- arguments to the builder.
-- TODO This use of coerceToString is probably not right and may -- TODO This use of coerceToString is probably not right and may
-- not have the right arguments. -- not have the right arguments.
"args" -> force v (\v2 -> Just <$> coerceNix v2) "args" -> force v $ fmap Just . coerceNixList
"__ignoreNulls" -> pure Nothing "__ignoreNulls" -> pure Nothing
_ -> force v $ \case _ -> force v $ \case
NVConstant NNull | ignoreNulls -> pure Nothing NVConstant NNull | ignoreNulls -> pure Nothing
v' -> Just <$> coerceNix v' v' -> Just <$> coerceNix v'
where where
coerceNix = toNix <=< coerceToString CopyToStore CoerceAny coerceNix = toNix <=< coerceToString CopyToStore CoerceAny
coerceNixList = toNix <=< traverse (\x -> force x coerceNix) <=< fromValue @[NThunk (Lazy m)]
traceEffect = putStrLn traceEffect = putStrLn
@ -611,7 +624,8 @@ getRecursiveSize :: MonadIntrospect m => a -> m (NValue m)
getRecursiveSize = toNix @Integer . fromIntegral <=< recursiveSize getRecursiveSize = toNix @Integer . fromIntegral <=< recursiveSize
runLazyM :: Options -> MonadIO m => Lazy m a -> m a runLazyM :: Options -> MonadIO m => Lazy m a -> m a
runLazyM opts = (`evalStateT` M.empty) runLazyM opts = runFreshIdT 0
. (`evalStateT` M.empty)
. (`runReaderT` newContext opts) . (`runReaderT` newContext opts)
. runLazy . runLazy
@ -790,10 +804,44 @@ fetchTarball v = v >>= \case
++ "url = \"" ++ Text.unpack url ++ "\"; " ++ "url = \"" ++ Text.unpack url ++ "\"; "
++ "sha256 = \"" ++ Text.unpack sha ++ "\"; }" ++ "sha256 = \"" ++ Text.unpack sha ++ "\"; }"
exec :: (MonadExec m, Framed e m, MonadThrow m, Alternative m, MonadCatch m, MonadFix m, MonadEffects m, GEq (Ref m), MonadAtomicRef m, Typeable m, Has e Options, Has e SrcSpan, Scoped (NThunk m) m) => [String] -> m (NValue m) exec
:: ( MonadExec m
, Framed e m
, MonadThrow m
, Alternative m
, MonadCatch m
, MonadFix m
, MonadEffects m
, MonadFreshId Int m
, GEq (Ref m)
, MonadAtomicRef m
, Typeable m
, Has e Options
, Has e SrcSpan
, Scoped (NThunk m) m
)
=> [String]
-> m (NValue m)
exec args = either throwError evalExprLoc =<< exec' args exec args = either throwError evalExprLoc =<< exec' args
nixInstantiateExpr :: (MonadInstantiate m, Framed e m, MonadThrow m, Alternative m, MonadCatch m, MonadFix m, MonadEffects m, GEq (Ref m), MonadAtomicRef m, Typeable m, Has e Options, Has e SrcSpan, Scoped (NThunk m) m) => String -> m (NValue m) nixInstantiateExpr
:: ( MonadInstantiate m
, Framed e m
, MonadThrow m
, Alternative m
, MonadCatch m
, MonadFix m
, MonadEffects m
, MonadFreshId Int m
, GEq (Ref m)
, MonadAtomicRef m
, Typeable m
, Has e Options
, Has e SrcSpan
, Scoped (NThunk m) m
)
=> String
-> m (NValue m)
nixInstantiateExpr s = either throwError evalExprLoc =<< instantiateExpr s nixInstantiateExpr s = either throwError evalExprLoc =<< instantiateExpr s
instance Monad m => Scoped (NThunk (Lazy m)) (Lazy m) where instance Monad m => Scoped (NThunk (Lazy m)) (Lazy m) where

View file

@ -70,6 +70,12 @@ mkSym = Fix . mkSymF
mkSymF :: Text -> NExprF a mkSymF :: Text -> NExprF a
mkSymF = NSym mkSymF = NSym
mkSynHole :: Text -> NExpr
mkSynHole = Fix . mkSynHoleF
mkSynHoleF :: Text -> NExprF a
mkSynHoleF = NSynHole
mkSelector :: Text -> NAttrPath NExpr mkSelector :: Text -> NAttrPath NExpr
mkSelector = (:| []) . StaticKey mkSelector = (:| []) . StaticKey

View file

@ -134,6 +134,8 @@ data NExprF r
-- evaluate the second argument. -- evaluate the second argument.
| NAssert !r !r | NAssert !r !r
-- ^ Assert that the first returns true before evaluating the second. -- ^ Assert that the first returns true before evaluating the second.
| NSynHole !VarName
-- ^ Syntactic hole, e.g. @^foo@ , @^hole_name@
deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor, deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor,
Foldable, Traversable, Show, NFData, Hashable) Foldable, Traversable, Show, NFData, Hashable)

View file

@ -190,6 +190,9 @@ nullSpan = SrcSpan nullPos nullPos
pattern NSym_ :: SrcSpan -> VarName -> NExprLocF r pattern NSym_ :: SrcSpan -> VarName -> NExprLocF r
pattern NSym_ ann x = Compose (Ann ann (NSym x)) pattern NSym_ ann x = Compose (Ann ann (NSym x))
pattern NSynHole_ :: SrcSpan -> Text -> NExprLocF r
pattern NSynHole_ ann x = Compose (Ann ann (NSynHole x))
pattern NConstant_ :: SrcSpan -> NAtom -> NExprLocF r pattern NConstant_ :: SrcSpan -> NAtom -> NExprLocF r
pattern NConstant_ ann x = Compose (Ann ann (NConstant x)) pattern NConstant_ ann x = Compose (Ann ann (NConstant x))

77
src/Nix/Fresh.hs Normal file
View file

@ -0,0 +1,77 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Nix.Fresh where
import Control.Applicative
import Control.Monad.Catch
import Control.Monad.Except
import Control.Monad.Reader
import Control.Monad.Ref
import Control.Monad.ST
import Control.Monad.State.Strict
import Control.Monad.Writer
#ifdef MIN_VERSION_haskeline
import System.Console.Haskeline.MonadException hiding (catch)
#endif
-- TODO better fresh name supply
class Monad m => MonadFreshId i m | m -> i where
freshId :: m i
default freshId :: (MonadFreshId i m', MonadTrans t, m ~ (t m')) => m i
freshId = lift freshId
newtype FreshIdT i m a = FreshIdT { unFreshIdT :: StateT i m a }
deriving
( Functor
, Applicative
, Alternative
, Monad
, MonadPlus
, MonadTrans
, MonadFix
, MonadRef
, MonadAtomicRef
, MonadIO
, MonadCatch
, MonadThrow
#ifdef MIN_VERSION_haskeline
, MonadException
#endif
)
instance (Monad m, Num i) => MonadFreshId i (FreshIdT i m) where
freshId = FreshIdT $ get <* modify (+ 1)
runFreshIdT :: Functor m => i -> FreshIdT i m a -> m a
runFreshIdT i m = fst <$> runStateT (unFreshIdT m) i
instance MonadFreshId i m => MonadFreshId i (ReaderT r m)
instance (Monoid w, MonadFreshId i m) => MonadFreshId i (WriterT w m)
instance MonadFreshId i m => MonadFreshId i (ExceptT e m)
instance MonadFreshId i m => MonadFreshId i (StateT s m)
-- Orphan instance needed by Infer.hs and Lint.hs
-- Since there's no forking, it's automatically atomic.
instance MonadAtomicRef (ST s) where
atomicModifyRef r f = do
v <- readRef r
let (a, b) = f v
writeRef r a
return b
atomicModifyRef' r f = do
v <- readRef r
let (a, b) = f v
writeRef r $! a
return b

View file

@ -1,4 +1,5 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
module Nix.Json where module Nix.Json where
@ -7,6 +8,7 @@ import Control.Monad
import Control.Monad.Trans import Control.Monad.Trans
import qualified Data.Aeson as A import qualified Data.Aeson as A
import qualified Data.Aeson.Encoding as A import qualified Data.Aeson.Encoding as A
import qualified Data.HashMap.Lazy as HM
import qualified Data.Text as Text import qualified Data.Text as Text
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL import qualified Data.Text.Lazy.Encoding as TL
@ -34,8 +36,9 @@ nvalueToJSON = \case
NVStr ns -> A.toJSON <$> extractNixString ns NVStr ns -> A.toJSON <$> extractNixString ns
NVList l -> NVList l ->
A.Array . V.fromList <$> traverse (join . lift . flip force (return . nvalueToJSON)) l A.Array . V.fromList <$> traverse (join . lift . flip force (return . nvalueToJSON)) l
NVSet m _ -> NVSet m _ -> case HM.lookup "outPath" m of
A.Object <$> traverse (join . lift . flip force (return . nvalueToJSON)) m Nothing -> A.Object <$> traverse (join . lift . flip force (return . nvalueToJSON)) m
Just outPath -> join $ lift $ force outPath (return . nvalueToJSON)
NVPath p -> do NVPath p -> do
fp <- lift $ unStorePath <$> addPath p fp <- lift $ unStorePath <$> addPath p
addSingletonStringContext $ StringContext (Text.pack fp) DirectPath addSingletonStringContext $ StringContext (Text.pack fp) DirectPath

View file

@ -36,7 +36,6 @@ import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as M import qualified Data.HashMap.Lazy as M
import Data.List import Data.List
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import Data.STRef
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as Text import qualified Data.Text as Text
import Nix.Atoms import Nix.Atoms
@ -46,6 +45,7 @@ import Nix.Eval (MonadEval(..))
import qualified Nix.Eval as Eval import qualified Nix.Eval as Eval
import Nix.Expr import Nix.Expr
import Nix.Frames import Nix.Frames
import Nix.Fresh
import Nix.String import Nix.String
import Nix.Options import Nix.Options
import Nix.Scope import Nix.Scope
@ -118,7 +118,7 @@ unpackSymbolic :: MonadVar m
unpackSymbolic = readVar . coerce unpackSymbolic = readVar . coerce
type MonadLint e m = (Scoped (SThunk m) m, Framed e m, MonadVar m, type MonadLint e m = (Scoped (SThunk m) m, Framed e m, MonadVar m,
MonadCatch m) MonadCatch m, MonadFreshId Int m)
symerr :: forall e m a. MonadLint e m => String -> m a symerr :: forall e m a. MonadLint e m => String -> m a
symerr = evalError @(Symbolic m) . ErrorCall symerr = evalError @(Symbolic m) . ErrorCall
@ -389,21 +389,17 @@ lintApp context fun arg = unpackSymbolic fun >>= \case
(head args,) <$> foldM (unify context) y ys (head args,) <$> foldM (unify context) y ys
newtype Lint s a = Lint newtype Lint s a = Lint
{ runLint :: ReaderT (Context (Lint s) (SThunk (Lint s))) (ST s) a } { runLint :: ReaderT (Context (Lint s) (SThunk (Lint s))) (FreshIdT Int (ST s)) a }
deriving (Functor, Applicative, Monad, MonadFix, deriving
MonadReader (Context (Lint s) (SThunk (Lint s)))) ( Functor
, Applicative
instance MonadRef (Lint s) where , Monad
type Ref (Lint s) = Ref (ST s) , MonadFix
newRef x = Lint $ newRef x , MonadReader (Context (Lint s) (SThunk (Lint s)))
readRef x = Lint $ readRef x , MonadFreshId Int
writeRef x y = Lint $ writeRef x y , MonadRef
, MonadAtomicRef
instance MonadAtomicRef (Lint s) where )
atomicModifyRef x f = Lint $ ReaderT $ \_ -> do
res <- snd . f <$> readSTRef x
_ <- modifySTRef x (fst . f)
return res
instance MonadThrow (Lint s) where instance MonadThrow (Lint s) where
throwM e = Lint $ ReaderT $ \_ -> throw e throwM e = Lint $ ReaderT $ \_ -> throw e
@ -412,7 +408,7 @@ instance MonadCatch (Lint s) where
catch _m _h = Lint $ ReaderT $ \_ -> error "Cannot catch in 'Lint s'" catch _m _h = Lint $ ReaderT $ \_ -> error "Cannot catch in 'Lint s'"
runLintM :: Options -> Lint s a -> ST s a runLintM :: Options -> Lint s a -> ST s a
runLintM opts = flip runReaderT (newContext opts) . runLint runLintM opts = runFreshIdT 0 . flip runReaderT (newContext opts) . runLint
symbolicBaseEnv :: Monad m => m (Scopes m (SThunk m)) symbolicBaseEnv :: Monad m => m (Scopes m (SThunk m))
symbolicBaseEnv = return emptyScopes symbolicBaseEnv = return emptyScopes

View file

@ -129,7 +129,8 @@ nixTerm = do
x == '<' || x == '<' ||
x == '/' || x == '/' ||
x == '"' || x == '"' ||
x == '\'' x == '\''||
x == '^'
case c of case c of
'(' -> nixSelect nixParens '(' -> nixSelect nixParens
'{' -> nixSelect nixSet '{' -> nixSelect nixSet
@ -138,6 +139,7 @@ nixTerm = do
'/' -> nixPath '/' -> nixPath
'"' -> nixString '"' -> nixString
'\'' -> nixString '\'' -> nixString
'^' -> nixSynHole
_ -> msum $ _ -> msum $
[ nixSelect nixSet | c == 'r' ] ++ [ nixSelect nixSet | c == 'r' ] ++
[ nixPath | pathChar c ] ++ [ nixPath | pathChar c ] ++
@ -157,6 +159,9 @@ nixToplevelForm = keywords <+> nixLambda <+> nixExpr
nixSym :: Parser NExprLoc nixSym :: Parser NExprLoc
nixSym = annotateLocation1 $ mkSymF <$> identifier nixSym = annotateLocation1 $ mkSymF <$> identifier
nixSynHole :: Parser NExprLoc
nixSynHole = annotateLocation1 $ mkSynHoleF <$> (char '^' >> identifier)
nixInt :: Parser NExprLoc nixInt :: Parser NExprLoc
nixInt = annotateLocation1 (mkIntF <$> integer <?> "integer") nixInt = annotateLocation1 (mkIntF <$> integer <?> "integer")

View file

@ -256,6 +256,7 @@ exprFNixDoc = \case
[ "assert" <+> withoutParens cond <> semi [ "assert" <+> withoutParens cond <> semi
, align $ withoutParens body , align $ withoutParens body
] ]
NSynHole name -> simpleExpr $ pretty ("^" <> unpack name)
where where
recPrefix = "rec" <> space recPrefix = "rec" <> space

View file

@ -7,6 +7,7 @@
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-}
module Nix.Render where module Nix.Render where
@ -16,13 +17,16 @@ import Control.Monad.Trans
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.Set as Set import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Data.Text.Prettyprint.Doc import Data.Text.Prettyprint.Doc
import Data.Void import Data.Void
import Debug.Trace
import Nix.Expr.Types.Annotated import Nix.Expr.Types.Annotated
import qualified System.Posix.Files as S
import qualified System.Directory as S import qualified System.Directory as S
import qualified System.Posix.Files as S
import Text.Megaparsec.Error import Text.Megaparsec.Error
import Text.Megaparsec.Pos (SourcePos(..)) import Text.Megaparsec.Pos
class Monad m => MonadFile m where class Monad m => MonadFile m where
readFile :: FilePath -> m ByteString readFile :: FilePath -> m ByteString
@ -69,6 +73,39 @@ posAndMsg (SourcePos _ lineNo _) msg =
FancyError (unPos lineNo) FancyError (unPos lineNo)
(Set.fromList [ErrorFail (show msg) :: ErrorFancy Void]) (Set.fromList [ErrorFail (show msg) :: ErrorFancy Void])
renderLocation :: Monad m => SrcSpan -> Doc a -> m (Doc a) renderLocation :: MonadFile m => SrcSpan -> Doc a -> m (Doc a)
renderLocation (SrcSpan beg@(SourcePos _ _ _) _) msg = renderLocation (SrcSpan (SourcePos file begLine begCol)
return $ pretty $ init $ parseErrorPretty @String (posAndMsg beg msg) (SourcePos file' endLine endCol)) msg
| file /= "<string>" && file == file' = do
exist <- doesFileExist file
if exist
then do
txt <- sourceContext file begLine begCol endLine endCol msg
return $ vsep
[ "In file " <> errorContext file begLine begCol endLine endCol <> ":"
, txt
]
else return msg
renderLocation (SrcSpan beg end) msg =
fail $ "Don't know how to render range from " ++ show beg ++ " to " ++ show end
++ " for error: " ++ show msg
errorContext :: FilePath -> Pos -> Pos -> Pos -> Pos -> Doc a
errorContext path bl bc _el _ec =
pretty path <> ":" <> pretty (unPos bl) <> ":" <> pretty (unPos bc)
sourceContext :: MonadFile m => FilePath -> Pos -> Pos -> Pos -> Pos -> Doc a -> m (Doc a)
sourceContext path (unPos -> begLine) (unPos -> begCol)
(unPos -> endLine) (unPos -> endCol) msg = do
traceM $ "Taking lines from " ++ path
traceM $ "begLine = " ++ show begLine
traceM $ "begCol = " ++ show begCol
traceM $ "endLine = " ++ show endLine
traceM $ "endCol = " ++ show endCol
traceM $ "msg = " ++ show msg
ls <- take (endLine - begLine)
. drop (pred begLine)
. T.lines
. T.decodeUtf8
<$> readFile path
pure $ vsep $ map pretty ls

View file

@ -75,12 +75,13 @@ renderFrame :: forall v e m ann.
MonadFile m, Typeable m, Typeable v) MonadFile m, Typeable m, Typeable v)
=> NixFrame -> m [Doc ann] => NixFrame -> m [Doc ann]
renderFrame (NixFrame level f) renderFrame (NixFrame level f)
| Just (e :: EvalFrame m v) <- fromException f = renderEvalFrame level e | Just (e :: EvalFrame m v) <- fromException f = renderEvalFrame level e
| Just (e :: ThunkLoop) <- fromException f = renderThunkLoop level e | Just (e :: ThunkLoop) <- fromException f = renderThunkLoop level e
| Just (e :: ValueFrame m) <- fromException f = renderValueFrame level e | Just (e :: ValueFrame m) <- fromException f = renderValueFrame level e
| Just (e :: NormalLoop m) <- fromException f = renderNormalLoop level e | Just (e :: NormalLoop m) <- fromException f = renderNormalLoop level e
| Just (e :: ExecFrame m) <- fromException f = renderExecFrame level e | Just (e :: ExecFrame m) <- fromException f = renderExecFrame level e
| Just (e :: ErrorCall) <- fromException f = pure [pretty (show e)] | Just (e :: ErrorCall) <- fromException f = pure [pretty (show e)]
| Just (e :: SynHoleInfo m v) <- fromException f = pure [pretty (show e)]
| otherwise = error $ "Unrecognized frame: " ++ show f | otherwise = error $ "Unrecognized frame: " ++ show f
wrapExpr :: NExprF r -> NExpr wrapExpr :: NExprF r -> NExpr
@ -107,7 +108,15 @@ renderEvalFrame level f = do
fmap (:[]) $ renderLocation ann $ fmap (:[]) $ renderLocation ann $
"While calling builtins." <> pretty name "While calling builtins." <> pretty name
_ -> pure [] SynHole synfo -> sequence $
let e@(Fix (Compose (Ann ann _))) = _synHoleInfo_expr synfo
in [ renderLocation ann =<<
renderExpr level "While evaluating" "Syntactic Hole" e
, pure $ pretty $ show (_synHoleInfo_scope synfo)
]
ForcingExpr _ _ -> pure []
renderExpr :: (MonadReader e m, Has e Options, MonadFile m) renderExpr :: (MonadReader e m, Has e Options, MonadFile m)
=> NixLevel -> String -> String -> NExprLoc -> m (Doc ann) => NixLevel -> String -> String -> NExprLoc -> m (Doc ann)

View file

@ -61,6 +61,7 @@ freeVars e = case unFix e of
-- This also makes sense because its value can be overridden by `x: with y; x` -- This also makes sense because its value can be overridden by `x: with y; x`
(NWith set expr) -> freeVars set `Set.union` freeVars expr (NWith set expr) -> freeVars set `Set.union` freeVars expr
(NAssert assertion expr) -> freeVars assertion `Set.union` freeVars expr (NAssert assertion expr) -> freeVars assertion `Set.union` freeVars expr
(NSynHole _) -> Set.empty
where where

View file

@ -1,20 +1,16 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE DeriveTraversable #-}
#if ENABLE_TRACING
{-# LANGUAGE BangPatterns #-}
#endif
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-} {-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Nix.Thunk where module Nix.Thunk where
@ -29,23 +25,19 @@ import Data.Typeable
import Unsafe.Coerce import Unsafe.Coerce
#if ENABLE_TRACING import Nix.Fresh
import Data.IORef
import System.IO.Unsafe
import Nix.Utils import Nix.Utils
counter :: IORef Int
counter = unsafePerformIO $ newIORef 0
{-# NOINLINE counter #-}
#endif
data Deferred m v = Deferred (m v) | Computed v data Deferred m v = Deferred (m v) | Computed v
deriving (Functor, Foldable, Traversable) deriving (Functor, Foldable, Traversable)
type Var m = Ref m type Var m = Ref m
--TODO: Eliminate the old MonadVar shims --TODO: Eliminate the old MonadVar shims
type MonadVar m = (MonadAtomicRef m, GEq (Ref m)) type MonadVar m =
( MonadAtomicRef m
, GEq (Ref m)
)
eqVar :: forall m a. GEq (Ref m) => Ref m a -> Ref m a -> Bool eqVar :: forall m a. GEq (Ref m) => Ref m a -> Ref m a -> Bool
eqVar a b = isJust $ geq a b eqVar a b = isJust $ geq a b
@ -73,7 +65,7 @@ instance GEq (STRef s) where
then Just $ unsafeCoerce Refl then Just $ unsafeCoerce Refl
else Nothing else Nothing
class Monad m => MonadThunk v t m | v -> m, v -> t, t -> m, t -> v where class Monad m => MonadThunk v t m | m -> t, t -> m, t -> v where
thunk :: m v -> m t thunk :: m v -> m t
force :: t -> (v -> m r) -> m r force :: t -> (v -> m r) -> m r
value :: v -> t value :: v -> t
@ -90,24 +82,15 @@ instance Exception ThunkLoop
valueRef :: v -> Thunk m v valueRef :: v -> Thunk m v
valueRef = Value valueRef = Value
buildThunk :: MonadVar m => m v -> m (Thunk m v) buildThunk :: (MonadVar m, MonadFreshId Int m) => m v -> m (Thunk m v)
buildThunk action = buildThunk action =do
#if ENABLE_TRACING freshThunkId <- freshId
let !x = unsafePerformIO (atomicModifyIORef' counter (\c -> (succ c, c))) in Thunk freshThunkId <$> newVar False <*> newVar (Deferred action)
Thunk x
#else
Thunk 0
#endif
<$> newVar False <*> newVar (Deferred action)
forceThunk :: (MonadVar m, MonadThrow m, MonadCatch m) forceThunk :: (MonadVar m, MonadThrow m, MonadCatch m)
=> Thunk m v -> (v -> m a) -> m a => Thunk m v -> (v -> m a) -> m a
forceThunk (Value ref) k = k ref forceThunk (Value ref) k = k ref
#if ENABLE_TRACING
forceThunk (Thunk n active ref) k = do forceThunk (Thunk n active ref) k = do
#else
forceThunk (Thunk _ active ref) k = do
#endif
eres <- readVar ref eres <- readVar ref
case eres of case eres of
Computed v -> k v Computed v -> k v
@ -115,15 +98,9 @@ forceThunk (Thunk _ active ref) k = do
nowActive <- atomicModifyVar active (True,) nowActive <- atomicModifyVar active (True,)
if nowActive if nowActive
then then
#if ENABLE_TRACING
throwM $ ThunkLoop (Just n) throwM $ ThunkLoop (Just n)
#else
throwM $ ThunkLoop Nothing
#endif
else do else do
#if ENABLE_TRACING
traceM $ "Forcing " ++ show n traceM $ "Forcing " ++ show n
#endif
v <- catch action $ \(e :: SomeException) -> do v <- catch action $ \(e :: SomeException) -> do
_ <- atomicModifyVar active (False,) _ <- atomicModifyVar active (False,)
throwM e throwM e

View file

@ -32,7 +32,7 @@ import Control.Monad.Logic
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Ref import Control.Monad.Ref
import Control.Monad.ST import Control.Monad.ST
import Control.Monad.State import Control.Monad.State.Strict
import Data.Fix import Data.Fix
import Data.Foldable import Data.Foldable
import qualified Data.HashMap.Lazy as M import qualified Data.HashMap.Lazy as M
@ -49,6 +49,7 @@ import Nix.Eval (MonadEval(..))
import qualified Nix.Eval as Eval import qualified Nix.Eval as Eval
import Nix.Expr.Types import Nix.Expr.Types
import Nix.Expr.Types.Annotated import Nix.Expr.Types.Annotated
import Nix.Fresh
import Nix.String import Nix.String
import Nix.Scope import Nix.Scope
import Nix.Thunk import Nix.Thunk
@ -63,14 +64,15 @@ import Nix.Utils
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- | Inference monad -- | Inference monad
newtype Infer s a = Infer newtype InferT s m a = InferT
{ getInfer :: { getInfer ::
ReaderT (Set.Set TVar, Scopes (Infer s) (JThunk s)) ReaderT (Set.Set TVar, Scopes (InferT s m) (JThunkT s m))
(StateT InferState (ExceptT InferError (ST s))) a (StateT InferState (ExceptT InferError m)) a
} }
deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadFix, deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadFix,
MonadReader (Set.Set TVar, Scopes (Infer s) (JThunk s)), MonadFail, MonadReader (Set.Set TVar, Scopes (InferT s m) (JThunkT s m)), MonadFail,
MonadState InferState, MonadError InferError) MonadState InferState, MonadError InferError,
MonadFreshId i)
-- | Inference state -- | Inference state
newtype InferState = InferState { count :: Int } newtype InferState = InferState { count :: Int }
@ -186,16 +188,26 @@ instance Monoid InferError where
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- | Run the inference monad -- | Run the inference monad
runInfer' :: Infer s a -> ST s (Either InferError a) runInfer' ::
( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
, MonadFix m
) => InferT s m a -> m (Either InferError a)
runInfer' = runExceptT runInfer' = runExceptT
. (`evalStateT` initInfer) . (`evalStateT` initInfer)
. (`runReaderT` (Set.empty, emptyScopes)) . (`runReaderT` (Set.empty, emptyScopes))
. getInfer . getInfer
runInfer :: (forall s. Infer s a) -> Either InferError a runInfer :: (forall s. InferT s (FreshIdT Int (ST s)) a) -> Either InferError a
runInfer m = runST (runInfer' m) runInfer m = runST (runFreshIdT 0 (runInfer' m))
inferType :: Env -> NExpr -> Infer s [(Subst, Type)] inferType ::
( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
, MonadFix m
) => Env -> NExpr -> InferT s m [(Subst, Type)]
inferType env ex = do inferType env ex = do
Judgment as cs t <- infer ex Judgment as cs t <- infer ex
let unbounds = Set.fromList (As.keys as) `Set.difference` let unbounds = Set.fromList (As.keys as) `Set.difference`
@ -224,17 +236,20 @@ inferExpr env ex = case runInfer (inferType env ex) of
closeOver :: Type -> Scheme closeOver :: Type -> Scheme
closeOver = normalize . generalize Set.empty closeOver = normalize . generalize Set.empty
extendMSet :: TVar -> Infer s a -> Infer s a extendMSet :: Monad m => TVar -> InferT s m a -> InferT s m a
extendMSet x = Infer . local (first (Set.insert x)) . getInfer extendMSet x = InferT . local (first (Set.insert x)) . getInfer
letters :: [String] letters :: [String]
letters = [1..] >>= flip replicateM ['a'..'z'] letters = [1..] >>= flip replicateM ['a'..'z']
fresh :: MonadState InferState m => m Type freshTVar :: MonadState InferState m => m TVar
fresh = do freshTVar = do
s <- get s <- get
put s{count = count s + 1} put s{count = count s + 1}
return $ TVar $ TV (letters !! count s) return $ TV (letters !! count s)
fresh :: MonadState InferState m => m Type
fresh = TVar <$> freshTVar
instantiate :: MonadState InferState m => Scheme -> m Type instantiate :: MonadState InferState m => Scheme -> m Type
instantiate (Forall as t) = do instantiate (Forall as t) = do
@ -306,34 +321,37 @@ binops u1 = \case
, typeFun [typeFloat, typeInt, typeFloat] , typeFun [typeFloat, typeInt, typeFloat]
]) ] ]) ]
liftInfer :: ST s a -> Infer s a liftInfer :: Monad m => m a -> InferT s m a
liftInfer = Infer . lift . lift . lift liftInfer = InferT . lift . lift . lift
instance MonadRef (Infer s) where instance (MonadRef m, Ref m ~ STRef s) => MonadRef (InferT s m) where
type Ref (Infer s) = STRef s type Ref (InferT s m) = Ref m
newRef x = liftInfer $ newSTRef x newRef x = liftInfer $ newRef x
readRef x = liftInfer $ readSTRef x readRef x = liftInfer $ readRef x
writeRef x y = liftInfer $ writeSTRef x y writeRef x y = liftInfer $ writeRef x y
instance MonadAtomicRef (Infer s) where instance (MonadAtomicRef m, Ref m ~ STRef s) => MonadAtomicRef (InferT s m) where
atomicModifyRef x f = liftInfer $ do atomicModifyRef x f = liftInfer $ do
res <- snd . f <$> readSTRef x res <- snd . f <$> readRef x
_ <- modifySTRef x (fst . f) _ <- modifyRef x (fst . f)
return res return res
newtype JThunk s = JThunk (Thunk (Infer s) (Judgment s)) newtype JThunkT s m = JThunk (Thunk (InferT s m) (Judgment s))
instance MonadThrow (Infer s) where instance Monad m => MonadThrow (InferT s m) where
throwM = throwError . EvaluationError throwM = throwError . EvaluationError
instance MonadCatch (Infer s) where instance Monad m => MonadCatch (InferT s m) where
catch m h = catchError m $ \case catch m h = catchError m $ \case
EvaluationError e -> EvaluationError e ->
maybe (error $ "Exception was not an exception: " ++ show e) h maybe (error $ "Exception was not an exception: " ++ show e) h
(fromException (toException e)) (fromException (toException e))
err -> error $ "Unexpected error: " ++ show err err -> error $ "Unexpected error: " ++ show err
instance MonadThunk (Judgment s) (JThunk s) (Infer s) where instance ( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
) => MonadThunk (Judgment s) (JThunkT s m) (InferT s m) where
thunk = fmap JThunk . buildThunk thunk = fmap JThunk . buildThunk
force (JThunk t) f = catch (forceThunk t f) $ \(_ :: ThunkLoop) -> force (JThunk t) f = catch (forceThunk t f) $ \(_ :: ThunkLoop) ->
@ -342,11 +360,19 @@ instance MonadThunk (Judgment s) (JThunk s) (Infer s) where
value = JThunk . valueRef value = JThunk . valueRef
instance MonadEval (Judgment s) (Infer s) where instance ( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
, MonadFix m
) => MonadEval (Judgment s) (InferT s m) where
freeVariable var = do freeVariable var = do
tv <- fresh tv <- fresh
return $ Judgment (As.singleton var tv) [] tv return $ Judgment (As.singleton var tv) [] tv
synHole var = do
tv <- fresh
return $ Judgment (As.singleton var tv) [] tv
-- If we fail to look up an attribute, we just don't know the type. -- If we fail to look up an attribute, we just don't know the type.
attrMissing _ _ = Judgment As.empty [] <$> fresh attrMissing _ _ = Judgment As.empty [] <$> fresh
@ -408,7 +434,8 @@ instance MonadEval (Judgment s) (Infer s) where
tv tv
evalAbs (Param x) k = do evalAbs (Param x) k = do
tv@(TVar a) <- fresh a <- freshTVar
let tv = TVar a
((), Judgment as cs t) <- ((), Judgment as cs t) <-
extendMSet a (k (pure (Judgment (As.singleton x tv) [] tv)) extendMSet a (k (pure (Judgment (As.singleton x tv) [] tv))
(\_ b -> ((),) <$> b)) (\_ b -> ((),) <$> b))
@ -450,11 +477,15 @@ data Judgment s = Judgment
} }
deriving Show deriving Show
instance FromValue NixString (Infer s) (Judgment s) where instance Monad m => FromValue NixString (InferT s m) (Judgment s) where
fromValueMay _ = return Nothing fromValueMay _ = return Nothing
fromValue _ = error "Unused" fromValue _ = error "Unused"
instance FromValue (AttrSet (JThunk s), AttrSet SourcePos) (Infer s) (Judgment s) where instance ( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
, MonadFix m
) => FromValue (AttrSet (JThunkT s m), AttrSet SourcePos) (InferT s m) (Judgment s) where
fromValueMay (Judgment _ _ (TSet _ xs)) = do fromValueMay (Judgment _ _ (TSet _ xs)) = do
let sing _ = Judgment As.empty [] let sing _ = Judgment As.empty []
pure $ Just (M.mapWithKey (\k v -> value (sing k v)) xs, M.empty) pure $ Just (M.mapWithKey (\k v -> value (sing k v)) xs, M.empty)
@ -463,7 +494,11 @@ instance FromValue (AttrSet (JThunk s), AttrSet SourcePos) (Infer s) (Judgment s
Just v -> pure v Just v -> pure v
Nothing -> pure (M.empty, M.empty) Nothing -> pure (M.empty, M.empty)
instance ToValue (AttrSet (JThunk s), AttrSet SourcePos) (Infer s) (Judgment s) where instance ( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
, MonadFix m
) => ToValue (AttrSet (JThunkT s m), AttrSet SourcePos) (InferT s m) (Judgment s) where
toValue (xs, _) = Judgment toValue (xs, _) = Judgment
<$> foldrM go As.empty xs <$> foldrM go As.empty xs
<*> (concat <$> traverse (`force` (pure . typeConstraints)) xs) <*> (concat <$> traverse (`force` (pure . typeConstraints)) xs)
@ -471,7 +506,11 @@ instance ToValue (AttrSet (JThunk s), AttrSet SourcePos) (Infer s) (Judgment s)
where where
go x rest = force x $ \x' -> pure $ As.merge (assumptions x') rest go x rest = force x $ \x' -> pure $ As.merge (assumptions x') rest
instance ToValue [JThunk s] (Infer s) (Judgment s) where instance ( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
, MonadFix m
) => ToValue [JThunkT s m] (InferT s m) (Judgment s) where
toValue xs = Judgment toValue xs = Judgment
<$> foldrM go As.empty xs <$> foldrM go As.empty xs
<*> (concat <$> traverse (`force` (pure . typeConstraints)) xs) <*> (concat <$> traverse (`force` (pure . typeConstraints)) xs)
@ -479,10 +518,18 @@ instance ToValue [JThunk s] (Infer s) (Judgment s) where
where where
go x rest = force x $ \x' -> pure $ As.merge (assumptions x') rest go x rest = force x $ \x' -> pure $ As.merge (assumptions x') rest
instance ToValue Bool (Infer s) (Judgment s) where instance ( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
, MonadFix m
) => ToValue Bool (InferT s m) (Judgment s) where
toValue _ = pure $ Judgment As.empty [] typeBool toValue _ = pure $ Judgment As.empty [] typeBool
infer :: NExpr -> Infer s (Judgment s) infer :: ( MonadFreshId Int m
, MonadAtomicRef m
, Ref m ~ STRef s
, MonadFix m
) => NExpr -> InferT s m (Judgment s)
infer = cata Eval.eval infer = cata Eval.eval
inferTop :: Env -> [(Text, NExpr)] -> Either InferError Env inferTop :: Env -> [(Text, NExpr)] -> Either InferError Env
@ -618,8 +665,8 @@ solve cs = solve' (nextSolvable cs)
s' <- lift $ instantiate s s' <- lift $ instantiate s
solve (EqConst t s' : cs) solve (EqConst t s' : cs)
instance Scoped (JThunk s) (Infer s) where instance Monad m => Scoped (JThunkT s m) (InferT s m) where
currentScopes = currentScopesReader currentScopes = currentScopesReader
clearScopes = clearScopesReader @(Infer s) @(JThunk s) clearScopes = clearScopesReader @(InferT s m) @(JThunkT s m)
pushScopes = pushScopesReader pushScopes = pushScopesReader
lookupVar = lookupVarReader lookupVar = lookupVarReader

View file

@ -14,9 +14,6 @@ import Control.Monad
import Control.Monad.Fix import Control.Monad.Fix
import qualified Data.Aeson as A import qualified Data.Aeson as A
import qualified Data.Aeson.Encoding as A import qualified Data.Aeson.Encoding as A
import Data.Bits
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Fix import Data.Fix
import Data.HashMap.Lazy (HashMap) import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as M import qualified Data.HashMap.Lazy as M
@ -127,26 +124,3 @@ uriAwareSplit = go where
let ((suffix, _):path) = go (Text.drop 3 e2) let ((suffix, _):path) = go (Text.drop 3 e2)
in (e1 <> Text.pack "://" <> suffix, PathEntryURI) : path in (e1 <> Text.pack "://" <> suffix, PathEntryURI) : path
| otherwise -> (e1, PathEntryPath) : go (Text.drop 1 e2) | otherwise -> (e1, PathEntryPath) : go (Text.drop 1 e2)
printHash32 :: ByteString -> Text
printHash32 bs = go (base32Len bs - 1) ""
where
go n s
| n >= 0 = go (n-1) (Text.snoc s $ nextCharHash32 bs n)
| otherwise = s
nextCharHash32 :: ByteString -> Int -> Char
nextCharHash32 bs n = Text.index base32Chars (c .&. 0x1f)
where
b = n * 5
i = b `div` 8
j = b `mod` 8
c = fromIntegral $ shiftR (B.index bs i) j .|. mask
mask = if i >= B.length bs - 1
then 0
else shiftL (B.index bs (i+1)) (8 - j)
-- e, o, u, and t are omitted (see base32Chars in nix/src/libutil/hash.cc)
base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"
base32Len :: ByteString -> Int
base32Len bs = ((B.length bs * 8 - 1) `div` 5) + 1

View file

@ -226,8 +226,13 @@ isClosureNF _ = False
thunkEq :: MonadThunk (NValue m) (NThunk m) m thunkEq :: MonadThunk (NValue m) (NThunk m) m
=> NThunk m -> NThunk m -> m Bool => NThunk m -> NThunk m -> m Bool
thunkEq lt rt = force lt $ \lv -> force rt $ \rv -> thunkEq lt rt = force lt $ \lv -> force rt $ \rv ->
case (lv, rv) of let unsafePtrEq = case (lt, rt) of
(NVClosure _ _, NVClosure _ _) -> pure True (NThunk _ (Thunk lid _ _), NThunk _ (Thunk rid _ _)) | lid == rid -> return True
_ -> valueEq lv rv
in case (lv, rv) of
(NVClosure _ _, NVClosure _ _) -> unsafePtrEq
(NVList _, NVList _) -> unsafePtrEq
(NVSet _ _, NVSet _ _) -> unsafePtrEq
_ -> valueEq lv rv _ -> valueEq lv rv
-- | Checks whether two containers are equal, using the given item equality -- | Checks whether two containers are equal, using the given item equality

View file

@ -13,6 +13,7 @@ import Control.Monad.Catch
import Control.Monad (when) import Control.Monad (when)
import Control.Monad.IO.Class import Control.Monad.IO.Class
import qualified Data.HashMap.Lazy as M import qualified Data.HashMap.Lazy as M
import Data.List ((\\))
import Data.Maybe (isJust) import Data.Maybe (isJust)
import Data.String.Interpolate.IsString import Data.String.Interpolate.IsString
import qualified Data.Set as S import qualified Data.Set as S
@ -41,7 +42,7 @@ case_zero_div = do
assertNixEvalThrows "builtins.div 1.0 0.0" assertNixEvalThrows "builtins.div 1.0 0.0"
case_bit_ops = do case_bit_ops = do
-- mic92 (2018-08-20): change to constantEqualText, -- mic92 (2018-08-20): change to constantEqualText,
-- when hnix's nix fork supports bitAnd/bitOr/bitXor -- when hnix's nix fork supports bitAnd/bitOr/bitXor
constantEqualText' "0" "builtins.bitAnd 1 0" constantEqualText' "0" "builtins.bitAnd 1 0"
constantEqualText' "1" "builtins.bitOr 1 1" constantEqualText' "1" "builtins.bitOr 1 1"
@ -373,17 +374,45 @@ case_empty_string_not_equal_null_is_true =
case_null_equal_not_empty_string_is_true = case_null_equal_not_empty_string_is_true =
constantEqualText "true" "null != \"\"" constantEqualText "true" "null != \"\""
case_list_nested_bottom_diverges =
assertNixEvalThrows "let nested = [(let x = x; in x)]; in nested == nested"
case_attrset_nested_bottom_diverges =
assertNixEvalThrows "let nested = { y = (let x = x; in x); }; in nested == nested"
case_list_list_nested_bottom_equal =
constantEqualText "true" "let nested = [[(let x = x; in x)]]; in nested == nested"
case_list_attrset_nested_bottom_equal =
constantEqualText "true" "let nested = [{ y = (let x = x; in x); }]; in nested == nested"
case_list_function_nested_bottom_equal =
constantEqualText "true" "let nested = [(_: let x = x; in x)]; in nested == nested"
case_attrset_list_nested_bottom_equal =
constantEqualText "true" "let nested = { y = [(let x = x; in x)];}; in nested == nested"
case_attrset_attrset_nested_bottom_equal =
constantEqualText "true" "let nested = { y = { y = (let x = x; in x); }; }; in nested == nested"
case_attrset_function_nested_bottom_equal =
constantEqualText "true" "let nested = { y = _: (let x = x; in x); }; in nested == nested"
----------------------- -----------------------
tests :: TestTree tests :: TestTree
tests = $testGroupGenerator tests = $testGroupGenerator
genEvalCompareTests = do genEvalCompareTests = do
files <- filter ((==".nix") . takeExtension) <$> D.listDirectory testDir td <- D.listDirectory testDir
return $ testGroup "Eval comparison tests" $ map mkTestCase files
let unmaskedFiles = filter ((==".nix") . takeExtension) td
let files = unmaskedFiles \\ maskedFiles
return $ testGroup "Eval comparison tests" $ map (mkTestCase testDir) files
where where
testDir = "tests/eval-compare" mkTestCase td f = testCase f $ assertEvalFileMatchesNix (td </> f)
mkTestCase f = testCase f $ assertEvalFileMatchesNix (testDir </> f)
instance (Show r, Show (NValueF m r), Eq r) => Eq (NValueF m r) where instance (Show r, Show (NValueF m r), Eq r) => Eq (NValueF m r) where
NVConstantF x == NVConstantF y = x == y NVConstantF x == NVConstantF y = x == y
@ -439,3 +468,10 @@ freeVarsEqual a xs = do
xs' = S.fromList xs xs' = S.fromList xs
free = freeVars a' free = freeVars a'
assertEqual "" xs' free assertEqual "" xs' free
maskedFiles :: [FilePath]
maskedFiles =
[ "builtins.fetchurl-01.nix" ]
testDir :: FilePath
testDir = "tests/eval-compare"

View file

@ -13,6 +13,8 @@ import Data.List (delete, sort)
import Data.List.Split (splitOn) import Data.List.Split (splitOn)
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Set (Set)
import qualified Data.Set as Set
import qualified Data.Text as Text import qualified Data.Text as Text
import qualified Data.Text.IO as Text import qualified Data.Text.IO as Text
import Data.Time import Data.Time
@ -56,11 +58,23 @@ From (git://nix)/tests/lang.sh we see that
groupBy :: Ord k => (v -> k) -> [v] -> Map k [v] groupBy :: Ord k => (v -> k) -> [v] -> Map k [v]
groupBy key = Map.fromListWith (++) . map (key &&& pure) groupBy key = Map.fromListWith (++) . map (key &&& pure)
-- | New tests, which have never yet passed. Once any of these is passing,
-- please remove it from this list. Do not add tests to this list if they have
-- previously passed.
newFailingTests :: Set String
newFailingTests = Set.fromList
[ "eval-okay-path"
, "eval-okay-fromTOML"
, "eval-okay-context-introspection"
, "eval-okay-concatmap"
, "eval-okay-builtins-add"
]
genTests :: IO TestTree genTests :: IO TestTree
genTests = do genTests = do
testFiles <- sort testFiles <- sort
-- jww (2018-05-07): Temporarily disable this test until #128 is fixed. -- jww (2018-05-07): Temporarily disable this test until #128 is fixed.
. filter ((/= "eval-okay-path") . takeBaseName) . filter ((`Set.notMember` newFailingTests) . takeBaseName)
. filter ((/= ".xml") . takeExtension) . filter ((/= ".xml") . takeExtension)
<$> globDir1 (compile "*-*-*.*") "data/nix/tests/lang" <$> globDir1 (compile "*-*-*.*") "data/nix/tests/lang"
let testsByName = groupBy (takeFileName . dropExtensions) testFiles let testsByName = groupBy (takeFileName . dropExtensions) testFiles

View file

@ -0,0 +1,25 @@
let
plain = (let x = x; in x);
nested_list = [(let x = x; in x)];
nested_attrset = { y = (let x = x; in x); };
nested_list_list = [[(let x = x; in x)]];
nested_list_attrset = [{ y = (let x = x; in x); }];
nested_list_function = [(_: let x = x; in x)];
nested_attrset_list = { y = [(let x = x; in x)]; };
nested_attrset_attrset = { y = { y = (let x = x; in x); }; };
nested_attrset_function = { y = (_: let x = x; in x); };
tests = [
# (plain == plain) # Diverges
# (nested_list == nested_list) # Diverges
# (nested_attrset == nested_attrset) # Diverges
(nested_list_list == nested_list_list)
(nested_list_attrset == nested_list_attrset)
(nested_list_function == nested_list_function)
(nested_attrset_attrset == nested_attrset_attrset)
(nested_attrset_list == nested_attrset_list)
(nested_attrset_function == nested_attrset_function)
];
in tests

View file

@ -0,0 +1,5 @@
with builtins;
let a = fetchurl "https://haskell.org";
in [ a (hasContext a) ]

View file

@ -0,0 +1 @@
builtins.hasContext builtins.currentSystem

View file

@ -0,0 +1,3 @@
let x = 1;
f = { ... }: x;
in f { x = 2; }