Compare commits

..

368 Commits

Author SHA1 Message Date
xenia 25b2b677cd wip: feat: add support for z3 bit rotate exts 2026-05-31 23:42:41 -04:00
xenia 92d4091567 fix: test regression on newer Z3
z3 > 4.8.8 will return sign_extend and zero_extend operators in the
model, which were not handled by the smt-lib2 decoder, despite being
handled in the encoder. support for these operators has been added
2026-05-31 23:41:26 -04:00
xenia 38d467618e fix: always use system Z3 by default
rosette (for convenience) provides functionality to download and install
a z3 binary during raco installation. unfortunately this package is
currently very out of date, so this commit removes all install-time
functionality, causing rosette to fall back to searching for the z3
binary in the system PATH
2026-05-31 23:41:19 -04:00
dependabot[bot] 29808a02d2
Bump Bogdanp/setup-racket from 1.13 to 1.14 (#293)
Bumps [Bogdanp/setup-racket](https://github.com/bogdanp/setup-racket) from 1.13 to 1.14.
- [Release notes](https://github.com/bogdanp/setup-racket/releases)
- [Commits](https://github.com/bogdanp/setup-racket/compare/v1.13...v1.14)

---
updated-dependencies:
- dependency-name: Bogdanp/setup-racket
  dependency-version: '1.14'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 17:00:27 -07:00
dependabot[bot] e62e10e3a8 Bump Bogdanp/setup-racket from 1.11 to 1.13
Bumps [Bogdanp/setup-racket](https://github.com/bogdanp/setup-racket) from 1.11 to 1.13.
- [Release notes](https://github.com/bogdanp/setup-racket/releases)
- [Commits](https://github.com/bogdanp/setup-racket/compare/v1.11...v1.13)

---
updated-dependencies:
- dependency-name: Bogdanp/setup-racket
  dependency-version: '1.13'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-16 08:49:46 +07:00
Anish Athalye 128317d61c Fix incorrect type signatures 2025-05-16 08:49:35 +07:00
Gus Smith 3155c6ac72 Fix formatting 2025-05-01 10:06:04 +07:00
Gus Smith 61fdf3485d Print warning to stderr instead of stdout
This warning was cluttering stdout, which contained otherwise parseable data, causing errors downstream in my compiler. Moves the warning to stderr. Could also convert to using the logging package, but that would require more setup.
2025-05-01 10:06:04 +07:00
Gus Smith 53d54aa149 Fix STP build
See https://github.com/stp/stp/issues/503
2025-05-01 09:00:22 +07:00
dependabot[bot] cf703c60e2 Bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 16:53:41 -07:00
Gus Smith bb0dec0e74 Attempt STP fix 2024-06-17 15:17:53 -07:00
Gus Smith edf682df5e
Add support for STP and Yices2 (#273)
Also add documentation for Bitwuzla and cvc5

---------

Co-authored-by: Vishal Canumalla <vishalc@cs.washington.edu>
2023-12-14 13:43:11 -08:00
dependabot[bot] 63524aa7fe
Bump Bogdanp/setup-racket from 1.10 to 1.11 (#269)
Bumps [Bogdanp/setup-racket](https://github.com/bogdanp/setup-racket) from 1.10 to 1.11.
- [Release notes](https://github.com/bogdanp/setup-racket/releases)
- [Commits](https://github.com/bogdanp/setup-racket/compare/v1.10...v1.11)

---
updated-dependencies:
- dependency-name: Bogdanp/setup-racket
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 05:13:13 -05:00
dependabot[bot] 2f183fd2f0
Bump docker/build-push-action from 4 to 5 (#266)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-14 09:27:49 +07:00
dependabot[bot] ad417cc928
Bump docker/metadata-action from 4 to 5 (#265)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-14 09:27:42 +07:00
dependabot[bot] b6e0ea375e
Bump docker/login-action from 2 to 3 (#264)
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-14 09:27:35 +07:00
dependabot[bot] fd38ca31ca
Bump docker/setup-buildx-action from 2 to 3 (#263)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-14 09:27:27 +07:00
Gus Smith b3f792a3b4
Add support for Bitwuzla and CVC5 (#260)
Note: I've only enabled QF_BV for both solvers, as this is all I need at the moment. If you want to enable new solvers, you can just add new entries to the solver-features list in each file. This will likely involve having to fix tests as well.
2023-09-14 08:38:57 +07:00
dependabot[bot] 6096abafac Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-06 22:00:37 -07:00
sorawee 5dd348906d
avoid quadratic time processing in solver-(assert,min/maximize) (#261)
* avoid quadratic time processing in solver-(assert,min/maximize)

The time complexity of n calls to solver-assert / solver-minimize /
solver-maximize is currently O(n^2) due to list appending at the tail,
which requires traversal. This PR fixes the problem. The ordering of
solver-minimize and solver-maximize matters, however
(it specifies the lexicographic ordering minimization),
so rearranging them is slightly more complicated.

* Add an optimization order test

* Clear the solver
2023-08-10 18:38:48 -07:00
Emina Torlak 649184faf1 Update docs. 2023-07-04 13:37:56 -07:00
James Bornholt 15647f24b4
Install a custom Z3 build on Apple Silicon Macs (#254)
* Revert #145

The fix was shipped in Racket v7.7, and our minimum version is now v8.1,
so there's no longer a need for this.

* Install a custom Z3 build on Apple Silicon Macs

The Z3 version we use predates aarch64 Macs, so there's no pre-packaged
release available. The x86 version of Z3 works if the user has Rosetta
installed, but there's no obvious way to detect that, and it fails in
weird/silent ways if Rosetta isn't available.

So instead, let's install a custom Z3 4.8.8 aarch64 build in this case.
We can remove it whenever we go past Z3 4.8.16, which is when they
started releasing an aarch64 Mac binary. In the meantime, this makes
installation more obvious for users on Apple Silicon Macs.
2023-04-09 15:50:56 -07:00
dependabot[bot] 6d41d0e2fc Bump Bogdanp/setup-racket from 1.9 to 1.10
Bumps [Bogdanp/setup-racket](https://github.com/Bogdanp/setup-racket) from 1.9 to 1.10.
- [Release notes](https://github.com/Bogdanp/setup-racket/releases)
- [Commits](https://github.com/Bogdanp/setup-racket/compare/v1.9...v1.10)

---
updated-dependencies:
- dependency-name: Bogdanp/setup-racket
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-16 14:15:29 -05:00
dependabot[bot] 7cfd9b226c
Bump docker/build-push-action from 3 to 4 (#251)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4.
2023-01-31 17:26:49 -08:00
sorawee 88b3fd96c4
Use cache for path->pkg (#250) 2023-01-19 16:52:50 -08:00
sorawee d3c7abf0e4
Fix unintentional value retain (#248)
* Fix unintentional value retain

To quote to the documentation of parameters
(https://docs.racket-lang.org/reference/parameters.html),

> as far as the memory manager is concerned,
> the value originally associated with a parameter through parameterize
> remains reachable as long the continuation is reachable,
> even if the parameter is mutated.

The parameter current-terms is initialized and/or parameterized to a
strong hash, making it not possible to GC the hash.
This PR fixes the issue by initializing and/or parameterizing to #f
first, and then mutates the parameter to a desired value later.

Fixes #247
2022-11-11 14:27:27 -08:00
sorawee b58652b26f
Fix the help description (#246)
Fix the help description
2022-11-11 14:23:14 -08:00
Anish Athalye 7e696132ca
Make evaluate preserve vector immutability (#244)
Prior to this patch, the following returned an immutable vector:

    (define empty-model (solve (void)))
    (evaluate (vector-immutable 1) empty-model)

However, the following returned a _mutable_ vector, even though
`evaluate` is given an immutable vector (containing no symbolics):

    (define-symbolic* b boolean?)
    (define model (solve (assert b)))
    (evaluate (vector-immutable 1) model)

With this patch, both examples above return an immutable vector.
2022-10-16 17:54:51 -07:00
dependabot[bot] 4c23c80712
Bump Bogdanp/setup-racket from 1.8 to 1.9 (#243)
Bumps [Bogdanp/setup-racket](https://github.com/Bogdanp/setup-racket) from 1.8 to 1.9.
- [Release notes](https://github.com/Bogdanp/setup-racket/releases)
- [Commits](https://github.com/Bogdanp/setup-racket/compare/v1.8...v1.9)

---
updated-dependencies:
- dependency-name: Bogdanp/setup-racket
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-14 18:34:48 -07:00
sorawee c407b871f2
Disallow define-symbolic with different types (#240)
Programs like:

```
(define (f type)
  (define-symbolic x type)
  x)

(+ 1 (f integer?))
(+ 2 (f boolean?))
```

should not work. The second call in particular should result in an
error, rather than using the cached term (which is an integer).

This PR makes the above program invalid. The SDSL benchmarks show that
the performance was not affected by the change.
2022-08-17 16:15:01 -07:00
James Bornholt ec1a0db464 Don't fail the whole install if Z3 fails
The Racket package server doesn't like this, and it gives up on
building/publishing the docs. The new error message is clear enough, so
just don't propagate the exception.
2022-08-15 11:47:44 -05:00
James Bornholt 426ffbfcf6 Make Z3 install failures more obvious on unsupported platforms
Previously, when installing on platforms that don't have a prebuilt Z3
binary available from GitHub, we'd silently install an x86 Linux binary
anyway, and then things would mysteriously fail at run time. So let's
make two changes:
(1) explicitly check the architecture and only install for x86_64
    systems, since there are no prebuilt Z3 binaries for other
    architectures on the version of Z3 we use; and
(2) provide more visible feedback if the install script fails to install
    a Z3 binary, by moving it to be a post-install rather than
    pre-install phase.
2022-08-13 19:36:32 -05:00
dependabot[bot] 096430e93e Bump docker/setup-buildx-action from 1 to 2
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1 to 2.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-06 12:04:14 -05:00
dependabot[bot] f1dd43b229 Bump docker/build-push-action from 2 to 3
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 2 to 3.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-06 12:04:07 -05:00
dependabot[bot] 117d7aa240 Bump docker/login-action from 1 to 2
Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-06 12:04:01 -05:00
dependabot[bot] 374e728f6c Bump docker/metadata-action from 3 to 4
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 3 to 4.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-05 09:53:14 -05:00
dependabot[bot] 9520c30b8f Bump Bogdanp/setup-racket from 1.7 to 1.8
Bumps [Bogdanp/setup-racket](https://github.com/Bogdanp/setup-racket) from 1.7 to 1.8.
- [Release notes](https://github.com/Bogdanp/setup-racket/releases)
- [Commits](https://github.com/Bogdanp/setup-racket/compare/v1.7...v1.8)

---
updated-dependencies:
- dependency-name: Bogdanp/setup-racket
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-02 15:19:23 -05:00
sorawee 536953702b
improve error location (#228) 2022-04-09 11:07:33 -07:00
Emina Torlak 1d042d1368 Close #224. 2022-04-02 10:45:48 -07:00
James Bornholt c2975b9400 Fix parsing of SMT-LIB-compliant models
SMT-LIB says that models should not start with a 'model symbol, but most
SMT solvers have been doing so anyway until recently. So let's just
support both variants for the widest compatibility.

We had already fixed this specifically for Boolector, but we need to do
it everywhere, so this change makes a small refactoring to allow all SMT
solvers to share the same model parsing code, while still preserving
Boolector-specific fixups to the model after parsing.
2022-03-23 11:30:19 -05:00
Luke Nelson 10178550a0 Properly handle tags in Docker workflow 2022-03-07 21:29:36 -08:00
Luke Nelson eeb5e127bc Run Docker workflow on tags 2022-03-07 21:25:33 -08:00
Luke Nelson e35e920b2f 4.1 Release notes 2022-03-07 20:33:05 -08:00
Luke Nelson bc90edd502 Use pretty-write instead of pretty-print in print-forms
Using an example from the Rosette documentation, compare the output of
pretty-print (with print-as-expression set to #t):

(list
 'define
 '(bvmul2_bitfast x)
 (list 'bvadd 'x (list 'bvxor 'x (bv #x00 8))))

With the output of pretty-write:

(define (bvmul2_bitfast x) (bvadd x (bvxor x (bv #x00 8))))

The latter is properly formatted as Racket code and matches what is
shown in the Rosette guide.

Partially reverts 8fabaa8a0a ("Cleanup.").
2022-03-07 17:11:09 -08:00
dependabot[bot] c53aff68f8 Bump Bogdanp/setup-racket from 1.1 to 1.7
Bumps [Bogdanp/setup-racket](https://github.com/Bogdanp/setup-racket) from 1.1 to 1.7.
- [Release notes](https://github.com/Bogdanp/setup-racket/releases)
- [Commits](https://github.com/Bogdanp/setup-racket/compare/v1.1...v1.7)

---
updated-dependencies:
- dependency-name: Bogdanp/setup-racket
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-07 14:37:07 -06:00
Nicolas Jeannerod 54e1df67f9
Add Docker image and continuous deployment for it (#219)
* Add Docker image and continuous deployment for it

* Implement suggestions by @lukenels & @jamesbornholt

* Cleanup unused comments

* ensure `expeditor` is available for the REPL
2022-03-07 11:56:14 -08:00
Emina Torlak 9d14d447d0 Fix ite* rewrite bug. 2021-12-05 21:16:58 -08:00
Emina Torlak 3d84cdc17f Bump up the required Racket version to 8.1. 2021-11-18 15:19:44 -08:00
Emina Torlak 8684625ffd Bump up the required Racket version to 8.1. 2021-11-18 15:16:37 -08:00
sorawee c6e8dbc2ff
Fix bitvector type sharing in extract and use hasheq (#207)
extract seems to unintentionally de-shares bitvector types. This PR
makes the types shared properly again.

The hasheq change is technically backward-incompatible for programs that use
(bitvector 2^64), but I really hope no one does that in practice.
2021-11-18 15:09:49 -08:00
James Bornholt 2e2896db37
Fix model parsing for Boolector 3.2.2 (#206)
The latest Boolector release [changed][] the format of SMT models to conform
to the SMT-LIB spec by not including the `model` symbol, which our
parser was expecting (mostly because that's what Z3 does). It's not hard
to support both versions, so let's do that.

[changed]: 5c862bcdbc
2021-11-10 18:54:11 -08:00
Emina Torlak dbfd8476d9 Weakens the synthesize query to match the documented formula (Closes #205). 2021-11-06 13:46:45 -07:00
James Bornholt 7ac31ffbb4
Fix one more spurious CAS failure (#199)
Follow-up to #198.
2021-07-23 16:19:25 -07:00
sorawee 7cac2c93a5
Workaround spurious CAS failures (#198)
* Workaround spurious CAS failures

Some processors, such as ARM, can't perform the CAS (compare-and-swap)
operation accurately. So we simply retry on failure for several times.

* retry the right way
2021-07-23 09:28:53 -05:00
John Clements 38743f6a5f
remove dependency on now-nonexistent file (#191)
* remove dependency on now-nonexistent file

see commit c76b803836 which removes
the file and the use of define/lift

* remove rosette/lib/lift from tooltip in guide docs
2021-05-20 08:36:22 -07:00
Emina Torlak 9f6322c9da Drop outdated support for Yices (#189). 2021-04-16 13:41:10 -07:00
Emina Torlak 3432d9529d Add int2bv feature to bvlib rotate tests (#189). 2021-04-16 12:39:27 -07:00
sorawee a7ea8a849b
optimize: restrict keyword to #:maximize and #:minimize (#188)
* optimize: restrict keyword to #:maximize and #:minimize

* allow #:maximize and then #:minimize

Providing #:minimize twice will still be prohibited,
since it will error already by function application
after the expansion of `optimize`.
2021-04-14 13:12:03 -07:00
sorawee a64e2bccfe
Scribble-ify (#184)
- More linkify
- Fix minor typos
- Fix quote style
2021-03-18 08:45:45 -07:00
Emina Torlak ac96664ae9 Update a cross-reference in the Guide. 2021-03-11 13:49:36 -08:00
Emina Torlak 9b3d3dcac0 Update synthcl to use new synthesis constructs. 2021-03-05 10:01:11 -08:00
Emina Torlak 59079bbb47 Fix synthax tests. 2021-03-04 15:05:30 -08:00
Emina Torlak 46d44cfdc4 Fix synthax tests. 2021-03-04 12:49:19 -08:00
Emina Torlak 1defa8edf6 Update the CI configuration. 2021-03-04 12:28:38 -08:00
Emina Torlak ca6fa33674 Update the CI configuration. 2021-03-04 11:44:51 -08:00
Emina Torlak dcdc60c0e2 Add Rosette 4.0 release notes. 2021-03-04 10:27:46 -08:00
Emina Torlak 723cd8a859 Merge remote-tracking branch 'upstream/master' 2021-03-04 09:12:39 -08:00
Emina Torlak c092b65b88 Add 3.2 release notes. 2021-03-04 09:07:45 -08:00
Emina Torlak fdb779ad13 Add concrete? and symbolic? predicates for checking if a value is fully concrete or not (#183). 2021-03-03 15:11:03 -08:00
Emina Torlak 1f8c86235c Update docs. 2021-03-01 10:33:33 -08:00
Emina Torlak 8acc778659 Document library procedures for operating on vectors with bitvector indices. 2021-02-26 14:27:14 -08:00
Emina Torlak 66c0fd55c0 Document library procedures for operating on lists with bitvector indices. 2021-02-26 14:11:11 -08:00
Emina Torlak 55f0239957 Update doc log files. 2021-02-26 09:51:31 -08:00
Emina Torlak 90739dddee Test library procedures for operating on lists and vectors with bitvector indices. 2021-02-25 17:05:34 -08:00
Emina Torlak 6b33bb5fb2 Add library procedures for operating on lists and vectors with bitvector indices. 2021-02-25 10:47:41 -08:00
Emina Torlak c138d015bc Cleanup. 2021-02-22 15:59:14 -08:00
Emina Torlak 4366dad61f Improve the synthax tests. 2021-02-22 14:40:40 -08:00
Emina Torlak 3aeb00ba61 Update comment. 2021-02-22 10:54:31 -08:00
Emina Torlak 34ff8d98fc Update info.rkt and CI config to use Racket 8.0. 2021-02-22 09:32:37 -08:00
Emina Torlak ada8683db9 Cleanup. 2021-02-19 14:42:07 -08:00
Emina Torlak 8fabaa8a0a Cleanup. 2021-02-19 14:00:19 -08:00
Emina Torlak f803299c5b Revise the guide: Debugging. 2021-02-17 15:56:10 -08:00
Emina Torlak 252ec0076c Revise the guide: Performance. 2021-02-17 12:26:39 -08:00
Emina Torlak 532aa8ec6f Revise the guide: Unsafe Operations. 2021-02-16 19:45:57 -08:00
Emina Torlak c185cf7065 Drop current-oracle and oracle. 2021-02-16 18:26:29 -08:00
Emina Torlak b4f6ae11ee Revise the guide: more consistent naming for Solvers and Solutions. 2021-02-16 15:27:59 -08:00
Emina Torlak 490c4fe08d Replace term-cache with (terms) and (with-terms ...). 2021-02-16 15:04:39 -08:00
Emina Torlak 91d25ea132 Rename normal -> normal and ans? -> normal?. 2021-02-12 21:30:02 -08:00
Emina Torlak 2fabc4b7dd Rename halt -> failed and halt? -> failed?. 2021-02-12 21:01:51 -08:00
Emina Torlak d34c2ae893 Revise the guide: Reflecting on Symbolic State. 2021-02-12 20:36:12 -08:00
Emina Torlak 091b6696a5 Rename vc-merge! -> merge-vc! and vc-get -> get-vc. 2021-02-12 20:27:27 -08:00
Emina Torlak 6281fa85fa Rename vc-clear! -> clear-vc!. 2021-02-12 19:50:22 -08:00
Emina Torlak ac0fb55594 Revise the guide: Reflecting on Symbolic State. 2021-02-12 18:39:36 -08:00
Emina Torlak e63e2afbce Revise the guide: Reflecting on Symbolic Values. 2021-02-11 10:42:55 -08:00
Emina Torlak c76b803836 Revise the guide: Reflecting on Symbolic Values. Drop define-lift. 2021-02-11 10:29:17 -08:00
Emina Torlak 7d1d80c615 Revise the guide: Utility Libraries (no edits). 2021-02-10 13:32:10 -08:00
Emina Torlak d33902935d Revise the guide: Exported Racket Libraries and Solver-Aided Libraries. 2021-02-10 12:54:13 -08:00
Emina Torlak 190f6dc864 Revise the guide: Structures. 2021-02-05 09:45:18 -08:00
Emina Torlak 4b95c79c82 Drop CPLEX. 2021-02-05 09:33:11 -08:00
Emina Torlak 21cd2a603d Revise the guide: Built-In Datatypes, section 9. 2021-02-05 09:13:51 -08:00
Emina Torlak 9f3935c945 Revise the guide: Built-In Datatypes, section 4-8. 2021-02-04 22:05:04 -08:00
Emina Torlak 5658e0bbb6 Revise the guide: Built-In Datatypes, section 3. Implement new doc value printer. 2021-02-04 16:14:15 -08:00
Emina Torlak c8c4f567eb Revise the guide: Built-In Datatypes, sections 1-2. 2021-02-03 20:37:14 -08:00
Emina Torlak d5fea77903 Revise the guide: Built-In Datatypes, section 1. 2021-02-03 15:45:59 -08:00
Emina Torlak e1bb97c363 Rename spec? -> vc? and spec-* -> vc-*. 2021-02-03 15:12:10 -08:00
Emina Torlak 7f1d49854e Rename spec? -> vc? and spec-* -> vc-*. 2021-02-03 15:07:04 -08:00
Emina Torlak 119c42b550 Rename get-vc -> vc-get. 2021-02-03 13:03:11 -08:00
Emina Torlak 4a47f2c60f Rename merge-vc! -> vc-merge!. 2021-02-03 12:56:43 -08:00
Emina Torlak 251d6979c4 Rename clear-vc! -> vc-clear!. 2021-02-03 12:35:59 -08:00
Emina Torlak 5efc79fdbd Rename spec-tt -> vc-true, spec-tt? -> vc-true?. 2021-02-03 11:59:57 -08:00
Emina Torlak a7b551f8ac Enable the updated parts of the doc to be built from the command line. 2021-02-03 10:53:56 -08:00
Emina Torlak 1626c801b2 Revise the guide: Solver-Aided Forms, sections 6-7. 2021-02-03 09:40:05 -08:00
Emina Torlak fcf41193b0 Revise the guide: Solver-Aided Forms, sections 1-5. 2021-02-02 21:47:55 -08:00
Emina Torlak 28eeaff660 Revise the guide: Rosette Essentials, all sections. 2021-02-02 10:12:23 -08:00
Emina Torlak 33a0d67e26 Revise the guide: Rosette Essentials, section 3.4.3. 2021-02-01 16:26:49 -08:00
Emina Torlak f5eaa8ab19 Revise the guide: Rosette Essentials, section 3.4.3. 2021-02-01 16:26:28 -08:00
Emina Torlak a24c26dd23 Limit the size of the printed representation of models. 2021-02-01 14:53:43 -08:00
Emina Torlak c0016dcd2f Enable error tracing tests. 2021-02-01 10:30:36 -08:00
Emina Torlak 160698a279 Merge branch 'error-tracer' into 'master'
reenable error tracer for Rosette 4

See merge request unsat/rosette4!2
2021-02-01 10:08:31 -08:00
Emina Torlak 9c602590db Revise the guide: Rosette Essentials, sections 3.4.2-3.4.3. 2021-01-29 22:58:55 -08:00
Emina Torlak 844e64c96d Revise the guide: Rosette Essentials, sections 3.4.2-3.4.3. 2021-01-29 22:15:15 -08:00
Emina Torlak 06fcf3f0c0 Patch the construction of the unfinitized model to account for bv->* casts. 2021-01-29 10:23:17 -08:00
Sorawee Porncharoenwase e37f49bb8a reenable error tracer for Rosette 4 2021-01-29 10:05:11 -08:00
Emina Torlak 9348350ddc Revise the guide: Rosette Essentials, sections 3.4.0-3.4.2. 2021-01-28 22:04:15 -08:00
Emina Torlak 7f8cc7ed33 Revise the guide: Rosette Essentials, sections 3.4.0-3.4.2. 2021-01-28 22:02:43 -08:00
Emina Torlak bb28ff3ff4 Revise the guide: Rosette Essentials, sections 3.4.0-3.4.1. 2021-01-28 15:41:13 -08:00
Emina Torlak a8a4963641 Revise the guide: Rosette Essentials, sections 3.2-3.3. 2021-01-28 13:26:18 -08:00
Emina Torlak bded3d198b Revise the guide: Rosette Essentials, sections 3.1-3.2. 2021-01-28 09:44:32 -08:00
Emina Torlak 264a380074 Revise the guide: Rosette Essentials, sections 3.1-3.2. 2021-01-27 21:36:29 -08:00
Emina Torlak 7793b854fa Revise the guide: Rosette Essentials, sections 3.1-3.2. 2021-01-27 21:34:47 -08:00
Emina Torlak 5cd994dc65 Revise the guide: Rosette Essentials, sections 3.1-3.2. 2021-01-27 21:12:34 -08:00
Emina Torlak 1db42caf32 Revise the guide: Rosette Essentials, sections 3.1-3.2. 2021-01-27 21:11:37 -08:00
Emina Torlak 61405b2758 Revise the guide: Rosette Essentials, sections 3.1-3.2. 2021-01-27 21:07:43 -08:00
Emina Torlak 9b494e8506 Revise the guide: Rosette Essentials, sections 1-3.1. 2021-01-26 13:14:52 -08:00
Emina Torlak a06b0bb3e8 Expose the grammar depth parameter. 2021-01-22 09:14:16 -08:00
Emina Torlak 40f01c47cc Optimize value merging. 2021-01-20 14:22:52 -08:00
Emina Torlak 38753c35be Optimize the symbolics procedure for large encodings. 2021-01-19 14:21:39 -08:00
Emina Torlak f254252e1a Add a define-sketch construct that provides syntactic sugar for defining single-production grammars. 2021-01-18 13:09:58 -08:00
Emina Torlak 925ad30a4c Clean up and patch bv tests. 2021-01-18 10:52:00 -08:00
Emina Torlak 46378e1f7d Clean up old code and patch bv tests. 2021-01-15 11:52:08 -08:00
Emina Torlak 7240f29d03 Update the syntax of define-symbolic[*] forms.
The new syntax uses the #:length keyword to specify the number of
symbolic constants to be created. This replaces the old syntax,
which allowed multidimensional lists to be created and used
ambiguous syntax.

To update client code to the new define-symbolic[*] forms, every
instance of

(define-symbolic id type [ k ])

should be replaced with

(define-symbolic id type #:length k)

Note that multidimensional lists are no longer supported. It's
easy for any application that needs this functionality
to implement it on top of one-dimensional lists.
2021-01-14 15:13:28 -08:00
Emina Torlak fcfd4105a4 Update print-forms to use pretty-display. 2021-01-11 13:07:26 -08:00
Emina Torlak a89e3195e7 Merge remote-tracking branch 'upstream/master' 2021-01-11 10:08:31 -08:00
Emina Torlak bb08ef1a1b Update docs with the minimum version for CVC4 (closes #179). 2021-01-11 10:06:57 -08:00
Emina Torlak f93f401c98 Merge remote-tracking branch 'upstream/master' 2021-01-11 09:38:12 -08:00
Emina Torlak a1b7ab9820 Add the define-grammar construct to the synthax library, along with unit tests. 2021-01-08 11:35:54 -08:00
Emina Torlak 7b692ae2bf Integrate the new sketching library and add more tests. 2021-01-06 14:44:50 -08:00
Emina Torlak 3e06ba6b52 Add more tests for the new sketching library. 2021-01-04 12:28:09 -08:00
Emina Torlak 6522b7eb70 Add a new sketching library and basic tests. 2020-12-31 19:57:32 -08:00
Yisu Remy Wang ce02eb4fa9
Support CVC4 1.8 (#178)
* Test CVC4 1.8

* Update CVC4 options.
2020-12-29 09:28:05 -08:00
Emina Torlak 5216bb9194 Add tests for the new boolean rewrites. 2020-12-28 14:23:20 -08:00
Emina Torlak 8f1a632ead Optimize VC merging. 2020-12-22 13:40:09 -08:00
Emina Torlak d463d90387 Optimize VC merging. 2020-12-22 12:44:56 -08:00
Emina Torlak 4b6f869ee8 Optimize VC merging. 2020-12-21 22:47:34 -08:00
Emina Torlak 715141f5ee Add a comment on term ordering guarantee. 2020-12-21 16:33:21 -08:00
Emina Torlak 003054078a Merge remote-tracking branch 'upstream/master' 2020-12-18 11:03:02 -08:00
sorawee ffc0b0ab16
bmv: handle #%variable-reference (#177)
* bmv: handle #%variable-reference

Because bmv transforms `define-values v` to `define-syntax v`,
a varref on `v` would fail, as `v` now doesn't have a location.

The fix is to adjust the shadow variables to have the same scope as the
original `v`, and perform varref on the shadow variables instead.
Note that while the names of shadow variables might collide with existing
variables, `generate-temporaries` makes sure that the scope of the
newly generated symbols won't collide, so this should cause no problem.

* substitute only when the variable is mutated
2020-12-18 09:48:27 -08:00
Emina Torlak 603088bf3b Add a rewrite to simplify ((x && y) && (x => y)) to (x && y). 2020-12-17 15:30:08 -08:00
Emina Torlak a683b38878 Change assume/assert implementation to avoid creating msg thunks. 2020-12-16 15:50:09 -08:00
Emina Torlak 30d12a6038 Drop the unused exceptions. 2020-12-15 11:20:52 -08:00
Emina Torlak cf9e56cced Add a key rewrite rule for => to partially evaluate default assumptions (due to branching). 2020-12-11 19:12:09 -08:00
Emina Torlak 20dcfe4ca0 Revert "Try a minimized encoding that loses context."
This reverts commit d0ffeffcde.

The problem with this encoding is that it loses context in a
way that breaks nested queries. Once we drop the context (i.e.,
assumes and asserts issued so far), then we no longer have a
way to tell whether a given query can ever be reached. This
was also a problem in Rosette 3.0, because nested queries
didn't account for the PC.
2020-12-11 14:41:46 -08:00
Emina Torlak d0ffeffcde Try a minimized encoding that loses context. 2020-12-11 13:53:57 -08:00
Emina Torlak a82907b015 Add more rewrite rules for boolean terms. 2020-12-11 10:50:15 -08:00
Emina Torlak 57034f50d1 Update websynth to use CVC4 for testing for now. 2020-12-10 15:12:19 -08:00
Emina Torlak f1acc839fe Drop with-asserts, with-asserts-only, asserts, clear-asserts. 2020-12-10 14:26:34 -08:00
Emina Torlak c335d8984c Update sdsl/bv and sdsl/synthcl. 2020-12-10 12:47:45 -08:00
Emina Torlak 8fc406ae91 Finish integrating new VC into rosette/base/core/*. 2020-12-09 16:05:17 -08:00
Emina Torlak 9ae245eade Integrate new VC into rosette/base/adt/* 2020-12-09 11:02:38 -08:00
Emina Torlak 2d2e72de33 Integrate new VC into queries. 2020-12-08 15:44:05 -08:00
Emina Torlak 9c1b16ed5c Drop the pc parameter. 2020-12-04 20:24:56 -08:00
Emina Torlak ac7ecd1244 Integrate new VC into base/core/bool, base/core/safe, base/core/forall, base/form/control, lib/roseunit. 2020-12-04 20:15:14 -08:00
Emina Torlak 822775ee3a Begin integrating new VC gen: replace the old VC gen implementation with failing stubs. 2020-12-04 14:17:09 -08:00
Emina Torlak cc965a0732 Clean up imports and uses of (pc). 2020-12-04 13:59:26 -08:00
Emina Torlak 4bd52bdbcb Drop the debug query. 2020-12-04 12:06:59 -08:00
Emina Torlak 1a0f00da29 Add tests for guarded evaluation. 2020-12-04 10:21:38 -08:00
Emina Torlak 3a781f13d2 Revise and add tests for new VC gen. 2020-12-02 20:10:20 -08:00
Emina Torlak 216932accd Simplify the exception hierarchy and handling for asserts / assumes. 2020-12-02 10:03:42 -08:00
Emina Torlak ca3059ff70 Add the new VC generation implementation. 2020-12-01 13:09:14 -08:00
Emina Torlak 5b98580f1a Merge remote-tracking branch 'upstream/master' 2020-11-30 10:22:46 -08:00
sorawee dde06f39ab
Preserve syntax properties whenever possible (#176) 2020-11-25 19:19:57 -08:00
Emina Torlak a50253de21 Implement, integrate, and test a new store tracking module. 2020-11-24 10:09:26 -08:00
Emina Torlak 484e26762c Disable tracing tests for now. 2020-11-20 13:08:40 -08:00
Emina Torlak 4d7a69cb08 Start implementing assumes; add a new module for capturing memory side-effects. 2020-11-20 11:13:20 -08:00
James Bornholt a24d2bc934
Remove Travis CI (#174) 2020-11-03 08:49:39 -08:00
James Bornholt d3dca21c4a
Add GitHub actions workflow for CI (#173) 2020-11-03 08:10:08 -08:00
sorawee 0e12b2ce72
Add the value destructuring library (#172)
* Add the value destructing library

* Fix doc + more restriction

* Add missing docs + adjust id checks

* clarify underscore

* Reorder actual vs expected

* Minor doc update.

* Add e.g. since there are other forms of ellipsis

Co-authored-by: Emina Torlak <emina@alum.mit.edu>
2020-11-02 20:37:12 -08:00
sorawee d82ce66f57
with-continuation-mark based tracer (#169)
* checkpoint: w-c-m based instrumentation

* checkpoint: make macro generated #%app push marks

* remove symbilic-trace-tail?

* Add stress tests

* Fix a bug when macro originates error

* Use eq? based comparison

* Track syntax on original core form + make cert from fun body

* Add a corresponding test

* Code clean up

* Check that we are instrumenting module form

If users use eval, it could be an arbitrary syntax
which could potentially break our invariant.
2020-09-15 10:10:58 -07:00
sorawee 9dac710924
Instrument define-values (#168)
* Instrument define-values

The current tracer fails when a macro expands to define-values
at the top level due to an erroneous assumption that define-values
could not result in an error, which is not true as demonstrated in
macro-define.rkt. This PR thus adds define-values to a list of forms
to instrument. Note that define-values is different from other forms
because it's not an expression, so it needs a special handling.
The change also requires us to manually keep track the inferred name.

* lib must be in a separate file to trigger the bug
2020-09-13 09:50:05 -07:00
sorawee 38a9d7ee59
Workaround a Racket CS bug (#167)
Racket doc guarantees that a procedure name in a "stack trace" will be a
symbol, but it's a string in Racket CS, causing a failure in the tracer.
This PR workarounds the problem.

Related: racket/racket#3398
2020-09-13 09:49:01 -07:00
James Bornholt 1b30a7c7aa
Mark `bv` constants as not quotable (#166)
Similar to #37, `bv`s need to be marked as not quotable, so that they
round-trip through `print` and `eval` correctly.
2020-08-20 11:24:07 -07:00
Anish Athalye 25b0673ade
Provide writeln and println from Racket (#165) 2020-06-30 12:43:55 -07:00
Emina Torlak 932dbb952b Fix the install script for Windows. 2020-06-25 12:28:08 -07:00
Emina Torlak f2d98dcf88 Add notes for the 3.1 release. 2020-06-25 10:13:47 -07:00
Emina Torlak e52783b20a Update the member procedure to take the optional is-equal? argument (resolves #151). 2020-06-25 09:21:52 -07:00
Emina Torlak c942e19744 Clean up the CI configuration. 2020-06-25 08:07:26 -07:00
Emina Torlak 929c848981 Update the default solver to the latest release of Z3.
This commit changes the Rosette install script to use the latest release of Z3
as the default solver instead of the custom Z3 version used so far. It also
changes some of the slower SDSL tests to use Boolector instead of Z3, to
compensate for the performance differences between the old and new Z3 versions.

The install script now works as follows. If the bin directory contains a symlink
to a Z3 binary, that symlink is left in place, so running `raco pkg install` has
no effect on the Z3 binary.  That is, Rosette will continue to use the symlink-ed
version of Z3. Otherwise, the install script downloads the latest release of Z3,
and this release replaces the Z3 binary (if any) that is currently in the bin
directory.
2020-06-24 15:49:22 -07:00
sorawee 3b58b5af88
Add node_modules to compile-omit-files (#163)
Prevent raco setup / raco pkg from traversing through ~5000 directories
inside node_modules
2020-06-16 08:49:45 -07:00
Anish Athalye e6ed9a4100
Add more bv rewrites (#162)
This patch adds the following bitvector-related rewrites:

- Add bv extract / extract rewrite
- Generalize bv extract / {sign,zero}-extend rewrite

Before this patch, extract / {sign,zero}-extend was only simplified if
we were extracting out exactly the argument of the {sign,zero}-extend.
However, we can simplify this for any `(extract i 0 _)` while
introducing at most as many new terms as we did before.
2020-06-02 14:43:32 -07:00
Emina Torlak 77581c6c8f
Switch to a different Travis OS (#162). 2020-06-02 14:16:56 -07:00
Anish Athalye dd86136d40
Generalize bv extract / concat rewrite (#160)
This patch generalizes the rewrite rule for
`(extract _ _ (concat _ _))` to handle all cases where the result
depends only on the left or right argument of the `concat`. The old set
of rewrite rules handled the case where the result was exactly one of
the arguments of the concat; e.g. if `x` and `y` are `(bitvector 32)`,
then optimizing:

    (extract 63 32 (concat x y)) -> x

We can generalize this and do a little bit better for the cases where
the result depends only on the left or right argument of the concat, but
does not literally simplify to the left or right argument. For example:

    (extract 63 33 (concat x y)) -> (extract 31 1 x)

Before this patch, this would not have been simplified at all.

Compared to the previous version, this version emits no more new terms.
2020-05-26 20:03:02 -07:00
sorawee 6a165a8229
Use custom-load so that users don't need to remove compiled dir (#159)
* Use custom-load so that users don't need to remove compiled dir

* Address PR feedback
2020-05-20 14:17:35 -07:00
sorawee 123f235dd0
Add rackunit-doc as a build-deps (#158) 2020-05-15 17:57:59 -07:00
sorawee a3df4f92cb
Client for error tracer (#156)
* Client for error tracer

* PR feedback + docs

* Remove symlinks

* minor cleanup

* Minor doc edits.

Co-authored-by: Emina Torlak <emina@alum.mit.edu>
2020-05-15 14:06:51 -07:00
sorawee b1940c9f85
Fix an error when events are empty; support no internet for SymPro (#157) 2020-05-15 12:47:43 -07:00
sorawee 9baab4c02f
Add an error tracer for symbolic evaluation (#153)
* Add an error tracer for symbolic evaluation

* PR feedback: macro is there for performance

* Make tests run on all-rosette-tests, fix error-print-width

* Cut errmsg to workaround racket/racket#2991 and older bug in v7.0

* Revise the error trace docs.

* Kill --infeasible flag, and always enable its behavior

* Minor edits to the error trace docs.

Co-authored-by: Emina Torlak <emina@alum.mit.edu>
2020-04-24 14:03:23 -07:00
James Bornholt 1d1fd15d95 Update output format argument to Boolector 2020-04-23 09:03:17 -07:00
Emina Torlak c0d4e29ccd Allow Racket CS build to fail. 2020-04-18 22:12:56 -07:00
Emina Torlak 4669ab3ca8 Add a bv extract / extend rewrite. 2020-04-18 21:38:12 -07:00
Emina Torlak 49b35f4190 Add a library of useful bitvector operators.
This commit closes #152 and #136 by providing a set of new bitvector
operators including rotations, min/max, add/sub 1, converting a
bitvector to a list of bits, and more. It also fixes a bug in the
core bitvector library that caused binary and n-ary operators to
not emit enough type assertions in some cases.
2020-04-12 22:17:22 -07:00
Spencer Florence 4f1e129a20
add missing pkg deps (#150) 2020-03-10 08:42:50 -07:00
sorawee a896457f60
Fix #147 (improve struct performance) and lift define-struct (#148)
This commit syncs changes from
0de88f203d
which speeds up struct performance when there are a lot of fields.
Note that there are other changes in `define-struct.rkt` that are left unsynced
which are probably safe to sync but have no real practical value in Rosette
(like supporting the #:authentic flag), so I leave them out. Following script

```
(define (generate n)
  (with-syntax ([(xs ...) (for/list ([i n]) (gensym))])
    (match-define-values (_ cpu _ _)
      (time-apply eval (list #'(struct foo (xs ...) #:transparent))))
    cpu))

;; warm up eval by padding with a dummy entry
(rest (for/list ([n (in-sequences '(0) (in-range 0 1001 100))])
        (list n (generate n))))
```

generates

```
'((0 3)
  (100 134)
  (200 474)
  (300 1122)
  (400 2156)
  (500 3378)
  (600 4857)
  (700 6840)
  (800 9492)
  (900 14984)
  (1000 22540))
```

before this commit. After applying the fix, the result is now:

```
'((0 40)
  (100 12)
  (200 68)
  (300 59)
  (400 69)
  (500 91)
  (600 116)
  (700 132)
  (800 224)
  (900 178)
  (1000 216))
```

This commit also fixes `define-struct` which was incorrectly exported, causing
it to be unlifted. In particular:

```
(define-struct a (b) #:transparent)

(define-symbolic b boolean?)

(a-b (if b (make-a 1) (make-a 2)))
```

in `#lang rosette/safe` used to fail, and this commit fixes the bug.
2020-03-07 19:13:32 -08:00
Emina Torlak 998caa95b7 Make Rosette's case form consistent with Racket's case. 2020-03-04 09:45:19 -08:00
James Bornholt 911e03adfa
Work around bug in Racket's get-pure-port redirection (#145)
GitHub has started using a lowercase "location" header for redirects
sometimes. Racket only checks for the uppercase version. So we need to
pull in that code and modify it to handle either version.
2020-02-24 20:13:31 -08:00
Emina Torlak 67802efd75 This closes #144 by renaming doc -> guide and simplifying the directory structure. 2020-02-24 16:18:45 -08:00
Stephen Chang d42de9dd11
add gui-lib and gui-doc deps (#142) 2020-02-03 14:00:57 -08:00
sorawee a2c26337ce
Reorder tests so that structs with prop:procedure are reported correctly (#141)
* Reorder tests so that a struct with prop:procedure is reported as a struct

* Reorder again to display computed procedure correctly
2020-01-29 14:57:20 -08:00
sorawee 9cfdc9855a
Add the value browser (#138)
* Add the value browser

* Improve docs

* +lazy rendering, +index

* Further doc improvement

* Split number to integer and real

* racket -> code, add a quote

* another doc rewrite

* Minor doc updates.
2020-01-29 13:33:00 -08:00
Emina Torlak 83ce51b542 Increase the minimum required Racket version to 7.0. 2020-01-29 11:32:54 -08:00
James Bornholt 09b4983b2b
Build latest Racket release (both 3m and CS) in Travis (#140) 2020-01-24 16:07:18 -08:00
Emina Torlak 97e35967bf Tighter bounds for the (extract n n x) rewrite rule. 2020-01-03 16:31:46 -08:00
Emina Torlak dc43d2140e Add a rewrite rule for simplifying (extract n n x) for any n (see #137). 2020-01-03 16:21:28 -08:00
Emina Torlak b7dbbfcf8c Revert to old quantifier behavior, with better docs and with-asserts semantics. 2019-12-27 15:07:31 -08:00
Emina Torlak 9b70472af8 Implement a more intuitive treatment of assertions emitted by the bodies of quantified formulas. 2019-12-18 14:50:14 -08:00
James Bornholt fd9d157652 Update support for Boolector 3.x (#132) 2019-12-09 10:40:49 -08:00
Emina Torlak 2a9bc24cd9 Fix a bug in evaluate that is due to the use of unsafe operators. 2019-12-06 12:28:05 -08:00
sorawee ee695d2894 Macrology and optimization for for/all (#130)
* Macrology and optimization for for/all

- Support multiple expressions in the body of for/all and for*/all
- Optimize for/all with concrete list when the value is also concrete.
  In that case, there's no need to perform speculation which is expensive:

Before:

> (time (for/all ([x 1 (range 1 10000)]) 1))

cpu time: 75 real time: 75 gc time: 14

After:

> (time (for/all ([x 1 (range 1 10000)]) 1))

cpu time: 12 real time: 12 gc time: 0

* fix a stray paren

* Address PR feedback
2019-12-05 12:57:51 -08:00
Emina Torlak efe22c924b Add a rewrite rule for (bvadd a (bvadd b c)) where a and b are constants, analogous to +. 2019-07-19 16:17:56 -07:00
James Bornholt 5f9f1404d4
Add Racket v7.2 to CI 2019-03-15 18:22:24 -07:00
sorawee c886f72b78 Fix typo in module* export (#120) 2019-03-15 18:20:22 -07:00
Emina Torlak 8fa8780031 Extend the SMT solution decoder to handle let expressions in SMT output. 2019-03-02 14:46:34 -08:00
Emina Torlak cb19801290 Equality tests need to run only once with the default solver. 2019-02-24 23:07:26 -08:00
Emina Torlak f42e03441d Fix equal? to work correctly in the presence of user-defined equal+hash procedures. 2019-02-24 22:49:34 -08:00
Sam Elliott cb701a9c74 Use the user-provided solver paths for non-Z3 solvers (#118) 2019-02-10 20:09:46 -05:00
Emina Torlak a9e88e7d6c Add a rewrite rule bvadd/ite, analogus to +/ite. 2019-02-06 17:06:50 -08:00
Emina Torlak c5c9dfb03a Closes #115.
The query in this issue causes Z3 to return algebraic numbers.
To avoid having to support these, we instead support decoding
Z3's approximate decimal representation of real numbers, and
when algebraic numbers are returned, we raise an error that
suggests re-solving the constraints using an instance of Z3
configured to print approximate decimal representation of (all)
real numbers.
2019-01-19 15:03:38 -08:00
Emina Torlak 08e141f69e Add solver-options to the public interface. 2019-01-16 09:51:22 -08:00
James Bornholt ecedf93098
Merge pull request #113 from rodamber/patch-1
Fix typo
2018-12-26 09:28:16 -08:00
Rodrigo Bernardo 49c6b8502c
Fix typo 2018-12-26 12:37:20 +00:00
James Bornholt 451a373b54 Use hash methods directly in env.rkt
The dict methods introduce a lot of overhead (~20-25%) for benchmarks
that are bottlenecks on encoding. In this file, we know envs are always
hashes, so we don't need the additional indirection.
2018-12-20 12:03:47 -08:00
Emina Torlak 3bbd7604c8 Extend SMT decoder to handle concat/extract terms in solver output. 2018-12-10 17:34:02 -08:00
sorawee d4c0e6e8b3 Fix an obvious wrong error message (#109) 2018-10-18 08:09:34 -07:00
Emina Torlak 8fb7435c77
Update the link to the guide. 2018-09-05 14:52:22 -07:00
James Bornholt 7b87f14cfd Add missing dependencies. 2018-08-24 08:57:06 -07:00
James Bornholt e4b56fae94 Add symbolic profiling to Rosette. 2018-08-23 13:44:24 -07:00
James Bornholt eb5a390596 Update release notes for Yices. 2018-08-08 12:41:11 -07:00
James Bornholt d6fe42d4db Don't test Yices on Travis
The prebuilt Yices binary requires a CPU that supports the BMI
extensions for x86 (i.e., Haswell or newer). But Travis sometimes runs
jobs on EC2 c3 instances, which are Ivy Bridge, causing illegal
instruction exceptions. Rather than going to the trouble of compiling
Yices during CI, let's just not test it.
2018-08-08 11:23:28 -07:00
James Bornholt 6b5ffa0de7 Make Travis fail if solver downloads fail 2018-08-07 15:43:03 -07:00
James Bornholt 160523fb7f Add Yices support 2018-08-07 14:21:05 -07:00
James Bornholt fd1e3115f7 Require Racket 6.9; test on 7.0. 2018-07-28 13:38:08 -07:00
James Bornholt 5bf0005b8a Bump info.rkt version 2018-07-28 13:38:02 -07:00
Emina Torlak 2a5f38556b Draft the release notes for Rosette 3.0. 2018-07-25 14:41:05 -07:00
Emina Torlak fabb30e528 Merge branch 'master' into v3.0 2018-07-25 13:30:35 -07:00
Emina Torlak c4a2cef095 Closes #107. 2018-07-25 13:29:21 -07:00
Emina Torlak ef3b790baa Merge branch 'master' into local-bitwidth 2018-07-25 12:36:31 -07:00
James Bornholt 305f6b78a1 Expose per-solver options and allow solvers to be cloned (#106) 2018-07-24 12:59:55 -07:00
James Bornholt 4cc23908e0 Add option to output SMT to an output-port?. (#105) 2018-07-06 16:42:41 -07:00
Emina Torlak 89944d4be7 Update the documentation for libraries. 2018-07-06 15:37:12 -07:00
Emina Torlak 0cc0d573f8 Update the documentation for built-in datatypes. 2018-07-06 15:19:04 -07:00
Emina Torlak 9edff24cc8 Update the documentation for solver-aided queries and reasoning precision. 2018-07-06 14:46:13 -07:00
Emina Torlak 5ea16ded7a Update the Rosette Essentials documentation. 2018-07-06 13:29:55 -07:00
Emina Torlak 7c99196ed8 Remove unused code. 2018-07-05 15:22:25 -07:00
Emina Torlak 320e34dadd Drop infinite-precision soundness guarantees for verify, solve, and solve+.
These three queries no longer guarantee that their output will be sound with
respect to infinite-precision semantics when current-bitwidth is set to a
positive integer. The returned solutions are only guaranteed to be sound
with respect to the finite-precision semantics, as for the synthesize and
debug queries. Providing this guarantee was inefficient, and it is also no
longer necessary now that the client code is explicitly opting into the
finite precision semantics (with current-bitwidth being #f by default).
2018-07-05 15:00:53 -07:00
Emina Torlak 43072eba1f Change the default current-bitwidth to #f. 2018-07-05 11:28:44 -07:00
Emina Torlak 1106df4495 Update the solve query to take a single expression, like other queries. 2018-07-03 16:51:46 -07:00
James Bornholt 0b1a630dca Cache well-formedness check results for Boolector (#104) 2018-06-28 12:02:04 -07:00
Mangpo Phothilimthana 1a3ca1dc5c Add CPLEX support (#100) 2018-06-18 11:49:21 -07:00
James Bornholt 269baefc0c Add support for CVC4 and Boolector (#103)
Add support for CVC4 and Boolector.
2018-06-15 16:17:21 -07:00
Emina Torlak 1fbf45203c Use direct encoding for integer->bitvectors. 2018-06-15 10:48:34 -07:00
Emina Torlak 8ea6a156fc Update the SMT encoder to avoid generating spurious constant declarations. 2018-06-13 14:12:09 -07:00
James Bornholt f669184e99 Fix mismatched paren in synthesize docs. 2018-06-12 13:18:07 -07:00
Emina Torlak 8373332030 Update documentation for the synthesize form. 2018-05-24 09:40:00 -07:00
Emina Torlak f94b760957 Extend the synthesize form with a keyword-free variant. 2018-05-23 14:39:09 -07:00
Emina Torlak 259ee18c45
Merge pull request #99 from jamesbornholt/fix-roseunit
Fixes for Racket 7
2018-05-21 14:00:15 -07:00
James Bornholt b9bf322b3f Fix eval namespace in synthax 2018-05-21 11:08:56 -07:00
James Bornholt 1b92adb708 Fix submod binding in roseunit 2018-05-21 10:02:05 -07:00
Emina Torlak 969d28114a Extend the debug query to handle more return types.
The core can now include procedures that produce
values of solvable? types, lists of values of solvable? types,
and unions of these two kinds of values.
2018-05-17 15:31:26 -07:00
James Bornholt 4d75e7e12e Improve some error messages when Z3 is missing
As discussed in #94
2018-03-31 13:26:59 -07:00
Anthony Quizon 8fa04ccfc9 Idiot proofing example code (#93)
* Idiot proofing example code

* Fix the build issue with #lang inside racketblock, and break up block into definitions and interactions.
2018-03-08 15:51:58 -08:00
Emina Torlak 65efa27456 Add documentation for solve+. 2018-03-08 11:49:56 -08:00
Emina Torlak 567e0ef93d Adopt Z3 convention for displaying bitvectors (#85). 2018-02-27 10:21:36 -08:00
Emina Torlak 0777e70694 Update docs to note that cond does not support => clauses (#83). 2018-02-16 10:42:18 -08:00
Emina Torlak a5a00c7721 Update docs to note that cond does not support => clauses (#83). 2018-02-16 10:39:51 -08:00
James Bornholt 983d6c05b3
Test Racket 6.12 2018-02-15 08:21:23 -08:00
Emina Torlak 5f9f39ff81 Update docs for define-synthax to explain interaction with ?? and choose. 2018-02-14 17:11:50 -08:00
Emina Torlak 5beca43376 Add documentation for with-asserts-only (#82). 2018-02-13 16:55:05 -08:00
Emina Torlak 2d5f08e138 Add documentation for symbolics (#79). 2018-02-13 13:50:08 -08:00
Emina Torlak 8d7eb54005 Patch symbolics to remove duplicates when given a list of symbolic constants. 2018-02-13 13:38:37 -08:00
Emina Torlak 8710f76aa8
Merge pull request #76 from pmatos/patch-2
Fix URL to z3 optimization guide
2018-01-15 09:52:36 -08:00
Paulo Matos 5537863aee
Fix URL to z3 optimization guide 2018-01-15 10:53:56 +01:00
Emina Torlak a1b9cde19c Ensure determinstic order of state updates in rollback/encapsulate. 2018-01-04 14:33:40 -08:00
Emina Torlak 9a2a73ecc1 Simplify and test the implementation of ordered dictionaries. 2018-01-04 14:18:55 -08:00
Emina Torlak 5dd866a5b7 Suppress the z3 warning message on the Racket package server. 2017-12-20 14:05:58 -08:00
Emina Torlak cb4a1b1bde
Merge pull request #69 from LinkiTools/master
Use travis-racket to perform a matrix test.
2017-11-10 09:03:56 -08:00
James Bornholt ecef854aab Also test Racket 6.6 2017-11-10 09:02:00 -08:00
Paulo Matos 0d975331c5 Use travis-racket to perform a matrix test. 2017-11-10 12:26:10 +01:00
Emina Torlak 1ea0e89089 Merge pull request #68 from pmatos/patch-1
Trivial doc fix.
2017-10-24 12:32:06 -07:00
Paulo Matos cf967bab30 Trivial doc fix. 2017-10-24 20:48:00 +02:00
Emina Torlak 57c8e41eda Merge pull request #67 from lkuper/master
Fix typo in docs.
2017-10-20 17:06:08 -07:00
Emina Torlak 98c5c39dd7 Modify for/all to preserve old semantics in the basic case.
So, (for/all ([v val]) expr) is a no-op unless val is a union.
Add a (for/all ([v val #:exhaustive]) expr) form to
recursively decompose all guarded values: ite, ite*, and union.
2017-10-20 17:01:20 -07:00
Lindsey Kuper 21366cd568 Fix typo in docs. 2017-10-20 15:31:06 -07:00
Emina Torlak 111af61a85 Amend docs as suggested in #66. 2017-10-20 11:31:08 -07:00
Emina Torlak 4a96e53f0a Patch @make-struct-type to correctly account for auto-fields. 2017-10-16 15:32:36 -07:00
Emina Torlak 05f1ee98c5 Modify for/all to fully decompose ite and ite* into guard/value pairs. 2017-10-12 16:44:31 -07:00
Emina Torlak 920f488d84 Add a procedure for completing a solution wrt a list of constants. 2017-10-12 16:04:07 -07:00
Emina Torlak af8e006d1e Extend the for/all form to support concretization of non-union values. 2017-10-11 14:09:30 -07:00
Emina Torlak ca045a379a Merge pull request #65 from jamesbornholt/vector-set
Remove spurious return value from vector-set!
2017-10-05 16:08:16 -07:00
James Bornholt 2e4a83e3c8 Remove spurious return value from vector-set! 2017-10-05 15:54:25 -07:00
Emina Torlak d33b21b466 Merge pull request #64 from rohinmshah/flatten
Bug fix: Flatten
2017-08-17 08:53:01 -07:00
Rohin Shah 78c2285d20 Flatten bug fix and tests 2017-08-16 22:12:09 -07:00
Emina Torlak cf602a320e Fix eq? for mutable values that are also equal?. 2017-07-26 17:50:11 -07:00
Emina Torlak af93195249 Update to Racket 6.9. 2017-07-18 11:20:01 -07:00
Emina Torlak eaa42a2350 Allow keyword arguments in generic methods. 2017-06-28 16:20:30 -07:00
Emina Torlak 61264084b7 Update the docs to add the require clause for using Z3 (#63). 2017-06-26 11:37:34 -07:00
Emina Torlak af188ae33e Merge pull request #61 from rohinmshah/generics
Generics
2017-06-21 11:55:00 -07:00
Emina Torlak b251a27a37 An implementation of symbolics without append (a patch to #60). 2017-06-21 11:08:28 -07:00
Emina Torlak 347b8b8337 Merge pull request #62 from rohinmshah/error
Fix error message
2017-06-21 10:05:36 -07:00
Rohin Shah d561576c64 Allow define-generics to define methods where the required struct argument comes at any position 2017-06-21 18:44:32 +02:00
Rohin Shah 1e66d98fb5 Remove irrelevant 'receiver' argument in generics code 2017-06-21 18:44:32 +02:00
Rohin Shah 8522367a4b Fix error message 2017-06-21 14:36:08 +02:00
Emina Torlak dab1a83bac Patch validation of bitvector solutions under infinite semantics. 2017-06-09 15:26:02 -07:00
Emina Torlak 464b4164f9 Fix the implementation of solvable-default for UFs. 2017-05-11 12:08:10 -07:00
Emina Torlak 600425a55f Use mrlib/graph to find dot for FSM visualization. 2017-05-09 15:58:48 -07:00
Emina Torlak 9b811d601c Add code for incremental solving of verification queries. 2017-05-09 10:33:52 -07:00
Emina Torlak b23562e6ee Update Z3 model decoding to allow arbitrary SMTLib expressions in define-fun bodies.
Update the definiton of the fv datatype to store an opaque procedure.
Disallow finitization in the presence of uninterpreted functions.
Update the documentation to reflect these updates.
2017-03-23 15:59:04 -07:00
Emina Torlak 83a96c9dc7 Fix for the concurrency bug found by @mangpo. 2017-03-10 15:05:52 -08:00
Emina Torlak d0fd43b386 Refactor solve+ to use closures instead of generators. 2017-03-01 11:42:50 -08:00
Emina Torlak a012368f2a Add documentation for the solver push / pop interface. 2017-02-27 11:20:29 -08:00
Emina Torlak cd22fbb245 Update solve+ tests. 2017-02-25 06:44:00 -08:00
Emina Torlak 08ee554616 Revise solve+ to support retracting constraints. 2017-02-25 06:39:15 -08:00
Emina Torlak 789ce7c5ae Fix URL
Fix URL
2017-01-03 08:52:35 -08:00
sorawee f5e1e9e277 Fix URL 2017-01-03 04:10:08 -05:00
Emina Torlak 0c289dc820 Remove bad comment
Remove bad comment
2017-01-02 08:39:54 -08:00
sorawee 71d848b9e5 Remove bad comment
To comment in Scribble, it should be `@; ...` or `@;{ ... }`
2016-12-31 03:23:12 -05:00
Emina Torlak 3d538c1abd Add caching of equality comparisons. 2016-12-16 11:31:40 -08:00
Emina Torlak 38c366c2ef Support comparing concrete and symbolic cyclic structures for equality. 2016-12-15 13:14:12 -08:00
Emina Torlak c7671c0771 Patch symbolics to handle cyclic structures.
Patch symbolics to handle cyclic structures.
2016-11-22 15:16:22 -08:00
Rohin Shah e3ad7961c2 Patch symbolics to handle cyclic structures. 2016-11-22 14:40:33 -08:00
Emina Torlak f40479f3a5 Patch the union printer to handle cyclic structures. 2016-11-16 16:06:42 -08:00
Emina Torlak 9ef1d7a1d5 Patch encoding of real literals to Z3.
By default, Racket uses the e notation to print some reals.
Z3 does not recognize this.  The patch forces all literals
to print as if using ~r.
2016-10-07 15:41:48 -07:00
Emina Torlak 677e066239 Update docs to say that Racket 6.6 is required. 2016-10-04 08:55:30 -07:00
Emina Torlak 2ba3b2dfa8 Update docs to remove unquote-splicing from safe forms. 2016-09-20 18:29:56 -07:00
Emina Torlak e1faa20b74 Fix evaluate to return default value for empty ite* expressions. 2016-09-20 10:28:00 -07:00
Emina Torlak 1c4d114a9a Fix xor to match Racket semantics (#44). 2016-09-02 13:59:15 -07:00
Emina Torlak 119a99f516 Add object-name property to lifted operators. 2016-08-31 14:40:06 -07:00
Emina Torlak f5e10499de Revert 36d6465f52
revert 36d6465f52
2016-08-31 09:35:34 -07:00
Stephen Chang 3e444f8ca5 revert 36d6465f52
This reverts commit 36d6465f52, since it changes the behavior of the FSM demo.
2016-08-31 12:12:22 -04:00
Emina Torlak 6efd78127b query/debug.rkt: use the outer stx object instead of proc
query/debug.rkt: use the outer stx object instead of proc
2016-08-30 15:14:39 -07:00
Alex Knauth 36d6465f52 query/debug.rkt: use the outer stx object instead of proc 2016-08-30 17:59:11 -04:00
Emina Torlak b89ef6c75e Fix doc typo, ambiguous "expr"
doc typo, ambiguous "expr"
2016-08-26 12:39:06 -07:00
Stephen Chang 88e94a87bb doc typo, ambiguous "expr" 2016-08-26 15:32:14 -04:00
Emina Torlak 87c54a07a2 Patch test-sat and test-unsat in roseunit. 2016-08-24 13:27:13 -07:00
Emina Torlak 3b7f8da010 fix printing of symbolic values within lists #37
fix printing of symbolic values within lists
2016-08-22 09:19:44 -07:00
Alex Knauth 2d45a493d1 mark symbolic terms as not quotable 2016-08-22 12:03:20 -04:00
Emina Torlak d471cf52c1 Fix handling of >2 arguments in lifted andmap, ormap, filter-map. 2016-08-18 11:00:44 -07:00
Emina Torlak ddf97337b8 Fix some doc links
Fix some doc links
2016-08-02 09:51:56 -07:00
Stephen Chang 49f2cbb667 Fix some doc links 2016-08-02 11:00:38 -04:00
Emina Torlak 5df1125158 Patch for #33 (Z3 produces different output for > 32 arguments to distinct?). 2016-07-30 17:24:13 -07:00
Emina Torlak 237321e6e9 Fix typo in the patch. 2016-07-30 14:22:33 -07:00
Emina Torlak d53c4496ef Fix typo in rosette-lib-test.rkt 2016-07-30 14:17:15 -07:00
Emina Torlak 76712ec70c Fix unbound identifier error in define-synthax example
Fix unbound identifier error in define-synthax example
2016-07-30 14:16:43 -07:00
Christopher Su 34b8f06ed1 Fix unbound identifier error in define-synthax example. 2016-07-30 12:22:23 -07:00
Emina Torlak b097f64693 Added support and tests for the various options in define-generics
Added support and tests for the various options in define-generics
2016-07-29 16:31:18 -07:00
Rohin Shah a5b42b1b7c When #:defined-predicate is not supplied, don't attempt to lift support 2016-07-29 16:06:53 -07:00
Rohin Shah 24f7ffbd36 Removed one more debugging line 2016-07-29 14:50:00 -07:00
Rohin Shah b3755b7fba Removed debugging code 2016-07-29 14:46:45 -07:00
Rohin Shah e595a3c01e Added support and tests for all of the keyword options for define-generics except #:defined-predicate 2016-07-29 14:45:05 -07:00
Rohin Shah 2b4601d1d5 Added support and tests for all of the keyword options for define-generics except #:defined-predicate 2016-07-29 14:37:26 -07:00
392 changed files with 28273 additions and 6989 deletions

6
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

50
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: Docker
on:
push:
tags:
- '*'
branches:
- master
pull_request:
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Clone Repository
uses: actions/checkout@v4
- name: Configure Docker Metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Authenticate to Package Registry
uses: docker/login-action@v3
if: ${{ github.event_name != 'pull_request' }}
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and Publish Rosette Image
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

102
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,102 @@
name: Tests
on: [push, pull_request]
env:
CVC4_URL: "http://cvc4.cs.stanford.edu/downloads/builds/x86_64-linux-opt/cvc4-1.8-x86_64-linux-opt"
BOOLECTOR_URL: "https://github.com/Boolector/boolector/archive/3.2.1.tar.gz"
CVC5_URL: "https://github.com/cvc5/cvc5/releases/download/cvc5-1.0.7/cvc5-Linux"
BITWUZLA_URL: "https://github.com/bitwuzla/bitwuzla/archive/93a3d930f622b4cef0063215e63b7c3bd10bd663.tar.gz"
STP_URL: "https://github.com/stp/stp/archive/d70085462f07c8a5a2f1225f727cda3ef505b141.tar.gz"
YICES2_URL: "https://github.com/SRI-CSL/yices2/archive/e27cf308cffb0ecc6cc7165c10e81ca65bc303b3.tar.gz"
jobs:
test:
strategy:
matrix:
racket-version: ['8.1', 'current']
racket-variant: ['CS']
allow-failure: [false]
name: Racket ${{ matrix.racket-version }} (${{ matrix.racket-variant }})
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.allow-failure }}
steps:
- uses: actions/checkout@master
- name: Setup Racket
uses: Bogdanp/setup-racket@v1.14
with:
architecture: x64
version: ${{ matrix.racket-version }}
variant: ${{ matrix.racket-variant }}
- name: Install solvers
# Note that setting LD_LIBRARY_PATH can be removed once this bug is
# fixed: https://github.com/stp/stp/issues/485
run: |
mkdir bin &&
wget $CVC4_URL -nv -O bin/cvc4 &&
chmod +x bin/cvc4 &&
wget $BOOLECTOR_URL -nv -O boolector.tar.gz &&
mkdir boolector &&
tar xzf boolector.tar.gz -C boolector --strip-components=1 &&
pushd boolector &&
./contrib/setup-cadical.sh &&
./contrib/setup-btor2tools.sh &&
./configure.sh &&
cd build &&
make &&
popd &&
cp boolector/build/bin/boolector bin/ &&
rm -rf boolector* &&
wget $CVC5_URL -nv -O bin/cvc5 &&
chmod +x bin/cvc5 &&
sudo apt-get update &&
sudo apt-get install -y ninja-build &&
pip3 install meson &&
wget $BITWUZLA_URL -nv -O bitwuzla.tar.gz &&
mkdir bitwuzla &&
tar xzf bitwuzla.tar.gz -C bitwuzla --strip-components=1 &&
pushd bitwuzla &&
./configure.py &&
pushd build &&
ninja &&
popd &&
popd &&
cp bitwuzla/build/src/main/bitwuzla bin/ &&
sudo apt-get install -y build-essential git cmake bison flex libboost-all-dev libtinfo-dev python3 perl &&
wget $STP_URL -nv -O stp.tar.gz &&
mkdir stp &&
tar xzf stp.tar.gz -C stp --strip-components=1 &&
pushd stp &&
echo "LD_LIBRARY_PATH=$PWD/deps/cadical/build:$PWD/deps/cadiback/:$LD_LIBRARY_PATH" >> $GITHUB_ENV &&
./scripts/deps/setup-gtest.sh &&
./scripts/deps/setup-outputcheck.sh &&
./scripts/deps/setup-cms.sh &&
./scripts/deps/setup-minisat.sh &&
mkdir build &&
pushd build &&
cmake .. &&
cmake --build . &&
popd &&
popd &&
ln -s stp/build/stp bin/stp &&
sudo apt-get install -y gperf &&
wget $YICES2_URL -nv -O yices2.tar.gz &&
mkdir yices2 &&
tar xvf yices2.tar.gz -C yices2 --strip-components=1 &&
pushd yices2 &&
autoconf &&
./configure --prefix=$PWD/out/ &&
make &&
make install &&
popd &&
cp yices2/out/bin/yices-smt2 bin/yices-smt2
- name: Install Rosette
run: raco pkg install --auto --name rosette
- name: Compile Rosette tests
run: raco make test/all-rosette-tests.rkt
- name: Run Rosette tests
run: raco test test/all-rosette-tests.rkt
- name: Compile SDSL tests
run: raco make test/all-sdsl-tests.rkt
- name: Run SDSL tests
run: raco test test/all-sdsl-tests.rkt

9
.gitignore vendored
View File

@ -8,11 +8,12 @@
ehthumbs.db
Thumbs.db
**/doc/*.css
**/doc/*.html
**/doc/*.js
**/rosette-guide/**
**/doc
**/doc/**
**/bin/**
**/compiled
**/compiled/**
*~
node_modules
.cache
yarn.lock

View File

@ -1,29 +0,0 @@
language: java
sudo: false
env:
global:
- RACKET_DIR=~/racket
- RACKET_URL="https://mirror.racket-lang.org/installers/6.6/racket-6.6-x86_64-linux.sh"
before_install:
- if [[ ! -e "$RACKET_DIR/bin/racket" ]]; then
rm -rf $RACKET_DIR;
curl -L -o racket.sh $RACKET_URL;
sh ./racket.sh --in-place --dest $RACKET_DIR;
else echo "using racket from cache"; fi
- export PATH="${RACKET_DIR}/bin:${PATH}"
install:
- raco pkg install
script:
- raco test
- time raco make test/all-rosette-tests.rkt
- raco test test/all-rosette-tests.rkt
- time raco make test/all-sdsl-tests.rkt
- raco test test/all-sdsl-tests.rkt
cache:
directories:
- $RACKET_DIR

97
Dockerfile Normal file
View File

@ -0,0 +1,97 @@
FROM alpine:3.15
## ========================== [ Install Racket ] =========================== ##
## Define default Racket version and variant. The Racket version is of the form
## <major>.<minor>. The variant can be "cs" (Chez Scheme), "bc" (Before Chez) or
## "natipkg" (where external libraries are included in the Racket packages).
##
ARG RACKET_VERSION=8.4
ARG RACKET_VARIANT=cs
## Install Racket. We first install system dependencies: [gcompat] is needed for
## Racket and [ncurses] is needed for the [xrepl] and [expeditor] packages,
## providing the REPL. We then download the installer, run it with the right
## parameters, then remove it. After that, all that remains is to set-up the
## Racket packages and install [expeditor]. See later for a description of the
## arguments to [raco pkg install].
##
RUN apk add --no-cache gcompat ncurses
RUN wget "https://download.racket-lang.org/installers/${RACKET_VERSION}/racket-minimal-${RACKET_VERSION}-x86_64-linux-${RACKET_VARIANT}.sh"
RUN echo 'yes\n1\n' | sh racket-minimal-${RACKET_VERSION}-x86_64-linux-${RACKET_VARIANT}.sh --create-dir --unix-style --dest /usr/
RUN rm racket-minimal-${RACKET_VERSION}-x86_64-linux-${RACKET_VARIANT}.sh
RUN raco setup --no-docs
RUN raco pkg install -i --batch --auto --no-docs expeditor-lib
## =================== [ Install Rosette's Dependencies ] =================== ##
## Work on Rosette's installation within /usr/local. This directory will be
## cleaned up later on so it could be anything.
##
WORKDIR /usr/local/rosette
## Get all the info.rkt files. Trying to install Rosette based only on these
## files would fail, but we can use them to only install dependencies.
##
COPY info.rkt .
COPY rosette/info.rkt rosette/
## Install only Rosette's dependencies. We have to install the external
## dependencies [libstdc++] and [libgcc] because Z3 needs them at runtime. As
## for the Racket dependencies only, we achieve that in three steps:
##
## 1. We use [raco pkg install --no-setup] to download and register Rosette
## and all its dependencies without setting them up, that is without
## compiling them. At this point, the system is in an inconsistent state,
## where packages are registered but not actually present. The other flags
## are the following:
##
## -i install packages for all users
## --batch disable interactive mode and suppress prompts
## --auto download missing packages automatically
##
## 2. We use [raco pkg remove --no-setup] to unregister Rosette. This keeps
## the dependencies as registered. The system is still in an inconsistent
## state. See above for the flags.
##
## 3. We use [raco setup] to set up all the registered package. This brings
## the system back in a consistent state. Since Rosette's dependencies were
## registered but not Rosette itself, this achieves our goal. The flags are
## the following:
##
## --fail-fast fail on the first error encountered
## --no-docs do not compile the documentations
##
RUN apk add --no-cache libstdc++ libgcc
RUN raco pkg install -i --batch --auto --no-setup ../rosette
RUN raco pkg remove -i --no-setup rosette
RUN raco setup --fail-fast --no-docs
## ========================== [ Install Rosette ] =========================== ##
## Get all of Rosette; build and install it. The dependencies should all be
## installed, so we can remove the --auto flag which will lead us to failure if
## a dependency cannot be found. The additional flags are the following:
##
## --copy copy content to install path (instead of linking)
##
COPY . .
RUN raco pkg install -i --batch --copy --no-docs ./rosette
RUN rm -R /usr/local/rosette
## ===================== [ Prepare Clean Entry Point ] ====================== ##
## For further use of the image, we can start with user `rosette`, group
## `rosette` in `/rosette` by default.
##
RUN addgroup rosette
RUN adduser --system --shell /bin/false --disabled-password \
--home /rosette --ingroup rosette rosette
RUN chown -R rosette:rosette /rosette
USER rosette
WORKDIR /rosette
## Rosette files are simply Racket files using the Rosette library: the default
## entry point of this image is therefore the Racket executable.
##
ENTRYPOINT ["/usr/bin/racket", "-I", "rosette"]

View File

@ -1,5 +1,72 @@
# Release Notes
## Version 4.1
This is a minor bug-fixing release.
## Version 4.0
This is a major release with significant changes to the language and the runtime. Rosette 4.0 is *not backward compatible* with Rosette 3.x. But porting Rosette 3.x code to Rosette 4.0 should be straightforward for most applications.
This release includes the following features:
- Support for assumptions (see `assume`).
- New symbolic evaluation core that tracks verification conditions (VCs) rather than path conditions and assertions.
- New symbolic reflection constructs for working with VCs, including `vc`, `with-vc`, and `clear-vc!`.
- New symbolic reflection facilities for managing symbolic `terms`, including the option of using a garbage-collected data structure.
- Updated `verify`, `synthesize`, `solve`, and `optimize` queries.
- New synthesis library with efficient support for grammar holes (see `define-grammar`).
- New list and vector operators that use bitvectors instead of integers.
- Updates to The Rosette Guide to document the new language in detail.
The following features have been removed:
- The `debug` query.
- Reflection facilities for working with path conditions and assertions: `pc`, `with-asserts`, `with-asserts-only`, `clear-asserts!`, and `asserts`.
- Support for CPLEX.
## Version 3.2
This release includes minor updates and a new [value destructuring library].
[value destructuring library]: https://docs.racket-lang.org/rosette-guide/sec_utility-libs.html#%28part._.Value_.Destructuring_.Library%29
## Version 3.1
This release includes bug fixes and updates Rosette to use the latest version of Z3 as its default SMT solver.
This release also includes the following new functionality contributed by [Sorawee Porncharoenwase][]:
- An interactive [value browser][] to help programmers navigate and read complex symbolic values.
- An *error tracer* for finding bugs in Rosette programs that manifest as exceptions intercepted during symbolic evaluation. To use the error tracer, run the command `raco symtrace <prog>`. The [debugging][] chapter in the Rosette guide describes some common issues due to intercepted exceptions, how to test for them, and how to find them with the error tracer.
[Sorawee Porncharoenwase]: https://github.com/sorawee
[debugging]: https://docs.racket-lang.org/rosette-guide/ch_error-tracing.html
[value browser]: https://docs.racket-lang.org/rosette-guide/sec_utility-libs.html#%28part._.Value_.Browser_.Library%29
## Version 3.0
This is a major release with significant changes to the language and the runtime. Rosette 3.0 is *not backward compatible* with Rosette 2.x. But porting Rosette 2.x code to Rosette 3.0 should be straightforward for most applications.
The semantics of Rosette 3.0 differs from Rosette 2.x in two ways:
- The `current-bitwidth` parameter that controls the reasoning precision is set to `#f` by default. As a result, symbolic constants that are declared to be integers or reals are interpreted in the theories of integers and reals, respectively. This means that the semantics of assertions over these types follows that of Racket. But reasoning about such assertions is expensive (or undecidable), so Rosette 3.0 still provides the option of approximating integer and real constants with finite-precision bitvectors. The key difference is that programs must now *explicitly opt into* this approximation by setting `current-bitwidth` to a positive integer.
- If `current-bitwidth` is set to a positive integer _k_, the solutions produced by the `verify`, `synthesize`, `solve`, and `debug` queries are guaranteed to be correct under the _k_-bit semantics for integer and real constants. They are _not_ guaranteed to be sound with respect to the infinite-precision semantics.
This release also includes the following new functionality and features contributed by [James Bornholt][] and [Phitchaya Mangpo Phothilimthana][]:
- Developed a new *symbolic profiler* for diagnosing performance issues in Rosette programs. The symbolic profiler instruments Rosette and tracks key performance metrics to identify potential issues. To use the symbolic profiler, run the command `raco symprofile program.rkt`. A new [performance][] chapter in the Rosette guide details common performance issues and how to use the symbolic profiler to identify them.
- Extended and generalized the interface to constraint solvers. The new interface allows the client code to specify a path to the solver, set the logic, provide solver-specific configuration options, and export the problem encodings sent to the solver.
- Added support for four new solvers: [Boolector][], [CVC4][], [Yices][], and [CPLEX][]. These solvers are not included in the default distribution and need to be installed separately for use with Rosette.
[performance]: https://docs.racket-lang.org/rosette-guide/ch_performance.html
[Boolector]: https://docs.racket-lang.org/rosette-guide/sec_solvers-and-solutions.html#%28def._%28%28lib._rosette%2Fsolver%2Fsmt%2Fboolector..rkt%29._boolector%29%29
[CVC4]: https://docs.racket-lang.org/rosette-guide/sec_solvers-and-solutions.html#%28def._%28%28lib._rosette%2Fsolver%2Fsmt%2Fcvc4..rkt%29._cvc4%29%29
[Yices]: https://docs.racket-lang.org/rosette-guide/sec_solvers-and-solutions.html#%28def._%28%28lib._rosette%2Fsolver%2Fsmt%2Fyices..rkt%29._yices%29%29
[CPLEX]: https://docs.racket-lang.org/rosette-guide/sec_solvers-and-solutions.html#%28def._%28%28lib._rosette%2Fsolver%2Fmip%2Fcplex..rkt%29._cplex%29%29
[Phitchaya Mangpo Phothilimthana]: https://github.com/mangpo
## Version 2.2
This release includes bug fixes and the following updates:
@ -54,7 +121,7 @@ This release includes the following features:
- Improved implementation for the `define-synthax` form and other
high-level synthesis constructs.
- Improved printing of symbolic values by [James Bornholt](https://homes.cs.washington.edu/~bornholt/).
- Improved printing of symbolic values by [James Bornholt][].
- Ported sample SDSLs to Rosette 2.0.
@ -68,6 +135,8 @@ The following features have been removed:
- Support for internal logging via `current-log-handler`.
[James Bornholt]: https://github.com/jamesbornholt
## Version 1.1
- This release includes a new reader for `rosette` and `rosette/safe`

View File

@ -1,7 +1,7 @@
The Rosette Language
====================
[![Build Status](https://travis-ci.org/emina/rosette.svg?branch=master)](https://travis-ci.org/emina/rosette)
[![Tests](https://github.com/emina/rosette/workflows/Tests/badge.svg)](https://github.com/emina/rosette/actions?query=workflow%3ATests)
[Rosette](http://emina.github.io/rosette/) is a solver-aided programming language that extends [Racket](http://racket-lang.org) with language constructs for program synthesis, verification, and more. This repository includes the source code for Rosette, as well as several example solver-aided DSLs.
@ -9,7 +9,7 @@ The Rosette Language
The easiest way to install Rosette is from Racket's package manager:
* Download and install Racket 6.6 from http://racket-lang.org
* Download and install Racket 8.1 or later from http://racket-lang.org
* Use Racket's `raco` tool to install Rosette:
@ -19,7 +19,7 @@ The easiest way to install Rosette is from Racket's package manager:
Alternatively, you can install Rosette from source:
* Download and install Racket 6.6 from http://racket-lang.org
* Download and install Racket 8.1 or later from http://racket-lang.org
* Clone the rosette repository:
@ -63,7 +63,7 @@ Alternatively, you can install Rosette from source:
* The `rosette` language includes all of Racket. This places the burden
on the programmer to decide whether a given Racket construct (which
is not overriden by Rosette) is safe to use in a given context.
is not overridden by Rosette) is safe to use in a given context.
Rosette provides no guarantees or checks for programs that use
unsafe constructs. In the best case, such a program will fail with
an exception if a symbolic value flows to a construct that does not
@ -73,7 +73,7 @@ Alternatively, you can install Rosette from source:
* For more on using Rosette, see [_The Rosette Guide_][1]. Rosette's internals are described in [this PLDI'14 paper][2].
[1]: http://emina.github.io/rosette/doc/rosette-guide/index.html
[1]: https://docs.racket-lang.org/rosette-guide/index.html
[2]: http://dl.acm.org/citation.cfm?id=2594340

View File

@ -2,16 +2,30 @@
(define collection 'multi)
(define deps '("r6rs-lib"
(define deps '("custom-load"
"sandbox-lib"
"scribble-lib"
("racket" #:version "8.1")
"r6rs-lib"
"rfc6455"
"net-lib"
"web-server-lib"
"rackunit-lib"
"slideshow-lib"
"gui-lib"
"base"))
(define build-deps '("pict-doc"
(define build-deps '("rackunit-doc"
"draw-lib"
"errortrace-lib"
"pict-lib"
"pict-doc"
"scribble-lib"
"racket-doc"))
"racket-doc"
"gui-doc"
"errortrace-doc"))
(define test-omit-paths (if (getenv "PLT_PKG_BUILD_SERVICE") 'all '()))
(define pkg-desc "Rosette solver-aided host language")
(define version "2.2")
(define version "4.0")

View File

@ -2,7 +2,7 @@
(require (for-syntax racket/syntax "../core/lift.rkt") racket/provide
"../core/safe.rkt" "generic.rkt"
(only-in "../core/effects.rkt" apply!)
(only-in "../core/store.rkt" store!)
(only-in "../core/type.rkt" define-lifted-type type-cast)
(only-in "../core/equality.rkt" @eq? @equal?)
(only-in "../core/bool.rkt" instance-of? && ||)
@ -40,12 +40,15 @@
[(box v) v]
[(union vs) (apply merge* (for/list ([gv vs]) (cons (car gv) (unbox (cdr gv)))))]))
(define (box-ref x idx) (unbox x)) ; For the purpose of tracking mutations to the store,
(define (box-set! x idx v) (set-box! x v)) ; boxes are treated as 1-element vectors that ignore the index argument.
(define (@set-box! b v)
(match (type-cast @box? b 'set-box!)
[(? box? x)
(apply! set-box! unbox x v)]
(store! x 0 v box-ref box-set!)]
[(union vs)
(for ([gv vs])
(let ([x (cdr gv)])
(apply! set-box! unbox x (merge (car gv) v (unbox x)))))]))
(store! x 0 (merge (car gv) v (unbox x)) box-ref box-set!)))]))

161
rosette/base/adt/bvseq.rkt Normal file
View File

@ -0,0 +1,161 @@
#lang racket
(require
(only-in "list.rkt" @list?)
(only-in "vector.rkt" @vector? @vector-set!)
(only-in "../core/lift.rkt" lift-id)
(only-in "../core/forall.rkt" for/all for*/all)
(only-in "../core/term.rkt" get-type type-cast term?)
"../core/union.rkt"
"../core/bitvector.rkt"
"../core/merge.rkt"
"../core/safe.rkt")
(provide @list-ref-bv @list-set-bv
@take-bv @take-right-bv
@drop-bv @drop-right-bv @list-tail-bv
@split-at-bv @split-at-right-bv
@length-bv
@vector-ref-bv @vector-set!-bv @vector-length-bv)
(define (bv-lit-or-term? v)
(or (bv? v) (and (term? v) (bitvector? (get-type v)))))
(define-syntax-rule (lift-body #:with (id xs idx seq-length) #:type t #:max n #:body body ...)
(let* ([t (get-type idx)]
[2^k (expt 2 (bitvector-size t))]
[sz (seq-length xs)]
[n (min sz 2^k)])
(when (>= (- 2^k 1) sz)
(assert (@bvult idx (@integer->bitvector sz t))
(index-too-large-error 'id xs idx)))
body ...))
(define-syntax (define-lift-bv stx)
(syntax-case stx ()
[(_ (proc-bv xs idx arg ...) @seq? seq?)
#`(define-lift-bv #,(lift-id #'proc-bv) (proc-bv xs idx arg ...) @seq? seq?)]
[(_ @proc-bv (proc-bv xs idx arg ...) @seq? seq?)
#'(define (@proc-bv xs idx arg ...)
(if (and (seq? xs) (bv-lit-or-term? idx))
(proc-bv xs idx arg ...)
(match* ((type-cast @seq? xs 'proc-bv)
(bvcoerce idx 'proc-bv))
[((? seq? xs) (? bv-lit-or-term? idx))
(proc-bv xs idx arg ...)]
[(xs idx)
(for*/all ([xs xs][idx idx])
(proc-bv xs idx arg ...))])))]))
(define-syntax (define-length-bv stx)
(syntax-case stx ()
[(_ length-bv @seq? seq? seq-length)
#`(begin
(define (length-bv xs t) ; (-> seq bitvector? @bv?)
(@integer->bitvector (seq-length xs) t))
(define (#,(lift-id #'length-bv) xs t)
(match (type-cast @seq? xs 'length-bv)
[(? seq? xs) (length-bv xs t)]
[xs (for/all ([xs xs]) (length-bv xs t))])))]))
(define-syntax-rule (define-ref-bv ref-bv @seq? seq? seq-ref seq-length)
(begin
(define (ref-bv xs idx) ; (-> type? bv-lit-or-term? any/c)
(if (bv? idx)
(seq-ref xs (@bitvector->natural idx))
(lift-body
#:with (ref-bv xs idx seq-length)
#:type t
#:max n
#:body
(apply
merge*
(for/list ([x xs] [i n])
(cons (@bveq (bv i t) idx) x))))))
(define-lift-bv (ref-bv xs idx) @seq? seq?)))
; ---- list bv procedures ---- ;
(define-length-bv length-bv @list? list? length)
(define-ref-bv list-ref-bv @list? list? list-ref length)
(define (list-set-bv xs idx v)
(if (bv? idx)
(list-set xs (@bitvector->natural idx) v)
(lift-body
#:with (list-set-bv xs idx length)
#:type t
#:max n
#:body (for/list ([(x i) (in-indexed xs)])
(if (< i n)
(merge (@bveq (bv i t) idx) v x)
x)))))
(define-lift-bv (list-set-bv xs idx v) @list? list?)
(define (pair-length ps bound)
(if (list? ps)
(min (length ps) bound)
(let loop ([ps ps] [acc 0])
(if (and (pair? ps) (< acc bound))
(loop (cdr ps) (add1 acc))
acc))))
(define-syntax (define-get-bv stx)
(syntax-case stx ()
[(_ get-bv seq-get)
#`(begin
(define (get-bv xs idx)
(if (bv? idx)
(seq-get xs (@bitvector->natural idx))
(let* ([t (get-type idx)]
[2^k (expt 2 (bitvector-size t))]
[sz (pair-length xs (sub1 2^k))])
(when (> (- 2^k 1) sz)
(assert (@bvule idx (@integer->bitvector sz t))
(index-too-large-error 'id xs idx)))
(apply
merge*
(for/list ([i (add1 sz)])
(cons (@bveq (bv i t) idx)
(seq-get xs i)))))))
(define (#,(lift-id #'get-bv) xs idx)
(if (and (not (union? xs)) (bv-lit-or-term? idx))
(get-bv xs idx)
(match* (xs (bvcoerce idx 'get-bv))
[((not (? union? xs)) (? bv-lit-or-term? idx))
(get-bv xs idx)]
[(xs idx)
(for*/all ([xs xs][idx idx])
(get-bv xs idx))]))))]))
(define-get-bv take-bv take)
(define-get-bv take-right-bv take-right)
(define-get-bv drop-bv drop)
(define-get-bv drop-right-bv drop-right)
(define-get-bv list-tail-bv list-tail)
(define (@split-at-bv xs idx)
(values (@take-bv xs idx) (@drop-bv xs idx)))
(define (@split-at-right-bv xs idx)
(values (@drop-right-bv xs idx) (@take-right-bv xs idx)))
; ---- vector bv procedures ---- ;
(define (vector-set!-bv xs idx v)
(if (bv? idx)
(@vector-set! xs (@bitvector->natural idx) v)
(lift-body
#:with (vector-set!-bv xs idx vector-length)
#:type t
#:max n
#:body
(for ([x xs] [i n])
(@vector-set! xs i (merge (@bveq (bv i t) idx) v x))))))
(define-length-bv vector-length-bv @vector? vector? vector-length)
(define-ref-bv vector-ref-bv @vector? vector? vector-ref vector-length)
(define-lift-bv (vector-set!-bv xs idx v) @vector? vector?)

View File

@ -99,7 +99,7 @@
;; List Iteration
(define (bad-lengths-error name . args)
(thunk (error name "all lists must have same size\n given: ~a" (map ~.a args))))
(argument-error name "lists of equal length" (map ~.a args)))
(define (lengths xs)
(match xs
@ -168,7 +168,9 @@
(iterator-next l1 (f (car l1) (car l2)) (loop (cdr l1) (cdr l2)))))]
[(f l . args)
(assert-arity-includes f (add1 (length args)) (quote id))
(assert (andmap (curry = (length l)) args) (apply bad-lengths-error (quote id) l args))
(let ([len (length l)])
(assert (for/and ([arg args]) (= len (length arg)))
(apply bad-lengths-error (quote id) l args)))
(if (null? l)
(iterator-next)
(let loop ([l l] [args args])
@ -280,20 +282,6 @@
(@+ rank (@if (ranked>? (key-of x) i (key-of y) j) 1 0))))])
(for/list ([i len])
(for/fold ([v 0]) ([x xs] [r ranks]) (merge (@= i r) x v))))]))
#|(define vars (for/list ([i (in-range len)]) (define-symbolic* rank @integer?) rank))
(for ([v vars])
(assert (@<= 0 v))
(assert (@< v len)))
(let loop ([vars vars] [xs l])
(match* (vars xs)
[((or (list) (list _)) _) (void)]
[((list v v-rest ...) (list x x-rest ...))
(let ([key (key-of x)])
(for ([v1 v-rest] [x1 x-rest])
(assert (@if (less? key (key-of x1)) (@< v v1) (@< v1 v)))))
(loop v-rest x-rest)]))
(for/list ([i (in-range (length l))])
(apply merge* (for/list ([x l] [v vars]) (cons (@= v i) x))))]))|#
(define (fast-sort less? getkey cache-keys? xs)
(sort xs less? #:key getkey #:cache-keys? cache-keys?))
(define/lift/applicator fast-sort less? getkey cache-keys? xs)
@ -322,7 +310,7 @@
[else (let ([a (car l)]) (@if (f a) a (loop (cdr l))))])))]
(define/lift/applicator memf f list)
(define/lift/applicator findf f list)
(define (@member x xs) (@memf (curry @equal? x) xs))
(define (@member x xs [is-equal? @equal?]) (@memf (curry is-equal? x) xs))
(define (@memq x xs) (@memf (curry @eq? x) xs))
(define @assoc (case-lambda [(x xs) (@findf (compose (curry @equal? x) @car) xs)]
[(x xs eq?) (assert-arity-includes eq? 2 'assoc)
@ -421,7 +409,8 @@
(define @cons? @pair?)
(define @flatten
(match-lambda [(union vs) (merge** vs flatten)]
(match-lambda [(union vs) (merge** vs @flatten)]
[(cons x y) (@append (@flatten x) (@flatten y))]
[other (flatten other)]))
(define @append*
@ -440,8 +429,8 @@
(define @apply
(case-lambda [() (error 'apply "arity mismatch;\n expected: at least 2\n given: 0")]
[(proc) (error 'apply "arity mismatch;\n expected: at least 2\n given: 1")]
(case-lambda [() (assert #f (argument-error 'apply "at least 2 arguments" 0))]
[(proc) (assert #f (argument-error 'apply "at least 2 arguments" 1))]
[(proc xs) (lift/apply/higher-order apply proc xs : list? -> @list?)]
[(proc x0 xs) (lift/apply/higher-order apply proc x0 xs : list? -> @list?)]
[(proc x0 x1 xs) (lift/apply/higher-order apply proc x0 x1 xs : list? -> @list?)]
@ -521,29 +510,26 @@
(merge** ys (insert* _ i v))]))))
(splicing-local
[(define (replace xs i v)
(let-values ([(left right) (split-at xs i)])
(append left (cons v (cdr right)))))
(define (replace* xs i v)
(apply merge* (for/list ([(x idx) (in-indexed xs)])
(cons (@= i idx) (replace xs idx v)))))]
(define (@replace xs i v)
(or (and (list? xs) (number? i) (replace xs i v))
(match* ((type-cast @list? xs 'replace) (type-cast @integer? i 'replace))
[((? list? xs) (? number? i)) (replace xs i v)]
[(define ($list-set xs i v)
(for/list ([(x idx) (in-indexed xs)])
(merge (@= i idx) v x)))]
(define (@list-set xs i v)
(or (and (list? xs) (number? i) (list-set xs i v))
(match* ((type-cast @list? xs 'list-set) (type-cast @integer? i 'list-set))
[((? list? xs) (? number? i)) (list-set xs i v)]
[((? list? xs) i)
(assert-bound [0 @<= i @< (length xs)] 'replace)
(replace* xs i v)]
(assert-bound [0 @<= i @< (length xs)] 'list-set)
($list-set xs i v)]
[((union ys) (? number? i))
(assert-bound [0 <= i] 'replace)
(assert-bound [0 <= i] 'list-set)
(apply merge* (assert-some
(for/list ([y ys] #:when (< i (length (cdr y))))
(cons (car y) (replace (cdr y) i v)))
(cons (car y) (list-set (cdr y) i v)))
#:unless (length ys)
(index-too-large-error 'replace xs i)))]
(index-too-large-error 'list-set xs i)))]
[((union ys) i)
(assert-bound [0 @<= i @< (@length xs)] 'replace)
(merge** ys (replace* _ i v))]))))
(assert-bound [0 @<= i @< (@length xs)] 'list-set)
(merge** ys ($list-set _ i v))]))))
#|

View File

@ -6,7 +6,7 @@
"../core/safe.rkt" "../core/lift.rkt" "seq.rkt" "../core/forall.rkt" "generic.rkt"
(only-in "list.rkt" @list?)
(only-in "../form/control.rkt" @when)
(only-in "../core/effects.rkt" apply!)
(only-in "../core/store.rkt" store!)
(only-in "../core/term.rkt" define-lifted-type @any/c type-cast)
(only-in "../core/equality.rkt" @eq? @equal?)
(only-in "../core/bool.rkt" instance-of? && ||)
@ -58,16 +58,15 @@
(define (merge-set! vec idx val guard)
(for ([i (in-range (vector-length vec))])
(apply! vector-set! vector-ref
vec i (merge (&& guard (@= i idx)) val (vector-ref vec i)))))
(store! vec i (merge (&& guard (@= i idx)) val (vector-ref vec i)) vector-ref vector-set!)))
(define (@vector-set! vec idx val)
;(printf "vector-set! ~a ~a ~a\n" (eq-hash-code vec) idx val)
(if (and (vector? vec) (number? idx))
(apply! vector-set! vector-ref vec idx val)
(store! vec idx val vector-ref vector-set!)
(match* ((type-cast @vector? vec 'vector-set!) (type-cast @integer? idx 'vector-set!))
[((? vector? vs) (? number? idx))
(apply! vector-set! vector-ref vs idx val)]
(store! vs idx val vector-ref vector-set!)]
[((? vector? vs) idx)
(assert-bound [0 @<= idx @< (vector-length vs)] 'vector-set!)
(merge-set! vs idx val #t)]
@ -76,28 +75,26 @@
(assert-|| (for/list ([v vs] #:when (< idx (vector-length (cdr v))))
(let ([guard (car v)]
[vec (cdr v)])
(apply! vector-set! vector-ref
vec idx (merge guard val (vector-ref vec idx)))
(store! vec idx (merge guard val (vector-ref vec idx)) vector-ref vector-set!)
guard))
#:unless (length vs)
(index-too-large-error 'vector-set! vec idx))]
[((union vs) idx)
(assert-bound [0 @<= idx @< (merge** vs vector-length)] 'vector-set!)
(for/list ([v vs])
(and (merge-set! (cdr v) idx val (car v)) (car v)))])))
(for ([v vs])
(merge-set! (cdr v) idx val (car v)))])))
(define (@vector-fill! vec val)
(match (type-cast @vector? vec 'vector-fill!)
[(? vector? vs)
(for ([i (in-range (vector-length vs))])
(apply! vector-set! vector-ref vs i val))]
(store! vs i val vector-ref vector-set!))]
[(union vs)
(for ([v vs])
(let ([guard (car v)]
[vec (cdr v)])
(for ([i (in-range (vector-length vec))])
(apply! vector-set! vector-ref
vec i (merge guard val (vector-ref vec i))))))]))
(store! vec i (merge guard val (vector-ref vec i)) vector-ref vector-set!))))]))
; Vector copy helper procedure. Requires dest and src to be
; vectors (rather than unions of vectors), and dest-start, src-start

View File

@ -4,20 +4,21 @@
(require
(for-syntax racket/syntax (only-in "core/lift.rkt" drop@))
racket/provide
"core/bool.rkt" "core/real.rkt" "core/numerics.rkt" "core/bitvector.rkt"
"core/bool.rkt" "core/real.rkt" "core/numerics.rkt" "core/bitvector.rkt" "core/bvlib.rkt"
"core/function.rkt"
"core/procedure.rkt" "core/equality.rkt" "core/distinct.rkt" "core/reflect.rkt"
"adt/box.rkt" "adt/list.rkt" "adt/vector.rkt"
"adt/box.rkt" "adt/list.rkt" "adt/vector.rkt" "adt/bvseq.rkt"
"struct/struct.rkt" "struct/generics.rkt"
"form/state.rkt" "form/define.rkt" "form/control.rkt" "form/module.rkt" "form/app.rkt")
"form/define.rkt" "form/control.rkt" "form/module.rkt" "form/app.rkt")
(provide
(rename-out [@|| ||]) ; The character sequence || does not play nicely with the filtered-out form.
(filtered-out drop@
(combine-out
; core/bool.rkt
pc with-asserts with-asserts-only asserts clear-asserts!
@assert @boolean? @false? @! @&& @=> @<=> @forall @exists
vc with-vc clear-vc! vc? vc-true? vc-true vc-assumes vc-asserts
@assert @assume
@boolean? @false? @! @&& @=> @<=> @forall @exists
; core/real.rkt
@integer? @real? @= @< @<= @>= @>
@+ @* @- @/ @quotient @remainder @modulo @abs
@ -32,7 +33,13 @@
@bvnot @bvor @bvand @bvxor @bvshl @bvlshr @bvashr
@bvneg @bvadd @bvsub @bvmul @bvudiv @bvsdiv @bvurem @bvsrem @bvsmod
@concat @extract @sign-extend @zero-extend
@z3_ext_rotate_left @z3_ext_rotate_right
@integer->bitvector @bitvector->integer @bitvector->natural
; core/bvlib.rkt
bit lsb msb bvzero? bvadd1 bvsub1
bvsmin bvsmax bvumin bvumax
rotate-left rotate-right bvrol bvror
bool->bitvector bitvector->bool bitvector->bits
; core/function.rkt
@fv? ~> function?
; core/distinct.rkt
@ -41,11 +48,14 @@
@eq? @equal?
; core/reflect.rkt
symbolics type? solvable? @any/c type-of type-cast for/all for*/all
symbolic? concrete?
term? constant? expression?
term expression constant term-type
term=? term->datum clear-terms! term-cache
term=? term->datum
terms terms-count terms-ref with-terms clear-terms! gc-terms!
union? union union-contents union-guards union-values
union-filter in-union in-union* in-union-guards in-union-values
result? result-value result-state normal normal? failed failed?
; adt/box.rkt
@box @box-immutable @box? @unbox @set-box!
; adt/list.rkt : Pair Constructors and Selectors
@ -69,9 +79,9 @@
@take @drop @split-at @take-right @drop-right @split-at-right
@add-between @append* @flatten @remove-duplicates
@filter-map @count @partition @append-map @filter-not @shuffle
@argmin @argmax
@argmin @argmax @list-set
; adt/list.rkt : Non-Standard Functions
@insert @replace
@insert
; adt/vector.rkt : Basic Functions
@vector? @vector @vector-immutable
@vector-length @vector-ref @vector-set! @vector->list @list->vector @vector->immutable-vector
@ -80,19 +90,23 @@
@vector-append
; adt/procedure.rkt
@procedure? @apply @procedure-rename @negate @void?
; adt/bvseq.rkt
@list-ref-bv @list-set-bv @length-bv
@take-bv @take-right-bv
@drop-bv @drop-right-bv @list-tail-bv
@split-at-bv @split-at-right-bv
@vector-ref-bv @vector-set!-bv @vector-length-bv
; struct/struct.rkt
struct struct-field-index define/generic define-struct
; struct/generics.rkt
@define-generics @make-struct-type-property
; form/state.rkt
current-oracle oracle oracle?
; form/define.rkt
define-symbolic define-symbolic*
; form/control.rkt
@if @and @or @not @nand @nor @xor @implies
@unless @when @cond @case else
; form/module.rkt
@#%module-begin @#%top-interaction @module @module @module+
@#%module-begin @#%top-interaction @module @module* @module+
; form/app.rkt
#%app #%plain-app
)))
@ -159,7 +173,7 @@
expand-syntax-to-top-form
; input and output
read read-syntax
write display print displayln fprintf printf eprintf format newline
write display print writeln displayln println fprintf printf eprintf format newline
pretty-print pretty-write pretty-display pretty-format
call-with-input-file
current-input-port current-output-port current-error-port eof

View File

@ -7,26 +7,26 @@
(require (only-in "real.rkt" @>= @> @= @integer? T*->integer?))
(provide
(rename-out [lift-op bvlift-op]) bvcoerce
(rename-out [@bv bv]) @bv? bv? bv-value bv-type
(rename-out [@bitvector bitvector]) bitvector-size bitvector?
@bveq @bvslt @bvsgt @bvsle @bvsge @bvult @bvugt @bvule @bvuge
@bvnot @bvor @bvand @bvxor @bvshl @bvlshr @bvashr
@bvneg @bvadd @bvsub @bvmul @bvudiv @bvsdiv @bvurem @bvsrem @bvsmod
@concat @extract @sign-extend @zero-extend @integer->bitvector @bitvector->integer @bitvector->natural)
@concat @extract @sign-extend @zero-extend
@z3_ext_rotate_left @z3_ext_rotate_right
@integer->bitvector @bitvector->integer @bitvector->natural)
;; ----------------- Bitvector Types ----------------- ;;
; Cache of all bitvector types constructed so far, mapping sizes to types.
(define bitvector-types (make-hash))
(define bitvector-types (make-hasheq))
; Returns the bitvector type of the given size.
(define (bitvector-type size)
(unless (exact-positive-integer? size)
(raise-argument-error 'bitvector "exact-positive-integer?" size))
(or (hash-ref bitvector-types size #f)
(let ([t (bitvector size)])
(hash-set! bitvector-types size t)
t)))
(assert (and (exact-positive-integer? size) (fixnum? size))
(argument-error 'bitvector "(and/c exact-positive-integer? fixnum?)" size))
(hash-ref! bitvector-types size (λ () (bitvector size))))
; Represents a bitvector type.
(struct bitvector (size)
@ -51,9 +51,9 @@
[(bv _ (== self)) v]
[(term _ (== self)) v]
[(union (list _ ... (cons gt (and (? typed? vt) (app get-type (== self)))) _ ...) _)
(assert gt (thunk (error caller "expected ~a, given ~.a" self v)))
(assert gt (type-error caller self v))
vt]
[_ (assert #f (thunk (error caller "expected ~a, given ~.a" self v)))]))
[_ (assert #f (type-error caller self v))]))
(define (type-eq? self u v) (@bveq u v))
(define (type-equal? self u v) (@bveq u v))
(define (type-compress self f? ps) (generic-merge* ps))
@ -86,11 +86,17 @@
#:transparent
#:methods gen:typed
[(define (get-type self) (bv-type self))]
#:property prop:custom-print-quotable 'never
#:methods gen:custom-write
[(define (write-proc self port mode)
(fprintf port "(bv ~a ~a)"
(bv-value self)
(bitvector-size (bv-type self))))])
(match self
[(bv v (bitvector bw))
(let*-values ([(q r) (quotient/remainder bw 4)]
[(p b mw) (if (zero? r) (values "x" 16 q) (values "b" 2 bw))])
(fprintf port "(bv #~a~a ~a)"
p
(~r (ufinitize v bw) #:base b #:pad-string "0" #:min-width mw)
bw))]))])
; Returns a signed representation of the given number, using the specified bitwidth.
; Assumes that val is a real, non-infinite, non-NaN number.
@ -113,14 +119,14 @@
; be either an exact-positive-integer? or a bitvector type.
; The number may be a real, non-infinite, non-NaN concrete value.
(define (make-bv val precision)
(unless (and (real? val) (not (infinite? val)) (not (nan? val)))
(raise-arguments-error 'bv "expected a real, non-infinite, non-NaN number" "value" val))
(assert (and (real? val) (not (infinite? val)) (not (nan? val)))
(arguments-error 'bv "expected a real, non-infinite, non-NaN number" "value" val))
(cond [(exact-positive-integer? precision)
(bv (sfinitize val precision) (bitvector-type precision))]
[(bitvector? precision)
(bv (sfinitize val (bitvector-size precision)) precision)]
[else
(raise-arguments-error 'bv "exact-positive-integer? or bitvector? type" "precision" precision)]))
(assert #f (arguments-error 'bv "exact-positive-integer? or bitvector? type" "precision" precision))]))
; Pattern matching for bitvector literals.
(define-match-expander @bv
@ -189,7 +195,6 @@
[_ (loop rest)])]
[(list _ rest ...)
(loop rest)]))
#:unless (max (length xs) (length ys))
#:error (bitvector-type-error (object-name op) x y))]
[(_ _) (assert #f (bitvector-type-error (object-name op) x y))]))
@ -213,7 +218,6 @@
[_ (loop rest)])]
[(list _ rest ...)
(loop rest)]))
#:unless (apply max (length vs) (map length ws))
#:error (apply bitvector-type-error (object-name op) xs))]
[_ (assert #f (apply bitvector-type-error (object-name op) xs))]))
@ -335,6 +339,13 @@
(ite (bveq (bv 0 t) (bvand x (bv (bvsmin t) t))) (bv 0 t) (bv -1 t))]
[(_ _) (expression @bvashr x y)]))
(define (z3_ext_rotate_left x y)
(expression @z3_ext_rotate_left x y))
(define (z3_ext_rotate_right x y)
(expression @z3_ext_rotate_right x y))
(define-lifted-operator @bvnot bvnot T*->T)
(define-lifted-operator @bvand bvand T*->T)
(define-lifted-operator @bvor bvor T*->T)
@ -342,6 +353,8 @@
(define-lifted-operator @bvshl bvshl T*->T)
(define-lifted-operator @bvlshr bvlshr T*->T)
(define-lifted-operator @bvashr bvashr T*->T)
(define-lifted-operator @z3_ext_rotate_left z3_ext_rotate_left T*->T)
(define-lifted-operator @z3_ext_rotate_right z3_ext_rotate_right T*->T)
;; ----------------- Simplification ruules for bitwise operators ----------------- ;;
@ -483,8 +496,10 @@
[((expression (== @bvadd) (expression (== @bvneg) (== y)) z) _) z]
[((expression (== @bvadd) z (expression (== @bvneg) (== y))) _) z]
[((expression (== @bvadd) (bv a _) b) (bv (app - a) _)) b]
[((expression (== @bvadd) (? bv? a) b) (? bv?)) (@bvadd (@bvadd a y) b)]
[((expression (== @bvadd) a b) (expression (== @bvneg) a)) b]
[((expression (== @bvadd) a b) (expression (== @bvneg) b)) a]
[((expression (== ite) a (? bv? b) (? bv? c)) (? bv?)) (ite a (bvadd b y) (bvadd c y))]
[((expression (== @bvadd) a ...) (expression (== @bvadd) b ...))
(let ([alen (length a)]
[blen (length b)])
@ -576,10 +591,19 @@
[(_ _ (bv b _))
(bv (sfinitize (bitwise-and (bitwise-not (arithmetic-shift -1 len)) (arithmetic-shift b (- j))) len)
(bitvector-type len))]
[(_ 0 (expression (== @concat) _ (and (? typed? (app get-type (bitvector (== len)))) a))) a]
[(_ _ (expression (== @concat)
(and (? typed? (app get-type (bitvector (== len)))) a)
(? typed? (app get-type (bitvector (== j)))))) a]
[(_ _ (expression (== @extract) _ k a)) (extract (+ i k) (+ j k) a)]
[(_ _ (expression (== @concat) _ (and (? typed? (app get-type (bitvector size))) a)))
#:when (< i size)
(extract i j a)]
[(_ _ (expression (== @concat) a (? typed? (app get-type (bitvector size)))))
#:when (>= j size)
(extract (- i size) (- j size) a)]
[(_ 0 (expression (and @bvop (or (== @sign-extend) (== @zero-extend)))
(and (? typed? (app get-type (bitvector size))) a)
_))
(if (< i size)
(extract i j a)
(expression @bvop a (bitvector-type (add1 i))))]
[(_ _ _) (expression @extract i j x)]))
(define-operator @extract
@ -600,9 +624,14 @@
[((? number?) _) (merge+ (for*/list ([k (in-range i -1 -1)])
(cons (@= k j) (extract i k x)))
#:unless (+ i 1) #:error (extract*-err x i j))]
[(_ _) (merge+ (for*/list ([n size] [k (add1 n)])
[(_ _)
(if (equal? i j)
(merge+ (for*/list ([n size])
(cons (&& (@= n i) (@= n j)) (extract n n x)))
#:unless size #:error (extract*-err x i j))
(merge+ (for*/list ([n size] [k (add1 n)])
(cons (&& (@= n i) (@= k j)) (extract n k x)))
#:unless (+ size (/ (* size (- size 1)) 2)) #:error (extract*-err x i j))]))]
#:unless (+ size (/ (* size (- size 1)) 2)) #:error (extract*-err x i j)))]))]
(lambda (@i @j @x)
(define i (type-cast @integer? @i 'extract))
(define j (type-cast @integer? @j 'extract))
@ -700,9 +729,9 @@
[(v (union ts))
(merge+ (for/list ([gt ts] #:when (bitvector? (cdr gt)))
(cons (car gt) (integer->bitvector v (cdr gt))))
#:unless (length ts) #:error (arguments-error "expected a bitvector type t" "t" @t))]
#:unless (length ts) #:error (arguments-error 'integer->bitvector "expected a bitvector type t" "t" @t))]
[(v (? bitvector? t)) (integer->bitvector v t)]
[(_ _) (assert #f (arguments-error "expected a bitvector type t" "t" @t))])))
[(_ _) (assert #f (arguments-error 'integer->bitvector "expected a bitvector type t" "t" @t))])))
(define-operator @bitvector->integer
#:identifier 'bitvector->integer

View File

@ -1,13 +1,17 @@
#lang racket
(require "term.rkt" "union.rkt")
(require "term.rkt" "union.rkt" "exn.rkt" "result.rkt" "reporter.rkt")
(provide @boolean? @false?
(provide
;; ---- lifted boolean? operations ---- ;;
@boolean? @false? @true?
! && || => <=> @! @&& @|| @=> @<=> @exists @forall
and-&& or-|| instance-of?
@assert pc with-asserts with-asserts-only
(rename-out [export-asserts asserts]) clear-asserts!
T*->boolean?)
and-&& or-|| instance-of? T*->boolean?
;; ---- VC generation ---- ;;
@assert @assume $assert $assume
(rename-out [get-vc vc]) clear-vc! merge-vc! with-vc
vc? vc-assumes vc-asserts
vc-true vc-true?)
;; ----------------- Boolean type ----------------- ;;
(define-lifted-type @boolean?
@ -22,9 +26,9 @@
[(? boolean?) v]
[(term _ (== self)) v]
[(union : [g (and (or (? boolean?) (term _ (== self))) u)] _ ...)
(@assert g (thunk (raise-argument-error caller "expected a boolean?" v)))
($assert g (argument-error caller "boolean?" v))
u]
[_ (@assert #f (thunk (raise-argument-error caller "expected a boolean?" v)))]))
[_ ($assert #f (argument-error caller "boolean?" v))]))
(define (type-compress self force? ps)
(match ps
[(list _) ps]
@ -55,7 +59,9 @@
#:unsafe $op
#:safe (lift-op $op)))
(define-syntax-rule (define-quantifier @op $op)
(define-syntax-rule (define-quantifier $op @op)
(begin
(define $op (quantifier @op))
(define-operator @op
#:identifier '$op
#:range T*->boolean?
@ -66,12 +72,9 @@
[((list (constant _ (? primitive-solvable?)) (... ...)) body)
($op @vars body)]
[(_ _)
(@assert
($assert
#f
(thunk
(raise-argument-error
'$op
"expected a list of symbolic constants of primitive solvable types" @vars)))]))))
(argument-error '$op "list of symbolic constants of primitive solvable types" @vars))])))))
;; ----------------- Basic boolean operators ----------------- ;;
(define (! x)
@ -83,9 +86,26 @@
(define && (logical-connective @&& @|| #t #f))
(define || (logical-connective @|| @&& #f #t))
(define (=> x y) (|| (! x) y))
(define (=> x y) ; (|| (! x) y))
(cond
[(equal? x y) #t]
[(eq? x #f) #t]
[(eq? y #t) #t]
[(eq? x #t) y]
[(eq? y #f) (! x)]
[(cancel? x y) y]
[else
(match y
[(expression (== @||) _ ... (== x) _ ...) #t]
[(expression (== @&&) (== x) b) (=> x b)]
[(expression (== @&&) b (== x)) (=> x b)]
[(expression (== @&&) (expression (== @||) _ ... (== x) _ ...) b) (=> x b)]
[(expression (== @&&) b (expression (== @||) _ ... (== x) _ ...)) (=> x b)]
[(expression (== @<=>) (== x) b) (=> x b)]
[(expression (== @<=>) b (== x)) (=> x b)]
[_ (|| (! x) y)])]))
(define (<=> x y) ;(|| (&& x y) (&& (! x) (! y))))))
(define (<=> x y)
(cond [(equal? x y) #t]
[(boolean? x) (if x y (! y))]
[(boolean? y) (if y x (! x))]
@ -112,14 +132,15 @@
[_ (loop (cdr xs))]))]
[_ #f]))
(define exists (quantifier @exists))
(define forall (quantifier @forall))
(define-quantifier @exists exists)
(define-quantifier @forall forall)
(define (@true? v)
(or (eq? #t v) (! (@false? v))))
(define-quantifier exists @exists)
(define-quantifier forall @forall)
;; ----------------- Additional operators ----------------- ;;
;; ----------------- Additional operators and utilities ----------------- ;;
(define-syntax and-&&
(syntax-rules ()
[(_) #t]
@ -143,6 +164,15 @@
(and (union? v) (apply || (for/list ([g (in-union-guards v symbolic-type)]) g))))]
[_ #f]))
(define (void))
(define-syntax first-term-or-bool
(syntax-rules ()
[(_ e) e]
[(_ e0 e ...) (let ([v e0])
(if (void? v)
(first-term-or-bool e ...)
v))]))
;; ----------------- Partial evaluation rules for ∀ and ∃ ----------------- ;;
@ -165,7 +195,7 @@
[((== !iden) _) !iden]
[(_ (== !iden)) !iden]
[(_ _)
(first-value
(first-term-or-bool
(simplify-connective op co !iden x y)
(if (term<? x y) (expression op x y) (expression op y x)))])]
[xs
@ -176,27 +206,19 @@
[(list x) x]
[ys (apply expression op (sort ys term<?))])])]))
(define (void))
(define-syntax first-value
(syntax-rules ()
[(_ e) e]
[(_ e0 e ...) (let ([v e0])
(if (void? v)
(first-value e ...)
v))]))
(define (simplify-connective op co !iden x y)
(match* (x y)
[(_ (== x)) x]
[((? expression?) (? expression?))
(first-value
(first-term-or-bool
(if (term<? y x)
(simplify-connective:expr/any op co !iden x y)
(simplify-connective:expr/any op co !iden y x)
(simplify-connective:expr/any op co !iden y x))
(simplify-connective:expr/expr op co !iden x y))]
[((? expression?) _)
(simplify-connective:expr/any op co !iden x y)]
(if (term<? y x) (simplify-connective:expr/any op co !iden x y) )]
[(_ (? expression?))
(simplify-connective:expr/any op co !iden y x)]
(if (term<? x y) (simplify-connective:expr/any op co !iden y x) )]
[(_ _) ]))
(define (simplify-connective:expr/any op co !iden x y)
@ -206,21 +228,13 @@
[(expression (== op) _ ... (== y) _ ...) x]
[(expression (== op) _ ... (expression (== @!) (== y)) _ ...) !iden]
[(expression (== @!) (expression (== co) _ ... (== y) _ ...)) !iden]
[(expression (== @!) (expression (== co) _ ... (expression (== @!) (== y)) _ ...)) x]
[(expression (== @!) (expression (== op) _ ... (expression (== @!) (== y)) _ ...)) y]
[(expression (== @!) a)
(match y
[(expression (== op) _ ... (== a) _ ...) !iden]
[_ ])]
[_ ]))
; Applies the following simplification rules symmetrically:
; (1) (op (op a1 ... an) (op ai ... aj)) ==> (op a1 ... an)
; (2) (op (op a1 ... ai ... an) (op b1 ... (neg ai) ... bn) ==> !iden
; (3) (op (co a1 ... an) (co ai ... aj)) ==> (co ai ... aj)
; Returns ⊥ if none of the rules applicable; otherwise returns the simplified result.
(define (simplify-connective:expr/expr op co !iden a b)
(match* (a b)
[((expression (== op) _ ... x _ ...) (expression (== @!) x)) !iden]
[((expression (== @!) x) (expression (== op) _ ... x _ ...)) !iden]
[((expression (== op) xs ...) (expression (== op) ys ...))
(cond [(sublist? xs ys) b]
[(sublist? ys xs) a]
@ -230,6 +244,12 @@
(cond [(sublist? xs ys) a]
[(sublist? ys xs) b]
[else ])]
[((expression (== op) xs ...) (expression (== co) ys ...))
(cond [(for*/or ([x xs][y ys]) (equal? x y)) a]
[else ])]
[((expression (== co) xs ...) (expression (== op) ys ...))
(cond [(for*/or ([y ys][x xs]) (equal? x y)) b]
[else ])]
[(_ _) ]))
(define (simplify-fp op co !iden xs)
@ -262,53 +282,196 @@
[(_ _) #f]))
;; ----------------- Assertions and path condition ----------------- ;;
(define (export-asserts) (remove-duplicates (asserts)))
;; ----------------- VC generation ----------------- ;;
(define (clear-asserts!) (asserts '()))
; A verification condition (VC) consists of two @boolean?
; values representing assumptions and assertions issued
; during execution. A VC is legal if at least one of its
; constituent fields is true under all models.
(define asserts
(struct vc (assumes asserts) #:transparent)
; The true verification condition.
(define vc-true (vc #t #t))
(define (vc-true? s) (equal? s vc-true))
; Returns (vc (s.assumes && (s.asserts => g)) s.asserts).
(define (assuming s g) ; g must be a symbolic or concrete boolean
(vc (&& (vc-assumes s) (=> (vc-asserts s) g)) (vc-asserts s)))
; Returns (vc s.assumes (s.asserts && (s.assumes => g))).
(define (asserting s g) ; g must be a symbolic or concrete boolean
(vc (vc-assumes s) (&& (vc-asserts s) (=> (vc-assumes s) g))))
; The current-vc parameter keeps track of the current verification condition,
; which is an instance of vc?. The default value for this parameter is vc-true.
(define current-vc
(make-parameter
'()
(match-lambda [(? list? xs) xs]
[x (if (eq? x #t) (asserts) (cons x (asserts)))])))
vc-true
(lambda (v) (unless (vc? v) (raise-argument-error 'vc "vc?" v)) v)))
(define pc
(make-parameter
#t
(lambda (new-pc)
(or (boolean? new-pc)
(and (term? new-pc) (equal? @boolean? (term-type new-pc)))
(error 'pc "expected a boolean path condition, given a ~s" (type-of new-pc)))
(or (&& (pc) new-pc)
(error 'pc "infeasible path condition")))))
; Returns the current vc, without exposing the parameter outside the module.
(define (get-vc) (current-vc))
; Clears the current vc by setting it to the true spec.
(define (clear-vc!) (current-vc vc-true))
; Returns #t if x && (g => y) is equivalent to x according to the embedded
; rewrite rules. Otherwise returns #f.
(define (merge-absorbs? x g y)
(match y
[(== x) #t] ; x && (g => x)
[(expression (== @&&) (== x) (== g)) #t] ; x && (g => (x && g))
[(expression (== @&&) (== g) (== x)) #t] ; x && (g => (x && g))
[(expression (== @&&) (== x) (expression (== @||) _ ... (== g) _ ...)) #t] ; x && (g => (x && (_ => g)))
[(expression (== @&&) (expression (== @||) _ ... (== g) _ ...) (== x)) #t] ; x && (g => ((_ => g) && x))
[_ #f]))
; Returns (field x) && (gs[0] => (field ys[0])) ... && (gs[n-1] => (field gs[n-1])).
(define (merge-field field x gs ys)
(define xf (field x))
(apply && xf
(for*/list ([(g y) (in-parallel gs ys)]
[yf (in-value (field y))]
#:unless (merge-absorbs? xf g yf))
(=> g yf))))
;; Returns (field x) && (gs[0] => (field ys[0])) ... && (gs[n-1] => (field gs[n-1])).
;; Assumes that ys[i] => x for all i, and at most one gs evaluates to true in any model.
;(define (merge-field field x gs ys)
; (define xf (field x))
; (define gs=>ys
; (for*/list ([(g y) (in-parallel gs ys)]
; [yf (in-value (field y))]
; #:unless (merge-absorbs? xf g yf))
; (=> g yf)))
; (match gs=>ys
; [(list) xf]
; [(list gy) (&& xf gy)]
; [(or (list (expression (== @||) _ ... g _ ...) (expression (== @||) _ ... (expression (== @!) g) _ ...))
; (list (expression (== @||) _ ... (expression (== @!) g) _ ...) (expression (== @||) _ ... g _ ...)))
; (apply && gs=>ys)]
; [_ (apply && xf gs=>ys)]))
; Takes as input a list of n guards and n vcs and sets the current vc
; to (current-vc) && (vc-guard guard1 vc1) && ... && (vc-guard guardn vcn).
; Then, it checks if either the assumes or the asserts of the resulting vc
; are false? and if so, throws either an exn:fail:svm:assume? or
; exn:fail:svm:assert? exception. This procedure makes the following assumptions:
; * at most one of the given guards is true in any model,
; * (vc-assumes vcs[i]) => (vc-assumes (current-vc)) for all i, and
; * (vc-asserts vcs[i]) => (vc-asserts (current-vc)) for all i.
(define (merge-vc! guards vcs)
(unless (null? vcs)
(define vc*
(vc (merge-field vc-assumes (current-vc) guards vcs)
(merge-field vc-asserts (current-vc) guards vcs)))
(current-vc vc*)
(when (false? (vc-assumes vc*))
(raise-exn:fail:svm:assume:core "contradiction"))
(when (false? (vc-asserts vc*))
(raise-exn:fail:svm:assert:core "contradiction"))))
; Sets the current vc to (vc-proc (current-vc) g) where g is (@true? val).
; If g is #f or the resulting vc's vc-field value is #f,
; uses raise-exn throws an exn:fail:svm exception.
(define-syntax-rule (vc-set! val msg vc-proc vc-field raise-exn)
(let* ([guard (@true? val)]
[vc* (vc-proc (current-vc) guard)])
(current-vc vc*)
(when (false? guard)
(raise-exn msg))
(when (false? (vc-field vc*))
(raise-exn "contradiction"))))
; Sets the current vc to (asserting (current-vc) g) where g is (@true? val).
; If g is #f or the resulting vc's asserts field is #f, throws an
; exn:fail:svm:assert exception of the given kind.
(define-syntax-rule (vc-assert! val msg raise-kind)
(vc-set! val msg asserting vc-asserts raise-kind))
; Sets the current vc to (assuming (current-vc) g) where g is (@true? val).
; If g is #f or the resulting vc's assumes field is #f, throws an
; exn:fail:svm:assume exception of the given kind.
(define-syntax-rule (vc-assume! val msg raise-kind)
(vc-set! val msg assuming vc-assumes raise-kind))
; The $assert form has three variants: ($assert val), ($assert val msg),
; and ($assert val msg kind), where val is the value being asserted, msg
; is the failure message, and kind is a procedure that returns a subtype of
; exn:fail:svm:assert. Default values for msg and kind are #f and
; raise-exn:fail:svm:assert:core, respectively.
; The first two variants of this form are used for issuing assertions from
; within the Rosette core. The third variant is used to implement the @assert
; form that is exposed to user code. An $assert call modifies the current vc to
; reflect the issued assertion. If the issued assertion or the vc-assert of the
; current vc reduce to #f, the call throws an exception of the given kind after
; updating the vc.
(define-syntax ($assert stx)
(syntax-case stx ()
[(_ val) (syntax/loc stx ($assert val #f raise-exn:fail:svm:assert:core))]
[(_ val msg) (syntax/loc stx ($assert val msg raise-exn:fail:svm:assert:core))]
[(_ val msg kind) (syntax/loc stx (vc-assert! val msg kind))]))
; Analogous to the $assert form, except that it modifies the current vc to
; reflect the issued assumption.
(define-syntax ($assume stx)
(syntax-case stx ()
[(_ val) (syntax/loc stx ($assume val #f raise-exn:fail:svm:assume:core))]
[(_ val msg) (syntax/loc stx ($assume val msg raise-exn:fail:svm:assume:core))]
[(_ val msg kind) (syntax/loc stx (vc-assume! val msg kind))]))
; The @assert form modifies the current vc to reflect the issued assertion.
; The form has two variants (@assert val) and (@assert val msg), where val
; is the value being asserted and msg is the optional error message in case
; val is #f. This form is exposed to user code.
(define-syntax (@assert stx)
(syntax-case stx ()
[(_ val) (syntax/loc stx (@assert val #f))]
[(_ val msg)
[(_ val) (syntax/loc stx ($assert val #f raise-exn:fail:svm:assert:user))]
[(_ val msg) (syntax/loc stx ($assert val msg raise-exn:fail:svm:assert:user))]))
; The @assume form modifies the current vc to reflect the issued assumption.
; The form has two variants (@assume val) and (@assume val msg), where val
; is the value being assume and msg is the optional error message in case
; val is #f. This form is exposed to user code.
(define-syntax (@assume stx)
(syntax-case stx ()
[(_ val) (syntax/loc stx ($assume val #f raise-exn:fail:svm:assume:user))]
[(_ val msg) (syntax/loc stx ($assume val msg raise-exn:fail:svm:assume:user))]))
(define (halt-svm ex)
(define result (failed ex (current-vc)))
((current-reporter) 'exception result)
result)
(define (halt-err ex) ; Treat an exn:fail? error as an assertion failure.
(define result
(failed (make-exn:fail:svm:assert:err (exn-message ex) (exn-continuation-marks ex))
(asserting (current-vc) #f)))
((current-reporter) 'exception result)
result)
; The with-vc form has two variants, (with-vc body) and (with-vc vc0 body).
; The former expands into (with-vc (current-vc) body). The latter sets the current
; vc to vc0, evaluates the given body, returns the result, and reverts current-vc
; to the value it held before the call to with-vc.
;
; If the evaluation of the body terminates normally, (with-vc vc0 body)
; outputs (normal v vc*) where v is the value computed by the body, and vc* is
; the vc (i.e., assumes and asserts) generated during the evaluation,
; with vc0 as the initial vc.
;
; If the evaluation of the body terminates abnormally with an exn:fail? exception,
; (with-vc vc0 body) outputs (failed v vc*) where v is an exn:fail:svm? exception
; that represents the cause of the abnormal termination, and vc* is the vc
; generated during the evaluation, with vc0 as the initial vc.
(define-syntax (with-vc stx)
(syntax-case stx ()
[(_ body) (syntax/loc stx (with-vc (current-vc) body))]
[(_ vc0 body)
(syntax/loc stx
(let ([guard (not-false? val)])
(asserts (=> (pc) guard))
(when (false? guard)
(raise-assertion-error msg))))]))
(define (not-false? v)
(or (eq? v #t) (! (@false? v))))
(define (raise-assertion-error msg)
(if (procedure? msg)
(msg)
(error 'assert (if msg (format "~a" msg) "failed"))))
(define-syntax (with-asserts stx)
(syntax-case stx (begin)
[(_ (begin form ...)) #'(with-asserts (let () form ...))]
[(_ form) #`(parameterize ([asserts (asserts)])
(let* ([val form]
[bools (remove-duplicates (asserts))])
(values val bools)))]))
(define-syntax-rule (with-asserts-only form)
(let-values ([(out asserts) (with-asserts form)])
asserts))
(parameterize ([current-vc vc0])
(with-handlers ([exn:fail:svm? halt-svm]
[exn:fail? halt-err])
(normal (let () body) (current-vc)))))]))

View File

@ -0,0 +1,93 @@
#lang racket
(require
(only-in racket/splicing splicing-let)
"bitvector.rkt" "merge.rkt" "safe.rkt" "term.rkt" "bool.rkt" "forall.rkt" "lift.rkt"
(only-in "real.rkt" @integer? @> @>= @=)
(only-in "numerics.rkt" extreme))
(provide bit lsb msb bvzero? bvadd1 bvsub1
bvsmin bvsmax bvumin bvumax
rotate-left rotate-right bvrol bvror
bool->bitvector bitvector->bool bitvector->bits)
(define-syntax (define-lifted stx)
(syntax-case stx ()
[(_ (id arg ...) expr ...)
#'(define-lifted id (lambda (arg ...) expr ...))]
[(_ id impl)
#'(define id (procedure-rename (bvlift-op impl) 'id))]))
(define (bit i x)
(@extract i i x))
(define (lsb x) (bit 0 x))
(define-lifted (msb x)
(let ([pos (sub1 (bitvector-size (get-type x)))])
(bit pos x)))
(define-lifted bvsmin (curry extreme @bvsle))
(define-lifted bvsmax (curry extreme @bvsge))
(define-lifted bvumin (curry extreme @bvule))
(define-lifted bvumax (curry extreme @bvuge))
(define (bool->bitvector x [t 1])
(merge (@false? x) (bv 0 t) (bv 1 t)))
(define (bitvector->bool x)
(! (bvzero? x)))
(define-lifted (bvzero? x)
(@bveq x (bv 0 (get-type x))))
(define-lifted (bvadd1 x)
(@bvadd x (bv 1 (get-type x))))
(define-lifted (bvsub1 x)
(@bvsub x (bv 1 (get-type x))))
(define-lifted (bitvector->bits v)
(for/list ([i (bitvector-size (get-type v))])
(bit i v)))
(define-syntax-rule (define-rotate id proc)
(splicing-let ([dir proc])
(define (id @i @x)
(define i (type-cast @integer? @i 'id))
(define x (bvcoerce @x id))
(match i
[0 x]
[_
(assert (@>= i 0) (arguments-error 'id "expected i >= 0" "i" i))
(for/all ([x x])
(let ([sz (bitvector-size (get-type x))])
(assert (@> sz i) (arguments-error 'id "expected (size-of x) > i" "x" x "i" i))
(if (integer? i)
(dir i sz x)
(merge+ (cons (cons (@= i 0) x)
(for/list ([n (in-range 1 sz)])
(cons (@= n i)
(dir n sz x))))
#:unless sz
#:error (arguments-error 'id "expected (size-of x) > i >= 0" "x" x "i" i)))))]))))
(define-rotate rotate-left
(lambda (i sz x)
(@concat (@extract (- sz i 1) 0 x) (@extract (- sz 1) (- sz i) x))))
(define-rotate rotate-right
(lambda (i sz x)
(@concat (@extract (- i 1) 0 x) (@extract (- sz 1) i x))))
; x and y must be bitvectors (not unions) of the same length.
; shift1 and shift2 are shift operators.
(define-syntax-rule (bvrotate x y shift1 shift2)
(let* ([sz (bitvector-size (get-type y))]
[n (bv sz sz)]
[amount (@bvurem y n)])
(@bvor (shift1 x amount) (shift2 x (@bvsub n amount)))))
(define-lifted (bvrol x y) (bvrotate x y @bvshl @bvlshr))
(define-lifted (bvror x y) (bvrotate x y @bvlshr @bvshl))

View File

@ -1,176 +0,0 @@
#lang racket
(require
(for-syntax racket))
(provide speculate speculate* apply! location=? (rename-out [state-val location-final-value]))
; The env parameter stores an eq? based hash-map which we use to keep
; track of boxes, vectors and structs that are mutated.
(define env (make-parameter #f))
; The speculate expression takes the form (speculate body), where body is
; an expression. A speculate call produces two values: the value that the
; body would produce if executed in the current environment, and a closure
; that stores a representation of all state updates that the execution of
; body would make. The closure accepts a two argument function f, and
; applies encapsulated state updates so that each updated location is set
; to (f v body-v), where body-v is the final value the body would assign to v.
;
; Any exceptions thrown by body are caught, all updates are rolled-back without
; encapsulating the final states, and the result of speculate is (values #f #f).
(define-syntax-rule (speculate body)
; using an eq? rather than equal? hash map to manage the environment bindings
; is critical for mutable objects whose hash code may change upon mutation. note
; that variables are keyed by the symbol representing their name, so eq? comparisons
; for them are equivalent to equal? comparisons.
(parameterize ([env (make-custom-hash eq? eq-hash-code)])
; roll-back state updates, encapsulate
; updates to set! variables as specified above,
; and return the value of the body together with the
; encapsulation of the state changes
(with-handlers ([exn:fail? rollback/suppress])
(values body (rollback/encapsulate)))))
; The speculate* expression takes the form (speculate* body), where body is
; an expression. A speculate* call produces two values: the value that the
; body would produce if executed in the current environment, and a list of
; locations, each of which encapsulates the pre and post state of a location
; mutated during the execution of the body. The returned locations can be
; compared with location=?.
;
; Each encapsulated update acts as a procedure that accepts a two-argument
; function f. The location for the encapsulated updated is then set to
; (f v body-v), where body-v is the final value the body would assign to the
; location and v is the current value in that location. The procedure
; (location-final-value loc) can be used to obtain the final value that the
; body would assign to a given location.
;
; Any exceptions thrown by body are caught, all updates are rolled-back without
; encapsulating the final states, and the result of speculate is (values #f #f).
(define-syntax-rule (speculate* body)
; using an eq? rather than equal? hash map to manage the environment bindings
; is critical for mutable objects whose hash code may change upon mutation. note
; that variables are keyed by the symbol representing their name, so eq? comparisons
; for them are equivalent to equal? comparisons.
(parameterize ([env (make-custom-hash eq? eq-hash-code)])
; roll-back state updates, encapsulate
; updates to set! variables as specified above,
; and return the value of the body together with the
; encapsulation of the state changes
(with-handlers ([exn:fail? rollback/suppress])
(values body (rollback/collect)))))
; A function that handles calls to structure mutators.
(define apply!
(case-lambda
[(setter getter receiver key val)
(record! receiver key getter setter)
(setter receiver key val)]
[(setter getter receiver val)
(record! receiver setter getter setter)
(setter receiver val)]))
; Stores the state of a mutation to the location in a given receiver,
; together with getters and setters that can be used to read/write
; the mutated location. The val field stores the value that was read
; from the location at some point in time (e.g., beginning/end of
; speculation). The attached procedure accepts a two argument function f
; and sets the encapsulated location to (f (getter) val).
(struct state (receiver location val getter setter)
#:transparent
#:property prop:procedure
(lambda (self proc)
(let ([receiver (state-receiver self)]
[location (state-location self)]
[getter (state-getter self)]
[setter (state-setter self)])
(record! receiver location getter setter)
(cond [(dict? receiver)
(setter receiver location (proc (getter receiver location) (state-val self)))]
[else ; struct or box
(setter receiver (proc (getter receiver) (state-val self)))]))))
(define (get getter receiver location)
(cond [(dict? receiver) (getter receiver location)]
[else (getter receiver)]))
(define (state-rollback! s)
(let ([receiver (state-receiver s)]
[location (state-location s)]
[getter (state-getter s)]
[setter (state-setter s)])
(cond [(dict? receiver)
(setter receiver location (state-val s))]
[else ; struct or box
(setter receiver (state-val s))])))
; Returns true iff both objects encapsulate updates to the same location.
(define (location=? s0 s1)
(match* (s0 s1)
[((state rec0 loc0 _ _ _) (state rec1 loc1 _ _ _))
(and (eq? rec0 rec1) (equal? loc0 loc1))]
[(_ _) #f]))
; Adds a record of the given variable's or object's current state
; to the environment, if the environment is valid and does not
; already have a mapping for the record!-ed variable or object.
(define-syntax-rule (record! obj location getter setter)
(when (and (env)
(not (env-has-state? obj location))) ; we do this check separately so that the getter/setter
(env-set! obj location getter setter))) ; lambdas don't get created unless they are needed
; Returns a true value if the current environment (assumed not be #f)
; has a state record for the given mutation receiver and location of
; mutation. For structs, the location is the field-setter function for
; the mutated field. For dictionary objects, the location is the key within the
; dictionary to which the dict-set! operation is being applied. For boxes,
; the location is the set-box! procedure.
(define (env-has-state? receiver location)
(let ([env (env)])
(and (dict-has-key? env receiver) ; compound object
(dict-has-key? (dict-ref env receiver) location))))
; Augments env with a mapping from the given receiver to a state record reflecting
; the current state at the given location, as obtained by the given getter
; procedure. This function assumes that (env-has-state? receiver location) is false.
(define (env-set! receiver location getter setter)
(let ([env (env)]
[new-state (state receiver location (get getter receiver location) getter setter)])
(let ([locations (dict-ref! env receiver make-hash)]) ; compound object
(dict-set! locations location new-state))))
; Reverts the state of set! variables and struct fields to
; their initial values, without encapsulating the final state updates.
; Returns (values #f #f). The error argument is ignored.
(define (rollback/suppress err)
;(printf "\n\nERROR: ~a\n\n" err)
(unless (zero? (dict-count (env)))
(for* ([states (in-dict-values (env))]
[s (if (list? states) (in-list states) (in-dict-values states))])
(state-rollback! s))) ; roll-back
(values #f #f))
; Reverts the state of set! variables and struct fields to
; their initial values, and returns an encapsulation of
; the final state updates.
(define (rollback/encapsulate)
(if (zero? (dict-count (env)))
void
(let ([updates (rollback/collect)])
(lambda (proc)
(for ([s (in-list updates)])
(s proc))))))
; Reverts the state of set! variables and struct fields to
; their initial values, and returns a list that contains a
; copy of the final state of each location bound in the current
; environment.
(define (rollback/collect)
(for*/list ([states (in-dict-values (env))]
[s (if (list? states) (in-list states) (in-dict-values states))])
(let ([final (get (state-getter s) (state-receiver s) (state-location s))])
(state-rollback! s) ; roll-back
(struct-copy state s [val final])))) ; collect final states

View File

@ -5,17 +5,53 @@
(provide @eq? ; (-> any/c any/c @boolean?)
@equal?) ; (-> any/c any/c @boolean?)
(define-syntax-rule (define-equality-predicate @=? =? type=?)
; We must use identity-based hashing and comparison of user-provided values,
; because user-defined structs can override equal/hash and cause unexpected
; errors when the overriden equal? is repeatedly called by a hash map. We also
; have to use (below) identity-based comparisons for shortcircuiting for the
; same reason---equal? might be overriden by a user-defined struct.
(struct key (x y)
#:transparent
#:methods gen:equal+hash
[(define (equal-proc a b equal?-recur)
(and (eq? (key-x a) (key-x b))
(eq? (key-y a) (key-y b))))
(define (hash-proc a hash-recur)
(hash-recur (cons (eq-hash-code (key-x a)) (eq-hash-code (key-y a)))))
(define (hash2-proc a hash2-recur)
(hash2-recur (cons (eq-hash-code (key-y a)) (eq-hash-code (key-x a)))))])
(define-syntax-rule (define-equality-predicate @=? type=? @cache @make-hash)
(define (@=? x y)
(cond [(=? x y) #t]
(let* ([cache (@cache)]
[toplevel? (false? cache)]
[k (key x y)])
(when toplevel?
(set! cache (@make-hash))
(@cache cache))
(if (hash-has-key? cache k)
(hash-ref cache k)
(begin
(hash-set! cache k #t)
(let ([result
(cond [(eq? x y) #t] ; We must use identity-based comparisons for short-circuiting.
[(union? x) (if (union? y)
(union=union? x y @=?)
(union=value? x y @=?))]
[(union? y) (union=value? y x @=?)]
[else (type=? (type-of x y) x y)])))
[else (type=? (type-of x y) x y)])])
(if toplevel?
(@cache #f)
(hash-set! cache k result))
result))))))
(define-equality-predicate @equal? equal? type-equal?)
(define-equality-predicate @eq? eq? type-eq?)
(define equal-cache (make-parameter #f))
(define eq-cache (make-parameter #f))
(define-equality-predicate @equal? type-equal? equal-cache make-hash)
(define-equality-predicate @eq? type-eq? eq-cache make-hash)
; (-> union? union? (-> any/c any/c @boolean?) @boolean?)
(define (union=union? x y =?)
@ -23,7 +59,10 @@
[((union vs t) (union ws s))
(and (or (subtype? t s) (subtype? s t))
(apply || (for*/list ([v vs] [w ws])
(and-&& (=? (cdr v) (cdr w)) (car v) (car w)))))]))
(and-&&
(=? (cdr v) (cdr w))
(car v)
(car w)))))]))
; (-> union? (not/c union?) (-> any/c any/c @boolean?) @boolean?)
(define (union=value? x y =?)

View File

@ -0,0 +1,61 @@
#lang racket
(require
(only-in "bool.rkt" with-vc $assume merge-vc!)
"exn.rkt" "result.rkt" "store.rkt" "merge.rkt")
(provide eval-assuming eval-guarded!)
; Takes as input a concrete or symbolic boolean and a thunk,
; evaluates thunk under the assumption that the guard holds,
; and returns the result. This result takes one of two forms.
;
; If the evaluation of the thunk terminates normally, the result
; is (normal (normal v st) vc*) where v is the value computed by the
; thunk, st captures all stores mutations performed during evaluation,
; and vc* captures the verification condition generated during the
; evaluation, starting from the current vc.
;
; If the thunk terminates abnormally, the result is (failed ex vc*),
; where ex is an exn:fail:svm? exception that represents the cause
; of the abnormal termination, and vc* captures the verification
; condition generated during the evaluation, starting from the current vc.
;
; Neither the current store nor the current vc are modified after
; eval-assuming returns.
(define (eval-assuming guard thunk)
(with-vc
(begin
($assume guard)
(with-store (thunk)))))
; Takes as input a list of n guards and n thunks, evaluates each thunk
; under its guard using eval-assuming, merges the resulting vcs into
; the current vc, merges the resulting stores (if any) into the current
; store, and merges the resulting values (if any) before returning them
; as output. If all of the thunks fail under their guards, eval-guarded
; raises an exn:fail:svm:merge exception after the specs are merged into
; the current vc.
; This procedure makes the following assumptions, based on the Lean
; formalization:
; (1) At most one guard evaluates to true under any model.
; (2) For all models m under which (vc) evaluates to vc-true, there is
; exactly one guard in guards that evaluates to #t under m.
; (3) For all models m under which (vc) doesn't evaluate to vc-true,
; every vc produced by evaluating the given thunks evaluates to
; the same spec as (vc) under m.
(define (eval-guarded! guards thunks)
(define results (map eval-assuming guards thunks))
(merge-vc! guards (map result-state results))
(define-values (gs rs)
(for/lists (gs rs) ([g guards][r results] #:when (normal? r))
(values g (result-value r))))
(if (null? rs)
(raise-exn:fail:svm:merge)
(begin
(merge-stores! gs (map result-state rs))
(apply merge* (for/list ([g gs][r rs])
(cons g (result-value r)))))))

107
rosette/base/core/exn.rkt Normal file
View File

@ -0,0 +1,107 @@
#lang racket
(require (only-in racket/string string-split)
(for-syntax racket/syntax racket/string)
racket/provide)
(provide (matching-identifiers-out #px"^exn:fail:svm.*\\?$" (all-defined-out))
(matching-identifiers-out #px"^make\\-exn:fail:svm.*$" (all-defined-out))
(matching-identifiers-out #px"^raise\\-exn:fail:svm.*$" (all-defined-out))
exn:fatal? fatal
argument-error arguments-error type-error contract-error index-too-large-error)
;; --------------- Exceptions --------------- ;;
; Four kinds of failures can happen during symbolic evaluation:
; (1) the execution reaches (assert e) where e evaluates to #f, or asserting e reduces vc's asserts to #f;
; (2) the execution reaches (assume e) where e evaluates to #f, or assuming e reduces vc's assumes to #f;
; (3) the execution reaches e where e raises an exn:fail? exception; and
; (4) all paths at a given merge point led to a failure.
; Within the first two types of failures, we distinguish between
; assertions and assumptions issued by user code and core (Rosette) code.
; The third type of failure is treated as an assertion failure for the
; purposes of verification condition generation. Finally,
; the fourth type of failure is tracked via exn:fail:svm:merge.
; The top of the exception hierarchy for failures raised
; during symbolic evaluation.
(struct exn:fail:svm exn:fail ())
; An assert exception can be one of the following kinds:
; * :core represents an assertion failure raised in Rosette code,
; * :user represents an assertion failure raised in user code, and
; * :err indicates that an exn:fail? exception was raised during evaluation.
(struct exn:fail:svm:assert exn:fail:svm ())
(struct exn:fail:svm:assert:core exn:fail:svm:assert ())
(struct exn:fail:svm:assert:user exn:fail:svm:assert ())
(struct exn:fail:svm:assert:err exn:fail:svm:assert ())
; An assume exception can be one of the following kinds:
; * :core represents an assumption failure raised in Rosette code, and
; * :user represents an assumption failure raised in user code.
(struct exn:fail:svm:assume exn:fail:svm ())
(struct exn:fail:svm:assume:core exn:fail:svm:assume ())
(struct exn:fail:svm:assume:user exn:fail:svm:assume ())
; An merge exception is raised when all paths at a branching point lead to a failure.
(struct exn:fail:svm:merge exn:fail:svm ())
(define-syntax (define-make-and-raise stx)
(syntax-case stx ()
[(_ id)
(with-syntax ([make-id (format-id #'id "make-~a" (syntax-e #'id))]
[raise-id (format-id #'id "raise-~a" (syntax-e #'id))]
[prefix (list-ref (string-split (symbol->string (syntax-e #'id)) ":") 3)])
#'(begin
(define (make-id [msg #f] [cont-marks #f])
(id (format "[~a] ~a" prefix (or msg "failed"))
(or cont-marks (current-continuation-marks))))
(define (raise-id [msg #f] [cont-marks #f])
(raise (make-id msg cont-marks)))))]
[(_ id ...)
#'(begin (define-make-and-raise id) ...)]))
; Creates two procedures make-* and raise-* for each exception type that
; creates and raises an exception of the given type, respectively.
(define-make-and-raise
exn:fail:svm:assert:core
exn:fail:svm:assert:user
exn:fail:svm:assert:err
exn:fail:svm:assume:core
exn:fail:svm:assume:user
exn:fail:svm:merge)
;; --------------- Messages --------------- ;;
; Fatal errors indicate bugs in the Rosette implementation.
; Since Rosette only catches and handles errors of subtype exn:fail?,
; exn:fatal is a subtype of exn and hence will not be caught as part
; of symbolic evaluation.
(struct exn:fatal exn ())
(define (fatal msg) (raise (exn:fatal msg (current-continuation-marks))))
(define (argument-error name expected given)
(format "~a: contract violation\n expected: ~a\n given: ~a"
name expected given))
(define (arguments-error name message . field-value)
(define o (open-output-string))
(fprintf o "~a: ~a" name message)
(let loop ([fvs field-value])
(match fvs
[(list) (get-output-string o)]
[(list f) (fatal (format "arguments-error: missing value after field string ~a" f))]
[(list f v rest ...)
(fprintf o "\n ~a: ~a" f v)
(loop rest)])))
(define (type-error name expected given)
(argument-error name (format "~a" expected) given))
(define (contract-error name contract given)
(argument-error name (format "~a" (contract-name contract)) given))
(define (index-too-large-error who xs idx)
(arguments-error who "index is too large" "index" idx "in" xs))

View File

@ -1,12 +1,15 @@
#lang racket
(require racket/splicing (for-syntax racket/syntax)
syntax/parse/define
(only-in racket/unsafe/ops [unsafe-car car] [unsafe-cdr cdr])
(only-in "merge.rkt" merge merge*)
(only-in "bool.rkt" ! || pc)
(only-in "union.rkt" union)
(only-in "effects.rkt" speculate* location=? location-final-value)
"safe.rkt")
(only-in "merge.rkt" merge merge* merge-same)
(only-in "bool.rkt" ! || &&)
(only-in "union.rkt" union union?)
(only-in "term.rkt" expression)
(only-in "polymorphic.rkt" guarded guarded-test guarded-value ite ite*)
(only-in "equality.rkt" @equal?)
"safe.rkt" "../core/eval.rkt" "../core/store.rkt" "../core/result.rkt")
(provide for/all for*/all guard-apply)
@ -17,15 +20,13 @@
; (for/all ([v0 val0])
; (for/all ([v1 val1])
; expr))
(define-syntax for*/all
(syntax-rules ()
[(_ () expr) expr]
[(_ ([v gv]) expr)
(for/all ([v gv]) expr)]
[(_ ([v0 gv0] [v gv] ...) expr)
(for/all ([v0 gv0])
(for*/all ([v gv] ...) expr))]))
(define-syntax-parser for*/all
#:disable-colon-notation
[(_ () e ...+) (syntax/loc this-syntax (begin e ...))]
[(_ (v0:gv0 v:gv ...) e ...+)
(syntax/loc this-syntax
(for/all (v0:gv0)
(for*/all (v:gv ...) e ...)))])
; This macro takes the following form:
; (for/all ([v val]) expr)
@ -36,74 +37,68 @@
; symbolic reference could point. If the provided
; value is not a symbolic reference, then the expression
; is simply evaluated with v bound to the value itself.
(define-syntax (for/all stx)
(syntax-case stx ()
[(_ ([v val]) expr)
(identifier? #'v)
(syntax/loc stx (let ([proc (lambda (v) expr)])
(define-syntax-parser for/all
[(_ ([v:id val]) e ...+)
(syntax/loc this-syntax
(let ([proc (lambda (v) e ...)])
(match val
[(union gvs) (guard-apply proc gvs)]
[v (proc v)])))]))
[other (proc other)])))]
[(_ ([v:id val #:exhaustive]) e ...+)
#:with ooo (quote-syntax ...)
(syntax/loc this-syntax
(let ([proc (lambda (v) e ...)])
(match val
[(or (? union? sym) (and (expression (or (== ite) (== ite*)) _ ooo) sym))
(guard-apply proc (flatten-guarded sym))]
[other (proc other)])))]
[(_ ([v:id val concrete]) e ...+)
(syntax/loc this-syntax (for/all ([v val concrete @equal?]) e ...))]
[(_ ([v:id val concrete ==]) e ...+)
(syntax/loc this-syntax
(let ([sym val] [=== ==])
(guard-apply
(lambda (v) e ...)
(for/list ([c concrete]) (cons (=== sym c) c)))))])
(define (flatten-guarded v)
(merge-same
(let loop ([guards '()][val v])
(match val
[(expression (== ite) c t e)
(append (loop (cons c guards) t)
(loop (cons (! c) guards) e))]
[(expression (== ite*) gvs ...)
(apply append
(for/list ([gv gvs])
(loop (cons (guarded-test gv) guards)
(guarded-value gv))))]
[(union gvs)
(apply append
(for/list ([gv gvs])
(loop (cons (car gv) guards)
(cdr gv))))]
[_ (list (cons (apply && guards) val))]))))
; Applies the given procedure to each of the guarded values,
; given as guard/value pairs. The application of the procedure
; given as guard/value structures. The application of the procedure
; to each value is done under the value's guard, and so are all
; the state updates performed during the evaluation. The result
; of this procedure is the result of this evaluation process.
; The guard-apply procedure also merges any state updates resulting
; from successful guarded evaluations of proc on the given values.
;
; All given guards are required to be pairwise mutually exclusive,
; and at least one of the guards must always evaluate to true.
(define (guard-apply proc guarded-values)
(define-values (guards outputs states)
(guard-speculate* proc guarded-values))
(when (null? guards)
(assert #f (thunk (error 'for/all "all paths infeasible"))))
(when (ormap pair? states)
(merge-states guards states))
(apply merge* (map cons guards outputs)))
; Speculatively executes the given procedure on the provided
; guarded values and returns three lists---guards, outputs,
; and states---of equal length. For each input pair (cons g v)
; in guarded-values for which (proc v) terminates without an
; error, there is an index i such that the ith element of the
; guards list is g, the ith element of the outputs list is
; (proc v), and the ith element of the states list is the list
; of all states updates that were performed when executing (proc v).
; Note that all state update objects for the ith execution are
; are unique according to location=?, but two state updates in
; different executions may be location=?. (That is, proc would
; update the same location if it were called with two different
; values.)
(define (guard-speculate* proc guarded-values)
(for/fold ([guards '()] [outputs '()] [states '()]) ([gv guarded-values])
(define guard (car gv))
(define val (cdr gv))
(define-values (output state)
(speculate*
(parameterize ([pc guard])
(proc val))))
(cond [state (values (cons guard guards) (cons output outputs) (cons state states))]
[else (assert (! guard) (thunk (error 'for/all "all paths infeasible")))
(values guards outputs states)])))
; At most one of the given guards may be true under any model.
(define (guard-apply proc guarded-values [guard-of car] [value-of cdr])
; If any of the guarded-values has #t as its guard, it's executed
; directly, since all the guards must be #f under all models.
(define gv (findf (lambda (gv) (eq? (guard-of gv) #t)) guarded-values))
(cond
[gv (proc (value-of gv))]
[else (eval-guarded! (map guard-of guarded-values)
(map (lambda (gv) (thunk (proc (value-of gv)))) guarded-values))]))
; Given a list of n guards and their corresponding lists of
; state-update objects, performs an n-way merge of all updates
; to memory locations that are encapsulated in those states.
(define (merge-states guards states)
(define locations (remove-duplicates (apply append states) location=?))
(define guarded-states (append-map (lambda (g sts) (map (curry cons g) sts)) guards states))
(define max-guards-per-location (length guards))
(define (merge-procedure gss)
(if (= (length gss) max-guards-per-location)
(lambda (pre post) (apply merge* gss))
(lambda (pre post) (apply merge* (cons (! (apply || (map car gss))) pre) gss))))
(for ([loc locations])
(loc (merge-procedure
(for/list ([gs guarded-states]
#:when (location=? loc (cdr gs)))
(cons (car gs) (location-final-value (cdr gs))))))))

View File

@ -5,7 +5,7 @@
"term.rkt" "bool.rkt" "safe.rkt" "union.rkt" "equality.rkt" "merge.rkt"
(only-in "procedure.rkt" @procedure?))
(provide (rename-out [fv-stx fv]) @fv? fv? fv-cond fv-else fv-type
(provide (rename-out [fv-stx fv]) @fv? fv? fv-type
~> function function? function-domain function-range)
#|-----------------------------------------------------------------------------------|#
@ -14,10 +14,8 @@
; is a non-empty list of primitive-solvable? types, and its range is a primitive-solvable?
; type.
;
; The only values that have function types are instances of the fv struct. This struct
; represents functions that are essentially lookup tables. Each fv has a set of 'cond'
; cases, which map specific inputs to outputs. All unmapped inputs are mapped to the
; 'else' value. An fv value is a procedure and can be directly applied to values
; The only values that have function types are instances of the fv struct.
; An fv value is a procedure and can be directly applied to values
; (symbolic, concrete, or a mix of the two).
#|-----------------------------------------------------------------------------------|#
@ -66,7 +64,9 @@
#:methods gen:solvable
[(define/generic generic-solvable-default solvable-default)
(define (solvable-default self)
(fv null (generic-solvable-default (function-range self)) self))
(fv self (procedure-reduce-arity
(lambda args (generic-solvable-default (function-range self)))
(length (function-domain self)))))
(define (solvable-domain self) (function-domain self))
(define (solvable-range self) (function-range self))]
#:methods gen:custom-write
@ -82,24 +82,22 @@
[(d0 d1 . rest) (function `(,d0 ,d1 ,@(drop-right rest 1)) (last rest))]))
; Represents a function value.
(struct fv (cond else type λ)
(struct fv (type λ)
#:property prop:procedure
[struct-field-index λ]
#:methods gen:typed
[(define (get-type self) (fv-type self))]
#:methods gen:custom-write
[(define (write-proc self port m)
(fprintf port "(fv ~a ~a ~a)" (fv-cond self) (fv-else self) (fv-type self)))])
(fprintf port "(fv ~a)" (fv-type self)))])
(define (make-fv ios o type)
(fv ios o type
(define (make-fv type proc)
(fv type
(procedure-reduce-arity
(lambda args
(let* ([args (for/list ([a args] [t (function-domain type)])
(type-cast t a))]
[parts (for/list ([io ios])
(cons (@equal? (car io) args) (cdr io)))])
(apply merge* (cons (! (apply || (map car parts))) o) parts)))
(apply proc
(for/list ([a args] [t (function-domain type)])
(type-cast t a))))
(length (function-domain type)))))
(define-match-expander fv-stx

View File

@ -52,12 +52,12 @@
(define-syntax (define/lift stx)
(syntax-case stx (: :: ->)
[(_ (id0 id ...) :: contracted? -> rosette-type?)
(or (identifier? #'contracted) (raise-argument-error "identifier?" #'contracted?))
(or (identifier? #'contracted?) (raise-argument-error "identifier?" #'contracted?))
#'(begin
(define/lift id0 :: contracted? -> rosette-type?)
(define/lift id :: contracted? -> rosette-type?) ...)]
[(_ id :: contracted? -> rosette-type?) ; repeated from (_ id : contracted? -> rosette-type?) - params don't work
(or (identifier? #'contracted) (raise-argument-error "identifier?" #'contracted?))
(or (identifier? #'contracted?) (raise-argument-error "identifier?" #'contracted?))
#`(define (#,(lift-id #'id) val)
(if (contracted? val)
(id val)

View File

@ -1,10 +1,9 @@
#lang racket
(require (only-in rnrs/base-6 assert)
(only-in racket/unsafe/ops [unsafe-car car] [unsafe-cdr cdr])
"term.rkt" "union.rkt" "bool.rkt")
(require (only-in racket/unsafe/ops [unsafe-car car] [unsafe-cdr cdr])
"term.rkt" "union.rkt" "bool.rkt" "reporter.rkt")
(provide merge merge* unsafe-merge*)
(provide merge merge* unsafe-merge* merge-same)
(define (merge b x y)
(match* (b x y)
@ -32,71 +31,59 @@
(do-merge* #t ps))
(define-syntax-rule (do-merge* force? ps)
(match (compress force? (simplify ps))
[(list (cons g v)) (assert (not (false? g))) v]
(let ([simp (simplify ps)])
((current-reporter) 'merge (length simp))
(match (compress force? simp)
[(list (cons g v)) v]
[(list _ (... ...) (cons #t v) _ (... ...)) v]
[vs (apply union vs)]))
[vs (apply union vs)])))
(define (guard-&& a b)
(match b
[(expression (== @&&) c ...) (apply && a c)]
[_ (&& a b)]))
(define (guard g vs)
(filter-map (lambda (v)
(let ([gv (guard-&& g (car v))])
(and gv (cons gv (cdr v)))))
vs))
(define (guard g gvs)
(for*/list ([gv gvs]
[gg (in-value (&& g (car gv)))]
#:when gg)
(cons gg (cdr gv))))
(define (simplify ps)
(let loop ([ps ps] [out '()])
(match ps
[(list) out]
[(list (and (cons #t v) p) _ ...)
[(list _ ... (and (cons #t _) p) _ ...)
(list p)]
[(list (cons #f _) rest ...)
(loop rest out)]
[(list (cons g (union (and (not (? null?)) vs))) rest ...)
(loop rest (append (guard g vs) out))]
[(list p rest ...)
(loop rest (cons p out))])))
[_ (for/fold ([out '()]) ([p ps])
(match p
[(cons #f _) out]
[(cons g (union (and (not (? null?)) gvs)))
(append (guard g gvs) out)]
[_ (cons p out)]))]))
(define (group ps)
(let ([types (remove-duplicates (for/list ([p ps]) (type-of (cdr p))))])
(for*/list ([t types] [p ps] #:when (equal? t (type-of (cdr p)))) p)))
(define (type-of-value gv) (type-of (cdr gv)))
(define (compress force? ps)
(match ps
[(list _) ps]
[(list (cons g (app type-of t)) (cons h (app type-of t)))
[(list (cons _ (app type-of t)) (cons _ (app type-of t)))
(type-compress t force? (merge-same ps))]
[(list _ _) ps]
[_ (let loop ([ps (group ps)] [type #f] [acc '()])
;(printf "compress ~a ~a ~a\n" ps type acc)
(match ps
[(list)
(append-map (lambda (group)
(type-compress (type-of (cdar group))
[_ (append-map
(lambda (group)
(type-compress
(type-of (cdar group))
force?
(merge-same group)))
acc)]
[(list (and (cons _ (app type-of (== type))) p) rest ...)
(loop rest type (cons (cons p (car acc)) (cdr acc)))]
[(list p rest ...)
(loop rest (type-of (cdr p)) (cons (list p) acc))]))]))
(group-by type-of-value ps))]))
(define (merge-same ps)
;(printf "merge ~a\n" ps)
(match ps
[(or (list) (list _)) ps]
[(list (cons g v) (cons h u)) (if (eq? v u) (list (cons (|| g h) v)) ps)]
[_ (let loop ([ps ps] [out '()])
(if (null? ps)
out
(match-let*-values
([((cons g v)) (car ps)]
[((list (cons h _) ...) rest) (partition (compose (curry eq? v) cdr) (cdr ps))]
[(g) (apply || g h)])
(if (equal? g #t)
[(list (cons g v) (cons h u))
(if (eq? v u) (list (cons (|| g h) v)) ps)]
[_ (let loop ([ps (group-by cdr ps eq?)] [out '()])
(match ps
[(list) out]
[(list (list gv) rest ...)
(loop rest (cons gv out))]
[(list group rest ...)
(let ([g (apply || (map car group))]
[v (cdar group)])
(if (eq? g #t)
(list (cons g v))
(loop rest (cons (cons g v) out))))))]))
(loop rest (cons (cons g v) out))))]))]))

View File

@ -7,9 +7,7 @@
(provide @number? @positive? @negative? @zero? @even? @odd?
@add1 @sub1 @sgn @truncate @floor @ceiling @min @max
@exact->inexact @inexact->exact @expt
;@sqrt @bitwise-not @bitwise-and @bitwise-ior @bitwise-xor
;@<< @>> @>>> @bitwise-bit-set? @bitwise-bit-field
)
extreme)
(define (@number? v) (or (number? v) (@real? v)))
(define (@positive? x) (@> x 0))

View File

@ -152,7 +152,7 @@
(match* ((car p) (cdr p))
[(a (expression (== ite) a x _)) (cons a x)]
[(a (expression (== ite) (expression (== @!) a) _ x)) (cons a x)]
[((expression (== @!) a) (expression (== ite) a _ x)) (cons a x)]
[((and (expression (== @!) a) !a) (expression (== ite) a _ x)) (cons !a x)]
[(_ _) p]))

View File

@ -3,7 +3,7 @@
(require
racket/provide
(for-syntax racket/syntax (only-in "lift.rkt" with@))
(only-in "type.rkt" define-lifted-type typed? get-type subtype? type-applicable? @any/c)
(only-in "type.rkt" define-lifted-type type-cast typed? get-type subtype? type-applicable? @any/c)
(only-in "bool.rkt" || @false?)
(only-in "union.rkt" union union? in-union-guards union-filter union-guards)
(only-in "safe.rkt" assert argument-error)
@ -75,8 +75,8 @@
[(union gvs) (guard-apply (curryr procedure-rename name) gvs)]
[(? procedure?) (procedure-rename proc name)]))
(define (@negate f)
(unless (@procedure? f) (raise-argument-error 'negate "procedure?" f))
(define (@negate p)
(define f (type-cast @procedure? p 'negate))
(let-values ([(arity) (procedure-arity f)] [(_ kwds) (procedure-keywords f)])
(case (and (null? kwds) arity) ; optimize some simple cases
[(0) (lambda () (@false? (f)))]

View File

@ -295,7 +295,7 @@
(define T*->integer? (const @integer?))
(define (undefined-for-zero-error name)
(thunk (raise-arguments-error name "undefined for 0")))
(arguments-error name "undefined for 0"))
(define-syntax-rule (define-lifted-int-operator @op $op op)
(define-operator @op

View File

@ -1,44 +1,74 @@
#lang racket
(require (only-in "forall.rkt" for/all for*/all)
"term.rkt" "union.rkt")
"term.rkt" "union.rkt" "result.rkt")
(provide type? solvable? @any/c type-of type-cast for/all for*/all
term? constant? expression?
term expression constant
term-type term=?
term->datum clear-terms! term-cache
term-type term=? term->datum
terms terms-count terms-ref with-terms clear-terms! gc-terms!
union? union union-contents union-guards union-values
union-filter in-union in-union* in-union-guards in-union-values
symbolics)
(struct-out normal) (struct-out failed) result? result-value result-state
symbolics concrete? symbolic?)
(define (term=? s0 s1)
(and (term? s0) (term? s1) (equal? s0 s1)))
(define (symbolics vs)
(match vs
[(list (? constant?) ...) vs]
[_ (let ([cache (make-hash)])
(let loop ([vs vs])
(hash-ref!
cache
vs
(lambda ()
(remove-duplicates
(match vs
[(list (? constant?) ...) (remove-duplicates vs)]
[(? constant?) (list vs)]
[_ (let ([terms (mutable-set)]
[objs (mutable-set)]
[result '()])
(let loop ([datum vs])
(if (term? datum)
(let ([id (term-id datum)])
(unless (set-member? terms id)
(set-add! terms id)
(match datum
[(expression _ x ...) (for-each loop x)]
[(? constant?) (set! result (cons datum result))])))
(unless (set-member? objs datum)
(set-add! objs datum)
(match datum
[(union (list (cons guard value) ...))
(append (append-map loop guard) (append-map loop value))]
[(expression _ x ...) (append-map loop x)]
[(? constant? v) (list v)]
(for-each loop guard) (for-each loop value)]
[(box v) (loop v)]
[(? list?) (append-map loop vs)]
[(cons x y) (append (loop x) (loop y))]
[(vector v ...) (append-map loop v)]
[(? list?) (for-each loop datum)]
[(cons x y) (loop x) (loop y)]
[(vector v ...) (for-each loop v)]
[(and (? typed?) (app get-type t))
(match (type-deconstruct t vs)
[(list (== vs)) '()]
[components (append-map loop components)])]
[_ '()]))))))]))
(match (type-deconstruct t datum)
[(list (== datum)) (void)]
[components (for-each loop components)])]
[_ (void)]))))
(reverse result))]))
(define (concrete? val)
(define objs (mutable-set))
(let all-concrete? ([val val])
(and (not (term? val))
(not (union? val))
(or
(set-member? objs val)
(begin
(set-add! objs val)
(match val
[(box v) (all-concrete? v)]
[(? list?) (for/and ([v val]) (all-concrete? v))]
[(cons x y) (and (all-concrete? x) (all-concrete? y))]
[(? vector?) (for/and ([v val]) (all-concrete? v))]
[(and (? typed?) (app get-type t))
(match (type-deconstruct t val)
[(list (== val)) #t]
[components (for/and ([v components]) (all-concrete? v))])]
[_ #t]))))))
(define (symbolic? val) (not (concrete? val)))
(define (term->datum val)
(let convert ([val val] [cache (make-hash)])

View File

@ -0,0 +1,14 @@
#lang racket
(provide (all-defined-out))
; The reporter is called when "interesting"
; events happen during symbolic execution; for example,
; when a merge occurs or a new term is created.
(define current-reporter
(make-parameter
void
(lambda (new-reporter)
(unless (procedure? new-reporter)
(raise-argument-error 'current-reporder "procedure?" new-reporter))
new-reporter)))

View File

@ -0,0 +1,17 @@
#lang racket
(provide (struct-out normal) (struct-out failed)
result? result-value result-state)
; Represents the result of symbolic evaluation,
; which includes an output value and a representation
; of some aspect of the symbolic state.
(struct result (value state) #:transparent)
; Represents the result of a normally terminated evaluation.
(struct normal result () #:transparent)
; Represents the result of an evaluation that resulted in
; an exn:fail? exception being raised. In this case,
; the result-value field stores the exception that was raised.
(struct failed result () #:transparent)

View File

@ -1,57 +1,38 @@
#lang racket
(require (only-in "type.rkt" type-cast)
"bool.rkt"
racket/performance-hint)
(require "bool.rkt" "exn.rkt")
(provide argument-error arguments-error type-error contract-error index-too-large-error
assert assert-some assert-|| assert-bound assert-arity-includes)
(begin-encourage-inline
(define (arguments-error name message . field-value)
(thunk (apply raise-arguments-error name message field-value)))
(define (argument-error name expected given)
(thunk (raise-argument-error name expected given)))
(define (type-error name expected given)
(argument-error name (format "~a" expected) given))
(define (contract-error name contract given)
(argument-error name (format "~a" (contract-name contract)) given))
(define (index-too-large-error who xs idx)
(arguments-error who "index is too large" "index" idx "in" xs))
)
(define-syntax (assert stx)
(syntax-case stx ()
[(_ expr err-thunk) (syntax/loc stx (@assert expr err-thunk))]
[(_ expr) (syntax/loc stx (@assert expr #f))]))
[(_ expr) (syntax/loc stx ($assert expr #f))]
[(_ expr msg) (syntax/loc stx ($assert expr msg))]))
(define-syntax assert-some
(syntax-rules ()
[(_ expr #:unless size err-thunk)
[(_ expr #:unless size msg)
(let* ([val expr])
(unless (= size (length val))
(assert (apply || (map car val)) err-thunk))
(assert (apply || (map car val)) msg))
val)]
[(_ expr #:unless size)
(assert-some expr #:unless size #f)]
[(_ expr err-thunk)
[(_ expr msg)
(let* ([val expr])
(assert (apply || (map car val)) err-thunk)
(assert (apply || (map car val)) msg)
val)]
[(_ expr)
(assert-some expr #f)]))
(define-syntax assert-||
(syntax-rules ()
[(_ expr #:unless size err-thunk)
[(_ expr #:unless size msg)
(let ([val expr])
(unless (= size (length val))
(assert (apply || val) err-thunk)))]
(assert (apply || val) msg)))]
[(_ expr #:unless size) (assert-|| expr #:unless size #f)]))

181
rosette/base/core/store.rkt Normal file
View File

@ -0,0 +1,181 @@
#lang racket
(require "result.rkt" "merge.rkt")
(provide with-store store! merge-stores!
location? location-base location-offset
location-ref location-set!)
; The current-store parameter contains a store that
; maps (abstract) memory locations to values. Each mapped
; location identifies a storage cell that has been the mutated
; via a store! call in the dynamic extent of a call to
; with-store. The store maps each such location to the value
; that was stored at that location before the current call
; to with-store.
(define current-store (make-parameter #f))
; A store maps abstract memory locations to their initial values.
; An abstract memory location identifies a storage cell that holds
; a single value; locations consist of a base object (e.g., a vector)
; and an offset value (e.g., the index 0) that identifies a
; storage cell within that object.
;
; A store uses a refs set, as returned by make-refs, to keep track
; of the locations that have been mutated via store! calls.
; The initial value of each mutated location is held in the store's
; vals list. This list maps locations to the values they held prior
; to the current call to with-store.
(struct store (refs [vals #:mutable]) #:transparent)
; Returns an empty store.
(define (make-store) (store (make-refs) (list)))
; Returns an empty set of base/offset pairs.
(define (make-refs) (make-hasheq))
; Adds the given base/offset pair to rs if not
; already present. Returns #t if rs changed as
; a result of this operation; otherwise returns #f.
(define (refs-add! rs base offset)
(define bits (hash-ref rs base 0))
(and (not (bitwise-bit-set? bits offset))
(hash-set! rs base (bitwise-ior bits (arithmetic-shift 1 offset)))
#t))
; Extends the store s with a mapping from the location
; (loc base offset getter setter) to its current value,
; unless s already contains a mapping for this location.
(define (store-add! s base offset getter setter)
(when (refs-add! (store-refs s) base offset)
(let ([l (location base offset getter setter)]
[vals (store-vals s)])
(set-store-vals! s (cons (cons l (location-ref l)) vals)))))
; Performs the mutation to the storage
; cell at the location (loc base offset getter setter),
; and if this cell has not been mutated before, its
; initial value is added to current-store.
; The getter and setter procedures should read / write
; the cell's value when applied to its base and offset.
(define (store! base offset val getter setter)
(let ([s (current-store)])
(when s
(store-add! s base offset getter setter)))
;(printf "store! ~a ~a ~a ~a ~a, ~a\n" base offset val getter setter (current-store))
(setter base offset val))
; Returns true if the store s is empty.
(define (store-empty? s)
(zero? (length (store-vals s))))
; Represents the location of a single mutable storage cell.
; A cell location consists of a base object (e.g., a vector)
; and an offset value (e.g., 0) that identifies a
; storage cell within that object. Locations
; also include getter and setter procedures that can be
; used to read from and write to the referenced cell.
; Two locations are equal? iff their base and offset
; are both eq? to one another.
(struct location (base offset accessor mutator)
#:transparent
#:methods gen:equal+hash
[(define (equal-proc l1 l2 rec-equal?)
(and (eq? (location-base l1) (location-base l2))
(eq? (location-offset l1) (location-offset l2))))
(define (hash-proc l rec-equal-hash)
(equal-hash-code (cons (eq-hash-code (location-base l)) (eq-hash-code (location-offset l)))))
(define (hash2-proc l rec-equal-hash2)
(equal-secondary-hash-code (cons (eq-hash-code (location-base l)) (eq-hash-code (location-offset l)))))])
; Returns the current value stored at the location l.
(define (location-ref l)
((location-accessor l) (location-base l) (location-offset l)))
; Stores the value v at the location l.
(define (location-set! l v)
(store! (location-base l) (location-offset l) v (location-accessor l) (location-mutator l)))
; Rollbacks the contents of all mutated storage cells to their initial
; values, as given in (current-store), and raises the exception e.
; The current-store is assumed to contain the values that
; mutated cells held before the current call to with-store.
; This procedure can be called only in the dynamic extent of a
; with-store call.
(define (rollback-exn! e)
;(printf "exn: ~a\n" e)
(for ([lv (store-vals (current-store))])
(match-define (cons (location base offset _ setter) init) lv)
(setter base offset init))
(raise e))
; Rollbacks the contents of all mutated storage cells to their initial
; values, as given in (current-store), and returns a list of pairs
; that maps a reference to each mutated cell to its current value.
; The current-store is assumed to contain the values that
; mutated cells held before the current call to with-store.
; This procedure can be called only in the dynamic extent of a
; with-store call.
(define (rollback-capture!)
;(printf "capture: ~a\n" (store-vals (current-store)))
(for/list ([lv (store-vals (current-store))])
(match-define (cons (location base offset getter setter) init) lv)
(define fin (getter base offset))
(setter base offset init)
(cons (car lv) fin)))
; The with-store form takes as input an expression, evaluates it,
; and reverts each mutated memory location to its pre-state
; (i.e., the value it held before the call to with-store).
;
; If the evaluation of the body terminates normally, (with-store body)
; outputs a result (normal v s) where v is the value computed by the body,
; and s is an association list that maps each mutated location? to its
; post-state (i.e., the value it held after the evaluation of the body).
; In essence, evaluating the body in the current environment has the
; same effect on memory as evaluating (with-store body) and then setting
; the returned memory locations to their post-state value.
;
; If the evaluation of the body terminates abnormally with an exn:fail?
; exception, with-store reverts all mutated locations to their pre-state
; and re-raises the same exception.
(define-syntax-rule (with-store body)
(parameterize ([current-store (make-store)])
(with-handlers ([exn:fail? rollback-exn!])
(let ([out body])
(normal out (rollback-capture!))))))
; Takes as input a list of n guards and a list of n stores, where
; each store is a list of location/value pairs. For each location l
; occurring in the stores, merge-store mutates l to contain the value
; m = (merge* ... (cons gi vi) ...), where gi = guards[i] and
; vi = stores[i][l] if stores[i] has a binding for l; otherwise,
; vi = (location-ref l). The procedure assumes that no store contains a
; duplicate binding for any location.
;
; This store merging procedure is correct under the assumption that
; (1) the guards are disjoint under all models (i.e., at most one
; is ever true), and (2) the verification conditions force at least
; one guard to be true under all models that satisfy both the
; asserts and the assumes.
(define (merge-stores! guards stores)
(match stores
[(list (list) ...) (void)] ; Nothing to merge.
[(list s) (for ([lv s]) ; If given only one store, just apply its effects
(location-set! (car lv) (cdr lv)))] ; since its guard must be true under the current spec.
[_ (define hash-stores (map make-hash stores))
(for ([lv (remove-duplicates (apply append stores) equal? #:key car)])
(define loc (car lv))
(define val (location-ref loc))
(location-set! loc
(apply merge*
(for/list ([g guards] [hs hash-stores])
(cons g (if (hash-has-key? hs loc)
(hash-ref hs loc)
val))))))]))

View File

@ -1,31 +1,38 @@
#lang racket
(require racket/syntax (for-syntax racket racket/syntax) racket/generic "type.rkt")
(require racket/syntax (for-syntax racket racket/syntax syntax/parse)
racket/generic syntax/parse
"type.rkt" "reporter.rkt")
(provide
term-cache clear-terms!
terms terms-count terms-ref with-terms clear-terms! gc-terms!
term? constant? expression?
(rename-out [a-term term] [an-expression expression] [a-constant constant])
(rename-out [a-term term] [an-expression expression] [a-constant constant] [term-ord term-id])
term-type term<? sublist? @app
define-operator operator? operator-unsafe
(all-from-out "type.rkt"))
#|-----------------------------------------------------------------------------------|#
; Term cache stores terms for the purposes of partial cannonicalization.
; The current-terms cache stores terms for the purposes of partial cannonicalization.
; That is, it ensures that no syntactically identical terms are created.
; It also assigns unique IDs (creation timestamps) to terms. These IDs
; are never reused, and they are used to impose an ordering on the children
; The current-index parameter is used to assign unique IDs (creation timestamps) to terms.
; These IDs are never reused, and they are used to impose an ordering on the children
; of expressions with commutative operators.
#|-----------------------------------------------------------------------------------|#
(define term-cache (make-parameter (make-hash)))
(define term-count (make-parameter 0))
; Clears the entire term-cache if invoked with #f (default), or
;; Initialize with #f so that the hash table cooperates with garbage collector.
;; See #247
(define current-terms (make-parameter #f))
(current-terms (make-hash))
(define current-index (make-parameter 0))
; Clears the entire term cache if invoked with #f (default), or
; it clears all terms reachable from the given set of leaf terms.
(define (clear-terms! [terms #f])
(if (false? terms)
(hash-clear! (term-cache))
(let ([cache (term-cache)]
(hash-clear! (current-terms))
(let ([cache (current-terms)]
[evicted (list->mutable-set terms)])
(for ([t terms])
(hash-remove! cache (term-val t)))
@ -39,12 +46,70 @@
(set-add! evicted t))
(loop))))))
; Sets the current term cache to a garbage-collected (weak) hash.
; The setting preserves all reachable terms from (current-terms).
(define (gc-terms!)
(unless (hash-weak? (current-terms)) ; Already a weak hash.
(define cache
(impersonate-hash
(make-weak-hash)
(lambda (h k)
(values k (lambda (h k e) (ephemeron-value e #f))))
(lambda (h k v)
(values k (make-ephemeron k v)))
(lambda (h k) k)
(lambda (h k) k)
hash-clear!))
(for ([(k v) (current-terms)])
(hash-set! cache k v))
(current-terms cache)))
; Returns the term from current-terms that has the given contents. If
; no such term exists, failure-result is returned, unless it is a procedure.
; If failure-result is a procedure, it is called and its result is returned instead.
(define (terms-ref contents [failure-result (lambda () (error 'terms-ref "no term for ~a" contents))])
(hash-ref (current-terms) contents failure-result))
; Returns a list of all terms in the current-term scache, in an unspecified order.
(define (terms)
(hash-values (current-terms)))
; Returns the size of the current-terms cache.
(define (terms-count)
(hash-count (current-terms)))
; Evaluates expr with (terms) set to terms-expr, returns the result, and
; restores (terms) to its old value. If terms-expr is not given, it defaults to
; (terms), so (with-terms expr) is equivalent to (with-terms (terms) expr).
(define-syntax (with-terms stx)
;; Parameterize with #f so that the hash table cooperates with garbage collector.
;; See #247
(syntax-parse stx
[(_ expr)
#'(let ([orig-terms (current-terms)])
(parameterize ([current-terms #f])
(current-terms (hash-copy orig-terms))
expr))]
[(_ terms-expr expr)
#'(let ([orig-terms (current-terms)])
(parameterize ([current-terms #f])
(current-terms (hash-copy-clear orig-terms))
(let ([ts terms-expr]
[cache (current-terms)])
(for ([t ts])
(hash-set! cache (term-val t) t))
expr)))]))
#|-----------------------------------------------------------------------------------|#
; The term structure defines a symbolic value, which can be a variable or an expression.
; The val field of a constant is its unique identifier, and it can be anything. The val
; field of an expression is a list, in which the first argument is always a function.
; That function can be interpreted (that is, an operator), or uninterpreted (that is,
; its interpretation is determined by the solver).
; its interpretation is determined by the solver). Terms are totally ordered and a
; subterm is guaranteed to be term<? than its parent.
#|-----------------------------------------------------------------------------------|#
(struct term
(val ; (or/c any/c (cons/c function? (non-empty-listof any/c)))
@ -52,6 +117,7 @@
ord) ; integer?
#:methods gen:typed
[(define (get-type v) (term-type v))]
#:property prop:custom-print-quotable 'never
#:methods gen:custom-write
[(define (write-proc self port mode)
(fprintf port "~a" (term->string self)))])
@ -66,13 +132,21 @@
(define (term<? s1 s2) (< (term-ord s1) (term-ord s2)))
(define-syntax-rule (make-term term-constructor args type rest ...)
(let ([val args])
(or (hash-ref (term-cache) val #f)
(let* ([ord (term-count)]
[out (term-constructor val type ord rest ...)])
(term-count (add1 ord))
(hash-set! (term-cache) val out)
out))))
(let ([val args]
[ty type])
(define cached (hash-ref (current-terms) val #f))
(cond
[cached
(unless (equal? (term-type cached) ty)
(error 'define-symbolic "type should remain unchanged"))
cached]
[else
(define ord (current-index))
(define out (term-constructor val ty ord rest ...))
(current-index (add1 ord))
((current-reporter) 'new-term out)
(hash-set! (current-terms) val out)
out])))
(define (make-const id t)
(unless (and (type? t) (solvable? t))
@ -123,6 +197,8 @@
(struct operator (identifier range safe unsafe)
#:property prop:procedure
(struct-field-index safe)
#:property prop:object-name
(struct-field-index identifier)
#:methods gen:custom-write
[(define (write-proc self port mode)
(fprintf port "~a" (id->string (operator-identifier self))))])

View File

@ -43,8 +43,8 @@
[type-cast type val [caller]] ; (-> type? any/c symbol? any/c)
[type-name type] ; (-> type? symbol?)
[type-applicable? type] ; (-> type? boolean?)
[type-eq? type u v] ; (-> type? (-> any/c any/c @boolean?)))
[type-equal? type u v] ; (-> type? (-> any/c any/c @boolean?)))
[type-eq? type u v] ; (-> type? any/c any/c @boolean?)
[type-equal? type u v] ; (-> type? any/c any/c @boolean?)
[type-compress type force? ps] ; (-> type? (listof (cons @boolean? any/c)) (listof (cons @boolean? any/c)))
[type-construct type vals] ; (-> type? (listof any/c) any/c)
[type-deconstruct type val]) ; (-> type? any/c (listof any/c))

View File

@ -1,6 +1,6 @@
#lang racket
(require "term.rkt")
(require "term.rkt" "reporter.rkt")
(provide union? (rename-out [a-union union])
union-contents union-type union-guards union-values union-filter
@ -20,25 +20,24 @@
[(define (get-type self) (union-type self))]
#:methods gen:custom-write
[(define (write-proc self port mode)
(fprintf port "{")
(fprintf port "(union")
(case mode
[(#t #f)
(fprintf port "~a:~a" (equal-hash-code self) (length (union-contents self)))]
(fprintf port " #:size ~a #:hash ~a" (length (union-contents self)) (equal-hash-code self))]
[else
(let ([vs (union-contents self)])
(unless (null? vs)
(parameterize ([error-print-width (max 4 (quotient (error-print-width) (* 2 (length vs))))])
(fprintf-entry port (car vs) mode)
(for ([v (cdr vs)])
(for ([v vs])
(fprintf port " ")
(fprintf-entry port v mode)))))])
(fprintf port "}"))])
(fprintf port ")"))])
(define (fprintf-entry port p mode)
(fprintf port "[")
(print (car p) port mode)
(fprintf port "~a" (car p))
(fprintf port " ")
(print (cdr p) port mode)
(fprintf port "~a" (cdr p))
(fprintf port "]"))
@ -51,17 +50,9 @@
#:property prop:procedure [struct-field-index procedure])
(define (make-union . vs)
((current-reporter) 'new-union (length vs))
(match vs
[(list) nil]
[(list (and c1 (cons g1 v1) (and c2 (cons g2 v2))))
(let ([vs (if (term<? g1 g2) vs (list c2 c1))]
[t (type-of v1 v2)])
(cond [(procedure? v1)
(λunion vs t (type-compress (lifted-type procedure?) #t (if (procedure? v2) vs (list c1))))]
[(procedure? v2)
(λunion vs t (type-compress (lifted-type procedure?) #t (list c2)))]
[else
(union vs t)]))]
[_
(let ([vs (sort vs term<? #:key car)]
[t (apply type-of (map cdr vs))])

View File

@ -1,27 +1,12 @@
#lang racket
(require "../core/effects.rkt"
(require "../core/eval.rkt" "../core/store.rkt" "../core/result.rkt"
"../core/term.rkt" "../core/equality.rkt"
"../core/merge.rkt" "../core/bool.rkt")
(provide @if @and @or @not @nand @nor @xor @implies
@unless @when @cond @case else)
; Symbolic conditions are handled by speculatively executing both branches,
; and then merging their results and updates to state (if any). When a branch is
; executed speculatively, its state mutations are captured and then undone.
; The result of the capture is a closure that can be used with a merging
; procedure to selectively re-apply the updates. If an error is thrown
; during speculation, all updates are undone, but they are not captured
; (since the branch is infeasible). After both branches have been speculatively
; executed, their results and updates to state are merged using the merge function.
;
; Speculative execution of either branch is guarded by the path condition, stored
; in the pc parameter. Parameterizing pc with a new value coinjoins that
; value with the current path condition. If the result of the conjunction is false,
; indicating that the branch is infeasible, an error is thrown, and the branch is
; not executed. The error is captured by the speculate form and later handled by
; the merge function.
(define-syntax (@if stx)
(syntax-case stx ()
[(_ test-expr then-expr else-expr)
@ -31,28 +16,10 @@
(thunk else-expr)))]))
(define (branch-and-merge test-expr then-branch else-branch)
(define test (! (@false? test-expr)))
(define test (@true? test-expr))
(cond [(eq? test #t) (then-branch)]
[(eq? test #f) (else-branch)]
[else
(let-values ([(then-val then-state) (speculate (parameterize ([pc test]) (then-branch)))]
[(else-val else-state) (speculate (parameterize ([pc (! test)]) (else-branch)))])
(cond [(and then-state else-state) ; both branches feasible
(then-state (lambda (pre post-then) (merge test post-then pre)))
(else-state (lambda (post-then post-else) (merge test post-then post-else)))
(merge test then-val else-val)]
[then-state ; only then branch feasible
(@assert test "both branches infeasible")
(then-state select-post)
then-val]
[else-state ; only else branch feasible
(@assert (! test) "both branches infeasible")
(else-state select-post)
else-val]
[else ; neither branch feasible
(@assert #f "both branches infeasible")]))]))
(define (select-post pre post) post)
[else (eval-guarded! (list test (! test)) (list then-branch else-branch))]))
(define-syntax (@and stx)
(syntax-case stx ()
@ -82,7 +49,7 @@
[(_ expr ...) (syntax/loc stx (@not (@and expr ...)))]))
(define (@xor a b)
(merge a (merge b #f a) b))
(@if a (@if b #f a) b))
(define-syntax (@unless stx)
(syntax-case stx ()
@ -103,22 +70,16 @@
(define-syntax (@case stx)
(syntax-case stx (else)
[(_ expr) (syntax/loc stx (@case expr [else (void)]))]
[(_ expr [else else-expr ...]) (syntax/loc stx (begin expr else-expr ...))]
[(_ expr
[(then-val0 ...) then-expr0 ...]
[(then-val ...) then-expr ...] ...
[else else-expr ...])
(syntax/loc stx
(let ([tmp expr])
(@cond [(@or (@equal? tmp (quote then-val0)) ...) then-expr0 ...]
[(@or (@equal? tmp (quote then-val)) ...) then-expr ...] ...
(@cond [(@or (@equal? tmp (quote then-val)) ...) then-expr ...] ...
[else else-expr ...])))]
[(_ expr
[(then-val0 ...) then-expr0 ...]
[(then-val ...) then-expr ...] ...)
(syntax/loc stx
(@case expr
[(then-val0 ...) then-expr0 ...]
[(then-val ...) then-expr ...] ...
[else (void)]))]))

View File

@ -1,85 +1,48 @@
#lang racket
(require (for-syntax racket)
"../util/array.rkt" "../core/term.rkt" "state.rkt")
(require syntax/parse (for-syntax syntax/parse racket)
"../core/term.rkt")
(provide define-symbolic define-symbolic*)
#|--------------define forms--------------|#
(define-for-syntax (module-or-top? . args)
(case (syntax-local-context)
[(module top-level) #t]
[else #f]))
(define-for-syntax (static? k)
(with-handlers ([exn:fail? module-or-top?])
(natural? (eval k))))
(define-syntax (define-symbolic stx)
(syntax-case stx ()
[(_ var type)
(identifier? #'var)
(syntax/loc stx (define var (constant #'var type)))]
[(_ var type [ k ... ])
(and (identifier? #'var) (implies (identifier? #'type) (identifier-binding #'type)))
(define-array stx #'var #'type #'(k ...))]
[(_ v ... type)
(andmap identifier? (syntax->list #'(v ...)))
(syntax/loc stx (define-values (v ...) (values (constant #'v type) ...)))]))
(syntax-parse stx
[(_ var:id type)
#'(define var (constant #'var type))]
[(_ var:id type #:length k)
#:declare k (expr/c #'natural? #:name "length argument")
#:fail-unless (static? #'k) "expected a natural? for #:length"
#'(define var
(for/list ([i k.c])
(constant (list #'var i) type)))]
[(_ var:id ...+ type)
#'(begin (define-symbolic var type) ...)]))
(define current-index (make-parameter 0))
(define (index!)
(define idx (current-index))
(current-index (add1 idx))
idx)
(define-syntax (define-symbolic* stx)
(syntax-case stx ()
[(_ [var oracle] type)
(identifier? #'var)
(syntax/loc stx (define var (constant (list #'var (oracle #'var)) type)))]
[(_ var type)
(identifier? #'var)
(syntax/loc stx (define-symbolic* [var (current-oracle)] type))]
[(_ var type [ k ... ])
(and (identifier? #'var) (implies (identifier? #'type) (identifier-binding #'type)))
(syntax/loc stx (define var (reshape (list k ...) (for/list ([i (in-range (* k ...))])
(syntax-parse stx
[(_ var:id type)
#'(define var (constant (list #'var (index!)) type))]
[(_ var:id type #:length k)
#:declare k (expr/c #'natural? #:name "length argument")
#'(define var
(for/list ([i k.c])
(define-symbolic* var type)
var))))]
[(_ v0 v ... type)
(and (identifier? #'v0) (andmap identifier? (syntax->list #'(v ...))))
(syntax/loc stx (begin (define-symbolic* v0 type) (define-symbolic* v type) ...))]
))
#|--------------helper functions--------------|#
(module util racket
(require racket/syntax)
(provide var-ids indices)
(define (var-ids id-stx dim-spec [separator '@])
(for/list ([idx (apply indices (dims dim-spec))])
(format-id id-stx "~a~a~a" id-stx separator idx #:source id-stx)))
(define (dims spec)
(begin0 spec
(for ([dim spec])
(unless (and (integer? dim) (>= dim 0))
(error 'define-symbolic "expected a non-negative integer, given ~a" dim)))))
(define (indices . k)
(cond [(null? k) k]
[(null? (cdr k)) (build-list (car k) (lambda (i) (format-symbol "~a" i)))]
[else (let ([car-idx (indices (car k))]
[cdr-idx (apply indices (cdr k))])
(append-map (lambda (i)
(map (lambda (j)
(format-symbol "~a:~a" i j))
cdr-idx))
car-idx))])))
(require (for-syntax 'util) 'util)
(define-for-syntax (define-array stx var type dims)
(with-syntax ([var var]
[type type]
[(k ...) dims])
(with-handlers ([exn:fail?
(lambda (e)
(case (syntax-local-context)
[(module top-level)
(quasisyntax/loc stx
(define var (reshape (list k ...)
(map (lambda (id) (constant id type))
(var-ids #'var (list k ...))))))]
[else (raise e)]))])
(with-syntax ([(v ...) (var-ids #'var (eval #'(list k ...)))])
(quasisyntax/loc stx
(define var (reshape (list k ...) (list (constant #'v type) ...))))))))
var))]
[(_ var:id ...+ type)
#'(begin (define-symbolic* var type) ...)]))

View File

@ -1,6 +1,6 @@
#lang racket
(require (for-syntax racket/dict syntax/parse syntax/id-table (only-in racket pretty-print)
(require (for-syntax racket/dict syntax/parse syntax/parse/define syntax/id-table (only-in racket pretty-print)
(only-in "../core/lift.rkt" drop@))
racket/require racket/undefined
(filtered-in drop@ "../adt/box.rkt")
@ -99,7 +99,14 @@
[(var:id ...) (syntax->list stx)]
[(var:id ... . rest:id) (syntax->list #'(var ... rest))]))
(begin-for-syntax
(define-simple-macro (quasisyntax* orig-stx new-stx)
(let ([orig-stx* orig-stx]
[new-stx* (quasisyntax new-stx)])
(datum->syntax new-stx* (syntax-e new-stx*) orig-stx* orig-stx*))))
(define-for-syntax (box-mutated-vars form tbl)
(define varref-tbl (make-free-id-table))
(define (mutated? id) (free-id-table-ref tbl id #f))
(define (any-mutated? ids) (for/or ([id ids]) (mutated? id)))
(define (bmv/list lstx)
@ -109,7 +116,7 @@
(define (bmv/rest stx lit lstx)
(let-values ([(pure? forms) (bmv/list lstx)])
(if pure? stx (quasisyntax/loc stx (#,lit #,@forms)))))
(if pure? stx (quasisyntax* stx (#,lit #,@forms)))))
(define (bmv/proc-body formals rest)
(let-values ([(pure? fs) (bmv/list rest)]
@ -126,33 +133,40 @@
(syntax-disarm stx orig-insp)
#:literal-sets (kernel-literals)
[var:id
(cond [(and (mutated? #'var) (lexical? #'var)) (syntax/loc stx (unbox var))]
(cond [(and (mutated? #'var) (lexical? #'var)) (quasisyntax* stx (unbox var))]
[else #'var])]
[(set! var expr)
(let ([e (bmv #'expr)])
(cond [(lexical? #'var) (quasisyntax/loc stx (set-box! var #,e))]
(cond [(lexical? #'var) (quasisyntax* stx (set-box! var #,e))]
[(eq? e #'expr) stx]
[else (quasisyntax/loc stx (set! var #,e))]))]
[else (quasisyntax* stx (set! var #,e))]))]
[(define-values (var) expr)
(let ([e (bmv #'expr)])
(cond [(mutated? #'var)
(with-syntax ([(loc) (generate-temporaries #'(var))])
(quasisyntax/loc stx
(splicing-let ([loc (box #,e)])
(dict-set! varref-tbl #'var #'loc)
(quasisyntax* stx
(begin
(define loc (box #,e))
(define-syntax var
(syntax-id-rules (set!)
[(set! var val) (set-box! loc val)]
[(var . arg) ((unbox loc) . arg)]
[var (unbox loc)])))))]
[(eq? e #'expr) stx]
[else (quasisyntax/loc stx (define-values (var) #,e))]))]
[else (quasisyntax* stx (define-values (var) #,e))]))]
[(define-values (var ...) expr)
(let ([e (bmv #'expr)]
[vs (syntax->list #'(var ...))])
(cond [(any-mutated? vs)
(let ([locs (generate-temporaries vs)])
(quasisyntax/loc stx
(splicing-let-values ([#,locs #,e])
(for ([v (in-list vs)]
[loc (in-list locs)]
#:when (mutated? v))
(dict-set! varref-tbl v loc))
(quasisyntax* stx
(begin
(define-values #,locs #,e)
#,@(for/list ([v vs][loc locs] #:when (mutated? v))
#`(set! #,loc (box #,loc)))
#,@(for/list ([v vs][loc locs])
@ -164,14 +178,14 @@
[#,v (unbox #,loc)]))
#`(define-values (#,v) #,loc))))))]
[(eq? e #'expr) stx]
[else (quasisyntax/loc stx (define-values (var ...) #,e))]))]
[else (quasisyntax* stx (define-values (var ...) #,e))]))]
[(let-values ([(var ...) expr] ...) body ...)
(let-values ([(pure-es? es) (bmv/list #'(expr ...))]
[(pure-fs? fs) (bmv/list #'(body ...))]
[(vs) (syntax->list #'(var ... ...))])
(cond [(any-mutated? vs)
(with-syntax ([(e ...) es])
(quasisyntax/loc stx
(quasisyntax* stx
(let-values ([(var ...) e] ...)
#,@(for/list ([v vs] #:when (mutated? v))
#`(set! #,v (box #,v)))
@ -179,7 +193,7 @@
[(and pure-es? pure-fs?) stx]
[else
(with-syntax ([(e ...) es])
(quasisyntax/loc stx
(quasisyntax* stx
(let-values ([(var ...) e] ...)
#,@fs)))]))]
[(letrec-values ([(var ...) expr] ...) body ...)
@ -188,7 +202,7 @@
[(vs) (syntax->list #'(var ... ...))])
(cond [(any-mutated? vs)
(let ([ves (syntax->list #'((var ...) ...))])
(quasisyntax/loc stx
(quasisyntax* stx
(letrec-values ([#,vs (apply values (make-list #,(length vs) undefined))])
#,@(for/list ([v vs] #:when (mutated? v))
#`(set! #,v (box #,v)))
@ -201,7 +215,7 @@
[(and pure-es? pure-fs?) stx]
[else
(with-syntax ([(e ...) es])
(quasisyntax/loc stx
(quasisyntax* stx
(letrec-values ([(var ...) e] ...)
#,@fs)))]))]
[(letrec-syntaxes+values stx-decls ([(var ...) expr] ...) body ...)
@ -210,7 +224,7 @@
[(vs) (syntax->list #'(var ... ...))])
(cond [(any-mutated? vs)
(let ([ves (syntax->list #'((var ...) ...))])
(quasisyntax/loc stx
(quasisyntax* stx
(letrec-syntaxes+values stx-decls ([#,vs (apply values (make-list #,(length vs) undefined))])
#,@(for/list ([v vs] #:when (mutated? v))
#`(set! #,v (box #,v)))
@ -223,13 +237,13 @@
[(and pure-es? pure-fs?) stx]
[else
(with-syntax ([(e ...) es])
(quasisyntax/loc stx
(quasisyntax* stx
(letrec-syntaxes+values stx-decls ([(var ...) e] ...)
#,@fs)))]))]
[(#%plain-lambda formals . rest)
(let ([body (bmv/proc-body #'formals #'rest)])
(cond [(eq? body #'rest) stx]
[else (quasisyntax/loc stx (#%plain-lambda formals #,@body))]))]
[else (quasisyntax* stx (#%plain-lambda formals #,@body))]))]
[(case-lambda . rest)
(let* ([r (syntax->list #'rest)]
[fs (for/list ([fb r])
@ -237,16 +251,19 @@
(let ([body (bmv/proc-body #'f #'b)])
(if (eq? body #'b)
fb
(quasisyntax/loc fb (f #,@body))))))])
(quasisyntax* fb (f #,@body))))))])
(cond [(equal? r fs) stx]
[else (quasisyntax/loc stx (case-lambda #,@fs))]))]
[else (quasisyntax* stx (case-lambda #,@fs))]))]
[(if . rest) (bmv/rest stx #'if #'rest)]
[(#%expression . rest) (bmv/rest stx #'#%expression #'rest)]
[(#%plain-app . rest) (bmv/rest stx #'#%plain-app #'rest)]
[(begin . rest) (bmv/rest stx #'begin #'rest)]
[(begin0 . rest) (bmv/rest stx #'begin0 #'rest)]
[(with-continuation-mark . rest) (bmv/rest stx #'with-continuation-mark #'rest)]
[(#%plain-module-begin . rest) (quasisyntax/loc stx (#%module-begin #,@(map bmv (syntax->list #'rest))))]
[(#%plain-module-begin . rest) (quasisyntax* stx (#%module-begin #,@(map bmv (syntax->list #'rest))))]
[(#%variable-reference x)
#`(#%variable-reference
#,(free-id-table-ref varref-tbl #'x (λ () #'x)))]
[_ stx]))
(bmv form))

View File

@ -1,29 +0,0 @@
#lang racket
(provide current-oracle oracle? (rename-out [make-oracle oracle]))
#|--------------current state parameters--------------|#
(struct oracle ([tbl])
#:property prop:procedure
(lambda (self var)
(let* ([vars (oracle-tbl self)]
[choice-idx (hash-ref vars var 0)])
(hash-set! vars var (+ choice-idx 1))
choice-idx))
#:methods gen:custom-write
[(define (write-proc self port mode)
(fprintf port "oracle~a" (oracle-tbl self)))])
(define make-oracle
(case-lambda
[() (oracle (make-hash))]
[(other) (oracle (hash-copy (oracle-tbl other)))]))
(define current-oracle
(make-parameter (make-oracle)
(lambda (oracle)
(unless (oracle? oracle)
(error 'current-oracle "expected an oracle procedure, given ~s" oracle))
oracle)))

View File

@ -1,58 +1,204 @@
#lang racket
(require (for-syntax (only-in racket/syntax format-id))
(require (for-syntax (only-in racket/syntax
format-id wrong-syntax generate-temporary
current-syntax-context)
(only-in syntax/stx stx-pair? stx-car stx-cdr))
(only-in racket/generic define-generics)
(only-in "../form/control.rkt" @if)
(only-in "../core/bool.rkt" @assert)
(only-in "../core/forall.rkt" for/all)
"../core/union.rkt")
(provide @define-generics @make-struct-type-property)
(begin-for-syntax
;; parse is copied from racket/generic
;; One modification (marked below): If the #:defined-predicate option
;; is not present, it returns #f instead of (generate-temporary)
(define (parse stx [options (hasheq)])
(syntax-case stx ()
[(#:defined-predicate name . args)
(identifier? #'name)
(if (hash-ref options 'support #f)
(wrong-syntax (stx-car stx)
"duplicate #:defined-predicate specification")
(parse #'args (hash-set options 'support #'name)))]
[(#:defined-predicate . other)
(wrong-syntax (stx-car stx) "invalid #:defined-predicate specification")]
[(#:defined-table name . args)
(identifier? #'name)
(if (hash-ref options 'table #f)
(wrong-syntax (stx-car stx)
"duplicate #:defined-table specification")
(parse #'args (hash-set options 'table #'name)))]
[(#:defined-table . other)
(wrong-syntax (stx-car stx) "invalid #:defined-table specification")]
[(#:defaults (clause ...) . args)
(if (hash-ref options 'defaults #f)
(wrong-syntax (stx-car stx) "duplicate #:defaults specification")
(let loop ([defaults '()]
[clauses (reverse (syntax->list #'(clause ...)))])
(if (pair? clauses)
(syntax-case (car clauses) ()
[(pred #:dispatch disp defn ...)
(loop (cons #'[pred disp defn ...] defaults)
(cdr clauses))]
[(pred defn ...)
(with-syntax ([name (generate-temporary #'pred)])
(loop (cons #'[pred #:same defn ...] defaults)
(cdr clauses)))]
[clause
(wrong-syntax #'clause "invalid #:defaults specification")])
(parse #'args
(hash-set* options 'defaults defaults)))))]
[(#:defaults . other)
(wrong-syntax (stx-car stx) "invalid #:defaults specification")]
[(#:fast-defaults (clause ...) . args)
(if (hash-ref options 'fast-defaults #f)
(wrong-syntax (stx-car stx)
"duplicate #:fast-defaults specification")
(let loop ([fast-defaults '()]
[clauses (reverse (syntax->list #'(clause ...)))])
(if (pair? clauses)
(syntax-case (car clauses) ()
[(pred #:dispatch disp defn ...)
(loop (cons #'[pred disp defn ...] fast-defaults)
(cdr clauses))]
[(pred defn ...)
(with-syntax ([name (generate-temporary #'pred)])
(loop (cons #'[pred #:same defn ...] fast-defaults)
(cdr clauses)))]
[clause
(wrong-syntax #'clause
"invalid #:fast-defaults specification")])
(parse #'args
(hash-set* options
'fast-defaults fast-defaults)))))]
[(#:fast-defaults . other)
(wrong-syntax (stx-car stx) "invalid #:fast-defaults specification")]
[(#:fallbacks [fallback ...] . args)
(if (hash-ref options 'fallbacks #f)
(wrong-syntax (stx-car stx) "duplicate #:fallbacks specification")
(parse #'args (hash-set options 'fallbacks #'[fallback ...])))]
[(#:fallbacks . other)
(wrong-syntax (stx-car stx) "invalid #:fallbacks specification")]
[(#:derive-property prop impl . args)
(parse #'args
(hash-set options
'derived
(cons (list #'prop #'impl)
(hash-ref options 'derived '()))))]
[(#:derive-property . other)
(wrong-syntax (stx-car stx) "invalid #:derive-property specification")]
[(kw . args)
(keyword? (syntax-e #'kw))
(wrong-syntax #'kw "invalid keyword argument")]
[((_ . _) . args)
(if (hash-ref options 'methods #f)
(wrong-syntax (stx-car stx) "duplicate methods list specification")
(let loop ([methods (list (stx-car stx))] [stx #'args])
(syntax-case stx ()
[((_ . _) . args) (loop (cons (stx-car stx) methods) #'args)]
[_ (parse stx (hash-set options 'methods (reverse methods)))])))]
[(other . args)
(wrong-syntax #'other
"expected a method identifier with formal arguments")]
[() (values (hash-ref options 'methods '())
;; MODIFICATION: Third argument to hash-ref changed
;; from generate-temporary to #f
(hash-ref options 'support #f)
(hash-ref options 'table #f)
(hash-ref options 'fast-defaults '())
(hash-ref options 'defaults '())
(hash-ref options 'fallbacks '())
(hash-ref options 'derived '()))]
[other
(wrong-syntax #'other
"expected a list of arguments with no dotted tail")]))
(define (index-of name-stx formals-stx)
(let loop ([i 0] [formals formals-stx])
(unless (stx-pair? formals)
(wrong-syntax
formals-stx
"did not find the generic name ~a among the required, by-position arguments"
(syntax->datum name-stx)))
(define c (stx-car formals))
(cond [(identifier? c)
(if (free-identifier=? name-stx (stx-car formals))
(datum->syntax name-stx i)
(loop (+ i 1) (stx-cdr formals)))]
[(keyword? (syntax->datum c)) ; count only by-position required arguments
(loop i (stx-cdr (stx-cdr formals)))]
[else
(wrong-syntax c "required arguments must precede optional arguments")]))))
(define-syntax (@define-generics stx)
(syntax-case stx ()
[(_ id method ...)
(with-syntax ([id? (format-id #'id "~a?" #'id #:source #'id)])
[(_ id . rest)
(parameterize ([current-syntax-context stx])
(unless (identifier? #'id)
(wrong-syntax #'id "expected an identifier"))
(define-values
(methods support table fasts defaults fallbacks derived)
(parse #'rest))
(when table
(wrong-syntax table
"#:defined-table option is not supported in Rosette"))
(with-syntax ([id? (format-id #'id "~a?" #'id #:source #'id)]
[((method-name . method-args) ...) methods]
[support-name support])
(with-syntax ([(method-index ...)
(map (lambda (args) (index-of #'id args))
(syntax-e #'(method-args ...)))])
(syntax/loc stx
(begin
(define-generics id
method ...)
(set! id? (lift id? receiver))
(handle-method method) ...)))]))
(define-syntax (handle-method stx)
(syntax-case stx ()
[(_ (method self arg ...))
(syntax/loc stx
(set! method (lift method receiver arg ...)))]
[(_ (method self arg ... . rest))
(syntax/loc stx
(set! method (lift-variadic method receiver arg ... . rest)))]))
(define-generics id . rest)
(lift-if-exists id? 0)
(lift-if-exists support-name 0)
(lift-if-exists method-name method-index) ...)))))]))
(define (@make-struct-type-property name [guard #f] [supers null] [can-impersonate? #f])
(define-values (prop:p p? p-ref)
(make-struct-type-property name guard supers can-impersonate?))
(values prop:p (lift p? self) (lift p-ref self)))
(values prop:p (lift p? 0) (lift p-ref 0)))
(define-syntax-rule (lift proc receiver arg ...)
(let ([proc proc])
(define-syntax (lift-if-exists stx)
(syntax-case stx ()
[(_ proc receiver-index)
(if (syntax->datum #'proc)
(syntax/loc stx
(set! proc (lift proc receiver-index)))
(syntax/loc stx
(void)))]))
(define (lift proc receiver-index)
(define-values (required-kws allowed-kws) (procedure-keywords proc))
(define arity (procedure-arity proc))
(procedure-rename
(lambda (receiver arg ...)
(if (null? allowed-kws)
(procedure-reduce-arity
(lambda args
(define receiver (list-ref args receiver-index))
(if (union? receiver)
(for/all ([r receiver]) (proc r arg ...))
(proc receiver arg ...)))
(or (object-name proc) 'proc))))
(define-syntax-rule (lift-variadic proc receiver arg ... . rest)
(let ([proc proc])
(procedure-rename
(lambda (receiver arg ... . rest)
(for/all ([r receiver])
(apply proc (list-set args receiver-index r)))
(apply proc args)))
arity)
(procedure-reduce-keyword-arity
(make-keyword-procedure
(lambda (kws kw-args . args)
(define receiver (list-ref args receiver-index))
(if (union? receiver)
(for/all ([r receiver]) (apply proc r arg ... rest))
(apply proc receiver arg ... rest)))
(or (object-name proc) 'proc))))
(for/all ([r receiver])
(keyword-apply proc kws kw-args (list-set args receiver-index r)))
(keyword-apply proc kws kw-args args))))
arity
required-kws
allowed-kws))
(or (object-name proc) 'lifted)))
#|
; sanity check

View File

@ -3,7 +3,7 @@
(require (for-syntax "../core/lift.rkt" racket/syntax)
(only-in racket/private/generic-methods generic-property)
(only-in "../core/effects.rkt" apply!)
(only-in "../core/store.rkt" store!)
"../core/term.rkt" "../core/lift.rkt" "../core/safe.rkt"
(only-in "../core/bool.rkt" || && and-&&)
(only-in "../core/type.rkt" @any/c type-cast gen:typed get-type)
@ -55,7 +55,7 @@
[_ #f]))
(define super (and super-type (typed? super-type) (get-type super-type)))
(define field-count (- init-field-cnt auto-field-cnt))
(define field-count (+ init-field-cnt auto-field-cnt))
(define immutable? (and (= init-field-cnt (length immutables)) (zero? auto-field-cnt)))
(define transparent? (not inspector))
(define equal+hash (let ([e+h (assoc (generic-property gen:equal+hash) props)])
@ -67,7 +67,7 @@
; (printf " immutable?: ~a\n" immutable?)
; (printf " transparent?: ~a\n" transparent?)
; (printf " procedure?: ~a\n" procedure?)
; (printf " equal+hash?: ~a\n" equal+hash?)
; (printf " equal+hash: ~a\n" equal+hash)
(define @struct:t
(struct-type
@ -80,33 +80,43 @@
(values struct:t make-t @struct:t t-ref t-set!))
(define (struct-field-accessor-name @struct:t i field-id)
(if field-id
(format "~a-~a" (object-name (struct-type-make @struct:t)) field-id)
(format "~a-field~a" (object-name (struct-type-make @struct:t)) i)))
(define (struct-field-mutator-name @struct:t i field-id)
(format "set-~a!" (struct-field-accessor-name @struct:t i field-id)))
(define (@make-struct-field-mutator struct:t i field-id)
(let* ([@struct:t (get-type struct:t)]
[native? (struct-type-native? @struct:t)]
[setter (make-struct-field-mutator (struct-type-set! @struct:t) i field-id)]
[getter (make-struct-field-accessor (struct-type-ref @struct:t) i field-id)])
[name (string->symbol (struct-field-mutator-name @struct:t i field-id))]
[setter (struct-type-set! @struct:t)]
[getter (struct-type-ref @struct:t)])
(procedure-rename
(lambda (receiver value)
(if (native? receiver)
(apply! setter getter receiver value)
(match (type-cast @struct:t receiver (object-name setter))
[(? native? r) (apply! setter getter receiver value)]
(store! receiver i value getter setter)
(match (type-cast @struct:t receiver name)
[(? native? r) (store! r i value getter setter)]
[(union rs) (for ([r rs])
(apply! setter getter (cdr r) (merge (car r) value (getter (cdr r)))))])))
(object-name setter))))
(store! (cdr r) i (merge (car r) value (getter (cdr r) i)) getter setter))])))
name)))
(define (@make-struct-field-accessor struct:t i field-id)
(let* ([@struct:t (get-type struct:t)]
[native? (struct-type-native? @struct:t)]
[getter (make-struct-field-accessor (struct-type-ref @struct:t) i field-id)])
[name (string->symbol (struct-field-accessor-name @struct:t i field-id))]
[getter (struct-type-ref @struct:t)])
(procedure-rename
(lambda (receiver)
(if (native? receiver)
(getter receiver)
(match (type-cast @struct:t receiver (object-name getter))
[(? native? r) (getter r)]
[(union r) (merge** r getter)])))
(object-name getter))))
(getter receiver i)
(match (type-cast @struct:t receiver name)
[(? native? r) (getter r i)]
[(union r) (merge** r (getter _ i))])))
name)))
(struct struct-type (pred super native? make ref set! fields immutable? transparent? procedure? equal+hash)
#:property prop:procedure

View File

@ -8,7 +8,7 @@
(for-syntax racket/base racket/struct-info racket/syntax
racket/private/procedure-alias "struct-type.rkt"))
(provide struct struct-field-index define/generic define-struct)
(provide struct define/generic (rename-out [define-struct* define-struct]))
(define-syntax (struct stx)
(define (config-has-name? config)
@ -133,6 +133,19 @@
(lambda (stx)
(raise-syntax-error #f "allowed only within a structure type definition" stx)))
(define-for-syntax (make-struct-field-index fields)
(lambda (stx)
(syntax-case stx ()
[(_ id)
(identifier? #'id)
(let loop ([pos 0] [fields (syntax->list fields)])
(cond
[(null? fields)
(raise-syntax-error #f "no such field" stx #'name)]
[(free-identifier=? #'id (car fields))
(datum->syntax #'here pos stx)]
[else (loop (add1 pos) (cdr fields))]))])))
(define (check-struct-type name what)
(when what
(unless (struct-type? what)
@ -609,14 +622,7 @@
(define-values (#,struct: #,make- #,? #,@sels #,@sets)
(let-values ([(struct: make- ? -ref -set!)
(syntax-parameterize ([struct-field-index
(lambda (stx)
(syntax-case stx #,(map field-id fields)
#,@(let loop ([fields fields][pos 0])
(cond
[(null? fields) null]
[else (cons #`[(_ #,(field-id (car fields))) #'#,pos]
(loop (cdr fields) (add1 pos)))]))
[(_ name) (raise-syntax-error #f "no such field" stx #'name)]))])
(make-struct-field-index (quote-syntax #,(map field-id fields)))])
(@make-struct-type #,reflect-name-expr
#,super-struct:
#,(- (length fields) auto-count)

View File

@ -1,76 +0,0 @@
#lang racket
(require racket/syntax racket/splicing )
(provide define-array array-procedure reshape split-at* list-ref*)
; Provides a macro for defining multidimensional arrays. The
; form (define-array var dims vals) defines a multidimensional
; array from a dimension specification and a flat list of values. The macro
; introduces a name transformer var. When used as an identifer
; the var transformer returns the array in the form of a list of
; lists. When used as a function, it takes a sequence of indices
; and returns the element (or a subarray) at the specified position.
;
; The dimension specification should be list of positive natural
; numbers. For example, '(3 2 3) specifies a 3x2x3 array. The
; vals list should contain exactly (apply * dims) values.
;
; The form (define-array var vals) assumes that vals is already a
; list of lists. It simply introduces a name transformer for var,
; as described above.
(define-syntax (define-array stx)
(syntax-case stx ()
[(_ id dims vals)
#`(define-array id (reshape dims vals))]
[(_ id vals)
#`(splicing-let ([array (array-procedure vals)])
(define-syntax id
(syntax-id-rules (set!)
[(set! id e)
(error 'set! "cannot modify an immutable reference: ~s" (syntax->datum #'id))]
[(id idx (... ...)) (array idx (... ...))]
[id (array)])))]))
; This macro expands to a procedure wrapper that allows
; the elements in the given nested list representation of
; an array to be accessed using a call of the form (vals idx ...).
; Applying the resulting procedure to no arguments yields the
; entire array (that is, list of lists).
(define-syntax-rule (array-procedure vals)
(let ([array vals])
(procedure-rename
(lambda pos
(apply list-ref* array pos))
(string->symbol (format "array~aD" (length array))))))
; This function returns a nested list representation
; of the given flat list using the given shape specification.
; The shape specification is a flat list of positive natural
; numbers. For example, '(3 2) specifies a nested list that
; corresponds to a 3x2 array in row major order, i.e.,
; (reshape '(3 2) '(0 1 2 3 4 5)) yields '((0 1 2) (3 4 5)).
; The behavior of this function is unspecified if the length of
; the vals list is not exactly (apply * dims).
(define (reshape dims vals)
(cond [(null? dims) null]
[(null? (cdr dims)) vals]
[else (let ([rest (cdr dims)])
(map (curry reshape (cdr dims)) (split-at* vals (apply * rest))))]))
; Splits a list of size k*n into k sublists of size n. The
; sublists are returned in a list. The behavior of this function
; is unspecified if the length of the list is not a multiple of n.
(define (split-at* vals n)
(if (null? vals)
null
(let-values ([(left right) (split-at vals n)])
(cons left (split-at* right n)))))
; Returns the value in the given nested list representation of a
; mulitdimensional array that is at the specified position. The
; value itself may be a list; for example, (list-ref* '((0 1) (2 3)) 0)
; produces '(0 1) while (list-ref* '((0 1) (2 3)) 1 0) produces 2.
(define (list-ref* vals . pos)
(if (null? pos)
vals
(apply list-ref* (list-ref vals (car pos)) (cdr pos))))

View File

@ -1,101 +0,0 @@
#lang racket
(provide ord-dict? [rename-out (make-ordered-dictionary ord-dict)
(make-immutable-ordered-dictionary immutable-ord-dict)]
last-key last-value
first-key first-value
dict-take dict-drop)
(define (last-key dict)
(last (ord-dict-order dict)))
(define (last-value dict)
(ord-dict-ref dict (last-key dict)))
(define (first-key dict)
(first (ord-dict-order dict)))
(define (first-value dict)
(ord-dict-ref dict (first-key dict)))
(define (dict-take dict pos)
(sub-dict dict (take (order dict) pos)))
(define (dict-drop dict pos)
(sub-dict dict (drop (order dict) pos)))
(define (sub-dict dict sub-order)
(let ([tbl (table dict)])
(ord-dict (for/hash ([key sub-order]) (values key (dict-ref tbl key)))
sub-order)))
(define ord-dict-ref
(case-lambda [(dict key) (dict-ref (table dict) key)]
[(dict key failure-result) (dict-ref (table dict) key failure-result)]))
(define (ord-dict-set! dict key value)
(unless (dict-has-key? (table dict) key)
(set-order! dict (append (order dict) (list key))))
(dict-set! (table dict) key value))
(define (ord-dict-remove! dict key)
(when (dict-has-key? (table dict) key)
(set-order! dict (remove key (order dict)))
(dict-remove! (table dict) key)))
(define (ord-dict-count dict) (dict-count (table dict)))
(define (ord-dict-iterate-first dict)
(and (not (null? (order dict)))
(order dict)))
(define (ord-dict-iterate-next dict pos)
(and (not (null? pos))
(not (null? (cdr pos)))
(cdr pos)))
(define (ord-dict-iterate-key dict pos) (car pos))
(define (ord-dict-iterate-value dict pos)
(dict-ref (table dict) (car pos)))
(struct ord-dict (table [order #:mutable])
#:property prop:dict
(vector ord-dict-ref
ord-dict-set! #f
ord-dict-remove! #f
ord-dict-count
ord-dict-iterate-first ord-dict-iterate-next
ord-dict-iterate-key ord-dict-iterate-value)
#:property prop:custom-write
(lambda (self port mode)
(let ([order (order self)]
[table (table self)])
(fprintf port "ordered-dict~s" (map (lambda (key) (cons key (dict-ref table key))) order)))))
(struct immutable-ord-dict ord-dict ()
#:property prop:dict
(vector ord-dict-ref
#f #f
#f #f
ord-dict-count
ord-dict-iterate-first ord-dict-iterate-next
ord-dict-iterate-key ord-dict-iterate-value))
(define table ord-dict-table)
(define order ord-dict-order)
(define set-order! set-ord-dict-order!)
(define (make-ordered-dictionary [assocs null])
(ord-dict (make-hash assocs) (map car assocs)))
(define (make-immutable-ordered-dictionary dict)
(if (immutable-ord-dict? dict)
dict
(let ([dict-hash (for/hash ([(key value) (in-dict dict)]) (values key value))])
(if (ord-dict? dict)
(immutable-ord-dict dict-hash (order dict))
(immutable-ord-dict dict-hash (for/list ([key (in-dict-keys dict)]) key))))))

View File

@ -1,274 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/form rosette/query/eval rosette/solver/solution
rosette/base/core/term (only-in rosette/query/finitize current-bitwidth)
(only-in rosette/base/core/union union?)
(only-in rosette/base/base bv bv? bitvector bitvector? bitvector-size
bveq bvslt bvsgt bvsle bvsge bvult bvugt bvule bvuge
bvnot bvor bvand bvxor bvshl bvlshr bvashr
bvneg bvadd bvsub bvmul bvudiv bvsdiv bvurem bvsrem bvsmod
concat extract sign-extend zero-extend
integer->bitvector bitvector->integer bitvector->natural)
(only-in rosette/base/core/safe assert)
(only-in rosette/base/core/bool asserts))
(for-label racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox
"../util/lifted.rkt")
@(define rosette-eval (rosette-evaluator))
@title[#:tag "sec:bitvectors"]{Bitvectors}
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
Rosette extends Racket with a primitive bitvector datatype whose values are
fixed-size words---or, machine integers. Mainstream programming languages, such as
C or Java, support bitvector types with a few fixed sizes (e.g., 8 bits, 16 bits,
and 32 bits). Rosette supports bitvectors of arbitrary size, as well as both signed and
unsigned versions of various bitvector operations (such as comparisons, division, remainder, etc.).
Technically, Rosette's bitvector datatype embeds the
@hyperlink["http://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV"]{theory of bitvectors}
into a programming language.
@examples[#:eval rosette-eval
(code:line (bv 4 (bitvector 7)) (code:comment "a bitvector literal of size 7"))
(code:line (bv 4 7) (code:comment "a shorthand for the same literal"))
(code:line (define-symbolic x y (bitvector 7)) (code:comment "symbolic bitvector constants"))
(code:line (bvslt (bv 4 7) (bv -1 7)) (code:comment "signed 7-bit < comparison of 4 and -1"))
(code:line (bvult (bv 4 7) (bv -1 7)) (code:comment "unsigned 7-bit < comparison of 4 and -1"))
(define-symbolic b boolean?)
(code:line (bvadd x (if b y (bv 3 4))) (code:comment "this typechecks only when b is true"))
(code:line (asserts) (code:comment "so Rosette emits a corresponding assertion"))]
@defproc[(bitvector [size (and/c integer? positive? (not/c term?) (not/c union?))]) bitvector?]{
Returns a type predicate that recognizes bitvectors of the given @racket[size].
Note that @racket[size] must be a concrete positive integer.
The type predicate itself is recognized by the @racket[bitvector?] predicate.
@examples[#:eval rosette-eval
(define bv6? (bitvector 6))
(bv6? 1)
(bv6? (bv 3 6))
(bv6? (bv 3 5))
(define-symbolic b boolean?)
(bv6? (if b (bv 3 6) #t))]
}
@defproc[(bitvector? [v any/c]) boolean?]{
Returns true if @racket[v] is a concrete type predicate that recognizes bitvector values.
@examples[#:eval rosette-eval
(define bv6? (bitvector 6))
(define bv7? (bitvector 7))
(define-symbolic b boolean?)
(code:line (bitvector? bv6?) (code:comment "a concrete bitvector type"))
(code:line (bitvector? (if b bv6? bv7?)) (code:comment "not a concrete type"))
(code:line (bitvector? integer?) (code:comment "not a bitvector type"))
(code:line (bitvector? 3) (code:comment "not a type"))]}
@defproc[(bv [val (and/c integer? (not/c term?) (not/c union?))]
[size (and/c (or/c bitvector? (and/c integer? positive?))
(not/c term?) (not/c union?))]) bv?]{
Returns a bitvector literal of the given @racket[size], which may be given either as a
concrete @racket[bitvector?] type or a concrete positive integer.}
@defproc[(bv? [v any/c]) boolean?]{
Recognizes concrete or symbolic bitvector values of any size.
@examples[#:eval rosette-eval
(bv? 1)
(bv? (bv 1 1))
(bv? (bv 2 2))
(define-symbolic b boolean?)
(bv? (if b (bv 3 6) #t))]
}
@(rosette-eval '(clear-asserts!))
@section{Comparison Operators}
@defproc*[([(bveq [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvslt [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvult [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvsle [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvule [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvsgt [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvugt [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvsge [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvuge [x (bitvector n)] [y (bitvector n)]) boolean?])]{
Compares two bitvector values of the same bitvector type.
Comparison relations include
equality (@racket[bveq]) and signed / unsigned versions of
<, <=, >, and >= (@racket[bvslt], @racket[bvult], @racket[bvsle], @racket[bvule],
@racket[bvsgt], and @racket[bvugt]).
@examples[#:eval rosette-eval
(code:line (define-symbolic u v (bitvector 7)) (code:comment "symbolic bitvector constants"))
(code:line (bvslt (bv 4 7) (bv -1 7)) (code:comment "signed 7-bit < comparison of 4 and -1"))
(code:line (bvult (bv 4 7) (bv -1 7)) (code:comment "unsigned 7-bit < comparison of 4 and -1"))
(define-symbolic b boolean?)
(code:line (bvsge u (if b v (bv 3 4))) (code:comment "this typechecks only when b is true"))
(code:line (asserts) (code:comment "so Rosette emits a corresponding assertion"))]
}
@(rosette-eval '(clear-asserts!))
@section{Bitwise Operators}
@defproc[(bvnot [x (bitvector n)]) (bitvector n)]{
Returns the bitwise negation of the given bitvector value.
@examples[#:eval rosette-eval
(bvnot (bv -1 4))
(bvnot (bv 0 4))
(define-symbolic b boolean?)
(code:line (bvnot (if b 0 (bv 0 4))) (code:comment "this typechecks only when b is false"))
(code:line (asserts) (code:comment "so Rosette emits a corresponding assertion"))]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(bvand [x (bitvector n)] ...+) (bitvector n)]
[(bvor [x (bitvector n)] ...+) (bitvector n)]
[(bvxor [x (bitvector n)] ...+) (bitvector n)])]{
Returns the bitwise "and", "or", "xor" of one or more bitvector values of the same type.
@examples[#:eval rosette-eval
(bvand (bv -1 4) (bv 2 4))
(bvor (bv 0 3) (bv 1 3))
(bvxor (bv -1 5) (bv 1 5))
(define-symbolic b boolean?)
(code:line (bvand (bv -1 4)
(if b 0 (bv 2 4))) (code:comment "this typechecks only when b is false"))
(code:line (asserts) (code:comment "so Rosette emits a corresponding assertion"))]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(bvshl [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvlshr [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvashr [x (bitvector n)] [y (bitvector n)]) (bitvector n)])]{
Returns the left, logical right, or arithmetic right shift of @racket[x] by
@racket[y] bits, where @racket[x] and @racket[y] are bitvector values of the same type.
@examples[#:eval rosette-eval
(bvshl (bv 1 4) (bv 2 4))
(bvlshr (bv -1 3) (bv 1 3))
(bvashr (bv -1 5) (bv 1 5))
(define-symbolic b boolean?)
(code:line (bvshl (bv -1 4)
(if b 0 (bv 2 4))) (code:comment "this typechecks only when b is false"))
(code:line (asserts) (code:comment "so Rosette emits a corresponding assertion"))]
}
@section{Arithmetic Operators}
@defproc[(bvneg [x (bitvector n)]) (bitvector n)]{
Returns the arithmetic negation of the given bitvector value.
@examples[#:eval rosette-eval
(bvneg (bv -1 4))
(bvneg (bv 0 4))
(define-symbolic z (bitvector 3))
(bvneg z)]
}
@(rosette-eval '(clear-asserts!))
@(rosette-eval '(clear-terms!))
@defproc*[([(bvadd [x (bitvector n)] ...+) (bitvector n)]
[(bvsub [x (bitvector n)] ...+) (bitvector n)]
[(bvmul [x (bitvector n)] ...+) (bitvector n)])]{
Returns the sum, difference, or product of one or more bitvector values of the same type.
@examples[#:eval rosette-eval
(bvadd (bv -1 4) (bv 2 4))
(bvsub (bv 0 3) (bv 1 3))
(bvmul (bv -1 5) (bv 1 5))
(define-symbolic b boolean?)
(bvadd (bv -1 4) (bv 2 4) (if b (bv 1 4) "bad"))
(asserts)]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(bvsdiv [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvudiv [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvsrem [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvurem [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvsmod [x (bitvector n)] [y (bitvector n)]) (bitvector n)])]{
Returns (un)signed quotient, remainder, or modulo of two bitvector values of the same type.
All five operations are defined even when the second argument is zero.
@examples[#:eval rosette-eval
(bvsdiv (bv -3 4) (bv 2 4))
(bvudiv (bv -3 3) (bv 2 3))
(bvsmod (bv 1 5) (bv 0 5))
(define-symbolic b boolean?)
(bvsrem (bv -3 4) (if b (bv 2 4) "bad"))
(asserts)]
}
@(rosette-eval '(clear-asserts!))
@section{Conversion Operators}
@defproc[(concat [x bv?] ...+) bv?]{
Returns the bitwise concatenation of the given bitvector values.
@examples[#:eval rosette-eval
(concat (bv -1 4) (bv 0 1) (bv -1 3))
(define-symbolic b boolean?)
(concat (bv -1 4) (if b (bv 0 1) (bv 0 2)) (bv -1 3))]
}
@defproc[(extract [i integer?] [j integer?] [x (bitvector n)]) (bitvector (+ 1 (- i j)))]{
Extracts bits @racket[i] down to @racket[j] from a bitvector of size @racket[n], yielding a
bitvector of size i - j + 1. This procedure assumes that @racket[n] > @racket[i] >= @racket[j] >= 0.
@examples[#:eval rosette-eval
(extract 2 1 (bv -1 4))
(extract 3 3 (bv 1 4))
(define-symbolic i j integer?)
(eval:alts (extract i j (bv 1 2)) (begin (extract i j (bv 1 2)) "{[(&& (= 0 j) (= 1 i)) (bv 1 2)] ...}"))
(asserts)]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(sign-extend [x bv?] [t (or/c bitvector? union?)]) bv?]
[(zero-extend [x bv?] [t (or/c bitvector? union?)]) bv?])]{
Returns a bitvector of type @racket[t] that represents the (un)signed
extension of the bitvector @racket[x].
Note that both @racket[x] and @racket[t] may be symbolic. The size of @racket[t]
must not be smaller than the size of @racket[x]'s type.
@examples[#:eval rosette-eval
(sign-extend (bv -3 4) (bitvector 6))
(zero-extend (bv -3 4) (bitvector 6))
(define-symbolic b c boolean?)
(zero-extend (bv -3 4) (if b (bitvector 5) (bitvector 6)))
(zero-extend (bv -3 4) (if b (bitvector 5) "bad"))
(asserts)
(zero-extend (bv -3 4) (if c (bitvector 5) (bitvector 1)))
(asserts)]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(bitvector->integer [x bv?]) integer?]
[(bitvector->natural [x bv?]) integer?])]{
Returns the (un)signed integer value of the given bitvector.
@examples[#:eval rosette-eval
(bitvector->integer (bv -1 4))
(bitvector->natural (bv -1 4))
(define-symbolic b boolean?)
(bitvector->integer (if b (bv -1 3) (bv -3 4)))
(bitvector->integer (if b (bv -1 3) "bad"))
(asserts)]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(integer->bitvector [i integer?] [t (or/c bitvector? union?)]) bv?])]{
Returns a bitvector of type @racket[t] that represents the @var[k] lowest order bits
of the integer @racket[i], where @var[k] is the size of @racket[t].
Note that both @racket[i] and @racket[t] may be symbolic.
@examples[#:eval rosette-eval
(integer->bitvector 4 (bitvector 2))
(integer->bitvector 15 (bitvector 4))
(define-symbolic b c boolean?)
(integer->bitvector (if b pi 3) (if c (bitvector 5) (bitvector 6)))
(asserts)]
}
@(kill-evaluator rosette-eval)

View File

@ -1,137 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/form rosette/query/eval rosette/solver/solution
rosette/base/core/term (only-in rosette/query/finitize current-bitwidth)
(only-in rosette/base/base ! && || => <=> exists forall function?)
(only-in rosette/base/core/safe assert)
(only-in rosette/base/core/bool asserts))
(except-in (for-label racket) =>)
scribble/core scribble/html-properties scribble/eval racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "bools-log")))
@(define bools (select '(boolean? false? true false boolean=? not nand nor implies xor)))
@(define nums (select '(number? complex? real? rational? integer? exact-integer? exact-nonnegative-integer? exact-positive-integer? inexact-real? fixnum? flonum? double-flonum? single-flonum? zero? positive? negative? even? odd? exact? inexact? inexact->exact exact->inexact real->single-flonum real->double-flonum + - * / quotient remainder quotient/ modulo add1 sub1 abs max min gcd lcm round floor ceiling truncate numerator denominator rationalize = < <= > >= sqrt integer-sqrt integer-sqrt/ expt exp log sin cos tan asin acos atan make-rectangular make-polar real-part imag-part magnitude angle bitwise-ior bitwise-and bitwise-xor bitwise-not bitwise-bit-set? bitwise-bit-field arithmetic-shift integer-length random random-seed make-pseudo-random-generator pseudo-random-generator? current-pseudo-random-generator pseudo-random-generator->vector vector->pseudo-random-generator vector->pseudo-random-generator! pseudo-random-generator-vector? number->string string->number real->decimal-string integer-bytes->integer integer->integer-bytes floating-point-bytes->real real->floating-point-bytes system-big-endian? pi pi.f degrees->radians radians->degrees sqr sgn conjugate sinh cosh tanh exact-round exact-floor exact-ceiling exact-truncate order-of-magnitude nan? infinite?)))
@title[#:tag "sec:bools+ints+reals"]{Booleans, Integers, and Reals}
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
Rosette lifts the following operations on booleans, integers, and reals:
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
(list (list @elem{Booleans} @bools)
(list @elem{Integers and Reals} @nums))]
Lifted boolean operations retain their Racket semantics on both concrete and symbolic values.
In particular, Rosette extends the intepretation of these operations to work on symbolic values in (logically) the
same way that they work on concrete values.
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(boolean? b)
(boolean? #t)
(boolean? #f)
(boolean? 1)
(code:line (not b) (code:comment "produces a logical negation of b"))]
Lifted numeric operations, in contrast, match their Racket semantics
only when applied to concrete values. Their symbolic semantics depends on the
current @tech["reasoning precision"], as determined by the @racket[current-bitwidth]
parameter. In particular, if this parameter is set to @racket[#f], operations on symbolic numbers
retain their infinite-precision Racket semantics. However, because infinite-precision
reasoning is not efficiently (or at all) decidable for arbitrary numeric operations,
@racket[current-bitwidth] is, by default, set to a small positive integer @var[k].
With this setting, symbolic numbers are treated as signed @var[k]-bit integers. See
@secref{sec:reasoning-precision} for details and examples.
@section[#:tag "sec:extra-bools"]{Additional Logical Operators}
In addition to lifting Racket's operations on booleans, Rosette also supports the following
logical operations: conjunction (@racket[&&]), disjunction (@racket[||]), implication (@racket[=>]),
bi-implication (@racket[<=>]), negation (@racket[!]), universal quantification (@racket[forall]), and
existential quantification (@racket[exists]). These operations have their usual logical meaning
(e.g., unlike Racket's shortcircuiting @racket[and] operator, the logical @racket[&&] operator
evaluates all of its arguments before taking their conjunction).
@(rosette-eval '(clear-asserts!))
@defproc[(! [v boolean?]) boolean?]{
Returns the negation of the given boolean value.
@examples[#:eval rosette-eval
(! #f)
(! #t)
(define-symbolic b boolean?)
(code:line (! (if b #f 3)) (code:comment "this typechecks only when b is true"))
(code:line (asserts) (code:comment "so Rosette emits a corresponding assertion"))]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(&& [v boolean?] ...) boolean?]
[(|| [v boolean?] ...) boolean?])]{
Returns the logical conjunction or disjunciton of zero or more boolean values.
@examples[#:eval rosette-eval
(&&)
(||)
(code:line (&& #f (begin (displayln "hello") #t)) (code:comment "no shortcircuiting"))
(define-symbolic a b boolean?)
(code:line (&& a (if b #t 1)) (code:comment "this typechecks only when b is true"))
(code:line (asserts) (code:comment "so Rosette emits a corresponding assertion"))]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(=> [x boolean?] [y boolean?]) boolean?]
[(<=> [x boolean?] [y boolean?]) boolean?])]{
Returns the logical implication or bi-implication of two boolean values.
@examples[#:eval rosette-eval
(code:line (=> #f (begin (displayln "hello") #f)) (code:comment "no shortcircuiting"))
(define-symbolic a b boolean?)
(code:line (<=> a (if b #t 1)) (code:comment "this typechecks only when b is true"))
(code:line (asserts) (code:comment "so Rosette emits a corresponding assertion"))]
}
@(rosette-eval '(clear-asserts!))
@defproc*[([(forall [vs (listof constant?)] [body boolean?]) boolean?]
[(exists [vs (listof constant?)] [body boolean?]) boolean?])]{
Returns a universally or existentially @deftech{quantified formula}, where the
symbolic constants @racket[vs] are treated as quantified variables. The @racket[body]
of the formula is a boolean expression over the quantified variables @racket[vs] and,
optionally, over free symbolic (Skolem) constants.
If a set of constraints is satisfiable, their @racket[model] includes bindings only for
free symbolic constants: no bindings are provided for constants that do not appear freely in any formula.
All quantified symbolics must be have a non-@racket[function?] @racket[solvable?] type.
The usual lexical scoping rules apply to quantified symbolics; if @racket[body] is
a quantified formula over a variable @var[v] in @racket[vs], then the
innermost quantification of @var[v] shadows any enclosing quantifications.
When executing queries over assertions that contain quantified formulas,
the @racket[current-bitwidth] parameter must be set to @racket[#f].
Quantified formulas may not appear in any assertion that is passed to a @racket[synthesize] query,
either via an (implicit or explicit) assumption or a guarantee expression.
@examples[#:eval rosette-eval
(current-bitwidth #f)
(define-symbolic a b integer?)
(forall (list) (= a b))
(code:line (define f (forall (list a) (exists (list b) (= a (+ a b))))) (code:comment "no free constants"))
(code:line (solve (assert f)) (code:comment "so the model has no bindings"))
(code:line (define g (forall (list a) (= a (+ a b)))) (code:comment "b is free in g"))
(code:line (solve (assert g)) (code:comment "so the model has a binding for b"))
(code:line (define h (exists (list a) (forall (list a) (= a (+ a a))))) (code:comment "body refers to the innermost a"))
(code:line (solve (assert h)) (code:comment "so h is unsatisfiable."))
]
}
@(kill-evaluator rosette-eval)

View File

@ -1,160 +0,0 @@
;; This file was created by make-log-based-eval
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((boolean? b) ((3) 0 () 0 () () (q values #t)) #"" #"")
((boolean? #t) ((3) 0 () 0 () () (q values #t)) #"" #"")
((boolean? #f) ((3) 0 () 0 () () (q values #t)) #"" #"")
((boolean? 1) ((3) 0 () 0 () () (q values #f)) #"" #"")
((not b)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(! b)"))))
#""
#"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((! #f) ((3) 0 () 0 () () (q values #t)) #"" #"")
((! #t) ((3) 0 () 0 () () (q values #f)) #"" #"")
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((! (if b #f 3)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "b")))))
#""
#"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((&&) ((3) 0 () 0 () () (q values #t)) #"" #"")
((||) ((3) 0 () 0 () () (q values #f)) #"" #"")
((&& #f (begin (displayln "hello") #t))
((3) 0 () 0 () () (q values #f))
#""
#"")
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((&& a (if b #t 1))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "a"))))
#""
#"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "b")))))
#""
#"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((=> #f (begin (displayln "hello") #f))
((3) 0 () 0 () () (q values #t))
#""
#"")
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((<=> a (if b #t 1))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "a"))))
#""
#"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "b")))))
#""
#"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic a b integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((forall (list) (= a b))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(= a b)"))))
#""
#"")
((define f (forall (list a) (exists (list b) (= a (+ a b)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((solve (assert f))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model)"))))
#""
#"")
((define g (forall (list a) (= a (+ a b))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((solve (assert g))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [b 0])"))))
#""
#"")
((define h (exists (list a) (forall (list a) (= a (+ a a)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((solve (assert h))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)"))))
#""
#"")

View File

@ -1,69 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/query
rosette/base/core/term
(only-in rosette/base/core/safe assert)
racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "pairs-log")))
@(define pairs:constructors+selectors (select '(pair? null? cons car cdr null list? list list* build-list)))
@(define list-operations (select '(length list-ref list-tail append reverse)))
@(define list-iteration (select '(map andmap ormap for-each foldl foldr)))
@(define list-filtering (select '(filter remove remq remv remove* remq* remv* sort)))
@(define list-searching (select '(member memv memq memf findf assoc assv assq assf)))
@(define more-pair-ops (select '(caar cadr cdar cddr caaar caadr cadar caddr cdaar cdadr cddar cdddr caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr)))
@(define more-list-ops (select '(empty cons? empty? first rest second third fourth fifth sixth seventh eighth ninth tenth last last-pair make-list take drop split-at takef dropf splitf-at take-right drop-right split-at-right takef-right dropf-right splitf-at-right add-between append* flatten remove-duplicates filter-map count partition range append-map filter-not shuffle permutations in-permutations argmin argmax )))
@title[#:tag "sec:pair"]{Pairs and Lists}
A pair combines two values, and a list is either the
constant @racket[null] or a pair whose second
element is a list. Pairs and lists are transparent immutable values, and they may
be concrete or symbolic.
Two pairs or two lists are @racket[eq?] (resp. @racket[equal?])
if their corresponding elements are @racket[eq?] (resp. @racket[equal?]).
As values of @tech[#:key "unsolvable type"]{unsolvable types}, symbolic pairs
and lists cannot be created
via @seclink["sec:symbolic-constants"]{@code{define-symbolic[*]}}.
Instead, they are created by applying pair- or list-producing procedures to symbolic inputs,
or by controlling the application of such procedures with symbolic values. This
pattern for creating non-primitive symbolic values generalizes to all unsolvable datatypes.
@examples[#:eval rosette-eval
(define-symbolic x y z n integer?)
(code:line (define xs (take (list x y z) n)) (code:comment "(1) xs is a symbolic list"))
(define sol (solve (assert (null? xs))))
(evaluate xs sol)
(define sol
(solve (begin
(assert (= (length xs) 2))
(assert (not (equal? xs (reverse xs))))
(assert (equal? xs (sort xs <))))))
(evaluate xs sol)]
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(code:line (define p (if b (cons 1 2) (cons 4 #f))) (code:comment "(2) p is a symbolic pair"))
(define sol (solve (assert (boolean? (cdr p)))))
(evaluate p sol)
(define sol (solve (assert (odd? (car p)))))
(evaluate p sol)
]
Rosette lifts the following operations on pairs and lists:
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
(list (list @elem{Pair Operations} @pairs:constructors+selectors)
(list @elem{List Operations} @list-operations)
(list @elem{List Iteration} @list-iteration)
(list @elem{List Filtering} @list-filtering)
(list @elem{List Searching} @list-searching)
(list @elem{Additional Pair Operations} @more-pair-ops)
(list @elem{Additional List Operations} @more-list-ops))]
@(kill-evaluator rosette-eval)

View File

@ -1,189 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/solver/solver rosette/solver/solution
(only-in rosette/query/debug debug)
rosette/solver/smt/z3
rosette/base/form/define rosette/query/query rosette/query/core
rosette/base/core/term (only-in rosette/base/base bv?)
(only-in rosette/base/core/safe assert)
racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "solvers-log")))
@title[#:tag "sec:solvers-and-solutions"]{Solvers and Solutions}
@declare-exporting[rosette/query/core
rosette/query/eval
rosette/solver/solver
rosette/solver/solution
rosette/solver/smt/z3
#:use-sources
(rosette/query/finitize
rosette/query/eval
rosette/solver/solver
rosette/solver/solution
rosette/solver/smt/z3)]
A @deftech{solver} is an automatic reasoning engine, used to answer
@seclink["sec:queries"]{queries} about Rosette programs. The result of
a solver invocation is a @deftech{solution}, containing either
a @tech{binding} of symbolic constants to concrete values, or
an @tech[#:key "MUC"]{unsatisfiable core}.
Solvers and solutions may not be symbolic. Two solvers (resp. solutions) are @racket[eq?]/@racket[equal?]
if they refer to the same object.
@section{The Solver Interface}
@defparam[current-solver solver solver?]{
The @racket[current-solver] parameter holds the solver object used for
answering solver-aided queries. Rosette's default solver is @racket[z3], although
new (SMT) solvers can be added well. Rosette will work with any solver that implements the
@racket[gen:solver] generic interface.
@examples[#:eval rosette-eval
(current-solver)]
}
@defthing[gen:solver solver?]{
A @hyperlink["https://docs.racket-lang.org/reference/struct-generics.html"]{generic interface}
that specifies the procedures provided by a solver. These include
@racket[solver-assert],
@racket[solver-clear],
@racket[solver-minimize],
@racket[solver-maximize],
@racket[solver-check],
@racket[solver-debug], and
@racket[solver-shutdown].
Solvers are stateful. Each solver contains the constraints that have been added to it
via @racket[solver-assert], and the numeric objective terms that have been added to it
via @racket[solver-minimize] and @racket[solver-maximize].
}
@defproc[(solver? [v any/c]) boolean?]{
Returns true if @racket[v] is a concrete value that implements the @racket[gen:solver] interface.}
@defproc[(solver-assert [solver solver?] [constraints (listof boolean?)]) void?]{
Adds the given constraints to the given solver. These constraints take the form of boolean terms
to be satisfied by subsequent calls to @racket[solver-check].}
@defproc[(solver-clear [solver solver?]) void?]{
Clears all constraints from the given solver.}
@defproc*[([(solver-minimize [solver solver?] [objs (listof (or/c integer? real? bv?))]) void?]
[(solver-maximize [solver solver?] [objs (listof (or/c integer? real? bv?))]) void?])]{
Adds the given optimization objectives to the given solver. These objectives take the form of
numeric terms whose value is to be minimized or maximized by subsequent calls to @racket[solver-check],
while satisfying all the boolean terms asserted via @racket[solver-assert].}
@defproc[(solver-check [solver solver?]) solution?]{
Searches for a binding from symbolic constants to concrete values that satisfies the
constraints (boolean terms) added to the solver via @racket[solver-assert].
If such a binding---or, a @racket[model]---exists,
it is returned in the form of a satisfiable (@racket[sat?]) solution, which optimizes
the objective terms added to the solver via @racket[solver-minimize] and @racket[solver-maximize].
Otherwise, an unsatisfiable (@racket[unsat?]) solution is returned, but without
computing an unsatisfiable @racket[core] (i.e., calling @racket[core] on the
resulting solution produces @racket[#f]).
}
@defproc[(solver-debug [solver solver?]) solution?]{
Searches for an unsatisfiable core of the constraints (boolean terms)
added to the solver via @racket[solver-assert] @emph{after} the most recent call to
@racket[clear] or @racket[solver-check] (if any).
If the constraints are satisfiable, or the given solver does
not support core extraction, an error is thrown. Otherwise, the result is an
@racket[unsat?] solution with a unsatisfiable @racket[core], expressed as a
list of boolean terms.
}
@defproc[(solver-shutdown [solver solver?]) void?]{
Terminates the current solving process (if any),
clears all added constraints, and releases all system resources associated
with the given solver instance. The solver must be able to reacquire these resources
if needed. That is, the solver should behave as though its state was merely cleared
(via @racket[solver-clear]) after a shutdown call.
}
@defproc[(z3) solver?]{
Returns a @racket[solver?] wrapper for the @hyperlink["https://github.com/Z3Prover/z3/"]{Z3} solver from Microsoft Research.}
@section{Solutions}
A solution to a set of formulas may be satisfiable (@racket[sat?]), unsatisfiable (@racket[unsat?]),
or unknown (@racket[unknown?]).
A satisfiable solution can be used as a procedure: when applied to a bound symbolic constant, it returns
a concrete value for that constant; when applied to any other value, it returns
the value itself.
The solver returns an @racket[unknown?] solution if it cannot determine whether
the given constraints are satisfiable or not.
A solution supports the following operations:
@defproc[(solution? [value any/c]) boolean?]{
Returns true if the given @racket[value] is a solution.}
@defproc[(sat? [value any/c]) boolean?]{
Returns true if the given @racket[value] is a satisfiable solution.}
@defproc[(unsat? [value any/c]) boolean?]{
Returns true if the given @racket[value] is an unsatisfiable solution.}
@defproc[(unknown? [value any/c]) boolean?]{
Returns true if the given @racket[value] is an unknown solution.}
@defproc*[([(sat) sat?]
[(sat [binding (hash/c constant? any/c #:immutable #t)]) sat?])]{
Returns a satisfiable solution that holds the given binding from symbolic
constants to values, or that holds the empty binding. The provided hash must
bind every symbolic constant in its keyset to a concrete value of the same type.
}
@defproc*[([(unsat) unsat?]
[(unsat [constraints (listof boolean?)]) unsat?])]{
Returns an unsatisfiable solution. The @racket[constraints] list, if provided,
consist of boolean values that are collectively unsatisfiable. If no constraints
are provided, applying @racket[core] to the resulting solution produces @racket[#f],
indicating that there is no satisfying solution but
core extraction was not performed. (Core extraction is an expensive
operation that is not supported by all solvers; those that do support it
usually don't compute a core unless explicitly asked for one via @racket[solver-debug].)}
@defproc[(unknown) unknown?]{
Returns an unknown solution.}
@defproc[(model [solution sat?]) (hash/c constant? any/c #:immutable #t)]{
Returns the binding stored in the given satisfiable solution. The binding is an immutable
hashmap from symbolic constants to values.
}
@defproc[(core [solution unsat?]) (or/c (listof (and/c constant? boolean?)) #f)]{
Returns the unsatisfiable core stored in the given satisfiable solution. If the solution is
@racket[unsat?] and a core was computed, the result is a list of boolean values that
are collectively unsatisfiable. Otherwise, the result is @racket[#f].
}
@defproc[(evaluate [v any/c] [solution sat?]) any/c]{
Given a Rosette value and a satisfiable solution, @racket[evaluate] produces a
new value obtained by replacing every symbolic constant @var[c] in @racket[v]
with @racket[(solution #, @var[c])] and simplifying the result.
@examples[#:eval rosette-eval
(define-symbolic a b boolean?)
(define-symbolic x y integer?)
(define sol
(solve (begin (assert a)
(assert (= x 1))
(assert (= y 2)))))
(sat? sol)
(evaluate (list 4 5 x) sol)
(define vec (vector a))
(evaluate vec sol)
(code:line (eq? vec (evaluate vec sol)) (code:comment "evaluation produces a new vector"))
(evaluate (+ x y) sol)
(evaluate (and a b) sol)
]}
@(kill-evaluator rosette-eval)

View File

@ -1,37 +0,0 @@
;; This file was created by make-log-based-eval
((current-solver)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "#<z3>"))))
#""
#"")
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define sol (solve (begin (assert a) (assert (= x 1)) (assert (= y 2)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((sat? sol) ((3) 0 () 0 () () (q values #t)) #"" #"")
((evaluate (list 4 5 x) sol) ((3) 0 () 0 () () (q values (4 5 1))) #"" #"")
((define vec (vector a)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((evaluate vec sol) ((3) 0 () 0 () () (c values c (v! #t))) #"" #"")
((eq? vec (evaluate vec sol)) ((3) 0 () 0 () () (q values #f)) #"" #"")
((evaluate (+ x y) sol) ((3) 0 () 0 () () (q values 3)) #"" #"")
((evaluate (and a b) sol)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "b"))))
#""
#"")

View File

@ -1,30 +0,0 @@
;; This file was created by make-log-based-eval
((define v1 (vector 1 2 #f)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define v2 (vector 1 2 #f)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((eq? v1 v2) ((3) 0 () 0 () () (q values #f)) #"" #"")
((equal? v1 v2) ((3) 0 () 0 () () (q values #t)) #"" #"")
((define v3 (vector-immutable 1 2 #f))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define v4 (vector-immutable 1 2 #f))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((eq? v3 v4) ((3) 0 () 0 () () (q values #t)) #"" #"")
((equal? v1 v3) ((3) 0 () 0 () () (q values #t)) #"" #"")
((define-symbolic x y z n integer?)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define xs (take (list x y z) n))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define vs (list->vector xs)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define sol (solve (assert (= 4 (vector-ref vs (sub1 n))))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate vs sol) ((3) 0 () 0 () () (c values c (v! 0 4))) #"" #"")
((evaluate xs sol) ((3) 0 () 0 () () (q values (0 4))) #"" #"")

View File

@ -1,59 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/query
rosette/base/core/term
(only-in rosette/base/core/safe assert)
racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "vectors-log")))
@(define vector-ops (select '(vector? make-vector vector vector-immutable vector-length vector-ref vector-set! vector->list list->vector vector->immutable-vector vector-fill! vector-copy! vector->values build-vector immutable?)))
@(define more-vector-ops (select '(vector-set*! vector-map vector-map! vector-append vector-take vector-take-right vector-drop vector-drop-right vector-split-at vector-split-at-right vector-copy vector-filter vector-filter-not vector-count vector-argmin vector-argmax vector-member vector-memv vector-memq)))
@title[#:tag "sec:vec"]{Vectors}
A vector is a fixed-length (im)mutable array.
Vectors may be concrete or symbolic, and they may be accessed using concrete
or symbolic indices. A concrete vector supports constant-time access for
concrete slot indices, and linear-time access for symbolic slot indices.
A symbolic vector supports (worst-case) linear- and quadratic-time access for concrete and
symbolic indices, respectively. Access time for symbolic vectors is given with
respect to the longest possible concrete array to which any symbolic vector
could @racket[evaluate] under any @racket[solution?].
Like @seclink["sec:pair"]{pairs and lists}, immutable vectors are transparent immutable values:
two such vectors are @racket[eq?] if they have the same length and @racket[eq?] contents.
Mutable vectors are references rather than values, and two mutable vectors are @racket[eq?] if and only if they
point to the same array object. Two vectors (regardless of mutability) are @racket[equal?]
if they have the same length and @racket[equal?] contents.
@examples[#:eval rosette-eval
(define v1 (vector 1 2 #f))
(define v2 (vector 1 2 #f))
(eq? v1 v2)
(equal? v1 v2)
(define v3 (vector-immutable 1 2 #f))
(define v4 (vector-immutable 1 2 #f))
(eq? v3 v4)
(equal? v1 v3)
]
@examples[#:eval rosette-eval
(define-symbolic x y z n integer?)
(code:line (define xs (take (list x y z) n)) (code:comment "xs is a symbolic list"))
(code:line (define vs (list->vector xs)) (code:comment "vs is a symbolic vector"))
(define sol (solve (assert (= 4 (vector-ref vs (sub1 n))))))
(evaluate vs sol)
(evaluate xs sol)]
The following vector operations are lifted to work on both concrete and symbolic values:
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
(list (list @elem{@vector-ops, @more-vector-ops}))]
@(kill-evaluator rosette-eval)

View File

@ -1,257 +0,0 @@
;; This file was created by make-log-based-eval
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
(b
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "b"))))
#""
#"")
((boolean? b) ((3) 0 () 0 () () (q values #t)) #"" #"")
((integer? b) ((3) 0 () 0 () () (q values #f)) #"" #"")
((vector b 1)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (v! (0 (u . "b")) 1)))
#""
#"")
((not b)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(! b)"))))
#""
#"")
((boolean? (not b)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((define-symbolic* n integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (static) (define-symbolic x boolean?) x)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (dynamic) (define-symbolic* y integer?) y)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((eq? (static) (static)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((eq? (dynamic) (dynamic))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(= y$0 y$1)"))))
#""
#"")
((define (yet-another-x) (define-symbolic x boolean?) x)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((eq? (static) (yet-another-x))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(<=> x x)"))))
#""
#"")
((assert #t) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assert #f) ((3) 0 () 0 () () (q exn "assert: failed")) #"" #"")
((assert (not b)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "(! b)")) q #f)))
#""
#"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts) ((3) 0 () 0 () () (q values ())) #"" #"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (poly x) (+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (factored x) (* x (+ x 1) (+ x 2) (+ x 2)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (same p f x) (assert (= (p x) (f x))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((same poly factored 0) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((same poly factored -1) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((same poly factored -2) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic i integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define cex (verify (same poly factored i)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate i cex) ((3) 0 () 0 () () (q values 12)) #"" #"")
((same poly factored 12) ((3) 0 () 0 () () (q exn "assert: failed")) #"" #"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((require (only-in racket/draw read-bitmap))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((require rosette/query/debug) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (poly x) (+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define/debug (factored x) (* x (+ x 1) (+ x 2) (+ x 2)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (same p f x) (assert (= (p x) (f x))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define ucore (debug (integer?) (same poly factored 12)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((require rosette/lib/synthax) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (poly x) (+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (factored x) (* (+ x (??)) (+ x 1) (+ x (??)) (+ x (??))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (same p f x) (assert (= (p x) (f x))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define-symbolic i integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define binding
(synthesize #:forall (list i) #:guarantee (same poly factored i)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
('(define (factored x) (* (+ x 0) (+ x 1) (+ x 2) (+ x 3)))
((3)
0
()
0
()
()
(q values (define (factored x) (* (+ x 0) (+ x 1) (+ x 2) (+ x 3)))))
#""
#"")
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define sol
(solve
(begin
(assert (not (= x y)))
(assert (< (abs x) 10))
(assert (< (abs y) 10))
(assert (not (= (poly x) 0)))
(assert (= (poly x) (poly y))))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate x sol) ((3) 0 () 0 () () (q values -5)) #"" #"")
((evaluate y sol) ((3) 0 () 0 () () (q values 2)) #"" #"")
((evaluate (poly x) sol) ((3) 0 () 0 () () (q values 120)) #"" #"")
((evaluate (poly y) sol) ((3) 0 () 0 () () (q values 120)) #"" #"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((current-bitwidth 5) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((solve (assert (= x 64)))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)"))))
#""
#"")
((verify (assert (not (= x 64))))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)"))))
#""
#"")
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((solve (assert (= x 64)))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [x 64])"))))
#""
#"")
((verify (assert (not (= x 64))))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [x 64])"))))
#""
#"")
((define-symbolic x integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((letrec ((adder
(lambda (vs n)
(if (null? vs) (list) (cons (+ (car vs) n) (adder (cdr vs) n))))))
(adder '(1 2 3) x))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(c (0 (u . "(+ 1 x)")) c (0 (u . "(+ 2 x)")) c (0 (u . "(+ 3 x)")))))
#""
#"")

View File

@ -1,314 +0,0 @@
#lang scribble/manual
@(require (for-label racket)
(for-label
rosette/base/form/define (only-in rosette/base/core/safe assert)
rosette/query/query (only-in rosette asserts clear-asserts!)
(only-in rosette/base/base bv?)
(except-in rosette/query/debug assert) rosette/query/eval
(only-in rosette/lib/synthax ?? print-forms) rosette/lib/render))
@(require racket/sandbox racket/runtime-path scribble/core
scribble/eval scribble/html-properties ;scriblib/footnote
(only-in racket [unsyntax racket/unsyntax])
(only-in racket/draw read-bitmap))
@(require (only-in "../refs.scrbl" ~cite rosette:onward13 rosette:pldi14)
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "essentials-log")))
@(define (symbolic s) @racketresultfont[s])
@(define seen '())
@(define (footnote . xs)
(define ord (add1 (length seen)))
(define mark (superscript (format "~a" ord)))
(define t (format "footnote~a" ord))
(set! seen (cons (apply elemtag t mark xs) seen))
(elemref t mark))
@(define (footnote-part)
(let ([ts (reverse seen)])
(if (null? ts)
null
(cons (para #:style (style #f (list (attributes '((class . "footnoteblock")))))
(car ts))
(map para (cdr ts))))))
@;(define-footnote footnote footnote-part)
@title[#:tag "ch:essentials"]{Rosette Essentials}
Rosette adds to Racket a collection of solver-aided facilities.
These facilities enable programmers to conveniently access a constraint solver
that can answer interesting questions about program behaviors. They are based on three
key concepts: @emph{symbolic values}, @emph{assertions} and @emph{queries}.
We use assertions to express desired program behaviors and symbolic values to
formulate queries about these behaviors.
This chapter illustrates the basics of solver-aided programming with a
few simple examples. More advanced tutorials, featuring extended examples, can be found
in Section 2 of @~cite[rosette:onward13 rosette:pldi14].@footnote{Code examples in
these references are written in Rosette 1.0.
While Rosette 2.0 is not backward compatible with Rosette 1.0,
they share the same conceptual core.}
The following chapters describe the subset
of Racket that can be @seclink["sec:langs"]{safely} used with solver-aided facilities, including the
supported datatypes (both @seclink["ch:built-in-datatypes"]{built-in}
and @seclink["ch:programmer-defined-datatypes"]{programmer-defined}),
@seclink["ch:syntactic-forms"]{syntactic forms}, and @seclink["ch:libraries"]{libraries}.
@section[#:tag "sec:symbolic-values"]{Symbolic Values}
The Rosette language includes two kinds of values: concrete and symbolic. Concrete values are plain Racket values (@racket[#t], @racket[#f], @racket[0], @racket[1], etc.), and Rosette programs that operate only on concrete values behave just like Racket programs. Accessing the solver-aided features of Rosette---such as code synthesis or verification---requires the use of symbolic values.
@deftech[#:key "symbolic constant"]{Symbolic constants} are the simplest kind of symbolic value. They can be created using the @racket[define-symbolic] form:
@def+int[#:eval rosette-eval
(define-symbolic b boolean?)
b]
This generates a fresh symbolic constant of type boolean and binds it to the variable @racket[b].
You can think of a symbolic constant as a placeholder for a concrete constant of the same type. As we will see shortly, the solver, once called, determines which concrete value a given symbolic constant represents: it will tell us whether the constant @symbolic{b} is @racket[#t] or @racket[#f], depending on what question we ask about the behavior of a program (or a procedure) applied to @symbolic{b}.
Symbolic values, including constants, can be used just like concrete values of the same type. They can be stored in data structures or passed to procedures to obtain other values, either concrete or symbolic:
@interaction[#:eval rosette-eval
(boolean? b)
(integer? b)
(vector b 1)
(not b)
(boolean? (not b))]
In our example, all but the fourth expression produce concrete values. The fourth expression returns another symbolic value---specifically, a symbolic @emph{expression} of type boolean. This expression represents the negation of @symbolic{b}. If the solver determines that @symbolic{b} is @racket[#t], for example, then @symbolic{(! b)} will be interpreted as @racket[#f].
Rosette provides one more construct for creating symbolic constants besides @racket[define-symbolic]:
@def+int[#:eval rosette-eval
(define-symbolic* n integer?)]
The two constructs differ in how they bind variables to constants when evaluated more than once.
The @racket[define-symbolic] form binds the variable to the same (unique) constant every time it is evaluated. The @racket[define-symbolic*] form, in contrast, creates a stream of (unique) constants, binding the variable to the next constant from its stream whenever the form is evaluated. The following example illustrates the difference:
@defs+int[#:eval rosette-eval
((define (static)
(define-symbolic x boolean?) (code:comment "creates the same constant when evaluated")
x)
(define (dynamic)
(define-symbolic* y integer?) (code:comment "creates a different constant when evaluated")
y))
(eq? (static) (static))
(eq? (dynamic) (dynamic))]
Printed constant names, such as @symbolic{x} or @symbolic{b}, are just comments. Two constants created by evaluating two distinct @racket[define-symbolic] (or, @racket[define-symbolic*]) forms are distinct, even if they have the same printed name. They may still represent the same concrete value, but that is determined by the solver:
@def+int[#:eval rosette-eval
(define (yet-another-x)
(define-symbolic x boolean?)
x)
; Produces a boolean expression whose meaning is 'true' if and only if the
; constant returned by (static) and the constant returned by (yet-another-x)
; have the same concrete interpretation.
(eq? (static) (yet-another-x))]
@section[#:tag "sec:asserts"]{Assertions}
Like many other languages, Rosette provides a construct for expressing @emph{assertions}---important properties of programs that are checked in every execution. Rosette assertions work just like Java or Racket assertions when given a concrete value: if the value is false, the execution terminates with a runtime error. Otherwise, the execution proceeds normally.
@interaction[#:eval rosette-eval
(assert #t)
(assert #f)]
When given a symbolic boolean value, however, a Rosette assertion has no immediate effect. Instead, its effect (whether it passes or fails) is eventually determined by the solver.
@interaction[#:eval rosette-eval
(code:comment "add (not b) to the stack of assertions to be solved")
(assert (not b))
(code:comment "retrieve the assertion store")
(asserts)
(code:comment "clear the assertion store")
(clear-asserts!)
(asserts)]
@(rosette-eval '(clear-asserts!))
@section[#:tag "sec:queries"]{Solver-Aided Queries}
The solver reasons about asserted properties only when we ask a question about them---for example, "Does my program have an execution that violates an assertion?" We pose such @emph{solver-aided queries} with the help of constructs explained in the remainder of this chapter.
We will illustrate the queries on the following toy example, where the @racket[factored] polynomial is intended to behave just like @racket[poly] on all inputs:
@defs+int[#:eval rosette-eval
((define (poly x)
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
(define (factored x)
(* x (+ x 1) (+ x 2) (+ x 2)))
(define (same p f x)
(assert (= (p x) (f x)))))
(code:comment "check zeros; all seems well ...")
(same poly factored 0)
(same poly factored -1)
(same poly factored -2)]
@subsection[#:tag "sec:verify"]{Verification}
To verify that @racket[poly] and @racket[factored] behave identically, we could simply enumerate all k-bit integers and apply the @racket[same] check to each. This naive approach to verification would, of course, be very slow for a large k---and impossible for infinite precision integers. A better approach is to delegate such checks to a constraint solver, which can search large input spaces more effectively. In Rosette, this is done with the help of the @racket[verify] query:
@interaction[#:eval rosette-eval
(define-symbolic i integer?)
(define cex (verify (same poly factored i)))]
The @racket[(verify #, @var[expr])] form queries the solver for a @deftech{binding} from symbolic constants to concrete values that causes the evaluation of @var[expr] to fail when the bound symbolic constants are replaced with the corresponding concrete values. If such a binding exists, as it does in our case, it is called a @emph{counterexample}.
Bindings are first-class values in Rosette, and they can be freely manipulated by programs. We can also interpret any Rosette value with respect to a binding using the built-in @racket[evaluate] procedure:
@interaction[#:eval rosette-eval
(evaluate i cex)
(same poly factored 12)]
In our example, evaluating @racket[i] with respect to @racket[cex] reveals that @racket[poly] and @racket[factored] produce different results on the input 12 (thus causing the assertion in the @racket[same] procedure to fail).
@(rosette-eval '(clear-asserts!))
@(rosette-eval '(require (only-in racket/draw read-bitmap)))
@subsection[#:tag "sec:debug"]{Debugging}
Now that we have an input on which @racket[factored] differs from @racket[poly], the next step is to debug it, by figuring out which of its subexpressions are responsible for the fault. Rosette provides a query for this as well. To access it, we import the debugging facilities, mark @racket[factored] as a candidate for debugging, and issue a @racket[debug] query:
@racketblock[
(require rosette/query/debug rosette/lib/render)
(define (poly x)
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
(define/debug (factored x) (code:comment "define/debug marks a procedure as part of")
(* x (+ x 1) (+ x 2) (+ x 2))) (code:comment "the code to be debugged")
(define (same p f x)
(assert (= (p x) (f x))))
(code:comment "Call after saving the above definitions to a file:")
#, @elem{>} (define ucore (debug [integer?] (same poly factored 12)))
#, @elem{>} (render ucore)
#,(call-with-input-file (build-path root "pict.png") (lambda (in) (read-bitmap in 'png)))]
@(rosette-eval '(require rosette/query/debug))
@(rosette-eval '(define (poly x)
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x))))
@(rosette-eval '(define/debug (factored x)
(* x (+ x 1) (+ x 2) (+ x 2))))
@(rosette-eval '(define (same p f x)
(assert (= (p x) (f x)))))
@(rosette-eval '(define ucore (debug [integer?] (same poly factored 12))))
The @racket[(debug [#, @var[predicate]] #, @var[expr])] query takes as input an expression whose execution leads to an assertion failure, and one or more dynamic type predicates specifying which executed expressions should be treated as potentially faulty by the solver. That is, the predicates express the hypothesis that the failure is caused by an expression with one of the given types. Expressions that produce values of a different type are assumed to be correct.@footnote{For now, only primitive (@racket[boolean?], @racket[integer?], @racket[real?], and @racket[bv?]) types can be used in @racket[debug] forms.}
The output of a @racket[debug] query is a minimal set of program expressions, called a @deftech[#:key "MUC"]{minimal unsatisfiable core}, that form an irreducible cause of the failure. Expressions outside of the core are irrelevant to the failure---there is no way to replace them with constants so that the resulting program satisfies the failing assertion. The failing assertion can only be satisfied if we are allowed to also replace one of the core expressions with a carefully chosen constant. In general, a failing expression may have many different cores, but since every core highlights a buggy subexpression, examining one or two cores often leads to the root cause of the error.
Like bindings, cores are first-class values. In our example, we simply visualize the core using the utility procedure @racket[render].@footnote{@racket[render] can only visualize cores for code that has been saved to a file.} The visualization reveals that the grayed-out subexpression @racket[(+ x 1)] is irrelevant to the failure of @racket[factored] on the input 12. To repair this failure, we have to modify at least one of the remaining expressions, which are highlighted in red.
@subsection[#:tag "sec:synthesize"]{Synthesis}
The solver can not only find failure-inducing inputs and localize faults, it can also synthesize repairs for buggy expressions. To repair a program, we first replace each buggy expression with a syntactic "@deftech{hole}." A program with holes is called a @deftech{sketch}. The solver completes a sketch by filling its holes with expressions, in such a way that all assertions in the resulting program pass on all inputs.
The following code snippet shows the sketch for our buggy @racket[factored] procedure. We obtained it by replacing the constants in the @seclink["sec:debug"]{minimal core} with @racket[(??)] holes, which are filled with numerical constants.@footnote{This simple replacement strategy is sufficient since we know that a factorization of an @var{n}-degree polynomial takes the form @tt{(* (+ x @var[c]@subscript{0}) ... (+ x @var[c]@subscript{@var{n}}))}, where @var[c]@subscript{@var{i}} is a constant.}
@defs+int[#:eval rosette-eval
((require rosette/lib/synthax)
(define (poly x)
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
(define (factored x)
(* (+ x (??)) (+ x 1) (+ x (??)) (+ x (??))))
(define (same p f x)
(assert (= (p x) (f x)))))]
The @racket[(??)] construct is imported from the @racket[rosette/lib/synthax] library, which also provides constructs for specifying more complex holes. For example, you can specify a hole that is filled with an expression, drawn from a grammar you define.
We query the solver for a correct completion of our sketch as follows:
@interaction[#:eval rosette-eval
(code:comment "Call after saving the above definitions to a file:")
(define-symbolic i integer?)
(define binding
(synthesize #:forall (list i)
#:guarantee (same poly factored i)))
(eval:alts (print-forms binding) '(define (factored x) (* (+ x 0) (+ x 1) (+ x 2) (+ x 3))))]
The @racket[(synthesize #:forall #, @var[input] #:guarantee #, @var[expr])] query uses the @var[input] form to specify a set of distinguished symbolic values, which are treated as inputs to the expression @var[expr]. The result, if any, is a binding for the remaining symbolic values, created by evaluating holes. This binding guarantees successful evaluation of @var[expr] for @emph{all} possible bindings of the @var[input] values. Passing it to the @racket[print-forms] procedure yields a syntactic representation of the completed sketch.@footnote{@racket[print-forms] can only print the completion of a sketch that has been saved to a file.}
@subsection[#:tag "sec:solve"]{Angelic Execution}
Rosette supports one more solver-aided query, which we call "angelic execution." This query is the opposite of verification. Given a program with symbolic values, it instructs the solver to find a binding for them that will cause the program to execute successfully---that is, without any assertion failures.
Angelic execution can be used to solve puzzles, to run incomplete code, or to "invert" a program, by searching for inputs that produce a desired output. For example, we can ask the solver to find two distinct input values, which are not zeros of the @racket[poly] function, but which @racket[poly] still maps to the same output:
@interaction[#:eval rosette-eval
(define-symbolic x y integer?)
(define sol
(solve (begin (assert (not (= x y)))
(assert (< (abs x) 10))
(assert (< (abs y) 10))
(assert (not (= (poly x) 0)))
(assert (= (poly x) (poly y))))))
(evaluate x sol)
(evaluate y sol)
(evaluate (poly x) sol)
(evaluate (poly y) sol)]
You can find more examples of angelic execution and other solver-aided queries in the @hyperlink["https://github.com/emina/rosette/blob/master/sdsl/"]{@racket[sdsl]} folder of your Rosette distribution.
@(rosette-eval '(clear-asserts!))
@section[#:tag "sec:notes"]{Symbolic Reasoning}
Rosette implements solver-aided queries by translating them to the input language of an SMT solver.
This translation is performed using a given @tech["reasoning precision"], as specified
by the @racket[current-bitwidth] parameter. Setting @racket[current-bitwidth]
to a positive integer @var{k} instructs Rosette to approximate both reals and integers with @var{k}-bit words.
Setting it to @racket[#f] instructs Rosette to use infinite precision for real and integer operations.
The following snippet shows the effect of different @racket[current-bitwdth] settings on query behavior:
@interaction[#:eval rosette-eval
(define-symbolic x integer?)
(current-bitwidth 5) (code:comment "no 5-bit solution or counterexample exists")
(solve (assert (= x 64)))
(verify (assert (not (= x 64))))
(current-bitwidth #f) (code:comment "but an integer solution and counterexample do exist")
(solve (assert (= x 64)))
(verify (assert (not (= x 64))))]
By default, @racket[current-bitwidth] is set to 5. Beware that using a large @var{k} or @racket[#f]
may have a negative effect on solver performance. In the worst case, using @racket[#f] can cause the underlying solver to run forever.@footnote{Technically, Rosette translates solver-aided queries to the theory of bitvectors when @racket[current-bitwidth] is set to an integer @var{k}. In particular, it uses @var{k}-bit bitvectors to represent integers and reals, with smaller bitvectors leading to better performance. When @racket[current-bitwidth] is @racket[#f], Rosette uses the theories of integers and reals instead. These theories work well for linear constraints, but reasoning about non-linear integer arithmetic is undecidable.}
Non-termination can also be caused by passing symbolic values to recursive procedures. In particular, the expression that determines whether a recursion (or a loop) terminates must be executed on concrete values.
@interaction[
(code:comment "while sandboxed evaluation of (ones x) times out,")
(code:comment "normal evaluation would not terminate")
(eval:alts (define-symbolic x integer?) (void))
(eval:alts
(letrec ([ones (lambda (n)
(if (<= n 0)
(list)
(cons 1 (ones (- n 1)))))])
(printf "~a" (ones 3))
(printf "~a" (ones x)))
(begin (printf "(1 1 1)")
(fprintf (current-error-port) "with-limit: out of time")))]
It is, however, safe to apply recursive procedures to symbolic values if they are not used in termination checks.
@interaction[#:eval rosette-eval
(define-symbolic x integer?)
(letrec ([adder (lambda (vs n)
(if (null? vs)
(list)
(cons (+ (car vs) n) (adder (cdr vs) n))))])
(adder '(1 2 3) x))]
See Chapters @seclink["ch:symbolic-reflection"]{7} and @seclink["ch:unsafe"]{8} to
learn more about common patterns and anti-patterns for effective symbolic reasoning.
@(kill-evaluator rosette-eval)
@(footnote-part)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -1,43 +0,0 @@
#lang rosette/safe
;(current-bitwidth #f)
(require rosette/query/debug rosette/lib/render)
(define (poly x)
(+ (* x x x x) (* 6 x x x) (* 11 x x) (* 6 x)))
(define/debug (factored x)
(* x (+ x 1) (+ x 2) (+ x 2)))
(define (same p f x)
(assert (= (p x) (f x))))
(define-symbolic i integer?)
(define cex (verify (same poly factored i)))
(evaluate i cex)
(define c (debug [integer?] (same poly factored 12)))
(render c)
(require rosette/lib/synthax)
(define (factored* x)
(* (+ x (??)) (+ x 1) (+ x (??)) (+ x (??))))
(define binding
(synthesize #:forall (list i)
#:guarantee (same poly factored* i)))
(print-forms binding)
(define-symbolic x y integer?)
(define env
(solve (begin (assert (not (= x y)))
(assert (< (abs x) 10))
(assert (< (abs y) 10))
(assert (not (= (poly x) 0)))
(assert (= (poly x) (poly y))))))
env

View File

@ -1,13 +0,0 @@
#lang scribble/manual
@(require (for-label racket))
@title[#:tag "ch:syntactic-forms" #:style 'toc]{Syntactic Forms}
The core of the Rosette language (@racket[rosette/safe]) consists of two kinds of syntax forms: a set of basic forms @deftech[#:key "lifted constructs"]{lifted} from Racket, and a set of forms for @seclink["ch:essentials"]{solver-aided programming}. We use the term "lifted" to refer to parts of the Racket language that can be used with symbolic values and other solver-aided constructs.
@[table-of-contents]
@include-section["racket-forms.scrbl"]
@include-section["rosette-forms.scrbl"]

View File

@ -1,351 +0,0 @@
;; This file was created by make-log-based-eval
((define (always-same) (define-symbolic x integer?) x)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((always-same)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "x"))))
#""
#"")
((always-same)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "x"))))
#""
#"")
((eq? (always-same) (always-same)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((define (always-different) (define-symbolic* x integer?) x)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((always-different)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "x$0"))))
#""
#"")
((always-different)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "x$1"))))
#""
#"")
((eq? (always-different) (always-different))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(= x$2 x$3)"))))
#""
#"")
((assert #t) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assert 1) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts) ((3) 0 () 0 () () (q values ())) #"" #"")
((define-symbolic x boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assert x) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "x")))))
#""
#"")
((assert #f "bad value")
((3) 0 () 0 () () (q exn "assert: bad value"))
#""
#"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c #f c (0 (u . "x")))))
#""
#"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts) ((3) 0 () 0 () () (q values ())) #"" #"")
((define-symbolic x y boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assert x) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "x")))))
#""
#"")
((define sol (solve (assert y)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "x")))))
#""
#"")
((evaluate x sol) ((3) 0 () 0 () () (q values #t)) #"" #"")
((evaluate y sol) ((3) 0 () 0 () () (q values #t)) #"" #"")
((solve (assert (not x)))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)"))))
#""
#"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x y boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assert x) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "x")))))
#""
#"")
((define sol (verify (assert y)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "x")))))
#""
#"")
((evaluate x sol) ((3) 0 () 0 () () (q values #t)) #"" #"")
((evaluate y sol) ((3) 0 () 0 () () (q values #f)) #"" #"")
((verify #:assume (assert y) #:guarantee (assert (and x y)))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)"))))
#""
#"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x c integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assert (even? x)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "(= 0 (remainder x 2))")))))
#""
#"")
((define sol
(synthesize #:forall (list x) #:guarantee (assert (odd? (+ x c)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "(= 0 (remainder x 2))")))))
#""
#"")
((evaluate x sol)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "x"))))
#""
#"")
((evaluate c sol) ((3) 0 () 0 () () (q values 1)) #"" #"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assert (< x 2)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "(< x 2)")))))
#""
#"")
((define sol
(optimize #:maximize (list (+ x y)) #:guarantee (assert (< (- y x) 1))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((asserts)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "(< x 2)")))))
#""
#"")
((evaluate x sol) ((3) 0 () 0 () () (q values 1)) #"" #"")
((evaluate y sol) ((3) 0 () 0 () () (q values 1)) #"" #"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((current-bitwidth 5) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x y real?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((current-bitwidth 5) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((solve (assert (= x 3.5)))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)"))))
#""
#"")
((solve (assert (= x 64)))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)"))))
#""
#"")
((solve (assert (forall (list x) (= x (+ x y)))))
((3)
0
()
0
()
()
(q
exn
"finitize: cannot use (current-bitwidth 5) with a quantified formula (forall (x) (= x (+ x y))); use (current-bitwidth #f) instead"))
#""
#"")
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((solve (assert (= x 3.5)))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [x 7/2])"))))
#""
#"")
((solve (assert (= x 64)))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [x 64.0])"))))
#""
#"")
((solve (assert (forall (list x) (= x (+ x y)))))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [y 0.0])"))))
#""
#"")

View File

@ -1,308 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/form rosette/query/eval rosette/solver/solution
rosette/base/core/term (only-in rosette/query/debug define/debug debug)
(only-in rosette/query/finitize current-bitwidth)
(only-in rosette/base/core/safe assert)
(only-in rosette/base/core/bool asserts clear-asserts!)
(only-in rosette/base/base bv?)
(only-in rosette/base/core/function function?))
(for-label racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "rosette-forms-log")))
@title[#:tag "ch:syntactic-forms:rosette"]{Solver-Aided Forms}
The @seclink["ch:essentials"]{Essentials} chapter introduced the key concepts of solver-aided programming. This section defines the corresponding syntactic constructs more precisely.
@declare-exporting[rosette/base/form/define
rosette/query/form
rosette/base/core/safe
rosette/base/core/bool
rosette/query/finitize
#:use-sources
(rosette/base/form/define
rosette/query/form
rosette/base/core/safe
rosette/base/core/bool
rosette/query/finitize)]
@section[#:tag "sec:symbolic-constants"]{Symbolic Constants}
@defform[(define-symbolic id ...+ type)
#:contracts
[(type (and/c solvable? type?))]]{
Binds each provided identifier to a distinct @tech["symbolic constant"] of the given
@tech["solvable type"]. The identifiers are bound to the same constants every time the form is
evaluated.
@examples[#:eval rosette-eval
(define (always-same)
(define-symbolic x integer?)
x)
(always-same)
(always-same)
(eq? (always-same) (always-same))]
}
@defform[(define-symbolic* id ...+ type)
#:contracts
[(type (and/c solvable? type?))]]{
Creates a stream of distinct @tech["symbolic constant"] of the given
@tech["solvable type"] for each identifier, binding the identifier to the
next element from its stream every time the form is evaluated.
@examples[#:eval rosette-eval
(define (always-different)
(define-symbolic* x integer?)
x)
(always-different)
(always-different)
(eq? (always-different) (always-different))]
}
@section[#:tag "sec:assertions"]{Assertions}
@defform[(assert expr maybe-message)
#:grammar
[(maybe-message (code:line) expr)]
#:contracts
[(expr (or/c string? procedure?))]]{
Provides a mechanism for communicating desired
program properties to the underlying solver. Rosette keeps track of all
assertions evaluated during an execution in an @tech{assertion store}.
If @racket[expr] evaluates to @racket[#f], an error is thrown using the
optional failure message, and @racket[#f] is added to the assertion store. The error message
can be either a string or a no-argument procedure that throws an error when called.
If @racket[expr] evaluates to a symbolic boolean value, that value is added to the assertion store.
If @racket[expr] evaluates to any other value, @racket[assert] has no effect. The contents
of the assertion store can be examined using the @racket[asserts] procedure, and they can be
cleared using the @racket[clear-asserts!] procedure.
@examples[#:eval rosette-eval
(code:line (assert #t) (code:comment "no effect"))
(code:line (assert 1) (code:comment "no effect"))
(code:line (asserts) (code:comment "retrieve the assertion store"))
(define-symbolic x boolean?)
(assert x)
(code:line (asserts) (code:comment "x added to the assertion store"))
(assert #f "bad value")
(asserts)
(code:line (clear-asserts!) (code:comment "clear the assertion store"))
(asserts)]
}
@section{Angelic Execution}
@defform[(solve expr)]{
Searches for a binding of symbolic constants to concrete values that satisfies all assertions encountered
before the invocation of @racket[solve] and during the evaluation of @racket[expr].
If such a binding exists, it is returned in the form of a satisfiable @racket[solution?]; otherwise,
the result is an unsatisfiable solution. The assertions encountered while
evaluating @racket[expr] are removed from the global @tech["assertion store"] once @racket[solve] returns. As a result,
@racket[solve] has no observable effect on the @tech["assertion store"].
The solver's ability to find solutions depends on the current @tech["reasoning precision"], as
determined by the @racket[current-bitwidth] parameter.
@examples[#:eval rosette-eval
(define-symbolic x y boolean?)
(assert x)
(code:line (asserts) (code:comment "x added to the assertion store"))
(define sol (solve (assert y)))
(code:line (asserts) (code:comment "assertion store same as before"))
(code:line (evaluate x sol) (code:comment "x must be true"))
(code:line (evaluate y sol) (code:comment "y must be true"))
(solve (assert (not x)))]
@;We refer to the
@;@racket[solve] query as @deftech{angelic execution} because it causes the solver to behave as an
@;angelic oracle---it supplies "good" bindings for symbolic constants that cause the execution to terminate successfully.
}
@(rosette-eval '(clear-asserts!))
@section{Verification}
@defform*[((verify guarantee-expr)
(verify #:assume assume-expr #:guarantee guarantee-expr))]{
Searches for a binding of symbolic constants to concrete values that violates at least one of the
assertions encountered during the evaluation of @racket[guarantee-expr], but that satisfies all
assertions encountered before the invocation of @racket[verify] and during the evaluation of
@racket[assume-expr]. If such a binding exists, it is returned in the form of a
satisfiable @racket[solution?]; otherwise, the result is an unsatisfiable solution. The assertions encountered while
evaluating @racket[assume-expr] and @racket[guarantee-expr] are removed from the global @tech["assertion store"] once
@racket[verify] returns.
The solver's ability to find solutions depends on the current @tech["reasoning precision"], as
determined by the @racket[current-bitwidth] parameter.
@examples[#:eval rosette-eval
(define-symbolic x y boolean?)
(assert x)
(code:line (asserts) (code:comment "x added to the assertion store"))
(define sol (verify (assert y)))
(code:line (asserts) (code:comment "assertion store same as before"))
(code:line (evaluate x sol) (code:comment "x must be true"))
(code:line (evaluate y sol) (code:comment "y must be false"))
(verify #:assume (assert y) #:guarantee (assert (and x y)))]
}
@(rosette-eval '(clear-asserts!))
@section{Synthesis}
@defform[(synthesize
#:forall input-expr
maybe-assume
#:guarantee guarantee-expr)
#:grammar ([maybe-assume (code:line) (code:line #:assume assume-expr)])
#:contracts [(input-expr (listof constant?))]]{
Searches for a binding of symbolic constants
to concrete values that has the following properties:
@itemlist[#:style 'ordered
@item{it does not map constants in the @racket[input-expr] list; and,}
@item{it satisfies all assertions encountered during the evaluation of
@racket[guarantee-expr], for every binding of @racket[input-expr] constants to values that satisfies
the assertions encountered before the invocation of @racket[synthesize] and during the evaluation of
@racket[assume-expr].}]
If no such binding exists, the result is an unsatisfiable @racket[solution?]. The assertions encountered while
evaluating @racket[assume-expr] and @racket[guarantee-expr] are removed from the global @tech["assertion store"] once
@racket[synthesize] returns. These assertions @bold{may not include} @tech[#:key "quantified formula"]{quantified formulas}.
The solver's ability to find solutions depends on the current @tech["reasoning precision"],
as determined by the @racket[current-bitwidth] parameter.
@examples[#:eval rosette-eval
(define-symbolic x c integer?)
(assert (even? x))
(code:line (asserts) (code:comment "assertion pushed on the store"))
(define sol
(synthesize #:forall (list x)
#:guarantee (assert (odd? (+ x c)))))
(code:line (asserts) (code:comment "assertion store same as before"))
(code:line (evaluate x sol) (code:comment "x is unbound"))
(code:line (evaluate c sol) (code:comment "c must an odd integer"))]
}
@(rosette-eval '(clear-asserts!))
@section{Optimization}
@defform[(optimize
maybe-minimize
maybe-maximize
#:guarantee guarantee-expr)
#:grammar ([maybe-minimize (code:line) (code:line #:minimize minimize-expr)]
[maybe-maximize (code:line) (code:line #:maximize maximize-expr)])
#:contracts [(minimize-expr (listof (or/c integer? real? bv?)))
(maximize-expr (listof (or/c integer? real? bv?)))]]{
Searches for a binding of symbolic constants to concrete values that satisfies all assertions encountered
before the invocation of @racket[optimize] and during the evaluation of @racket[minimize-expr],
@racket[maximize-expr], and @racket[guarantee-expr].
If such a binding exists, it is returned in the form of a satisfiable @racket[solution?]; otherwise,
the result is an unsatisfiable solution. Any satisfiable solution returned by @racket[optimize] is optimal with respect
to the cost terms provided in the @racket[minimize-expr] and @racket[maximize-expr] lists. Specifically, these
terms take on the minimum or maximum values when evaluated with respect to a satisfiable solution. For more details on
solving optimization problems, see the
@hyperlink["http://rise4fun.com/z3opt/tutorialcontent/guide"]{Z3 optimization tutorial}.
As is the case for other solver-aided queries, the assertions encountered while
evaluating @racket[minimize-expr],
@racket[maximize-expr], and @racket[guarantee-expr] are removed from the global @tech["assertion store"] once
the query returns. As a result,
@racket[optimize] has no observable effect on the @tech["assertion store"].
The solver's ability to find solutions (as well as their optimality) depends on the current @tech["reasoning precision"],
as determined by the @racket[current-bitwidth] parameter.
@examples[#:eval rosette-eval
(code:line (current-bitwidth #f) (code:comment "use infinite-precision arithmetic"))
(define-symbolic x y integer?)
(assert (< x 2))
(code:line (asserts) (code:comment "assertion added to the store"))
(define sol
(optimize #:maximize (list (+ x y))
#:guarantee (assert (< (- y x) 1))))
(code:line (asserts) (code:comment "assertion store same as before"))
(code:line (evaluate x sol) (code:comment "x + y is maximal at x = 1"))
(code:line (evaluate y sol) (code:comment "and y = 1"))]
}
@(rosette-eval '(clear-asserts!))
@(rosette-eval '(current-bitwidth 5))
@section{Debugging}
@defmodule[rosette/query/debug #:use-sources (rosette/query/debug)]
@defform[(define/debug head body ...)
#:grammar
([head id (id ...)])]{
Defines a procedure or an expression, and marks it as a candidate for debugging.
When a @racket[debug] query is applied to a failing execution,
forms that are not marked in this way are considered
correct. The solver will apply the debugging algorithm only to
expressions and procedures marked as potentially faulty using
@racket[define/debug].
}
@defform[(debug [type ...+] expr)
#:contracts
([type (and/c solvable? type? (not/c function?))])]{
Searches for a minimal set of @racket[define/debug] expressions of
the given @tech["solvable type"](s) that are collectively responsible for the observed failure of @racket[expr].
If no expressions of the specified types are relevent to the failure, an error is thrown. The
returned expressions, if any, are called a minimal unsatisfiable core. The core expressions
are relevant to the observed failure in that preventing the failure requries modifying at least one
core expression. In particular, if all of the non-core expressions were replaced with
fresh constants created using @racket[define-symbolic*], @racket[(solve expr)] would still fail. It
can only execute successfully if at least one of the core expressions is also
replaced with a fresh constant. See the @seclink["ch:essentials"]{Essentials} chapter for example uses of
the @racket[debug] form.}
@section[#:tag "sec:reasoning-precision"]{Reasoning Precision}
@defparam[current-bitwidth k (or/c #f positive-integer?)
#:value 5]{
A parameter that defines the current @deftech[#:key "reasoning precision"]{reasoning precision}
for solver-aided queries over @racket[integer?] and @racket[real?] constants.
Setting @racket[current-bitwidth] to a positive integer @racket[k] instructs Rosette to approximate
both reals and integers with signed @racket[k]-bit words. Setting it to @racket[#f] instructs Rosette to use
infinite precision for real and integer operations. As a general rule, @racket[current-bitwidth] should
be set once, before any numeric operations are evaluated.
Technically, when @racket[current-bitwidth] is a positive integer @racket[k],
Rosette translates queries over reals and integers into constraints in the
@hyperlink["http://rise4fun.com/z3/tutorial"]{theory of bitvectors}
(of size @racket[k]), which can be efficiently decided by SMT solvers.
When this form of translation is used, a @racket[solve] or @racket[verify]
query will produce a satisfiable result if and only if there is a
solution under @racket[k]-bit semantics that is also correct under infinite-precision semantics.
(Note that this guarantee is limited in the case of unsatisfiability---it says only
that no @racket[k]-bit solution corresponds to an infinite-precision solution.)
Rosette does not provide such a soundness guarantee for other queries because it is
computationally expensive or impossible to provide. A @racket[synthesize] query, for example,
may produce a solution that is correct under @racket[k]-bit semantics, but incorrect under
infinite-precision semantics.
When @racket[current-bitwidth] is @racket[#f], Rosette translates queries over
reals and integers into constraints in the
@hyperlink["http://rise4fun.com/z3/tutorial"]{theories of reals and integers}.
These theories are effectively decidable only for linear constraints,
so most applications will perform better when @racket[current-bitwidth] is
set to a positive integer.
The @racket[current-bitwidth] parameter must be set to @racket[#f] when
executing queries over assertions that contain @tech[#:key "quantified formula"]{quantified formulas}.
Otherwise, such a query will throw an exception.
@examples[
#:eval rosette-eval
(define-symbolic x y real?)
(current-bitwidth 5)
(code:line (solve (assert (= x 3.5))) (code:comment "there is no solution under"))
(code:line (solve (assert (= x 64))) (code:comment "5-bit signed integer semantics"))
(code:line (solve (assert (forall (list x) (= x (+ x y))))) (code:comment "and quantifiers are not supported"))
(current-bitwidth #f)
(code:line (solve (assert (= x 3.5))) (code:comment "but there is a solution under"))
(code:line (solve (assert (= x 64))) (code:comment "infinite-precision semantics"))
(code:line (solve (assert (forall (list x) (= x (+ x y))))) (code:comment "and quantifiers work"))]
}
@(kill-evaluator rosette-eval)

View File

@ -1,43 +0,0 @@
#lang rosette
(require rosette/lib/synthax)
(define (div2 x) ([choose bvshl bvashr bvlshr bvadd bvsub bvmul] x (?? (bitvector 8))))
(define-symbolic i (bitvector 8))
(define m1
(synthesize #:forall (list i)
#:guarantee (assert (equal? (div2 i) (bvudiv i (bv 2 8))))))
(print-forms m1)
(generate-forms m1)
(define-synthax (nnf x y depth)
#:base (choose x (! x) y (! y))
#:else (choose
x (! x) y (! y)
((choose && ||) (nnf x y (- depth 1))
(nnf x y (- depth 1)))))
(define (nnf=> x y) (nnf a b 1))
(define-symbolic a b boolean?)
(print-forms
(synthesize
#:forall (list a b)
#:guarantee (assert (equal? (=> a b) (nnf=> a b)))))
#|
(define-synthax [shift terminal ... k]
#:assert (>= k 0)
[choose
terminal ... (??)
([choose >> << >>>] (shift terminal ... (- k 1))
(shift terminal ... (- k 1)))])
(define (div2mul4 x) (shift x 2))
(define m2
(synthesize #:forall (list i)
#:assume (assert (>= i 0))
#:guarantee (assert (= (div2mul4 i) (* 4 (quotient i 2))))))
(print-forms m2)|#

View File

@ -1,155 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/solver/solution rosette/query/query rosette/query/eval
rosette/base/core/term rosette/lib/angelic
(except-in rosette/query/debug assert )
(only-in rosette/lib/synthax ?? choose define-synthax generate-forms print-forms)
(only-in rosette/base/core/safe assert)
(only-in rosette/base/base function? bitvector bvshl bvashr bvlshr bvadd bvsub bvmul)
rosette/lib/render
racket (only-in pict pict?))
scribble/core scribble/html-properties scribble/eval racket/sandbox
"../util/lifted.rkt")
@(define rosette-eval (rosette-evaluator))
@title[#:tag "sec:rosette-libs"]{Solver-Aided Libraries}
In principle, solver-aided programming requires only symbolic values and the basic constructs described in Chapter @seclink["ch:syntactic-forms:rosette"]{3}. In practice, however, it is often convenient to work with richer constructs, which are built on top of these primitives. Rosette ships with three libraries that provide such constructs, as well as utility procedures for turning the results of synthesis and debugging queries into code.
@section{Synthesis Library}
@defmodule[rosette/lib/synthax #:use-sources (rosette/lib/synthax/core rosette/lib/synthax/form)]
@(rosette-eval '(require rosette/lib/synthax))
@defform[(?? maybe-type)
#:grammar [(maybe-type (code:line)
type-expr)]
#:contracts ([type-expr (and/c solvable? type? (not/c function?))])
]{
Introduces a @tech{hole} into a program---a placeholder for a concrete constant of the given type.
The default type for holes, if one is not provided, is @racket[integer?].
Chapter @seclink["sec:synthesize"]{2.3.3} shows an example of using integer holes to @tech{sketch}
a factored polynomial function, which is then completed with the help of a @racket[synthesize] query.
The @racket[(??)] construct @seclink["sec:symbolic-constants"]{creates}
and returns a fresh symbolic constant of type @racket[type-expr] (or @racket[integer?]).
}
@defform[(choose expr ...+)]{
Introduces a choice @tech{hole} into a program---a placeholder to be filled with one of the given expressions.
This construct defines @var[n]-1 fresh boolean constants and uses them to conditionally select one of the @var[n]
provided expressions.
@examples[#:eval rosette-eval
(define (div2 x)
([choose bvshl bvashr bvlshr bvadd bvsub bvmul] x (?? (bitvector 8))))
(define-symbolic i (bitvector 8))
(eval:alts
(print-forms
(synthesize #:forall (list i)
#:guarantee (assert (equal? (div2 i) (bvudiv i (bv 2 8))))))
'(define (div2 x) (bvlshr x (bv 1 8))))]
}
@defform*[((define-synthax id
([pattern expr] ...+))
(define-synthax (id terminal ... k)
#:base base-expr
#:else else-expr))]{
Defines a grammar of expressions that can be used to
fill holes of the form @racket[(id e ...)]. That is, writing
@racket[(id e ...)] introduces a @tech{hole} that is to
be filled with an expression from the @racket[id] grammar.
The first form, @racket[(define-synthax id ([pattern expr] ...+))],
works like Racket's @racket[syntax-rules] macro: it fills
the hole @racket[(id e ...)] with the expression @racket[expr] that
corresponds to the first @racket[pattern] matching the hole.
The second form, @racket[(define-synthax (id terminal ... k) #:base base-expr #:else else-expr)],
defines a recursive grammar that is inlined a finite number of times
to fill a hole. The @racket[base-expr] usually chooses among
the provided terminals,
while the @racket[else-expr] chooses among the terminals and a set of productions.
The hole @racket[(id e ... k)] must specify the inlining bound
@racket[k] as an integer literal.
@examples[#:eval rosette-eval
(eval:no-prompt
(code:comment "Defines a grammar for boolean expressions")
(code:comment "in negation normal form (NNF).")
(define-synthax (nnf x y depth)
#:base (choose x (! x) y (! y))
#:else (choose
x (! x) y (! y)
((choose && ||) (nnf x y (- depth 1))
(nnf x y (- depth 1))))))
(eval:no-prompt
(code:comment "The body of nnf=> is a hole to be filled with an")
(code:comment "expression of depth (up to) 1 from the NNF grammar.")
(define (nnf=> a b)
(nnf x y 1)))
(define-symbolic a b boolean?)
(eval:alts
(print-forms
(synthesize
#:forall (list a b)
#:guarantee (assert (equal? (=> a b) (nnf=> a b)))))
`(define (nnf=> x y) (,|| (! a) b)))
]
Since @racket[define-synthax] uses macros to implement recursive grammars,
instantiating a recursive grammar with a large limit (e.g., k > 3) can cause
long compilation times, especially if @racket[else-expr] contains many
recursive instantiations of the grammar.
}
@defproc[(generate-forms [solution solution?]) (listof syntax?)]{
Given a satisfiable @racket[solution?] to a @racket[synthesize] query,
returns a list of @tech{sketch} completions for that query.
Sketch completions can only be generated for programs that have been saved to disk.
}
@defproc[(print-forms [solution solution?]) void?]{
Pretty-prints the result of applying @racket[generate-forms] to the given
@racket[solution]. Sketch completions can only be generated and printed
for programs that have been saved to disk.
}
@section{Angelic Execution Library}
@defmodule[rosette/lib/angelic #:use-sources (rosette/lib/angelic)]
@(rosette-eval '(require rosette/lib/angelic))
@defproc[(choose* [v any/c] ...+) any/c]{
Non-determinstically chooses between the provided values. The difference
between @racket[choose*] and @racket[choose] is analogous to the difference
between @racket[define-symbolic*] and @racket[define-symbolic]: the former
is dynamic and the latter is static.
@examples[#:eval rosette-eval
(define (static) (choose 1 2 3))
(define (dynamic) (choose* 1 2 3))
(static)
(static)
(dynamic)
(dynamic)
(equal? (static) (static))
(equal? (dynamic) (dynamic))]
}
@section{Debugging Library}
@defmodule[rosette/lib/render #:use-sources (rosette/lib/render)]
@defproc[(render [solution solution?] [font-size natural/c 16]) pict?]{
Given an unsatisfiable @racket[solution?] to a @racket[debug] query, returns a
@racket[pict?] visualization of that solution. The visualization displays the
debugged code, highlighting the faulty expressions (i.e., those in the @racket[solution]'s core) in red.
The optional @racket[font-size] parameter controls the size of the font used to typeset the code.
Visualizations can only be constructed for programs that have been saved to disk.
See Chapter @seclink["sec:debug"]{2.3.2} for an example of using @racket[render].
}
@(kill-evaluator rosette-eval)

View File

@ -1,121 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/query/query rosette/base/form/define
rosette/base/core/term
(only-in rosette/base/core/bool pc asserts clear-asserts! with-asserts with-asserts-only)
(only-in rosette/base/core/safe assert)
racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox
"../util/lifted.rkt")
@(require (only-in "../refs.scrbl" ~cite rosette:pldi14))
@(define rosette-eval (rosette-evaluator))
@title[#:tag "sec:state-reflection"]{Reflecting on Symbolic State}
Like standard program execution, Rosette's symbolic evaluation @~cite[rosette:pldi14] can be
understood as a sequence of transitions from one @deftech{program state} to the next. In
addition to the memory and register values, the state of a Rosette program also includes
the current @deftech{path condition} and the current @deftech{assertion store}. The path
condition is a boolean value encoding the branch decisions taken to reach the present state,
and the assertion store is the set of boolean values (i.e., constraints) that have been asserted
so far. This section describes the built-in facilities for accessing and modifying various
aspects of the symbolic state from within a Rosette program. In general, these facilities should
only be used by expert Rosette programmers to implement low-level solver-aided tools.
@declare-exporting[rosette/base/base #:use-sources (rosette/base/core/bool rosette/base/core/term)]
@defproc[(pc) boolean?]{
Returns the current path condition.
@examples[#:eval rosette-eval
(define-symbolic a b boolean?)
(if a
(if b
#f
(pc))
#f)]
}
@defproc[(asserts) (listof boolean?)]{
Returns the contents of the @tech["assertion store"] as a list of symbolic
boolean values (or @racket[#f]) that have been asserted so far.
@examples[#:eval rosette-eval
(define-symbolic a b boolean?)
(assert a)
(asserts)
(assert b)
(asserts)]
}
@(rosette-eval '(clear-asserts!))
@defproc[(clear-asserts!) void?]{
Clears the @tech["assertion store"] from all symbolic
boolean values (or @racket[#f]) that have been asserted so far.
Rosette programs @emph{should not clear} the assertion
store unless they are implementing low-level tools.
@examples[#:eval rosette-eval
(define-symbolic a b boolean?)
(assert a)
(assert b)
(asserts)
(clear-asserts!)
(asserts)]
}
@(rosette-eval '(clear-asserts!))
@defform[(with-asserts expr)]{
Returns two values: the result of evaluating @racket[expr] and the assertions
generated during the evaluation of @racket[expr]. These
assertions will not appear in the assertion store after
@racket[with-asserts] returns.
@examples[#:eval rosette-eval
(define-symbolic a b boolean?)
(define-values (result asserted)
(with-asserts
(begin (assert a)
(assert b)
4)))
(printf "result = ~a\n" result)
(printf "asserted = ~a\n" asserted)
(asserts)
]
}
@(rosette-eval '(clear-terms!))
@defparam[term-cache h hash?]{
A parameter that holds the cache of all @seclink["sec:symbolic-terms"]{symbolic terms}
constructed during symbolic evaluation. This cache stores terms for the purposes of
partial cannonicalization. In particular, Rosette uses the term cache to ensure that no
syntactically identical terms are created. Rosette programs
@emph{should not modify} the term cache unless they are implementing low-level tools.
@examples[#:eval rosette-eval
(pretty-print (term-cache))
(define-symbolic a b c d integer?)
(pretty-print (term-cache))
(* d (- (+ a b) c))
(pretty-print (term-cache))]
}
@(rosette-eval '(clear-terms!))
@defproc[(clear-terms! [terms (or/c #f (listof term?)) #f]) void?]{
Clears the entire term-cache if invoked with @racket[#f] (default), or
it evicts all of the given @racket[terms] as well as any expressions that transitively
contain them. Rosette programs @emph{should not clear} the term cache unless they are
implementing low-level tools.
@examples[#:eval rosette-eval
(term-cache)
(define-symbolic a b c d integer?)
(pretty-print (term-cache))
(* d (- (+ a b) c))
(pretty-print (term-cache))
(clear-terms! (list c d))
(pretty-print (term-cache))
(clear-terms!)
(pretty-print (term-cache))]
}
@(kill-evaluator rosette-eval)

View File

@ -1,23 +0,0 @@
#lang rosette
(define-symbolic b boolean?)
(define v (vector 1))
(define w (vector 2 3))
(define s (if b v w))
s
(type-of s)
(eq? s v)
(eq? s w)
(define u (if b '(1 2) 3))
u
(type-of u)
(define (test)
(define-symbolic c boolean?)
(define v (if c #t 0))
(define u (if b (vector v) 4))
(list v u))
(test)
(union-contents u)

View File

@ -1,278 +0,0 @@
#lang scribble/manual
@(require (for-label
rosette/solver/solver rosette/solver/solution rosette/query/query
rosette/base/form/define rosette/query/eval (only-in rosette/base/base bitvector ~>)
rosette/base/core/term rosette/base/core/type rosette/base/core/union
(only-in rosette/base/core/bool asserts)
rosette/base/core/forall rosette/lib/lift ;rosette/lib/match
(only-in rosette/base/core/safe assert)
racket)
;(except-in racket match match* match-lambda))
scribble/core scribble/html-properties scribble/eval racket/sandbox
"../util/lifted.rkt")
@(define rosette-eval (rosette-evaluator))
@title[#:tag "sec:value-reflection"]{Reflecting on Symbolic Values}
There are two kinds of symbolic values in Rosette: symbolic terms and
symbolic unions. A Rosette program can inspect the representation of
both kinds of values. This is useful for @tech[#:key "lifted constructs"]{lifting} additional
(unlifted) Racket procedures to work on symbolic values, and for
controlling the performance of Rosette's symbolic evaluator.
@section[#:tag "sec:symbolic-terms"]{Symbolic Terms}
@declare-exporting[rosette/base/core/term #:use-sources (rosette/base/core/type rosette/base/core/term)]
A @deftech{symbolic term} is either a symbolic constant, created via
@seclink["sec:symbolic-constants"]{@code{define-symbolic[*]}},
or a symbolic expression, produced by a lifted operator.
Terms are strongly typed, and always belong to a @tech{solvable type}.
Symbolic values of all other (@tech[#:key "unsolvable type"]{unsolvable}) types take the form of
@seclink["sec:symbolic-unions"]{symbolic unions}.
@deftogether[(@defproc[(term? [v any/c]) boolean?]
@defproc[(expression? [v any/c]) boolean?]
@defproc[(constant? [v any/c]) boolean?])]{
Predicates for recognizing symbolic terms, expressions, and constants, respectively.
@examples[#:eval rosette-eval
(code:line (define-symbolic x integer?) (code:comment "constant"))
(code:line (define e (+ x 1)) (code:comment "expression"))
(list (term? x) (term? e))
(list (constant? x) (constant? e))
(list (expression? x) (expression? e))
(term? 1)]
}
@(rosette-eval '(require racket/match))
@deftogether[(@defform[(term content type)]
@defform[(expression op child ...+)]
@defform[(constant id type)])]{
Pattern matching forms for symbolic terms, expressions, and constants, respectively.
@examples[#:eval rosette-eval
(code:line (define-symbolic x integer?) (code:comment "constant"))
(code:line (define e (+ x 1)) (code:comment "expression"))
(match x
[(constant identifier type) (list identifier type)])
(match x
[(term content type) (list content type)])
(match e
[(expression op child ...) (cons op child)])
(match e
[(term content type) (list content type)])]}
@defproc*[([(type-of [v any/c] ...+) type?])]{
Returns the most specific @racket[type?] predicate that accepts all of the given values.
@examples[#:eval rosette-eval
(define-symbolic x integer?)
(type-of x)
(type-of (+ x 1))
(type-of x 3.14)
(type-of #t)
(type-of #t 1)]
}
@defproc[(type? [v any/c]) boolean?]{
Returns true when given a predicate that recognizes a @seclink["ch:built-in-datatypes"]{built-in} or
a @seclink["ch:programmer-defined-datatypes"]{structure} type. Otherwise returns false.
@examples[#:eval rosette-eval
(type? integer?)
(type? boolean?)
(type? list?)
(struct circle (radius))
(type? circle?)
(type? any/c)
(type? 1)]
}
@defproc[(solvable? [v any/c]) boolean?]{
Returns true if @racket[v] is a type predicate for a @tech{solvable type}.
@examples[#:eval rosette-eval
(solvable? boolean?)
(solvable? integer?)
(solvable? real?)
(solvable? (~> (bitvector 3) (bitvector 4)))
(solvable? list?)
(struct circle (radius))
(solvable? circle?)
(solvable? any/c)
]}
@section[#:tag "sec:symbolic-unions"]{Symbolic Unions}
@declare-exporting[rosette/base/core/union #:use-sources (rosette/base/core/union)]
Rosette represents symbolic values of an @tech[#:key "unsolvable type"]{unsolvable type}
(e.g., @racket[list?]) as @deftech[#:key "symbolic union"]{symbolic unions}.
A symbolic union is a set of two or more @deftech[#:key "guarded value"]{guarded values}.
A guarded value, in turn, combines a guard, which is a symbolic @racket[boolean?] term,
and a (non-union) value. Rosette's symbolic evaluator guarantees that the guards in
a symbolic union are disjoint: only one of them can ever be true. For example, the
symbolic vector @racket[s] defined below is represented as a symbolic union of two guarded vectors:
@interaction[#:eval rosette-eval
(define-symbolic b boolean?)
(define v (vector 1))
(define w (vector 2 3))
(define s (if b v w))
s
(type-of s)
(eq? s v)
(eq? s w)]
The values that appear in a union are themselves never unions. They may, however, contain unions.
They may also belong to several different types. In that case, the type of the union is the most
specific @racket[type?] predicate that accepts all members of the union.
This will always be an unsolvable type---possibly @racket[any/c], the most general unsolvable type.
@interaction[#:eval rosette-eval
(define-symbolic b boolean?)
(define-symbolic c boolean?)
(define v (if c "c" 0))
(define u (if b (vector v) 4))
u
(type-of u)]
Symbolic unions are recognized by the @racket[union?] predicate, and Rosette programs can inspect
union contents using the @racket[union-contents] procedure. Applications may use @racket[union?] and
@racket[union-contents] directly to @tech[#:key "lifted constructs"]{lift} Racket code to work on
symbolic unions, but Rosette also provides dedicated lifting constructs, described in
the @seclink["sec:lifting-constructs"]{next section},
to make this process easier and the resulting lifted code more efficient.
@defproc[(union? [v any/c]) boolean?]{
Returns true if the given value is a symbolic union. Otherwise returns false.
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(define u (if b '(1 2) 3))
(union? u)
(union? b)]
}
@defproc[(union-contents [u union?]) (listof (cons/c (and/c boolean? term?) (not/c union?)))]{
Returns a list of guard-value pairs contained in the given union.
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(define v (if b '(1 2) 3))
(union-contents v)]
}
@section[#:tag "sec:lifting-constructs"]{Symbolic Lifting}
Rosette provides two main constructs for @tech[#:key "lifted constructs"]{lifting}
Racket code to work on symbolic unions: @racket[for/all] and @racket[define-lift].
The @racket[for/all] construct is built into the language. It is used internally by Rosette
to lift operations on @tech[#:key "unsolvable type"]{unsolvable types}. The
@racket[define-lift] construct is syntactic sugar implemented on top of
@racket[for/all]; it is exported by the @racket[rosette/lib/lift] library.
@declare-exporting[rosette/base/core/forall rosette/lib/lift
#:use-sources (rosette/base/core/forall rosette/lib/lift)]
@defform[(for/all ([id val-expr]) body)]{
If @racket[val-expr] evaluates to a value that is not a @racket[union?],
@racket[for/all] behaves like a @racket[let] expression. It binds
@racket[id] to the value and evaluates the @racket[body] with that binding.
If @racket[val-expr] evaluates to a symbolic union, then for each
guard-value pair @racket['(#, @var[g] . #, @var[v])] in that union, @racket[for/all]
binds @racket[id] to @var[v] and evaluates the @racket[body]
under the guard @var[g]. The results of the individual evaluations of
the @racket[body] are re-assembled into a single (concrete or symbolic)
output value, which is the result of the @racket[for/all] expression.
If the evaluation of @racket[body] executes any procedure @var[p] that is neither
implemented in nor provided by the @racket[rosette/safe] language, then @var[p]
@bold{must be pure}---it may not perform any observable side-effects,
such as writes to memory or disk. There is no purity requirement for using procedures
that are implemented in or exported by @racket[rosette/safe] (e.g., @racket[vector-set!]).
The @racket[for/all] construct is useful both for lifting pure Racket procedures to work
on symbolic unions and for controling the performance of Rosette's symbolic evaluation.
The following examples show both use cases:
@itemlist[
@item{@emph{Lifting a pure Racket procedure
to work on symbolic unions.}
@defs+int[#:eval rosette-eval
[(require (only-in racket [string-length racket/string-length]))
(define (string-length value)
(for/all ([str value])
(racket/string-length str)))]
(string-length "abababa")
(string-length 3)
(define-symbolic b boolean?)
(string-length (if b "a" "abababa"))
(string-length (if b "a" 3))
(asserts)
(string-length (if b 3 #f))]}
@item{@emph{Making symbolic evaluation more efficient.} @(rosette-eval '(clear-asserts!))
@defs+int[#:eval rosette-eval
[(require (only-in racket build-list))
(define limit 1000)
(define (slow xs)
(and (= (length xs) limit) (car (map add1 xs))))
(define (fast xs)
(for/all ([xs xs]) (slow xs)))
(define ys (build-list limit identity))
(define-symbolic a boolean?)
(define xs (if a ys (cdr ys)))]
(time (slow xs))
(time (fast xs))]
Note that the above transformation will not always lead to better performance.
Experimenting is the best way to determine whether and where to insert
performance-guiding @racket[for/all]s.
}]}
@defform[(for*/all ([id val-expr] ...+) body)]{
Expands to a nested use of @racket[for/all],
just like @racket[let*] expands to a nested use of @racket[let].}
@defmodule[rosette/lib/lift #:no-declare]
@defform*[((define-lift id [(arg-type ...) racket-procedure-id])
(define-lift id [arg-type racket-procedure-id]))]{
Binds @racket[id] to a procedure that lifts @racket[racket-procedure-id] to
work on symbolic unions. In particular, the lifted procedure will work when given
either a concrete Racket value or a symbolic union contains a guarded value of
a suitable type, as given by @racket[arg-type]. Note that the lifted procedure
will not work on symbolic terms, only on symbolic unions or concrete values. The
Racket procedure bound to @racket[racket-procedure-id] must be pure (see @racket[for/all]).
When @racket[racket-procedure-id] takes a specific number of arguments,
the first form should be used, and the type of each argument should be given.
When @racket[racket-procedure-id] takes a variable number of arguments,
the type of all arguments should be given. Note that the second form omits
the parentheses around the argument type to indicate a variable number of
arguments, just like Racket's @racket[case-lambda] form.
The following example shows how to lift Racket's @racket[string-length] procedure
to work on symbolic unions that contain strings.
@defs+int[#:eval rosette-eval
[(require rosette/lib/lift)
(require (only-in racket [string-length racket/string-length] string?))
(define-lift string-length [(string?) racket/string-length])]
(string-length "abababa")
(define-symbolic b boolean?)
(string-length (if b "a" "abababa"))
(string-length (if b "a" 3))
(asserts)]
}
@(kill-evaluator rosette-eval)

View File

@ -1,23 +0,0 @@
#lang scribble/manual
@(require scriblib/autobib scribble/core (only-in racket match))
@(provide (all-defined-out))
@(define-cite ~cite citet generate-bibliography #:style number-style)
@(abbreviate-given-names #t)
@(define rosette:onward13
(make-bib
#:title @hyperlink["http://homes.cs.washington.edu/~emina/pubs/rosette.onward13.pdf"]{Growing Solver-Aided Languages with Rosette}
#:author (authors "Emina Torlak" "Rastislav Bodik")
#:date 2013
#:location "New Ideas, New Paradigms, and Reflections on Programming and Software (Onward!)"))
@(define rosette:pldi14
(make-bib
#:title @hyperlink["http://homes.cs.washington.edu/~emina/pubs/rosette.pldi14.pdf"]{A Lightweight Symbolic Virtual Machine for Solver-Aided Host Languages}
#:author (authors "Emina Torlak" "Rastislav Bodik")
#:date 2014
#:location "Programming Language Design and Implementation (PLDI)"))

View File

@ -1,67 +0,0 @@
#lang racket
(provide select rosette-evaluator rosette-log-evaluator logfile opaque)
(require
(for-label racket racket/generic)
(only-in rosette rosette union union-contents union?)
racket/sandbox racket/serialize scribble/eval
(only-in scribble/manual elem racket))
(define lifted?
(let ([lifted (apply set (rosette))])
(lambda (id) (set-member? lifted id))))
(define (select racket-ids)
(apply elem
(add-between (map (lambda (id) (racket #,#`#,id))
(filter lifted? racket-ids)) ", ")))
(define (rosette-printer v)
(match v
[(? void?) (void)]
[(? custom-write?)
((custom-write-accessor v) v (current-output-port) 1)]
[(? pair?) (printf "'~a" v)]
[(? null?) (printf "'()")]
[(? symbol?) (printf "'~a" v)]
[_ (printf "~a" v)]))
(define (rosette-evaluator [eval-limits #f])
(parameterize ([sandbox-output 'string]
[sandbox-error-output 'string]
[sandbox-path-permissions `((execute ,(byte-regexp #".*")))]
[sandbox-memory-limit #f]
[sandbox-eval-limits eval-limits]
[current-print rosette-printer])
(make-evaluator 'rosette/safe)))
(define (logfile root [filename "log"])
(build-path root (format "~a.txt" filename)))
(define (serialize-for-logging v)
(match v
[(or (? boolean?) (? number?) (? string?) (? void?)) v]
[(? box?) (box (serialize-for-logging (unbox v)))]
[(? pair?) (cons (serialize-for-logging (car v)) (serialize-for-logging (cdr v)))]
[(? list?) (map serialize-for-logging v)]
[(? vector?) (list->vector (map serialize-for-logging (vector->list v)))]
[(? struct?)
(let ([output-str (open-output-string)])
(display v output-str)
(opaque (get-output-string output-str)))]
[_ v]))
(serializable-struct opaque (str)
#:methods gen:custom-write
[(define (write-proc self port mode)
(fprintf port "~a" (opaque-str self)))])
(define (serializing-evaluator evaluator)
(lambda (expr) (serialize-for-logging (evaluator expr))))
(define (rosette-log-evaluator logfile [eval-limits #f])
(if (file-exists? logfile)
(make-log-based-eval logfile 'replay)
(parameterize ([current-eval (serializing-evaluator (rosette-evaluator eval-limits))])
(make-log-based-eval logfile 'record))))

View File

@ -0,0 +1,469 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define
rosette/base/core/term
(only-in rosette/base/core/union union?)
(only-in rosette/base/base bv bv? bitvector bitvector? bitvector-size
bveq bvslt bvsgt bvsle bvsge bvult bvugt bvule bvuge
bvnot bvor bvand bvxor bvshl bvlshr bvashr
bvneg bvadd bvsub bvmul bvudiv bvsdiv bvurem bvsrem bvsmod
concat extract sign-extend zero-extend
integer->bitvector bitvector->integer bitvector->natural
bit lsb msb bvzero? bvadd1 bvsub1
bvsmin bvsmax bvumin bvumax
rotate-left rotate-right bvrol bvror
bool->bitvector bitvector->bool bitvector->bits
assert vc))
(for-label racket)
scribble/core scribble/html-properties scribble/examples racket/sandbox
"../util/lifted.rkt")
@(define rosette-eval (rosette-evaluator))
@title[#:tag "sec:bitvectors"]{Bitvectors}
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
Rosette extends Racket with a primitive bitvector datatype whose values are
fixed-size words---or, machine integers. Mainstream programming languages, such as
C or Java, support bitvector types with a few fixed sizes (e.g., 8 bits, 16 bits,
and 32 bits). Rosette supports bitvectors of arbitrary size, as well as both signed and
unsigned versions of various bitvector operations (such as comparisons, division, remainder, etc.).
Technically, Rosette's bitvector datatype embeds the
@hyperlink["http://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV"]{theory of bitvectors}
into a programming language.
@examples[#:eval rosette-eval
(code:line (bv 4 (bitvector 7)) (code:comment "A bitvector literal of size 7."))
(code:line (bv 4 7) (code:comment "A shorthand for the same literal."))
(code:line (define-symbolic x y (bitvector 7)) (code:comment "Symbolic bitvector constants."))
(code:line (bvslt (bv 4 7) (bv -1 7)) (code:comment "Signed 7-bit < comparison of 4 and -1."))
(code:line (bvult (bv 4 7) (bv -1 7)) (code:comment "Unsigned 7-bit < comparison of 4 and -1."))
(define-symbolic b boolean?)
(code:line (bvadd x (if b y (bv 3 4))) (code:comment "This typechecks only when b is true,"))
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
@defproc[(bitvector [size (and/c integer? positive? (not/c term?) (not/c union?))]) bitvector?]{
Returns a type predicate that recognizes bitvectors of the given @racket[size].
Note that @racket[size] must be a concrete positive integer.
The type predicate itself is recognized by the @racket[bitvector?] predicate.
@examples[#:eval rosette-eval
(define bv6? (bitvector 6))
(bv6? 1)
(bv6? (bv 3 6))
(bv6? (bv 3 5))
(define-symbolic b boolean?)
(bv6? (if b (bv 3 6) #t))]
}
@defproc[(bitvector? [v any/c]) boolean?]{
Returns true if @racket[v] is a concrete type predicate that recognizes bitvector values.
@examples[#:eval rosette-eval
(define bv6? (bitvector 6))
(define bv7? (bitvector 7))
(define-symbolic b boolean?)
(code:line (bitvector? bv6?) (code:comment "A concrete bitvector type."))
(code:line (bitvector? (if b bv6? bv7?)) (code:comment "Not a concrete type."))
(code:line (bitvector? integer?) (code:comment "Not a bitvector type."))
(code:line (bitvector? 3) (code:comment "Not a type."))]}
@defproc[(bv [val (and/c integer? (not/c term?) (not/c union?))]
[size (and/c (or/c bitvector? (and/c integer? positive?))
(not/c term?) (not/c union?))]) bv?]{
Returns a bitvector literal of the given @racket[size], which may be given either as a
concrete @racket[bitvector?] type or a concrete positive integer.}
@defproc[(bv? [v any/c]) boolean?]{
Recognizes concrete or symbolic bitvector values of any size.
@examples[#:eval rosette-eval
(bv? 1)
(bv? (bv 1 1))
(bv? (bv 2 2))
(define-symbolic b boolean?)
(bv? (if b (bv 3 6) #t))]
}
@(rosette-eval '(clear-vc!))
@section{Comparison Operators}
@defproc*[([(bveq [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvslt [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvult [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvsle [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvule [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvsgt [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvugt [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvsge [x (bitvector n)] [y (bitvector n)]) boolean?]
[(bvuge [x (bitvector n)] [y (bitvector n)]) boolean?])]{
Compares two bitvector values of the same bitvector type.
Comparison relations include
equality (@racket[bveq]) and signed / unsigned versions of
<, <=, >, and >= (@racket[bvslt], @racket[bvult], @racket[bvsle], @racket[bvule],
@racket[bvsgt], and @racket[bvugt]).
@examples[#:eval rosette-eval
(code:line (define-symbolic u v (bitvector 7)) (code:comment "Symbolic bitvector constants."))
(code:line (bvslt (bv 4 7) (bv -1 7)) (code:comment "Signed 7-bit < comparison of 4 and -1."))
(code:line (bvult (bv 4 7) (bv -1 7)) (code:comment "Unsigned 7-bit < comparison of 4 and -1."))
(define-symbolic b boolean?)
(code:line (bvsge u (if b v (bv 3 4))) (code:comment "This typechecks only when b is true,"))
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
}
@(rosette-eval '(clear-vc!))
@section{Bitwise Operators}
@defproc[(bvnot [x (bitvector n)]) (bitvector n)]{
Returns the bitwise negation of the given bitvector value.
@examples[#:eval rosette-eval
(bvnot (bv -1 4))
(bvnot (bv 0 4))
(define-symbolic b boolean?)
(code:line (bvnot (if b 0 (bv 0 4))) (code:comment "This typechecks only when b is false,"))
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(bvand [x (bitvector n)] ...+) (bitvector n)]
[(bvor [x (bitvector n)] ...+) (bitvector n)]
[(bvxor [x (bitvector n)] ...+) (bitvector n)])]{
Returns the bitwise ``and'', ``or'', ``xor'' of one or more bitvector values of the same type.
@examples[#:eval rosette-eval
(bvand (bv -1 4) (bv 2 4))
(bvor (bv 0 3) (bv 1 3))
(bvxor (bv -1 5) (bv 1 5))
(define-symbolic b boolean?)
(code:line
(bvand (bv -1 4)
(if b 0 (bv 2 4))) (code:comment "This typechecks only when b is false,"))
(code:line
(vc) (code:comment "so Rosette emits a corresponding assertion."))]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(bvshl [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvlshr [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvashr [x (bitvector n)] [y (bitvector n)]) (bitvector n)])]{
Returns the left, logical right, or arithmetic right shift of @racket[x] by
@racket[y] bits, where @racket[x] and @racket[y] are bitvector values of the same type.
@examples[#:eval rosette-eval
(bvshl (bv 1 4) (bv 2 4))
(bvlshr (bv -1 3) (bv 1 3))
(bvashr (bv -1 5) (bv 1 5))
(define-symbolic b boolean?)
(code:line (bvshl (bv -1 4)
(if b 0 (bv 2 4))) (code:comment "This typechecks only when b is false,"))
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
}
@section{Arithmetic Operators}
@defproc[(bvneg [x (bitvector n)]) (bitvector n)]{
Returns the arithmetic negation of the given bitvector value.
@examples[#:eval rosette-eval
(bvneg (bv -1 4))
(bvneg (bv 0 4))
(define-symbolic z (bitvector 3))
(bvneg z)]
}
@(rosette-eval '(clear-vc!))
@(rosette-eval '(clear-terms!))
@defproc*[([(bvadd [x (bitvector n)] ...+) (bitvector n)]
[(bvsub [x (bitvector n)] ...+) (bitvector n)]
[(bvmul [x (bitvector n)] ...+) (bitvector n)])]{
Returns the sum, difference, or product of one or more bitvector values of the same type.
@examples[#:eval rosette-eval
(bvadd (bv -1 4) (bv 2 4))
(bvsub (bv 0 3) (bv 1 3))
(bvmul (bv -1 5) (bv 1 5))
(define-symbolic b boolean?)
(bvadd (bv -1 4) (bv 2 4) (if b (bv 1 4) "bad"))
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(bvsdiv [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvudiv [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvsrem [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvurem [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvsmod [x (bitvector n)] [y (bitvector n)]) (bitvector n)])]{
Returns (un)signed quotient, remainder, or modulo of two bitvector values of the same type.
All five operations are defined even when the second argument is zero.
@examples[#:eval rosette-eval
(bvsdiv (bv -3 4) (bv 2 4))
(bvudiv (bv -3 3) (bv 2 3))
(bvsmod (bv 1 5) (bv 0 5))
(define-symbolic b boolean?)
(bvsrem (bv -3 4) (if b (bv 2 4) "bad"))
(vc)]
}
@(rosette-eval '(clear-vc!))
@section{Conversion Operators}
@defproc[(concat [x bv?] ...+) bv?]{
Returns the bitwise concatenation of the given bitvector values.
@examples[#:eval rosette-eval
(concat (bv -1 4) (bv 0 1) (bv -1 3))
(define-symbolic b boolean?)
(concat (bv -1 4) (if b (bv 0 1) (bv 0 2)) (bv -1 3))]
}
@defproc[(extract [i integer?] [j integer?] [x (bitvector n)]) (bitvector (+ 1 (- i j)))]{
Extracts bits @racket[i] down to @racket[j] from a bitvector of size @racket[n], yielding a
bitvector of size i - j + 1. This procedure assumes that @racket[n] > @racket[i] >= @racket[j] >= 0.
@examples[#:eval rosette-eval
(extract 2 1 (bv -1 4))
(extract 3 3 (bv 1 4))
(define-symbolic i j integer?)
(extract i j (bv 1 2))
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(sign-extend [x bv?] [t (or/c bitvector? union?)]) bv?]
[(zero-extend [x bv?] [t (or/c bitvector? union?)]) bv?])]{
Returns a bitvector of type @racket[t] that represents the (un)signed
extension of the bitvector @racket[x].
Note that both @racket[x] and @racket[t] may be symbolic. The size of @racket[t]
must not be smaller than the size of @racket[x]'s type.
@examples[#:eval rosette-eval
(sign-extend (bv -3 4) (bitvector 6))
(zero-extend (bv -3 4) (bitvector 6))
(define-symbolic b c boolean?)
(zero-extend (bv -3 4) (if b (bitvector 5) (bitvector 6)))
(zero-extend (bv -3 4) (if b (bitvector 5) "bad"))
(vc)
(zero-extend (bv -3 4) (if c (bitvector 5) (bitvector 1)))
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(bitvector->integer [x bv?]) integer?]
[(bitvector->natural [x bv?]) integer?])]{
Returns the (un)signed integer value of the given bitvector.
@examples[#:eval rosette-eval
(bitvector->integer (bv -1 4))
(bitvector->natural (bv -1 4))
(define-symbolic b boolean?)
(bitvector->integer (if b (bv -1 3) (bv -3 4)))
(bitvector->integer (if b (bv -1 3) "bad"))
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(integer->bitvector [i integer?] [t (or/c bitvector? union?)]) bv?])]{
Returns a bitvector of type @racket[t] that represents the @var[k] lowest order bits
of the integer @racket[i], where @var[k] is the size of @racket[t].
Note that both @racket[i] and @racket[t] may be symbolic.
@examples[#:eval rosette-eval
(integer->bitvector 4 (bitvector 2))
(integer->bitvector 15 (bitvector 4))
(define-symbolic b c boolean?)
(integer->bitvector (if b pi 3) (if c (bitvector 5) (bitvector 6)))
(vc)]
}
@section{Additional Operators}
@(rosette-eval '(clear-vc!))
@defproc[(bit [i integer?] [x (bitvector n)]) (bitvector 1)]{
Extracts the @racket[i]th bit from the bitvector @racket[x] of size @racket[n], yielding a
bitvector of size 1. This procedure assumes that @racket[n] > @racket[i] >= 0.
@examples[#:eval rosette-eval
(bit 1 (bv 3 4))
(bit 2 (bv 1 4))
(define-symbolic i integer?)
(define-symbolic x (bitvector 4))
(bit i x)
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(lsb [x (bitvector n)]) (bitvector 1)]
[(msb [x (bitvector n)]) (bitvector 1)])]{
Returns the least or most significant bit of @racket[x].
@examples[#:eval rosette-eval
(lsb (bv 3 4))
(msb (bv 3 4))
(define-symbolic x (bitvector 4))
(define-symbolic y (bitvector 8))
(lsb (if b x y))
(msb (if b x y))
]
}
@(rosette-eval '(clear-vc!))
@defproc[(bvzero? [x (bitvector n)]) boolean?]{
Returns @racket[(bveq x (bv 0 n))].
@examples[#:eval rosette-eval
(define-symbolic x (bitvector 4))
(bvzero? x)
(define-symbolic y (bitvector 8))
(bvzero? y)
(define-symbolic b boolean?)
(bvzero? (if b x y))
]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(bvadd1 [x (bitvector n)]) (bitvector n)]
[(bvsub1 [x (bitvector n)]) (bitvector n)])]{
Returns @racket[(bvadd x (bv 1 n))] or @racket[(bvsub x (bv 1 n))].
@examples[#:eval rosette-eval
(define-symbolic x (bitvector 4))
(bvadd1 x)
(define-symbolic y (bitvector 8))
(bvsub1 y)
(define-symbolic b boolean?)
(bvadd1 (if b x y))
(bvsub1 (if b x y))
]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(bvsmin [x (bitvector n)] ...+) (bitvector n)]
[(bvumin [x (bitvector n)] ...+) (bitvector n)]
[(bvsmax [x (bitvector n)] ...+) (bitvector n)]
[(bvumax [x (bitvector n)] ...+) (bitvector n)])]{
Returns the (un)signed minimum or maximum of one or more bitvector values of the same type.
@examples[#:eval rosette-eval
(bvsmin (bv -1 4) (bv 2 4))
(bvumin (bv -1 4) (bv 2 4))
(bvsmax (bv -1 4) (bv 2 4))
(bvumax (bv -1 4) (bv 2 4))
(define-symbolic b boolean?)
(bvsmin (bv -1 4) (bv 2 4) (if b (bv 1 4) (bv 3 8)))
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(bvrol [x (bitvector n)] [y (bitvector n)]) (bitvector n)]
[(bvror [x (bitvector n)] [y (bitvector n)]) (bitvector n)])]{
Returns the left or right rotation of @racket[x] by @racket[(bvurem y n)] bits, where
@racket[x] and @racket[y] are bitvector values of the same type.
@examples[#:eval rosette-eval
(bvrol (bv 3 4) (bv 2 4))
(bvrol (bv 3 4) (bv -2 4))
(define-symbolic b boolean?)
(code:line
(bvror (bv 3 4)
(if b 0 (bv 2 4))) (code:comment "This typechecks only when b is false,"))
(code:line
(vc) (code:comment "so Rosette emits a corresponding assertion."))]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(rotate-left [i integer?] [x (bitvector n)]) (bitvector n)]
[(rotate-right [i integer?] [x (bitvector n)]) (bitvector n)])]{
Returns the left or right rotation of @racket[x] by @racket[i] bits.
These procedures assume that @racket[n] > @racket[i] >= 0. See @racket[bvrol]
and @racket[bvror] for an alternative way to perform rotations that usually
leads to faster solving times.
@examples[#:eval rosette-eval
(rotate-left 3 (bv 3 4))
(rotate-right 1 (bv 3 4))
(define-symbolic i integer?)
(define-symbolic b boolean?)
(rotate-left i (if b (bv 3 4) (bv 7 8)))
(vc)
]
}
@(rosette-eval '(clear-vc!))
@defproc[(bitvector->bits [x (bitvector n)]) (listof (bitvector 1))]{
Returns the bits of @racket[x] as a list, i.e., @racket[(list (bit 0 x) ... (bit (- n 1) x))].
@examples[#:eval rosette-eval
(bitvector->bits (bv 3 4))
(define-symbolic y (bitvector 2))
(bitvector->bits y)
(define-symbolic b boolean?)
(bitvector->bits (if b (bv 3 4) y)) ]
}
@defproc[(bitvector->bool [x (bitvector n)]) boolean?]{
Returns @racket[(not (bvzero? x))].
}
@(rosette-eval '(clear-vc!))
@defproc[(bool->bitvector [b any/c] [t (or/c positive-integer? (bitvector n)) (bitvector 1)]) bv?]{
Returns @racket[(bv 0 t)] if @racket[(false? b)] and otherwise returns @racket[(bv 1 t)], where
@racket[t] is @racket[(bitvector 1)] by default. If provided, @racket[t] must be a concrete positive
integer or a concrete bitvector type value.
@examples[#:eval rosette-eval
(bool->bitvector #f 3)
(bool->bitvector "non-false-value")
(define-symbolic b boolean?)
(bool->bitvector b)
]
}
@(kill-evaluator rosette-eval)

View File

@ -0,0 +1,187 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/form rosette/query/query rosette/solver/solution
rosette/base/core/term (only-in rosette/query/finitize current-bitwidth)
(only-in rosette/base/base ! && || => <=> exists forall function? assert vc with-vc
result-state result-value))
(except-in (for-label racket) =>)
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "bools-log")))
@(define bools (select '(boolean? false? true false boolean=? not nand nor implies xor)))
@(define nums (select (remove* '(expt) '(number? complex? real? rational? integer? exact-integer? exact-nonnegative-integer? exact-positive-integer? inexact-real? fixnum? flonum? double-flonum? single-flonum? zero? positive? negative? even? odd? exact? inexact? inexact->exact exact->inexact real->single-flonum real->double-flonum + - * / quotient remainder quotient/ modulo add1 sub1 abs max min gcd lcm round floor ceiling truncate numerator denominator rationalize = < <= > >= sqrt integer-sqrt integer-sqrt/ expt exp log sin cos tan asin acos atan make-rectangular make-polar real-part imag-part magnitude angle bitwise-ior bitwise-and bitwise-xor bitwise-not bitwise-bit-set? bitwise-bit-field arithmetic-shift integer-length random random-seed make-pseudo-random-generator pseudo-random-generator? current-pseudo-random-generator pseudo-random-generator->vector vector->pseudo-random-generator vector->pseudo-random-generator! pseudo-random-generator-vector? number->string string->number real->decimal-string integer-bytes->integer integer->integer-bytes floating-point-bytes->real real->floating-point-bytes system-big-endian? pi pi.f degrees->radians radians->degrees sqr sgn conjugate sinh cosh tanh exact-round exact-floor exact-ceiling exact-truncate order-of-magnitude nan? infinite?))))
@title[#:tag "sec:bools+ints+reals"]{Booleans, Integers, and Reals}
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
Rosette lifts the following operations on booleans, integers, and reals:
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
(list (list @elem{Booleans} @bools)
(list @elem{Integers and Reals} @nums))]
Lifted boolean operations retain their Racket semantics on both concrete and symbolic values.
In particular, Rosette extends the intepretation of these operations to work on symbolic values in (logically) the
same way that they work on concrete values.
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(boolean? b)
(boolean? #t)
(boolean? #f)
(boolean? 1)
(code:line (not b) (code:comment "Produces a logical negation of b."))]
Lifted numeric operations, in contrast, match their Racket semantics
only when applied to concrete values. Their symbolic semantics depends on the
current @tech["reasoning precision"], as determined by the @racket[current-bitwidth]
parameter. In particular, if this parameter is set to @racket[#f], operations on symbolic numbers
retain their infinite-precision Racket semantics. However, because infinite-precision
reasoning is not efficiently (or at all) decidable for arbitrary numeric operations,
programs may need to set @racket[current-bitwidth] to a small positive integer @var[k].
With this setting, symbolic numbers are treated as signed @var[k]-bit integers. See
@secref{sec:reasoning-precision} for details and examples.
@section[#:tag "sec:extra-bools"]{Additional Logical Operators}
In addition to lifting Racket's operations on booleans,
Rosette supports the following logical operations:
conjunction (@racket[&&]), disjunction (@racket[||]),
implication (@racket[=>]), bi-implication (@racket[<=>]),
and negation (@racket[!]). These operations have their usual
logical meaning; e.g., unlike Racket's shortcircuiting
@racket[and] operator, the logical @racket[&&] operator
evaluates all of its arguments before taking their
conjunction.
@(rosette-eval '(clear-vc!))
@defproc[(! [v boolean?]) boolean?]{
Returns the negation of the given boolean value.
@examples[#:eval rosette-eval
(! #f)
(! #t)
(define-symbolic b boolean?)
(code:line (! (if b #f 3)) (code:comment "This typechecks only when b is true,"))
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(&& [v boolean?] ...) boolean?]
[(|| [v boolean?] ...) boolean?])]{
Returns the logical conjunction or disjunction of zero or more boolean values.
@examples[#:eval rosette-eval
(&&)
(||)
(code:line (&& #f (begin (displayln "hello") #t)) (code:comment "No shortcircuiting."))
(define-symbolic a b boolean?)
(code:line (&& a (if b #t 1)) (code:comment "This typechecks only when b is true,"))
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(=> [x boolean?] [y boolean?]) boolean?]
[(<=> [x boolean?] [y boolean?]) boolean?])]{
Returns the logical implication or bi-implication of two boolean values.
@examples[#:eval rosette-eval
(code:line (=> #f (begin (displayln "hello") #f)) (code:comment "No shortcircuiting."))
(define-symbolic a b boolean?)
(code:line (<=> a (if b #t 1)) (code:comment "This typechecks only when b is true,"))
(code:line (vc) (code:comment "so Rosette emits a corresponding assertion."))]
}
@section[#:tag "sec:quantifiers"]{Quantifiers}
Rosette also provides constructs for creating universally
(@racket[forall]) and existentially (@racket[exists])
quantified formulas. These differ from the usual logical
quantifiers in that the evaluation of a quantified formula's
body may have side effects (e.g., generate assertions). When
there are no side effects, however, these constructs have
their usual logical meaning.
@(rosette-eval '(clear-vc!))
@(rosette-eval '(current-bitwidth #f))
@defproc*[([(forall [vs (listof constant?)] [body boolean?]) boolean?]
[(exists [vs (listof constant?)] [body boolean?]) boolean?])]{
Returns a universally or existentially @deftech{quantified formula}, where the
symbolic constants @racket[vs] are treated as quantified variables.
Each constant in @racket[vs] must have a non-@racket[function?] @racket[solvable?] type.
The @racket[body] argument is a boolean value, which is usually a symbolic
boolean expression over the quantified variables @racket[vs] and,
optionally, over free symbolic (Skolem) constants. Any assertions and assumptions emitted during
the evaluation of @racket[body] are added to the current verification condition @racket[(vc)].
This may be the desired behavior in some circumstances but not in others, so to avoid
surprises, it is best to handle side effects separately and call quantifiers
with pure bodies, as shown below.
@examples[#:eval rosette-eval
(current-bitwidth #f)
(define-symbolic x y integer?)
(code:line
(exists (list x y) (= x y)) (code:comment "Pure body expression."))
(define-symbolic b boolean?)
(code:line
(forall (list b x y)
(= (+ (if b x 'x) 1) y)) (code:comment "Body emits a type assertion."))
(vc)
(clear-vc!)
(code:comment "To avoid surprises, capture assertions and assumptions using with-vc,")
(code:comment "and handle as desired, e.g.:")
(define out (with-vc (= (+ (if b x 'x) 1) y)))
out
(define out-val (result-value out))
(define out-vc (result-state out))
(forall (list b x y)
(=> (&& (vc-assumes out-vc) (vc-asserts out-vc)) out-val))
(vc)
]
The usual lexical scoping rules apply to quantified symbolics; if @racket[body] is
a quantified formula over a variable @var[v] in @racket[vs], then the
innermost quantification of @var[v] shadows any enclosing quantifications.
Quantified symbolics are not bound in a @racket[model], unless they also appear
freely in some formulas.
@examples[#:eval rosette-eval
(define-symbolic x y integer?)
(code:line
(define f
(forall (list x)
(exists (list y)
(= x (+ x y))))) (code:comment "x and y are not free in f,"))
(code:line
(solve (assert f)) (code:comment "so they are not bound in the model."))
(code:line
(define g
(forall (list x)
(= x (+ x y)))) (code:comment "y is free in g,"))
(code:line
(solve (assert g)) (code:comment "so it is bound in the model."))
(code:line
(define h
(exists (list x)
(forall (list x)
(= x (+ x x))))) (code:comment "The body of h refers to the innermost x,"))
(code:line
(solve (assert h)) (code:comment "so h is unsatisfiable."))
]
When executing queries over assertions that contain quantified formulas,
the @racket[current-bitwidth] parameter must be set to @racket[#f].
Quantified formulas may not appear in any assertion or assumption that is passed
to a @racket[synthesize] query.
}
@(kill-evaluator rosette-eval)

View File

@ -0,0 +1,238 @@
;; This file was created by make-log-based-eval
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((boolean? b) ((3) 0 () 0 () () (q values #t)) #"" #"")
((boolean? #t) ((3) 0 () 0 () () (q values #t)) #"" #"")
((boolean? #f) ((3) 0 () 0 () () (q values #t)) #"" #"")
((boolean? 1) ((3) 0 () 0 () () (q values #f)) #"" #"")
((not b)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(! b)\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((! #f) ((3) 0 () 0 () () (q values #t)) #"" #"")
((! #t) ((3) 0 () 0 () () (q values #f)) #"" #"")
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((! (if b #f 3)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t b)\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((&&) ((3) 0 () 0 () () (q values #t)) #"" #"")
((||) ((3) 0 () 0 () () (q values #f)) #"" #"")
((&& #f (begin (displayln "hello") #t))
((3) 0 () 0 () () (q values #f))
#"hello\n"
#"")
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((&& a (if b #t 1))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "a\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t b)\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((=> #f (begin (displayln "hello") #f))
((3) 0 () 0 () () (q values #t))
#"hello\n"
#"")
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((<=> a (if b #t 1))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "a\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t b)\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((exists (list x y) (= x y))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(exists (x y) (= x y))\n"))))
#""
#"")
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((forall (list b x y) (= (+ (if b x 'x) 1) y))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(forall (b x y) (= y (+ 1 x)))\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t b)\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define out (with-vc (= (+ (if b x 'x) 1) y)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(out
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(normal (= y (+ 1 x)) (vc #t b))\n"))))
#""
#"")
((define out-val (result-value out))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define out-vc (result-state out))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((forall
(list b x y)
(=> (&& (vc-assumes out-vc) (vc-asserts out-vc)) out-val))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(forall (b x y) (|| (! b) (= y (+ 1 x))))\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t #t)\n"))))
#""
#"")
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define f (forall (list x) (exists (list y) (= x (+ x y)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((solve (assert f))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model)\n"))))
#""
#"")
((define g (forall (list x) (= x (+ x y))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((solve (assert g))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [y 0])\n"))))
#""
#"")
((define h (exists (list x) (forall (list x) (= x (+ x x)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((solve (assert h))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#""
#"")

View File

@ -15,6 +15,40 @@
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate v1 sol) ((3) 0 () 0 () () (c values c (b! . 4))) #"" #"")
((evaluate v2 sol) ((3) 0 () 0 () () (c values c (b! . 4))) #"" #"")
(sol
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [x 4]\n [b #t])\n"))))
#""
#"")
((evaluate v1 sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'#&4\n"))))
#""
#"")
((evaluate v2 sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'#&4\n"))))
#""
#"")
((evaluate (eq? v1 v2) sol) ((3) 0 () 0 () () (q values #t)) #"" #"")

View File

@ -1,7 +1,8 @@
#lang scribble/manual
@(require (for-label rosette/base/form/define racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox racket/runtime-path
@(require (for-label rosette/base/form/define rosette/query/query racket
(only-in rosette/base/base assert))
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define box-ops (select '(box? box box-immutable unbox set-box! box-cas!)))
@ -30,9 +31,10 @@ they point to the same box object. Boxes can be concrete or symbolic, and they
@examples[#:eval rosette-eval
(define-symbolic x integer?)
(define-symbolic b boolean?)
(code:line (define v1 (box x)) (code:comment "v1 is a box with symbolic contents"))
(code:line (define v2 (if b v1 (box 3))) (code:comment "v2 is a symbolic box"))
(code:line (define v1 (box x)) (code:comment "v1 is a box with symbolic contents."))
(code:line (define v2 (if b v1 (box 3))) (code:comment "v2 is a symbolic box."))
(define sol (solve (assert (= 4 (unbox v2)))))
sol
(evaluate v1 sol)
(evaluate v2 sol)
(evaluate (eq? v1 v2) sol)]

View File

@ -19,25 +19,25 @@
((point-x p)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(ite b 1 3)"))))
(c values c (0 (u . "(ite b 1 3)\n"))))
#""
#"")
((point-y p)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(ite b 2 4)"))))
(c values c (0 (u . "(ite b 2 4)\n"))))
#""
#"")
((define sol (solve (assert (= (point-x p) 3))))
@ -47,13 +47,13 @@
((evaluate p sol)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "#(struct:point 3 4)"))))
(c values c (0 (u . "(point 3 4)\n"))))
#""
#"")
((define-generics viewable (view viewable))
@ -87,13 +87,13 @@
((view p)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(ite b 2 3)"))))
(c values c (0 (u . "(ite b 2 3)\n"))))
#""
#"")
((define sol (solve (assert (= (view p) 3))))
@ -103,12 +103,12 @@
((evaluate p sol)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "#(struct:circle 3)"))))
(c values c (0 (u . "(circle 3)\n"))))
#""
#"")

View File

@ -1,10 +1,10 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/query rosette/query/eval
rosette/base/core/term (only-in rosette/base/core/safe assert)
rosette/base/form/define rosette/query/query
rosette/base/core/term (only-in rosette/base/base assert)
racket racket/generic)
scribble/core scribble/html-properties scribble/eval racket/sandbox racket/runtime-path
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@ -27,10 +27,10 @@ Structure types are defined using the @racket[struct] syntax. Defining a
structure type in this way also defines the necessary procedures for
creating instances of that type and for accessing their fields.
@interaction[#:eval rosette-eval
(struct point (x y) #:transparent) (code:comment "immutable transparent type")
(struct pt (x y)) (code:comment "opaque immutable type")
(struct pnt (x y) #:mutable #:transparent) (code:comment "mutable transparent type")]
@examples[#:eval rosette-eval #:label #f
(struct point (x y) #:transparent) (code:comment "Immutable transparent type.")
(struct pt (x y)) (code:comment "Opaque immutable type.")
(struct pnt (x y) #:mutable #:transparent) (code:comment "Mutable transparent type.")]
Rosette structures can be concrete or symbolic. Their semantics matches that of Racket,
with one important exception: immutable transparent structures are treated as values
@ -39,10 +39,10 @@ rather than references. This @seclink["sec:equality"]{means} that two such stru
Structures of opaque or mutable types are treated as references. Two such structures are
@racket[eq?] only if the point to the same instance of the same type.
@interaction[#:eval rosette-eval
(code:line (eq? (point 1 2) (point 1 2)) (code:comment "point structures are values"))
(code:line (eq? (pt 1 2) (pt 1 2)) (code:comment "pt structures are references"))
(code:line (eq? (pnt 1 2) (pnt 1 2)) (code:comment "pnt structures are references"))]
@examples[#:eval rosette-eval #:label #f
(code:line (eq? (point 1 2) (point 1 2)) (code:comment "point structures are values."))
(code:line (eq? (pt 1 2) (pt 1 2)) (code:comment "pt structures are references."))
(code:line (eq? (pnt 1 2) (pnt 1 2)) (code:comment "pnt structures are references."))]
Like @tech[#:key "unsolvable type"]{unsolvable built-in datatypes},
symbolic structures cannot be created using @racket[define-symbolic]. Instead,
@ -50,9 +50,9 @@ they are created implicitly, by, for example, using an @racket[if] expression
together with a symbolic value.
@interaction[#:eval rosette-eval
@examples[#:eval rosette-eval #:label #f
(define-symbolic b boolean?)
(code:line (define p (if b (point 1 2) (point 3 4))) (code:comment "p holds a symbolic structure"))
(code:line (define p (if b (point 1 2) (point 3 4))) (code:comment "p holds a symbolic structure."))
(point-x p)
(point-y p)
(define sol (solve (assert (= (point-x p) 3))))
@ -67,7 +67,8 @@ properties, generic interfaces, and facilities for defining new properties and i
(list @elem{Lifted Generics} @elem{@generics} ))]
Lifted generics work as expected with symbolic values:
@interaction[#:eval rosette-eval
@examples[#:eval rosette-eval #:label #f
(define-generics viewable (view viewable))
(struct square (side)
@ -80,7 +81,7 @@ Lifted generics work as expected with symbolic values:
[(define (view self) (circle-radius self))])
(define-symbolic b boolean?)
(code:line (define p (if b (square 2) (circle 3))) (code:comment "p holds a symbolic structure"))
(code:line (define p (if b (square 2) (circle 3))) (code:comment "p holds a symbolic structure."))
(view p)
(define sol (solve (assert (= (view p) 3))))
(evaluate p sol)]

View File

@ -1,8 +1,8 @@
#lang scribble/manual
@(require (for-label rosette/base/form/define racket)
(for-label (only-in rosette/base/base function? distinct?))
scribble/core scribble/html-properties scribble/eval racket/sandbox
(for-label (only-in rosette/base/base function? distinct? ~> bv))
scribble/core scribble/html-properties scribble/examples racket/sandbox
"../util/lifted.rkt")
@ -27,7 +27,7 @@ notion of @racket[equal?] is specified for a particular datatype.
(equal? n 1)
(equal? (box n) (box 1))
(define-symbolic f g (~> integer? integer?))
(code:line (equal? f g) (code:comment "f and g are different procedures"))]
(code:line (equal? f g) (code:comment "f and g are different procedures."))]
@(kill-evaluator rosette-eval)
@(set! rosette-eval (rosette-evaluator))
@ -43,25 +43,25 @@ or two transparent immutable values with @racket[eq?] contents.
@examples[#:eval rosette-eval
(eq? 1 #t)
(code:line (eq? 1 1.0) (code:comment "equal? transparent solvable values"))
(code:line (eq? (list 1) (list 1.0)) (code:comment "transparent immutable values with eq? contents"))
(code:line (eq? (box 1) (box 1)) (code:comment "but boxes are mutable, so eq? follows Racket"))
(code:line (eq? 1 1.0) (code:comment "equal? transparent solvable values."))
(code:line (eq? (list 1) (list 1.0)) (code:comment "Transparent immutable values with eq? contents."))
(code:line (eq? (box 1) (box 1)) (code:comment "But boxes are mutable, so eq? follows Racket,"))
(eq? (list (box 1)) (list (box 1)))
(define-symbolic n integer?)
(eq? n 1)
(eq? (box n) (box 1))
(define-symbolic f g (~> integer? integer?))
(code:line (eq? f g) (code:comment "and procedures are opaque, so eq? follows Racket"))
(code:line (eq? f g) (code:comment "and procedures are opaque, so eq? follows Racket."))
(eq? f f)]
In addition to lifting Racket's equality predicates, Rosette also provides a @racket[distinct?] predicate
that returns true iff all of its arguments are distinct from each other. Invoking this predicate
on arbitrary values has the effect of performing O(@var[N]@superscript{2}) @racket[not] @racket[equal?]
on arbitrary values has the effect of performing O(@var[N]@superscript{2}) equality
comparisons. But when applied to symbolic values of a primitive
@tech[#:key "solvable type"]{solvable} type, @racket[distinct?] will produce a compact
symbolic value that can be directly solved by the underlying solver.
@(rosette-eval '(clear-asserts!))
@(rosette-eval '(clear-vc!))
@defproc[(distinct? [v any/c] ...) boolean?]{
Returns true iff all of the given values @racket[v] are distinct---i.e., pairwise un-@racket[equal?]
to each other. If all values @racket[v] are of the same primitive (non-@racket[function?])

View File

@ -11,7 +11,18 @@
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate xs sol) ((3) 0 () 0 () () (q values ())) #"" #"")
((evaluate xs sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'()\n"))))
#""
#"")
((define sol
(solve
(begin
@ -21,7 +32,18 @@
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate xs sol) ((3) 0 () 0 () () (q values (1 4))) #"" #"")
((evaluate xs sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'(-1 0)\n"))))
#""
#"")
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define p (if b (cons 1 2) (cons 4 #f)))
((3) 0 () 0 () () (c values c (void)))
@ -31,9 +53,31 @@
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate p sol) ((3) 0 () 0 () () (q values (4 . #f))) #"" #"")
((evaluate p sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'(4 . #f)\n"))))
#""
#"")
((define sol (solve (assert (odd? (car p)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate p sol) ((3) 0 () 0 () () (q values (1 . 2))) #"" #"")
((evaluate p sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'(1 . 2)\n"))))
#""
#"")

View File

@ -0,0 +1,193 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/query
rosette/base/core/term
(only-in rosette/base/base assert vc clear-vc! define-symbolic
length-bv list-ref-bv list-set-bv
take-bv take-right-bv
drop-bv drop-right-bv
list-tail-bv split-at-bv split-at-right-bv
union? bitvector bitvector? bv?
bitvector->natural integer->bitvector)
racket)
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "pairs-log")))
@(define pairs:constructors+selectors (select '(pair? null? cons car cdr null list? list list* build-list)))
@(define list-operations (select '(length list-ref list-tail append reverse)))
@(define list-iteration (select '(map andmap ormap for-each foldl foldr)))
@(define list-filtering (select '(filter remove remq remv remove* remq* remv* sort)))
@(define list-searching (select '(member memv memq memf findf assoc assv assq assf)))
@(define more-pair-ops (select '(caar cadr cdar cddr caaar caadr cadar caddr cdaar cdadr cddar cdddr caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr)))
@(define more-list-ops (select '(empty cons? empty? first rest second third fourth fifth sixth seventh eighth ninth tenth last last-pair make-list take drop split-at takef dropf splitf-at take-right drop-right split-at-right takef-right dropf-right splitf-at-right add-between append* flatten remove-duplicates filter-map count partition range append-map filter-not shuffle permutations in-permutations argmin argmax list-set)))
@title[#:tag "sec:pair"]{Pairs and Lists}
A pair combines two values, and a list is either the
constant @racket[null] or a pair whose second
element is a list. Pairs and lists are transparent immutable values, and they may
be concrete or symbolic.
Two pairs or two lists are @racket[eq?] (resp. @racket[equal?])
if their corresponding elements are @racket[eq?] (resp. @racket[equal?]).
As values of @tech[#:key "unsolvable type"]{unsolvable types}, symbolic pairs
and lists cannot be created
via @seclink["sec:symbolic-constants"]{@code{define-symbolic[*]}}.
Instead, they are created by applying pair- or list-producing procedures to symbolic inputs,
or by controlling the application of such procedures with symbolic values. This
pattern for creating non-primitive symbolic values generalizes to all unsolvable datatypes.
@examples[#:eval rosette-eval
(define-symbolic x y z n integer?)
(code:line (define xs (take (list x y z) n)) (code:comment "(1) xs is a symbolic list."))
(define sol (solve (assert (null? xs))))
(evaluate xs sol)
(define sol
(solve (begin
(assert (= (length xs) 2))
(assert (not (equal? xs (reverse xs))))
(assert (equal? xs (sort xs <))))))
(evaluate xs sol)]
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(code:line (define p (if b (cons 1 2) (cons 4 #f))) (code:comment "(2) p is a symbolic pair."))
(define sol (solve (assert (boolean? (cdr p)))))
(evaluate p sol)
(define sol (solve (assert (odd? (car p)))))
(evaluate p sol)
]
@section{Lifted Operations on Pairs and Lists}
Rosette lifts the following operations on pairs and lists:
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
(list (list @elem{Pair Operations} @pairs:constructors+selectors)
(list @elem{List Operations} @list-operations)
(list @elem{List Iteration} @list-iteration)
(list @elem{List Filtering} @list-filtering)
(list @elem{List Searching} @list-searching)
(list @elem{Additional Pair Operations} @more-pair-ops)
(list @elem{Additional List Operations} @more-list-ops))]
@(kill-evaluator rosette-eval)
@(set! rosette-eval (rosette-evaluator))
@section{Additional Operations on Pairs and Lists}
Rosette provides the following procedures for operating on lists using @seclink["sec:bitvectors"]{bitvector} indices and lengths. These procedures produce symbolic values that avoid @racketlink[bitvector->natural]{casting} their bitvector arguments to integers, leading to @seclink["sec:notes"]{more efficiently solvable queries}.
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
@defproc[(length-bv [lst list?] [t (or/c bitvector? union?)]) bv?]{
Equivalent to @racket[(integer->bitvector (length lst) t)] but avoids the @racket[integer->bitvector] cast for better solving performance.
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(define xs (if b '(1 2) '(3 4 5 6)))
xs
(integer->bitvector (length xs) (bitvector 4))
(length-bv xs (bitvector 4))]
}
@defproc[(list-ref-bv [lst list?] [pos bv?]) any/c]{
Equivalent to @racket[(list-ref lst (bitvector->natural pos))] but avoids the @racket[bitvector->natural] cast for better solving performance.
@examples[#:eval rosette-eval
(define-symbolic p (bitvector 1))
(define xs '(1 2 3 4))
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
(list-ref xs (bitvector->natural p))
(vc)
(clear-vc!)
(code:comment "No cast and no redundant range assertion:")
(list-ref-bv xs p)
(vc)
(code:comment "But the range assertion is generated when needed:")
(define-symbolic q (bitvector 4))
(list-ref-bv xs q)
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc[(list-set-bv [lst list?] [pos bv?] [val any/c]) list?]{
Equivalent to @racket[(list-set lst (bitvector->natural pos) val)] but avoids the @racket[bitvector->natural] cast for better solving performance.
@examples[#:eval rosette-eval
(define-symbolic p (bitvector 1))
(define xs '(1 2 3 4))
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
(list-set xs (bitvector->natural p) 5)
(vc)
(clear-vc!)
(code:comment "No cast and no redundant range assertion:")
(list-set-bv xs p 5)
(vc)
(code:comment "But the range assertion is generated when needed:")
(define-symbolic q (bitvector 4))
(list-set-bv xs q 5)
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(take-bv [lst any/c] [pos bv?]) list?]
[(take-right-bv [lst any/c] [pos bv?]) any/c]
[(drop-bv [lst any/c] [pos bv?]) any/c]
[(drop-right-bv [lst any/c] [pos bv?]) list?]
[(list-tail-bv [lst any/c] [pos bv?]) any/c])]{
Equivalent to @racket[take], @racket[take-right],
@racket[drop], @racket[drop-right], or @racket[list-tail]
applied to @racket[lst] and
@racket[(bitvector->natural pos)], but avoids the
@racket[bitvector->natural] cast for better solving
performance.
@examples[#:eval rosette-eval
(define-symbolic p (bitvector 1))
(define xs (cons 1 (cons 2 (cons 3 4))))
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
(take xs (bitvector->natural p))
(vc)
(clear-vc!)
(code:comment "No cast and no redundant range assertion:")
(take-bv xs p)
(vc)
(code:comment "But the range assertion is generated when needed:")
(define-symbolic q (bitvector 4))
(take-bv xs q)
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc*[([(split-at-bv [lst any/c] [pos bv?]) (list? any/c)]
[(split-at-right-bv [lst any/c] [pos bv?]) (list? any/c)])]{
Equivalent to
@racket[(split-at lst (bitvector->natural pos))] or
@racket[(split-at-right lst (bitvector->natural pos))], but
avoids the @racket[bitvector->natural] cast for better
solving performance.
@examples[#:eval rosette-eval
(define-symbolic p (bitvector 1))
(define xs (cons 1 2))
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
(split-at xs (bitvector->natural p))
(vc)
(clear-vc!)
(code:comment "No cast and no redundant range assertion:")
(split-at-bv xs p)
(vc)
(code:comment "But the range assertion is generated when needed:")
(define-symbolic q (bitvector 4))
(split-at-bv xs q)
(vc)]}
@(kill-evaluator rosette-eval)

View File

@ -13,13 +13,13 @@
((evaluate p sol)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "*"))))
(c values c (0 (u . "*\n"))))
#""
#"")
((define sol (synthesize #:forall (list x) #:guarantee (assert (= x (p x 0)))))
@ -29,12 +29,12 @@
((evaluate p sol)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "-"))))
(c values c (0 (u . "-\n"))))
#""
#"")

View File

@ -1,11 +1,11 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/query rosette/query/eval
rosette/base/form/define rosette/query/query
rosette/base/core/term
(only-in rosette/base/core/safe assert)
(only-in rosette/base/base assert)
racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox racket/runtime-path
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@ -27,7 +27,7 @@ which any symbolic procedure could @racket[evaluate] under any @racket[solution?
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(define-symbolic x integer?)
(code:line (define p (if b * -)) (code:comment "p is a symbolic procedure"))
(code:line (define p (if b * -)) (code:comment "p is a symbolic procedure."))
(define sol (synthesize #:forall (list x)
#:guarantee (assert (= x (p x 1)))))
(evaluate p sol)

View File

@ -0,0 +1,531 @@
#lang scribble/manual
@(require (for-label
rosette/solver/solver rosette/solver/solution
rosette/solver/smt/z3 rosette/solver/smt/cvc4
rosette/solver/smt/boolector
rosette/solver/smt/bitwuzla
rosette/solver/smt/cvc5
rosette/solver/smt/stp
rosette/solver/smt/yices
rosette/base/form/define rosette/query/query
rosette/base/core/term (only-in rosette/base/base bv?)
(only-in rosette/base/base assert)
racket)
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "solvers-log")))
@title[#:tag "sec:solvers-and-solutions"]{Solvers and Solutions}
@declare-exporting[rosette/query/query
rosette/solver/solver
rosette/solver/solution
rosette/solver/smt/z3
rosette/solver/smt/cvc4
rosette/solver/smt/boolector
rosette/solver/smt/bitwuzla
rosette/solver/smt/cvc5
rosette/solver/smt/stp
rosette/solver/smt/yices
#:use-sources
(rosette/query/finitize
rosette/query/query
rosette/solver/solver
rosette/solver/solution
rosette/solver/smt/z3
rosette/solver/smt/cvc4
rosette/solver/smt/boolector
rosette/solver/smt/bitwuzla
rosette/solver/smt/cvc5
rosette/solver/smt/stp
rosette/solver/smt/yices)]
A @deftech{solver} is an automatic reasoning engine, used to answer
@seclink["sec:queries"]{queries} about Rosette programs. The result of
a solver invocation is a @deftech{solution}, containing either
a @tech{binding} of symbolic constants to concrete values, or
an @link["https://en.wikipedia.org/wiki/Unsatisfiable_core"]{unsatisfiable core}.
Solvers and solutions may not be symbolic. Two solvers (resp. solutions) are @racket[eq?]/@racket[equal?]
if they refer to the same object.
@section{The Solver Interface}
A solver contains a stack of assertions (boolean terms) to satisfy and a set of objectives (numeric terms) to optimize.
The assertion stack is partitioned into levels, with each level containing
a set of assertions. The bottom (0) assertion level cannot be removed, but more levels
can be created and removed using the @racket[solver-push] and @racket[solver-pop] procedures.
The @racket[solver-assert] procedure adds assertions to the top level of the assertion stack, while
the @racket[solver-minimize] and @racket[solver-maximize] procedures add new terms to the current set of optimization objectives.
The @racket[solver-check] procedure checks the satisfiability of all assertions in the assertion stack,
optimizing the resulting solution (if any) with respect to the provided objectives.
@defparam[current-solver solver solver?]{
The @racket[current-solver] parameter holds the solver object used for
answering solver-aided queries. Rosette's default solver is @racket[z3], although
new (SMT) solvers can be added as well. Rosette will work with any solver that implements the
@racket[gen:solver] generic interface.
@examples[#:eval rosette-eval
(current-solver)]
}
@defthing[gen:solver solver?]{
A @hyperlink["https://docs.racket-lang.org/reference/struct-generics.html"]{generic interface}
that specifies the procedures provided by a solver. These include
@racket[solver-assert],
@racket[solver-push],
@racket[solver-pop],
@racket[solver-clear],
@racket[solver-minimize],
@racket[solver-maximize],
@racket[solver-check],
@racket[solver-debug],
@racket[solver-shutdown], and
@racket[solver-features].
A solver may support a subset of this interface, which loosely follows
the @hyperlink["http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.5-r2015-06-28.pdf"]{SMTLib solver interface}.
}
@defproc[(solver? [v any/c]) boolean?]{
Returns true if @racket[v] is a concrete value that implements the @racket[gen:solver] interface.}
@defproc[(solver-assert [solver solver?] [constraints (listof boolean?)]) void?]{
Takes as input a list of boolean terms or values and
adds them to the current (top) level in the assertion stack.}
@defproc[(solver-push [solver solver?]) void?]{
Pushes a new level onto the solver's assertion stack. Subsequent calls to
@racket[solver-assert] will add assertions to this level.}
@defproc[(solver-pop [solver solver?] [levels integer?]) void?]{
Pops the given number of levels off the solver's assertion stack,
removing all the assertions at the popped levels. The number of @racket[levels] to
pop must be a positive integer that is no greater than the number of preceding
calls to @racket[solver-push].}
@defproc[(solver-clear [solver solver?]) void?]{
Clears the assertion stack of all levels and all assertions,
and removes all objectives from the current set of objectives to optimize.}
@defproc*[([(solver-minimize [solver solver?] [objs (listof (or/c integer? real? bv?))]) void?]
[(solver-maximize [solver solver?] [objs (listof (or/c integer? real? bv?))]) void?])]{
Adds the given optimization objectives to the given solver. These objectives take the form of
numeric terms whose value is to be minimized or maximized by subsequent calls to @racket[solver-check],
while satisfying all the boolean terms asserted via @racket[solver-assert].}
@defproc[(solver-check [solver solver?]) solution?]{
Searches for a binding from symbolic constants to concrete values that satisfies all
constraints (boolean terms) added to the solver via @racket[solver-assert].
If such a binding---or, a @racket[model]---exists,
it is returned in the form of a satisfiable (@racket[sat?]) solution, which optimizes
the objective terms added to the solver via @racket[solver-minimize] and @racket[solver-maximize].
Otherwise, an unsatisfiable (@racket[unsat?]) solution is returned, but without
computing an unsatisfiable @racket[core] (i.e., calling @racket[core] on the
resulting solution produces @racket[#f]).
}
@defproc[(solver-debug [solver solver?]) solution?]{
Searches for an unsatisfiable core of all constraints (boolean terms)
added to the solver via @racket[solver-assert] @emph{after} the most recent call to
@racket[clear] or @racket[solver-check] (if any).
If the constraints are satisfiable, or the given solver does
not support core extraction, an error is thrown. Otherwise, the result is an
@racket[unsat?] solution with a unsatisfiable @racket[core], expressed as a
list of boolean terms.
}
@defproc[(solver-shutdown [solver solver?]) void?]{
Terminates the current solving process (if any),
clears all added constraints, and releases all system resources associated
with the given solver instance. The solver must be able to reacquire these resources
if needed. That is, the solver should behave as though its state was merely cleared
(via @racket[solver-clear]) after a shutdown call.
}
@defproc[(solver-features [solver solver?]) (listof symbol?)]{
Returns the list of features supported by the solver.
The possible features, which correspond roughly to SMTLib @emph{logics},
extended with some additional options, are:
@itemize[
@item{@racket['qf_bv] (quantifier-free fixed-width bitvectors)}
@item{@racket['qf_uf] (quantifier-free uninterpreted functions and equality)}
@item{@racket['qf_lia] (quantifier-free linear integer arithmetic)}
@item{@racket['qf_nia] (quantifier-free non-linear integer arithmetic)}
@item{@racket['qf_lra] (quantifier-free linear real arithmetic)}
@item{@racket['qf_nra] (quantifier-free non-linear real arithmetic)}
@item{@racket['quantifiers] (quantified versions of the supported quantifier-free logics)}
@item{@racket['optimize] (support for objective function optimization)}
@item{@racket['unsat-cores] (unsatisfiable core generation)}
]
}
@defproc[(solver-options [solver solver?]) (hash/c symbol? any/c)]{
Returns the options the given solver is configured with
(as specified by the @racket[#:options] argument to solver constructors).
}
@defparam[output-smt on? (or/c boolean? path-string? output-port?)]{
Enables verbose output of generated SMT constraints.
When the @racket[output-smt] parameter is @racket[#t] or a @racket[path-string?],
Rosette will log the SMT encoding of all solver queries to temporary files.
A new temporary file is created for each solver process Rosette spawns.
Note that a single solver-aided query may spawn multiple solver processes,
and Rosette may reuse a solver process across several solver-aided queries.
When @racket[output-smt] is @racket[#t], the temporary files are created
in the system's temporary directory; otherwise,
the temporary files are created in the given path (which must be a directory).
The path to each temporary file is printed to @racket[current-error-port]
when it is first created.
When the @racket[output-smt] parameter is an @racket[output-port?],
Rosette will log the SMT encoding to that output port.
For example, setting @racket[output-smt] to @racket[(current-output-port)]
will print the SMT encoding to standard output.
All solvers will log to the same output port,
so several separate encodings may be interleaved when multiple solvers are in use.
Default value is @racket[#f].
}
@section{Supported Solvers}
Rosette supports several SMT solvers.
The @racket[current-solver] parameter controls the solver used for answering solver-aided queries.
Each supported solver is contained in a separate module
(e.g., @racketmodname[rosette/solver/smt/z3]),
which exports a constructor (e.g., @racket[z3])
to create a new solver instance.
@subsection{Z3}
@defmodule[rosette/solver/smt/z3 #:no-declare]
@defproc*[([(z3 [#:path path (or/c path-string? #f) #f]
[#:logic logic (or/c symbol? #f) #f]
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
[(z3? [v any/c]) boolean?])]{
Returns a @racket[solver?] wrapper for the @hyperlink["https://github.com/Z3Prover/z3/"]{Z3} solver from Microsoft Research.
Rosette automatically installs a version of Z3;
the optional @racket[path] argument overrides this version with a path to a new Z3 binary.
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
emitted constraints fall within the chosen logic. The default is @racket[#f],
which uses Z3's default logic.
The @racket[options] argument provides additional options that are sent to Z3
via the @tt{set-option} SMT command.
For example, setting @racket[options] to @racket[(hash ':smt.relevancy 0)]
will send the command @tt{(set-option :smt.relevancy 0)} to Z3 prior to solving.
}
@subsection{CVC4}
@defmodule[rosette/solver/smt/cvc4 #:no-declare]
@defproc*[([(cvc4 [#:path path (or/c path-string? #f) #f]
[#:logic logic (or/c symbol? #f) #f]
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
[(cvc4? [v any/c]) boolean?])]{
Returns a @racket[solver?] wrapper for the @hyperlink["http://cvc4.cs.stanford.edu/web/"]{CVC4} solver from NYU and UIowa.
To use this solver, download and install CVC4 (version 1.8 or later),
and either add the @tt{cvc4} executable to your @tt{PATH}
or pass the path to the executable as the optional @racket[path] argument.
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
emitted constraints fall within the chosen logic. The default is @racket[#f],
which uses CVC4's default logic.
The @racket[options] argument provides additional options that are sent to CVC4
via the @tt{set-option} SMT command.
For example, setting @racket[options] to @racket[(hash ':bv-propagate 'true)]
will send the command @tt{(set-option :bv-propagate true)} to CVC4 prior to solving.
}
@defproc[(cvc4-available?) boolean?]{
Returns true if the CVC4 solver is available for use (i.e., Rosette can locate a @tt{cvc4} binary).
If this returns @racket[#f], @racket[(cvc4)] will not succeed
without its optional @racket[path] argument.}
@subsection{Boolector}
@defmodule[rosette/solver/smt/boolector #:no-declare]
@defproc*[([(boolector [#:path path (or/c path-string? #f) #f]
[#:logic logic (or/c symbol? #f) #f]
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
[(boolector? [v any/c]) boolean?])]{
Returns a @racket[solver?] wrapper for the @hyperlink["http://fmv.jku.at/boolector/"]{Boolector} solver from JKU.
To use this solver, download and install Boolector (version 2.4.1 or later),
and either add the @tt{boolector} executable to your @tt{PATH}
or pass the path to the executable as the optional @racket[path] argument.
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
emitted constraints fall within the chosen logic. The default is @racket[#f],
which uses Boolector's default logic.
The @racket[options] argument provides additional options that are sent to Boolector
via the @tt{set-option} SMT command.
For example, setting @racket[options] to @racket[(hash ':seed 5)]
will send the command @tt{(set-option :seed 5)} to Boolector prior to solving.
}
@defproc[(boolector-available?) boolean?]{
Returns true if the Boolector solver is available for use (i.e., Rosette can locate a @tt{boolector} binary).
If this returns @racket[#f], @racket[(boolector)] will not succeed
without its optional @racket[path] argument.}
@subsection{Bitwuzla}
@defmodule[rosette/solver/smt/bitwuzla #:no-declare]
@defproc*[([(bitwuzla [#:path path (or/c path-string? #f) #f]
[#:logic logic (or/c symbol? #f) #f]
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
[(bitwuzla? [v any/c]) boolean?])]{
Returns a @racket[solver?] wrapper for the @hyperlink["https://bitwuzla.github.io/"]{Bitwuzla} solver.
To use this solver, download prebuilt Bitwuzla or build it yourself,
and ensure the executable is on your @tt{PATH} or pass the path to the
executable as the optional @racket[path] argument.
Rosette currently tests Bitwuzla at commit
@tt{93a3d930f622b4cef0063215e63b7c3bd10bd663}.
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
emitted constraints fall within the chosen logic. The default is @racket[#f],
which uses Bitwuzla's default logic.
The @racket[options] argument provides additional options that are sent to Bitwuzla
via the @tt{set-option} SMT command.
For example, setting @racket[options] to @racket[(hash ':seed 5)]
will send the command @tt{(set-option :seed 5)} to Bitwuzla prior to solving.
}
@defproc[(bitwuzla-available?) boolean?]{
Returns true if the Bitwuzla solver is available for use (i.e., Rosette can locate a @tt{bitwuzla} binary).
If this returns @racket[#f], @racket[(bitwuzla)] will not succeed
without its optional @racket[path] argument.}
@subsection{CVC5}
@defmodule[rosette/solver/smt/cvc5 #:no-declare]
@defproc*[([(cvc5 [#:path path (or/c path-string? #f) #f]
[#:logic logic (or/c symbol? #f) #f]
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
[(cvc5? [v any/c]) boolean?])]{
Returns a @racket[solver?] wrapper for the @hyperlink["https://cvc5.github.io/"]{CVC5} solver.
To use this solver, download prebuilt CVC5 or build it yourself,
and ensure the executable is on your @tt{PATH} or pass the path to the
executable as the optional @racket[path] argument.
Rosette currently tests CVC5 at version 1.0.7.
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
emitted constraints fall within the chosen logic. The default is @racket[#f],
which uses CVC5's default logic.
The @racket[options] argument provides additional options that are sent to CVC5
via the @tt{set-option} SMT command.
For example, setting @racket[options] to @racket[(hash ':seed 5)]
will send the command @tt{(set-option :seed 5)} to CVC5 prior to solving.
}
@defproc[(cvc5-available?) boolean?]{
Returns true if the CVC5 solver is available for use (i.e., Rosette can locate a @tt{cvc5} binary).
If this returns @racket[#f], @racket[(cvc5)] will not succeed
without its optional @racket[path] argument.}
@subsection{STP}
@defmodule[rosette/solver/smt/stp #:no-declare]
@defproc*[([(stp [#:path path (or/c path-string? #f) #f]
[#:logic logic (or/c symbol? #f) #f]
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
[(stp? [v any/c]) boolean?])]{
Returns a @racket[solver?] wrapper for the @hyperlink["https://stp.github.io/"]{STP} solver.
To use this solver, download prebuilt STP or build it yourself,
and ensure the executable is on your @tt{PATH} or pass the path to the
executable as the optional @racket[path] argument.
Rosette currently tests STP at commit
@tt{0510509a85b6823278211891cbb274022340fa5c}.
Note that as of December 2023, the STP version on Mac Homebrew is too old to be
supported by Rosette.
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
emitted constraints fall within the chosen logic. The default is @racket[#f],
which uses STP's default logic.
The @racket[options] argument provides additional options that are sent to STP
via the @tt{set-option} SMT command.
For example, setting @racket[options] to @racket[(hash ':seed 5)]
will send the command @tt{(set-option :seed 5)} to STP prior to solving.
}
@defproc[(stp-available?) boolean?]{
Returns true if the STP solver is available for use (i.e., Rosette can locate a @tt{stp} binary).
If this returns @racket[#f], @racket[(stp)] will not succeed
without its optional @racket[path] argument.}
@subsection{Yices2}
@defmodule[rosette/solver/smt/yices #:no-declare]
@defproc*[([(yices [#:path path (or/c path-string? #f) #f]
[#:logic logic (or/c symbol? #f) 'QF_BV]
[#:options options (hash/c symbol? any/c) (hash)]) solver?]
[(yices? [v any/c]) boolean?])]{
Returns a @racket[solver?] wrapper for the @hyperlink["https://yices.csl.sri.com/"]{Yices2} solver.
To use this solver, download prebuilt Yices2 or build it yourself,
and ensure the executable is on your @tt{PATH} or pass the path to the
executable as the optional @racket[path] argument.
Rosette specifically uses the @tt{yices-smt2} executable, which is the Yices2
solver with its SMTLIB2 frontend enabled.
Note that just building (without installing) Yices2 will produce an executable
named @tt{yices_smt2}. Running the installation step produces an executable
with the correct name. However, it is safe to skip the installation step and
simply rename or symlink the @tt{yices_smt2} executable to @tt{yices-smt2}.
Rosette currently tests Yices2 at commit
@tt{e27cf308cffb0ecc6cc7165c10e81ca65bc303b3}.
The optional @racket[logic] argument specifies an SMT logic for the solver to use (e.g., @racket['QF_BV]).
Specifying a logic can improve solving performance, but Rosette makes no effort to check that
emitted constraints fall within the chosen logic. Yices2 expects a logic to be
set; Rosette defaults to @racket['QF_BV].
The @racket[options] argument provides additional options that are sent to Yices2
via the @tt{set-option} SMT command.
For example, setting @racket[options] to @racket[(hash ':seed 5)]
will send the command @tt{(set-option :seed 5)} to Yices2 prior to solving.
}
@defproc[(yices-available?) boolean?]{
Returns true if the Yices2 solver is available for use (i.e., Rosette can locate a @tt{yices-smt2} binary).
If this returns @racket[#f], @racket[(yices)] will not succeed
without its optional @racket[path] argument.}
@section{Solutions}
A solution to a set of formulas may be satisfiable (@racket[sat?]), unsatisfiable (@racket[unsat?]),
or unknown (@racket[unknown?]).
A satisfiable solution can be used as a procedure: when applied to a bound symbolic constant, it returns
a concrete value for that constant; when applied to any other value, it returns
the value itself.
The solver returns an @racket[unknown?] solution if it cannot determine whether
the given constraints are satisfiable or not.
A solution supports the following operations:
@defproc[(solution? [v any/c]) boolean?]{
Returns true if @racket[v] is a solution.}
@defproc[(sat? [v any/c]) boolean?]{
Returns true if @racket[v] is a satisfiable solution.}
@defproc[(unsat? [v any/c]) boolean?]{
Returns true if @racket[v] is an unsatisfiable solution.}
@defproc[(unknown? [v any/c]) boolean?]{
Returns true if @racket[v] is an unknown solution.}
@defproc*[([(sat) sat?]
[(sat [binding (hash/c constant? any/c #:immutable #t)]) sat?])]{
Returns a satisfiable solution that holds the given binding from symbolic
constants to values, or that holds the empty binding. The provided hash must
bind every symbolic constant in its keyset to a concrete value of the same type.
}
@defproc*[([(unsat) unsat?]
[(unsat [constraints (listof boolean?)]) unsat?])]{
Returns an unsatisfiable solution. The @racket[constraints] list, if provided,
consist of boolean values that are collectively unsatisfiable. If no constraints
are provided, applying @racket[core] to the resulting solution produces @racket[#f],
indicating that there is no satisfying solution but
core extraction was not performed. (Core extraction is an expensive
operation that is not supported by all solvers; those that do support it
do not compute a core unless explicitly asked for one via @racket[solver-debug].)}
@defproc[(unknown) unknown?]{
Returns an unknown solution.}
@defproc[(model [sol sat?]) (hash/c constant? any/c #:immutable #t)]{
Returns the binding stored in the given satisfiable solution. The binding is an immutable
hashmap from symbolic constants to values.
}
@defproc[(core [sol unsat?]) (or/c (listof (and/c constant? boolean?)) #f)]{
Returns the unsatisfiable core stored in the given satisfiable solution. If the solution is
@racket[unsat?] and a core was computed, the result is a list of boolean values that
are collectively unsatisfiable. Otherwise, the result is @racket[#f].
}
@defproc[(evaluate [v any/c] [sol sat?]) any/c]{
Given a Rosette value and a satisfiable solution, @racket[evaluate] produces a
new value obtained by replacing every symbolic constant @var[c] in @racket[v]
with @racket[(sol #, @var[c])] and simplifying the result.
@examples[#:eval rosette-eval
(define-symbolic a b boolean?)
(define-symbolic x y integer?)
(define sol
(solve (begin (assert a)
(assert (= x 1))
(assert (= y 2)))))
(sat? sol)
(evaluate (list 4 5 x) sol)
(define vec (vector a))
(evaluate vec sol)
(code:line (eq? vec (evaluate vec sol)) (code:comment "Evaluation produces a new vector."))
(evaluate (+ x y) sol)
(evaluate (and a b) sol)
]}
@defproc[(complete-solution [sol solution?] [consts (listof constant?)]) solution?]{
Given a solution @racket[sol] and a list of symbolic
constants @racket[consts], returns a solution that is
complete with respect to the given list. In particular, if
@racket[sol] is satisfiable, the returned solution is also
satisfiable, and it extends the @racket[sol] model with
default bindings for all constants in @racket[consts] that
are not bound by @racket[sol]. Otherwise, @racket[sol]
itself is returned.
@examples[#:eval rosette-eval
(define-symbolic a boolean?)
(define-symbolic x integer?)
(define sol (solve (assert a)))
(code:line sol (code:comment "No binding for x."))
(complete-solution sol (list a x))
(complete-solution (solve (assert #f)) (list a x))
]}
@(kill-evaluator rosette-eval)

View File

@ -0,0 +1,101 @@
;; This file was created by make-log-based-eval
((current-solver)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "#<z3>\n"))))
#""
#"")
((define-symbolic a b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x y integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define sol (solve (begin (assert a) (assert (= x 1)) (assert (= y 2)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((sat? sol) ((3) 0 () 0 () () (q values #t)) #"" #"")
((evaluate (list 4 5 x) sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'(4 5 1)\n"))))
#""
#"")
((define vec (vector a)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((evaluate vec sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'#(#t)\n"))))
#""
#"")
((eq? vec (evaluate vec sol)) ((3) 0 () 0 () () (q values #f)) #"" #"")
((evaluate (+ x y) sol) ((3) 0 () 0 () () (q values 3)) #"" #"")
((evaluate (and a b) sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "b\n"))))
#""
#"")
((define-symbolic a boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic x integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define sol (solve (assert a)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(sol
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [a #t])\n"))))
#""
#"")
((complete-solution sol (list a x))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model\n [a #t]\n [x 0])\n"))))
#""
#"")
((complete-solution (solve (assert #f)) (list a x))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#""
#"")

View File

@ -1,4 +1,5 @@
;; This file was created by make-log-based-eval
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic f (~> integer? boolean?))
((3) 0 () 0 () () (c values c (void)))
#""
@ -6,38 +7,38 @@
((f 1)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(app f 1)"))))
(c values c (0 (u . "(app f 1)\n"))))
#""
#"")
((define-symbolic x real?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((f x)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(app f (real->integer x))"))))
(c values c (0 (u . "(app f (real->integer x))\n"))))
#""
#"")
((asserts)
((vc)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (c (0 (u . "(int? x)")))))
(c values c (0 (u . "(vc #t (int? x))\n"))))
#""
#"")
((define sol (solve (assert (not (equal? (f x) (f 1))))))
@ -48,29 +49,29 @@
(g
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(fv (((1) . #f) ((0) . #t)) #f integer?~>boolean?)"))))
(c values c (0 (u . "(fv integer?~>boolean?)\n"))))
#""
#"")
((evaluate x sol) ((3) 0 () 0 () () (q values 0)) #"" #"")
((fv? f) ((3) 0 () 0 () () (q values #t)) #"" #"")
((fv? g) ((3) 0 () 0 () () (q values #t)) #"" #"")
((g 2) ((3) 0 () 0 () () (q values #f)) #"" #"")
((g 2) ((3) 0 () 0 () () (q values #t)) #"" #"")
((g x)
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(= 0 (real->integer x))"))))
(c values c (0 (u . "(ite (= 1 (real->integer x)) #f #t)\n"))))
#""
#"")
((define t (~> integer? real? boolean? (bitvector 4)))
@ -80,13 +81,13 @@
(t
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "integer?~>real?~>boolean?~>(bitvector? 4)"))))
(c values c (0 (u . "integer?~>real?~>boolean?~>(bitvector 4)\n"))))
#""
#"")
((~> t integer?)
@ -98,7 +99,7 @@
()
(q
exn
"function: expected a list of primitive solvable types\n domain: '(integer?~>real?~>boolean?~>(bitvector? 4))"))
"function: expected a list of primitive solvable types\n domain: '(integer?~>real?~>boolean?~>(bitvector 4))"))
#""
#"")
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
@ -111,7 +112,7 @@
()
(q
exn
"function: expected a primitive solvable type\n range: {[b boolean?] [(! b) real?]}"))
"function: expected a primitive solvable type\n range: (union [b boolean?] [(! b) real?])"))
#""
#"")
((~> real?)
@ -123,7 +124,7 @@
()
(q
exn
"~>: arity mismatch;\n the expected number of arguments does not match the given number\n given: 1\n arguments...:\n real?"))
"~>: arity mismatch;\n the expected number of arguments does not match the given number\n expected: at least 2\n given: 1"))
#""
#"")
((define t0? (~> integer? real? boolean? (bitvector 4)))
@ -140,7 +141,7 @@
((function? (if b t0? t1?)) ((3) 0 () 0 () () (q values #f)) #"" #"")
((function? integer?) ((3) 0 () 0 () () (q values #f)) #"" #"")
((function? 3) ((3) 0 () 0 () () (q values #f)) #"" #"")
((clear-asserts!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic f (~> boolean? boolean?))
((3) 0 () 0 () () (c values c (void)))
#""
@ -151,13 +152,13 @@
((fv? (if b f 1))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "b"))))
(c values c (0 (u . "b\n"))))
#""
#"")
((define sol (solve (begin (assert (not (f #t))) (assert (f #f)))))
@ -168,28 +169,25 @@
(g
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(fv (((#f) . #t) ((#t) . #f)) #t boolean?~>boolean?)"))))
(c values c (0 (u . "(fv boolean?~>boolean?)\n"))))
#""
#"")
((fv? g) ((3) 0 () 0 () () (q values #t)) #"" #"")
((verify (assert (equal? (g b) (not b))))
((3)
1
(((lib "rosette/doc/guide/scribble/util/lifted.rkt")
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)"))))
(c values c (0 (u . "(unsat)\n"))))
#""
#"")

View File

@ -1,23 +1,23 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/form rosette/query/eval rosette/solver/solution
rosette/base/form/define rosette/query/query rosette/solver/solution
rosette/base/core/term (only-in rosette/query/finitize current-bitwidth)
(only-in rosette/base/core/union union?)
(only-in rosette/base/core/function ~> function? fv?)
(only-in rosette/base/base bv bitvector)
(only-in rosette/base/core/safe assert)
(only-in rosette/base/core/bool asserts))
(only-in rosette/base/base bv bitvector assert vc clear-vc!))
(for-label racket) racket/runtime-path
scribble/core scribble/html-properties scribble/eval racket/sandbox
scribble/core scribble/html-properties scribble/examples racket/sandbox
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "uninterpreted-log")))
@title{Uninterpreted Functions}
@title[#:tag "sec:UF"]{Uninterpreted Functions}
@declare-exporting[rosette/base/base #:use-sources (rosette/base/core/function)]
@declare-exporting[rosette/base/base #:use-sources (rosette/base/core/function
rosette/query/finitize
rosette/base/base)]
In Rosette, functions are special kinds of @seclink["sec:proc"]{procedures} that are pure
(have no side effects) and total (defined on every input value).
@ -32,54 +32,61 @@ no fixed meaning. Their meaning (or interpretation) is determined by the underl
as the result of a @seclink["sec:queries"]{solver-aided query}.
@examples[#:eval rosette-eval
(code:comment "an uninterpreted function from integers to booleans:")
(current-bitwidth #f)
(code:comment "An uninterpreted function from integers to booleans:")
(define-symbolic f (~> integer? boolean?))
(code:line (f 1) (code:comment "no built-in interpretation for 1"))
(code:line (f 1) (code:comment "No built-in interpretation for 1."))
(define-symbolic x real?)
(code:line (f x) (code:comment "this typechecks when x is an integer"))
(code:line (asserts) (code:comment "so Rosette emits the corresponding assertion"))
(code:line (f x) (code:comment "This typechecks when x is an integer,"))
(code:line (vc) (code:comment "so Rosette emits the corresponding assertion."))
(define sol (solve (assert (not (equal? (f x) (f 1))))))
(code:line (define g (evaluate f sol)) (code:comment "an interpretation of f"))
(code:line (define g (evaluate f sol)) (code:comment "An interpretation of f."))
g
(evaluate x sol)
(code:line (fv? f) (code:comment "f is a function value"))
(code:line (fv? g) (code:comment "and so is g"))
(code:line (g 2) (code:comment "we can apply g to concrete values"))
(code:line (g x) (code:comment "and to symbolic values"))]
(code:line (fv? f) (code:comment "f is a function value,"))
(code:line (fv? g) (code:comment "and so is g."))
(code:line (g 2) (code:comment "We can apply g to concrete values"))
(code:line (g x) (code:comment "and to symbolic values."))]
@defproc[(~> [d (and/c solvable? (not/c function?))] ...+
[r (and/c solvable? (not/c function?))]) function?]{
Returns a type predicate for recognizing functions that take as input
values of types @racket[d...+] and produce values of type @racket[r].
The domain and range arguments must be concrete @racket[solvable?] types that are
not themselves functions. Note that @racket[~>] expects at least one domain
type to be given, disallowing zero-argument functions.
@examples[#:eval rosette-eval
(define t (~> integer? real? boolean? (bitvector 4)))
t
(~> t integer?)
(eval:error (~> t integer?))
(define-symbolic b boolean?)
(~> integer? (if b boolean? real?))
(~> real?)]
(eval:error (~> integer? (if b boolean? real?)))
(eval:error (~> real?))]
}
@defproc[(function? [v any/c]) boolean?]{
Returns true if @racket[v] is a concrete type predicate that recognizes function values.
@examples[#:eval rosette-eval
(define t0? (~> integer? real? boolean? (bitvector 4)))
(define t1? (~> integer? real?))
(function? t0?)
(function? t1?)
(define-symbolic b boolean?)
(code:line (function? (if b t0? t1?)) (code:comment "not a concrete type"))
(code:line (function? integer?) (code:comment "not a function type"))
(code:line (function? 3) (code:comment "not a type"))]
(code:line (function? (if b t0? t1?)) (code:comment "Not a concrete type."))
(code:line (function? integer?) (code:comment "Not a function type."))
(code:line (function? 3) (code:comment "Not a type."))]
}
@(rosette-eval '(clear-asserts!))
@(rosette-eval '(clear-vc!))
@defproc[(fv? [v any/c]) boolean?]{
Returns true if @racket[v] is a concrete or symbolic function value.
@examples[#:eval rosette-eval
(define-symbolic f (~> boolean? boolean?))
(fv? f)
@ -92,9 +99,9 @@ g
(assert (not (f #t)))
(assert (f #f)))))
(define g (evaluate f sol))
(code:line g (code:comment "g implements logical negation"))
(code:line g (code:comment "g implements logical negation."))
(fv? g)
(code:comment "verify that g implements logical negation:")
(code:comment "Verify that g implements logical negation:")
(verify (assert (equal? (g b) (not b))))]
}

View File

@ -0,0 +1,327 @@
;; This file was created by make-log-based-eval
((define v1 (vector 1 2 #f)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define v2 (vector 1 2 #f)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((eq? v1 v2) ((3) 0 () 0 () () (q values #f)) #"" #"")
((equal? v1 v2) ((3) 0 () 0 () () (q values #t)) #"" #"")
((define v3 (vector-immutable 1 2 #f))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define v4 (vector-immutable 1 2 #f))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((eq? v3 v4) ((3) 0 () 0 () () (q values #t)) #"" #"")
((equal? v1 v3) ((3) 0 () 0 () () (q values #t)) #"" #"")
((define-symbolic x y z n integer?)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define xs (take (list x y z) n))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define vs (list->vector xs)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define sol
(solve (begin (assert (< n 3)) (assert (= 4 (vector-ref vs (sub1 n)))))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((evaluate n sol) ((3) 0 () 0 () () (q values 1)) #"" #"")
((evaluate (list x y z) sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'(4 0 0)\n"))))
#""
#"")
((evaluate vs sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'#(4)\n"))))
#""
#"")
((evaluate xs sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'(4)\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define xs (if b (vector 1 2) (vector 3 4 5 6)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(xs
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(union [b #(1 2)] [(! b) #(3 4 5 6)])\n"))))
#""
#"")
((integer->bitvector (vector-length xs) (bitvector 4))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(integer->bitvector (ite b 2 4) (bitvector 4))\n"))))
#""
#"")
((vector-length-bv xs (bitvector 4))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(ite b (bv #x2 4) (bv #x4 4))\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic p (bitvector 1))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define xs (vector 1 2 3 4)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vector-ref xs (bitvector->natural p))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(ite*\n (⊢ (= 0 (bitvector->natural p)) 1)\n (⊢ (= 1 (bitvector->natural p)) 2)\n (⊢ (= 2 (bitvector->natural p)) 3)\n (⊢ (= 3 (bitvector->natural p)) 4))\n\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(vc #t (&& (<= 0 (bitvector->natural p)) (< (bitvector->natural p) 4)))\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vector-ref-bv xs p)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(ite* (⊢ (bveq (bv #b0 1) p) 1) (⊢ (bveq (bv #b1 1) p) 2))\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t #t)\n"))))
#""
#"")
((define-symbolic q (bitvector 4))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((vector-ref-bv xs q)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(ite*\n (⊢ (bveq (bv #x0 4) q) 1)\n (⊢ (bveq (bv #x1 4) q) 2)\n (⊢ (bveq (bv #x2 4) q) 3)\n (⊢ (bveq (bv #x3 4) q) 4))\n\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t (bvult q (bv #x4 4)))\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic p (bitvector 1))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define xs (vector 1 2 3 4)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vector-set! xs (bitvector->natural p) 5)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(xs
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(vector\n (ite (= 0 (bitvector->natural p)) 5 1)\n (ite (= 1 (bitvector->natural p)) 5 2)\n (ite (= 2 (bitvector->natural p)) 5 3)\n (ite (= 3 (bitvector->natural p)) 5 4))\n\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(vc #t (&& (<= 0 (bitvector->natural p)) (< (bitvector->natural p) 4)))\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define xs (vector 1 2 3 4)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vector-set!-bv xs p 5) ((3) 0 () 0 () () (c values c (void))) #"" #"")
(xs
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(vector (ite (bveq (bv #b0 1) p) 5 1) (ite (bveq (bv #b1 1) p) 5 2) 3 4)\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t #t)\n"))))
#""
#"")
((define-symbolic q (bitvector 4))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define xs (vector 1 2 3 4)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vector-set!-bv xs q 5) ((3) 0 () 0 () () (c values c (void))) #"" #"")
(xs
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(vector\n (ite (bveq (bv #x0 4) q) 5 1)\n (ite (bveq (bv #x1 4) q) 5 2)\n (ite (bveq (bv #x2 4) q) 5 3)\n (ite (bveq (bv #x3 4) q) 5 4))\n\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc #t (bvult q (bv #x4 4)))\n"))))
#""
#"")

View File

@ -0,0 +1,135 @@
#lang scribble/manual
@(require (for-label
rosette/base/form/define rosette/query/query
rosette/base/core/term
rosette/solver/solution
(only-in rosette/base/base assert define-symbolic union?
vc clear-vc! bitvector bitvector? bv?
bitvector->natural integer->bitvector
vector-length-bv vector-ref-bv vector-set!-bv)
racket)
scribble/core scribble/html-properties scribble/examples racket/sandbox racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "vectors-log")))
@(define vector-ops (select '(vector? make-vector vector vector-immutable vector-length vector-ref vector-set! vector->list list->vector vector->immutable-vector vector-fill! vector-copy! vector->values build-vector immutable?)))
@(define more-vector-ops (select '(vector-set*! vector-map vector-map! vector-append vector-take vector-take-right vector-drop vector-drop-right vector-split-at vector-split-at-right vector-copy vector-filter vector-filter-not vector-count vector-argmin vector-argmax vector-member vector-memv vector-memq)))
@title[#:tag "sec:vec"]{Vectors}
A vector is a fixed-length (im)mutable array.
Vectors may be concrete or symbolic, and they may be accessed using concrete
or symbolic indices. A concrete vector supports constant-time access for
concrete slot indices, and linear-time access for symbolic slot indices.
A symbolic vector supports (worst-case) linear- and quadratic-time access for concrete and
symbolic indices, respectively. Access time for symbolic vectors is given with
respect to the longest possible concrete array to which any symbolic vector
could @racket[evaluate] under any @racket[solution?].
Like @seclink["sec:pair"]{pairs and lists}, immutable vectors are transparent immutable values:
two such vectors are @racket[eq?] if they have the same length and @racket[eq?] contents.
Mutable vectors are references rather than values, and two mutable vectors are @racket[eq?] if and only if they
point to the same array object. Two vectors (regardless of mutability) are @racket[equal?]
if they have the same length and @racket[equal?] contents.
@examples[#:eval rosette-eval
(define v1 (vector 1 2 #f))
(define v2 (vector 1 2 #f))
(eq? v1 v2)
(equal? v1 v2)
(define v3 (vector-immutable 1 2 #f))
(define v4 (vector-immutable 1 2 #f))
(eq? v3 v4)
(equal? v1 v3)
]
@examples[#:eval rosette-eval
(define-symbolic x y z n integer?)
(code:line (define xs (take (list x y z) n)) (code:comment "xs is a symbolic list."))
(code:line (define vs (list->vector xs)) (code:comment "vs is a symbolic vector."))
(define sol
(solve
(begin
(assert (< n 3))
(assert (= 4 (vector-ref vs (sub1 n)))))))
(evaluate n sol)
(evaluate (list x y z) sol)
(evaluate vs sol)
(evaluate xs sol)]
@section{Lifted Operations on Vectors}
The following vector operations are lifted to work on both concrete and symbolic values:
@tabular[#:style (style #f (list (attributes '((id . "lifted")(class . "boxed")))))
(list (list @elem{Vector Operations} @elem{@vector-ops, @more-vector-ops}))]
@(rosette-eval '(clear-vc!))
@section{Additional Operations on Vectors}
Rosette provides the following procedures for operating on vectors using @seclink["sec:bitvectors"]{bitvector} indices and lengths. These procedures produce symbolic values that avoid @racketlink[bitvector->natural]{casting} their bitvector arguments to integers, leading to @seclink["sec:notes"]{more efficiently solvable queries}.
@declare-exporting[rosette/base/base #:use-sources (rosette/base/base)]
@defproc[(vector-length-bv [vec vector?] [t (or/c bitvector? union?)]) bv?]{
Equivalent to @racket[(integer->bitvector (vector-length vec) t)] but avoids the @racket[integer->bitvector] cast for better solving performance.
@examples[#:eval rosette-eval
(define-symbolic b boolean?)
(define xs (if b (vector 1 2) (vector 3 4 5 6)))
xs
(integer->bitvector (vector-length xs) (bitvector 4))
(vector-length-bv xs (bitvector 4))]
}
@(rosette-eval '(clear-vc!))
@defproc[(vector-ref-bv [vec vector?] [pos bv?]) any/c]{
Equivalent to @racket[(vector-ref vec (bitvector->natural pos))] but avoids the @racket[bitvector->natural] cast for better solving performance.
@examples[#:eval rosette-eval
(define-symbolic p (bitvector 1))
(define xs (vector 1 2 3 4))
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
(vector-ref xs (bitvector->natural p))
(vc)
(clear-vc!)
(code:comment "No cast and no redundant range assertion:")
(vector-ref-bv xs p)
(vc)
(code:comment "But the range assertion is generated when needed:")
(define-symbolic q (bitvector 4))
(vector-ref-bv xs q)
(vc)]
}
@(rosette-eval '(clear-vc!))
@defproc[(vector-set!-bv [vec vector?] [pos bv?] [val any/c]) void?]{
Equivalent to @racket[(vector-set! vec (bitvector->natural pos) val)] but avoids the @racket[bitvector->natural] cast for better solving performance.
@examples[#:eval rosette-eval
(define-symbolic p (bitvector 1))
(define xs (vector 1 2 3 4))
(code:comment "Uses a cast and generates a redundant assertion on the range of p:")
(vector-set! xs (bitvector->natural p) 5)
xs
(vc)
(clear-vc!)
(code:comment "No cast and no redundant range assertion:")
(define xs (vector 1 2 3 4))
(vector-set!-bv xs p 5)
xs
(vc)
(code:comment "But the range assertion is generated when needed:")
(define-symbolic q (bitvector 4))
(define xs (vector 1 2 3 4))
(vector-set!-bv xs q 5)
xs
(vc)]
}

View File

@ -0,0 +1,224 @@
;; This file was created by make-log-based-eval
((require (only-in rosette/guide/scribble/util/lifted format-opaque))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define-symbolic xs integer? #:length 4)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (sum xs) (foldl + xs)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((verify (assert (= (sum xs) (sum (filter-not zero? xs)))))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(model)\n"))))
#""
#"")
((define-symbolic opt boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((synthesize
#:forall
xs
#:guarantee
(assert (= (sum xs) (apply (if opt + -) xs))))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#""
#"")
((require rackunit) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (post xs) (assert (= (sum xs) (sum (filter-not zero? xs)))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (query xs) (verify (post xs)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define example-tests
(test-suite
"An example suite for a sum query."
#:before
clear-vc!
#:after
clear-vc!
(test-case
"Test sum with concrete values."
(check = (sum '()) 0)
(check = (sum '(-1)) -1)
(check = (sum '(-2 2)) 0)
(check = (sum '(-1 0 3)) 2))
(test-case
"Test query post for exceptions."
(before (clear-vc!) (check-not-exn (thunk (post xs)))))
(test-case
"Test query outcome."
(before (clear-vc!) (check-pred unsat? (query xs))))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((format-opaque "~a" (run-test example-tests))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(#<test-error> #<test-failure> #<test-failure>)\n"))))
#""
#"")
((define (sum xs)
(cond
((null? xs) 0)
((null? (cdr xs)) (car xs))
((andmap (curry = (car xs)) (cdr xs)) (* (length xs) (cdr xs)))
(else (apply + xs))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((assume (positive? (sum xs))) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((verify (assert (ormap positive? xs)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#""
#"")
((define (pre xs) (assume (positive? (sum xs))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (post xs) (assert (ormap positive? xs)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (query xs) (pre xs) (verify (post xs)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define example-tests
(test-suite
"An example suite for a sum query."
#:before
clear-vc!
#:after
clear-vc!
(test-case
"Test sum with concrete values."
(check = (sum '()) 0)
(check = (sum '(-1)) -1)
(check = (sum '(-2 2)) 0)
(check = (sum '(-1 0 3)) 2))
(test-case
"Test query post for exceptions."
(before (clear-vc!) (check-not-exn (thunk (pre xs)))))
(test-case
"Test query post for exceptions."
(before (clear-vc!) (check-not-exn (thunk (post xs)))))
(test-case
"Test query outcome."
(before (clear-vc!) (check-pred unsat? (query xs))))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((format-opaque "~a" (run-test example-tests))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(#<test-success> #<test-success> #<test-success> #<test-success>)\n"))))
#""
#"")
((test-case "Test sum for any failures." (check-pred unsat? (verify (sum xs))))
((3) 0 () 0 () () (c values c (void)))
#""
#"--------------------\nTest sum for any failures.\nFAILURE\nname: check-pred\nlocation: eval:20:0\nparams:\n '(#<procedure:unsat?> (model\n [xs$0 0]\n [xs$1 0]\n [xs$2 0]\n [xs$3 0]))\n--------------------\n")
((verify (begin (assume (positive? (sum xs))) (assert (ormap positive? xs))))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [xs$0 1]\n [xs$1 1]\n [xs$2 1]\n [xs$3 1])\n"))))
#""
#"")
((assume (positive? (sum xs))) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((verify (assert (ormap positive? xs)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#""
#"")
((define (select xs n)
(cond
((empty? xs) (assert #f "unexpected empty list"))
(else
(define pivot (first xs))
(define non-pivot (rest xs))
(define <pivot (filter (λ (x) (< x pivot)) non-pivot))
(define >=pivot (filter (λ (x) (>= x pivot)) non-pivot))
(define len< (length <pivot))
(cond
((= n len<) pivot)
((< n len<) (select <pivot))
(else (select >=pivot (- n len< 1)))))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define-symbolic n k integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assume (and (<= 0 n (sub1 (length xs))) (= k (select xs n))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((verify (assert (= k (list-ref (sort xs <) n))))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#""
#"")

View File

@ -0,0 +1,420 @@
#lang scribble/manual
@(require scribble/core scribble/html-properties
scribble/bnf scribble/example
(for-label (except-in racket list-set) errortrace
rosette/base/core/term
rosette/base/form/define
rosette/query/form
rosette/base/core/union
(only-in rosette unsat model evaluate sat? unsat? clear-vc! current-bitwidth)
(only-in rosette/base/base assume assert vc clear-vc!)
rackunit)
racket/runtime-path
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "error-tracer-log") #f 'rosette))
@(define-runtime-path interface.png "interface.png")
@(define-runtime-path quickselect.png "quickselect.png")
@(rosette-eval '(require (only-in rosette/guide/scribble/util/lifted format-opaque)))
@title[#:tag "ch:error-tracing"]{Debugging}
Bugs in Rosette programs often manifest as runtime
exceptions. For example, calling a procedure with too few
arguments will cause a runtime exception in Rosette, just as
it would in Racket. But unlike Racket, Rosette treats
exceptions as assertion failures: it catches the exception,
updates the @tech{verification condition} to reflect the
failure, and proceeds with symbolic evaluation. This
treatment of exceptions ensures that the program's
@seclink["ch:syntactic-forms:rosette"]{ solver-aided
queries} correctly return a @racket[sat?] or @racket[unsat?]
solution, but it can also make solver-aided code tricky to
debug. This chapter describes common problems that are due
to intercepted exceptions, how to test for them, and how to
find them with the @code{symtrace} tool for error tracing.
@section[#:tag "sec:errors-in-rosette"]{Common Bugs in Solver-Aided Code}
Rosette intercepts exceptions in two places: within
solver-aided queries and within conditional expressions.
When converted to assertion failures, these exceptions can
lead to unexpected query results, as well as subtle logical
errors with no obvious manifestation. We illustrate both
kinds of problems next and show how to test for them.
@subsection[#:tag "sec:errors-under-queries"]{Bugs Due to Exceptions in Solver-Aided Queries}
When an exception is intercepted within a solver-aided
query, the query will often produce an unexpected result:
a model when we expect @racket[unsat], and vice versa.
As an example, consider the following verification query,
which tries to prove that the sum of a list of integers
remains the same when all zeros are removed from the list:
@examples[#:eval rosette-eval #:label #f
(define-symbolic xs integer? #:length 4)
(code:line (define (sum xs) (foldl + xs)) (code:comment "bug: missing 0 after +"))
(verify (assert (= (sum xs) (sum (filter-not zero? xs)))))
]
Because we expect this property to hold, we expect the query
to return @racket[unsat]. Instead, it returns the empty model.
To see why, note that the @racket[sum] procedure contains a
simple bug. We forgot to provide the initial value of 0 to
@racket[foldl], so @racket[foldl] is called with too few
arguments. This omission will cause every call to
@racket[sum] to raise an exception, including
@racket[(sum xs)] in the body of our query. Rosette
intercepts this exception and adds @racket[#f] to the
query's @tech{verification condition} because the exception
happens unconditionally (on all paths).
This false assertion then causes the query to return a
trivial counterexample, @racket[(model)], indicating that
@emph{any} binding of @racket[xs] to concrete integers leads
to an error.
As another example, consider the following synthesis query
involving @racket[sum]:
@examples[#:eval rosette-eval #:label #f
(define-symbolic opt boolean?)
(synthesize
#:forall xs
#:guarantee (assert (= (sum xs) (apply (if opt + -) xs))))
]
Here, the expected result is a model that binds @racket[opt]
to the value @racket[#t], and this is the outcome we see
once we fix the bug in @racket[sum]. The bug, however,
causes the @racket[#:guarantee] expression to fail
unconditionally. Rosette then intercepts the exception and
returns @racket[(unsat)] to indicate that no choice of
@racket[opt] can satisfy the specification.
Bugs of this kind can be found through testing. A good test
suite should check that queries produce expected results on
small inputs, and that query parts do not throw exceptions.
When possible, it is also good practice to test all
solver-aided code against concrete inputs and outputs. Here
is an example test suite for our first query that includes
all of these checks:
@examples[#:eval rosette-eval #:label #f
(eval:no-prompt
(require rackunit)
(define (post xs)
(assert (= (sum xs) (sum (filter-not zero? xs)))))
(define (query xs)
(verify (post xs)))
(define example-tests
(test-suite
"An example suite for a sum query."
#:before clear-vc!
#:after clear-vc!
(test-case
"Test sum with concrete values."
(check = (sum '()) 0)
(check = (sum '(-1)) -1)
(check = (sum '(-2 2)) 0)
(check = (sum '(-1 0 3)) 2))
(test-case
"Test query post for exceptions."
(before
(clear-vc!)
(check-not-exn (thunk (post xs)))))
(test-case
"Test query outcome."
(before
(clear-vc!)
(check-pred unsat? (query xs)))))))
(eval:alts
(run-test example-tests)
(format-opaque "~a" (run-test example-tests)))
]
All tests in this suite fail when invoked on the
buggy @racket[sum], and they all pass once the bug is fixed.
@subsection[#:tag "sec:errors-under-symbolic-eval"]{Bugs Due to Exceptions in Conditionals}
As we saw above, basic tests can easily uncover problems
caused by exceptions that are raised unconditionally, on all
paths. This is not surprising since such problems are also
easy to discover in concrete code---they correspond to
obvious bugs that cause an immediate crash on every input.
Catching bugs that raise exceptions only on some paths is
trickier, in both concrete and solver-aided code, as our next
example shows.
Consider the following buggy version of @racket[sum]:
@examples[#:eval rosette-eval #:label #f
(define (sum xs)
(cond
[(null? xs) 0]
[(null? (cdr xs)) (car xs)]
[(andmap (curry = (car xs)) (cdr xs))
(* (length xs) (cdr xs))] (code:comment "Bug: cdr should be car.")
[else (apply + xs)]))
]
This version of @racket[sum] implements three simple
optimizations. It returns 0 when given an empty list;
@code{xs[0]} when given a list of length 1; and
@code{|xs| * xs[0]} when given a list of identical elements.
This last optimization is buggy (it uses @racket[cdr] when
it should have used @racket[car]), and any execution path
that goes through it will end with an exception.
Suppose that we want to verify another simple property of
@racket[sum]: if it returns a positive integer, then at least
one element in the argument list must have been positive.
@examples[#:eval rosette-eval #:label #f
(assume (positive? (sum xs)))
(verify
(assert (ormap positive? xs)))]
This query returns @racket[(unsat)], as expected, despite
the bug in @racket[sum]. To see why, recall that
@racket[(verify #, @var{expr})] searches for an input that
violates an assertion in @var{expr}, while satisfying all
the assumptions and assertions accumulated in the
verification condition @racket[(vc)] before the call to
@racket[verify]. So, our query is @racket[unsat?] because
@racket[(ormap positive? xs)] holds whenever
@racket[(sum xs)] successfully computes a positive value.
A basic test suite, adapted from the
@seclink["sec:errors-under-queries"]{previous section}, will
not uncover this bug. If we run the tests against the new
@racket[sum], all the checks pass:
@examples[#:eval rosette-eval #:label #f
(eval:no-prompt
(define (pre xs)
(assume (positive? (sum xs))))
(define (post xs)
(assert (ormap positive? xs)))
(define (query xs)
(pre xs)
(verify (post xs)))
(define example-tests
(test-suite
"An example suite for a sum query."
#:before clear-vc!
#:after clear-vc!
(test-case
"Test sum with concrete values."
(check = (sum '()) 0)
(check = (sum '(-1)) -1)
(check = (sum '(-2 2)) 0)
(check = (sum '(-1 0 3)) 2))
(test-case
"Test query post for exceptions."
(before
(clear-vc!)
(check-not-exn (thunk (pre xs)))))
(test-case
"Test query post for exceptions."
(before
(clear-vc!)
(check-not-exn (thunk (post xs)))))
(test-case
"Test query outcome."
(before
(clear-vc!)
(check-pred unsat? (query xs)))))))
(eval:alts
(run-test example-tests)
(format-opaque "~a" (run-test example-tests)))
]
One way to detect bugs of this kind is to run a "unit
verification query" for each key procedure in the program,
searching for assertion failures where none are expected:
@examples[#:eval rosette-eval #:label #f
(test-case
"Test sum for any failures."
(check-pred unsat? (verify (sum xs))))
]
Another strategy is to avoid issuing any assumptions or
assertions outside of queries:
@examples[#:eval rosette-eval #:label #f
(verify
(begin
(assume (positive? (sum xs)))
(assert (ormap positive? xs))))]
But neither strategy is always possible, or
foolproof, for large programs. So, in addition to testing,
we recommend debugging all important queries with
@tech[#:key "error tracer"]{error tracing}.
@section[#:tag "sec:error-tracer"]{Error Tracer}
To help debug solver-aided code, Rosette provides an
@deftech[#:key "error tracer"]{error tracer} that tracks and
displays all exceptions raised during symbolic evaluation.
Some of these exceptions are due to bugs and some are
intentional, especially in the context of synthesis queries.
It is not possible to automatically distinguish between
these two, so the error tracer leaves that task to the
programmer.
To run the error tracer on a program file @nonterm{prog},
use the @exec{raco} command:
@commandline{raco symtrace @nonterm{prog}}
The error tracer will open a web browser and stream
all exceptions that Rosette intercepted. For instance,
here is the output from the error tracer when running our
last query on the buggy @racket[sum] example from the
@seclink["sec:errors-under-symbolic-eval"]{previous section}:
@examples[#:eval rosette-eval #:label #f #:no-prompt
(assume (positive? (sum xs)))
(verify
(assert (ormap positive? xs)))]
@(image interface.png #:scale 0.5)
The output shows a table of exceptions that Rosette
intercepted; here, there is one only exception, which is caused by our bug,
so there is only one row.
Each row consists of a shorter error message and an error location
(source file, line, and column). All rows can be expanded to show
more details: the full error message, the stack trace,
and the erroring (blamed) expression.
@subsection[#:tag "sec:symtrace:opts"]{Options and Caveats}
By default, the error tracer instruments only code that is
within a module with either @tt{rosette} or @tt{
rosette/safe} as its initial path. This default is inherited
from the symbolic profiler, and it means that only files
beginning with @tt{#lang rosette} or @tt{#lang rosette/safe}
will be instrumented. The shown call stacks and expressions
will not include non-instrumented files. To instrument
@emph{all} code, use the @DFlag{racket} flag described
below.
Similarly, by default, the error tracer instruments only code that
does not belong to installed packages. To instrument
given installed packages, use the @DFlag{pkg} flag described below.
The @exec{raco symtrace @nonterm{prog}} command accepts the following command-line flags:
@itemlist[
@item{@DFlag{module} @nonterm{module-name} --- run the
specified @nonterm{module-name} submodule of @nonterm{prog}
(defaults to the @tt{main} submodule).}
@item{@DFlag{racket} --- instrument code in any language, not
just those derived from Rosette.}
@;{@item{@DFlag{solver} --- do not show exceptions raised on
infeasible paths, using the solver to decide if paths are
feasible. This option can cause significant performance degradation.}}
@item{@DFlag{assert} --- do not show exceptions due to
assertion errors, which are usually expected exceptions.}
@item{@DFlag{pkg} @nonterm{pkg-name} --- instrument code in @nonterm{pkg-name}.}
]
Inside the web browser, the output can be customized further.
@itemlist[
@item{The @bold{Group similar rows} switch will @emph{heuristically}
group similar rows together, enabling easier navigation
when many exceptions originate from the same place and due to the same cause.}
@item{The @bold{Show Racket stacktrace} switch will display the top 32 entries of
the Racket stack trace in addition to the Rosette stack trace.
The Racket stack trace includes the details of evaluating Rosette's internal procedures,
which the Rosette trace omits. These details are usually not necessary for
understanding errors in Rosette code, so the switch is off by default.}
@item{The search box can be used to find rows that include the search string in their error message.}
]
@section{Walkthrough: Tracing Errors in Rosette}
To illustrate a typical error tracing process, consider
verifying the following buggy implementation of the
@link["https://en.wikipedia.org/wiki/Quickselect"]{
quickselect algorithm}.
@examples[#:eval rosette-eval #:label #f #:no-prompt
(define (select xs n)
(cond
[(empty? xs) (assert #f "unexpected empty list")]
[else (define pivot (first xs))
(define non-pivot (rest xs))
(define <pivot (filter (λ (x) (< x pivot)) non-pivot))
(define >=pivot (filter (λ (x) (>= x pivot)) non-pivot))
(define len< (length <pivot))
(cond
[(= n len<) pivot]
[(< n len<) (select <pivot)] (code:comment "Bug: should be (select <pivot n).")
[else (select >=pivot (- n len< 1))])]))
(define-symbolic n k integer?)
(assume
(and (<= 0 n (sub1 (length xs)))
(= k (select xs n))))
(verify
(assert (= k (list-ref (sort xs <) n))))
]
As before, the verification query succeeds despite the bug.
But unlike before, the bug is harder to detect. So we
run the error tracer on it and obtain the following output:
@(image quickselect.png #:scale 0.5)
The output from the error tracer includes 8 exceptions. Four
are arity mismatch exceptions that are due to the bug, and
the rest are benign assertion failures that cannot happen in
our example.
Because benign assertion failures are so common, the error
tracer provides an option to heuristically suppress them
from the output via the
@seclink["sec:symtrace:opts"]{@DFlag{assert}} flag. With the
flag enabled, the output contains only the four arity
mismatch exceptions.
Some assertion failures are bugs, however, so filtering with
@DFlag{assert} can end up hiding true positives and should
be used with this caveat in mind.

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

View File

@ -0,0 +1,24 @@
#lang rosette
(define-symbolic xs integer? #:length 4)
(define-symbolic k integer?)
(define-symbolic n integer?)
(define (select xs n)
(cond
[(empty? xs) (assert #f "unexpected empty list")]
[else (define pivot (first xs))
(define non-pivot (rest xs))
(define <pivot (filter (λ (x) (< x pivot)) non-pivot))
(define >=pivot (filter (λ (x) (>= x pivot)) non-pivot))
(define len< (length <pivot))
(cond
[(= n len<) pivot]
[(< n len<) (select <pivot)]
[else (select >=pivot (- n len< 1))])]))
(assume (and (<= 0 n (sub1 (length xs)))
(= k (select xs n))))
(verify (assert (= k (list-ref (sort xs <) n))))

View File

@ -0,0 +1,15 @@
#lang rosette
(define (sum xs)
(cond
[(null? xs) 0]
[(null? (cdr xs)) (car xs)]
[(andmap (curry = (car xs)) (cdr xs))
(* (length xs) (cdr xs))]
[else (apply + xs)]))
(define-symbolic xs integer? #:length 4)
(assume (positive? (sum xs)))
(verify (assert (ormap positive? xs)))

View File

@ -0,0 +1,96 @@
#lang rosette
(define (sum0 xs) (foldl + xs))
(define (sum1 xs) (foldl + 0 xs))
(define (sum2 xs)
(cond
[(null? xs) 0]
[(null? (cdr xs)) (car xs)]
[(andmap (curry = (car xs)) (cdr xs))
(* (length xs) (cdr xs))]
[else (apply + xs)]))
(define-symbolic xs integer? #:length 4)
(require rackunit)
(define (tests0 sum)
(define (post xs)
(assert (= (sum xs) (sum (filter-not zero? xs)))))
(define (query xs)
(verify (post xs)))
(test-suite
"An example suite for a sum query."
#:before clear-vc!
#:after clear-vc!
(test-case
"Test sum with concrete values."
(check = (sum '()) 0)
(check = (sum '(-1)) -1)
(check = (sum '(-2 2)) 0)
(check = (sum '(-1 0 3)) 2))
(test-case
"Test query post for exceptions."
(before
(clear-vc!)
(check-not-exn (thunk (post xs)))))
(test-case
"Test query outcome."
(before
(clear-vc!)
(check-pred unsat? (query xs))))))
(define (tests1 sum)
(define (pre xs)
(assume (positive? (sum xs))))
(define (post xs)
(assert (ormap positive? xs)))
(define (query xs)
(pre xs)
(verify (post xs)))
(test-suite
"An example suite for a sum query."
#:before clear-vc!
#:after clear-vc!
(test-case
"Test sum with concrete values."
(check = (sum '()) 0)
(check = (sum '(-1)) -1)
(check = (sum '(-2 2)) 0)
(check = (sum '(-1 0 3)) 2))
(test-case
"Test query post for exceptions."
(before
(clear-vc!)
(check-not-exn (thunk (pre xs)))))
(test-case
"Test query post for exceptions."
(before
(clear-vc!)
(check-not-exn (thunk (post xs)))))
(test-case
"Test query outcome."
(before
(clear-vc!)
(check-pred unsat? (query xs))))))
(run-test (tests0 sum0))
(run-test (tests0 sum1))
(run-test (tests0 sum2))
(run-test (tests1 sum2))

View File

@ -0,0 +1,202 @@
#lang rosette
(require (only-in racket/sandbox with-deep-time-limit))
(require rosette/solver/smt/z3)
(require rosette/guide/scribble/util/demo)
(require rosette/lib/synthax)
(provide (all-defined-out))
(define int32? (bitvector 32))
(define (int32 i)
(bv i int32?))
(define (bvmid lo hi)
(bvsdiv (bvadd lo hi) (int32 2)))
(define (bvmid-no-overflow lo hi)
(bvadd lo (bvsdiv (bvsub hi lo) (int32 2))))
(define (check-mid impl lo hi)
(assume (bvsle (int32 0) lo))
(assume (bvsle lo hi))
(define mi (impl lo hi))
(define diff
(bvsub (bvsub hi mi)
(bvsub mi lo)))
(assert (bvsle lo mi))
(assert (bvsle mi hi))
(assert (bvsle (int32 0) diff))
(assert (bvsle diff (int32 1))))
(define-symbolic l h int32?)
(define-grammar (fast-int32 x y)
[expr (choose
x y (?? int32?)
((bop) (expr) (expr))
((uop) (expr)))]
[bop (choose bvadd bvsub bvand bvor bvxor bvshl bvlshr bvashr)]
[uop (choose bvneg bvnot)])
(define (bvmid-fast lo hi)
(fast-int32 lo hi #:depth 2))
(define (bvmid-and? lo hi)
(equal? (fast-int32 l h #:depth 1) (fast-int32 l h #:depth 1)))
(define (check-sqrt impl n)
(assume (bvsle (int32 0) n))
(define √n (impl l))
(define √n+1 (bvadd √n (int32 1)))
(assert (bvule (bvmul √n √n) n))
(assert (bvult n (bvmul √n+1 √n+1))))
(define (check-mid-slow impl lo hi)
(assume (bvsle (int32 0) lo))
(assume (bvsle lo hi))
(assert
(equal?
(bitvector->integer (impl lo hi))
(quotient (+ (bitvector->integer lo) (bitvector->integer hi)) 2))))
(define-demo check-mid-demo
(demo (check-mid bvmid (int32 0) (int32 0)))
(demo (check-mid bvmid (int32 0) (int32 1)))
(demo (check-mid bvmid (int32 0) (int32 2)))
(demo (check-mid bvmid (int32 10) (int32 10000))))
(define-demo verify-demo
(define cex (time (verify (check-mid bvmid l h))))
(demo cex)
(define cl (evaluate l cex))
(define ch (evaluate h cex))
(demo cl)
(demo ch)
(demo (bvmid cl ch))
(demo (check-mid bvmid cl ch))
(demo (verify (check-mid bvmid-no-overflow l h))))
(define-demo synthesize-demo
(define sol
(time
(synthesize
#:forall (list l h)
#:guarantee (check-mid bvmid-fast l h))))
(demo (dict-count (model sol)))
(demo sol)
(demo (print-forms sol)))
(define-demo solve-demo
(define (bvmid-fast lo hi)
(bvlshr (bvadd hi lo) (bv #x00000001 32)))
(demo
(print-forms
(time
(synthesize
#:forall (list l h)
#:guarantee
(begin
(assume (not (equal? l h)))
(assume (bvsle (int32 0) l))
(assume (bvsle l h))
(assert
(<=> (bvmid-and? l h)
(equal? (bvand l h) (bvmid-fast l h))))))))))
(define-demo slowdown-demo
(demo (time (verify (check-mid bvmid l h))))
(demo (time (verify (check-mid-slow bvmid l h))))
(demo (time (verify (check-mid bvmid-no-overflow l h))))
(demo (with-deep-time-limit 10 (verify (check-mid-slow bvmid-no-overflow l h)))))
(define-demo current-bitwidth-64-demo
(parameterize ([current-bitwidth 64])
(demo (time (verify (check-mid-slow bvmid l h))))
(demo (time (verify (check-mid-slow bvmid-no-overflow l h))))))
(define-demo current-bitwidth-32-demo
(parameterize ([current-bitwidth 32])
(demo (time (verify (check-mid-slow bvmid l h))))
(demo (time (verify (check-mid-slow bvmid-no-overflow l h))))))
(define-demo solver-options-demo
(demo (current-solver (z3 #:logic 'QF_BV)))
(demo (time (verify (check-mid bvmid l h))))
(demo (time (verify (check-mid-slow bvmid l h))))
(current-solver (z3)))
(define-demo infinite-loop-demo
(define (bvsqrt n)
(cond
[(bvult n (int32 2)) n]
[else
(define s0 (bvshl (bvsqrt (bvlshr n (int32 2))) (int32 1)))
(define s1 (bvadd s0 (int32 1)))
(if (bvugt (bvmul s1 s1) n) s0 s1)]))
(demo (bvsqrt (int32 3)))
(demo (bvsqrt (int32 4)))
(demo (bvsqrt (int32 15)))
(demo (bvsqrt (int32 16)))
(demo (with-terms
(with-deep-time-limit 10 (bvsqrt l)))))
(define-demo sound-finitization-demo
(define fuel (make-parameter 5))
(define-syntax-rule
(define-bounded (id param ...) body ...)
(define (id param ...)
(assert (> (fuel) 0) "Out of fuel") ; <--- no false negatives
(parameterize ([fuel (sub1 (fuel))])
body ...)))
(define-bounded (bvsqrt n)
(cond
[(bvult n (int32 2)) n]
[else
(define s0 (bvshl (bvsqrt (bvlshr n (int32 2))) (int32 1)))
(define s1 (bvadd s0 (int32 1)))
(if (bvugt (bvmul s1 s1) n) s0 s1)]))
(demo (time (verify (check-sqrt bvsqrt l))))
(demo (fuel 16))
(demo (time (verify (check-sqrt bvsqrt l)))))
(define-demo complete-finitization-demo
(define fuel (make-parameter 5))
(define-syntax-rule
(define-bounded (id param ...) body ...)
(define (id param ...)
(assume (> (fuel) 0) "Out of fuel") ; <--- no false positives
(parameterize ([fuel (sub1 (fuel))])
body ...)))
(define-bounded (bvsqrt n)
(cond
[(bvult n (int32 2)) n]
[else
(define s0 (bvshl (bvsqrt (bvlshr n (int32 2))) (int32 1)))
(define s1 (bvadd s0 (int32 1)))
(if (bvugt (bvmul s1 s1) n) s0 s1)]))
(demo (time (verify (check-sqrt bvsqrt l))))
(demo (fuel 16))
(demo (time (verify (check-sqrt bvsqrt l)))))
(module+ main
(check-mid-demo)
(verify-demo)
(synthesize-demo)
(solve-demo)
(slowdown-demo)
(current-bitwidth-64-demo)
(current-bitwidth-32-demo)
(solver-options-demo)
(infinite-loop-demo)
(sound-finitization-demo)
(complete-finitization-demo))

View File

@ -0,0 +1,787 @@
;; This file was created by make-log-based-eval
((define-symbolic b boolean?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
(b
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "b\n"))))
#""
#"")
((boolean? b) ((3) 0 () 0 () () (q values #t)) #"" #"")
((integer? b) ((3) 0 () 0 () () (q values #f)) #"" #"")
((vector b 1)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vector b 1)\n"))))
#""
#"")
((not b)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(! b)\n"))))
#""
#"")
((boolean? (not b)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((define-symbolic* n integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (static) (define-symbolic x boolean?) x)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (dynamic) (define-symbolic* y integer?) y)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((eq? (static) (static)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((eq? (dynamic) (dynamic))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(= y$1 y$2)\n"))))
#""
#"")
((define (yet-another-x) (define-symbolic x boolean?) x)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((eq? (static) (yet-another-x))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(<=> x x)\n"))))
#""
#"")
((assert #t) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assert #f) ((3) 0 () 0 () () (q exn "[assert] failed")) #"" #"")
((vc-asserts (vc)) ((3) 0 () 0 () () (q values #f)) #"" #"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vc-asserts (vc)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((assert (not b)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vc-asserts (vc))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(! b)\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assume #t) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vc-assumes (vc)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((assume #f) ((3) 0 () 0 () () (q exn "[assume] failed")) #"" #"")
((vc-assumes (vc)) ((3) 0 () 0 () () (q values #f)) #"" #"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-symbolic i j integer?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((assume (> j 0)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vc-assumes (vc))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(< 0 j)\n"))))
#""
#"")
((assert (< (- i j) i)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((vc-asserts (vc))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(|| (! (< 0 j)) (< (+ i (- j)) i))\n"))))
#""
#"")
((vc)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(vc (< 0 j) (|| (! (< 0 j)) (< (+ i (- j)) i)))\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define int32? (bitvector 32)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (int32 i) (bv i int32?))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((int32? 1) ((3) 0 () 0 () () (q values #f)) #"" #"")
((int32? (int32 1)) ((3) 0 () 0 () () (q values #t)) #"" #"")
((int32 1)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x00000001 32)\n"))))
#""
#"")
((define (bvmid lo hi) (bvsdiv (bvadd lo hi) (int32 2)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (check-mid impl lo hi)
(assume (bvsle (int32 0) lo))
(assume (bvsle lo hi))
(define mi (impl lo hi))
(define diff (bvsub (bvsub hi mi) (bvsub mi lo)))
(assert (bvsle lo mi))
(assert (bvsle mi hi))
(assert (bvsle (int32 0) diff))
(assert (bvsle diff (int32 1))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((check-mid bvmid (int32 0) (int32 0))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((check-mid bvmid (int32 0) (int32 1))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((check-mid bvmid (int32 0) (int32 2))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((check-mid bvmid (int32 10) (int32 10000))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define-symbolic l h int32?) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define cex (verify (check-mid bvmid l h)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(cex
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [l (bv #x394f0402 32)]\n [h (bv #x529e7c00 32)])\n"))))
#""
#"")
((define cl (evaluate l cex)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define ch (evaluate h cex)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((list cl ch)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(list (bv #x394f0402 32) (bv #x529e7c00 32))\n"))))
#""
#"")
((define il (bitvector->integer cl))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define ih (bitvector->integer ch))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((list il ih)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "'(961479682 1386118144)\n"))))
#""
#"")
((define m (bvmid cl ch)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
(m
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #xc5f6c001 32)\n"))))
#""
#"")
((bitvector->integer m) ((3) 0 () 0 () () (q values -973684735)) #"" #"")
((quotient (+ il ih) 2) ((3) 0 () 0 () () (q values 1173798913)) #"" #"")
((int32 (quotient (+ il ih) 2))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x45f6c001 32)\n"))))
#""
#"")
((check-mid bvmid cl ch) ((3) 0 () 0 () () (q exn "[assert] failed")) #"" #"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((bvadd cl ch)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x8bed8002 32)\n"))))
#""
#"")
((bitvector->integer (bvadd cl ch))
((3) 0 () 0 () () (q values -1947369470))
#""
#"")
((+ il ih) ((3) 0 () 0 () () (q values 2347597826)) #"" #"")
((- (expt 2 31) 1) ((3) 0 () 0 () () (q values 2147483647)) #"" #"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (bvmid-no-overflow lo hi) (bvadd lo (bvsdiv (bvsub hi lo) (int32 2))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((verify (check-mid bvmid-no-overflow l h))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#""
#"")
((require rosette/lib/synthax) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define-grammar
(fast-int32 x y)
(expr (choose x y (?? int32?) ((bop) (expr) (expr)) ((uop) (expr))))
(bop (choose bvadd bvsub bvand bvor bvxor bvshl bvlshr bvashr))
(uop (choose bvneg bvnot)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((require (only-in rosette/guide/scribble/essentials/bvmid bvmid-fast))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((require (only-in rosette/guide/scribble/util/demo print-forms-alt))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define sol
(synthesize #:forall (list l h) #:guarantee (check-mid bvmid-fast l h)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(sol
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(model\n [0$choose:bvmid:37:9$expr:bvmid:37:3$fast-int32:bvmid:45:3 #f]\n [1$choose:bvmid:37:9$expr:bvmid:37:3$fast-int32:bvmid:45:3 #f]\n [2$choose:bvmid:37:9$expr:bvmid:37:3$fast-int32:bvmid:45:3 #f]\n [3$choose:bvmid:37:9$expr:bvmid:37:3$fast-int32:bvmid:45:3 #t]\n ...)\n\n"))))
#""
#"")
((print-forms-alt sol)
((3) 0 () 0 () () (c values c (void)))
#"(define (bvmid-fast lo hi) (bvlshr (bvadd hi lo) (bv #x00000001 32)))\n"
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (bvmid-fast lo hi) (bvlshr (bvadd hi lo) (bv 1 32)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define sol
(solve
(begin
(assume (not (equal? l h)))
(assume (bvsle (int32 0) l))
(assume (bvsle l h))
(assert (equal? (bvand l h) (bvmid-fast l h))))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(sol
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [l (bv #x3f761e94 32)]\n [h (bv #x3f761e95 32)])\n"))))
#""
#"")
((evaluate (bvand l h) sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x3f761e94 32)\n"))))
#""
#"")
((evaluate (bvmid-fast l h) sol)
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x3f761e94 32)\n"))))
#""
#"")
((define (bvmid-and? lo hi) #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((void) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (check-mid-slow impl lo hi)
(assume (bvsle (int32 0) lo))
(assume (bvsle lo hi))
(assert
(equal?
(bitvector->integer (impl lo hi))
(quotient (+ (bitvector->integer lo) (bitvector->integer hi)) 2))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((require (only-in racket error))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((time (verify (check-mid bvmid l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [l (bv #x394f0402 32)]\n [h (bv #x529e7c00 32)])\n"))))
#"cpu time: 0 real time: 38 gc time: 0\n"
#"")
((time (verify (check-mid-slow bvmid l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [l (bv #x2faef9a1 32)]\n [h (bv #x5eefb8dd 32)])\n"))))
#"cpu time: 1 real time: 172 gc time: 0\n"
#"")
((time (verify (check-mid bvmid-no-overflow l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#"cpu time: 0 real time: 160 gc time: 0\n"
#"")
((error 'call-with-deep-time-limit "out of time")
((3) 0 () 0 () () (q exn "call-with-deep-time-limit: out of time"))
#""
#"")
((current-bitwidth) ((3) 0 () 0 () () (q values #f)) #"" #"")
((current-bitwidth 64) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((time (verify (check-mid-slow bvmid l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [l (bv #x00000001 32)]\n [h (bv #x7fffffff 32)])\n"))))
#"cpu time: 0 real time: 23 gc time: 0\n"
#"")
((time (verify (check-mid-slow bvmid-no-overflow l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#"cpu time: 0 real time: 159 gc time: 0\n"
#"")
((current-bitwidth 32) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((time (verify (check-mid-slow bvmid l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#"cpu time: 0 real time: 0 gc time: 0\n"
#"")
((time (verify (check-mid-slow bvmid-no-overflow l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [l (bv #x71979fa3 32)]\n [h (bv #x76b91b88 32)])\n"))))
#"cpu time: 1 real time: 152 gc time: 0\n"
#"")
((current-bitwidth 512) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((time (verify (check-mid-slow bvmid l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [l (bv #x70000006 32)]\n [h (bv #x73fffffa 32)])\n"))))
#"cpu time: 1 real time: 385 gc time: 0\n"
#"")
((time (verify (check-mid-slow bvmid-no-overflow l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#"cpu time: 0 real time: 430 gc time: 0\n"
#"")
((current-bitwidth #f) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((require rosette/solver/smt/z3)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((current-solver (z3 #:logic 'QF_BV))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((time (verify (check-mid bvmid l h)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(model\n [l (bv #x394f0402 32)]\n [h (bv #x529e7c00 32)])\n"))))
#"cpu time: 3 real time: 33 gc time: 0\n"
#"")
((time (verify (check-mid-slow bvmid l h)))
((3)
0
()
0
()
()
(q
exn
"read-solution: unrecognized solver output: (error line 68 column 19: Invalid function definition: unknown sort 'Int')"))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((current-solver (z3)) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define (bvsqrt n)
(cond
((bvult n (int32 2)) n)
(else
(define s0 (bvshl (bvsqrt (bvlshr n (int32 2))) (int32 1)))
(define s1 (bvadd s0 (int32 1)))
(if (bvugt (bvmul s1 s1) n) s0 s1))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((bvsqrt (int32 3))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x00000001 32)\n"))))
#""
#"")
((bvsqrt (int32 4))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x00000002 32)\n"))))
#""
#"")
((bvsqrt (int32 15))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x00000003 32)\n"))))
#""
#"")
((bvsqrt (int32 16))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bv #x00000004 32)\n"))))
#""
#"")
((error 'call-with-deep-time-limit "out of time")
((3) 0 () 0 () () (q exn "call-with-deep-time-limit: out of time"))
#""
#"")
((define n0 l) ((3) 0 () 0 () () (c values c (void))) #"" #"")
(n0
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "l\n"))))
#""
#"")
((define n1 (bvlshr n0 (int32 2)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(n1
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(bvlshr l (bv #x00000002 32))\n"))))
#""
#"")
((define n2 (bvlshr n1 (int32 2)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(n2
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0 (u . "(bvlshr (bvlshr l (bv #x00000002 32)) (bv #x00000002 32))\n"))))
#""
#"")
((define n3 (bvlshr n2 (int32 2)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
(n3
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c
values
c
(0
(u
.
"(bvlshr\n (bvlshr (bvlshr l (bv #x00000002 32)) (bv #x00000002 32))\n (bv #x00000002 32))\n\n"))))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((require (only-in racket make-parameter parameterize))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define fuel (make-parameter 5))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define-syntax-rule
(define-bounded (id param ...) body ...)
(define (id param ...)
(assert (> (fuel) 0) "Out of fuel.")
(parameterize ((fuel (sub1 (fuel)))) body ...)))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define-bounded
(bvsqrt n)
(cond
((bvult n (int32 2)) n)
(else
(define s0 (bvshl (bvsqrt (bvlshr n (int32 2))) (int32 1)))
(define s1 (bvadd s0 (int32 1)))
(if (bvugt (bvmul s1 s1) n) s0 s1))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define (check-sqrt impl n)
(assume (bvsle (int32 0) n))
(define √n (impl l))
(define √n+1 (bvadd √n (int32 1)))
(assert (bvule (bvmul √n √n) n))
(assert (bvult n (bvmul √n+1 √n+1))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define cex (time (verify (check-sqrt bvsqrt l))))
((3) 0 () 0 () () (c values c (void)))
#"cpu time: 4 real time: 1143 gc time: 0\n"
#"")
((bvsqrt (evaluate l cex))
((3) 0 () 0 () () (q exn "[assert] Out of fuel."))
#""
#"")
((clear-vc!) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((fuel 16) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((time (verify (check-sqrt bvsqrt l)))
((3)
1
(((lib "rosette/guide/scribble/util/lifted.rkt")
.
deserialize-info:opaque-v0))
0
()
()
(c values c (0 (u . "(unsat)\n"))))
#"cpu time: 4 real time: 67938 gc time: 0\n"
#"")

View File

@ -0,0 +1,537 @@
#lang scribble/manual
@(require (for-label
racket
(only-in racket/sandbox with-deep-time-limit)
rosette/base/form/define
rosette/query/query
rosette/solver/solution
(only-in rosette/base/base
assert assume vc vc-asserts vc-assumes clear-vc!
bv? bitvector
bvsdiv bvadd bvsle bvsub bvand
bvor bvxor bvshl bvlshr bvashr
bvnot bvneg)
rosette/lib/synthax))
@(require racket/sandbox racket/runtime-path scribble/core scribble/racket
scribble/example scribble/html-properties scriblib/footnote)
@(require (only-in "../refs.scrbl" ~cite rosette:onward13 rosette:pldi14)
"../util/lifted.rkt")
@(define-runtime-path root ".")
@(define rosette-eval (rosette-log-evaluator (logfile root "essentials-log")))
@(define (symbolic s) @racketresultfont[s])
@(define-footnote footnote footnote-part)
@title[#:tag "ch:essentials"]{Rosette Essentials}
Rosette adds to Racket a collection of solver-aided facilities.
These facilities enable programmers to conveniently access a constraint solver
that can answer interesting questions about program behaviors. They are based on four
key concepts: @emph{symbolic values}, @emph{assertions}, @emph{assumptions}, and @emph{queries}.
We use assertions and assumptions to express desired program behaviors and symbolic values to
formulate queries about these behaviors.
This chapter illustrates the basics of solver-aided programming.
More advanced tutorials, featuring extended examples, can be found
in Section 2 of @~cite[rosette:onward13 rosette:pldi14].@footnote{Code examples in
these references are written in earlier versions of Rosette.
While Rosette 4 is not backward compatible with these versions,
they share the same conceptual core.}
The following chapters describe the subset
of Racket that can be @seclink["sec:langs"]{safely} used with solver-aided facilities, including the
supported datatypes (both @seclink["ch:built-in-datatypes"]{built-in}
and @seclink["ch:programmer-defined-datatypes"]{programmer-defined}),
@seclink["ch:syntactic-forms"]{syntactic forms}, and @seclink["ch:libraries"]{libraries}.
@section[#:tag "sec:symbolic-values"]{Symbolic Values}
The Rosette language includes two kinds of values: concrete and symbolic. Concrete values are plain Racket values (@racket[#t], @racket[#f], @racket[0], @racket[1], etc.), and Rosette programs that operate only on concrete values behave like Racket programs. Accessing the solver-aided features of Rosette---such as code synthesis or verification---requires the use of symbolic values.
@deftech[#:key "symbolic constant"]{Symbolic constants} are the simplest kind of symbolic value. They can be created using the @racket[define-symbolic] form:
@examples[#:eval rosette-eval #:label #f
(define-symbolic b boolean?)
b]
This generates a fresh symbolic constant of type boolean and binds it to the variable @racket[b].
You can think of a symbolic constant as a placeholder for a concrete constant of the same type. As we will see shortly, the solver, once called, determines which concrete value a given symbolic constant represents: it will tell us whether the constant @symbolic{b} is @racket[#t] or @racket[#f], depending on what question we ask about the behavior of a program (or a procedure) applied to @symbolic{b}.
Symbolic values, including constants, can be used just like concrete values of the same type. They can be stored in data structures or passed to procedures to obtain other values, either concrete or symbolic:
@examples[#:eval rosette-eval #:label #f
(boolean? b)
(integer? b)
(vector b 1)
(not b)
(boolean? (not b))]
In our example, all but the fourth expression produce concrete values. The fourth expression returns another symbolic value---specifically, a symbolic @emph{expression} of type boolean. This expression represents the negation of @symbolic{b}. If the solver determines that @symbolic{b} is @racket[#t], for example, then @symbolic{(! b)} will be interpreted as @racket[#f].
Rosette provides one more construct for creating symbolic constants besides @racket[define-symbolic]:
@examples[#:eval rosette-eval #:label #f #:no-prompt
(define-symbolic* n integer?)]
The two constructs differ in how they bind variables to constants when evaluated more than once.
The @racket[define-symbolic] form binds the variable to the same (unique) constant every time it is evaluated. The @racket[define-symbolic*] form, in contrast, creates a stream of (unique) constants, binding the variable to the next constant from its stream whenever the form is evaluated. The following example illustrates the difference:
@examples[#:eval rosette-eval #:label #f
(eval:no-prompt
(define (static)
(define-symbolic x boolean?) (code:comment "Creates the same constant when evaluated.")
x))
(eval:no-prompt
(define (dynamic)
(define-symbolic* y integer?) (code:comment "Creates a fresh constant when evaluated.")
y))
(eq? (static) (static))
(eq? (dynamic) (dynamic))]
Printed constant names, such as @symbolic{x} or @symbolic{b}, are just comments. Two constants created by evaluating two distinct @racket[define-symbolic] (or, @racket[define-symbolic*]) forms are distinct, even if they have the same printed name. They may still represent the same concrete value, but that is determined by the solver:
@examples[#:eval rosette-eval #:label #f
(eval:no-prompt
(define (yet-another-x)
(define-symbolic x boolean?)
x))
(eq? (static) (yet-another-x))]
@section[#:tag "sec:asserts"]{Assertions and Assumptions}
Like many languages, Rosette provides a construct for expressing @emph{assertions}---important properties of programs that are checked in every execution. Rosette assertions work just like Java or Racket assertions when given a concrete value: if the value is false, the execution terminates with a runtime exception. Otherwise, the execution proceeds normally.
@examples[#:eval rosette-eval #:label #f
(assert #t)
(eval:error (assert #f))]
When given a symbolic boolean value, however, a Rosette assertion has no immediate effect. Instead, the value is accumulated in the current @tech{verification condition} (VC), and the assertion's effect (whether it passes or fails) is eventually determined by the solver.
@examples[#:eval rosette-eval #:label #f
(code:line (vc-asserts (vc)) (code:comment "We asserted #f above, so the current VC reflects that."))
(code:line (clear-vc!) (code:comment "Clear the current VC."))
(vc-asserts (vc))
(code:line (assert (not b)) (code:comment "Add the assertion (not b) to the VC."))
(vc-asserts (vc))
(clear-vc!)]
Assertions express properties that a program must satisfy on all @emph{legal} inputs. In Rosette, as in other solver-aided frameworks, we use @emph{assumptions} to describe which inputs are legal. If a program violates an assertion on a legal input, we blame the program. But if it violates an assertion on an illegal input, we blame the caller. In other words, a program is considered incorrect only when it violates an assertion on a legal input.
Assumptions behave analogously to assertions on both concrete and symbolic values. In the concrete case, assuming @racket[#f] aborts the execution with a runtime exception, and assuming a true value is equivalent to calling @racket[(void)]. In the symbolic case, the assumed value is accumulated in the current VC.
@examples[#:eval rosette-eval #:label #f
(assume #t)
(vc-assumes (vc))
(eval:alts
(code:line (assume #f) (code:comment "Assuming #f aborts the execution with an exception."))
(eval:error (assume #f)))
(vc-assumes (vc))
(clear-vc!)
(define-symbolic i j integer?)
(code:line (assume (> j 0)) (code:comment "Add the assumption (> j 0) to the VC."))
(vc-assumes (vc))
(assert (< (- i j) i))
(code:line (vc-asserts (vc)) (code:comment "The assertions must hold when the assumptions hold."))
(code:line (vc) (code:comment "VC tracks the assumptions and the assertions."))]
@(rosette-eval '(clear-vc!))
@section[#:tag "sec:queries"]{Solver-Aided Queries}
The solver reasons about assumed and asserted properties only when we ask a question about them---for example, ``Does my program have an execution that violates an assertion while satisfying all the assumptions?'' We pose such @emph{solver-aided queries} with the help of constructs explained in the remainder of this chapter.
We will illustrate the queries on the following toy example. Suppose that we want to implement a
procedure @racket[bvmid] that takes as input two non-negative 32-bit integers, @racket[lo] ≤ @racket[hi],
and returns the midpoint of the interval [@racket[lo], @racket[hi]]. In C or Java, we would declare
the inputs and output of @racket[bvmid] to be of type ``int''. In Rosette, we model finite precision
(i.e., machine) integers as @seclink["sec:bitvectors"]{bitvectors} of length 32.
@examples[#:eval rosette-eval #:label #f #:no-prompt
(code:comment "int32? is a shorthand for the type (bitvector 32).")
(define int32? (bitvector 32))]
@examples[#:eval rosette-eval #:label #f #:no-prompt
(code:comment "int32 takes as input an integer literal and returns")
(code:comment "the corresponding 32-bit bitvector value.")
(define (int32 i)
(bv i int32?))]
@examples[#:eval rosette-eval #:label #f
(code:line (int32? 1) (code:comment "1 is not a 32-bit integer"))
(code:line (int32? (int32 1)) (code:comment "but (int32 1) is."))
(int32 1)]
Bitvectors support the usual operations on machine integers, and we can use them to implement @racket[bvmid] as follows:
@examples[#:eval rosette-eval #:label #f #:no-prompt
(code:comment "Returns the midpoint of the interval [lo, hi].")
(define (bvmid lo hi) (code:comment "(lo + hi) / 2")
(bvsdiv (bvadd lo hi) (int32 2)))]
As the above implementation suggests, we intend the midpoint to be the mathematical
integer @tt{mi = (lo + hi) / 2}, where @tt{/} stands for integer division. Assuming
that @tt{0 ≤ lo ≤ hi}, the midpoint @tt{mi} is fully characterized by two properties:
(1) @tt{lo ≤ mi ≤ hi} and (2) @tt{0 ≤ (hi - mi) - (mi - lo) ≤ 1}. We can use these
properties to define a generic correctness specification for any implementation of
@racket[bvmid] as follows:
@examples[#:eval rosette-eval #:label #f #:no-prompt
(code:line
(define (check-mid impl lo hi) (code:comment "Assuming that")
(assume (bvsle (int32 0) lo)) (code:comment "0 ≤ lo and")
(assume (bvsle lo hi)) (code:comment "lo ≤ hi,")
(define mi (impl lo hi)) (code:comment "and letting mi = impl(lo, hi) and")
(define diff (code:comment "diff = (hi - mi) - (mi - lo),")
(bvsub (bvsub hi mi)
(bvsub mi lo))) (code:comment "we require that")
(assert (bvsle lo mi)) (code:comment "lo ≤ mi,")
(assert (bvsle mi hi)) (code:comment "mi ≤ hi,")
(assert (bvsle (int32 0) diff)) (code:comment "0 ≤ diff, and")
(assert (bvsle diff (int32 1)))) (code:comment "diff ≤ 1."))]
This is not the only way to specify the behavior of @racket[bvmid], and we will see an
alternative specification later on. In general, there are many ways to describe what it
means for a program to be correct, and often, these descriptions are partial:
they constrain some aspects of the implementation (e.g., the output is positive)
without fully defining its behavior. In our example, @racket[check-mid] is a
@emph{full functional correctness specification} in that it admits exactly one output value for @racket[(impl lo hi)], namely, @tt{(lo + hi) / 2}.
Testing @racket[bvmid] against its specification on a few concrete legal inputs, we find
that it triggers no assertion failures, as expected:
@examples[#:eval rosette-eval #:label #f
(check-mid bvmid (int32 0) (int32 0))
(check-mid bvmid (int32 0) (int32 1))
(check-mid bvmid (int32 0) (int32 2))
(check-mid bvmid (int32 10) (int32 10000)) ]
But does it work correctly on @emph{all} legal inputs? The answer, as we will see below, is ``no''.
In fact, @racket[bvmid] reproduces @hyperlink["https://en.wikipedia.org/wiki/Binary_search_algorithm#Implementation_issues"]{a famous bug}
that lurked for years in widely used C and Java implementations of binary search.
@subsection[#:tag "sec:verify"]{Verification}
How can we check if @racket[bvmid] satisfies its specification on all legal inputs? One approach is to enumerate all pairs of 32-bit integers with @racket[0 ≤ lo ≤ hi] and apply @racket[(check-mid bvmid hi lo)] to each. This approach is sound (it is guaranteed to find a bug if one exists), but a quick calculation shows that it is impractical even for our toy example: @racket[bvmid] has roughly 2.3 × 10@superscript{18} legal inputs. A better approach is to delegate such checks to a constraint solver, which can search large input spaces much more effectively than naive enumeration. In Rosette, this is done with the help of the @racket[verify] query:
@examples[#:eval rosette-eval #:label #f
(eval:no-prompt (define-symbolic l h int32?))
(define cex (verify (check-mid bvmid l h)))
cex]
The @racket[(verify #, @var[expr])] form queries the solver for a @deftech{binding} from symbolic constants to concrete values that causes the evaluation of @var[expr] to violate an assertion, while satisfying all the assumptions, when the bound symbolic constants are replaced with the corresponding concrete values. If such a binding exists, as it does in our case, it is called a @emph{counterexample}.
Bindings are first-class values in Rosette, and they can be freely manipulated by programs. We can also interpret any Rosette value with respect to a binding using the built-in @racket[evaluate] procedure:
@examples[#:eval rosette-eval #:label #f
(define cl (evaluate l cex))
(define ch (evaluate h cex))
(list cl ch)
(code:comment "We can convert these values to integer? constants for debugging:")
(define il (bitvector->integer cl))
(define ih (bitvector->integer ch))
(list il ih)
(code:comment "Here is the computed midpoint:")
(define m (bvmid cl ch))
m
(bitvector->integer m)
(code:comment "This is clearly wrong. We expect (il + ih) / 2 instead:")
(quotient (+ il ih) 2)
(code:comment "Expressed as a 32-bit integer, the correct answer is:")
(int32 (quotient (+ il ih) 2))
(code:comment "So, check-mid fails on (bvmid cl ch):")
(eval:error (check-mid bvmid cl ch))]
@(rosette-eval '(clear-vc!))
In our example, evaluating @racket[l] and @racket[h] with respect to @racket[cex] reveals that @racket[bvmid] fails to return the correct midpoint value, thus causing the first assertion in the @racket[check-mid] procedure to fail. The bug is due to overflow:
the expression @racket[(bvadd lo hi)] in @racket[bvmid] produces a negative value in
the 32-bit representation when the sum of
@racket[lo] and @racket[hi] exceeds 2@\superscript{31}-1.
@examples[#:eval rosette-eval #:label #f
(bvadd cl ch)
(bitvector->integer (bvadd cl ch))
(+ il ih)
(- (expt 2 31) 1)]
@(rosette-eval '(clear-vc!))
A common @hyperlink["https://en.wikipedia.org/wiki/Binary_search_algorithm#Implementation_issues"]{solution}
to this problem is to calculate the midpoint as @tt{lo + ((hi - lo) / 2)}. It is easy to see that all intermediate values in this calculation are at most @racket[hi] when @racket[lo] and @racket[hi] are both non-negative, so no overflow can happen. We can also verify this with Rosette:
@examples[#:eval rosette-eval #:label #f
(eval:no-prompt
(define (bvmid-no-overflow lo hi)
(bvadd lo (bvsdiv (bvsub hi lo) (int32 2)))))
(verify (check-mid bvmid-no-overflow l h))]
@subsection[#:tag "sec:synthesize"]{Synthesis}
The solution given in @racket[bvmid-no-overflow] avoids the overflow problem in @racket[bvmid] at the cost of performing an additional arithmetic operation. Both implementations also rely on signed division, which is slow and expensive compared to addition, subtraction, and bitwise operations. So our ideal implementation would be correct, small, and composed of only cheap arithmetic and bitwise operations. Does such an implementation exist? To find out, we turn to Rosette's @racket[synthesize] query.
The synthesis query uses the solver to search for a correct program in a space of candidate implementations defined by a syntactic @deftech{sketch}. A sketch is a program with @deftech[#:key "hole"]{holes}, which the solver fills with expressions drawn from a specified set of options. For example, @racket[(?? int32?)] stands for a hole that can be filled with any 32-bit integer constant, so the sketch @racket[(bvadd x (?? int32?))] represents all 2@superscript{32} programs that add a 32-bit constant to the variable @racket[x]. Rosette also lets you define richer holes that can be filled with expressions from a given grammar. For example, here is a grammar of all @racket[int32?] expressions that consist of cheap arithmetic and bitwise operations:
@examples[#:eval rosette-eval #:label #f #:no-prompt
(code:line
(require rosette/lib/synthax) (code:comment "Require the sketching library."))
(code:line
(define-grammar (fast-int32 x y) (code:comment "Grammar of int32 expressions over two inputs:")
[expr
(choose x y (?? int32?) (code:comment "<expr> := x | y | <32-bit integer constant> |")
((bop) (expr) (expr)) (code:comment " (<bop> <expr> <expr>) |")
((uop) (expr)))] (code:comment " (<uop> <expr>)")
[bop
(choose bvadd bvsub bvand (code:comment "<bop> := bvadd | bvsub | bvand |")
bvor bvxor bvshl (code:comment " bvor | bvxor | bvshl |")
bvlshr bvashr)] (code:comment " bvlshr | bvashr")
[uop
(choose bvneg bvnot)]) (code:comment "<uop> := bvneg | bvnot"))]
Using this grammar, we can sketch a fast implementation of the midpoint calculation as follows:
@examples[#:eval rosette-eval #:label #f #:no-prompt
(eval:alts
(define (bvmid-fast lo hi)
(fast-int32 lo hi #:depth 2))
(require (only-in rosette/guide/scribble/essentials/bvmid bvmid-fast)))]
The above sketch describes the space of all expressions from the @racket[fast-int32] grammar that have parse trees of depth at most 2. The depth argument is optional. If ommitted, Rosette will use the value of the @racket[(current-grammar-depth)] parameter to bound the depth of the expressions drawn from the grammar.
At this point, it is worth noting that holes and sketches are not fundamental concepts in Rosette. Instead, they are macros defined in terms of the core constructs we have already seen, symbolic constants and assertions. For example, @racket[(?? int32?)] is syntactic sugar for @racket[(let () (define-symbolic #, @var[id] int32?) #, @var[id])], where @var[id] is an internally generated name. Similarly, @racket[(choose bvneg bvnot)] expands to @racket[(if (?? boolean?) bvneg bvnot)]. Finally, a grammar hole such as @racket[(fast-int32 lo hi #:depth 2)] inlines its productions @racket[#:depth] times to create a nested expression of the form @racket[(choose lo hi (?? int32?) ((choose bvadd ...) (choose ...) (choose ...)) ((choose bvneg bvnot) (choose ...)))]. Assigning concrete values to the symbolic constants generated by this expression has the effect of selecting a parse tree (of depth 2 in our example) from the hole's grammar. So, completing a sketch is a matter of finding a suitable binding for the symbolic constants generated by the holes.
With this in mind, we can query the solver for a completion of the @racket[bvmid-fast] sketch (if any) that satisfies our correctness specification:
@(rosette-eval '(require (only-in rosette/guide/scribble/util/demo print-forms-alt)))
@examples[#:eval rosette-eval #:label #f
(code:comment "Save the above definitions to a file before calling print-forms.")
(define sol
(synthesize
#:forall (list l h)
#:guarantee (check-mid bvmid-fast l h)))
sol
(eval:alts
(print-forms sol)
(print-forms-alt sol))]
The synthesis query takes the form @racket[(synthesize #:forall #, @var[input] #:guarantee #, @var[expr])], where @var[input] lists the symbolic constants that represent inputs to a sketched program, and @var[expr] gives the correctness specification for the sketch. The solver searches for a binding from the hole (i.e., non-@var[input]) constants to values such that @var[expr] satisfies its assertions on all legal @var[input]s. Passing this binding to @racket[print-forms] converts it to a syntactic representation of the completed sketch.@footnote{@racket[print-forms] works only on sketches that have been saved to disk.} In our example, the synthesized program implements the midpoint calculation using the logical shift operation, i.e., the midpoint between @racket[lo] and @racket[hi] is calculated as @tt{(lo + hi) >>@subscript{u} 1}.
@(rosette-eval '(clear-vc!))
@subsection[#:tag "sec:solve"]{Angelic Execution}
Rosette supports one more solver-aided query, which we call angelic execution. This query is the dual of verification. Given a program with symbolic values, it instructs the solver to find a binding for them that will cause the program to execute normally---that is, without any assumption or assertion failures.
Angelic execution can be used to solve puzzles, to run incomplete code, or to ``invert'' a program, by searching for inputs that produce a desired output. For example, we can ask the solver to search for two distinct legal inputs, @racket[l] and @racket[h], whose midpoint is the bitwise-and of their bits:
@examples[#:eval rosette-eval #:label #f
(define (bvmid-fast lo hi)
(bvlshr (bvadd hi lo) (bv #x00000001 32)))
(define sol
(solve
(begin
(assume (not (equal? l h)))
(assume (bvsle (int32 0) l))
(assume (bvsle l h))
(assert (equal? (bvand l h) (bvmid-fast l h))))))
sol
(evaluate (bvand l h) sol)
(evaluate (bvmid-fast l h) sol)]
As a fun exercise that builds on this result, try using program synthesis to discover the condition, @racket[(bvmid-and? l h)], that is both necessary and sufficient to ensure that @racket[bvand] and @racket[bvmid-fast] produce the same value on all distinct legal inputs @racket[l] and @racket[r]. (Hint: you can reuse the @racket[fast-int32] grammar from the previous section.)
@examples[#:eval rosette-eval #:label #f
(eval:no-prompt
(define (bvmid-and? lo hi)
#f (code:comment "<--- replace with your sketch\n")
))
(eval:alts
(print-forms
(synthesize
#:forall (list l h)
#:guarantee
(begin
(assume (not (equal? l h)))
(assume (bvsle (int32 0) l))
(assume (bvsle l h))
(assert
(<=> (bvmid-and? l h)
(equal? (bvand l h) (bvmid-fast l h)))))))
(void))]
@(rosette-eval '(clear-vc!))
@section[#:tag "sec:notes"]{Symbolic Reasoning}
We conclude this chapter with a quick overview of common patterns and anti-patterns for programming in Rosette. For more details, see Chapters @seclink["ch:unsafe"]{8}--@seclink["ch:error-tracing"]{10}.
@subsection{Mixing Theories}
Rosette implements solver-aided queries by translating them to the input language of an SMT solver. By default, this translation respects types: a symbolic constant of type @racket[integer?] will be translated to an SMT constant of the same type, i.e., an infinite precision mathematical integer. These types determine which @emph{theories} the solver will need to use to solve a query. As a rule of thumb, the theory of bitvectors tends to elicit fastest solving times, and mixing theories can lead to severe performance degradation. For that reason, it is best to use the types from the same theory throughout your program (e.g., bitvectors).
To illustrate the impact of mixing theories, consider the following mixed-theory specification for our midpoint example:
@examples[#:eval rosette-eval #:label #f #:no-prompt
(code:line
(define (check-mid-slow impl lo hi) (code:comment "Assuming that")
(assume (bvsle (int32 0) lo)) (code:comment "0 ≤ lo and")
(assume (bvsle lo hi)) (code:comment "lo ≤ hi,")
(assert (code:comment "we require that")
(equal?
(bitvector->integer (impl lo hi)) (code:comment "⌈impl(lo, hi)⌉ = ")
(quotient (code:comment "(⌈lo⌉ + ⌈hi⌉) / 2, where")
(+ (bitvector->integer lo) (code:comment "⌈e⌉ stands for the mathematical")
(bitvector->integer hi)) (code:comment "integer corresponding to the")
2)))) (code:comment "32-bit integer e."))]
This new specification uses both bitvectors and integers. Compared to @racket[check-mid], which uses only bitvectors, @racket[check-mid-slow] causes one of our verification queries to become an order of magnitude slower and the other to time out:
@(rosette-eval '(require (only-in racket error)))
@examples[#:eval rosette-eval #:label #f
(time (verify (check-mid bvmid l h)))
(time (verify (check-mid-slow bvmid l h)))
(time (verify (check-mid bvmid-no-overflow l h)))
(eval:alts
(with-deep-time-limit 600 (code:comment "Timeout after 10 minutes ...")
(verify (check-mid-slow bvmid-no-overflow l h)))
(eval:error (error 'call-with-deep-time-limit "out of time")))]
@subsection{Reasoning Precision}
While slower than bitvectors, integers are more convenient to use for demos, prototyping, and interfacing with Racket. To bridge this gap, Rosette provides the option of approximating symbolic integers (and reals) as bitvectors of length @var{k}, by setting the @racket[current-bitwidth] parameter to @var{k}. With this setting, integers (and reals) are treated as infinite precision values during evaluation, but when solving queries, they are translated to bitvectors of length @var{k} for better performance.
For example, our slow midpoint queries become orders-of-magnitude faster when allowed to approximate integers with bitvectors:
@examples[#:eval rosette-eval #:label #f
(code:comment "By default, current-bitwidth is set to #f, so Rosette translates")
(code:comment "integer? values precisely, using the SMT theory of integers.")
(current-bitwidth)
(code:comment "After we set current-bitwidth to 64, integer? values in")
(code:comment "check-mid-slow are translated to SMT bitvectors of length 64.")
(current-bitwidth 64)
(time (verify (check-mid-slow bvmid l h)))
(time (verify (check-mid-slow bvmid-no-overflow l h)))]
In this example, we have chosen @racket[current-bitwidth] carefully to ensure that the resulting approximation is both performant and sound---i.e., the approximate query returns a counterexample exactly when one would be returned by the corresponding integer query. But choosing the right bitwidth is difficult to do in general. If we underapproximate the number of bits that are needed to represent every integer value in a query, we lose soundness, and if we overapproximate it, we lose performance.
For instance, when we re-run the slow midpoint queries with @racket[current-bitwidth] set to 32, the buggy query fails to produce a counterexample and the correct query returns a bogus counterexample. Both results are correct for 32-bit machine integers but incorrect for (infinite-precision) mathematical integers:
@examples[#:eval rosette-eval #:label #f
(code:comment "The bitwidth is too low, so we get ...")
(current-bitwidth 32)
(code:comment "no counterexample to a buggy query, and")
(time (verify (check-mid-slow bvmid l h)))
(code:comment "a bogus counterexample to a correct query.")
(time (verify (check-mid-slow bvmid-no-overflow l h)))]
We can restore soundness by sacrificing performance and setting @racket[current-bitwidth] conservatively to a large value (e.g., 512). In our case, the queries are small so an order-of-magnitude slowdown is acceptable. For large queries, this would lead to timeouts:
@examples[#:eval rosette-eval #:label #f
(code:comment "The bitwidth is too high, so we get a 3-10X slowdown.")
(current-bitwidth 512)
(time (verify (check-mid-slow bvmid l h)))
(time (verify (check-mid-slow bvmid-no-overflow l h)))]
In practice, it is usually best to leave @racket[current-bitwidth] at its default setting (@racket[#f]), and limit the use of integers to code that will be evaluated concretely. This approach works especially well when the solver is configured to accept only bitvectors, so if any integers have made it into a query, the solver fails fast:
@examples[#:eval rosette-eval #:label #f
(current-bitwidth #f)
(require rosette/solver/smt/z3)
(code:line (current-solver (z3 #:logic 'QF_BV)) (code:comment "Allow only bitvectors."))
(code:line (time (verify (check-mid bvmid l h))) (code:comment "Accepted."))
(eval:alts
(code:line (time (verify (check-mid-slow bvmid l h))) (code:comment "Rejected."))
(eval:error (time (verify (check-mid-slow bvmid l h)))))]
@(rosette-eval '(clear-vc!))
@(rosette-eval '(current-solver (z3)))
@subsection{Symbolic Evaluation}
The process by which Rosette turns a query into an SMT constraint is called @deftech{symbolic evaluation}. At a high level, Rosette's symbolic evaluation works by executing @emph{all} paths through a program, and collecting all the assumptions and assertions on these paths into the current verification condition @racket[(vc)]. The resulting @racket[(vc)] is then translated to the SMT language and passed to the solver. This evaluation model has two practical implications on writing performant and terminating Rosette code.
First, if a program is slow or runs forever under standard (concrete) evaluation, it will perform at least as poorly under all-path (symbolic) evaluation. Second, if a program terminates quickly on all concrete inputs, it can still perform poorly or fail to terminate on symbolic inputs. So, extra care must be taken to ensure good performance and termination in the presence of symbolic values.
To illustrate, consider the procedure @racket[bvsqrt] for computing the @hyperlink["https://en.wikipedia.org/wiki/Integer_square_root#Using_bitwise_operations"]{integer square root} of non-negative 32-bit integers. This procedure terminates on all concrete values of @racket[n] but runs forever when given a symbolic input:
@examples[#:eval rosette-eval #:label #f
(eval:no-prompt
(define (bvsqrt n)
(cond
[(bvult n (int32 2)) n]
[else
(define s0 (bvshl (bvsqrt (bvlshr n (int32 2))) (int32 1)))
(define s1 (bvadd s0 (int32 1)))
(if (bvugt (bvmul s1 s1) n) s0 s1)])))
(bvsqrt (int32 3))
(bvsqrt (int32 4))
(bvsqrt (int32 15))
(bvsqrt (int32 16))
(eval:alts
(with-deep-time-limit 10 (code:comment "Timeout after 10 seconds ...")
(bvsqrt l))
(eval:error (error 'call-with-deep-time-limit "out of time")))]
The reason is simple: a call to @racket[bvsqrt] terminates when @racket[n] becomes less than 2. But if we start with a symbolic @racket[n], this never happens because Rosette right-shifts @racket[n] by 2 in each recursive call to generate a new symbolic value:
@examples[#:eval rosette-eval #:label #f
(define n0 l)
n0
(define n1 (bvlshr n0 (int32 2)))
n1
(define n2 (bvlshr n1 (int32 2)))
n2
(define n3 (bvlshr n2 (int32 2)))
n3]
In general, recursion terminates under symbolic evaluation only when the stopping condition is reached with concrete values.
We can force termination by placing a concrete bound @var{k} on the number of times @racket[bvsqrt] can call itself recursively. This approach is called @deftech{finitization}, and it is the standard way to handle unbounded loops and recursion under symbolic evaluation. The following code shows how to implement a @emph{sound} finitization policy. If a @racket[verify] query returns @racket[(unsat)] under a sound policy, we know that (1) the unrolling bound @var{k} is sufficient to execute all possible inputs to @racket[bvsqrt], and (2) all of these executions satisfy the query. If we pick a bound that is too small, the query will generate a counterexample input that needs a larger bound to compute the result. In our example, the bound of 16 is sufficient to verify the correctness of @racket[bvsqrt] on all inputs:
@(rosette-eval '(clear-vc!))
@(rosette-eval '(require (only-in racket make-parameter parameterize)))
@examples[#:eval rosette-eval #:label #f #:no-prompt
(code:comment "Parameter that controls the number of unrollings (5 by default).")
(define fuel (make-parameter 5))
(eval:no-prompt)
(code:comment "A simple macro for defining bounded procedures")
(code:comment "that use (fuel) to limit recursion.")
(define-syntax-rule
(define-bounded (id param ...) body ...)
(define (id param ...)
(assert (> (fuel) 0) "Out of fuel.")
(parameterize ([fuel (sub1 (fuel))])
body ...)))
(eval:no-prompt)
(code:comment "Computes bvsqrt taking at most (fuel) steps.")
(define-bounded (bvsqrt n)
(cond
[(bvult n (int32 2)) n]
[else
(define s0 (bvshl (bvsqrt (bvlshr n (int32 2))) (int32 1)))
(define s1 (bvadd s0 (int32 1)))
(if (bvugt (bvmul s1 s1) n) s0 s1)]))
(eval:no-prompt)
(code:comment "Correctness specification for bvsqrt:")
(code:line
(define (check-sqrt impl n)
(assume (bvsle (int32 0) n)) (code:comment "Assuming n ≥ 0,")
(define √n (impl l))
(define √n+1 (bvadd √n (int32 1))) (code:comment "we require that")
(assert (bvule (bvmul √n √n) n)) (code:comment "(√n)^2 ≤ n and")
(assert (bvult n (bvmul √n+1 √n+1)))) (code:comment "n < (√n + 1)^2."))]
@examples[#:eval rosette-eval #:label #f
(code:comment "Verification fails due to insufficient fuel.")
(define cex (time (verify (check-sqrt bvsqrt l))))
(eval:error (bvsqrt (evaluate l cex)))
(clear-vc!)
(code:comment "Verification succeeds with enough fuel.")
(fuel 16)
(time (verify (check-sqrt bvsqrt l)))]
Many other finitization policies can be defined in a similar way. For example, if we change @racket[define-bounded] to use @racket[assume] instead of @racket[assert], we obtain a finitization policy that ensures @emph{completeness}. If a @racket[verify] query returns a counterexample under a complete policy, we know that the program is buggy, and it violates a query assertion within @var{k} recursive calls. But if the query returns @racket[(unsat)], we know only that there are no bugs within @var[k] or fewer unrollings---we cannot conclude anything about longer executions. So a complete policy prevents false positives, while a sound one prevents false negatives. What kind of policy to use depends on the application, and Rosette leaves that choice to the programmer.
@(kill-evaluator rosette-eval)
@(footnote-part)

View File

@ -0,0 +1,13 @@
#lang scribble/manual
@(require (for-label racket))
@title[#:tag "ch:syntactic-forms" #:style 'toc]{Syntactic Forms}
The core of the Rosette language (@racketmodname[rosette/safe]) consists of two kinds of syntax forms: a set of basic forms @deftech[#:key "lifted constructs"]{lifted} from Racket, and a set of forms for @seclink["ch:essentials"]{solver-aided programming}. We use the term ``lifted'' to refer to parts of the Racket language that can be used with symbolic values and other solver-aided constructs.
@[table-of-contents]
@include-section["racket-forms.scrbl"]
@include-section["rosette-forms.scrbl"]

View File

@ -2,7 +2,7 @@
@(require (for-label rosette/base/form/define)
(for-label racket)
scribble/core scribble/html-properties scribble/eval racket/sandbox
scribble/core scribble/html-properties scribble/examples racket/sandbox
"../util/lifted.rkt")
@ -23,7 +23,7 @@
(select '(let let* letrec let-values let*-values letrec-values let-syntax
letrec-syntax let-syntaxes letrec-syntaxes letrec-syntaxes+values)))
@(define local-defs (select '(local)))
@(define conditionals (select '(if cond else and or)))
@(define conditionals (select '(if and or)))
@(define dispatch (select '(case)))
@(define definitions
(select '(define define-values define-syntax define-syntaxes
@ -31,7 +31,7 @@
@(define sequencing (select '(begin begin0 begin-for-syntax)))
@(define guarded-eval (select '(when unless)))
@(define assignment (select '(set! set!-values)))
@(define quasiquoting (select '(quasiquote unquote unquote-splicing)))
@(define quasiquoting (select '(quasiquote unquote)))
@(define syntax-quoting (select '(quote-syntax)))
@(define rosette-eval (rosette-evaluator))
@ -48,7 +48,7 @@ Rosette lifts the following @seclink["syntax" #:doc '(lib "scribblings/reference
(list @elem{Procedure Expressions} @procs)
(list @elem{Local Binding} @local-binding)
(list @elem{Local Definitions} @local-defs)
(list @elem{Conditionals} @conditionals)
(list @elem{Conditionals} (list @conditionals ", " @racket[cond] " with " @racket[[#, @var[test] #, @var[body] ...+]] " and " @racket[[else #, @var[body] ...+]] " clauses"))
(list @elem{Dispatch} @dispatch)
(list @elem{Definitions} @definitions)
(list @elem{Sequencing} @sequencing)
@ -58,7 +58,7 @@ Rosette lifts the following @seclink["syntax" #:doc '(lib "scribblings/reference
(list @elem{Syntax Quoting} @syntax-quoting))]
Lifted forms have the same meaning in Rosette programs as they do in Racket programs. For example, the Racket expression @racket[(if #, @var[test-expr] #, @var[then-expr] #, @var[else-expr])] evaluates @var[test-expr] first and then, depending on the outcome, it returns the result of evaluating either @var[then-expr] or @var[else-expr]. Rosette preserves this interpretation of @racket[if] for concrete values, and also extends it to work with symbolic values:
@interaction[#:eval rosette-eval
@examples[#:eval rosette-eval #:label #f
(let ([y 0])
(if #t (void) (set! y 3))
(printf "y unchanged: ~a\n" y)

Some files were not shown because too many files have changed in this diff Show More