{"id":13647034,"url":"https://github.com/ucsd-progsys/liquidhaskell","last_synced_at":"2026-01-18T00:40:28.440Z","repository":{"id":4763397,"uuid":"5913967","full_name":"ucsd-progsys/liquidhaskell","owner":"ucsd-progsys","description":"Liquid Types For Haskell","archived":false,"fork":false,"pushed_at":"2025-05-05T21:19:11.000Z","size":60457,"stargazers_count":1238,"open_issues_count":450,"forks_count":147,"subscribers_count":28,"default_branch":"develop","last_synced_at":"2025-05-05T21:27:28.752Z","etag":null,"topics":["haskell","refinement-types","smt","verification"],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ucsd-progsys.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2012-09-22T15:06:55.000Z","updated_at":"2025-05-05T20:51:56.000Z","dependencies_parsed_at":"2023-07-06T01:46:47.506Z","dependency_job_id":"df17bbe5-1287-4ceb-82bd-f6e51104042a","html_url":"https://github.com/ucsd-progsys/liquidhaskell","commit_stats":{"total_commits":10242,"total_committers":113,"mean_commits":90.63716814159292,"dds":0.6014450302675258,"last_synced_commit":"63337d432b47c1ba1ec9925db0994fc5cdce3eaf"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ucsd-progsys%2Fliquidhaskell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ucsd-progsys%2Fliquidhaskell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ucsd-progsys%2Fliquidhaskell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ucsd-progsys%2Fliquidhaskell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ucsd-progsys","download_url":"https://codeload.github.com/ucsd-progsys/liquidhaskell/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254020640,"owners_count":22000756,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["haskell","refinement-types","smt","verification"],"created_at":"2024-08-02T01:03:18.771Z","updated_at":"2026-01-18T00:40:28.426Z","avatar_url":"https://github.com/ucsd-progsys.png","language":"Haskell","funding_links":[],"categories":["Haskell"],"sub_categories":[],"readme":"![LiquidHaskell](/resources/logo.png)\n\n\n[![Hackage](https://img.shields.io/hackage/v/liquidhaskell.svg)](https://hackage.haskell.org/package/liquidhaskell) [![Build Status](https://img.shields.io/circleci/project/ucsd-progsys/liquidhaskell/develop.svg)]([https://circleci.com/gh/ucsd-progsys/liquidhaskell](https://app.circleci.com/pipelines/github/ucsd-progsys/liquidhaskell?branch=develop))\n\nThis is the **development** site of the LiquidHaskell formal verification tool.\n\nIf you're a LiquidHaskell **user** (or just curious), you probably want to go to [the documentation website](https://ucsd-progsys.github.io/liquidhaskell/) instead.\n\n## Trying it from Hackage\n\nAssuming the [Z3](https://github.com/Z3Prover/z3) SMT solver is installed and\navailable in your `PATH`, you can run\n\n```\ncabal install --lib liquidhaskell liquid-prelude liquid-vector --package-env . --force-reinstalls\nghc -fplugin=LiquidHaskell FILE.hs\n```\n\n`--package-env .` creates a `.ghc.environment*` file in the current folder that makes\n`ghc` find the `LiquidHaskell` plugin only when invoking it from there.\nOtherwise, global user configuration would be affected.\n\nNote that the installation step is influenced by the dependencies that are\nalready installed in your system, so different results may be obtained by\ndifferent users.\n\n## Trying it from GitHub\n\nThe github repo only builds with GHC 9.12.2, which must be in your `PATH`.\n\n```\ngit clone https://github.com/ucsd-progsys/liquidhaskell.git\ncd liquidhaskell\ngit submodule update --init\ncabal build liquidhaskell liquid-prelude liquid-vector\n\ncabal exec -- ghc -fplugin=LiquidHaskell FILE.hs\n```\n\n## Asking for Help\n\nIf you have questions or you just need help, you can always reach out on our [slack channel](https://join.slack.com/t/liquidhaskell/shared_invite/enQtMjY4MTk3NDkwODE3LTFmZGFkNGEzYWRkNDJmZDQ0ZGU1MzBiZWZiZDhhNmY3YTJiMjUzYTRlNjMyZDk1NDU3ZGIxYzhlOTIzN2UxNWE), [google groups mailing list](https://groups.google.com/forum/#!forum/liquidhaskell), [GitHub issue tracker](https://github.com/ucsd-progsys/liquidhaskell/issues), or by emailing [Ranjit Jhala](https://github.com/ranjitjhala), [Niki Vazou](https://github.com/nikivazou).\n\n# Contributing\n\nThis is an open-source project, and we love getting feedback (and patches)!\n\n## Reporting a Bug\n\nIf something doesn't work as it should, please consider opening a [github issue](https://github.com/ucsd-progsys/liquidhaskell/issues)\nto let us know. If possible, try to:\n\n* Try to use a descriptive title;\n* State as clearly as possible what is the problem you are facing;\n* Provide a small Haskell file producing the issue;\n* Write down the expected behaviour vs the actual behaviour;\n* Please, let us know which liquidhaskell version you are using.\n\n## Your first Pull Request\n\nWe are thrilled to get PRs! Please follow these guidelines, as doing so will increase the chances of\nhaving your PR accepted:\n\n* The main LH repo [lives here](https://github.com/ucsd-progsys/liquidhaskell)\n* Please create pull requests against the `develop` branch.\n* Please be sure to include test cases that illustrate the effect of the PR\n    - e.g. show new features that that are supported or how it fixes some previous issue\n* If you're making user-visible changes, please also add documentation\n    - e.g. [options.md](docs/mkDocs/docs/options.md), [specifications.md](docs/mkDocs/docs/specifications.md), the [main tutorial](https:///github.com/ucsd-progsys/intro-refinement-types) (as relevant)\n\nPull requests don't just have to be about code: documentation can often be improved too!\n\n# General Development Guide\n\nFor those diving into the implementation of LiquidHaskell, here are a few tips:\n\n## Faster recompilation\n\nWhen changing the `liquidhaskell-boot` library, sometimes we don't want\nto rebuild `liquidhaskell` or `liquid-vector` when testing the changes.\nIn these cases we can set the environment variable `LIQUID_DEV_MODE=true`\nwhen running `cabal` to skip rebuilding those packages.\n\nDANGER: Note that this can give an invalid result if the changes to\n`liquidhaskell-boot` do require rebuilding other `liquid*` packages.\n\n## How To Run Regression Tests\n\nFor documentation on the `test-driver` executable itself, please refer to the\n`README.md` in `tests/` or run `cabal run tests:test-driver -- --help`\n\nYou can run *all* the tests by\n\n    $ ./scripts/test/test_plugin.sh\n\nYou can run a bunch of particular test-groups instead by\n\n    $ ./scripts/test/test_plugin.sh \u003ctest-group-name1\u003e \u003ctest-group-name2\u003e ...\n\nand you can list all the possible test options with\n\n    $ ./scripts/test/test_plugin.sh --help\n\nor get a list of just the test groups, one per line, with\n\n    $ ./scripts/test/test_plugin.sh --show-all\n\nTo pass in specific parameters, you can invoke cabal directly with\n\n    $ cabal build tests:\u003ctest-group-name\u003e --ghc-options=-fplugin-opt=LiquidHaskell:--no-termination\n\nFor example:\n\n    $ cabal build tests:unit-neg --ghc-options=-fplugin-opt=LiquidHaskell:--no-termination\n\nAnother useful option is to change the underlying solver:\n\n    $ cabal build tests:unit-pos --ghc-options=-fplugin-opt=LiquidHaskell:--smtsolver=cvc5\n\nYou can also modify the number of used threads, depending on cores etc.\n\nYou can directly extend and run the tests by modifying the files in\n\n    tests/harness/\n\n### Parallelism in Tests\n\nTests run in parallel, unless the flag `--measure-timings` is specified to `test_plugin.sh`.\n\n## How to create performance comparison charts\n\nFirstly, make sure to remove previous compilation artifacts of tests before measuring performance,\nor GHC will skip running Liquid Haskell on modules that don't need recompilation. Unfortunately,\nusing `-fforce-recomp` is not ideal (see [#2565](https://github.com/ucsd-progsys/liquidhaskell/pull/2565)\nfor more details). At the time of writing, previous compilation artifacts of tests can be removed with\n\n```bash\nfind dist-newstyle -name \"tests-*\" -o -name \"prover-ple-lib-*\" | xargs rm -r\n```\n\nWhen `liquidhaskell` tests run, we can collect timing information with\n\n    $ ./scripts/test/test_plugin.sh --measure-timings\n\nMeasures will be collected in `.dump-timings` files under `dist-newstyle` directory. These can be\nconverted to json data with\n\n```bash\ncabal v2-build ghc-timings\ncabal v2-exec ghc-timings dist-newstyle\n```\n\nwhich will produce `tmp/*.json` files.\n\nThen a csv report can be generated from the json files with\n```\ncabal v2-run benchmark-timings -- tmp/*.json --phase LiquidHaskellCPU -o summary.csv\n```\nOn each line, the report will contain the the wall-clock time taken by each test.\nUse `--phase LiquidHaskell` to get the wall-clock time instead. Although in that\ncase you need to use the `--measure-timings-j1` option when running the tests,\nwhich validates modules one at a time.\n\nComparison charts in `svg` format can be generated by invoking\n\n```\ncabal v2-run plot-performance -- -b path_to_before_summary.csv -a path_to_after_summary.csv -s 50\n```\n\nThis will generate two files `top.svg` and `bot.svg` containing the top 50 speedups and slowdowns\nover the entire test set, both enabled by the `-s` option) in the current directory. Alternatively,\na subset of the tests can be selected by a prefix of their names with `-f \u003cprefix\u003e`, for instance\n`-f benchmarks`. `-f` will generate a file `filtered.svg` containing the comparisons of the selected tests.\nThe `-f` and `-s` options can be used/omitted independently. If both are omitted, a single `perf.svg`\nwill be produced covering the full input test set. Additionally, their effects can be combined by providing a third `-c`\noption (this  will produce 2 files `filtered-top.svg` and `filtered-bot.svg` instead of 3). An optional key `-o` can be\nsupplied to specify an output directory for the generated files.\n\nThere is also a legacy script `scripts/plot-performance/chart_perf.sh` that can be used to generate comparison charts\nin both `svg` and `png` formats. It requires [gnuplot](http://www.gnuplot.info/) to run and assumes both files contain\nthe same test set. The following command will produce two files `perf.svg` and `perf.png` in the current directory.\n\n    $ scripts/plot-performance/chart_perf.sh path_to_before_summary.csv path_to_after_summary.csv\n\nThe current formatting is optimized for comparing some subsets of the full test run, typically just the benchmarks alone.\nIf one wishes to save time or is not interested in top speedups/slowdowns, the benchmark subset can be obtained by running\n\n    $ scripts/test/test_plugin.sh \\\n        benchmark-stitch-lh \\\n        benchmark-bytestring \\\n        benchmark-vector-algorithms \\\n        benchmark-cse230 \\\n        benchmark-esop2013 \\\n        benchmark-icfp15-pos \\\n        benchmark-icfp15-neg\n\n## Miscelaneous tasks\n\n* **Profiling** See the instructions in [scripts/profiling-driver/ProfilingDriver.hs](scripts/profiling-driver/ProfilingDriver.hs).\n* **Getting stack traces on exceptions** See `-xc` flag in the [GHC user's guide][ghc-users-guide].\n* **Working with submodules** See `man gitsubmodules` or the [git documentation site][git-documentation].\n\n[ghc-users-guide]: https://downloads.haskell.org/ghc/latest/docs/users_guide/\n[git-documentation]: https://git-scm.com/doc\n\n## GHC support policy\n\nLH supports only one version of GHC at any given time. This is because LH depends heavily on the `ghc` library\nand there is currently no distinction between public API's and API's internal to GHC. There are currently no\nrelease notes for the `ghc` library and breaking changes happen without notice and without deprecation\nperiods. Supporting only one GHC version saves developer time because it obviates the need for `#ifdef`'s\nthroughout the codebase, or for an compatibility layer that becomes increasingly difficult to write as we\nattempt to support more GHC versions. Porting to newer GHC versions takes less time, the code is easier to\nread and there is less code duplication.\n\nUsers of older versions of GHC can still use older versions of LH.\n\n## The GHC.API module\n\nIn order to minimize the effort in porting LH to new releases of GHC, we need a way to abstract over breaking\nchanges in the `ghc` library, which might change substantially with every major GHC release. This is\naccomplished by the [GHC.API][] module. The idea is that **rather than importing multiple `ghc` modules,\nLH developers must import this single module in order to write future-proof code**. This is especially\nimportant for versions of the compiler greater than 9, where the module hierarchy changed substantially,\nand using the [GHC.API][] makes it easier to support new versions of GHC when they are released.\n\n### Fragile import strategy\n\n```haskell\nimport Predicate\nimport TyCoRep\n\n...\n\n-- This will break if 'isEqPrimPred' is (re)moved or the import hierarchy changes.\nfoo :: Type -\u003e Bool\nfoo = isEqPrimPred\n```\n\n### Recommended import strategy\n\n```haskell\nimport qualified Language.Haskell.Liquid.GHC.API as GHC\n\n...\n\nfoo :: GHC.Type -\u003e Bool\nfoo = GHC.isEqPrimPred -- OK.\n```\n\n# GHC Plugin Development Guide\n\nThis code commentary describes the current architecture for the GHC [Plugin][] that enables LiquidHaskell\nto check files as part of the normal compilation process. For the sake of this commentary, we refer to\nthe code provided as part of the `release/0.8.10.2` branch, commit `9a2f8284c5fe5b18ed0410e842acd3329a629a6b`.\n\n## GHC.Interface vs GHC.Plugin\n\nThe module [GHC.Plugin][] is the main entrypoint for all the plugin functionalities. Whenever possible, this\nmodule is reusing common functionalities from the [GHC.Interface][], which is the original module used to\ninterface LH with the old executable. Generally speaking, the [GHC.Interface][] module is considered \"legacy\"\nand it's rarely what one wants to modify. It will probably be removed at some point.\n\n## Plugin architecture\n\nBroadly speaking, the Plugin is organised this way: In the [typechecking phase][], we typecheck and desugar\neach module via the GHC API in order to extract the unoptimised [core binds][] that are needed by\nLH to work correctly. This is due to a tension in the design space; from one side LH needs access to the\n\"raw\" core binds (binds where primitives types are not unboxed in the presence of a PRAGMA annotation,\nfor example) but yet the user can specify any arbitrary optimisation settings during compilation and we do\nnot want to betray the principle of least expectation by silently compiling the code with `-O0`. Practically\nspeaking, this introduces some overhead and is far from ideal, but for now it allows us to iterate quickly.\nThis phase is also responsible for:\n\n* Extracting the [BareSpec][]s associated to any of the dependent modules;\n* Producing the [LiftedSpec][] for the currently-compiled module;\n* Storing the [LiftedSpec][] into an interface annotation for later retrieval;\n* Checking and verifying the module using LH's existing API.\n\nThe reason why we do everything in the [typechecking phase][] is also to allow integrations with tools like\n[ghcide][]. There are a number of differences between the plugin and the operations performed as part of the\n[GHC.Interface][], which we are going to outline in the next section.\n\n## Differences with the GHC.Interface\n\n- The [GHC.Interface][] pre-processes the input files and calls into [configureGhcTargets][] trying to\n  build a dependency graph by discovering dependencies the target files might require. Then, from this\n  list any file in the include directory is filtered out, as well as any module which has\n  a \"fresh\" `.bspec` file on disk, to save time during checking. In the [GHC.Plugin][] module though\n  we don't do this and for us, essentially, each input file is considered a target, where we exploit the\n  fact GHC will skip recompilation if unnecessary. This also implies that while the [GHC.Interface][] calls\n  into [processTargetModule][] only for target files, the [GHC.Plugin][] has a single, flat function simply\n  called [processModule][] that essentially does the same as `GHC.Interface.processModule` and\n  `GHC.Interface.processTargetModule` fused together.\n\n- While the [GHC.Interface][] sometimes \"assembles\" a [BareSpec][] by `mappend`ing the `commSpec` (i.e. comment spec)\n  with the [LiftedSpec][] fetched from disk, if any, the Plugin doesn't do this but rather piggybacks on the\n  [SpecFinder][] (described later) to fetch dependencies' specs.\n\n- There is a difference in how we process LIQUID pragmas. In particular, for the executable they seems to\n  be accumulated \"in bulk\" i.e. if we are refining a *target* module `A` that depends on `B`, `B` seems to\n  inherit whichever flags we were using in the *target* module `A`. Conversely, the source plugin is \"stateless\"\n  when it comes to LIQUID options, i.e. it doesn't have memory of _past_ options, what it counts when compiling\n  a module `B` is the _global_ options and _any_ option this module defines. The analogy is exactly the\n  same as with GHC language extensions, they have either global scope (i.e. `default-extensions` in the\n  cabal manifest) or local scope (i.e. `{-# LANGUAGE ... #-}`).\n\n## Finding specs for existing modules\n\nThis is all done by a specialised module called the [SpecFinder][]. The main exported function is\n[findRelevantSpecs][] which, given a list of `Module`s, tries to retrieve the `LiftedSpec`s associated with\nthem. Typically this is done by looking into the interface files of the input modules, trying to deserialise\nany `LiftedSpec` from the interface file's annotations.\n\n# General Development FAQs\n\n## A new version of GHC is out. How do I support it?\n\nTypically the first thing you might want to do is to run a \"clean\" `cabal build` using\nthe latest compiler and \"check the damage\". If you are lucky, everything works out of the box, otherwise\ncompilation might fail with an error, typically because some `ghc` API function has been removed/moved/renamed.\nThe way to fix it is to modify the [GHC.API][] shim module and perform any required change, likely by\nconditionally compiling some code in a `CPP` block. For minor changes, it's usually enough to perform small\nchanges, but for more tricky migrations it might be necessary to backport some GHC code, or create some\npatter synonym to deal with changes in type constructors.\n\n## Is there a way to run the testsuite for different versions of GHC?\n\nCurrently, no. Only one version of GHC is supported and that is the one\nthat can be tested with `./scripts/test/test_plugin.sh`.\n\n[GHC.API]:             liquidhaskell-boot/src-ghc/Liquid/GHC/API.hs\n[Plugin]:              liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Plugin.hs\n[GHC.Plugin]:          liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Plugin.hs\n[GHC.Interface]:       liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Interface.hs\n[SpecFinder]:          liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Plugin/SpecFinder.hs\n[LiftedSpec]:          liquidhaskell-boot/src/Language/Haskell/Liquid/Types/Specs.hs#L559\n[TargetSrc]:           liquidhaskell-boot/src/Language/Haskell/Liquid/Types/Specs.hs#L158\n[typechecking phase]:  liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Plugin.hs#L211-L226\n[ghcide]:              https://github.com/haskell/ghcide\n[findRelevantSpecs]:   liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Plugin/SpecFinder.hs#L65\n[core binds]:          https://hackage.haskell.org/package/ghc-9.6.3/docs/GHC-Core.html#t:CoreBind\n[configureGhcTargets]: liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Interface.hs#L254\n[processTargetModule]: liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Interface.hs#L483\n[processModule]:       liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Plugin.hs#L509\n\n# Module Verification Process\n\nThe following graph summarizes the verification process of a module by showing\nits specification life cycle.\nSee [Specs.hs](./liquidhaskell-boot/src/Language/Haskell/Liquid/Types/Specs.hs)\nfor detailed documentation on the specification stages.\nThere are many moving parts hidden within each graph link,\nbut some places worth noticing here are\n[Liquid.hs](./liquidhaskell-boot/src/Language/Haskell/Liquid/Liquid.hs)\n—which implements constraint generation— and\n[SpecFinder.hs](./liquidhaskell-boot/src/Language/Haskell/Liquid/Liquid.hs)\n—that deals with the gathering of dependencies and `LHAssumptions`—.\n\n```mermaid\nflowchart LR\n  subgraph Liquid Haskell\n\n    subgraph Inputs\n      direction TB\n      LiftedDeps[\n        Dependencies of A\n        LifttedSpecs\n        ]\n      CoreBinds[CoreBinds]\n      Bare[BarseSpec]\n    end\n\n    subgraph Outputs\n      direction TB\n      T[TargetSpec] --\u003e Constraints[Verification\n                                    Constraints]\n      L[LiftedSpec]\n    end\n\n    Inputs --\u003e Outputs\n\n  end\n\n  A\u003eDepsOfA_LHAssumptions] --\u003e LiftedDeps\n  Deps\u003eDepsOfA.hi] --\u003e LiftedDeps\n  Mod(A.hs) --\u003e CoreBinds \u0026 Bare\n  Constraints --\u003e|checked by| fixpoint([liquid-fixpoint])\n  L ---\u003e |if verified:\n           serialize| File\u003eA.hi]\n```\n\nReference:\n[Implementing a GHC Plugin for Liquid Haskell](https://well-typed.com/blog/2020/08/implementing-a-ghc-plugin-for-liquid-haskell/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fucsd-progsys%2Fliquidhaskell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fucsd-progsys%2Fliquidhaskell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fucsd-progsys%2Fliquidhaskell/lists"}