{"id":15168753,"url":"https://github.com/goose-lang/goose","last_synced_at":"2026-04-05T17:36:36.804Z","repository":{"id":34703393,"uuid":"169891530","full_name":"goose-lang/goose","owner":"goose-lang","description":"Goose converts a subset of Go to Rocq","archived":false,"fork":false,"pushed_at":"2025-03-25T15:44:36.000Z","size":3642,"stargazers_count":112,"open_issues_count":16,"forks_count":14,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-25T16:43:17.357Z","etag":null,"topics":["coq","go"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/goose-lang.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2019-02-09T17:15:07.000Z","updated_at":"2025-03-12T17:29:17.000Z","dependencies_parsed_at":"2023-11-17T20:29:22.844Z","dependency_job_id":"f7af62de-0d02-4814-b447-0753772d0e59","html_url":"https://github.com/goose-lang/goose","commit_stats":{"total_commits":819,"total_committers":15,"mean_commits":54.6,"dds":0.2991452991452992,"last_synced_commit":"6787e01cef93d900c0018ed84f44141f26d1fbb9"},"previous_names":["goose-lang/goose","tchajed/goose"],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goose-lang%2Fgoose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goose-lang%2Fgoose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goose-lang%2Fgoose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goose-lang%2Fgoose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/goose-lang","download_url":"https://codeload.github.com/goose-lang/goose/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247325693,"owners_count":20920714,"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":["coq","go"],"created_at":"2024-09-27T06:41:29.903Z","updated_at":"2026-04-05T17:36:31.770Z","avatar_url":"https://github.com/goose-lang.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Goose: a subset of Go with a semantics in Coq\n\n[![CI](https://github.com/goose-lang/goose/actions/workflows/ci.yml/badge.svg)](https://github.com/goose-lang/goose/actions/workflows/ci.yml)\n[![](https://godoc.org/github.com/goose-lang/goose?status.svg)](https://godoc.org/github.com/goose-lang/goose)\n\nGoose is a subset of Go equipped with a semantics in Coq, as well as translator to automatically convert Go programs written in Go to a model in Coq. The model plugs into [Perennial](https://github.com/mit-pdos/perennial) for carrying out verification of concurrent storage systems.\n\nGoose includes a reasonable subset of Go, enough to write serious concurrent programs. The translator and semantics are trusted; you can view the process as giving a semantics to Go. The semantics is given via a translation to GooseLang, an untyped lambda calculus with references and concurrency, including some special support to work for Go.\n\n## Demo: an example conversion\n\nTo give a flavor of what goose does, let's look at an example:\n\nFile `append_log.go`:\n\n```go\n// Append-only, sequential, crash-safe log.\n//\n// The main interesting feature is that the log supports multi-block atomic\n// appends, which are implemented by atomically updating an on-disk header with\n// the number of valid blocks in the log.\npackage append_log\n\nimport (\n\t\"sync\"\n\n\t\"github.com/tchajed/marshal\"\n\n\t\"github.com/goose-lang/goose/machine/disk\"\n)\n\ntype Log struct {\n\tm      *sync.Mutex\n\tsz     uint64\n\tdiskSz uint64\n}\n\nfunc (log *Log) mkHdr() disk.Block {\n\tenc := marshal.NewEnc(disk.BlockSize)\n\tenc.PutInt(log.sz)\n\tenc.PutInt(log.diskSz)\n\treturn enc.Finish()\n}\n\nfunc (log *Log) writeHdr() {\n\tdisk.Write(0, log.mkHdr())\n}\n\nfunc Open() *Log {\n\thdr := disk.Read(0)\n\tdec := marshal.NewDec(hdr)\n\tsz := dec.GetInt()\n\tdiskSz := dec.GetInt()\n\treturn \u0026Log{m: new(sync.Mutex), sz: sz, diskSz: diskSz}\n}\n```\n\nGoose output:\n\n```coq\n(* autogenerated from append_log *)\nFrom Perennial.goose_lang Require Import prelude.\nFrom Perennial.goose_lang Require Import ffi.disk_prelude.\n\nFrom Goose Require github_com.tchajed.marshal.\n\n(* Append-only, sequential, crash-safe log.\n\n   The main interesting feature is that the log supports multi-block atomic\n   appends, which are implemented by atomically updating an on-disk header with\n   the number of valid blocks in the log. *)\n\nModule Log.\n  Definition S := struct.decl [\n    \"m\" :: lockRefT;\n    \"sz\" :: uint64T;\n    \"diskSz\" :: uint64T\n  ].\nEnd Log.\n\nDefinition Log__mkHdr: val :=\n  rec: \"Log__mkHdr\" \"log\" :=\n    let: \"enc\" := marshal.NewEnc disk.BlockSize in\n    marshal.Enc__PutInt \"enc\" (struct.loadF Log.S \"sz\" \"log\");;\n    marshal.Enc__PutInt \"enc\" (struct.loadF Log.S \"diskSz\" \"log\");;\n    marshal.Enc__Finish \"enc\".\n\nDefinition Log__writeHdr: val :=\n  rec: \"Log__writeHdr\" \"log\" :=\n    disk.Write #0 (Log__mkHdr \"log\").\n\nDefinition Open: val :=\n  rec: \"Open\" \u003c\u003e :=\n    let: \"hdr\" := disk.Read #0 in\n    let: \"dec\" := marshal.NewDec \"hdr\" in\n    let: \"sz\" := marshal.Dec__GetInt \"dec\" in\n    let: \"diskSz\" := marshal.Dec__GetInt \"dec\" in\n    struct.new Log.S [\n      \"m\" ::= lock.new #();\n      \"sz\" ::= \"sz\";\n      \"diskSz\" ::= \"diskSz\"\n    ].\n```\n\n## Go support\n\nGoose doesn't support arbitrary Go code; it uses a carefully-selected subset of Go that is still idiomatic Go (for the most part) while being easy to translate. See the [implementation design doc](docs/implementation.md) for more details.\n\nThere are three aspects of the Go model worth mentioning here:\n\n- Assignments are not supported, only bindings. This imposes a \"linearity\" restriction where each variable is constant after being first defined, which aligns well with how Perennial code works. Mutable variables can still be emulated using pointers and heap allocation.\n- Goose supports a few common control flow patterns so code can be written with early returns and some loops.\n- Many operations are carefully modeled as being non-linearizable in Perennial, although this area is subtle and hard to reconcile with Go's documented memory model.\n\n## Related approaches\n\nGoose solves the problem of using Perennial to verify runnable systems while ensuring some connection between the verification and the code. How else do verification projects get executable code?\n\n_Extraction_ is a popular approach, especially in Coq. Extraction is much like compilation in that Coq code is translated to OCaml or Haskell, relying on many similarities between the languages. (Side note: the reason it's called extraction is that it also does _proof erasure_, where proofs mixed into Coq code are removed so they don't have to be run.) To get side effects in extracted code the verification engineers write an interpreter in the target language that runs the verified part, using the power of the target language to interact with the outside world (eg, via the console, filesystem, or network stack).\n\nA big disadvantage of extraction is performance. The source code is several steps away from the extracted code and interpreter. This distance makes it hard to debug performance problems and reduces developers' control over what the running code does.\n\nAnother approach is to use a deeply-embedded language that closely represents an efficient imperative language, such as C. Then one can write code directly in this language (eg, Cogent), produce it as part of proof-producing synthesis (eg, Fiat Cryptography), import it from normal source code (eg, `clightgen` for VST). Regardless of how the code in the language is produced, this approach requires a model of the syntax and semantics of the language at a fairly low-level, including modeled function calls, variable bindings, and local stacks, not to mention concurrency and side effects.\n\nDirectly modeling a low-level, imperative language (and especially writing in it) gives a great deal of control, which is good for performance. This approach is also well-suited to end-to-end verification: at first, one can pretty-print the imperative language and use any compiler, but eventually one can shift to a verified compiler and push the assumptions down the software stack.\n\n## Running goose\n\nGoose requires Go 1.22+.\n\nYou can install goose with either `go install github.com/goose-lang/goose/cmd/goose@latest` or from a clone of this repo with `go install ./cmd/goose`. These install\nthe `goose` binary to `~/go/bin`.\n\nTo run `goose` on one of the internal examples and update the Coq output in\nPerennial, run (for the append_log example):\n\n```\ngoose -out $perennial/src/goose_lang/examples/append_log.v \\\n  ./internal/examples/append_log\n```\n\nwhere `$perennial` is the path to a clone of [Perennial](https://github.com/mit-pdos/perennial).\n\n## Developing goose\n\nThe bulk of goose is implemented in `goose.go` (which translates Go) and\n`internal/coq/coq.go` (which has structs to represent Coq and GooseLang code,\nand code to convert these to strings).\n\nGoose has integration tests that translate example code and compare against a\n\"gold\" file in the repo. Changes to these gold files are hand-audited when\nthey are initially created, and then the test ensures that previously-working\ncode continues to translate correctly. To update the gold files after\nmodifying the tests or fixing goose, run `go test -update-gold` (and manually\nreview the diff before committing).\n\nThe tests include `internal/examples/unittest`, a collection of small\nexamples intended for wide coverage, as well as some real programs in other\ndirectories within `internal/examples`.\n\nThe Coq output is separately tested by building it within the [Perennial\n](https://github.com/mit-pdos/perennial) source tree.\n\nAdditionally, the `internal/examples/semantics` package contains a set of\nsemantic tests for checking that translated programs preserve their semantic\nmeaning. The testing infrastructure requires test functions to have the form\n`func test*() bool` and return true (that is, they begin with \"test\", take no\narguments, and return a bool expected to always be true). Any helper functions\nshould _not_ follow this pattern.\n\nThe `cmd/test_gen/main.go` file generates the required test files from these\nfunctions. The files are:\n\n- A `.go` file which uses the Go `testing` package. This is for confirming that\n  tests actually do always return true, as intended. Note that the use of the\n  testing package means that this file must end in `_test.go`. It should go in\n  the `semantics/` directory.\n- A `.v` file that runs using Perennial's semantic interpreter. This file\n  should go in Perennial's `goose_lang/interpreter/` directory and can have any name.\n\nTo generate these files, the executable takes a required flag of either `-go` or\n`-coq` which specifies the file type, an optional `-out` argument specifying\nwhere to write the output, and finally the path to the semantics package. For\nexample, `go run ./cmd/test_gen -coq -out ~/code/perennial/src/goose_lang/interpreter/generated_test.v ./internal/examples/semantics` generates the Coq file of semantics tests.\n(You'll probably need to adjust the path to Perennial.) To re-generate the Go\ntest file you can just run `go generate ./...`.\n\n### Running tests\n\nUse a tool such as [act](https://github.com/nektos/act) to run the CI tests locally.\nTo automatically fix all formatting issues and update the generated files, run `make fix`.\nIf the 'gold' files need updating, run `go test -update-gold`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoose-lang%2Fgoose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoose-lang%2Fgoose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoose-lang%2Fgoose/lists"}